[
  {
    "path": ".gitignore",
    "content": "*~\n*.pyc\n"
  },
  {
    "path": "Android.bp",
    "content": "dirgroup {\n    name: \"trusty_dirgroup_system_core\",\n    dirs: [\".\"],\n    visibility: [\"//trusty/vendor/google/aosp/scripts\"],\n}\n"
  },
  {
    "path": "CleanSpec.mk",
    "content": "# Copyright (C) 2007 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# If you don't need to do a full clean build but would like to touch\n# a file or delete some intermediate files, add a clean step to the end\n# of the list.  These steps will only be run once, if they haven't been\n# run before.\n#\n# E.g.:\n#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)\n#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)\n#\n# Always use \"touch -c\" and \"rm -f\" or \"rm -rf\" to gracefully deal with\n# files that are missing or have been moved.\n#\n# Use $(PRODUCT_OUT) to get to the \"out/target/product/blah/\" directory.\n# Use $(OUT_DIR) to refer to the \"out\" directory.\n#\n# If you need to re-do something that's already mentioned, just copy\n# the command and add it to the bottom of the list.  E.g., if a change\n# that you made last week required touching a file and a change you\n# made today requires touching the same file, just copy the old\n# touch step and add it to the end of the list.\n#\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n\n# For example:\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)\n#$(call add-clean-step, find $(OUT_DIR) -type f -name \"IGTalkSession*\" -print0 | xargs -0 rm -f)\n#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)\n\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/reboot)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libsysutils_intermediates/import_includes)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/grep $(PRODUCT_OUT)/system/bin/toolbox)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.$(TARGET_DEVICE).so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.$(TARGET_DEVICE).so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/init.rc)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libtrusty.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libtrusty.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/keystore.trusty.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/keystore.trusty.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/hw/gatekeeper.trusty.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/hw/gatekeeper.trusty.so)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/secure-storage-unit-test)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/storageproxyd)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/tipc-test)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/trusty_keymaster_tipc)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/root)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/ld.config.txt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/llndk.libraries.txt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vndksp.libraries.txt)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin/charger)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin/charger)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/sbin)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product_services.img)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product_services)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/product_services)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/product_services)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/debug_ramdisk/product_services)\n$(call add-clean-step, find $(PRODUCT_OUT) -type l -name \"charger\" -print0 | xargs -0 rm -f)\n$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/bin/adbd)\n$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/init/snapshotctl.rc)\n"
  },
  {
    "path": "MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "OWNERS",
    "content": "# Bug component: 128577\nenh@google.com\n"
  },
  {
    "path": "PREUPLOAD.cfg",
    "content": "[Builtin Hooks]\nclang_format = true\nrustfmt = true\nbpfmt = true\n\n[Builtin Hooks Options]\nclang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp\nrustfmt = --config-path=rustfmt.toml\n\n[Hook Scripts]\naosp_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} \".\"\n"
  },
  {
    "path": "bootstat/Android.bp",
    "content": "//\n// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nbootstat_lib_src_files = [\n    \"boot_event_record_store.cpp\",\n]\n\ncc_defaults {\n    name: \"bootstat_defaults\",\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"liblog\",\n    ],\n    header_libs: [\"libgtest_prod_headers\"],\n}\n\n// bootstat static library\n// -----------------------------------------------------------------------------\ncc_library_static {\n    name: \"libbootstat\",\n    defaults: [\"bootstat_defaults\"],\n    srcs: bootstat_lib_src_files,\n}\n\n// bootstat static library, debug\n// -----------------------------------------------------------------------------\ncc_library_static {\n    name: \"libbootstat_debug\",\n    defaults: [\"bootstat_defaults\"],\n    host_supported: true,\n    srcs: bootstat_lib_src_files,\n\n    target: {\n        host: {\n            cflags: [\"-UNDEBUG\"],\n        },\n    },\n}\n\n// bootstat binary\n// -----------------------------------------------------------------------------\ncc_binary {\n    name: \"bootstat\",\n    defaults: [\"bootstat_defaults\"],\n    static_libs: [\"libbootstat\"],\n    shared_libs: [\n        \"libstatslog\"\n    ],\n    init_rc: [\"bootstat.rc\"],\n    product_variables: {\n        debuggable: {\n            init_rc: [\"bootstat-debug.rc\"],\n        },\n    },\n    srcs: [\"bootstat.cpp\"],\n}\n\n// Native tests\n// -----------------------------------------------------------------------------\ncc_test {\n    name: \"bootstat_tests\",\n    test_suites: [\"device-tests\"],\n    defaults: [\"bootstat_defaults\"],\n    host_supported: true,\n    static_libs: [\n        \"libbootstat_debug\",\n        \"libgmock\",\n    ],\n    srcs: [\n        \"boot_event_record_store_test.cpp\",\n        \"testrunner.cpp\",\n    ],\n    test_options: {\n        unit_test: true,\n    },\n}\n"
  },
  {
    "path": "bootstat/OWNERS",
    "content": "dvander@google.com\nachant@google.com\nmarkcheng@google.com\n"
  },
  {
    "path": "bootstat/README.md",
    "content": "# bootstat #\n\nThe bootstat command records boot events (e.g., `firmware_loaded`,\n`boot_complete`) and the relative time at which these events occurred. The\ncommand also aggregates boot event metrics locally and logs the metrics for\nanalysis.\n\n    Usage: bootstat [options]\n    options include:\n      -h, --help            Show this help\n      -l, --log             Log all metrics to logstorage\n      -p, --print           Dump the boot event records to the console\n      -r, --record          Record the timestamp of a named boot event\n      --record_boot_reason  Record the reason why the device booted\n      --record_time_since_factory_reset Record the time since the device was reset\n\n## Relative time ##\n\nThe timestamp recorded by bootstat is the uptime of the system, i.e., the\nnumber of seconds since the system booted.\n\n## Recording boot events ##\n\nTo record the relative time of an event during the boot phase, call `bootstat`\nwith the `-r` option and the name of the boot event.\n\n    $ bootstat -r boot_complete\n\nThe relative time at which the command runs is recorded along with the name of\nthe boot event to be persisted.\n\n## Logging boot events ##\n\nTo log the persisted boot events, call `bootstat` with the `-l` option.\n\n    $ bootstat -l\n\nbootstat logs all boot events recorded using the `-r` option to the EventLog\nusing the Tron histogram. These logs may be uploaded by interested parties\nfor aggregation and analysis of boot time across different devices and\nversions.\n\n## Printing boot events ##\n\nTo print the set of persisted boot events, call `bootstat` with the `-p` option.\n\n    $ bootstat -p\n    Boot events:\n    ------------\n    boot_complete   71"
  },
  {
    "path": "bootstat/boot_event_record_store.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"boot_event_record_store.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <utime.h>\n\n#include <chrono>\n#include <cstdlib>\n#include <string>\n#include <utility>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n\nnamespace {\n\nconst char BOOTSTAT_DATA_DIR[] = \"/data/misc/bootstat/\";\n\n// Given a boot even record file at |path|, extracts the event's relative time\n// from the record into |uptime|.\nbool ParseRecordEventTime(const std::string& path, int32_t* uptime) {\n  DCHECK_NE(static_cast<int32_t*>(nullptr), uptime);\n\n  struct stat file_stat;\n  if (stat(path.c_str(), &file_stat) == -1) {\n    PLOG(ERROR) << \"Failed to read \" << path;\n    return false;\n  }\n\n  *uptime = file_stat.st_mtime;\n\n  return true;\n}\n\n}  // namespace\n\nBootEventRecordStore::BootEventRecordStore() {\n  SetStorePath(BOOTSTAT_DATA_DIR);\n}\n\nvoid BootEventRecordStore::AddBootEvent(const std::string& event) {\n  auto uptime = std::chrono::duration_cast<std::chrono::seconds>(\n      android::base::boot_clock::now().time_since_epoch());\n  AddBootEventWithValue(event, uptime.count());\n}\n\n// The implementation of AddBootEventValue makes use of the mtime file\n// attribute to store the value associated with a boot event in order to\n// optimize on-disk size requirements and small-file thrashing.\nvoid BootEventRecordStore::AddBootEventWithValue(const std::string& event, int32_t value) {\n  std::string record_path = GetBootEventPath(event);\n  int record_fd = creat(record_path.c_str(), S_IRUSR | S_IWUSR);\n  if (record_fd == -1) {\n    PLOG(ERROR) << \"Failed to create \" << record_path;\n    return;\n  }\n\n  // Fill out the stat structure for |record_path| in order to get the atime to\n  // set in the utime() call.\n  struct stat file_stat;\n  if (stat(record_path.c_str(), &file_stat) == -1) {\n    PLOG(ERROR) << \"Failed to read \" << record_path;\n    close(record_fd);\n    return;\n  }\n\n  // Set the |modtime| of the file to store the value of the boot event while\n  // preserving the |actime| (as read by stat).\n  struct utimbuf times = {/* actime */ file_stat.st_atime, /* modtime */ value};\n  if (utime(record_path.c_str(), &times) == -1) {\n    PLOG(ERROR) << \"Failed to set mtime for \" << record_path;\n    close(record_fd);\n    return;\n  }\n\n  close(record_fd);\n}\n\nbool BootEventRecordStore::GetBootEvent(const std::string& event, BootEventRecord* record) const {\n  CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);\n  CHECK(!event.empty());\n\n  const std::string record_path = GetBootEventPath(event);\n  int32_t uptime;\n  if (!ParseRecordEventTime(record_path, &uptime)) {\n    LOG(ERROR) << \"Failed to parse boot time record: \" << record_path;\n    return false;\n  }\n\n  *record = std::make_pair(event, uptime);\n  return true;\n}\n\nstd::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::GetAllBootEvents() const {\n  std::vector<BootEventRecord> events;\n\n  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(store_path_.c_str()), closedir);\n\n  // This case could happen due to external manipulation of the filesystem,\n  // so crash out if the record store doesn't exist.\n  CHECK_NE(static_cast<DIR*>(nullptr), dir.get());\n\n  struct dirent* entry;\n  while ((entry = readdir(dir.get())) != NULL) {\n    // Only parse regular files.\n    if (entry->d_type != DT_REG) {\n      continue;\n    }\n\n    const std::string event = entry->d_name;\n    BootEventRecord record;\n    if (!GetBootEvent(event, &record)) {\n      LOG(ERROR) << \"Failed to parse boot time event: \" << event;\n      continue;\n    }\n\n    events.push_back(record);\n  }\n\n  return events;\n}\n\nvoid BootEventRecordStore::SetStorePath(const std::string& path) {\n  DCHECK_EQ('/', path.back());\n  store_path_ = path;\n}\n\nstd::string BootEventRecordStore::GetBootEventPath(const std::string& event) const {\n  DCHECK_EQ('/', store_path_.back());\n  return store_path_ + event;\n}\n"
  },
  {
    "path": "bootstat/boot_event_record_store.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef BOOT_EVENT_RECORD_STORE_H_\n#define BOOT_EVENT_RECORD_STORE_H_\n\n#include <android-base/macros.h>\n#include <gtest/gtest_prod.h>\n#include <cstdint>\n#include <string>\n#include <utility>\n#include <vector>\n\n// BootEventRecordStore manages the persistence of boot events to the record\n// store and the retrieval of all boot event records from the store.\nclass BootEventRecordStore {\n public:\n  // A BootEventRecord consists of the event name and the timestamp the event\n  // occurred.\n  typedef std::pair<std::string, int32_t> BootEventRecord;\n\n  BootEventRecordStore();\n\n  // Persists the boot |event| in the record store.\n  void AddBootEvent(const std::string& event);\n\n  // Persists the boot |event| with the associated |value| in the record store.\n  void AddBootEventWithValue(const std::string& event, int32_t value);\n\n  // Queries the named boot |event|. |record| must be non-null. |record|\n  // contains the boot event data on success. Returns true iff the query is\n  // successful.\n  bool GetBootEvent(const std::string& event, BootEventRecord* record) const;\n\n  // Returns a list of all of the boot events persisted in the record store.\n  std::vector<BootEventRecord> GetAllBootEvents() const;\n\n private:\n  // The tests call SetStorePath to override the default store location with a\n  // more test-friendly path.\n  FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);\n  FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);\n  FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);\n  FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);\n  FRIEND_TEST(BootEventRecordStoreTest, GetBootEventNoFileContent);\n\n  // Sets the filesystem path of the record store.\n  void SetStorePath(const std::string& path);\n\n  // Constructs the full path of the given boot |event|.\n  std::string GetBootEventPath(const std::string& event) const;\n\n  // The filesystem path of the record store.\n  std::string store_path_;\n\n  DISALLOW_COPY_AND_ASSIGN(BootEventRecordStore);\n};\n\n#endif  // BOOT_EVENT_RECORD_STORE_H_\n"
  },
  {
    "path": "bootstat/boot_event_record_store_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"boot_event_record_store.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <cstdint>\n#include <cstdlib>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n\nusing testing::UnorderedElementsAreArray;\n\nnamespace {\n\n// Creates a fake boot event record file at |record_path| containing the boot\n// record |value|. This method is necessary as truncating a\n// BootEventRecordStore-created file would modify the mtime, which would alter\n// the value of the record.\nbool CreateEmptyBootEventRecord(const std::string& record_path, int32_t value) {\n  android::base::unique_fd record_fd(creat(record_path.c_str(), S_IRUSR | S_IWUSR));\n  if (record_fd == -1) {\n    return false;\n  }\n\n  // Set the |mtime| of the file to store the value of the boot event while\n  // preserving the |atime|.\n  struct timeval atime = {/* tv_sec */ 0, /* tv_usec */ 0};\n  struct timeval mtime = {/* tv_sec */ value, /* tv_usec */ 0};\n  const struct timeval times[] = {atime, mtime};\n  if (utimes(record_path.c_str(), times) != 0) {\n    return false;\n  }\n\n  return true;\n}\n\n// Returns true if the time difference between |a| and |b| is no larger\n// than 10 seconds.  This allow for a relatively large fuzz when comparing\n// two timestamps taken back-to-back.\nbool FuzzUptimeEquals(int32_t a, int32_t b) {\n  const int32_t FUZZ_SECONDS = 10;\n  return (abs(a - b) <= FUZZ_SECONDS);\n}\n\n// Recursively deletes the directory at |path|.\nvoid DeleteDirectory(const std::string& path) {\n  typedef std::unique_ptr<DIR, decltype(&closedir)> ScopedDIR;\n  ScopedDIR dir(opendir(path.c_str()), closedir);\n  ASSERT_NE(nullptr, dir.get());\n\n  struct dirent* entry;\n  while ((entry = readdir(dir.get())) != NULL) {\n    const std::string entry_name(entry->d_name);\n    if (entry_name == \".\" || entry_name == \"..\") {\n      continue;\n    }\n\n    const std::string entry_path = path + \"/\" + entry_name;\n    if (entry->d_type == DT_DIR) {\n      DeleteDirectory(entry_path);\n    } else {\n      unlink(entry_path.c_str());\n    }\n  }\n\n  rmdir(path.c_str());\n}\n\n// Returns the time in seconds since boot.\ntime_t GetUptimeSeconds() {\n  return std::chrono::duration_cast<std::chrono::seconds>(\n             android::base::boot_clock::now().time_since_epoch())\n      .count();\n}\n\nclass BootEventRecordStoreTest : public ::testing::Test {\n public:\n  BootEventRecordStoreTest() { store_path_ = std::string(store_dir_.path) + \"/\"; }\n\n  const std::string& GetStorePathForTesting() const { return store_path_; }\n\n private:\n  void TearDown() {\n    // This removes the record store temporary directory even though\n    // TemporaryDir should already take care of it, but this method cleans up\n    // the test files added to the directory which prevent TemporaryDir from\n    // being able to remove the directory.\n    DeleteDirectory(store_path_);\n  }\n\n  // A scoped temporary directory. Using this abstraction provides creation of\n  // the directory and the path to the directory, which is stored in\n  // |store_path_|.\n  TemporaryDir store_dir_;\n\n  // The path to the temporary directory used by the BootEventRecordStore to\n  // persist records.  The directory is created and destroyed for each test.\n  std::string store_path_;\n\n  DISALLOW_COPY_AND_ASSIGN(BootEventRecordStoreTest);\n};\n\n}  // namespace\n\nTEST_F(BootEventRecordStoreTest, AddSingleBootEvent) {\n  BootEventRecordStore store;\n  store.SetStorePath(GetStorePathForTesting());\n\n  time_t uptime = GetUptimeSeconds();\n  ASSERT_NE(-1, uptime);\n\n  store.AddBootEvent(\"cenozoic\");\n\n  auto events = store.GetAllBootEvents();\n  ASSERT_EQ(1U, events.size());\n  EXPECT_EQ(\"cenozoic\", events[0].first);\n  EXPECT_TRUE(FuzzUptimeEquals(uptime, events[0].second));\n}\n\nTEST_F(BootEventRecordStoreTest, AddMultipleBootEvents) {\n  BootEventRecordStore store;\n  store.SetStorePath(GetStorePathForTesting());\n\n  time_t uptime = GetUptimeSeconds();\n  ASSERT_NE(-1, uptime);\n\n  store.AddBootEvent(\"cretaceous\");\n  store.AddBootEvent(\"jurassic\");\n  store.AddBootEvent(\"triassic\");\n\n  const std::string EXPECTED_NAMES[] = {\n      \"cretaceous\", \"jurassic\", \"triassic\",\n  };\n\n  auto events = store.GetAllBootEvents();\n  ASSERT_EQ(3U, events.size());\n\n  std::vector<std::string> names;\n  std::vector<int32_t> timestamps;\n  for (auto i = events.begin(); i != events.end(); ++i) {\n    names.push_back(i->first);\n    timestamps.push_back(i->second);\n  }\n\n  EXPECT_THAT(names, UnorderedElementsAreArray(EXPECTED_NAMES));\n\n  for (auto i = timestamps.cbegin(); i != timestamps.cend(); ++i) {\n    EXPECT_TRUE(FuzzUptimeEquals(uptime, *i));\n  }\n}\n\nTEST_F(BootEventRecordStoreTest, AddBootEventWithValue) {\n  BootEventRecordStore store;\n  store.SetStorePath(GetStorePathForTesting());\n\n  store.AddBootEventWithValue(\"permian\", 42);\n\n  auto events = store.GetAllBootEvents();\n  ASSERT_EQ(1U, events.size());\n  EXPECT_EQ(\"permian\", events[0].first);\n  EXPECT_EQ(42, events[0].second);\n}\n\nTEST_F(BootEventRecordStoreTest, GetBootEvent) {\n  BootEventRecordStore store;\n  store.SetStorePath(GetStorePathForTesting());\n\n  // Event does not exist.\n  BootEventRecordStore::BootEventRecord record;\n  bool result = store.GetBootEvent(\"nonexistent\", &record);\n  EXPECT_EQ(false, result);\n\n  // Empty path.\n  EXPECT_DEATH(store.GetBootEvent(std::string(), &record), std::string());\n\n  // Success case.\n  store.AddBootEventWithValue(\"carboniferous\", 314);\n  result = store.GetBootEvent(\"carboniferous\", &record);\n  EXPECT_EQ(true, result);\n  EXPECT_EQ(\"carboniferous\", record.first);\n  EXPECT_EQ(314, record.second);\n\n  // Null |record|.\n  EXPECT_DEATH(store.GetBootEvent(\"carboniferous\", nullptr), std::string());\n}\n\n// Tests that the BootEventRecordStore is capable of handling an older record\n// protocol which does not contain file contents.\nTEST_F(BootEventRecordStoreTest, GetBootEventNoFileContent) {\n  BootEventRecordStore store;\n  store.SetStorePath(GetStorePathForTesting());\n\n  EXPECT_TRUE(CreateEmptyBootEventRecord(store.GetBootEventPath(\"devonian\"), 2718));\n\n  BootEventRecordStore::BootEventRecord record;\n  bool result = store.GetBootEvent(\"devonian\", &record);\n  EXPECT_EQ(true, result);\n  EXPECT_EQ(\"devonian\", record.first);\n  EXPECT_EQ(2718, record.second);\n}\n"
  },
  {
    "path": "bootstat/boot_reason_test.sh",
    "content": "#! /bin/bash\n#\n# Bootstat boot reason tests\n#\n# throughout testing:\n# - manual tests can only run on eng/userdebug builds\n# - watch adb logcat -b all -d -s bootstat\n# - watch adb logcat -b all -d | audit2allow\n# - wait until screen is up, boot has completed, can mean wait for\n#   sys.boot_completed=1 and sys.bootstat.first_boot_completed=1 to be true\n#\n# All test frames, and nothing else, must be function names prefixed and\n# specifiged with the pattern 'test_<test>() {' as this is also how the\n# script discovers the full list of tests by inspecting its own code.\n#\n\n# Helper variables\n\nSPACE=\" \"\nESCAPE=\"\u001b\"\nTAB=\"\t\"\nGREEN=\"${ESCAPE}[38;5;40m\"\nRED=\"${ESCAPE}[38;5;196m\"\nNORMAL=\"${ESCAPE}[0m\"\n# Best guess to an average device's reboot time, refined as tests return\nDURATION_DEFAULT=45\nSTOP_ON_FAILURE=false\nprogname=\"${0##*/}\"\nprogpath=\"${0%${progname}}\"\n\n# Helper functions\n\n[ \"USAGE: inFastboot\n\nReturns: true if device is in fastboot mode\" ]\ninFastboot() {\n  fastboot devices | grep \"^${ANDROID_SERIAL}[${SPACE}${TAB}]\" > /dev/null\n}\n\n[ \"USAGE: inAdb\n\nReturns: true if device is in adb mode\" ]\ninAdb() {\n  adb devices | grep -v 'List of devices attached' | grep \"^${ANDROID_SERIAL}[${SPACE}${TAB}]\" > /dev/null\n}\n\n[ \"USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr\n\nReturns: true if the command succeeded\" ]\nadb_sh() {\n  local args=\n  for i in \"${@}\"; do\n    [ -z \"${args}\" ] || args=\"${args} \"\n    if [ X\"${i}\" != X\"${i#\\'}\" ]; then\n      args=\"${args}${i}\"\n    elif [ X\"${i}\" != X\"${i#*\\\\}\" ]; then\n      args=\"${args}`echo ${i} | sed 's/\\\\\\\\/\\\\\\\\\\\\\\\\/g'`\"\n    elif [ X\"${i}\" != X\"${i#* }\" ]; then\n      args=\"${args}'${i}'\"\n    elif [ X\"${i}\" != X\"${i#*${TAB}}\" ]; then\n      args=\"${args}'${i}'\"\n    else\n      args=\"${args}${i}\"\n    fi\n  done\n  adb shell \"${args}\"\n}\n\n[ \"USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr\n\nReturns: true if the command running as root succeeded\" ]\nadb_su() {\n  adb_sh su root \"${@}\"\n}\n\n[ \"USAGE: hasPstore\n\nReturns: true if device (likely) has pstore data\" ]\nhasPstore() {\n  if inAdb && [ 0 -eq `adb_su ls /sys/fs/pstore </dev/null | wc -l` ]; then\n    false\n  fi\n}\n\n[ \"USAGE: get_property <prop>\n\nReturns the property value\" ]\nget_property() {\n  adb_sh getprop ${1} 2>&1 </dev/null\n}\n\n[ \"USAGE: isDebuggable\n\nReturns: true if device is (likely) a debug build\" ]\nisDebuggable() {\n  if inAdb && [ 1 -ne `get_property ro.debuggable` ]; then\n    false\n  fi\n}\n\n[ \"USAGE: checkDebugBuild [--noerror]\n\nReturns: true if device is a userdebug or eng release\" ]\ncheckDebugBuild() {\n  if isDebuggable; then\n    echo \"INFO: '${TEST}' test requires userdebug build\"\n  elif [ -n \"${1}\" ]; then\n    echo \"WARNING: '${TEST}' test requires userdebug build\"\n    false\n  else\n    echo \"ERROR: '${TEST}' test requires userdebug build, skipping FAILURE\"\n    duration_prefix=\"~\"\n    duration_estimate=1\n    false\n  fi >&2\n}\n\n[ \"USAGE: setBootloaderBootReason [value]\n\nReturns: true if device supports and set boot reason injection\" ]\nsetBootloaderBootReason() {\n  inAdb || ( echo \"ERROR: device not in adb mode.\" >&2 ; false ) || return 1\n  if [ -z \"`adb_sh ls /etc/init/bootstat-debug.rc 2>/dev/null </dev/null`\" ]; then\n    echo \"ERROR: '${TEST}' test requires /etc/init/bootstat-debug.rc\" >&2\n    return 1\n  fi\n  checkDebugBuild || return 1\n  if adb_su \"cat /proc/cmdline | tr '\\\\0 ' '\\\\n\\\\n'\" </dev/null |\n     grep '^androidboot[.]bootreason=[^ ]' >/dev/null; then\n    echo \"ERROR: '${TEST}' test requires a device with a bootloader that\" >&2\n    echo \"       does not set androidboot.bootreason kernel parameter.\" >&2\n    return 1\n  fi\n  adb_su setprop persist.test.boot.reason \"'${1}'\" 2>/dev/null </dev/null\n  test_reason=\"`get_property persist.test.boot.reason`\"\n  if [ X\"${test_reason}\" != X\"${1}\" ]; then\n    echo \"ERROR: can not set persist.test.boot.reason to '${1}'.\" >&2\n    return 1\n  fi\n}\n\n[ \"USAGE: enterPstore\n\nPrints a warning string requiring functional pstore\n\nReturns: pstore_ok variable set to true or false\" ]\nenterPstore() {\n  if hasPstore; then\n    echo \"INFO: '${TEST}' test requires functional and reliable pstore\"\n    pstore_ok=true\n  else\n    echo \"WARNING: '${TEST}' test requires functional pstore\"\n    pstore_ok=false\n  fi >&2\n  ${pstore_ok}\n}\n\n[ \"USAGE: exitPstore\n\nPrints an error string requiring functional pstore\n\nReturns: clears error if pstore dysfunctional\" ]\nexitPstore() {\n  save_ret=${?}\n  if [ ${save_ret} != 0 ]; then\n    if hasPstore; then\n      return ${save_ret}\n    fi\n    if [ true = ${pstore_ok} ]; then\n      echo \"WARNING: '${TEST}' test requires functional pstore\"\n      return ${save_ret}\n    fi\n    echo \"ERROR: '${TEST}' test requires functional pstore, skipping FAILURE\"\n    duration_prefix=\"~\"\n    duration_estimate=1\n  fi >&2\n}\n\n[ \"USAGE: format_duration <seconds>\n\nhuman readable output whole seconds, whole minutes or mm:ss\" ]\nformat_duration() {\n  if [ -z \"${1}\" ]; then\n    echo unknown\n    return\n  fi\n  seconds=`expr ${1} % 60`\n  minutes=`expr ${1} / 60`\n  if [ 0 -eq ${minutes} ]; then\n    if [ 1 -eq ${1} ]; then\n      echo 1 second\n      return\n    fi\n    echo ${1} seconds\n    return\n  elif [ 60 -eq ${1} ]; then\n    echo 1 minute\n    return\n  elif [ 0 -eq ${seconds} ]; then\n    echo ${minutes} minutes\n    return\n  fi\n  echo ${minutes}:`expr ${seconds} / 10``expr ${seconds} % 10`\n}\n\nwait_for_screen_timeout=900\n[ \"USAGE: wait_for_screen [-n] [TIMEOUT]\n\n-n - echo newline at exit\nTIMEOUT - default `format_duration ${wait_for_screen_timeout}`\" ]\nwait_for_screen() {\n  exit_function=true\n  if [ X\"-n\" = X\"${1}\" ]; then\n    exit_function=echo\n    shift\n  fi\n  timeout=${wait_for_screen_timeout}\n  if [ ${#} -gt 0 ]; then\n    timeout=${1}\n    shift\n  fi\n  counter=0\n  while true; do\n    if inFastboot; then\n      fastboot reboot\n    elif inAdb; then\n      if [ 0 != ${counter} ]; then\n        adb wait-for-device </dev/null >/dev/null 2>/dev/null\n      fi\n      if [ -n \"`get_property sys.boot.reason`\" ]\n      then\n        vals=`get_property |\n              sed -n 's/[[]sys[.]\\(boot_completed\\|logbootcomplete\\|bootstat[.]first_boot_completed\\)[]]: [[]\\([01]\\)[]]$/\\1=\\2/p'`\n        if [ X\"${vals}\" != X\"${vals##*boot_completed=1}\" ]; then\n          if [ X\"${vals}\" != X\"${vals##*logbootcomple=1}\" ]; then\n            sleep 1\n            break\n          fi\n          if [ X\"${vals}\" != X\"${vals##*bootstat.first_boot_completed=1}\" ]; then\n            sleep 1\n            break\n          fi\n        fi\n      fi\n    fi\n    counter=`expr ${counter} + 1`\n    if [ ${counter} -gt ${timeout} ]; then\n      ${exit_function}\n      echo \"ERROR: wait_for_screen() timed out (`format_duration ${timeout}`)\" >&2\n      return 1\n    fi\n    sleep 1\n  done\n  ${exit_function}\n}\n\n[ \"USAGE: EXPECT_EQ <lval> <rval> [message]\n\nReturns true if (regex) lval matches rval\" ]\nEXPECT_EQ() {\n  lval=\"${1}\"\n  rval=\"${2}\"\n  shift 2\n  if ! ( echo X\"${rval}\" | grep '^X'\"${lval}\"'$' >/dev/null 2>/dev/null ); then\n    if [ `echo ${lval}${rval}${*} | wc -c` -gt 50 -o \"${rval}\" != \"${rval%\n*}\" ]; then\n      echo \"ERROR: expected \\\"${lval}\\\"\" >&2\n      echo \"       got \\\"${rval}\\\"\" |\n        sed ': again\n             N\n             s/\\(\\n\\)\\([^ ]\\)/\\1             \\2/\n             t again' >&2\n      if [ -n \"${*}\" ] ; then\n        echo \"       ${*}\" >&2\n      fi\n    else\n      echo \"ERROR: expected \\\"${lval}\\\" got \\\"${rval}\\\" ${*}\" >&2\n    fi\n    return 1\n  fi\n  if [ -n \"${*}\" ] ; then\n    if [ X\"${lval}\" != X\"${rval}\" ]; then\n      if [ `echo ${lval}${rval}${*} | wc -c` -gt 60 -o \"${rval}\" != \"${rval%\n*}\" ]; then\n        echo \"INFO: ok \\\"${lval}\\\"\" >&2\n        echo \"       = \\\"${rval}\\\"\" |\n          sed ': again\n               N\n               s/\\(\\n\\)\\([^ ]\\)/\\1          \\2/\n               t again' >&2\n        if [ -n \"${*}\" ] ; then\n          echo \"      ${*}\" >&2\n        fi\n      else\n        echo \"INFO: ok \\\"${lval}\\\" = \\\"${rval}\\\" ${*}\" >&2\n      fi\n    else\n      echo \"INFO: ok \\\"${lval}\\\" ${*}\" >&2\n    fi\n  fi\n  return 0\n}\n\nBAD_BOOTLOADER_REASON=\n\n[ \"USAGE: EXPECT_PROPERTY <prop> <value> [--allow_failure]\n\nReturns true (0) if current return (regex) value is true and the result matches\nand the incoming return value is true as well (wired-or)\" ]\nEXPECT_PROPERTY() {\n  save_ret=${?}\n  property=\"${1}\"\n  value=\"${2}\"\n  shift 2\n  val=`get_property ${property}`\n  EXPECT_EQ \"${value}\" \"${val}\" for Android property ${property}\n  local_ret=${?}\n  if [ 0 != ${local_ret} -a \"ro.boot.bootreason\" = \"${property}\" ]; then\n    if [ -z \"${BAD_BOOTLOADER_REASON}\" ]; then\n      BAD_BOOTLOADER_REASON=${val}\n    elif [ X\"${BAD_BOOTLOADER_REASON}\" = X\"${val}\" ]; then\n      local_ret=0\n    fi\n  fi\n  if [ 0 != ${local_ret} ]; then\n    if [ -z \"${1}\" ] ; then\n      save_ret=${local_ret}\n    fi\n  fi\n  return ${save_ret}\n}\n\n[ \"USAGE: adb_date >/dev/stdout\n\nReturns: report device epoch time (suitable for logcat -t)\" ]\nadb_date() {\n  adb_sh date +%s.%N </dev/null\n}\n\n[ \"USAGE: report_bootstat_logs [-t<timestamp>] <expected> ...\n\nif not prefixed with a minus (-), <expected> will become a series of expected\nmatches:\n\n    bootstat: Canonical boot reason: <expected_property_value>\n\nIf prefixed with a minus, <expected> will look for an exact match after\nremoving the minux prefix.  All expected content is _dropped_ from the output\nand in essence forms a known blacklist, unexpected content will show.\n\nReport any logs, minus a known blacklist, preserve the current exit status\" ]\nreport_bootstat_logs() {\n  save_ret=${?}\n  match=\n  timestamp=-d\n  for i in \"${@}\"; do\n    if [ X\"${i}\" != X\"${i#-t}\" ]; then\n      timestamp=\"${i}\"\n    elif [ X\"${i}\" != X\"${i#-}\" ]; then\n      match=\"${match}\n${i#-}\"\n    else\n      match=\"${match}\nbootstat: Canonical boot reason: ${i}\"\n    fi\n  done\n  adb logcat -b all ${timestamp} |\n  grep bootstat[^e] |\n  grep -v -F \"bootstat: Service started: /system/bin/bootstat --record_boot_complete${match}\nbootstat: Service started: /system/bin/bootstat --record_boot_reason\nbootstat: Service started: /system/bin/bootstat --set_system_boot_reason\nbootstat: Service started: /system/bin/bootstat --record_time_since_factory_reset\nbootstat: Service started: /system/bin/bootstat -l\nbootstat: Service started: /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l\nbootstat: Battery level at shutdown 100%\nbootstat: Battery level at startup 100%\ninit    : Parsing file /system/etc/init/bootstat.rc...\ninit    : Parsing file /system/etc/init/bootstat-debug.rc...\ninit    : processing action (persist.test.boot.reason=*) from (/system/etc/init/bootstat-debug.rc:\ninit    : Command 'setprop ro.boot.bootreason \\${persist.test.boot.reason}' action=persist.test.boot.reason=* (/system/etc/init/bootstat-debug.rc:\ninit    : processing action (post-fs-data) from (/system/etc/init/bootstat.rc\ninit    : processing action (boot) from (/system/etc/init/bootstat.rc\ninit    : processing action (ro.boot.bootreason=*) from (/system/etc/init/bootstat.rc\ninit    : processing action (ro.boot.bootreason=* && post-fs) from (/system/etc/init/bootstat.rc\ninit    : processing action (sys.bootstat.first_zygote_start=0 && zygote-start) from (/system/etc/init/bootstat.rc\ninit    : processing action (sys.boot_completed=1 && sys.bootstat.first_boot_completed=0) from (/system/etc/init/bootstat.rc\n (/system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'\n (/system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l)'\ninit    : Command 'exec - system log -- /system/bin/bootstat --record_boot_complete' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:\ninit    : Command 'exec - system log -- /system/bin/bootstat --record_boot_reason' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:\ninit    : Command 'exec - system log -- /system/bin/bootstat --record_time_since_factory_reset' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc:\ninit    : Command 'exec_background - system log -- /system/bin/bootstat --set_system_boot_reason --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l' action=sys.boot_completed=1 && sys.bootstat.first_boot_completed=0 (/system/etc/init/bootstat.rc\n (/system/bin/bootstat --record_boot_complete)'...\n (/system/bin/bootstat --record_boot_complete)' (pid${SPACE}\n (/system/bin/bootstat --record_boot_reason)'...\n (/system/bin/bootstat --record_boot_reason)' (pid${SPACE}\n (/system/bin/bootstat --record_time_since_factory_reset)'...\n (/system/bin/bootstat --record_time_since_factory_reset)' (pid${SPACE}\n (/system/bin/bootstat --set_system_boot_reason)'...\n (/system/bin/bootstat --set_system_boot_reason)' (pid${SPACE}\n (/system/bin/bootstat -l)'...\n (/system/bin/bootstat -l)' (pid \" |\n  grep -v 'bootstat: Unknown boot reason: $' # Hikey Special\n  return ${save_ret}\n}\n\n[ \"USAGE: start_test [message]\n\nRecord start of test, preserve exit status\" ]\nstart_test() {\n  save_ret=${?}\n  duration_prefix=\"~\"\n  duration_estimate=1\n  START=`date +%s`\n  echo \"${GREEN}[ RUN      ]${NORMAL} ${TEST} ${*}\"\n  return ${save_ret}\n}\n\nduration_sum_diff=0\nduration_num=0\n[ \"USAGE: duration_test [[prefix]seconds]\n\nReport the adjusted and expected test duration\" ]\nduration_test() {\n  duration_prefix=${1%%[0123456789]*}\n  if [ -z \"${duration_prefix}\" ]; then\n    duration_prefix=\"~\"\n  fi\n  duration_estimate=\"${1#${duration_prefix}}\"\n  if [ -z \"${duration_estimate}\" ]; then\n    duration_estimate=\"${DURATION_DEFAULT}\"\n  fi\n  duration_new_estimate=\"${duration_estimate}\"\n  if [ 0 -ne ${duration_num} ]; then\n    duration_new_estimate=`expr ${duration_new_estimate} + \\\n      \\( ${duration_num} / 2 + ${duration_sum_diff} \\) / ${duration_num}`\n    # guard against catastrophe\n    if [ -z \"${duration_new_estimate}\" ]; then\n      duration_new_estimate=${duration_estimate}\n    fi\n  fi\n  # negative values are so undignified\n  if [ 0 -ge ${duration_new_estimate} ]; then\n    duration_new_estimate=1\n  fi\n  echo \"INFO: expected duration of '${TEST}' test\" \\\n       \"${duration_prefix}`format_duration ${duration_new_estimate}`\" >&2\n}\n\n[ \"USAGE: end_test [message]\n\nDocument duration and success of test, preserve exit status\" ]\nend_test() {\n  save_ret=${?}\n  END=`date +%s`\n  duration=`expr ${END} - ${START} 2>/dev/null`\n  [ 0 -ge ${duration} ] ||\n    echo \"INFO: '${TEST}' test duration `format_duration ${duration}`\" >&2\n  if [ ${save_ret} = 0 ]; then\n    if [ 0 -lt ${duration} -a 0 -lt ${duration_estimate} -a \\( \\\n           X\"~\" = X\"${duration_prefix}\" -o \\\n           ${duration_estimate} -gt ${duration} \\) ]; then\n      duration_sum_diff=`expr ${duration_sum_diff} + \\\n                              ${duration} - ${duration_estimate}`\n      duration_num=`expr ${duration_num} + 1`\n    fi\n    echo \"${GREEN}[       OK ]${NORMAL} ${TEST} ${*}\"\n  else\n    echo \"${RED}[  FAILED  ]${NORMAL} ${TEST} ${*}\"\n    if ${STOP_ON_FAILURE}; then\n      exit ${save_ret}\n    fi\n  fi\n  return ${save_ret}\n}\n\n[ \"USAGE: wrap_test <test> [message]\n\nAll tests below are wrapped with this helper\" ]\nwrap_test() {\n  if [ -z \"${1}\" -o X\"nothing\" = X\"${1}\" ]; then\n    return\n  fi\n  TEST=${1}\n  shift\n  start_test ${1}\n  eval test_${TEST}\n  end_test ${2}\n}\n\n[ \"USAGE: validate_reason <value>\n\nCheck property for CTS compliance with our expectations. Return a cleansed\nstring representing what is acceptable.\n\nNB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp\" ]\nvalidate_reason() {\n  var=`echo -n ${*} |\n       tr '[A-Z]' '[a-z]' |\n       tr ' \\f\\t\\r\\n' '_____'`\n  case ${var} in\n    watchdog | watchdog,?* ) ;;\n    kernel_panic | kernel_panic,?* ) ;;\n    recovery | recovery,?* ) ;;\n    bootloader | bootloader,?* ) ;;\n    cold | cold,?* ) ;;\n    hard | hard,?* ) ;;\n    warm | warm,?* ) ;;\n    shutdown | shutdown,?* ) ;;\n    reboot,reboot | reboot,reboot,* )     var=${var#reboot,} ; var=${var%,} ;;\n    reboot,cold | reboot,cold,* )         var=${var#reboot,} ; var=${var%,} ;;\n    reboot,hard | reboot,hard,* )         var=${var#reboot,} ; var=${var%,} ;;\n    reboot,warm | reboot,warm,* )         var=${var#reboot,} ; var=${var%,} ;;\n    reboot,recovery | reboot,recovery,* ) var=${var#reboot,} ; var=${var%,} ;;\n    reboot,bootloader | reboot,bootloader,* ) var=${var#reboot,} ; var=${var%,} ;;\n    reboot | reboot,?* ) ;;\n    # Aliases and Heuristics\n    *wdog* | *watchdog* )                                    var=\"watchdog\" ;;\n    *powerkey* | *power_on_key* | *power_key* | *PowerKey* ) var=\"cold,powerkey\" ;;\n    *panic* | *kernel_panic* )                               var=\"kernel_panic\" ;;\n    *thermal* )                                              var=\"shutdown,thermal\" ;;\n    *s3_wakeup* )                                            var=\"warm,s3_wakeup\" ;;\n    *hw_reset* )                                             var=\"hard,hw_reset\" ;;\n    *usb* | *power_on_cable* )                               var=\"cold,charger\" ;;\n    *rtc* )                                                  var=\"cold,rtc\" ;;\n    *2sec_reboot* )                                          var=\"cold,rtc,2sec\" ;;\n    *wdt_by_pass_pwk* )                                      var=\"warm\" ;;\n    wdt )                                                    var=\"reboot\" ;;\n    *tool_by_pass_pwk* )                                     var=\"reboot,tool\" ;;\n    *bootloader* )                                           var=\"bootloader\" ;;\n    * )                                                      var=\"reboot\" ;;\n  esac\n  echo ${var}\n}\n\n[ \"USAGE: validate_property <property>\n\nCheck property for CTS compliance with our expectations. Return a cleansed\nstring representing what is acceptable.\n\nNB: must also roughly match heuristics in system/core/bootstat/bootstat.cpp\" ]\nvalidate_property() {\n  val=`get_property ${1}`\n  ret=`validate_reason \"${val}\"`\n  if [ \"reboot\" = \"${ret}\" ]; then\n    ret=`validate_reason \"reboot,${val}\"`\n  fi\n  echo ${ret}\n}\n\n[ \"USAGE: check_boilerblate_properties\n\nCheck for common property values\" ]\ncheck_boilerplate_properties() {\n  EXPECT_PROPERTY persist.sys.boot.reason \"\"\n  save_ret=${?}\n  reason=`validate_property sys.boot.reason`\n  ( exit ${save_ret} )  # because one can not just do ?=${save_ret}\n  EXPECT_PROPERTY persist.sys.boot.reason.history \"${reason},[1-9][0-9]*\\(\\|[^0-9].*\\)\"\n}\n\n#\n# Actual test frames\n#\n\n[ \"USAGE: test_properties\n\nproperties test\n- (wait until screen is up, boot has completed)\n- adb shell getprop ro.boot.bootreason (bootloader reason)\n- adb shell getprop persist.sys.boot.reason (last reason)\n- adb shell getprop sys.boot.reason.last (last last reason)\n- adb shell getprop sys.boot.reason (system reason)\n- NB: all should have a value that is compliant with our known set.\" ]\ntest_properties() {\n  duration_test 1\n  wait_for_screen\n  retval=0\n  # sys.boot.reason is last for a reason\n  check_set=\"ro.boot.bootreason sys.boot.reason.last sys.boot.reason\"\n  bootloader=\"\"\n  # NB: this test could fail if performed _after_ optional_factory_reset test\n  # and will report\n  #  ERROR: expected \"reboot\" got \"\"\n  #        for Android property sys.boot.reason.last\n  # following is mitigation for the persist.sys.boot.reason, skip it\n  if [ \"reboot,factory_reset\" = \"`validate_property ro.boot_bootreason`\" ]; then\n    check_set=\"ro.boot.bootreason sys.boot.reason\"\n    bootloader=\"bootloader\"\n  fi\n  for prop in ${check_set}; do\n    reason=`validate_property ${prop}`\n    EXPECT_PROPERTY ${prop} ${reason} || retval=${?}\n  done\n  check_boilerplate_properties || retval=${?}\n  report_bootstat_logs ${reason} ${bootloader}\n  return ${retval}\n}\n\n[ \"USAGE: test_ota\n\nota test\n- rm out/.kati_stamp-* out/build_date.txt out/build_number.txt\n- rm out/target/product/*/*/*.prop\n- rm -r out/target/product/*/obj/ETC/system_build_prop_intermediates\n- m\n- NB: ro.build.date.utc should update\n- fastboot flashall\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report ota\n\nDecision to change the build itself rather than trick bootstat by\nrummaging through its data files was made.\" ]\ntest_ota() {\n  duration_test \">300\"\n  echo \"      extended by build and flashing times\" >&2\n  if [ -z \"${TARGET_PRODUCT}\" -o \\\n       -z \"${ANDROID_PRODUCT_OUT}\" -o \\\n       -z \"${ANDROID_BUILD_TOP}\" -o \\\n       -z \"${TARGET_BUILD_VARIANT}\" ]; then\n    echo \"ERROR: Missing envsetup.sh and lunch\" >&2\n    return 1\n  fi\n  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/.kati_stamp-* ||\n    true\n  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_date.txt ||\n    true\n  rm ${ANDROID_PRODUCT_OUT%/out/*}/out/build_number.txt ||\n    true\n  rm ${ANDROID_PRODUCT_OUT}/*/*.prop ||\n    true\n  rm -r ${ANDROID_PRODUCT_OUT}/obj/ETC/system_build_prop_intermediates ||\n    true\n  pushd ${ANDROID_BUILD_TOP} >&2\n  build/soong/soong_ui.bash --make-mode >&2\n  if [ ${?} != 0 ]; then\n    popd >&2\n    return 1\n  fi\n  if ! inFastboot; then\n    adb reboot-bootloader >&2\n  fi\n  fastboot flashall >&2\n  popd >&2\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason \"\\(reboot,ota\\|bootloader\\)\"\n  EXPECT_PROPERTY sys.boot.reason.last bootloader\n  check_boilerplate_properties\n  report_bootstat_logs reboot,ota bootloader\n}\n\n[ \"USAGE: test_optional_ota\n\nfast and fake (touch build_date on device to make it different)\" ]\ntest_optional_ota() {\n  checkDebugBuild || return\n  duration_test\n  adb_su touch /data/misc/bootstat/build_date >&2 </dev/null\n  adb reboot ota\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason reboot,ota\n  EXPECT_PROPERTY sys.boot.reason.last reboot,ota\n  check_boilerplate_properties\n  report_bootstat_logs reboot,ota\n}\n\n[ \"USAGE: [TEST=<test>] blind_reboot_test\n\nSimple tests helper\n- adb reboot <test>\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report <test>, or reboot,<test> depending on canonical rules\n\nWe interleave the simple reboot tests between the hard/complex ones\nas a means of checking sanity and any persistent side effect of the\nother tests.\" ]\nblind_reboot_test() {\n  duration_test\n  case ${TEST} in\n    bootloader | recovery | cold | hard | warm ) reason=${TEST} ;;\n    *)                                           reason=reboot,${TEST#optional_} ;;\n  esac\n  adb reboot ${TEST#optional_}\n  wait_for_screen\n  bootloader_reason=`validate_property ro.boot.bootreason`\n  EXPECT_PROPERTY ro.boot.bootreason ${bootloader_reason}\n  # to make sys.boot.reason report user friendly\n  reasons=${reason}\n  if [ \"${bootloader_reason}\" != \"${reason}\" -a -n \"${bootloader_reason}\" ]; then\n    reasons=\"\\(${reason}\\|${bootloader_reason}\\)\"\n  fi\n  EXPECT_PROPERTY sys.boot.reason ${reasons}\n  EXPECT_PROPERTY sys.boot.reason.last ${reason}\n  check_boilerplate_properties\n  report_bootstat_logs ${reason} ${bootloader_reason}\n}\n\n[ \"USAGE: test_cold\n\ncold test\n- adb reboot cold\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report cold\" ]\ntest_cold() {\n  blind_reboot_test\n}\n\n[ \"USAGE: test_factory_reset\n\nfactory_reset test\n- adb shell su root rm /data/misc/bootstat/build_date\n- adb reboot\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report factory_reset\n\nDecision to rummage through bootstat data files was made as\na _real_ factory_reset is too destructive to the device.\" ]\ntest_factory_reset() {\n  checkDebugBuild || return\n  duration_test\n  adb_su rm /data/misc/bootstat/build_date >&2 </dev/null\n  adb reboot >&2\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason reboot,factory_reset\n  EXPECT_PROPERTY sys.boot.reason.last \"reboot,.*\"\n  check_boilerplate_properties\n  report_bootstat_logs reboot,factory_reset reboot, reboot,adb \\\n    \"-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory\" \\\n    \"-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date\"\n}\n\n[ \"USAGE: test_optional_factory_reset\n\nfactory_reset test\n- adb reboot-bootloader\n- fastboot format userdata\n- fastboot reboot\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report factory_reset\n\nFor realz, and disruptive\" ]\ntest_optional_factory_reset() {\n  duration_test 60\n  if ! inFastboot; then\n    adb reboot-bootloader\n  fi\n  fastboot format userdata >&2\n  save_ret=${?}\n  if [ 0 != ${save_ret} ]; then\n    echo \"ERROR: fastboot can not format userdata\" >&2\n  fi\n  fastboot reboot >&2\n  wait_for_screen\n  ( exit ${save_ret} )  # because one can not just do ?=${save_ret}\n  EXPECT_PROPERTY sys.boot.reason reboot,factory_reset\n  EXPECT_PROPERTY sys.boot.reason.last \"\\(\\|bootloader\\)\"\n  check_boilerplate_properties\n  report_bootstat_logs reboot,factory_reset bootloader \\\n    \"-bootstat: Failed to read /data/misc/bootstat/last_boot_time_utc: No such file or directory\" \\\n    \"-bootstat: Failed to parse boot time record: /data/misc/bootstat/last_boot_time_utc\" \\\n    \"-bootstat: Failed to read /data/misc/bootstat/build_date: No such file or directory\" \\\n    \"-bootstat: Failed to parse boot time record: /data/misc/bootstat/build_date\" \\\n    \"-bootstat: Failed to read /data/misc/bootstat/factory_reset: No such file or directory\" \\\n    \"-bootstat: Failed to parse boot time record: /data/misc/bootstat/factory_reset\"\n}\n\n[ \"USAGE: test_hard\n\nhard test:\n- adb reboot hard\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report hard\" ]\ntest_hard() {\n  blind_reboot_test\n}\n\n[ \"USAGE: test_battery\n\nbattery test (trick):\n- echo healthd: battery l=2<space> | adb shell su root tee /dev/kmsg\n- adb reboot cold\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report reboot,battery, unless healthd managed to log\n  before reboot in above trick.\n\n- Bonus points (manual extras)\n- Make sure the following is added to the /init.rc file in post-fs\n  section before logd is started:\n    +    setprop logd.kernel false\n    +    rm /sys/fs/pstore/console-ramoops\n    +    rm /sys/fs/pstore/console-ramoops-0\n    +    write /dev/kmsg \\\"healthd: battery l=2${SPACE}\n    +\\\"\n- adb reboot fs\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report reboot,battery\n- (replace set logd.kernel true to the above, and retry test)\" ]\ntest_battery() {\n  checkDebugBuild || return\n  duration_test 120\n  enterPstore\n  # Send it _many_ times to combat devices with flakey pstore\n  for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do\n    echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null\n  done\n  adb reboot cold >&2\n  adb wait-for-device\n  wait_for_screen\n  adb_su </dev/null \\\n    cat /proc/fs/pstore/console-ramoops \\\n        /proc/fs/pstore/console-ramoops-0 2>/dev/null |\n    grep 'healthd: battery l=' |\n    tail -1 |\n    grep 'healthd: battery l=2 ' >/dev/null || (\n      if ! EXPECT_PROPERTY sys.boot.reason reboot,battery >/dev/null 2>/dev/null; then\n        # retry\n        for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do\n          echo 'healthd: battery l=2 ' | adb_su tee /dev/kmsg >/dev/null\n        done\n        adb reboot cold >&2\n        adb wait-for-device\n        wait_for_screen\n      fi\n    )\n\n  EXPECT_PROPERTY sys.boot.reason shutdown,battery\n  EXPECT_PROPERTY sys.boot.reason.last cold\n  check_boilerplate_properties\n  report_bootstat_logs shutdown,battery \"-bootstat: Battery level at shutdown 2%\"\n  exitPstore\n}\n\n[ \"USAGE: test_optional_battery\n\nbattery shutdown test:\n- adb shell setprop sys.powerctl shutdown,battery\n- (power up the device)\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report shutdown,battery\" ]\ntest_optional_battery() {\n  duration_test \">60\"\n  echo \"      power on request\" >&2\n  adb_sh setprop sys.powerctl shutdown,battery </dev/null\n  sleep 5\n  echo -n \"WARNING: Please power device back up, waiting ... \" >&2\n  wait_for_screen -n >&2\n  EXPECT_PROPERTY sys.boot.reason shutdown,battery\n  EXPECT_PROPERTY sys.boot.reason.last shutdown,battery\n  check_boilerplate_properties\n  report_bootstat_logs shutdown,battery\n}\n\n[ \"USAGE: test_optional_battery_thermal\n\nbattery thermal shutdown test:\n- adb shell setprop sys.powerctl shutdown,thermal,battery\n- (power up the device)\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report shutdown,thermal,battery\" ]\ntest_optional_battery_thermal() {\n  duration_test \">60\"\n  echo \"      power on request\" >&2\n  adb_sh setprop sys.powerctl shutdown,thermal,battery </dev/null\n  sleep 5\n  echo -n \"WARNING: Please power device back up, waiting ... \" >&2\n  wait_for_screen -n >&2\n  EXPECT_PROPERTY sys.boot.reason shutdown,thermal,battery\n  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal,battery\n  check_boilerplate_properties\n  report_bootstat_logs shutdown,thermal,battery\n}\n\n[ \"USAGE: test_unknown\n\nunknown test\n- adb reboot unknown\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report reboot,unknown\n- NB: expect log \\\"... I bootstat: Unknown boot reason: reboot,unknown\\\"\" ]\ntest_unknown() {\n  blind_reboot_test\n}\n\n[ \"USAGE: test_kernel_panic\n\nkernel_panic test:\n- echo c | adb shell su root tee /proc/sysrq-trigger\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report kernel_panic,sysrq\" ]\ntest_kernel_panic() {\n  checkDebugBuild || return\n  duration_test \">90\"\n  panic_msg=\"kernel_panic,sysrq\"\n  enterPstore\n  if [ ${?} != 0 ]; then\n    echo \"         or functional bootloader\" >&2\n    panic_msg=\"\\(kernel_panic,sysrq\\|kernel_panic\\)\"\n    pstore_ok=true\n  fi\n  echo c | adb_su tee /proc/sysrq-trigger >/dev/null\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason ${panic_msg}\n  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}\n  check_boilerplate_properties\n  report_bootstat_logs kernel_panic,sysrq\n  exitPstore\n}\n\n[ \"USAGE: test_kernel_panic_subreason\n\nkernel_panic_subreason test:\n- echo SysRq : Trigger a crash : 'test' | adb shell su root tee /dev/kmsg\n- echo c | adb shell su root tee /proc/sysrq-trigger\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report kernel_panic,sysrq,test\" ]\ntest_kernel_panic_subreason() {\n  checkDebugBuild || return\n  duration_test \">90\"\n  panic_msg=\"kernel_panic,sysrq,test\"\n  enterPstore\n  if [ ${?} != 0 ]; then\n    echo \"         or functional bootloader\" >&2\n    panic_msg=\"\\(kernel_panic,sysrq,test\\|kernel_panic\\)\"\n    pstore_ok=true\n  fi\n  echo \"SysRq : Trigger a crash : 'test'\" | adb_su tee /dev/kmsg\n  echo c | adb_su tee /proc/sysrq-trigger >/dev/null\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason ${panic_msg}\n  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}\n  check_boilerplate_properties\n  report_bootstat_logs kernel_panic,sysrq,test \\\n    \"-bootstat: Unknown boot reason: kernel_panic,sysrq,test\"\n  exitPstore\n}\n\n[ \"USAGE: test_kernel_panic_hung\n\nkernel_panic_hung test:\n- echo Kernel panic - not synching: hung_task: blocked tasks |\n  adb shell su root tee /dev/kmsg\n- adb reboot warm\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report kernel_panic,hung\" ]\ntest_kernel_panic_hung() {\n  checkDebugBuild || return\n  duration_test\n  panic_msg=\"kernel_panic,hung\"\n  enterPstore\n  if [ ${?} != 0 ]; then\n    echo \"         or functional bootloader\" >&2\n    panic_msg=\"\\(kernel_panic,hung\\|reboot,hung\\)\"\n    pstore_ok=true\n  fi\n  echo \"Kernel panic - not syncing: hung_task: blocked tasks\" |\n    adb_su tee /dev/kmsg\n  adb reboot warm\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason ${panic_msg}\n  EXPECT_PROPERTY sys.boot.reason.last ${panic_msg}\n  check_boilerplate_properties\n  report_bootstat_logs kernel_panic,hung\n  exitPstore\n}\n\n[ \"USAGE: test_warm\n\nwarm test\n- adb reboot warm\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report warm\" ]\ntest_warm() {\n  blind_reboot_test\n}\n\n[ \"USAGE: test_thermal_shutdown\n\nthermal shutdown test:\n- adb shell setprop sys.powerctl shutdown,thermal\n- (power up the device)\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report shutdown,thermal\" ]\ntest_thermal_shutdown() {\n  duration_test \">60\"\n  echo \"      power on request\" >&2\n  adb_sh setprop sys.powerctl shutdown,thermal </dev/null\n  sleep 5\n  echo -n \"WARNING: Please power device back up, waiting ... \" >&2\n  wait_for_screen -n >&2\n  EXPECT_PROPERTY sys.boot.reason shutdown,thermal\n  EXPECT_PROPERTY sys.boot.reason.last shutdown,thermal\n  check_boilerplate_properties\n  report_bootstat_logs shutdown,thermal\n}\n\n[ \"USAGE: test_userrequested_shutdown\n\nuserrequested shutdown test:\n- adb shell setprop sys.powerctl shutdown,userrequested\n- (power up the device)\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report shutdown,userrequested\" ]\ntest_userrequested_shutdown() {\n  duration_test \">60\"\n  echo \"      power on request\" >&2\n  adb_sh setprop sys.powerctl shutdown,userrequested </dev/null\n  sleep 5\n  echo -n \"WARNING: Please power device back up, waiting ... \" >&2\n  wait_for_screen -n >&2\n  EXPECT_PROPERTY sys.boot.reason shutdown,userrequested\n  EXPECT_PROPERTY sys.boot.reason.last shutdown,userrequested\n  check_boilerplate_properties\n  report_bootstat_logs shutdown,userrequested\n}\n\n[ \"USAGE: test_shell_reboot\n\nshell reboot test:\n- adb shell reboot\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report reboot,shell\" ]\ntest_shell_reboot() {\n  duration_test\n  adb_sh reboot </dev/null\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason reboot,shell\n  EXPECT_PROPERTY sys.boot.reason.last reboot,shell\n  check_boilerplate_properties\n  report_bootstat_logs reboot,shell\n}\n\n[ \"USAGE: test_adb_reboot\n\nadb reboot test:\n- adb reboot\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report reboot,adb\" ]\ntest_adb_reboot() {\n  duration_test\n  adb reboot\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason reboot,adb\n  EXPECT_PROPERTY sys.boot.reason.last reboot,adb\n  check_boilerplate_properties\n  report_bootstat_logs reboot,adb\n}\n\n[ \"USAGE: test_rescueparty\n\nrescueparty test\n- adb reboot rescueparty\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- adb shell getprop ro.boot.bootreason\n- NB: should report reboot,rescueparty\" ]\ntest_optional_rescueparty() {\n  blind_reboot_test\n  echo \"WARNING: legacy devices are allowed to fail following ro.boot.bootreason result\" >&2\n  EXPECT_PROPERTY ro.boot.bootreason '\\(reboot\\|reboot,rescueparty\\)'\n}\n\n[ \"USAGE: test_Its_Just_So_Hard_reboot\n\nIts Just So Hard reboot test:\n- adb shell reboot 'Its Just So Hard'\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report reboot,its_just_so_hard\n- NB: expect log \\\"... I bootstat: Unknown boot reason: reboot,its_just_so_hard\\\"\" ]\ntest_Its_Just_So_Hard_reboot() {\n  if isDebuggable; then       # see below\n    duration_test\n  else\n    duration_test `expr ${DURATION_DEFAULT} + ${DURATION_DEFAULT}`\n  fi\n  adb_sh 'reboot \"Its Just So Hard\"' </dev/null\n  wait_for_screen\n  EXPECT_PROPERTY sys.boot.reason reboot,its_just_so_hard\n  EXPECT_PROPERTY sys.boot.reason.last reboot,its_just_so_hard\n  check_boilerplate_properties\n  report_bootstat_logs reboot,its_just_so_hard\n}\n\n[ \"USAGE: run_bootloader [value [expected]]\n\nbootloader boot reason injection tests:\n- setBootloaderBootReason value\n- adb shell reboot\n- (wait until screen is up, boot has completed)\n- adb shell getprop sys.boot.reason\n- NB: should report reboot,value\" ]\nrun_bootloader() {\n  bootloader_expected=\"${1}\"\n  if [ -z \"${bootloader_expected}\" ]; then\n    bootloader_expected=\"${TEST#bootloader_}\"\n  fi\n  if ! setBootloaderBootReason ${bootloader_expected}; then\n    echo \"       Skipping FAILURE.\" 2>&1\n    return\n  fi\n  duration_test\n  if [ X\"warm\" = X\"${bootloader_expected}\" ]; then\n    last_expected=cold\n  else\n    last_expected=warm\n  fi\n  adb reboot ${last_expected}\n  wait_for_screen\n  # Reset so that other tests do not get unexpected injection\n  setBootloaderBootReason\n  # Determine the expected values\n  sys_expected=\"${2}\"\n  if [ -z \"${sys_expected}\" ]; then\n    sys_expected=\"`validate_reason ${bootloader_expected}`\"\n    if [ \"reboot\" = \"${sys_expected}\" ]; then\n      sys_expected=\"${last_expected}\"\n    fi\n  else\n    sys_expected=`validate_reason ${sys_expected}`\n  fi\n  case ${sys_expected} in\n    kernel_panic | kernel_panic,* | watchdog | watchdog,* )\n      last_expected=${sys_expected}\n      ;;\n  esac\n  # Check values\n  EXPECT_PROPERTY ro.boot.bootreason \"${bootloader_expected}\"\n  EXPECT_PROPERTY sys.boot.reason \"${sys_expected}\"\n  EXPECT_PROPERTY sys.boot.reason.last \"${last_expected}\"\n  check_boilerplate_properties\n  report_bootstat_logs \"${sys_expected}\"\n}\n\n[ \"USAGE: test_bootloader_<type>\n\nbootloader boot reasons test injection\" ]\ntest_bootloader_normal() {\n  run_bootloader\n}\n\ntest_bootloader_watchdog() {\n  run_bootloader\n}\n\ntest_bootloader_kernel_panic() {\n  run_bootloader\n}\n\ntest_bootloader_oem_powerkey() {\n  run_bootloader\n}\n\ntest_bootloader_wdog_reset() {\n  run_bootloader\n}\n\ntest_bootloader_cold() {\n  run_bootloader\n}\n\ntest_bootloader_warm() {\n  run_bootloader\n}\n\ntest_bootloader_hard() {\n  run_bootloader\n}\n\ntest_bootloader_recovery() {\n  run_bootloader\n}\n\n[ \"USAGE: run_kBootReasonMap [--boot_reason_enum] value expected\n\nbootloader boot reason injection tests:\n- if --boot_reason_enum run bootstat executable for result instead.\n- inject boot reason into sys.boot.reason\n- run bootstat --set_system_boot_reason\n- check for expected enum\n- \" ]\nrun_kBootReasonMap() {\n  if [ X\"--boot_reason_enum\" = X\"${1}\" ]; then\n    shift\n    local sys_expected=\"${1}\"\n    shift\n    local enum_expected=\"${1}\"\n    adb_su bootstat --boot_reason_enum=\"${sys_expected}\" |\n      (\n        local retval=-1\n        while read -r id match; do\n          if [ ${retval} = -1 -a ${enum_expected} = ${id} ]; then\n            retval=0\n          fi\n          if [ ${enum_expected} != ${id} ]; then\n            echo \"ERROR: ${enum_expected} ${sys_expected} got ${id} ${match}\" >&2\n            retval=1\n          fi\n        done\n        exit ${retval}\n      )\n    return\n  fi\n  local sys_expected=\"${1}\"\n  shift\n  local enum_expected=\"${1}\"\n  adb_su setprop sys.boot.reason \"${sys_expected}\" </dev/null\n  adb_su bootstat --record_boot_reason </dev/null\n  # Check values\n  EXPECT_PROPERTY sys.boot.reason \"${sys_expected}\"\n  local retval=${?}\n  local result=`adb_su stat -c %Y /data/misc/bootstat/system_boot_reason </dev/null 2>/dev/null`\n  [ \"${enum_expected}\" = \"${result}\" ] ||\n    (\n      [ -n \"${result}\" ] || result=\"<nothing>\"\n      echo \"ERROR: ${enum_expected} ${sys_expected} got ${result}\" >&2\n      false\n    ) ||\n    retval=${?}\n  return ${retval}\n}\n\n[ \"USAGE: filter_kBootReasonMap </dev/stdin >/dev/stdout\n\nconvert any regex expressions into a series of non-regex test strings\" ]\nfilter_kBootReasonMap() {\n  while read -r id match; do\n    case ${match} in\n      'reboot,[empty]')\n        echo ${id}          # matches b/c of special case\n        echo ${id} reboot,y # matches b/c of regex\n        echo 1 reboot,empty # negative test (ID for unknown is 1)\n        ;;\n      reboot)\n        echo 1 reboog       # negative test (ID for unknown is 1)\n        ;;\n      'reboot,pmic_off_fault,.*')\n        echo ${id} reboot,pmic_off_fault,hello,world\n        echo ${id} reboot,pmic_off_fault,\n        echo 1 reboot,pmic_off_fault\n        ;;\n    esac\n    echo ${id} \"${match}\"   # matches b/c of exact\n  done\n}\n\n[ \"USAGE: test_kBootReasonMap\n\nkBootReasonMap test\n- (wait until screen is up, boot has completed)\n- read bootstat for kBootReasonMap entries and test them all\" ]\ntest_kBootReasonMap() {\n  checkDebugBuild || return\n  duration_test 15\n  local tempfile=\"`mktemp`\"\n  local arg=--boot_reason_enum\n  adb_su bootstat ${arg} </dev/null 2>/dev/null |\n    filter_kBootReasonMap >${tempfile}\n  if [ ! -s \"${tempfile}\" ]; then\n    wait_for_screen\n    arg=\n    sed -n <${progpath}bootstat.cpp \\\n      '/kBootReasonMap = {/,/^};/s/.*{\"\\([^\"]*\\)\", *\\([0-9][0-9]*\\)},.*/\\2 \\1/p' |\n      sed 's/\\\\\\\\/\\\\/g' |\n      filter_kBootReasonMap >${tempfile}\n  fi\n  T=`adb_date`\n  retval=0\n  while read -r enum string; do\n    if [ X\"${string}\" != X\"${string#*[[].[]]}\" -o X\"${string}\" != X\"${string#*\\\\.}\" ]; then\n      if [ 'reboot\\.empty' != \"${string}\" ]; then\n        echo \"WARNING: regex snuck through filter_kBootReasonMap ${enum} ${string}\" >&2\n        enum=1\n      fi\n    fi\n    run_kBootReasonMap ${arg} \"${string}\" \"${enum}\" </dev/null || retval=${?}\n  done <${tempfile}\n  rm ${tempfile}\n  ( exit ${retval} )\n  # See filter_kBootReasonMap() for negative tests and add them here too\n  report_bootstat_logs -t${T} \\\n    '-bootstat: Service started: bootstat --boot_reason_enum=' \\\n    '-bootstat: Unknown boot reason: reboot,empty' \\\n    '-bootstat: Unknown boot reason: reboog' \\\n    '-bootstat: Unknown boot reason: reboot,pmic_off_fault'\n}\n\n[ \"USAGE: ${progname} [-s SERIAL] [tests]...\n\nMainline executive to run the above tests\" ]\n\n# Rudimentary argument parsing\n\nif [ ${#} -ge 2 -a X\"-s\" = X\"${1}\" ]; then\n  export ANDROID_SERIAL=\"${2}\"\n  shift 2\nfi\n\n# Helpful for debugging, allows us to import the functions.\nif [ X\"--macros\" != X\"${1}\" ]; then\n\n  if [ X\"--help\" = X\"${1}\" -o X\"-h\" = X\"${1}\" -o X\"-?\" = X\"${1}\" ]; then\n    echo \"USAGE: ${progname} [-s SERIAL] [tests]...\"\n    echo tests - `sed -n 's/^test_\\([^ ()]*\\)() {/\\1/p' $0 </dev/null`\n    exit 0\n  fi\n\n  if [ X\"--stop\" = X\"${1}\" ]; then\n    STOP_ON_FAILURE=true\n    shift\n  fi\n\n  # Check if all conditions for the script are valid\n\n  if [ -z \"${ANDROID_SERIAL}\" ]; then\n    ndev=`(\n        adb devices | grep -v 'List of devices attached'\n        fastboot devices\n      ) |\n      grep -v \"^[${SPACE}${TAB}]*\\$\" |\n      wc -l`\n    if [ ${ndev} -gt 1 ]; then\n      echo \"ERROR: no target device specified, ${ndev} connected\" >&2\n      echo \"${RED}[  FAILED  ]${NORMAL}\"\n      exit 1\n    fi\n    echo \"WARNING: no target device specified\" >&2\n  fi\n\n  ret=0\n\n  # Test Series\n  if [ X\"all\" = X\"${*}\" ]; then\n    # automagically pick up all test_<function>s.\n    eval set nothing `sed -n 's/^test_\\([^ ()]*\\)() {/\\1/p' $0 </dev/null`\n    if [ X\"nothing\" = X\"${1}\" ]; then\n      shift 1\n    fi\n  fi\n  if [ -z \"$*\" ]; then\n    # automagically pick up all test_<function>, except test_optional_<function>.\n    eval set nothing `sed -n 's/^test_\\([^ ()]*\\)() {/\\1/p' $0 </dev/null |\n                              grep -v '^optional_'`\n    if [ -z \"${2}\" ]; then\n      # Hard coded should shell fail to find them (search/permission issues)\n      eval set properties ota cold factory_reset hard battery unknown \\\n               kernel_panic kernel_panic_subreason kernel_panic_hung warm \\\n               thermal_shutdown userrequested_shutdown shell_reboot adb_reboot \\\n               Its_Just_So_Hard_reboot bootloader_normal bootloader_watchdog \\\n               bootloader_kernel_panic bootloader_oem_powerkey \\\n               bootloader_wdog_reset bootloader_cold bootloader_warm \\\n               bootloader_hard bootloader_recovery kBootReasonMap\n    fi\n    if [ X\"nothing\" = X\"${1}\" ]; then\n      shift 1\n    fi\n  fi\n  echo \"INFO: selected test(s): ${@}\" >&2\n  echo\n  # Prepare device\n  setBootloaderBootReason 2>/dev/null\n  # Start pouring through the tests.\n  failures=\n  successes=\n  for t in \"${@}\"; do\n    wrap_test ${t}\n    retval=${?}\n    if [ 0 = ${retval} ]; then\n      if [ -z \"${successes}\" ]; then\n        successes=${t}\n      else\n        successes=\"${successes} ${t}\"\n      fi\n    else\n      ret=${retval}\n      if [ -z \"${failures}\" ]; then\n        failures=${t}\n      else\n        failures=\"${failures} ${t}\"\n      fi\n    fi\n    echo\n  done\n\n  if [ -n \"${successes}\" ]; then\n    echo \"${GREEN}[  PASSED  ]${NORMAL} ${successes}\"\n  fi\n  if [ -n \"${failures}\" ]; then\n    echo \"${RED}[  FAILED  ]${NORMAL} ${failures}\"\n  fi\n  exit ${ret}\n\nfi\n"
  },
  {
    "path": "bootstat/bootstat-debug.rc",
    "content": "# This file is the userdebug LOCAL_INIT_RC file for the bootstat command.\n\n# FOR TESTING\n# For devices w/o bootloader boot reason reported, mirror test boot reason\n# to bootloader boot reason to allow test to inject reasons\non property:persist.test.boot.reason=*\n    setprop ro.boot.bootreason ${persist.test.boot.reason}\n"
  },
  {
    "path": "bootstat/bootstat.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// The bootstat command provides options to persist boot events with the current\n// timestamp, dump the persisted events, and log all events to EventLog to be\n// uploaded to Android log storage via Tron.\n\n#include <getopt.h>\n#include <sys/klog.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <cmath>\n#include <cstddef>\n#include <cstdio>\n#include <ctime>\n#include <iterator>\n#include <map>\n#include <memory>\n#include <regex>\n#include <string>\n#include <string_view>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android/log.h>\n#include <cutils/android_reboot.h>\n#include <cutils/properties.h>\n#include <statslog.h>\n\n#include \"boot_event_record_store.h\"\n\nnamespace {\n\nstruct AtomInfo {\n  int32_t atom;\n  int32_t event;\n};\n\n// Maps BootEvent used inside bootstat into statsd atom defined in\n// frameworks/proto_logging/stats/atoms.proto.\nconst std::unordered_map<std::string_view, AtomInfo> kBootEventToAtomInfo = {\n    // ELAPSED_TIME\n    {\"ro.boottime.init\",\n     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ANDROID_INIT_STAGE_1}},\n    {\"boot_complete\",\n     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE}},\n    {\"boot_complete_no_encryption\",\n     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__BOOT_COMPLETE_NO_ENCRYPTION}},\n    {\"factory_reset_boot_complete\",\n     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE}},\n    {\"factory_reset_boot_complete_no_encryption\",\n     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,\n      android::util::\n          BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FACTORY_RESET_BOOT_COMPLETE_NO_ENCRYPTION}},\n    {\"ota_boot_complete\",\n     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE}},\n    {\"ota_boot_complete_no_encryption\",\n     {android::util::BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__OTA_BOOT_COMPLETE_NO_ENCRYPTION}},\n    // DURATION\n    {\"absolute_boot_time\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__ABSOLUTE_BOOT_TIME}},\n    {\"boottime.bootloader.1BLE\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_FIRST_STAGE_EXEC}},\n    {\"boottime.bootloader.1BLL\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_FIRST_STAGE_LOAD}},\n    {\"boottime.bootloader.KL\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_KERNEL_LOAD}},\n    {\"boottime.bootloader.2BLE\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_SECOND_STAGE_EXEC}},\n    {\"boottime.bootloader.2BLL\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_SECOND_STAGE_LOAD}},\n    {\"boottime.bootloader.SW\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_UI_WAIT}},\n    {\"boottime.bootloader.total\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__BOOTLOADER_TOTAL}},\n    {\"boottime.init.cold_boot_wait\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__COLDBOOT_WAIT}},\n    {\"time_since_factory_reset\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__FACTORY_RESET_TIME_SINCE_RESET}},\n    {\"ro.boottime.init.first_stage\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__ANDROID_INIT_STAGE_1}},\n    {\"ro.boottime.init.selinux\",\n     {android::util::BOOT_TIME_EVENT_DURATION_REPORTED,\n      android::util::BOOT_TIME_EVENT_DURATION__EVENT__SELINUX_INIT}},\n    // UTC_TIME\n    {\"factory_reset\",\n     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RESET_TIME}},\n    {\"factory_reset_current_time\",\n     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_CURRENT_TIME}},\n    {\"factory_reset_record_value\",\n     {android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED,\n      android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RECORD_VALUE}},\n    // ERROR_CODE\n    {\"factory_reset_current_time_failure\",\n     {android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED,\n      android::util::BOOT_TIME_EVENT_ERROR_CODE__EVENT__FACTORY_RESET_CURRENT_TIME_FAILURE}},\n};\n\n// Scans the boot event record store for record files and logs each boot event\n// via EventLog.\nvoid LogBootEvents() {\n  BootEventRecordStore boot_event_store;\n  auto events = boot_event_store.GetAllBootEvents();\n  std::vector<std::string_view> notSupportedEvents;\n  for (const auto& event : events) {\n    const auto& name = event.first;\n    const auto& info = kBootEventToAtomInfo.find(name);\n    if (info != kBootEventToAtomInfo.end()) {\n      if (info->second.atom == android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED) {\n        android::util::stats_write(static_cast<int32_t>(info->second.atom),\n                                   static_cast<int32_t>(info->second.event),\n                                   static_cast<int32_t>(event.second));\n      } else {\n        android::util::stats_write(static_cast<int32_t>(info->second.atom),\n                                   static_cast<int32_t>(info->second.event),\n                                   static_cast<int64_t>(event.second));\n      }\n    } else {\n      notSupportedEvents.push_back(name);\n    }\n  }\n  if (!notSupportedEvents.empty()) {\n    LOG(WARNING) << \"LogBootEvents, atomInfo not defined for events:\"\n                 << android::base::Join(notSupportedEvents, ',');\n  }\n}\n\n// Records the named boot |event| to the record store. If |value| is non-empty\n// and is a proper string representation of an integer value, the converted\n// integer value is associated with the boot event.\nvoid RecordBootEventFromCommandLine(const std::string& event, const std::string& value_str) {\n  BootEventRecordStore boot_event_store;\n  if (!value_str.empty()) {\n    int32_t value = 0;\n    if (android::base::ParseInt(value_str, &value)) {\n      boot_event_store.AddBootEventWithValue(event, value);\n    }\n  } else {\n    boot_event_store.AddBootEvent(event);\n  }\n}\n\nvoid PrintBootEvents() {\n  printf(\"Boot events:\\n\");\n  printf(\"------------\\n\");\n\n  BootEventRecordStore boot_event_store;\n  auto events = boot_event_store.GetAllBootEvents();\n  for (auto i = events.cbegin(); i != events.cend(); ++i) {\n    printf(\"%s\\t%d\\n\", i->first.c_str(), i->second);\n  }\n}\n\nvoid ShowHelp(const char* cmd) {\n  fprintf(stderr, \"Usage: %s [options]...\\n\", cmd);\n  fprintf(stderr,\n          \"options include:\\n\"\n          \"  -h, --help              Show this help\\n\"\n          \"  -l, --log               Log all metrics to logstorage\\n\"\n          \"  -p, --print             Dump the boot event records to the console\\n\"\n          \"  -r, --record            Record the timestamp of a named boot event\\n\"\n          \"  --value                 Optional value to associate with the boot event\\n\"\n          \"  --record_boot_complete  Record metrics related to the time for the device boot\\n\"\n          \"  --record_boot_reason    Record the reason why the device booted\\n\"\n          \"  --record_time_since_factory_reset  Record the time since the device was reset\\n\"\n          \"  --boot_reason_enum=<reason>  Report the match to the kBootReasonMap table\\n\");\n}\n\n// Constructs a readable, printable string from the givencommand line\n// arguments.\nstd::string GetCommandLine(int argc, char** argv) {\n  std::string cmd;\n  for (int i = 0; i < argc; ++i) {\n    cmd += argv[i];\n    cmd += \" \";\n  }\n\n  return cmd;\n}\n\nconstexpr int32_t kEmptyBootReason = 0;\nconstexpr int32_t kUnknownBootReason = 1;\n\n// A mapping from boot reason string, as read from the ro.boot.bootreason\n// system property, to a unique integer ID. Viewers of log data dashboards for\n// the boot_reason metric may refer to this mapping to discern the histogram\n// values.  Regex matching, to manage the scale, as a minimum require either\n// [, \\ or * to be present in the string to switch to checking.\nconst std::map<std::string, int32_t> kBootReasonMap = {\n    {\"reboot,[empty]\", kEmptyBootReason},\n    {\"__BOOTSTAT_UNKNOWN__\", kUnknownBootReason},\n    {\"normal\", 2},\n    {\"recovery\", 3},\n    {\"reboot\", 4},\n    {\"PowerKey\", 5},\n    {\"hard_reset\", 6},\n    {\"kernel_panic\", 7},\n    {\"rpm_err\", 8},\n    {\"hw_reset\", 9},\n    {\"tz_err\", 10},\n    {\"adsp_err\", 11},\n    {\"modem_err\", 12},\n    {\"mba_err\", 13},\n    {\"Watchdog\", 14},\n    {\"Panic\", 15},\n    {\"power_key\", 16},  // aliasReasons to cold,powerkey (Mediatek)\n    {\"power_on\", 17},   // aliasReasons to cold,powerkey\n    {\"Reboot\", 18},\n    {\"rtc\", 19},\n    {\"edl\", 20},\n    {\"oem_pon1\", 21},\n    {\"oem_powerkey\", 22},  // aliasReasons to cold,powerkey\n    {\"oem_unknown_reset\", 23},\n    {\"srto: HWWDT reset SC\", 24},\n    {\"srto: HWWDT reset platform\", 25},\n    {\"srto: bootloader\", 26},\n    {\"srto: kernel panic\", 27},\n    {\"srto: kernel watchdog reset\", 28},\n    {\"srto: normal\", 29},\n    {\"srto: reboot\", 30},\n    {\"srto: reboot-bootloader\", 31},\n    {\"srto: security watchdog reset\", 32},\n    {\"srto: wakesrc\", 33},\n    {\"srto: watchdog\", 34},\n    {\"srto:1-1\", 35},\n    {\"srto:omap_hsmm\", 36},\n    {\"srto:phy0\", 37},\n    {\"srto:rtc0\", 38},\n    {\"srto:touchpad\", 39},\n    {\"watchdog\", 40},\n    {\"watchdogr\", 41},\n    {\"wdog_bark\", 42},\n    {\"wdog_bite\", 43},\n    {\"wdog_reset\", 44},\n    {\"shutdown,\", 45},  // Trailing comma is intentional. Do NOT use.\n    {\"shutdown,userrequested\", 46},\n    {\"reboot,bootloader\", 47},\n    {\"reboot,cold\", 48},\n    {\"reboot,recovery\", 49},\n    {\"thermal_shutdown\", 50},\n    {\"s3_wakeup\", 51},\n    {\"kernel_panic,sysrq\", 52},\n    {\"kernel_panic,NULL\", 53},\n    {\"kernel_panic,null\", 53},\n    {\"kernel_panic,BUG\", 54},\n    {\"kernel_panic,bug\", 54},\n    {\"bootloader\", 55},\n    {\"cold\", 56},\n    {\"hard\", 57},\n    {\"warm\", 58},\n    {\"reboot,kernel_power_off_charging__reboot_system\", 59},  // Can not happen\n    {\"thermal-shutdown\", 60},\n    {\"shutdown,thermal\", 61},\n    {\"shutdown,battery\", 62},\n    {\"reboot,ota\", 63},\n    {\"reboot,factory_reset\", 64},\n    {\"reboot,\", 65},\n    {\"reboot,shell\", 66},\n    {\"reboot,adb\", 67},\n    {\"reboot,userrequested\", 68},\n    {\"shutdown,container\", 69},  // Host OS asking Android Container to shutdown\n    {\"cold,powerkey\", 70},\n    {\"warm,s3_wakeup\", 71},\n    {\"hard,hw_reset\", 72},\n    {\"shutdown,suspend\", 73},    // Suspend to RAM\n    {\"shutdown,hibernate\", 74},  // Suspend to DISK\n    {\"power_on_key\", 75},        // aliasReasons to cold,powerkey\n    {\"reboot_by_key\", 76},       // translated to reboot,by_key\n    {\"wdt_by_pass_pwk\", 77},     // Mediatek\n    {\"reboot_longkey\", 78},      // translated to reboot,longkey\n    {\"powerkey\", 79},            // aliasReasons to cold,powerkey\n    {\"usb\", 80},                 // aliasReasons to cold,charger (Mediatek)\n    {\"wdt\", 81},                 // Mediatek\n    {\"tool_by_pass_pwk\", 82},    // aliasReasons to reboot,tool (Mediatek)\n    {\"2sec_reboot\", 83},         // aliasReasons to cold,rtc,2sec (Mediatek)\n    {\"reboot,by_key\", 84},\n    {\"reboot,longkey\", 85},\n    {\"reboot,2sec\", 86},  // Deprecate in two years, replaced with cold,rtc,2sec\n    {\"shutdown,thermal,battery\", 87},\n    {\"reboot,its_just_so_hard\", 88},  // produced by boot_reason_test\n    {\"reboot,Its Just So Hard\", 89},  // produced by boot_reason_test\n    {\"reboot,rescueparty\", 90},\n    {\"charge\", 91},\n    {\"oem_tz_crash\", 92},\n    {\"uvlo\", 93},  // aliasReasons to reboot,undervoltage\n    {\"oem_ps_hold\", 94},\n    {\"abnormal_reset\", 95},\n    {\"oemerr_unknown\", 96},\n    {\"reboot_fastboot_mode\", 97},\n    {\"watchdog_apps_bite\", 98},\n    {\"xpu_err\", 99},\n    {\"power_on_usb\", 100},  // aliasReasons to cold,charger\n    {\"watchdog_rpm\", 101},\n    {\"watchdog_nonsec\", 102},\n    {\"watchdog_apps_bark\", 103},\n    {\"reboot_dmverity_corrupted\", 104},\n    {\"reboot_smpl\", 105},  // aliasReasons to reboot,powerloss\n    {\"watchdog_sdi_apps_reset\", 106},\n    {\"smpl\", 107},  // aliasReasons to reboot,powerloss\n    {\"oem_modem_failed_to_powerup\", 108},\n    {\"reboot_normal\", 109},\n    {\"oem_lpass_cfg\", 110},\n    {\"oem_xpu_ns_error\", 111},\n    {\"power_key_press\", 112},  // aliasReasons to cold,powerkey\n    {\"hardware_reset\", 113},\n    {\"reboot_by_powerkey\", 114},  // aliasReasons to cold,powerkey (is this correct?)\n    {\"reboot_verity\", 115},\n    {\"oem_rpm_undef_error\", 116},\n    {\"oem_crash_on_the_lk\", 117},\n    {\"oem_rpm_reset\", 118},\n    {\"reboot,powerloss\", 119},\n    {\"reboot,undervoltage\", 120},\n    {\"factory_cable\", 121},\n    {\"oem_ar6320_failed_to_powerup\", 122},\n    {\"watchdog_rpm_bite\", 123},\n    {\"power_on_cable\", 124},  // aliasReasons to cold,charger\n    {\"reboot_unknown\", 125},\n    {\"wireless_charger\", 126},\n    {\"0x776655ff\", 127},\n    {\"oem_thermal_bite_reset\", 128},\n    {\"charger\", 129},\n    {\"pon1\", 130},\n    {\"unknown\", 131},\n    {\"reboot_rtc\", 132},\n    {\"cold_boot\", 133},\n    {\"hard_rst\", 134},\n    {\"power-on\", 135},\n    {\"oem_adsp_resetting_the_soc\", 136},\n    {\"kpdpwr\", 137},\n    {\"oem_modem_timeout_waiting\", 138},\n    {\"usb_chg\", 139},\n    {\"warm_reset_0x02\", 140},\n    {\"warm_reset_0x80\", 141},\n    {\"pon_reason_0xb0\", 142},\n    {\"reboot_download\", 143},\n    {\"reboot_recovery_mode\", 144},\n    {\"oem_sdi_err_fatal\", 145},\n    {\"pmic_watchdog\", 146},\n    {\"software_master\", 147},\n    {\"cold,charger\", 148},\n    {\"cold,rtc\", 149},\n    {\"cold,rtc,2sec\", 150},   // Mediatek\n    {\"reboot,tool\", 151},     // Mediatek\n    {\"reboot,wdt\", 152},      // Mediatek\n    {\"reboot,unknown\", 153},  // Mediatek\n    {\"kernel_panic,audit\", 154},\n    {\"kernel_panic,atomic\", 155},\n    {\"kernel_panic,hung\", 156},\n    {\"kernel_panic,hung,rcu\", 157},\n    {\"kernel_panic,init\", 158},\n    {\"kernel_panic,oom\", 159},\n    {\"kernel_panic,stack\", 160},\n    {\"kernel_panic,sysrq,livelock,alarm\", 161},   // llkd\n    {\"kernel_panic,sysrq,livelock,driver\", 162},  // llkd\n    {\"kernel_panic,sysrq,livelock,zombie\", 163},  // llkd\n    {\"kernel_panic,modem\", 164},\n    {\"kernel_panic,adsp\", 165},\n    {\"kernel_panic,dsps\", 166},\n    {\"kernel_panic,wcnss\", 167},\n    {\"kernel_panic,_sde_encoder_phys_cmd_handle_ppdone_timeout\", 168},\n    {\"recovery,quiescent\", 169},\n    {\"reboot,quiescent\", 170},\n    {\"reboot,rtc\", 171},\n    {\"reboot,dm-verity_device_corrupted\", 172},\n    {\"reboot,dm-verity_enforcing\", 173},\n    {\"reboot,keys_clear\", 174},\n    {\"reboot,pmic_off_fault,.*\", 175},\n    {\"reboot,pmic_off_s3rst,.*\", 176},\n    {\"reboot,pmic_off_other,.*\", 177},\n    {\"reboot,userrequested,fastboot\", 178},\n    {\"reboot,userrequested,recovery\", 179},\n    {\"reboot,userrequested,recovery,ui\", 180},\n    {\"shutdown,userrequested,fastboot\", 181},\n    {\"shutdown,userrequested,recovery\", 182},\n    {\"reboot,unknown[0-9]*\", 183},\n    {\"reboot,longkey,.*\", 184},\n    {\"reboot,boringssl-self-check-failed\", 185},\n    {\"reboot,userspace_failed,shutdown_aborted\", 186},\n    {\"reboot,userspace_failed,watchdog_triggered\", 187},\n    {\"reboot,userspace_failed,watchdog_fork\", 188},\n    {\"reboot,userspace_failed,*\", 189},\n    {\"reboot,mount_userdata_failed\", 190},\n    {\"reboot,forcedsilent\", 191},\n    {\"reboot,forcednonsilent\", 192},\n    {\"reboot,thermal,tj\", 193},\n    {\"reboot,emergency\", 194},\n    {\"reboot,factory\", 195},\n    {\"reboot,fastboot\", 196},\n    {\"reboot,gsa,hard\", 197},\n    {\"reboot,gsa,soft\", 198},\n    {\"reboot,master_dc,fault_n\", 199},\n    {\"reboot,master_dc,reset\", 200},\n    {\"reboot,ocp\", 201},\n    {\"reboot,pin\", 202},\n    {\"reboot,rom_recovery\", 203},\n    {\"reboot,uvlo\", 204},\n    {\"reboot,uvlo,pmic,if\", 205},\n    {\"reboot,uvlo,pmic,main\", 206},\n    {\"reboot,uvlo,pmic,sub\", 207},\n    {\"reboot,warm\", 208},\n    {\"watchdog,aoc\", 209},\n    {\"watchdog,apc\", 210},\n    {\"watchdog,apc,bl,debug,early\", 211},\n    {\"watchdog,apc,bl,early\", 212},\n    {\"watchdog,apc,early\", 213},\n    {\"watchdog,apm\", 214},\n    {\"watchdog,gsa,hard\", 215},\n    {\"watchdog,gsa,soft\", 216},\n    {\"watchdog,pmucal\", 217},\n    {\"reboot,early,bl\", 218},\n    {\"watchdog,apc,gsa,crashed\", 219},\n    {\"watchdog,apc,bl31,crashed\", 220},\n    {\"watchdog,apc,pbl,crashed\", 221},\n    {\"reboot,memory_protect,hyp\", 222},\n    {\"reboot,tsd,pmic,main\", 223},\n    {\"reboot,tsd,pmic,sub\", 224},\n    {\"reboot,ocp,pmic,main\", 225},\n    {\"reboot,ocp,pmic,sub\", 226},\n    {\"reboot,sys_ldo_ok,pmic,main\", 227},\n    {\"reboot,sys_ldo_ok,pmic,sub\", 228},\n    {\"reboot,smpl_timeout,pmic,main\", 229},\n    {\"reboot,ota,.*\", 230},\n    {\"reboot,periodic,.*\", 231},\n    {\"reboot,early,abl\", 232},\n    {\"reboot,early,bl2\", 233},\n    {\"reboot,longkey,pmic_cold\", 234},\n    {\"reboot,longkey,master_dc\", 235},\n    {\"reboot,ocp2,pmic,if\", 236},\n    {\"reboot,ocp,pmic,if\", 237},\n    {\"reboot,fship.*\", 238},\n    {\"reboot,ocp,.*\", 239},\n    {\"reboot,ntc,pmic,sub\", 240},\n};\n\n// Converts a string value representing the reason the system booted to an\n// integer representation. This is necessary for logging the boot_reason metric\n// via Tron, which does not accept non-integer buckets in histograms.\nint32_t BootReasonStrToEnum(const std::string& boot_reason) {\n  auto mapping = kBootReasonMap.find(boot_reason);\n  if (mapping != kBootReasonMap.end()) {\n    return mapping->second;\n  }\n\n  if (boot_reason.empty()) {\n    return kEmptyBootReason;\n  }\n\n  for (const auto& [match, id] : kBootReasonMap) {\n    // Regex matches as a minimum require either [, \\ or * to be present.\n    if (match.find_first_of(\"[\\\\*\") == match.npos) continue;\n    // enforce match from beginning to end\n    auto exact = match;\n    if (exact[0] != '^') exact = \"^\" + exact;\n    if (exact[exact.size() - 1] != '$') exact = exact + \"$\";\n    if (std::regex_search(boot_reason, std::regex(exact))) return id;\n  }\n\n  LOG(INFO) << \"Unknown boot reason: \" << boot_reason;\n  return kUnknownBootReason;\n}\n\n// Canonical list of supported primary reboot reasons.\nconst std::vector<std::string> knownReasons = {\n    // clang-format off\n    // kernel\n    \"watchdog\",\n    \"kernel_panic\",\n    // strong\n    \"recovery\",    // Should not happen from ro.boot.bootreason\n    \"bootloader\",  // Should not happen from ro.boot.bootreason\n    // blunt\n    \"cold\",\n    \"hard\",\n    \"warm\",\n    // super blunt\n    \"shutdown\",    // Can not happen from ro.boot.bootreason\n    \"reboot\",      // Default catch-all for anything unknown\n    // clang-format on\n};\n\n// Returns true if the supplied reason prefix is considered detailed enough.\nbool isStrongRebootReason(const std::string& r) {\n  for (auto& s : knownReasons) {\n    if (s == \"cold\") break;\n    // Prefix defined as terminated by a nul or comma (,).\n    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {\n      return true;\n    }\n  }\n  return false;\n}\n\n// Returns true if the supplied reason prefix is associated with the kernel.\nbool isKernelRebootReason(const std::string& r) {\n  for (auto& s : knownReasons) {\n    if (s == \"recovery\") break;\n    // Prefix defined as terminated by a nul or comma (,).\n    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {\n      return true;\n    }\n  }\n  return false;\n}\n\n// Returns true if the supplied reason prefix is considered known.\nbool isKnownRebootReason(const std::string& r) {\n  for (auto& s : knownReasons) {\n    // Prefix defined as terminated by a nul or comma (,).\n    if (android::base::StartsWith(r, s) && ((r.length() == s.length()) || (r[s.length()] == ','))) {\n      return true;\n    }\n  }\n  return false;\n}\n\n// If the reboot reason should be improved, report true if is too blunt.\nbool isBluntRebootReason(const std::string& r) {\n  if (isStrongRebootReason(r)) return false;\n\n  if (!isKnownRebootReason(r)) return true;  // Can not support unknown as detail\n\n  size_t pos = 0;\n  while ((pos = r.find(',', pos)) != std::string::npos) {\n    ++pos;\n    std::string next(r.substr(pos));\n    if (next.length() == 0) break;\n    if (next[0] == ',') continue;\n    if (!isKnownRebootReason(next)) return false;  // Unknown subreason is good.\n    if (isStrongRebootReason(next)) return false;  // eg: reboot,reboot\n  }\n  return true;\n}\n\nbool readPstoreConsole(std::string& console) {\n  if (android::base::ReadFileToString(\"/sys/fs/pstore/console-ramoops-0\", &console)) {\n    return true;\n  }\n  return android::base::ReadFileToString(\"/sys/fs/pstore/console-ramoops\", &console);\n}\n\n// Implement a variant of std::string::rfind that is resilient to errors in\n// the data stream being inspected.\nclass pstoreConsole {\n private:\n  const size_t kBitErrorRate = 8;  // number of bits per error\n  const std::string& console;\n\n  // Number of bits that differ between the two arguments l and r.\n  // Returns zero if the values for l and r are identical.\n  size_t numError(uint8_t l, uint8_t r) const { return std::bitset<8>(l ^ r).count(); }\n\n  // A string comparison function, reports the number of errors discovered\n  // in the match to a maximum of the bitLength / kBitErrorRate, at that\n  // point returning npos to indicate match is too poor.\n  //\n  // Since called in rfind which works backwards, expect cache locality will\n  // help if we check in reverse here as well for performance.\n  //\n  // Assumption: l (from console.c_str() + pos) is long enough to house\n  //             _r.length(), checked in rfind caller below.\n  //\n  size_t numError(size_t pos, const std::string& _r) const {\n    const char* l = console.c_str() + pos;\n    const char* r = _r.c_str();\n    size_t n = _r.length();\n    const uint8_t* le = reinterpret_cast<const uint8_t*>(l) + n;\n    const uint8_t* re = reinterpret_cast<const uint8_t*>(r) + n;\n    size_t count = 0;\n    n = 0;\n    do {\n      // individual character bit error rate > threshold + slop\n      size_t num = numError(*--le, *--re);\n      if (num > ((8 + kBitErrorRate) / kBitErrorRate)) return std::string::npos;\n      // total bit error rate > threshold + slop\n      count += num;\n      ++n;\n      if (count > ((n * 8 + kBitErrorRate - (n > 2)) / kBitErrorRate)) {\n        return std::string::npos;\n      }\n    } while (le != reinterpret_cast<const uint8_t*>(l));\n    return count;\n  }\n\n public:\n  explicit pstoreConsole(const std::string& console) : console(console) {}\n  // scope of argument must be equal to or greater than scope of pstoreConsole\n  explicit pstoreConsole(const std::string&& console) = delete;\n  explicit pstoreConsole(std::string&& console) = delete;\n\n  // Our implementation of rfind, use exact match first, then resort to fuzzy.\n  size_t rfind(const std::string& needle) const {\n    size_t pos = console.rfind(needle);  // exact match?\n    if (pos != std::string::npos) return pos;\n\n    // Check to make sure needle fits in console string.\n    pos = console.length();\n    if (needle.length() > pos) return std::string::npos;\n    pos -= needle.length();\n    // fuzzy match to maximum kBitErrorRate\n    for (;;) {\n      if (numError(pos, needle) != std::string::npos) return pos;\n      if (pos == 0) break;\n      --pos;\n    }\n    return std::string::npos;\n  }\n\n  // Our implementation of find, use only fuzzy match.\n  size_t find(const std::string& needle, size_t start = 0) const {\n    // Check to make sure needle fits in console string.\n    if (needle.length() > console.length()) return std::string::npos;\n    const size_t last_pos = console.length() - needle.length();\n    // fuzzy match to maximum kBitErrorRate\n    for (size_t pos = start; pos <= last_pos; ++pos) {\n      if (numError(pos, needle) != std::string::npos) return pos;\n    }\n    return std::string::npos;\n  }\n\n  operator const std::string&() const { return console; }\n};\n\n// If bit error match to needle, correct it.\n// Return true if any corrections were discovered and applied.\nbool correctForBitError(std::string& reason, const std::string& needle) {\n  bool corrected = false;\n  if (reason.length() < needle.length()) return corrected;\n  const pstoreConsole console(reason);\n  const size_t last_pos = reason.length() - needle.length();\n  for (size_t pos = 0; pos <= last_pos; pos += needle.length()) {\n    pos = console.find(needle, pos);\n    if (pos == std::string::npos) break;\n\n    // exact match has no malice\n    if (needle == reason.substr(pos, needle.length())) continue;\n\n    corrected = true;\n    reason = reason.substr(0, pos) + needle + reason.substr(pos + needle.length());\n  }\n  return corrected;\n}\n\n// If bit error match to needle, correct it.\n// Return true if any corrections were discovered and applied.\n// Try again if we can replace underline with spaces.\nbool correctForBitErrorOrUnderline(std::string& reason, const std::string& needle) {\n  bool corrected = correctForBitError(reason, needle);\n  std::string _needle(needle);\n  std::transform(_needle.begin(), _needle.end(), _needle.begin(),\n                 [](char c) { return (c == '_') ? ' ' : c; });\n  if (needle != _needle) {\n    corrected |= correctForBitError(reason, _needle);\n  }\n  return corrected;\n}\n\n// Converts a string value representing the reason the system booted to a\n// string complying with Android system standard reason.\nvoid transformReason(std::string& reason) {\n  std::transform(reason.begin(), reason.end(), reason.begin(), ::tolower);\n  std::transform(reason.begin(), reason.end(), reason.begin(),\n                 [](char c) { return ::isblank(c) ? '_' : c; });\n  std::transform(reason.begin(), reason.end(), reason.begin(),\n                 [](char c) { return ::isprint(c) ? c : '?'; });\n}\n\n// Check subreasons for reboot,<subreason> kernel_panic,sysrq,<subreason> or\n// kernel_panic,<subreason>.\n//\n// If quoted flag is set, pull out and correct single quoted ('), newline (\\n)\n// or unprintable character terminated subreason, pos is supplied just beyond\n// first quote.  if quoted false, pull out and correct newline (\\n) or\n// unprintable character terminated subreason.\n//\n// Heuristics to find termination is painted into a corner:\n\n// single bit error for quote ' that we can block.  It is acceptable for\n// the others 7, g in reason.  2/9 chance will miss the terminating quote,\n// but there is always the terminating newline that usually immediately\n// follows to fortify our chances.\nbool likely_single_quote(char c) {\n  switch (static_cast<uint8_t>(c)) {\n    case '\\'':         // '\\''\n    case '\\'' ^ 0x01:  // '&'\n    case '\\'' ^ 0x02:  // '%'\n    case '\\'' ^ 0x04:  // '#'\n    case '\\'' ^ 0x08:  // '/'\n      return true;\n    case '\\'' ^ 0x10:  // '7'\n      break;\n    case '\\'' ^ 0x20:  // '\\a' (unprintable)\n      return true;\n    case '\\'' ^ 0x40:  // 'g'\n      break;\n    case '\\'' ^ 0x80:  // 0xA7 (unprintable)\n      return true;\n  }\n  return false;\n}\n\n// ::isprint(c) and likely_space() will prevent us from being called for\n// fundamentally printable entries, except for '\\r' and '\\b'.\n//\n// Except for * and J, single bit errors for \\n, all others are non-\n// printable so easy catch.  It is _acceptable_ for *, J or j to exist in\n// the reason string, so 2/9 chance we will miss the terminating newline.\n//\n// NB: J might not be acceptable, except if at the beginning or preceded\n//     with a space, '(' or any of the quotes and their BER aliases.\n// NB: * might not be acceptable, except if at the beginning or preceded\n//     with a space, another *, or any of the quotes or their BER aliases.\n//\n// To reduce the chances to closer to 1/9 is too complicated for the gain.\nbool likely_newline(char c) {\n  switch (static_cast<uint8_t>(c)) {\n    case '\\n':         // '\\n' (unprintable)\n    case '\\n' ^ 0x01:  // '\\r' (unprintable)\n    case '\\n' ^ 0x02:  // '\\b' (unprintable)\n    case '\\n' ^ 0x04:  // 0x0E (unprintable)\n    case '\\n' ^ 0x08:  // 0x02 (unprintable)\n    case '\\n' ^ 0x10:  // 0x1A (unprintable)\n      return true;\n    case '\\n' ^ 0x20:  // '*'\n    case '\\n' ^ 0x40:  // 'J'\n      break;\n    case '\\n' ^ 0x80:  // 0x8A (unprintable)\n      return true;\n  }\n  return false;\n}\n\n// ::isprint(c) will prevent us from being called for all the printable\n// matches below.  If we let unprintables through because of this, they\n// get converted to underscore (_) by the validation phase.\nbool likely_space(char c) {\n  switch (static_cast<uint8_t>(c)) {\n    case ' ':          // ' '\n    case ' ' ^ 0x01:   // '!'\n    case ' ' ^ 0x02:   // '\"'\n    case ' ' ^ 0x04:   // '$'\n    case ' ' ^ 0x08:   // '('\n    case ' ' ^ 0x10:   // '0'\n    case ' ' ^ 0x20:   // '\\0' (unprintable)\n    case ' ' ^ 0x40:   // 'P'\n    case ' ' ^ 0x80:   // 0xA0 (unprintable)\n    case '\\t':         // '\\t'\n    case '\\t' ^ 0x01:  // '\\b' (unprintable) (likely_newline counters)\n    case '\\t' ^ 0x02:  // '\\v' (unprintable)\n    case '\\t' ^ 0x04:  // '\\r' (unprintable) (likely_newline counters)\n    case '\\t' ^ 0x08:  // 0x01 (unprintable)\n    case '\\t' ^ 0x10:  // 0x19 (unprintable)\n    case '\\t' ^ 0x20:  // ')'\n    case '\\t' ^ 0x40:  // '1'\n    case '\\t' ^ 0x80:  // 0x89 (unprintable)\n      return true;\n  }\n  return false;\n}\n\nstd::string getSubreason(const std::string& content, size_t pos, bool quoted) {\n  static constexpr size_t max_reason_length = 256;\n\n  std::string subReason(content.substr(pos, max_reason_length));\n  // Correct against any known strings that Bit Error Match\n  for (const auto& s : knownReasons) {\n    correctForBitErrorOrUnderline(subReason, s);\n  }\n  std::string terminator(quoted ? \"'\" : \"\");\n  for (const auto& m : kBootReasonMap) {\n    if (m.first.length() <= strlen(\"cold\")) continue;  // too short?\n    if (correctForBitErrorOrUnderline(subReason, m.first + terminator)) continue;\n    if (m.first.length() <= strlen(\"reboot,cold\")) continue;  // short?\n    if (android::base::StartsWith(m.first, \"reboot,\")) {\n      correctForBitErrorOrUnderline(subReason, m.first.substr(strlen(\"reboot,\")) + terminator);\n    } else if (android::base::StartsWith(m.first, \"kernel_panic,sysrq,\")) {\n      correctForBitErrorOrUnderline(subReason,\n                                    m.first.substr(strlen(\"kernel_panic,sysrq,\")) + terminator);\n    } else if (android::base::StartsWith(m.first, \"kernel_panic,\")) {\n      correctForBitErrorOrUnderline(subReason, m.first.substr(strlen(\"kernel_panic,\")) + terminator);\n    }\n  }\n  for (pos = 0; pos < subReason.length(); ++pos) {\n    char c = subReason[pos];\n    if (!(::isprint(c) || likely_space(c)) || likely_newline(c) ||\n        (quoted && likely_single_quote(c))) {\n      subReason.erase(pos);\n      break;\n    }\n  }\n  transformReason(subReason);\n  return subReason;\n}\n\nvoid addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) {\n  // Check for kernel panic types to refine information\n  if ((console.rfind(\"SysRq : Trigger a crash\") != std::string::npos) ||\n      (console.rfind(\"PC is at sysrq_handle_crash+\") != std::string::npos)) {\n    ret = \"kernel_panic,sysrq\";\n    // Invented for Android to allow daemons that specifically trigger sysrq\n    // to communicate more accurate boot subreasons via last console messages.\n    static constexpr char sysrqSubreason[] = \"SysRq : Trigger a crash : '\";\n    auto pos = console.rfind(sysrqSubreason);\n    if (pos != std::string::npos) {\n      ret += \",\" + getSubreason(console, pos + strlen(sysrqSubreason), /* quoted */ true);\n    }\n    return;\n  }\n  if (console.rfind(\"Unable to handle kernel NULL pointer dereference at virtual address\") !=\n      std::string::npos) {\n    ret = \"kernel_panic,null\";\n    return;\n  }\n  if (console.rfind(\"Kernel BUG at \") != std::string::npos) {\n    ret = \"kernel_panic,bug\";\n    return;\n  }\n\n  std::string panic(\"Kernel panic - not syncing: \");\n  auto pos = console.rfind(panic);\n  if (pos == std::string::npos) return;\n\n  static const std::vector<std::pair<const std::string, const std::string>> panicReasons = {\n      {\"Out of memory\", \"oom\"},\n      {\"out of memory\", \"oom\"},\n      {\"Oh boy, that early out of memory\", \"oom\"},  // omg\n      {\"BUG!\", \"bug\"},\n      {\"hung_task: blocked tasks\", \"hung\"},\n      {\"audit: \", \"audit\"},\n      {\"scheduling while atomic\", \"atomic\"},\n      {\"Attempted to kill init!\", \"init\"},\n      {\"Requested init\", \"init\"},\n      {\"No working init\", \"init\"},\n      {\"Could not decompress init\", \"init\"},\n      {\"RCU Stall\", \"hung,rcu\"},\n      {\"stack-protector\", \"stack\"},\n      {\"kernel stack overflow\", \"stack\"},\n      {\"Corrupt kernel stack\", \"stack\"},\n      {\"low stack detected\", \"stack\"},\n      {\"corrupted stack end\", \"stack\"},\n      {\"subsys-restart: Resetting the SoC - modem crashed.\", \"modem\"},\n      {\"subsys-restart: Resetting the SoC - adsp crashed.\", \"adsp\"},\n      {\"subsys-restart: Resetting the SoC - dsps crashed.\", \"dsps\"},\n      {\"subsys-restart: Resetting the SoC - wcnss crashed.\", \"wcnss\"},\n  };\n\n  ret = \"kernel_panic\";\n  for (auto& s : panicReasons) {\n    if (console.find(panic + s.first, pos) != std::string::npos) {\n      ret += \",\" + s.second;\n      return;\n    }\n  }\n  auto reason = getSubreason(console, pos + panic.length(), /* newline */ false);\n  if (reason.length() > 3) {\n    ret += \",\" + reason;\n  }\n}\n\nvoid addKernelPanicSubReason(const std::string& content, std::string& ret) {\n  addKernelPanicSubReason(pstoreConsole(content), ret);\n}\n\nconst char system_reboot_reason_property[] = \"sys.boot.reason\";\nconst char last_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY;\nconst char last_reboot_reason_file[] = LAST_REBOOT_REASON_FILE;\nconst char last_last_reboot_reason_property[] = \"sys.boot.reason.last\";\nconstexpr size_t history_reboot_reason_size = 4;\nconst char history_reboot_reason_property[] = LAST_REBOOT_REASON_PROPERTY \".history\";\nconst char bootloader_reboot_reason_property[] = \"ro.boot.bootreason\";\n\n// Land system_boot_reason into system_reboot_reason_property.\n// Shift system_boot_reason into history_reboot_reason_property.\nvoid BootReasonAddToHistory(const std::string& system_boot_reason) {\n  if (system_boot_reason.empty()) return;\n  LOG(INFO) << \"Canonical boot reason: \" << system_boot_reason;\n\n  // skip system_boot_reason(factory_reset, ota) shift since device boot up from shipmode\n  const auto bootloader_boot_reason =\n      android::base::GetProperty(bootloader_reboot_reason_property, \"\");\n  const char reg_fship[] = \".*fship.*\";\n  if (std::regex_search(bootloader_boot_reason, std::regex(reg_fship)) != 0) {\n    if (system_boot_reason == \"reboot,factory_reset\" || system_boot_reason == \"reboot,ota\") {\n      LOG(INFO) << \"skip boot reason (\" << system_boot_reason\n                << \") shift since device boot up from shipmode.\";\n      return;\n    }\n  }\n\n  auto old_system_boot_reason = android::base::GetProperty(system_reboot_reason_property, \"\");\n  if (!android::base::SetProperty(system_reboot_reason_property, system_boot_reason)) {\n    android::base::SetProperty(system_reboot_reason_property,\n                               system_boot_reason.substr(0, PROPERTY_VALUE_MAX - 1));\n  }\n  auto reason_history =\n      android::base::Split(android::base::GetProperty(history_reboot_reason_property, \"\"), \"\\n\");\n  static auto mark = time(nullptr);\n  auto mark_str = std::string(\",\") + std::to_string(mark);\n  auto marked_system_boot_reason = system_boot_reason + mark_str;\n  if (!reason_history.empty()) {\n    // delete any entries that we just wrote in a previous\n    // call and leveraging duplicate line handling\n    auto last = old_system_boot_reason + mark_str;\n    // trim the list to (history_reboot_reason_size - 1)\n    ssize_t max = history_reboot_reason_size;\n    for (auto it = reason_history.begin(); it != reason_history.end();) {\n      if (it->empty() || (last == *it) || (marked_system_boot_reason == *it) || (--max <= 0)) {\n        it = reason_history.erase(it);\n      } else {\n        last = *it;\n        ++it;\n      }\n    }\n  }\n  // insert at the front, concatenating mark (<epoch time>) detail to the value.\n  reason_history.insert(reason_history.begin(), marked_system_boot_reason);\n  // If the property string is too long ( > PROPERTY_VALUE_MAX)\n  // we get an error, so trim out last entry and try again.\n  while (!android::base::SetProperty(history_reboot_reason_property,\n                                     android::base::Join(reason_history, '\\n'))) {\n    auto it = std::prev(reason_history.end());\n    if (it == reason_history.end()) break;\n    reason_history.erase(it);\n  }\n}\n\n// Scrub, Sanitize, Standardize and Enhance the boot reason string supplied.\nstd::string BootReasonStrToReason(const std::string& boot_reason) {\n  auto ret = android::base::GetProperty(system_reboot_reason_property, \"\");\n  std::string reason(boot_reason);\n\n  // skip BootReasonStrToReason() if device boot up from shipmode\n  const char reg_fship[] = \".*fship.*\";\n  if (reason == ret && std::regex_search(reason, std::regex(reg_fship)) != 0) {\n    LOG(INFO) << \"skip boot reason enhancement if device boot up from shipmode\";\n    return ret;\n  }\n\n  // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate\n  if (reason == ret) ret = \"\";\n\n  transformReason(reason);\n\n  // Is the current system boot reason sys.boot.reason valid?\n  if (!isKnownRebootReason(ret)) ret = \"\";\n\n  if (ret == \"\") {\n    // Is the bootloader boot reason ro.boot.bootreason known?\n    std::vector<std::string> words(android::base::Split(reason, \",_-\"));\n    for (auto& s : knownReasons) {\n      std::string blunt;\n      for (auto& r : words) {\n        if (r == s) {\n          if (isBluntRebootReason(s)) {\n            blunt = s;\n          } else {\n            ret = s;\n            break;\n          }\n        }\n      }\n      if (ret == \"\") ret = blunt;\n      if (ret != \"\") break;\n    }\n  }\n\n  if (ret == \"\") {\n    // A series of checks to take some officially unsupported reasons\n    // reported by the bootloader and find some logical and canonical\n    // sense.  In an ideal world, we would require those bootloaders\n    // to behave and follow our CTS standards.\n    //\n    // first member is the output\n    // second member is an unanchored regex for an alias\n    //\n    // If output has a prefix of <bang> '!', we do not use it as a\n    // match needle (and drop the <bang> prefix when landing in output),\n    // otherwise look for it as well. This helps keep the scale of the\n    // following table smaller.\n    static const std::vector<std::pair<const std::string, const std::string>> aliasReasons = {\n        {\"watchdog\", \"wdog\"},\n        {\"kernel_panic\", \"panic\"},\n        {\"shutdown,thermal\", \"thermal\"},\n        {\"warm,s3_wakeup\", \"s3_wakeup\"},\n        {\"hard,hw_reset\", \"hw_reset\"},\n        {\"cold,charger\", \"usb|power_on_cable\"},\n        {\"cold,powerkey\", \"powerkey|power_key|PowerKey|power_on\"},\n        {\"cold,rtc\", \"rtc\"},\n        {\"cold,rtc,2sec\", \"2sec_reboot\"},\n        {\"!warm\", \"wdt_by_pass_pwk\"},  // change flavour of blunt\n        {\"!reboot\", \"^wdt$\"},          // change flavour of blunt\n        {\"reboot,tool\", \"tool_by_pass_pwk\"},\n        {\"!reboot,longkey\", \"reboot_longkey\"},\n        {\"!reboot,longkey\", \"kpdpwr\"},\n        {\"!reboot,undervoltage\", \"uvlo\"},\n        {\"!reboot,powerloss\", \"smpl\"},\n        {\"bootloader\", \"\"},\n    };\n\n    for (auto& s : aliasReasons) {\n      size_t firstHasNot = s.first[0] == '!';\n      if (!firstHasNot && (reason.find(s.first) != std::string::npos)) {\n        ret = s.first;\n        break;\n      }\n      if (s.second.size() && std::regex_search(reason, std::regex(s.second))) {\n        ret = s.first.substr(firstHasNot);\n        break;\n      }\n    }\n  }\n\n  // If watchdog is the reason, see if there is a security angle?\n  if (ret == \"watchdog\") {\n    if (reason.find(\"sec\") != std::string::npos) {\n      ret += \",security\";\n    }\n  }\n\n  if (ret == \"kernel_panic\") {\n    // Check to see if last klog has some refinement hints.\n    std::string content;\n    if (readPstoreConsole(content)) {\n      addKernelPanicSubReason(content, ret);\n    }\n  } else if (isBluntRebootReason(ret)) {\n    // Check the other available reason resources if the reason is still blunt.\n\n    // Check to see if last klog has some refinement hints.\n    std::string content;\n    if (readPstoreConsole(content)) {\n      const pstoreConsole console(content);\n      // The toybox reboot command used directly (unlikely)? But also\n      // catches init's response to Android's more controlled reboot command.\n      if (console.rfind(\"reboot: Power down\") != std::string::npos) {\n        ret = \"shutdown\";  // Still too blunt, but more accurate.\n        // ToDo: init should record the shutdown reason to kernel messages ala:\n        //           init: shutdown system with command 'last_reboot_reason'\n        //       so that if pstore has persistence we can get some details\n        //       that could be missing in last_reboot_reason_property.\n      }\n\n      static const char cmd[] = \"reboot: Restarting system with command '\";\n      size_t pos = console.rfind(cmd);\n      if (pos != std::string::npos) {\n        std::string subReason(getSubreason(content, pos + strlen(cmd), /* quoted */ true));\n        if (subReason != \"\") {  // Will not land \"reboot\" as that is too blunt.\n          if (isKernelRebootReason(subReason)) {\n            ret = \"reboot,\" + subReason;  // User space can't talk kernel reasons.\n          } else if (isKnownRebootReason(subReason)) {\n            ret = subReason;\n          } else {\n            ret = \"reboot,\" + subReason;  // legitimize unknown reasons\n          }\n        }\n        // Some bootloaders shutdown results record in last kernel message.\n        if (!strcmp(ret.c_str(), \"reboot,kernel_power_off_charging__reboot_system\")) {\n          ret = \"shutdown\";\n        }\n      }\n\n      // Check for kernel panics, allowed to override reboot command.\n      (void)addKernelPanicSubReason(console, ret);\n    }\n\n    // TODO: use the HAL to get battery level (http://b/77725702).\n\n    // Is there a controlled shutdown hint in last_reboot_reason_property?\n    if (isBluntRebootReason(ret)) {\n      // Content buffer no longer will have console data. Beware if more\n      // checks added below, that depend on parsing console content.\n      if (!android::base::ReadFileToString(last_reboot_reason_file, &content)) {\n        content = android::base::GetProperty(last_reboot_reason_property, \"\");\n      }\n      transformReason(content);\n\n      // Anything in last is better than 'super-blunt' reboot or shutdown.\n      if ((ret == \"\") || (ret == \"reboot\") || (ret == \"shutdown\") || !isBluntRebootReason(content)) {\n        ret = content;\n      }\n    }\n\n    // Other System Health HAL reasons?\n\n    // ToDo: /proc/sys/kernel/boot_reason needs a HAL interface to\n    //       possibly offer hardware-specific clues from the PMIC.\n  }\n\n  // If unknown left over from above, make it \"reboot,<boot_reason>\"\n  if (ret == \"\") {\n    ret = \"reboot\";\n    if (android::base::StartsWith(reason, \"reboot\")) {\n      reason = reason.substr(strlen(\"reboot\"));\n      while ((reason[0] == ',') || (reason[0] == '_')) {\n        reason = reason.substr(1);\n      }\n    }\n    if (reason != \"\") {\n      ret += \",\";\n      ret += reason;\n    }\n  }\n\n  LOG(INFO) << \"Canonical boot reason: \" << ret;\n  return ret;\n}\n\n// Returns the appropriate metric key prefix for the boot_complete metric such\n// that boot metrics after a system update are labeled as ota_boot_complete;\n// otherwise, they are labeled as boot_complete.  This method encapsulates the\n// bookkeeping required to track when a system update has occurred by storing\n// the UTC timestamp of the system build date and comparing against the current\n// system build date.\nstd::string CalculateBootCompletePrefix() {\n  static const std::string kBuildDateKey = \"build_date\";\n  std::string boot_complete_prefix = \"boot_complete\";\n\n  auto build_date_str = android::base::GetProperty(\"ro.build.date.utc\", \"\");\n  int32_t build_date;\n  if (!android::base::ParseInt(build_date_str, &build_date)) {\n    return std::string();\n  }\n\n  BootEventRecordStore boot_event_store;\n  BootEventRecordStore::BootEventRecord record;\n  if (!boot_event_store.GetBootEvent(kBuildDateKey, &record)) {\n    boot_complete_prefix = \"factory_reset_\" + boot_complete_prefix;\n    boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);\n    BootReasonAddToHistory(\"reboot,factory_reset\");\n  } else if (build_date != record.second) {\n    boot_complete_prefix = \"ota_\" + boot_complete_prefix;\n    boot_event_store.AddBootEventWithValue(kBuildDateKey, build_date);\n    BootReasonAddToHistory(\"reboot,ota\");\n  }\n\n  return boot_complete_prefix;\n}\n\n// Records the value of a given ro.boottime.init property in milliseconds.\nvoid RecordInitBootTimeProp(BootEventRecordStore* boot_event_store, const char* property) {\n  auto value = android::base::GetProperty(property, \"\");\n\n  int32_t time_in_ms;\n  if (android::base::ParseInt(value, &time_in_ms)) {\n    boot_event_store->AddBootEventWithValue(property, time_in_ms);\n  }\n}\n\n// A map from bootloader timing stage to the time that stage took during boot.\ntypedef std::map<std::string, int32_t> BootloaderTimingMap;\n\n// Returns a mapping from bootloader stage names to the time those stages\n// took to boot.\nconst BootloaderTimingMap GetBootLoaderTimings() {\n  BootloaderTimingMap timings;\n\n  // |ro.boot.boottime| is of the form 'stage1:time1,...,stageN:timeN',\n  // where timeN is in milliseconds.\n  auto value = android::base::GetProperty(\"ro.boot.boottime\", \"\");\n  if (value.empty()) {\n    // ro.boot.boottime is not reported on all devices.\n    return BootloaderTimingMap();\n  }\n\n  auto stages = android::base::Split(value, \",\");\n  for (const auto& stageTiming : stages) {\n    // |stageTiming| is of the form 'stage:time'.\n    auto stageTimingValues = android::base::Split(stageTiming, \":\");\n    DCHECK_EQ(2U, stageTimingValues.size());\n\n    if (stageTimingValues.size() < 2) continue;\n    std::string stageName = stageTimingValues[0];\n    int32_t time_ms;\n    if (android::base::ParseInt(stageTimingValues[1], &time_ms)) {\n      timings[stageName] = time_ms;\n    }\n  }\n\n  return timings;\n}\n\n// Returns the total bootloader boot time from the ro.boot.boottime system property.\nint32_t GetBootloaderTime(const BootloaderTimingMap& bootloader_timings) {\n  int32_t total_time = 0;\n  for (const auto& timing : bootloader_timings) {\n    total_time += timing.second;\n  }\n\n  return total_time;\n}\n\n// Parses and records the set of bootloader stages and associated boot times\n// from the ro.boot.boottime system property.\nvoid RecordBootloaderTimings(BootEventRecordStore* boot_event_store,\n                             const BootloaderTimingMap& bootloader_timings) {\n  int32_t total_time = 0;\n  for (const auto& timing : bootloader_timings) {\n    total_time += timing.second;\n    boot_event_store->AddBootEventWithValue(\"boottime.bootloader.\" + timing.first, timing.second);\n  }\n\n  boot_event_store->AddBootEventWithValue(\"boottime.bootloader.total\", total_time);\n}\n\n// Returns the closest estimation to the absolute device boot time, i.e.,\n// from power on to boot_complete, including bootloader times.\nstd::chrono::milliseconds GetAbsoluteBootTime(const BootloaderTimingMap& bootloader_timings,\n                                              std::chrono::milliseconds uptime) {\n  int32_t bootloader_time_ms = 0;\n\n  for (const auto& timing : bootloader_timings) {\n    if (timing.first.compare(\"SW\") != 0) {\n      bootloader_time_ms += timing.second;\n    }\n  }\n\n  auto bootloader_duration = std::chrono::milliseconds(bootloader_time_ms);\n  return bootloader_duration + uptime;\n}\n\n// Records the closest estimation to the absolute device boot time in seconds.\n// i.e. from power on to boot_complete, including bootloader times.\nvoid RecordAbsoluteBootTime(BootEventRecordStore* boot_event_store,\n                            std::chrono::milliseconds absolute_total) {\n  auto absolute_total_sec = std::chrono::duration_cast<std::chrono::seconds>(absolute_total);\n  boot_event_store->AddBootEventWithValue(\"absolute_boot_time\", absolute_total_sec.count());\n}\n\n// Logs the total boot time and reason to statsd.\nvoid LogBootInfoToStatsd(std::chrono::milliseconds end_time,\n                         std::chrono::milliseconds total_duration, int32_t bootloader_duration_ms,\n                         double time_since_last_boot_sec) {\n  auto reason = android::base::GetProperty(bootloader_reboot_reason_property, \"<EMPTY>\");\n  auto system_reason = android::base::GetProperty(system_reboot_reason_property, \"<EMPTY>\");\n  android::util::stats_write(android::util::BOOT_SEQUENCE_REPORTED, reason.c_str(),\n                             system_reason.c_str(), end_time.count(), total_duration.count(),\n                             (int64_t)bootloader_duration_ms,\n                             (int64_t)time_since_last_boot_sec * 1000);\n}\n\nvoid SetSystemBootReason() {\n  const auto bootloader_boot_reason =\n      android::base::GetProperty(bootloader_reboot_reason_property, \"\");\n  const std::string system_boot_reason(BootReasonStrToReason(bootloader_boot_reason));\n  // Record the scrubbed system_boot_reason to the property\n  BootReasonAddToHistory(system_boot_reason);\n  // Shift last_reboot_reason_property to last_last_reboot_reason_property\n  std::string last_boot_reason;\n  if (!android::base::ReadFileToString(last_reboot_reason_file, &last_boot_reason)) {\n    PLOG(ERROR) << \"Failed to read \" << last_reboot_reason_file;\n    last_boot_reason = android::base::GetProperty(last_reboot_reason_property, \"\");\n    LOG(INFO) << \"Value of \" << last_reboot_reason_property << \" : \" << last_boot_reason;\n  } else {\n    LOG(INFO) << \"Last reboot reason read from \" << last_reboot_reason_file << \" : \"\n              << last_boot_reason << \". Last reboot reason read from \"\n              << last_reboot_reason_property << \" : \"\n              << android::base::GetProperty(last_reboot_reason_property, \"\");\n  }\n  if (last_boot_reason.empty() || isKernelRebootReason(system_boot_reason)) {\n    last_boot_reason = system_boot_reason;\n  } else {\n    transformReason(last_boot_reason);\n  }\n  LOG(INFO) << \"Normalized last reboot reason : \" << last_boot_reason;\n  android::base::SetProperty(last_last_reboot_reason_property, last_boot_reason);\n  android::base::SetProperty(last_reboot_reason_property, \"\");\n  if (unlink(last_reboot_reason_file) != 0) {\n    PLOG(ERROR) << \"Failed to unlink \" << last_reboot_reason_file;\n  }\n}\n\n// Gets the boot time offset. This is useful when Android is running in a\n// container, because the boot_clock is not reset when Android reboots.\nstd::chrono::nanoseconds GetBootTimeOffset() {\n  static const int64_t boottime_offset =\n      android::base::GetIntProperty<int64_t>(\"ro.boot.boottime_offset\", 0);\n  return std::chrono::nanoseconds(boottime_offset);\n}\n\n// Returns the current uptime, accounting for any offset in the CLOCK_BOOTTIME\n// clock.\nandroid::base::boot_clock::duration GetUptime() {\n  return android::base::boot_clock::now().time_since_epoch() - GetBootTimeOffset();\n}\n\n// Records several metrics related to the time it takes to boot the device.\nvoid RecordBootComplete() {\n  BootEventRecordStore boot_event_store;\n  BootEventRecordStore::BootEventRecord record;\n\n  auto uptime_ns = GetUptime();\n  auto uptime_s = std::chrono::duration_cast<std::chrono::seconds>(uptime_ns);\n  time_t current_time_utc = time(nullptr);\n  time_t time_since_last_boot = 0;\n\n  if (boot_event_store.GetBootEvent(\"last_boot_time_utc\", &record)) {\n    time_t last_boot_time_utc = record.second;\n    time_since_last_boot = difftime(current_time_utc, last_boot_time_utc);\n    boot_event_store.AddBootEventWithValue(\"time_since_last_boot\", time_since_last_boot);\n  }\n\n  boot_event_store.AddBootEventWithValue(\"last_boot_time_utc\", current_time_utc);\n\n  // The boot_complete metric has two variants: boot_complete and\n  // ota_boot_complete.  The latter signifies that the device is booting after\n  // a system update.\n  std::string boot_complete_prefix = CalculateBootCompletePrefix();\n  if (boot_complete_prefix.empty()) {\n    // The system is hosed because the build date property could not be read.\n    return;\n  }\n\n  // The *_no_encryption events are emitted unconditionally, since they are left\n  // over from a time when encryption meant \"full-disk encryption\".  But Android\n  // now always uses file-based encryption instead of full-disk encryption.  At\n  // some point, these misleading and redundant events should be removed.\n  boot_event_store.AddBootEventWithValue(boot_complete_prefix + \"_no_encryption\",\n                                         uptime_s.count());\n\n  // Record the total time from device startup to boot complete.  Note: we are\n  // recording seconds here even though the field in statsd atom specifies\n  // milliseconds.\n  boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime_s.count());\n\n  RecordInitBootTimeProp(&boot_event_store, \"ro.boottime.init\");\n  RecordInitBootTimeProp(&boot_event_store, \"ro.boottime.init.first_stage\");\n  RecordInitBootTimeProp(&boot_event_store, \"ro.boottime.init.selinux\");\n  RecordInitBootTimeProp(&boot_event_store, \"ro.boottime.init.cold_boot_wait\");\n\n  const BootloaderTimingMap bootloader_timings = GetBootLoaderTimings();\n  int32_t bootloader_boot_duration = GetBootloaderTime(bootloader_timings);\n  RecordBootloaderTimings(&boot_event_store, bootloader_timings);\n\n  auto uptime_ms = std::chrono::duration_cast<std::chrono::milliseconds>(uptime_ns);\n  auto absolute_boot_time = GetAbsoluteBootTime(bootloader_timings, uptime_ms);\n  RecordAbsoluteBootTime(&boot_event_store, absolute_boot_time);\n\n  auto boot_end_time_point = std::chrono::system_clock::now().time_since_epoch();\n  auto boot_end_time = std::chrono::duration_cast<std::chrono::milliseconds>(boot_end_time_point);\n\n  LogBootInfoToStatsd(boot_end_time, absolute_boot_time, bootloader_boot_duration,\n                      time_since_last_boot);\n}\n\n// Records the boot_reason metric by querying the ro.boot.bootreason system\n// property.\nvoid RecordBootReason() {\n  const auto reason = android::base::GetProperty(bootloader_reboot_reason_property, \"\");\n\n  if (reason.empty()) {\n    // TODO(b/148575354): Replace with statsd.\n    // Log an empty boot reason value as '<EMPTY>' to ensure the value is intentional\n    // (and not corruption anywhere else in the reporting pipeline).\n    // android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,\n    //                                        android::metricslogger::FIELD_PLATFORM_REASON,\n    //                                        \"<EMPTY>\");\n  } else {\n    // TODO(b/148575354): Replace with statsd.\n    // android::metricslogger::LogMultiAction(android::metricslogger::ACTION_BOOT,\n    //                                        android::metricslogger::FIELD_PLATFORM_REASON,\n    //                                        reason);\n  }\n\n  // Log the raw bootloader_boot_reason property value.\n  int32_t boot_reason = BootReasonStrToEnum(reason);\n  BootEventRecordStore boot_event_store;\n  boot_event_store.AddBootEventWithValue(\"boot_reason\", boot_reason);\n\n  // Log the scrubbed system_boot_reason.\n  const auto system_reason = android::base::GetProperty(system_reboot_reason_property, \"\");\n  int32_t system_boot_reason = BootReasonStrToEnum(system_reason);\n  boot_event_store.AddBootEventWithValue(\"system_boot_reason\", system_boot_reason);\n\n  if (reason == \"\") {\n    android::base::SetProperty(bootloader_reboot_reason_property, system_reason);\n  }\n}\n\n// Records two metrics related to the user resetting a device: the time at\n// which the device is reset, and the time since the user last reset the\n// device.  The former is only set once per-factory reset.\nvoid RecordFactoryReset() {\n  BootEventRecordStore boot_event_store;\n  BootEventRecordStore::BootEventRecord record;\n\n  time_t current_time_utc = time(nullptr);\n\n  if (current_time_utc < 0) {\n    // UMA does not display negative values in buckets, so convert to positive.\n    // Logging via BootEventRecordStore.\n    android::util::stats_write(\n        static_cast<int32_t>(android::util::BOOT_TIME_EVENT_ERROR_CODE_REPORTED),\n        static_cast<int32_t>(\n            android::util::BOOT_TIME_EVENT_ERROR_CODE__EVENT__FACTORY_RESET_CURRENT_TIME_FAILURE),\n        static_cast<int32_t>(std::abs(current_time_utc)));\n\n    // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram\n    // is losing records somehow.\n    boot_event_store.AddBootEventWithValue(\"factory_reset_current_time_failure\",\n                                           std::abs(current_time_utc));\n    return;\n  } else {\n    android::util::stats_write(\n        static_cast<int32_t>(android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED),\n        static_cast<int32_t>(\n            android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_CURRENT_TIME),\n        static_cast<int64_t>(current_time_utc));\n\n    // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram\n    // is losing records somehow.\n    boot_event_store.AddBootEventWithValue(\"factory_reset_current_time\", current_time_utc);\n  }\n\n  // The factory_reset boot event does not exist after the device is reset, so\n  // use this signal to mark the time of the factory reset.\n  if (!boot_event_store.GetBootEvent(\"factory_reset\", &record)) {\n    boot_event_store.AddBootEventWithValue(\"factory_reset\", current_time_utc);\n\n    // Don't log the time_since_factory_reset until some time has elapsed.\n    // The data is not meaningful yet and skews the histogram buckets.\n    return;\n  }\n\n  // Calculate and record the difference in time between now and the\n  // factory_reset time.\n  time_t factory_reset_utc = record.second;\n  android::util::stats_write(\n      static_cast<int32_t>(android::util::BOOT_TIME_EVENT_UTC_TIME_REPORTED),\n      static_cast<int32_t>(\n          android::util::BOOT_TIME_EVENT_UTC_TIME__EVENT__FACTORY_RESET_RECORD_VALUE),\n      static_cast<int64_t>(factory_reset_utc));\n\n  // Logging via BootEventRecordStore to see if using android::metricslogger::LogHistogram\n  // is losing records somehow.\n  boot_event_store.AddBootEventWithValue(\"factory_reset_record_value\", factory_reset_utc);\n\n  time_t time_since_factory_reset = difftime(current_time_utc, factory_reset_utc);\n  boot_event_store.AddBootEventWithValue(\"time_since_factory_reset\", time_since_factory_reset);\n}\n\n// List the associated boot reason(s), if arg is nullptr then all.\nvoid PrintBootReasonEnum(const char* arg) {\n  int value = -1;\n  if (arg != nullptr) {\n    value = BootReasonStrToEnum(arg);\n  }\n  for (const auto& [match, id] : kBootReasonMap) {\n    if ((value < 0) || (value == id)) {\n      printf(\"%u\\t%s\\n\", id, match.c_str());\n    }\n  }\n}\n\n}  // namespace\n\nint main(int argc, char** argv) {\n  android::base::InitLogging(argv);\n\n  const std::string cmd_line = GetCommandLine(argc, argv);\n  LOG(INFO) << \"Service started: \" << cmd_line;\n\n  int option_index = 0;\n  static const char value_str[] = \"value\";\n  static const char system_boot_reason_str[] = \"set_system_boot_reason\";\n  static const char boot_complete_str[] = \"record_boot_complete\";\n  static const char boot_reason_str[] = \"record_boot_reason\";\n  static const char factory_reset_str[] = \"record_time_since_factory_reset\";\n  static const char boot_reason_enum_str[] = \"boot_reason_enum\";\n  static const struct option long_options[] = {\n      // clang-format off\n      { \"help\",                 no_argument,       NULL,   'h' },\n      { \"log\",                  no_argument,       NULL,   'l' },\n      { \"print\",                no_argument,       NULL,   'p' },\n      { \"record\",               required_argument, NULL,   'r' },\n      { value_str,              required_argument, NULL,   0 },\n      { system_boot_reason_str, no_argument,       NULL,   0 },\n      { boot_complete_str,      no_argument,       NULL,   0 },\n      { boot_reason_str,        no_argument,       NULL,   0 },\n      { factory_reset_str,      no_argument,       NULL,   0 },\n      { boot_reason_enum_str,   optional_argument, NULL,   0 },\n      { NULL,                   0,                 NULL,   0 }\n      // clang-format on\n  };\n\n  std::string boot_event;\n  std::string value;\n  int opt = 0;\n  while ((opt = getopt_long(argc, argv, \"hlpr:\", long_options, &option_index)) != -1) {\n    switch (opt) {\n      // This case handles long options which have no single-character mapping.\n      case 0: {\n        const std::string option_name = long_options[option_index].name;\n        if (option_name == value_str) {\n          // |optarg| is an external variable set by getopt representing\n          // the option argument.\n          value = optarg;\n        } else if (option_name == system_boot_reason_str) {\n          SetSystemBootReason();\n        } else if (option_name == boot_complete_str) {\n          RecordBootComplete();\n        } else if (option_name == boot_reason_str) {\n          RecordBootReason();\n        } else if (option_name == factory_reset_str) {\n          RecordFactoryReset();\n        } else if (option_name == boot_reason_enum_str) {\n          PrintBootReasonEnum(optarg);\n        } else {\n          LOG(ERROR) << \"Invalid option: \" << option_name;\n        }\n        break;\n      }\n\n      case 'h': {\n        ShowHelp(argv[0]);\n        break;\n      }\n\n      case 'l': {\n        LogBootEvents();\n        break;\n      }\n\n      case 'p': {\n        PrintBootEvents();\n        break;\n      }\n\n      case 'r': {\n        // |optarg| is an external variable set by getopt representing\n        // the option argument.\n        boot_event = optarg;\n        break;\n      }\n\n      default: {\n        DCHECK_EQ(opt, '?');\n\n        // |optopt| is an external variable set by getopt representing\n        // the value of the invalid option.\n        LOG(ERROR) << \"Invalid option: \" << optopt;\n        ShowHelp(argv[0]);\n        return EXIT_FAILURE;\n      }\n    }\n  }\n\n  if (!boot_event.empty()) {\n    RecordBootEventFromCommandLine(boot_event, value);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "bootstat/bootstat.rc",
    "content": "# This file is the LOCAL_INIT_RC file for the bootstat command.\n\n# Mirror bootloader boot reason to system boot reason\n# ro.boot.bootreason should be set by init already\n# before post-fs trigger\non post-fs && property:ro.boot.bootreason=*\n    setprop sys.boot.reason ${ro.boot.bootreason}\n\non post-fs-data\n    mkdir /data/misc/bootstat 0700 system log\n    # To deal with ota transition resulting from a change in DAC from\n    # root.root to system.log, may be deleted after ota has settled.\n    chown system log /data/misc/bootstat/absolute_boot_time\n    chown system log /data/misc/bootstat/boot_complete\n    chown system log /data/misc/bootstat/boot_complete_no_encryption\n    chown system log /data/misc/bootstat/boot_reason\n    chown system log /data/misc/bootstat/boottime.bootloader.1BLE\n    chown system log /data/misc/bootstat/boottime.bootloader.1BLL\n    chown system log /data/misc/bootstat/boottime.bootloader.2BLE\n    chown system log /data/misc/bootstat/boottime.bootloader.2BLL\n    chown system log /data/misc/bootstat/boottime.bootloader.AVB\n    chown system log /data/misc/bootstat/boottime.bootloader.KD\n    chown system log /data/misc/bootstat/boottime.bootloader.KL\n    chown system log /data/misc/bootstat/boottime.bootloader.ODT\n    chown system log /data/misc/bootstat/boottime.bootloader.SW\n    chown system log /data/misc/bootstat/boottime.bootloader.total\n    chown system log /data/misc/bootstat/build_date\n    chown system log /data/misc/bootstat/factory_reset\n    chown system log /data/misc/bootstat/factory_reset_boot_complete\n    chown system log /data/misc/bootstat/factory_reset_boot_complete_no_encryption\n    chown system log /data/misc/bootstat/factory_reset_current_time\n    chown system log /data/misc/bootstat/factory_reset_record_value\n    chown system log /data/misc/bootstat/last_boot_time_utc\n    chown system log /data/misc/bootstat/ota_boot_complete\n    chown system log /data/misc/bootstat/ota_boot_complete_no_encryption\n    chown system log /data/misc/bootstat/ro.boottime.init\n    chown system log /data/misc/bootstat/ro.boottime.init.cold_boot_wait\n    chown system log /data/misc/bootstat/ro.boottime.init.selinux\n    chown system log /data/misc/bootstat/time_since_factory_reset\n    chown system log /data/misc/bootstat/time_since_last_boot\n    # end ota transitional support\n\n# Initialize bootstat state machine.\n#\n# sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens\n# only once per device hard reboot. Possible values:\n#\n#   sys.bootstat.first_boot_completed=0 - first boot completed trigger wasn't processed yet.\n#   sys.bootstat.first_boot_completed=1 - first boot completed trigger was processed and\n#                                         record_boot_complete was called. Subsequent boot completed\n#                                         triggers (e.g. due to userspace reboot) won't retrigger\n#                                         record_boot_complete\n#\n# IMPORTANT, ro.persistent_properties.ready=1 trigger is used here to ensure that we initialize\n# state machine only once, which as result ensures that bootstat --set_system_boot_reason and\n# bootstat --record_boot_complete will be called only once per full reboot.\non property:ro.persistent_properties.ready=true\n  setprop sys.bootstat.first_boot_completed 0\n\n# Set boot reason\non property:ro.persistent_properties.ready=true\n    # Converts bootloader boot reason and persist.sys.boot.reason to system boot reason\n    # Need go after persist peroperties are loaded which is right before zygote-start trigger\n    exec_background - system log -- /system/bin/bootstat --set_system_boot_reason\n\n# Record boot complete metrics.\non property:sys.boot_completed=1 && property:sys.bootstat.first_boot_completed=0\n    # Record boot_complete and related stats (decryption, etc).\n    # Record the boot reason.\n    # Record time since factory reset.\n    # Log all boot events.\n    exec_background - system log -- /system/bin/bootstat --record_boot_complete --record_boot_reason --record_time_since_factory_reset -l\n    setprop sys.bootstat.first_boot_completed 1\n"
  },
  {
    "path": "bootstat/testrunner.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n#include <gtest/gtest.h>\n\nint main(int argc, char** argv) {\n  ::testing::InitGoogleTest(&argc, argv);\n  android::base::InitLogging(argv, android::base::StderrLogger);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "cli-test/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"cli-test\",\n    host_supported: true,\n    srcs: [\"cli-test.cpp\"],\n    cflags: [\"-Wall\", \"-Werror\"],\n    shared_libs: [\"libbase\"],\n}\n"
  },
  {
    "path": "cli-test/README.md",
    "content": "# cli-test\n\n## What?\n\n`cli-test` makes integration testing of command-line tools easier.\n\n## Goals\n\n* Readable syntax. Common cases should be concise, and pretty much anyone\n  should be able to read tests even if they've never seen this tool before.\n\n* Minimal issues with quoting. The toybox tests -- being shell scripts --\n  quickly become a nightmare of quoting. Using a non ad hoc format (such as\n  JSON) would have introduced similar but different quoting issues. A custom\n  format, while annoying, side-steps this.\n\n* Sensible defaults. We expect your exit status to be 0 unless you say\n  otherwise. We expect nothing on stderr unless you say otherwise. And so on.\n\n* Convention over configuration. Related to sensible defaults, we don't let you\n  configure things that aren't absolutely necessary. So you can't keep your test\n  data anywhere except in the `files/` subdirectory of the directory containing\n  your test, for example.\n\n## Non Goals\n\n* Portability. Just being able to run on Linux (host and device) is sufficient\n  for our needs. macOS is probably easy enough if we ever need it, but Windows\n  probably doesn't make sense.\n\n## Syntax\n\nAny all-whitespace line, or line starting with `#` is ignored.\n\nA test looks like this:\n```\nname: unzip -l\ncommand: unzip -l $FILES/example.zip d1/d2/x.txt\nafter: [ ! -f d1/d2/x.txt ]\nexpected-stdout:\n\tArchive:  $FILES/example.zip\n\t  Length      Date    Time    Name\n\t---------  ---------- -----   ----\n\t     1024  2017-06-04 08:45   d1/d2/x.txt\n\t---------                     -------\n\t     1024                     1 file\n---\n```\n\nThe `name:` line names the test, and is only for human consumption.\n\nThe `command:` line is the command to be run. Additional commands can be\nsupplied as zero or more `before:` lines (run before `command:`) and zero or\nmore `after:` lines (run after `command:`). These are useful for both\nsetup/teardown but also for testing post conditions (as in the example above).\n\nAny `command:`, `before:`, or `after:` line is expected to exit with status 0.\nAnything else is considered a test failure.\n\nThe `expected-stdout:` line is followed by zero or more tab-prefixed lines that\nare otherwise the exact output expected from the command. (There's magic behind\nthe scenes to rewrite the test files directory to `$FILES` because otherwise any\npath in the output would depend on the temporary directory used to run the test.)\n\nThere is currently no `expected-stderr:` line. Standard error is implicitly\nexpected to be empty, and any output will cause a test failure. (The support is\nthere, but not wired up because we haven't needed it yet.)\n\nThe fields can appear in any order, but every test must contain at least a\n`name:` line and a `command:` line.\n\n## Output\n\nThe output is intended to resemble gtest.\n\n## Future Directions\n\n* It's often useful to be able to *match* against stdout/stderr/a file rather\n  than give exact expected output. We might want to add explicit support for\n  this. In the meantime, it's possible to use an `after:` with `grep -q` if\n  you redirect in your `command:`.\n\n* In addition to using a `before:` (which will fail a test), it can be useful\n  to be able to specify tests that would cause us to *skip* a test. An example\n  would be \"am I running as root?\".\n\n* It might be useful to be able to make exit status assertions other than 0?\n\n* There's currently no way (other than the `files/` directory) to share repeated\n  setup between tests.\n"
  },
  {
    "path": "cli-test/cli-test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <getopt.h>\n#include <inttypes.h>\n#include <libgen.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/test_utils.h>\n\n// Example:\n\n// name: unzip -n\n// before: mkdir -p d1/d2\n// before: echo b > d1/d2/a.txt\n// command: unzip -q -n $FILES/zip/example.zip d1/d2/a.txt && cat d1/d2/a.txt\n// expected-stdout:\n// \tb\n\nstruct Test {\n  std::string test_filename;\n  std::string name;\n  std::string command;\n  std::vector<std::string> befores;\n  std::vector<std::string> afters;\n  std::string expected_stdout;\n  std::string expected_stderr;\n  int exit_status = 0;\n};\n\nstatic const char* g_progname;\nstatic bool g_verbose;\n\nstatic const char* g_file;\nstatic size_t g_line;\n\nenum Color { kRed, kGreen };\n\nstatic void Print(Color c, const char* lhs, const char* fmt, ...) {\n  va_list ap;\n  va_start(ap, fmt);\n  if (isatty(0)) printf(\"%s\", (c == kRed) ? \"\\e[31m\" : \"\\e[32m\");\n  printf(\"%s%s\", lhs, isatty(0) ? \"\\e[0m\" : \"\");\n  vfprintf(stdout, fmt, ap);\n  putchar('\\n');\n  va_end(ap);\n}\n\nstatic void Die(int error, const char* fmt, ...) {\n  va_list ap;\n  va_start(ap, fmt);\n  fprintf(stderr, \"%s: \", g_progname);\n  vfprintf(stderr, fmt, ap);\n  if (error != 0) fprintf(stderr, \": %s\", strerror(error));\n  fprintf(stderr, \"\\n\");\n  va_end(ap);\n  _exit(1);\n}\n\nstatic void V(const char* fmt, ...) {\n  if (!g_verbose) return;\n\n  va_list ap;\n  va_start(ap, fmt);\n  fprintf(stderr, \"           - \");\n  vfprintf(stderr, fmt, ap);\n  fprintf(stderr, \"\\n\");\n  va_end(ap);\n}\n\nstatic void SetField(const char* what, std::string* field, std::string_view value) {\n  if (!field->empty()) {\n    Die(0, \"%s:%zu: %s already set to '%s'\", g_file, g_line, what, field->c_str());\n  }\n  field->assign(value);\n}\n\n// Similar to ConsumePrefix, but also trims, so \"key:value\" and \"key: value\"\n// are equivalent.\nstatic bool Match(std::string* s, const std::string& prefix) {\n  if (!android::base::StartsWith(*s, prefix)) return false;\n  s->assign(android::base::Trim(s->substr(prefix.length())));\n  return true;\n}\n\nstatic void CollectTests(std::vector<Test>* tests, const char* test_filename) {\n  std::string absolute_test_filename;\n  if (!android::base::Realpath(test_filename, &absolute_test_filename)) {\n    Die(errno, \"realpath '%s'\", test_filename);\n  }\n\n  std::string content;\n  if (!android::base::ReadFileToString(test_filename, &content)) {\n    Die(errno, \"couldn't read '%s'\", test_filename);\n  }\n\n  size_t count = 0;\n  g_file = test_filename;\n  g_line = 0;\n  auto lines = android::base::Split(content, \"\\n\");\n  std::unique_ptr<Test> test(new Test);\n  while (g_line < lines.size()) {\n    auto line = lines[g_line++];\n    if (line.empty() || line[0] == '#') continue;\n\n    if (line[0] == '-') {\n      if (test->name.empty() || test->command.empty()) {\n        Die(0, \"%s:%zu: each test requires both a name and a command\", g_file, g_line);\n      }\n      test->test_filename = absolute_test_filename;\n      tests->push_back(*test.release());\n      test.reset(new Test);\n      ++count;\n    } else if (Match(&line, \"name:\")) {\n      SetField(\"name\", &test->name, line);\n    } else if (Match(&line, \"command:\")) {\n      SetField(\"command\", &test->command, line);\n    } else if (Match(&line, \"before:\")) {\n      test->befores.push_back(line);\n    } else if (Match(&line, \"after:\")) {\n      test->afters.push_back(line);\n    } else if (Match(&line, \"expected-exit-status:\")) {\n      char* end_p;\n      errno = 0;\n      test->exit_status = strtol(line.c_str(), &end_p, 10);\n      if (errno != 0 || *end_p != '\\0') {\n        Die(0, \"%s:%zu: bad exit status: \\\"%s\\\"\", g_file, g_line, line.c_str());\n      }\n    } else if (Match(&line, \"expected-stdout:\")) {\n      // Collect tab-indented lines.\n      std::string text;\n      while (g_line < lines.size() && !lines[g_line].empty() && lines[g_line][0] == '\\t') {\n        text += lines[g_line++].substr(1) + \"\\n\";\n      }\n      SetField(\"expected stdout\", &test->expected_stdout, text);\n    } else {\n      Die(0, \"%s:%zu: syntax error: \\\"%s\\\"\", g_file, g_line, line.c_str());\n    }\n  }\n  if (count == 0) Die(0, \"no tests found in '%s'\", g_file);\n}\n\nstatic const char* Plural(size_t n) {\n  return (n == 1) ? \"\" : \"s\";\n}\n\nstatic std::string ExitStatusToString(int status) {\n  if (WIFSIGNALED(status)) {\n    return android::base::StringPrintf(\"was killed by signal %d (%s)\", WTERMSIG(status),\n                                       strsignal(WTERMSIG(status)));\n  }\n  if (WIFSTOPPED(status)) {\n    return android::base::StringPrintf(\"was stopped by signal %d (%s)\", WSTOPSIG(status),\n                                       strsignal(WSTOPSIG(status)));\n  }\n  return android::base::StringPrintf(\"exited with status %d\", WEXITSTATUS(status));\n}\n\nstatic bool RunCommands(const char* what, const std::vector<std::string>& commands) {\n  bool result = true;\n  for (auto& command : commands) {\n    V(\"running %s \\\"%s\\\"\", what, command.c_str());\n    int exit_status = system(command.c_str());\n    if (exit_status != 0) {\n      result = false;\n      fprintf(stderr, \"Command (%s) \\\"%s\\\" %s\\n\", what, command.c_str(),\n              ExitStatusToString(exit_status).c_str());\n    }\n  }\n  return result;\n}\n\nstatic bool CheckOutput(const char* what, std::string actual_output,\n                        const std::string& expected_output, const std::string& FILES) {\n  // Rewrite the output to reverse any expansion of $FILES.\n  actual_output = android::base::StringReplace(actual_output, FILES, \"$FILES\", true);\n\n  bool result = (actual_output == expected_output);\n  if (!result) {\n    fprintf(stderr, \"Incorrect %s.\\nExpected:\\n%s\\nActual:\\n%s\\n\", what, expected_output.c_str(),\n            actual_output.c_str());\n  }\n  return result;\n}\n\nstatic int RunTests(const std::vector<Test>& tests) {\n  std::vector<std::string> failures;\n\n  Print(kGreen, \"[==========]\", \" Running %zu tests.\", tests.size());\n  android::base::Timer total_timer;\n  for (const auto& test : tests) {\n    bool failed = false;\n\n    Print(kGreen, \"[ RUN      ]\", \" %s\", test.name.c_str());\n    android::base::Timer test_timer;\n\n    // Set $FILES for this test.\n    std::string FILES = android::base::Dirname(test.test_filename) + \"/files\";\n    V(\"setenv(\\\"FILES\\\", \\\"%s\\\")\", FILES.c_str());\n    setenv(\"FILES\", FILES.c_str(), 1);\n\n    // Make a safe space to run the test.\n    TemporaryDir td;\n    V(\"chdir(\\\"%s\\\")\", td.path);\n    if (chdir(td.path)) Die(errno, \"chdir(\\\"%s\\\")\", td.path);\n\n    // Perform any setup specified for this test.\n    if (!RunCommands(\"before\", test.befores)) failed = true;\n\n    if (!failed) {\n      V(\"running command \\\"%s\\\"\", test.command.c_str());\n      CapturedStdout test_stdout;\n      CapturedStderr test_stderr;\n      int status = system(test.command.c_str());\n      test_stdout.Stop();\n      test_stderr.Stop();\n\n      V(\"system() returned status %d\", status);\n      if (WEXITSTATUS(status) != test.exit_status) {\n        failed = true;\n        fprintf(stderr, \"Incorrect exit status: expected %d but %s\\n\", test.exit_status,\n                ExitStatusToString(status).c_str());\n      }\n\n      if (!CheckOutput(\"stdout\", test_stdout.str(), test.expected_stdout, FILES)) failed = true;\n      if (!CheckOutput(\"stderr\", test_stderr.str(), test.expected_stderr, FILES)) failed = true;\n\n      if (!RunCommands(\"after\", test.afters)) failed = true;\n    }\n\n    std::stringstream duration;\n    duration << test_timer;\n    if (failed) {\n      failures.push_back(test.name);\n      Print(kRed, \"[  FAILED  ]\", \" %s (%s)\", test.name.c_str(), duration.str().c_str());\n    } else {\n      Print(kGreen, \"[       OK ]\", \" %s (%s)\", test.name.c_str(), duration.str().c_str());\n    }\n  }\n\n  // Summarize the whole run and explicitly list all the failures.\n\n  std::stringstream duration;\n  duration << total_timer;\n  Print(kGreen, \"[==========]\", \" %zu tests ran. (%s total)\", tests.size(), duration.str().c_str());\n\n  size_t fail_count = failures.size();\n  size_t pass_count = tests.size() - fail_count;\n  Print(kGreen, \"[  PASSED  ]\", \" %zu test%s.\", pass_count, Plural(pass_count));\n  if (!failures.empty()) {\n    Print(kRed, \"[  FAILED  ]\", \" %zu test%s.\", fail_count, Plural(fail_count));\n    for (auto& failure : failures) {\n      Print(kRed, \"[  FAILED  ]\", \" %s\", failure.c_str());\n    }\n  }\n  return (fail_count == 0) ? 0 : 1;\n}\n\nstatic void ShowHelp(bool full) {\n  fprintf(full ? stdout : stderr, \"usage: %s [-v] FILE...\\n\", g_progname);\n  if (!full) exit(EXIT_FAILURE);\n\n  printf(\n      \"\\n\"\n      \"Run tests.\\n\"\n      \"\\n\"\n      \"-v\\tVerbose (show workings)\\n\");\n  exit(EXIT_SUCCESS);\n}\n\nint main(int argc, char* argv[]) {\n  g_progname = basename(argv[0]);\n\n  static const struct option opts[] = {\n      {\"help\", no_argument, 0, 'h'},\n      {\"verbose\", no_argument, 0, 'v'},\n      {},\n  };\n\n  int opt;\n  while ((opt = getopt_long(argc, argv, \"hv\", opts, nullptr)) != -1) {\n    switch (opt) {\n      case 'h':\n        ShowHelp(true);\n        break;\n      case 'v':\n        g_verbose = true;\n        break;\n      default:\n        ShowHelp(false);\n        break;\n    }\n  }\n\n  argv += optind;\n  if (!*argv) Die(0, \"no test files provided\");\n  std::vector<Test> tests;\n  for (; *argv; ++argv) CollectTests(&tests, *argv);\n  return RunTests(tests);\n}\n"
  },
  {
    "path": "code_coverage/Android.bp",
    "content": "\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nprebuilt_etc {\n    name: \"code_coverage.policy\",\n    sub_dir: \"seccomp_policy\",\n    filename_from_src: true,\n    arch: {\n        arm: {\n            src: \"empty_policy/code_coverage.arm.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.arm.policy\",\n                },\n            },\n        },\n        arm64: {\n            src: \"empty_policy/code_coverage.arm64.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.arm64.policy\",\n                },\n            },\n        },\n        riscv64: {\n            src: \"empty_policy/code_coverage.riscv64.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.riscv64.policy\",\n                },\n            },\n        },\n        x86: {\n            src: \"empty_policy/code_coverage.x86.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.x86.policy\",\n                },\n            },\n        },\n        x86_64: {\n            src: \"empty_policy/code_coverage.x86_64.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.x86_64.policy\",\n                },\n            },\n        },\n    },\n    required: [\n        \"code_coverage.policy.other\",\n    ],\n}\n\nprebuilt_etc {\n    name: \"code_coverage.policy.other\",\n    sub_dir: \"seccomp_policy\",\n    filename_from_src: true,\n    arch: {\n        arm: {\n            src: \"empty_policy/code_coverage.arm64.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.arm64.policy\",\n                },\n            },\n        },\n        arm64: {\n            src: \"empty_policy/code_coverage.arm.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.arm.policy\",\n                },\n            },\n        },\n        riscv64: {\n            // riscv64 doesn't have a secondary architecture.\n            enabled: false,\n        },\n        x86: {\n            src: \"empty_policy/code_coverage.x86_64.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.x86_64.policy\",\n                },\n            },\n        },\n        x86_64: {\n            src: \"empty_policy/code_coverage.x86.policy\",\n            product_variables: {\n                native_coverage: {\n                    src: \"seccomp_policy/code_coverage.x86.policy\",\n                },\n            },\n        },\n    },\n}\n"
  },
  {
    "path": "code_coverage/empty_policy/code_coverage.arm.policy",
    "content": "# empty unless code_coverage is enabled.\n# code_coverage.arm.policy\n"
  },
  {
    "path": "code_coverage/empty_policy/code_coverage.arm64.policy",
    "content": "# empty unless code_coverage is enabled.\n# code_coverage.arm64.policy\n"
  },
  {
    "path": "code_coverage/empty_policy/code_coverage.riscv64.policy",
    "content": "# empty unless code_coverage is enabled.\n# code_coverage.riscv64.policy\n"
  },
  {
    "path": "code_coverage/empty_policy/code_coverage.x86.policy",
    "content": "# empty unless code_coverage is enabled.\n# code_coverage.x86.policy\n"
  },
  {
    "path": "code_coverage/empty_policy/code_coverage.x86_64.policy",
    "content": "# empty unless code_coverage is enabled.\n# code_coverage.x86_64.policy\n"
  },
  {
    "path": "code_coverage/seccomp_policy/code_coverage.arm.policy",
    "content": "close: 1\nfchmod: 1\nmkdirat: 1\nmsync: 1\nmunmap: 1\nopenat: 1\nwrite: 1\nfcntl64: 1\nfstat64: 1\nftruncate64: 1\ngeteuid32: 1\n_llseek: 1\nmmap2: 1\nsigreturn: 1\ngettimeofday: 1\nprctl: 1\n"
  },
  {
    "path": "code_coverage/seccomp_policy/code_coverage.arm64.policy",
    "content": "close: 1\nfchmod: 1\nmkdirat: 1\nmsync: 1\nmunmap: 1\nopenat: 1\nwrite: 1\nfcntl: 1\nfstat: 1\nftruncate: 1\ngeteuid: 1\nlseek: 1\nmmap: 1\nrt_sigreturn: 1\nprctl: 1\n"
  },
  {
    "path": "code_coverage/seccomp_policy/code_coverage.policy.def",
    "content": "// SECCOMP_MODE_STRICT\n//\n// minijail allowances for code coverage\n// this is processed with generate.sh, so we can use appropriate directives\n// size specific: __LP64__ for 64 bit, else 32 bit\n// arch specific: __arm__, __aarch64__, __i386__, __x86_64__\n\n// includes *all* syscalls used during the coverage dumping\n// no skipping just because they might have been in another policy file.\n\n// coverage tool uses different operations on different passes\n// 1st: uses write() to fill the file\n// 2nd-Nth: uses mmap() to update in place\n\nclose: 1\n// fchmod allowed to set libprofile-clang-extras, which wraps `open` calls, to\n// set correct permission for coverage files.\nfchmod: 1\nmkdirat: 1\nmsync: 1\nmunmap: 1\nopenat: 1\nwrite: 1\n\n#if     defined(__LP64__)\nfcntl: 1\nfstat: 1\nftruncate: 1\ngeteuid: 1\nlseek: 1\nmmap: 1\nrt_sigreturn: 1\n#else\nfcntl64: 1\nfstat64: 1\nftruncate64: 1\ngeteuid32: 1\n_llseek: 1\nmmap2: 1\nsigreturn: 1\n#endif\n\n#if     defined(__arm__)\ngettimeofday: 1\n#endif\n\n#if     defined(__i386__)\nmadvise: 1\n#endif\n\n#if     defined(__arm__)\nprctl: 1\n#elif   defined(__aarch64__)\nprctl: 1\n#endif\n\n"
  },
  {
    "path": "code_coverage/seccomp_policy/code_coverage.riscv64.policy",
    "content": "close: 1\nfchmod: 1\nmkdirat: 1\nmsync: 1\nmunmap: 1\nopenat: 1\nwrite: 1\nfcntl: 1\nfstat: 1\nftruncate: 1\ngeteuid: 1\nlseek: 1\nmmap: 1\nrt_sigreturn: 1\nprctl: 1\n"
  },
  {
    "path": "code_coverage/seccomp_policy/code_coverage.x86.policy",
    "content": "close: 1\nfchmod: 1\nmkdirat: 1\nmsync: 1\nmunmap: 1\nopenat: 1\nwrite: 1\nfcntl64: 1\nfstat64: 1\nftruncate64: 1\ngeteuid32: 1\n_llseek: 1\nmmap2: 1\nsigreturn: 1\nmadvise: 1\n"
  },
  {
    "path": "code_coverage/seccomp_policy/code_coverage.x86_64.policy",
    "content": "close: 1\nfchmod: 1\nmkdirat: 1\nmsync: 1\nmunmap: 1\nopenat: 1\nwrite: 1\nfcntl: 1\nfstat: 1\nftruncate: 1\ngeteuid: 1\nlseek: 1\nmmap: 1\nrt_sigreturn: 1\n"
  },
  {
    "path": "code_coverage/seccomp_policy/generate.sh",
    "content": "#!/bin/bash\n\n# generate the arch-specific files from the generic one\n\nset -ex\n\ncd \"$(dirname \"$0\")\"\nCPP='cpp -undef -E -P code_coverage.policy.def'\n$CPP -D__arm__ -o code_coverage.arm.policy\n$CPP -D__aarch64__ -D__LP64__ -o code_coverage.arm64.policy\n$CPP -D__i386__ -o code_coverage.x86.policy\n$CPP -D__x86_64__ -D__LP64__ -o code_coverage.x86_64.policy\n"
  },
  {
    "path": "debuggerd/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"debuggerd_defaults\",\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n        \"-Wno-gcc-compat\",\n        \"-Wno-unused-argument\",\n        \"-Wno-unused-function\",\n        \"-Wno-nullability-completeness\",\n        \"-Wno-reorder-init-list\",\n        \"-Os\",\n        \"-fno-finite-loops\",\n        \"-DANDROID_DEBUGGABLE=0\",\n    ],\n\n    local_include_dirs: [\"include\"],\n    product_variables: {\n        debuggable: {\n            cflags: [\n                \"-UANDROID_DEBUGGABLE\",\n                \"-DANDROID_DEBUGGABLE=1\",\n            ],\n        },\n    },\n}\n\ncc_library_headers {\n    name: \"libdebuggerd_common_headers\",\n    export_include_dirs: [\"common/include\"],\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    apex_available: [\n        \"com.android.runtime\",\n        \"com.android.virt\",\n        \"//apex_available:platform\",\n    ],\n}\n\ncc_library_shared {\n    name: \"libtombstoned_client\",\n    defaults: [\"debuggerd_defaults\"],\n    srcs: [\n        \"tombstoned/tombstoned_client.cpp\",\n        \"util.cpp\",\n    ],\n\n    header_libs: [\"libdebuggerd_common_headers\"],\n\n    static_libs: [\n        \"libasync_safe\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n    ],\n    apex_available: [\n        \"com.android.virt\",\n        \"//apex_available:platform\",\n    ],\n\n    export_header_lib_headers: [\"libdebuggerd_common_headers\"],\n    export_include_dirs: [\"tombstoned/include\"],\n}\n\n// Utility library to talk to tombstoned and get an output fd.\ncc_library_static {\n    name: \"libtombstoned_client_static\",\n    defaults: [\"debuggerd_defaults\"],\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    srcs: [\n        \"tombstoned/tombstoned_client.cpp\",\n        \"util.cpp\",\n    ],\n\n    header_libs: [\"libdebuggerd_common_headers\"],\n\n    whole_static_libs: [\n        \"libasync_safe\",\n        \"libcutils\",\n        \"libbase\",\n    ],\n\n    export_header_lib_headers: [\"libdebuggerd_common_headers\"],\n    export_include_dirs: [\"tombstoned/include\"],\n    apex_available: [\"com.android.runtime\"],\n}\n\n// Core implementation, linked into libdebuggerd_handler and the dynamic linker.\ncc_library_static {\n    name: \"libdebuggerd_handler_core\",\n    defaults: [\"debuggerd_defaults\"],\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    srcs: [\"handler/debuggerd_handler.cpp\"],\n\n    header_libs: [\n        \"libbase_headers\",\n        \"libdebuggerd_common_headers\",\n        \"bionic_libc_platform_headers\",\n        \"gwp_asan_headers\",\n    ],\n\n    whole_static_libs: [\n        \"libasync_safe\",\n        \"libcutils\",\n        \"libdebuggerd\",\n    ],\n\n    export_header_lib_headers: [\"libdebuggerd_common_headers\"],\n    export_include_dirs: [\"include\"],\n    apex_available: [\n        \"com.android.runtime\",\n    ],\n}\n\n// Implementation with a no-op fallback.\ncc_library_static {\n    name: \"libdebuggerd_handler\",\n    defaults: [\"debuggerd_defaults\"],\n    srcs: [\"handler/debuggerd_fallback_nop.cpp\"],\n\n    header_libs: [\"bionic_libc_platform_headers\"],\n    export_header_lib_headers: [\"bionic_libc_platform_headers\"],\n\n    whole_static_libs: [\n        \"libdebuggerd_handler_core\",\n    ],\n\n    export_include_dirs: [\"include\"],\n}\n\n// Fallback implementation, for use in the Bionic linker only.\ncc_library_static {\n    name: \"libdebuggerd_handler_fallback\",\n    visibility: [\"//bionic/linker\"],\n    apex_available: [\n        \"com.android.runtime\",\n        \"//apex_available:platform\",\n    ],\n    defaults: [\"debuggerd_defaults\"],\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    srcs: [\n        \"handler/debuggerd_fallback.cpp\",\n    ],\n\n    whole_static_libs: [\n        \"libdebuggerd_handler_core\",\n        \"libtombstoned_client_static\",\n        \"libasync_safe\",\n        \"libbase\",\n        \"libdebuggerd\",\n        \"libunwindstack_no_dex\",\n        \"liblzma\",\n        \"libcutils\",\n    ],\n\n    header_libs: [\"bionic_libc_platform_headers\"],\n    export_header_lib_headers: [\"bionic_libc_platform_headers\"],\n\n    export_include_dirs: [\"include\"],\n}\n\ncc_library {\n    name: \"libdebuggerd_client\",\n    defaults: [\"debuggerd_defaults\"],\n    srcs: [\n        \"client/debuggerd_client.cpp\",\n        \"util.cpp\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"libprocinfo\",\n    ],\n\n    header_libs: [\n        \"libdebuggerd_common_headers\",\n        \"bionic_libc_platform_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libdebuggerd_common_headers\",\n        \"bionic_libc_platform_headers\",\n    ],\n\n    export_include_dirs: [\"include\"],\n}\n\ncc_library {\n    name: \"libdebuggerd_tombstone_proto_to_text\",\n    defaults: [\"debuggerd_defaults\"],\n    ramdisk_available: true,\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    host_supported: true,\n\n    local_include_dirs: [\"libdebuggerd/include\"],\n    export_include_dirs: [\"libdebuggerd/include\"],\n\n    srcs: [\n        \"libdebuggerd/tombstone_proto_to_text.cpp\",\n        \"libdebuggerd/utility_host.cpp\",\n    ],\n\n    static_libs: [\n        \"libbase\",\n    ],\n\n    whole_static_libs: [\n        \"libtombstone_proto\",\n        \"libprotobuf-cpp-lite\",\n    ],\n\n    shared_libs: [\n        \"liblog\",\n    ],\n\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.runtime\",\n    ],\n}\n\ncc_library_static {\n    name: \"libdebuggerd\",\n    defaults: [\"debuggerd_defaults\"],\n    ramdisk_available: true,\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n\n    srcs: [\n        \"libdebuggerd/backtrace.cpp\",\n        \"libdebuggerd/gwp_asan.cpp\",\n        \"libdebuggerd/open_files_list.cpp\",\n        \"libdebuggerd/scudo.cpp\",\n        \"libdebuggerd/tombstone.cpp\",\n        \"libdebuggerd/tombstone_proto.cpp\",\n        \"libdebuggerd/utility.cpp\",\n    ],\n\n    cflags: [\n        \"-DUSE_SCUDO\",\n    ],\n\n    local_include_dirs: [\"libdebuggerd/include\"],\n    export_include_dirs: [\"libdebuggerd/include\"],\n\n    include_dirs: [\n        // Needed for private/bionic_fdsan.h\n        \"bionic/libc\",\n    ],\n    header_libs: [\n        \"bionic_libc_platform_headers\",\n        \"gwp_asan_headers\",\n        \"liblog_headers\",\n        \"scudo_headers\",\n    ],\n\n    static_libs: [\n        \"libdexfile_support\", // libunwindstack dependency\n        \"libunwindstack\",\n        \"liblzma\",\n        \"libbase\",\n        \"libcutils\",\n    ],\n\n    whole_static_libs: [\n        \"libdebuggerd_tombstone_proto_to_text\",\n        \"libasync_safe\",\n        \"gwp_asan_crash_handler\",\n        \"libtombstone_proto\",\n        \"libprocinfo\",\n        \"libprotobuf-cpp-lite\",\n        \"libscudo\",\n    ],\n\n    target: {\n        recovery: {\n            exclude_static_libs: [\n                \"libdexfile_support\",\n            ],\n            exclude_runtime_libs: [\n                \"libdexfile\",\n            ],\n        },\n        vendor_ramdisk: {\n            exclude_static_libs: [\n                \"libdexfile_support\",\n            ],\n            exclude_runtime_libs: [\n                \"libdexfile\",\n            ],\n        },\n        ramdisk: {\n            exclude_static_libs: [\n                \"libdexfile_support\",\n            ],\n            exclude_runtime_libs: [\n                \"libdexfile\",\n            ],\n        },\n        android: {\n            runtime_libs: [\n                \"libdexfile\", // libdexfile_support dependency\n            ],\n        },\n    },\n\n    product_variables: {\n        debuggable: {\n            cflags: [\"-DROOT_POSSIBLE\"],\n        },\n\n        malloc_low_memory: {\n            cflags: [\"-UUSE_SCUDO\"],\n            exclude_static_libs: [\"libscudo\"],\n        },\n    },\n    apex_available: [\n        \"com.android.runtime\",\n    ],\n}\n\ncc_binary {\n    name: \"pbtombstone\",\n    host_supported: true,\n    defaults: [\"debuggerd_defaults\"],\n    srcs: [\n        \"pbtombstone.cpp\",\n        \"tombstone_symbolize.cpp\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"libdebuggerd_tombstone_proto_to_text\",\n        \"liblog\",\n        \"libprotobuf-cpp-lite\",\n        \"libtombstone_proto\",\n    ],\n}\n\ncc_test_library {\n    name: \"libcrash_test\",\n    defaults: [\"debuggerd_defaults\"],\n    srcs: [\"crash_test.cpp\"],\n}\n\ncc_test {\n    name: \"debuggerd_test\",\n    defaults: [\"debuggerd_defaults\"],\n    require_root: true,\n\n    cflags: [\"-Wno-missing-field-initializers\"],\n    srcs: [\n        \"libdebuggerd/test/dump_memory_test.cpp\",\n        \"libdebuggerd/test/elf_fake.cpp\",\n        \"libdebuggerd/test/log_fake.cpp\",\n        \"libdebuggerd/test/mte_stack_record_test.cpp\",\n        \"libdebuggerd/test/open_files_list_test.cpp\",\n        \"libdebuggerd/test/tombstone_proto_to_text_test.cpp\",\n    ],\n\n    target: {\n        android: {\n            srcs: [\n                \"client/debuggerd_client_test.cpp\",\n                \"debuggerd_test.cpp\",\n            ],\n            static_libs: [\n                \"libasync_safe\",\n                \"libtombstoned_client_static\",\n            ],\n        },\n    },\n\n    sanitize: {\n        memtag_heap: true,\n    },\n\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"libdebuggerd_client\",\n        \"liblog\",\n        \"libnativehelper\",\n        \"libunwindstack\",\n    ],\n\n    static_libs: [\n        \"libdebuggerd\",\n        \"libgmock\",\n        \"libminijail\",\n    ],\n\n    header_libs: [\n        \"bionic_libc_platform_headers\",\n        \"gwp_asan_headers\",\n    ],\n\n    local_include_dirs: [\n        \"libdebuggerd\",\n    ],\n\n    compile_multilib: \"both\",\n    multilib: {\n        lib32: {\n            stem: \"debuggerd_test32\",\n        },\n        lib64: {\n            stem: \"debuggerd_test64\",\n        },\n    },\n\n    data: [\n        \":libcrash_test\",\n    ],\n\n    test_suites: [\"device-tests\"],\n}\n\ncc_benchmark {\n    name: \"debuggerd_benchmark\",\n    defaults: [\"debuggerd_defaults\"],\n    srcs: [\"debuggerd_benchmark.cpp\"],\n    shared_libs: [\n        \"libbase\",\n        \"libdebuggerd_client\",\n    ],\n}\n\ncc_binary {\n    name: \"crash_dump\",\n    srcs: [\n        \"crash_dump.cpp\",\n        \"tombstone_handler.cpp\",\n        \"util.cpp\",\n    ],\n    defaults: [\"debuggerd_defaults\"],\n\n    compile_multilib: \"both\",\n    multilib: {\n        lib32: {\n            suffix: \"32\",\n        },\n        lib64: {\n            suffix: \"64\",\n        },\n    },\n\n    header_libs: [\n        \"bionic_libc_platform_headers\",\n        \"libnative_bridge_support_accessor_headers\",\n    ],\n\n    static_libs: [\n        \"libtombstoned_client_static\",\n        \"libdebuggerd\",\n        \"libcutils\",\n\n        \"libtombstone_proto\",\n        \"libprotobuf-cpp-lite\",\n\n        \"libnative_bridge_guest_state_accessor\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n        \"libprocinfo\",\n        \"libunwindstack\",\n    ],\n\n    apex_available: [\n        \"com.android.runtime\",\n    ],\n\n    // Required for tests.\n    required: [\"crash_dump.policy\"],\n\n    target: {\n        android: {\n            header_libs: [\n                \"libnative_bridge_support_accessor_headers\", // For dlext_namespaces.h\n            ],\n            shared_libs: [\"libdl_android\"], // For android_get_exported_namespace implementation\n        },\n    },\n}\n\ncc_binary {\n    name: \"debuggerd\",\n    srcs: [\n        \"debuggerd.cpp\",\n    ],\n    defaults: [\"debuggerd_defaults\"],\n\n    shared_libs: [\n        \"libbase\",\n        \"libdebuggerd_client\",\n        \"liblog\",\n        \"libprocessgroup\",\n        \"libprocinfo\",\n    ],\n\n    local_include_dirs: [\"include\"],\n}\n\ncc_defaults {\n    name: \"tombstoned_defaults\",\n    srcs: [\n        \"util.cpp\",\n        \"tombstoned/intercept_manager.cpp\",\n        \"tombstoned/tombstoned.cpp\",\n    ],\n    defaults: [\"debuggerd_defaults\"],\n\n    header_libs: [\n        \"bionic_libc_platform_headers\",\n        \"libdebuggerd_common_headers\",\n    ],\n\n    static_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"libevent\",\n        \"liblog\",\n    ],\n}\n\ncc_binary {\n    name: \"tombstoned\",\n    defaults: [\"tombstoned_defaults\"],\n    init_rc: [\"tombstoned/tombstoned.rc\"],\n}\n\ncc_binary {\n    name: \"tombstoned.microdroid\",\n    defaults: [\"tombstoned_defaults\"],\n    init_rc: [\"tombstoned/tombstoned.microdroid.rc\"],\n}\n\nprebuilt_etc {\n    name: \"crash_dump.policy\",\n    sub_dir: \"seccomp_policy\",\n    filename_from_src: true,\n    arch: {\n        arm: {\n            src: \"seccomp_policy/crash_dump.arm.policy\",\n            required: [\n                \"crash_dump.policy_other\",\n            ],\n        },\n        arm64: {\n            src: \"seccomp_policy/crash_dump.arm64.policy\",\n            required: [\n                \"crash_dump.policy_other\",\n            ],\n        },\n        riscv64: {\n            src: \"seccomp_policy/crash_dump.riscv64.policy\",\n        },\n        x86: {\n            src: \"seccomp_policy/crash_dump.x86.policy\",\n            required: [\n                \"crash_dump.policy_other\",\n            ],\n        },\n        x86_64: {\n            src: \"seccomp_policy/crash_dump.x86_64.policy\",\n            required: [\n                \"crash_dump.policy_other\",\n            ],\n        },\n    },\n}\n\n// This installs the \"other\" architecture (so 32-bit on 64-bit device).\nprebuilt_etc {\n    name: \"crash_dump.policy_other\",\n    sub_dir: \"seccomp_policy\",\n    filename_from_src: true,\n    arch: {\n        arm: {\n            src: \"seccomp_policy/crash_dump.arm64.policy\",\n        },\n        arm64: {\n            src: \"seccomp_policy/crash_dump.arm.policy\",\n        },\n        riscv64: {\n            enabled: false,\n        },\n        x86: {\n            src: \"seccomp_policy/crash_dump.x86_64.policy\",\n        },\n        x86_64: {\n            src: \"seccomp_policy/crash_dump.x86.policy\",\n        },\n    },\n}\n"
  },
  {
    "path": "debuggerd/MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "debuggerd/OWNERS",
    "content": "cferris@google.com\n"
  },
  {
    "path": "debuggerd/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"debuggerd_test\"\n    },\n    {\n      \"name\": \"debuggerd_test\",\n      \"keywords\": [\"primary-device\"]\n    },\n    {\n      \"name\": \"libtombstoned_client_rust_test\"\n    },\n    {\n      \"name\": \"MicrodroidHostTestCases\"\n    }\n  ],\n  \"hwasan-presubmit\": [\n    {\n      \"name\": \"debuggerd_test\"\n    }\n  ],\n  \"postsubmit\": [\n    {\n      \"name\": \"CtsCrashDetailHostTestCases\"\n    }\n  ]\n}\n"
  },
  {
    "path": "debuggerd/client/debuggerd_client.cpp",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <debuggerd/client.h>\n\n#include <fcntl.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <sys/poll.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <iomanip>\n\n#include <android-base/cmsg.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <bionic/reserved_signals.h>\n#include <cutils/sockets.h>\n#include <procinfo/process.h>\n\n#include \"debuggerd/handler.h\"\n#include \"protocol.h\"\n#include \"util.h\"\n\nusing namespace std::chrono_literals;\n\nusing android::base::ReadFileToString;\nusing android::base::SendFileDescriptors;\nusing android::base::StringAppendV;\nusing android::base::unique_fd;\nusing android::base::WriteStringToFd;\n\n#define TAG \"libdebuggerd_client: \"\n\n// Log an error both to the log (via LOG(ERROR)) and to the given fd.\nstatic void log_error(int fd, int errno_value, const char* format, ...) __printflike(3, 4) {\n  std::string message(TAG);\n\n  va_list ap;\n  va_start(ap, format);\n  StringAppendV(&message, format, ap);\n  va_end(ap);\n\n  if (errno_value != 0) {\n    message = message + \": \" + strerror(errno_value);\n  }\n\n  if (fd != -1) {\n    dprintf(fd, \"%s\\n\", message.c_str());\n  }\n\n  LOG(ERROR) << message;\n}\n\ntemplate <typename Duration>\nstatic void populate_timeval(struct timeval* tv, const Duration& duration) {\n  auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);\n  auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration - seconds);\n  tv->tv_sec = static_cast<long>(seconds.count());\n  tv->tv_usec = static_cast<long>(microseconds.count());\n}\n\n/**\n * Returns the wchan data for each thread in the process,\n * or empty string if unable to obtain any data.\n */\nstatic std::string get_wchan_data(int fd, pid_t pid) {\n  std::vector<pid_t> tids;\n  if (!android::procinfo::GetProcessTids(pid, &tids)) {\n    log_error(fd, 0, \"failed to get process tids\");\n    return \"\";\n  }\n\n  std::stringstream data;\n  for (int tid : tids) {\n    std::string path = \"/proc/\" + std::to_string(pid) + \"/task/\" + std::to_string(tid) + \"/wchan\";\n    std::string wchan_str;\n    if (!ReadFileToString(path, &wchan_str, true)) {\n      log_error(fd, errno, \"failed to read \\\"%s\\\"\", path.c_str());\n      continue;\n    }\n    data << \"sysTid=\" << std::left << std::setw(10) << tid << wchan_str << \"\\n\";\n  }\n\n  std::stringstream buffer;\n  if (std::string str = data.str(); !str.empty()) {\n    buffer << \"\\n----- Waiting Channels: pid \" << pid << \" at \" << get_timestamp() << \" -----\\n\"\n           << \"Cmd line: \" << android::base::Join(get_command_line(pid), \" \") << \"\\n\";\n    buffer << \"\\n\" << str << \"\\n\";\n    buffer << \"----- end \" << std::to_string(pid) << \" -----\\n\";\n    buffer << \"\\n\";\n  }\n  return buffer.str();\n}\n\nbool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms,\n                            unique_fd output_fd) {\n  if (dump_type == kDebuggerdJavaBacktrace) {\n    // Java dumps always get sent to the tgid, so we need to resolve our tid to a tgid.\n    android::procinfo::ProcessInfo procinfo;\n    std::string error;\n    if (!android::procinfo::GetProcessInfo(tid, &procinfo, &error)) {\n      log_error(output_fd, 0, \"failed to get process info: %s\", error.c_str());\n      return false;\n    }\n    tid = procinfo.pid;\n  }\n\n  LOG(INFO) << TAG \"started dumping process \" << tid;\n\n  // Rather than try to deal with poll() all the way through the flow, we update\n  // the socket timeout between each step (and only use poll() during the final\n  // copy loop).\n  const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);\n  auto update_timeout = [timeout_ms, &output_fd](int sockfd, auto end) {\n    if (timeout_ms <= 0) return true;\n\n    auto remaining = end - std::chrono::steady_clock::now();\n    if (remaining < decltype(remaining)::zero()) {\n      log_error(output_fd, 0, \"timeout expired (update_timeout)\");\n      return false;\n    }\n\n    struct timeval timeout;\n    populate_timeval(&timeout, remaining);\n    if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {\n      log_error(output_fd, errno, \"failed to set receive timeout\");\n      return false;\n    }\n    if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {\n      log_error(output_fd, errno, \"failed to set send timeout\");\n      return false;\n    }\n    return true;\n  };\n\n  unique_fd sockfd(socket(AF_LOCAL, SOCK_SEQPACKET, 0));\n  if (sockfd == -1) {\n    log_error(output_fd, errno, \"failed to create socket\");\n    return false;\n  }\n\n  if (!update_timeout(sockfd, end)) return false;\n\n  if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,\n                                  ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {\n    log_error(output_fd, errno, \"failed to connect to tombstoned\");\n    return false;\n  }\n\n  InterceptRequest req = {\n      .dump_type = dump_type,\n      .pid = tid,\n  };\n\n  // Create an intermediate pipe to pass to the other end.\n  unique_fd pipe_read, pipe_write;\n  if (!Pipe(&pipe_read, &pipe_write)) {\n    log_error(output_fd, errno, \"failed to create pipe\");\n    return false;\n  }\n\n  std::string pipe_size_str;\n  int pipe_buffer_size = 1024 * 1024;\n  if (android::base::ReadFileToString(\"/proc/sys/fs/pipe-max-size\", &pipe_size_str)) {\n    pipe_size_str = android::base::Trim(pipe_size_str);\n\n    if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {\n      LOG(FATAL) << \"failed to parse pipe max size '\" << pipe_size_str << \"'\";\n    }\n  }\n\n  if (fcntl(pipe_read.get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {\n    log_error(output_fd, errno, \"failed to set pipe buffer size\");\n  }\n\n  if (!update_timeout(sockfd, end)) return false;\n  ssize_t rc = SendFileDescriptors(sockfd, &req, sizeof(req), pipe_write.get());\n  pipe_write.reset();\n  if (rc != sizeof(req)) {\n    log_error(output_fd, errno, \"failed to send output fd to tombstoned\");\n    return false;\n  }\n\n  auto get_response = [&output_fd](const char* kind, int sockfd, InterceptResponse* response) {\n    ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd, response, sizeof(*response), MSG_TRUNC));\n    if (rc == 0) {\n      log_error(output_fd, 0, \"failed to read %s response from tombstoned: timeout reached?\", kind);\n      return false;\n    } else if (rc == -1) {\n      log_error(output_fd, errno, \"failed to read %s response from tombstoned\", kind);\n      return false;\n    } else if (rc != sizeof(*response)) {\n      log_error(output_fd, 0,\n                \"received packet of unexpected length from tombstoned while reading %s response: \"\n                \"expected %zd, received %zd\",\n                kind, sizeof(*response), rc);\n      return false;\n    }\n    return true;\n  };\n\n  // Check to make sure we've successfully registered.\n  InterceptResponse response;\n  if (!update_timeout(sockfd, end)) return false;\n  if (!get_response(\"initial\", sockfd, &response)) return false;\n  if (response.status != InterceptStatus::kRegistered) {\n    log_error(output_fd, 0, \"unexpected registration response: %d\",\n              static_cast<int>(response.status));\n    return false;\n  }\n\n  // Send the signal.\n  const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER;\n  sigval val = {.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0};\n  if (sigqueue(tid, signal, val) != 0) {\n    log_error(output_fd, errno, \"failed to send signal to pid %d\", tid);\n    return false;\n  }\n\n  if (!update_timeout(sockfd, end)) return false;\n  if (!get_response(\"status\", sockfd, &response)) return false;\n  if (response.status != InterceptStatus::kStarted) {\n    response.error_message[sizeof(response.error_message) - 1] = '\\0';\n    log_error(output_fd, 0, \"tombstoned reported failure: %s\", response.error_message);\n    return false;\n  }\n\n  // Forward output from the pipe to the output fd.\n  while (true) {\n    auto remaining = end - std::chrono::steady_clock::now();\n    auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count();\n    if (timeout_ms <= 0) {\n      remaining_ms = -1;\n    } else if (remaining_ms < 0) {\n      log_error(output_fd, 0, \"timeout expired before poll\");\n      return false;\n    }\n\n    struct pollfd pfd = {\n        .fd = pipe_read.get(), .events = POLLIN, .revents = 0,\n    };\n\n    rc = poll(&pfd, 1, remaining_ms);\n    if (rc == -1) {\n      if (errno == EINTR) {\n        continue;\n      } else {\n        log_error(output_fd, errno, \"error while polling\");\n        return false;\n      }\n    } else if (rc == 0) {\n      log_error(output_fd, 0, \"poll timeout expired\");\n      return false;\n    }\n\n    // WARNING: It's not possible to replace the below with a splice call.\n    // Due to the way debuggerd does many small writes across the pipe,\n    // this would cause splice to copy a page for each write. The second\n    // pipe fills up based on the number of pages being copied, even\n    // though there is not much data being transferred per page. When\n    // the second pipe is full, everything stops since there is nothing\n    // reading the second pipe to clear it.\n    char buf[1024];\n    rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), buf, sizeof(buf)));\n    if (rc == 0) {\n      // Done.\n      break;\n    } else if (rc == -1) {\n      log_error(output_fd, errno, \"error while reading\");\n      return false;\n    }\n\n    if (!android::base::WriteFully(output_fd.get(), buf, rc)) {\n      log_error(output_fd, errno, \"error while writing\");\n      return false;\n    }\n  }\n\n  LOG(INFO) << TAG \"done dumping process \" << tid;\n\n  return true;\n}\n\nint dump_backtrace_to_file(pid_t tid, DebuggerdDumpType dump_type, int fd) {\n  return dump_backtrace_to_file_timeout(tid, dump_type, 0, fd);\n}\n\nint dump_backtrace_to_file_timeout(pid_t tid, DebuggerdDumpType dump_type, int timeout_secs,\n                                   int fd) {\n  android::base::unique_fd copy(dup(fd));\n  if (copy == -1) {\n    return -1;\n  }\n\n  // debuggerd_trigger_dump results in every thread in the process being interrupted\n  // by a signal, so we need to fetch the wchan data before calling that.\n  std::string wchan_data = get_wchan_data(fd, tid);\n\n  int timeout_ms = timeout_secs > 0 ? timeout_secs * 1000 : 0;\n  int ret = debuggerd_trigger_dump(tid, dump_type, timeout_ms, std::move(copy)) ? 0 : -1;\n\n  // Dump wchan data, since only privileged processes (CAP_SYS_ADMIN) can read\n  // kernel stack traces (/proc/*/stack).\n  if (!WriteStringToFd(wchan_data, fd)) {\n    LOG(WARNING) << TAG \"Failed to dump wchan data for pid: \" << tid;\n  }\n\n  return ret;\n}\n"
  },
  {
    "path": "debuggerd/client/debuggerd_client_test.cpp",
    "content": "/*\n * Copyright 2017, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <debuggerd/client.h>\n\n#include <fcntl.h>\n#include <stdio.h>\n#include <sys/eventfd.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <thread>\n#include <vector>\n\n#include <gtest/gtest.h>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"util.h\"\n\nusing namespace std::chrono_literals;\nusing android::base::unique_fd;\n\nstatic int getThreadCount() {\n  int threadCount = 1024;\n  std::vector<std::string> characteristics =\n      android::base::Split(android::base::GetProperty(\"ro.build.characteristics\", \"\"), \",\");\n  if (std::find(characteristics.begin(), characteristics.end(), \"embedded\")\n      != characteristics.end()) {\n    // 128 is the realistic number for iot devices.\n    threadCount = 128;\n  }\n  return threadCount;\n}\n\nTEST(debuggerd_client, race) {\n  static int THREAD_COUNT = getThreadCount();\n\n  // Semaphore incremented once per thread started.\n  unique_fd barrier(eventfd(0, EFD_SEMAPHORE));\n  ASSERT_NE(-1, barrier.get());\n\n  pid_t forkpid = fork();\n  ASSERT_NE(-1, forkpid);\n  if (forkpid == 0) {\n    // Spawn a bunch of threads, to make crash_dump take longer.\n    std::vector<std::thread> threads;\n    threads.reserve(THREAD_COUNT);\n    for (int i = 0; i < THREAD_COUNT; ++i) {\n      threads.emplace_back([&barrier]() {\n        uint64_t count = 1;\n        ASSERT_NE(-1, write(barrier.get(), &count, sizeof(count)));\n        for (;;) {\n          pause();\n        }\n      });\n    }\n    for (;;) {\n      pause();\n    }\n  }\n\n  // Wait for the child to spawn all of its threads.\n  for (int i = 0; i < THREAD_COUNT; ++i) {\n    uint64_t count;\n    ASSERT_NE(-1, read(barrier.get(), &count, sizeof(count)));\n  }\n\n  unique_fd pipe_read, pipe_write;\n  ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));\n\n  // 16 MiB should be enough for everyone.\n  constexpr int PIPE_SIZE = 16 * 1024 * 1024;\n  ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));\n\n  ASSERT_TRUE(\n      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 60000, std::move(pipe_write)));\n  // Immediately kill the forked child, to make sure that the dump didn't return early.\n  ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);\n\n  // Check the output.\n  std::string result;\n  ASSERT_TRUE(android::base::ReadFdToString(pipe_read.get(), &result));\n\n  // Look for \"----- end <PID> -----\"\n  int found_end = 0;\n\n  std::string expected_end = android::base::StringPrintf(\"----- end %d -----\", forkpid);\n\n  std::vector<std::string> lines = android::base::Split(result, \"\\n\");\n  for (const std::string& line : lines) {\n    if (line == expected_end) {\n      ++found_end;\n    }\n  }\n\n  EXPECT_EQ(1, found_end) << \"\\nOutput: \\n\" << result;\n}\n\nTEST(debuggerd_client, no_timeout) {\n  unique_fd pipe_read, pipe_write;\n  ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));\n\n  pid_t forkpid = fork();\n  ASSERT_NE(-1, forkpid);\n  if (forkpid == 0) {\n    pipe_write.reset();\n    char dummy;\n    TEMP_FAILURE_RETRY(read(pipe_read.get(), &dummy, sizeof(dummy)));\n    exit(0);\n  }\n\n  pipe_read.reset();\n\n  unique_fd output_read, output_write;\n  ASSERT_TRUE(Pipe(&output_read, &output_write));\n  ASSERT_TRUE(\n      debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 0, std::move(output_write)));\n}\n"
  },
  {
    "path": "debuggerd/common/include/dump_type.h",
    "content": "#pragma once\n\n/*\n * Copyright 2017, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/types.h>\n\n#include <ostream>\n\nenum DebuggerdDumpType : uint8_t {\n  kDebuggerdNativeBacktrace,\n  kDebuggerdTombstone,\n  kDebuggerdJavaBacktrace,\n  kDebuggerdAnyIntercept,\n  kDebuggerdTombstoneProto,\n};\n\ninline const char* get_dump_type_name(const DebuggerdDumpType& dump_type) {\n  switch (dump_type) {\n    case kDebuggerdNativeBacktrace:\n      return \"kDebuggerdNativeBacktrace\";\n    case kDebuggerdTombstone:\n      return \"kDebuggerdTombstone\";\n    case kDebuggerdJavaBacktrace:\n      return \"kDebuggerdJavaBacktrace\";\n    case kDebuggerdAnyIntercept:\n      return \"kDebuggerdAnyIntercept\";\n    case kDebuggerdTombstoneProto:\n      return \"kDebuggerdTombstoneProto\";\n    default:\n      return \"[unknown]\";\n  }\n}\n\ninline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) {\n  stream << get_dump_type_name(rhs);\n  return stream;\n}\n"
  },
  {
    "path": "debuggerd/crash_dump.cpp",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <arpa/inet.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <stdlib.h>\n#include <sys/prctl.h>\n#include <sys/ptrace.h>\n#include <sys/types.h>\n#include <sys/un.h>\n#include <sys/user.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#if defined(__i386__)\n#include <asm/ldt.h>\n#endif\n\n#include <cstdint>\n#include <limits>\n#include <map>\n#include <memory>\n#include <set>\n#include <vector>\n\n#include <android-base/errno_restorer.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <bionic/macros.h>\n#include <bionic/reserved_signals.h>\n#include <bionic/tls_defines.h>\n#include <cutils/sockets.h>\n#include <log/log.h>\n#include <private/android_filesystem_config.h>\n#include <procinfo/process.h>\n\n#define ATRACE_TAG ATRACE_TAG_BIONIC\n#include <utils/Trace.h>\n\n#include <unwindstack/AndroidUnwinder.h>\n#include <unwindstack/Error.h>\n#include <unwindstack/MachineArm.h>\n#include <unwindstack/MachineArm64.h>\n#include <unwindstack/MachineRiscv64.h>\n#include <unwindstack/Regs.h>\n#include <unwindstack/RegsArm.h>\n#include <unwindstack/RegsArm64.h>\n#include <unwindstack/RegsRiscv64.h>\n#include <unwindstack/UserArm.h>\n#include <unwindstack/UserArm64.h>\n#include <unwindstack/UserRiscv64.h>\n\n#include <native_bridge_support/guest_state_accessor/accessor.h>\n\n#include \"libdebuggerd/backtrace.h\"\n#include \"libdebuggerd/tombstone.h\"\n#include \"libdebuggerd/utility.h\"\n\n#include \"debuggerd/handler.h\"\n#include \"tombstone_handler.h\"\n\n#include \"protocol.h\"\n#include \"util.h\"\n\nusing android::base::ErrnoRestorer;\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\n\n// This stores guest architecture. When the architecture is supported, tombstone file will output\n// guest state information.\nstatic Architecture g_guest_arch = Architecture::NONE;\n\nstatic bool pid_contains_tid(int pid_proc_fd, pid_t tid) {\n  struct stat st;\n  std::string task_path = StringPrintf(\"task/%d\", tid);\n  return fstatat(pid_proc_fd, task_path.c_str(), &st, 0) == 0;\n}\n\nstatic pid_t get_tracer(pid_t tracee) {\n  // Check to see if the thread is being ptraced by another process.\n  android::procinfo::ProcessInfo process_info;\n  if (android::procinfo::GetProcessInfo(tracee, &process_info)) {\n    return process_info.tracer;\n  }\n  return -1;\n}\n\n// Attach to a thread, and verify that it's still a member of the given process\nstatic bool ptrace_seize_thread(int pid_proc_fd, pid_t tid, std::string* error, int flags = 0) {\n  if (ptrace(PTRACE_SEIZE, tid, 0, flags) != 0) {\n    if (errno == EPERM) {\n      ErrnoRestorer errno_restorer;  // In case get_tracer() fails and we fall through.\n      pid_t tracer_pid = get_tracer(tid);\n      if (tracer_pid > 0) {\n        *error = StringPrintf(\"failed to attach to thread %d, already traced by %d (%s)\", tid,\n                              tracer_pid, get_process_name(tracer_pid).c_str());\n        return false;\n      }\n    }\n\n    *error = StringPrintf(\"failed to attach to thread %d: %s\", tid, strerror(errno));\n    return false;\n  }\n\n  // Make sure that the task we attached to is actually part of the pid we're dumping.\n  if (!pid_contains_tid(pid_proc_fd, tid)) {\n    if (ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {\n      PLOG(WARNING) << \"failed to detach from thread \" << tid;\n    }\n    *error = StringPrintf(\"thread %d is not in process\", tid);\n    return false;\n  }\n\n  return true;\n}\n\nstatic bool wait_for_stop(pid_t tid, int* received_signal) {\n  while (true) {\n    int status;\n    pid_t result = waitpid(tid, &status, __WALL);\n    if (result != tid) {\n      PLOG(ERROR) << \"waitpid failed on \" << tid << \" while detaching\";\n      return false;\n    }\n\n    if (WIFSTOPPED(status)) {\n      if (status >> 16 == PTRACE_EVENT_STOP) {\n        *received_signal = 0;\n      } else {\n        *received_signal = WSTOPSIG(status);\n      }\n      return true;\n    }\n  }\n}\n\n// Interrupt a process and wait for it to be interrupted.\nstatic bool ptrace_interrupt(pid_t tid, int* received_signal) {\n  if (ptrace(PTRACE_INTERRUPT, tid, 0, 0) == 0) {\n    return wait_for_stop(tid, received_signal);\n  }\n\n  PLOG(ERROR) << \"failed to interrupt \" << tid << \" to detach\";\n  return false;\n}\n\nstatic bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data,\n                                    bool recoverable_crash) {\n  ATRACE_CALL();\n  android::base::unique_fd amfd(socket_local_client(\n      \"/data/system/ndebugsocket\", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM));\n  if (amfd.get() == -1) {\n    PLOG(ERROR) << \"unable to connect to activity manager\";\n    return false;\n  }\n\n  struct timeval tv = {\n      .tv_sec = 1 * android::base::HwTimeoutMultiplier(),\n      .tv_usec = 0,\n  };\n  if (setsockopt(amfd.get(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) == -1) {\n    PLOG(ERROR) << \"failed to set send timeout on activity manager socket\";\n    return false;\n  }\n  tv.tv_sec = 3 * android::base::HwTimeoutMultiplier();  // 3 seconds on handshake read\n  if (setsockopt(amfd.get(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1) {\n    PLOG(ERROR) << \"failed to set receive timeout on activity manager socket\";\n    return false;\n  }\n\n  // Activity Manager protocol:\n  //  - 32-bit network-byte-order: pid\n  //  - 32-bit network-byte-order: signal number\n  //  - byte: recoverable_crash\n  //  - bytes: raw text of the dump\n  //  - null terminator\n\n  uint32_t datum = htonl(pid);\n  if (!android::base::WriteFully(amfd, &datum, sizeof(datum))) {\n    PLOG(ERROR) << \"AM pid write failed\";\n    return false;\n  }\n\n  datum = htonl(signal);\n  if (!android::base::WriteFully(amfd, &datum, sizeof(datum))) {\n    PLOG(ERROR) << \"AM signo write failed\";\n    return false;\n  }\n\n  uint8_t recoverable_crash_byte = recoverable_crash ? 1 : 0;\n  if (!android::base::WriteFully(amfd, &recoverable_crash_byte, sizeof(recoverable_crash_byte))) {\n    PLOG(ERROR) << \"AM recoverable_crash_byte write failed\";\n    return false;\n  }\n\n  if (!android::base::WriteFully(amfd, amfd_data.c_str(), amfd_data.size() + 1)) {\n    PLOG(ERROR) << \"AM data write failed\";\n    return false;\n  }\n\n  // 3 sec timeout reading the ack; we're fine if the read fails.\n  char ack;\n  android::base::ReadFully(amfd, &ack, 1);\n  return true;\n}\n\n// Globals used by the abort handler.\nstatic pid_t g_target_thread = -1;\nstatic bool g_tombstoned_connected = false;\nstatic unique_fd g_tombstoned_socket;\nstatic unique_fd g_output_fd;\nstatic unique_fd g_proto_fd;\n\nstatic void DefuseSignalHandlers() {\n  // Don't try to dump ourselves.\n  struct sigaction action = {};\n  action.sa_handler = SIG_DFL;\n  debuggerd_register_handlers(&action);\n\n  sigset_t mask;\n  sigemptyset(&mask);\n  if (sigprocmask(SIG_SETMASK, &mask, nullptr) != 0) {\n    PLOG(FATAL) << \"failed to set signal mask\";\n  }\n}\n\nstatic void Initialize(char** argv) {\n  android::base::InitLogging(argv);\n  android::base::SetAborter([](const char* abort_msg) {\n    // If we abort before we get an output fd, contact tombstoned to let any\n    // potential listeners know that we failed.\n    if (!g_tombstoned_connected) {\n      if (!connect_tombstone_server(g_target_thread, &g_tombstoned_socket, &g_output_fd,\n                                    &g_proto_fd, kDebuggerdAnyIntercept)) {\n        // We failed to connect, not much we can do.\n        LOG(ERROR) << \"failed to connected to tombstoned to report failure\";\n        _exit(1);\n      }\n    }\n\n    dprintf(g_output_fd.get(), \"crash_dump failed to dump process\");\n    if (g_target_thread != 1) {\n      dprintf(g_output_fd.get(), \" %d: %s\\n\", g_target_thread, abort_msg);\n    } else {\n      dprintf(g_output_fd.get(), \": %s\\n\", abort_msg);\n    }\n\n    _exit(1);\n  });\n}\n\nstatic void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdDumpType* dump_type) {\n  if (argc != 4) {\n    LOG(FATAL) << \"wrong number of args: \" << argc << \" (expected 4)\";\n  }\n\n  if (!android::base::ParseInt(argv[1], &g_target_thread, 1, std::numeric_limits<pid_t>::max())) {\n    LOG(FATAL) << \"invalid target tid: \" << argv[1];\n  }\n\n  if (!android::base::ParseInt(argv[2], pseudothread_tid, 1, std::numeric_limits<pid_t>::max())) {\n    LOG(FATAL) << \"invalid pseudothread tid: \" << argv[2];\n  }\n\n  int dump_type_int;\n  if (!android::base::ParseInt(argv[3], &dump_type_int, 0)) {\n    LOG(FATAL) << \"invalid requested dump type: \" << argv[3];\n  }\n\n  *dump_type = static_cast<DebuggerdDumpType>(dump_type_int);\n  switch (*dump_type) {\n    case kDebuggerdNativeBacktrace:\n    case kDebuggerdTombstone:\n    case kDebuggerdTombstoneProto:\n      break;\n\n    default:\n      LOG(FATAL) << \"invalid requested dump type: \" << dump_type_int;\n  }\n}\n\nstatic void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,\n                          std::unique_ptr<unwindstack::Regs>* regs, ProcessInfo* process_info,\n                          bool* recoverable_crash) {\n  std::aligned_storage<sizeof(CrashInfo) + 1, alignof(CrashInfo)>::type buf;\n  CrashInfo* crash_info = reinterpret_cast<CrashInfo*>(&buf);\n  ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf)));\n  *recoverable_crash = false;\n  if (rc == -1) {\n    PLOG(FATAL) << \"failed to read target ucontext\";\n  }\n  ssize_t expected_size = 0;\n  switch (crash_info->header.version) {\n    case 1:\n    case 2:\n    case 3:\n      expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);\n      break;\n\n    case 4:\n      expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);\n      break;\n\n    default:\n      LOG(FATAL) << \"unexpected CrashInfo version: \" << crash_info->header.version;\n      break;\n  }\n\n  if (rc < expected_size) {\n    LOG(FATAL) << \"read \" << rc << \" bytes when reading target crash information, expected \"\n                << expected_size;\n  }\n\n  switch (crash_info->header.version) {\n    case 4:\n      process_info->fdsan_table_address = crash_info->data.d.fdsan_table_address;\n      process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state;\n      process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata;\n      process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot;\n      process_info->scudo_stack_depot_size = crash_info->data.d.scudo_stack_depot_size;\n      process_info->scudo_region_info = crash_info->data.d.scudo_region_info;\n      process_info->scudo_ring_buffer = crash_info->data.d.scudo_ring_buffer;\n      process_info->scudo_ring_buffer_size = crash_info->data.d.scudo_ring_buffer_size;\n      *recoverable_crash = crash_info->data.d.recoverable_crash;\n      process_info->crash_detail_page = crash_info->data.d.crash_detail_page;\n      FALLTHROUGH_INTENDED;\n    case 1:\n    case 2:\n    case 3:\n      process_info->abort_msg_address = crash_info->data.s.abort_msg_address;\n      *siginfo = crash_info->data.s.siginfo;\n      if (signal_has_si_addr(siginfo)) {\n        process_info->has_fault_address = true;\n        process_info->maybe_tagged_fault_address = reinterpret_cast<uintptr_t>(siginfo->si_addr);\n        process_info->untagged_fault_address =\n            untag_address(reinterpret_cast<uintptr_t>(siginfo->si_addr));\n      }\n      regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),\n                                                        &crash_info->data.s.ucontext));\n      break;\n\n    default:\n      __builtin_unreachable();\n  }\n}\n\n// Wait for a process to clone and return the child's pid.\n// Note: this leaves the parent in PTRACE_EVENT_STOP.\nstatic pid_t wait_for_clone(pid_t pid, bool resume_child) {\n  int status;\n  pid_t result = TEMP_FAILURE_RETRY(waitpid(pid, &status, __WALL));\n  if (result == -1) {\n    PLOG(FATAL) << \"failed to waitpid\";\n  }\n\n  if (WIFEXITED(status)) {\n    LOG(FATAL) << \"traced process exited with status \" << WEXITSTATUS(status);\n  } else if (WIFSIGNALED(status)) {\n    LOG(FATAL) << \"traced process exited with signal \" << WTERMSIG(status);\n  } else if (!WIFSTOPPED(status)) {\n    LOG(FATAL) << \"process didn't stop? (status = \" << status << \")\";\n  }\n\n  if (status >> 8 != (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) {\n    LOG(FATAL) << \"process didn't stop due to PTRACE_O_TRACECLONE (status = \" << status << \")\";\n  }\n\n  pid_t child;\n  if (ptrace(PTRACE_GETEVENTMSG, pid, 0, &child) != 0) {\n    PLOG(FATAL) << \"failed to get child pid via PTRACE_GETEVENTMSG\";\n  }\n\n  int stop_signal;\n  if (!wait_for_stop(child, &stop_signal)) {\n    PLOG(FATAL) << \"failed to waitpid on child\";\n  }\n\n  CHECK_EQ(0, stop_signal);\n\n  if (resume_child) {\n    if (ptrace(PTRACE_CONT, child, 0, 0) != 0) {\n      PLOG(FATAL) << \"failed to resume child (pid = \" << child << \")\";\n    }\n  }\n\n  return child;\n}\n\nstatic pid_t wait_for_vm_process(pid_t pseudothread_tid) {\n  // The pseudothread will double-fork, we want its grandchild.\n  pid_t intermediate = wait_for_clone(pseudothread_tid, true);\n  pid_t vm_pid = wait_for_clone(intermediate, false);\n  if (ptrace(PTRACE_DETACH, intermediate, 0, 0) != 0) {\n    PLOG(FATAL) << \"failed to detach from intermediate vm process\";\n  }\n\n  return vm_pid;\n}\n\nstatic void InstallSigPipeHandler() {\n  struct sigaction action = {};\n  action.sa_handler = SIG_IGN;\n  action.sa_flags = SA_RESTART;\n  sigaction(SIGPIPE, &action, nullptr);\n}\n\nstatic bool PtracePeek(int request, pid_t tid, uintptr_t addr, void* data, std::string_view err_msg,\n                       uintptr_t* result) {\n  errno = 0;\n  *result = ptrace(request, tid, addr, data);\n  if (errno != 0) {\n    PLOG(ERROR) << err_msg;\n    return false;\n  }\n  return true;\n}\n\nstatic bool GetGuestRegistersFromCrashedProcess(pid_t tid, NativeBridgeGuestRegs* guest_regs) {\n  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(tid);\n\n  uintptr_t header_ptr = 0;\n  uintptr_t base = 0;\n#if defined(__aarch64__)\n  // base is implicitly casted to uint64_t.\n  struct iovec pt_iov {\n    .iov_base = &base, .iov_len = sizeof(base),\n  };\n\n  if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) != 0) {\n    PLOG(ERROR) << \"failed to read thread register for thread \" << tid;\n    return false;\n  }\n#elif defined(__arm__)\n  // Arm doesn't support any guest architectures yet.\n  return false;\n#elif defined(__i386__)\n  struct user_regs_struct regs;\n  struct iovec pt_iov = {.iov_base = &regs, .iov_len = sizeof(regs)};\n  if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) != 0) {\n    PLOG(ERROR) << \"failed to get registers for thread \" << tid;\n    return false;\n  }\n\n  struct user_desc desc;\n  desc.entry_number = regs.xgs >> 3;\n  if (ptrace(PTRACE_GET_THREAD_AREA, tid, desc.entry_number, &desc) != 0) {\n    PLOG(ERROR) << \"failed to get thread area for thread \" << tid;\n    return false;\n  }\n  base = desc.base_addr;\n#elif defined(__riscv)\n  struct user_regs_struct regs;\n  struct iovec pt_iov = {.iov_base = &regs, .iov_len = sizeof(regs)};\n  if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) != 0) {\n    PLOG(ERROR) << \"failed to read thread register for thread \" << tid;\n    return false;\n  }\n  base = reinterpret_cast<uintptr_t>(regs.tp);\n#elif defined(__x86_64__)\n  if (!PtracePeek(PTRACE_PEEKUSER, tid, offsetof(user_regs_struct, fs_base), nullptr,\n                  \"failed to read thread register for thread \" + std::to_string(tid), &base)) {\n    return false;\n  }\n#else\n  // TODO(b/339287219): Add case for Riscv host.\n  return false;\n#endif\n  auto ptr_to_guest_slot = base + TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE * sizeof(uintptr_t);\n  if (!process_memory->ReadFully(ptr_to_guest_slot, &header_ptr, sizeof(uintptr_t))) {\n    PLOG(ERROR) << \"failed to get guest state TLS slot content for thread \" << tid;\n    return false;\n  }\n\n  NativeBridgeGuestStateHeader header;\n  if (!process_memory->ReadFully(header_ptr, &header, sizeof(NativeBridgeGuestStateHeader)) ||\n      header.signature != NATIVE_BRIDGE_GUEST_STATE_SIGNATURE) {\n    // Return when ptr points to unmapped memory or no valid guest state.\n    return false;\n  }\n\n  auto guest_state_data_copy = std::make_unique<unsigned char[]>(header.guest_state_data_size);\n  if (!process_memory->ReadFully(reinterpret_cast<uintptr_t>(header.guest_state_data),\n                                 guest_state_data_copy.get(), header.guest_state_data_size)) {\n    PLOG(ERROR) << \"failed to read the guest state data for thread \" << tid;\n    return false;\n  }\n\n  LoadGuestStateRegisters(guest_state_data_copy.get(), header.guest_state_data_size, guest_regs);\n  return true;\n}\n\nstatic void ReadGuestRegisters(std::unique_ptr<unwindstack::Regs>* regs, pid_t tid) {\n  NativeBridgeGuestRegs guest_regs;\n  if (!GetGuestRegistersFromCrashedProcess(tid, &guest_regs)) {\n    return;\n  }\n\n  switch (guest_regs.guest_arch) {\n#if defined(__LP64__)\n    case NATIVE_BRIDGE_ARCH_ARM64: {\n      unwindstack::arm64_user_regs arm64_user_regs = {};\n      for (size_t i = 0; i < unwindstack::ARM64_REG_R31; i++) {\n        arm64_user_regs.regs[i] = guest_regs.regs_arm64.x[i];\n      }\n      arm64_user_regs.sp = guest_regs.regs_arm64.sp;\n      arm64_user_regs.pc = guest_regs.regs_arm64.ip;\n      regs->reset(unwindstack::RegsArm64::Read(&arm64_user_regs));\n\n      g_guest_arch = Architecture::ARM64;\n      break;\n    }\n    case NATIVE_BRIDGE_ARCH_RISCV64: {\n      unwindstack::riscv64_user_regs riscv64_user_regs = {};\n      // RISCV64_REG_PC is at the first position.\n      riscv64_user_regs.regs[0] = guest_regs.regs_riscv64.ip;\n      for (size_t i = 1; i < unwindstack::RISCV64_REG_REAL_COUNT; i++) {\n        riscv64_user_regs.regs[i] = guest_regs.regs_riscv64.x[i];\n      }\n      regs->reset(unwindstack::RegsRiscv64::Read(&riscv64_user_regs, tid));\n\n      g_guest_arch = Architecture::RISCV64;\n      break;\n    }\n#else\n    case NATIVE_BRIDGE_ARCH_ARM: {\n      unwindstack::arm_user_regs arm_user_regs = {};\n      for (size_t i = 0; i < unwindstack::ARM_REG_LAST; i++) {\n        arm_user_regs.regs[i] = guest_regs.regs_arm.r[i];\n      }\n      regs->reset(unwindstack::RegsArm::Read(&arm_user_regs));\n\n      g_guest_arch = Architecture::ARM32;\n      break;\n    }\n#endif\n    default:\n      break;\n  }\n}\n\nint main(int argc, char** argv) {\n  DefuseSignalHandlers();\n  InstallSigPipeHandler();\n\n  // There appears to be a bug in the kernel where our death causes SIGHUP to\n  // be sent to our process group if we exit while it has stopped jobs (e.g.\n  // because of wait_for_debugger). Use setsid to create a new process group to\n  // avoid hitting this.\n  setsid();\n\n  atrace_begin(ATRACE_TAG, \"before reparent\");\n  pid_t target_process = getppid();\n\n  // Open /proc/`getppid()` before we daemonize.\n  std::string target_proc_path = \"/proc/\" + std::to_string(target_process);\n  int target_proc_fd = open(target_proc_path.c_str(), O_DIRECTORY | O_RDONLY);\n  if (target_proc_fd == -1) {\n    PLOG(FATAL) << \"failed to open \" << target_proc_path;\n  }\n\n  // Make sure getppid() hasn't changed.\n  if (getppid() != target_process) {\n    LOG(FATAL) << \"parent died\";\n  }\n  atrace_end(ATRACE_TAG);\n\n  // Reparent ourselves to init, so that the signal handler can waitpid on the\n  // original process to avoid leaving a zombie for non-fatal dumps.\n  // Move the input/output pipes off of stdout/stderr, out of paranoia.\n  unique_fd output_pipe(dup(STDOUT_FILENO));\n  unique_fd input_pipe(dup(STDIN_FILENO));\n\n  unique_fd fork_exit_read, fork_exit_write;\n  if (!Pipe(&fork_exit_read, &fork_exit_write)) {\n    PLOG(FATAL) << \"failed to create pipe\";\n  }\n\n  pid_t forkpid = fork();\n  if (forkpid == -1) {\n    PLOG(FATAL) << \"fork failed\";\n  } else if (forkpid == 0) {\n    fork_exit_read.reset();\n  } else {\n    // We need the pseudothread to live until we get around to verifying the vm pid against it.\n    // The last thing it does is block on a waitpid on us, so wait until our child tells us to die.\n    fork_exit_write.reset();\n    char buf;\n    TEMP_FAILURE_RETRY(read(fork_exit_read.get(), &buf, sizeof(buf)));\n    _exit(0);\n  }\n\n  ATRACE_NAME(\"after reparent\");\n  pid_t pseudothread_tid;\n  DebuggerdDumpType dump_type;\n  ProcessInfo process_info;\n\n  Initialize(argv);\n  ParseArgs(argc, argv, &pseudothread_tid, &dump_type);\n\n  // Die if we take too long.\n  //\n  // Note: processes with many threads and minidebug-info can take a bit to\n  //       unwind, do not make this too small. b/62828735\n  alarm(30 * android::base::HwTimeoutMultiplier());\n\n  // Collect the list of open files.\n  OpenFilesList open_files;\n  {\n    ATRACE_NAME(\"open files\");\n    populate_open_files_list(&open_files, g_target_thread);\n  }\n\n  // In order to reduce the duration that we pause the process for, we ptrace\n  // the threads, fetch their registers and associated information, and then\n  // fork a separate process as a snapshot of the process's address space.\n  std::set<pid_t> threads;\n  if (!android::procinfo::GetProcessTids(g_target_thread, &threads)) {\n    PLOG(FATAL) << \"failed to get process threads\";\n  }\n\n  std::map<pid_t, ThreadInfo> thread_info;\n  siginfo_t siginfo;\n  std::string error;\n  bool recoverable_crash = false;\n\n  {\n    ATRACE_NAME(\"ptrace\");\n    for (pid_t thread : threads) {\n      // Trace the pseudothread separately, so we can use different options.\n      if (thread == pseudothread_tid) {\n        continue;\n      }\n\n      if (!ptrace_seize_thread(target_proc_fd, thread, &error)) {\n        bool fatal = thread == g_target_thread;\n        LOG(fatal ? FATAL : WARNING) << error;\n      }\n\n      ThreadInfo info;\n      info.pid = target_process;\n      info.tid = thread;\n      info.uid = getuid();\n      info.thread_name = get_thread_name(thread);\n\n      unique_fd attr_fd(openat(target_proc_fd, \"attr/current\", O_RDONLY | O_CLOEXEC));\n      if (!android::base::ReadFdToString(attr_fd, &info.selinux_label)) {\n        PLOG(WARNING) << \"failed to read selinux label\";\n      }\n\n      if (!ptrace_interrupt(thread, &info.signo)) {\n        PLOG(WARNING) << \"failed to ptrace interrupt thread \" << thread;\n        ptrace(PTRACE_DETACH, thread, 0, 0);\n        continue;\n      }\n\n      struct iovec tagged_addr_iov = {\n          &info.tagged_addr_ctrl,\n          sizeof(info.tagged_addr_ctrl),\n      };\n      if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TAGGED_ADDR_CTRL,\n                 reinterpret_cast<void*>(&tagged_addr_iov)) == -1) {\n        info.tagged_addr_ctrl = -1;\n      }\n\n      struct iovec pac_enabled_keys_iov = {\n          &info.pac_enabled_keys,\n          sizeof(info.pac_enabled_keys),\n      };\n      if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_PAC_ENABLED_KEYS,\n                 reinterpret_cast<void*>(&pac_enabled_keys_iov)) == -1) {\n        info.pac_enabled_keys = -1;\n      }\n\n#if defined(__aarch64__)\n      struct iovec tls_iov = {\n          &info.tls,\n          sizeof(info.tls),\n      };\n      if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TLS, reinterpret_cast<void*>(&tls_iov)) == -1) {\n        info.tls = 0;\n      }\n#endif\n      if (thread == g_target_thread) {\n        // Read the thread's registers along with the rest of the crash info out of the pipe.\n        ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, &recoverable_crash);\n        info.siginfo = &siginfo;\n        info.signo = info.siginfo->si_signo;\n\n        info.command_line = get_command_line(g_target_thread);\n      } else {\n        info.registers.reset(unwindstack::Regs::RemoteGet(thread));\n        if (!info.registers) {\n          PLOG(WARNING) << \"failed to fetch registers for thread \" << thread;\n          ptrace(PTRACE_DETACH, thread, 0, 0);\n          continue;\n        }\n      }\n      ReadGuestRegisters(&info.guest_registers, thread);\n\n      thread_info[thread] = std::move(info);\n    }\n  }\n\n  // Trace the pseudothread with PTRACE_O_TRACECLONE and tell it to fork.\n  if (!ptrace_seize_thread(target_proc_fd, pseudothread_tid, &error, PTRACE_O_TRACECLONE)) {\n    LOG(FATAL) << \"failed to seize pseudothread: \" << error;\n  }\n\n  if (TEMP_FAILURE_RETRY(write(output_pipe.get(), \"\\1\", 1)) != 1) {\n    PLOG(FATAL) << \"failed to write to pseudothread\";\n  }\n\n  pid_t vm_pid = wait_for_vm_process(pseudothread_tid);\n  if (ptrace(PTRACE_DETACH, pseudothread_tid, 0, 0) != 0) {\n    PLOG(FATAL) << \"failed to detach from pseudothread\";\n  }\n\n  // The pseudothread can die now.\n  fork_exit_write.reset();\n\n  // Defer the message until later, for readability.\n  bool wait_for_debugger = android::base::GetBoolProperty(\n      \"debug.debuggerd.wait_for_debugger\",\n      android::base::GetBoolProperty(\"debug.debuggerd.wait_for_gdb\", false));\n  if (siginfo.si_signo == BIONIC_SIGNAL_DEBUGGER) {\n    wait_for_debugger = false;\n  }\n\n  // Detach from all of our attached threads before resuming.\n  for (const auto& [tid, thread] : thread_info) {\n    int resume_signal = thread.signo == BIONIC_SIGNAL_DEBUGGER ? 0 : thread.signo;\n    if (wait_for_debugger) {\n      resume_signal = 0;\n      if (tgkill(target_process, tid, SIGSTOP) != 0) {\n        PLOG(WARNING) << \"failed to send SIGSTOP to \" << tid;\n      }\n    }\n\n    LOG(DEBUG) << \"detaching from thread \" << tid;\n    if (ptrace(PTRACE_DETACH, tid, 0, resume_signal) != 0) {\n      PLOG(ERROR) << \"failed to detach from thread \" << tid;\n    }\n  }\n\n  // Drop our capabilities now that we've fetched all of the information we need.\n  drop_capabilities();\n\n  {\n    ATRACE_NAME(\"tombstoned_connect\");\n    LOG(INFO) << \"obtaining output fd from tombstoned, type: \" << dump_type;\n    g_tombstoned_connected = connect_tombstone_server(g_target_thread, &g_tombstoned_socket,\n                                                      &g_output_fd, &g_proto_fd, dump_type);\n  }\n\n  if (g_tombstoned_connected) {\n    if (TEMP_FAILURE_RETRY(dup2(g_output_fd.get(), STDOUT_FILENO)) == -1) {\n      PLOG(ERROR) << \"failed to dup2 output fd (\" << g_output_fd.get() << \") to STDOUT_FILENO\";\n    }\n  } else {\n    unique_fd devnull(TEMP_FAILURE_RETRY(open(\"/dev/null\", O_RDWR)));\n    TEMP_FAILURE_RETRY(dup2(devnull.get(), STDOUT_FILENO));\n    g_output_fd = std::move(devnull);\n  }\n\n  LOG(INFO) << \"performing dump of process \" << target_process\n            << \" (target tid = \" << g_target_thread << \")\";\n\n  int signo = siginfo.si_signo;\n  bool fatal_signal = signo != BIONIC_SIGNAL_DEBUGGER;\n  bool backtrace = false;\n\n  // si_value is special when used with BIONIC_SIGNAL_DEBUGGER.\n  //   0: dump tombstone\n  //   1: dump backtrace\n  if (!fatal_signal) {\n    int si_val = siginfo.si_value.sival_int;\n    if (si_val == 0) {\n      backtrace = false;\n    } else if (si_val == 1) {\n      backtrace = true;\n    } else {\n      LOG(WARNING) << \"unknown si_value value \" << si_val;\n    }\n  }\n\n  // TODO: Use seccomp to lock ourselves down.\n\n  unwindstack::AndroidRemoteUnwinder unwinder(vm_pid, unwindstack::Regs::CurrentArch());\n  unwindstack::ErrorData error_data;\n  if (!unwinder.Initialize(error_data)) {\n    LOG(FATAL) << \"Failed to initialize unwinder object: \"\n               << unwindstack::GetErrorCodeString(error_data.code);\n  }\n\n  std::string amfd_data;\n  if (backtrace) {\n    ATRACE_NAME(\"dump_backtrace\");\n    dump_backtrace(std::move(g_output_fd), &unwinder, thread_info, g_target_thread);\n  } else {\n    {\n      ATRACE_NAME(\"fdsan table dump\");\n      populate_fdsan_table(&open_files, unwinder.GetProcessMemory(),\n                           process_info.fdsan_table_address);\n    }\n\n    {\n      ATRACE_NAME(\"engrave_tombstone\");\n      unwindstack::ArchEnum regs_arch = unwindstack::ARCH_UNKNOWN;\n      switch (g_guest_arch) {\n        case Architecture::ARM32:\n          regs_arch = unwindstack::ARCH_ARM;\n          break;\n        case Architecture::ARM64:\n          regs_arch = unwindstack::ARCH_ARM64;\n          break;\n        case Architecture::RISCV64:\n          regs_arch = unwindstack::ARCH_RISCV64;\n          break;\n        default:\n          break;\n      }\n      if (regs_arch == unwindstack::ARCH_UNKNOWN) {\n        engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,\n                          g_target_thread, process_info, &open_files, &amfd_data);\n      } else {\n        unwindstack::AndroidRemoteUnwinder guest_unwinder(vm_pid, regs_arch);\n        engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info,\n                          g_target_thread, process_info, &open_files, &amfd_data, &g_guest_arch,\n                          &guest_unwinder);\n      }\n    }\n  }\n\n  if (fatal_signal) {\n    // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout.\n    if (thread_info[target_process].thread_name != \"system_server\") {\n      activity_manager_notify(target_process, signo, amfd_data, recoverable_crash);\n    }\n  }\n\n  if (wait_for_debugger) {\n    // Use ALOGI to line up with output from engrave_tombstone.\n    ALOGI(\n        \"***********************************************************\\n\"\n        \"* Process %d has been suspended while crashing.\\n\"\n        \"* To attach the debugger, run this on the host:\\n\"\n        \"*\\n\"\n        \"*     lldbclient.py -p %d\\n\"\n        \"*\\n\"\n        \"***********************************************************\",\n        target_process, target_process);\n  }\n\n  // Close stdout before we notify tombstoned of completion.\n  close(STDOUT_FILENO);\n  if (g_tombstoned_connected &&\n      !notify_completion(g_tombstoned_socket.get(), g_output_fd.get(), g_proto_fd.get())) {\n    LOG(ERROR) << \"failed to notify tombstoned of completion\";\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "debuggerd/crash_test.cpp",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdint.h>\n\n#include \"crash_test.h\"\n\nextern \"C\" {\n\nJITDescriptor __dex_debug_descriptor = {.version = 1};\n\nvoid crash() {\n  *reinterpret_cast<volatile char*>(0xdead) = '1';\n}\n}\n"
  },
  {
    "path": "debuggerd/crash_test.h",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n// Only support V1 of these structures.\n// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html\n// for information on the JIT Compilation Interface.\n// Also, see libunwindstack/GlobalDebugImpl.h for the full definition of\n// these structures.\nstruct JITCodeEntry {\n  uintptr_t next;\n  uintptr_t prev;\n  uintptr_t symfile_addr;\n  uint64_t symfile_size;\n};\n\nstruct JITDescriptor {\n  uint32_t version;\n  uint32_t action_flag;\n  uintptr_t relevant_entry;\n  uintptr_t first_entry;\n};\n"
  },
  {
    "path": "debuggerd/crasher/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"crasher-defaults\",\n\n    cflags: [\n        \"-W\",\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Wunused\",\n        \"-Werror\",\n        \"-O0\",\n        \"-fstack-protector-all\",\n        \"-Wno-date-time\",\n    ],\n    srcs: [\"crasher.cpp\"],\n    arch: {\n        arm: {\n            srcs: [\"arm/crashglue.S\"],\n        },\n        arm64: {\n            srcs: [\"arm64/crashglue.S\"],\n        },\n        riscv64: {\n            srcs: [\"riscv64/crashglue.S\"],\n        },\n        x86: {\n            srcs: [\"x86/crashglue.S\"],\n        },\n        x86_64: {\n            srcs: [\"x86_64/crashglue.S\"],\n        },\n    },\n    compile_multilib: \"both\",\n}\n\ncc_binary {\n    name: \"crasher\",\n\n    defaults: [\"crasher-defaults\"],\n    header_libs: [\"bionic_libc_platform_headers\"],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libseccomp_policy\",\n    ],\n    multilib: {\n        lib32: {\n            stem: \"crasher\",\n        },\n        lib64: {\n            stem: \"crasher64\",\n        },\n    },\n}\n\ncc_binary {\n    name: \"static_crasher\",\n\n    defaults: [\"crasher-defaults\"],\n    cppflags: [\"-DSTATIC_CRASHER\"],\n    static_executable: true,\n    header_libs: [\"bionic_libc_platform_headers\"],\n    static_libs: [\n        \"libdebuggerd_handler\",\n        \"libbase\",\n        \"liblog\",\n        \"libseccomp_policy\",\n    ],\n    multilib: {\n        lib32: {\n            stem: \"static_crasher\",\n        },\n        lib64: {\n            stem: \"static_crasher64\",\n        },\n    },\n}\n"
  },
  {
    "path": "debuggerd/crasher/arm/crashglue.S",
    "content": "/*\n * Copyright 2006, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.globl crash1\n.type crash1, %function\ncrash1:\n\t.cfi_startproc\n\tpush {lr}\n\t.cfi_def_cfa_offset 4\n\t.cfi_rel_offset lr, 0\n\tldr r0, =0xa5a50000\n\tldr r1, =0xa5a50001\n\tldr r2, =0xa5a50002\n\tldr r3, =0xa5a50003\n\tldr r4, =0xa5a50004\n\tldr r5, =0xa5a50005\n\tldr r6, =0xa5a50006\n\tldr r7, =0xa5a50007\n\tldr r8, =0xa5a50008\n\tldr r9, =0xa5a50009\n\tldr r10, =0xa5a50010\n\tldr r11, =0xa5a50011\n\tldr r12, =0xa5a50012\n\n\tmov lr, #0\n\tldr lr, [lr]\n\tb .\n\t.cfi_endproc\n\t.size crash1, .-crash1\n\n.globl crash_no_stack\n.type crash_no_stack, %function\ncrash_no_stack:\n\t.cfi_startproc\n\tmov r1, sp\n\t.cfi_def_cfa_register r1\n\tmov sp, #0\n\tmov r0, #0\n\tldr r0, [r0]\n\tb .\n\t.cfi_endproc\n\t.size crash_no_stack, .-crash_no_stack\n"
  },
  {
    "path": "debuggerd/crasher/arm64/crashglue.S",
    "content": "/*\n * Copyright 2013, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.globl crash1\n.type crash1, %function\ncrash1:\n\t.cfi_startproc\n\tstp x29, x30, [sp, -16]!\n\t.cfi_def_cfa_offset 16\n\t.cfi_rel_offset x29, 0\n\t.cfi_rel_offset x30, 8\n\tldr x0, =0xa5a50000\n\tldr x1, =0xa5a50001\n\tldr x2, =0xa5a50002\n\tldr x3, =0xa5a50003\n\tldr x4, =0xa5a50004\n\tldr x5, =0xa5a50005\n\tldr x6, =0xa5a50006\n\tldr x7, =0xa5a50007\n\tldr x8, =0xa5a50008\n\tldr x9, =0xa5a50009\n\tldr x10, =0xa5a50010\n\tldr x11, =0xa5a50011\n\tldr x12, =0xa5a50012\n\tldr x13, =0xa5a50013\n\tldr x14, =0xa5a50014\n\tldr x15, =0xa5a50015\n\tldr x16, =0xa5a50016\n\tldr x17, =0xa5a50017\n\tldr x18, =0xa5a50018\n\tldr x19, =0xa5a50019\n\tldr x20, =0xa5a50020\n\tldr x21, =0xa5a50021\n\tldr x22, =0xa5a50022\n\tldr x23, =0xa5a50023\n\tldr x24, =0xa5a50024\n\tldr x25, =0xa5a50025\n\tldr x26, =0xa5a50026\n\tldr x27, =0xa5a50027\n\tldr x28, =0xa5a50028\n\tldr x29, =0xa5a50029\n\n\tmov x30, xzr\n\tldr x30, [x30]\n\tb .\n\t.cfi_endproc\n\t.size crash1, .-crash1\n\n\n.globl crash_no_stack\n.type crash_no_stack, %function\ncrash_no_stack:\n\t.cfi_startproc\n\tmov x1, sp\n\t.cfi_def_cfa_register x1\n\tmov x0, xzr\n\tadd sp, x0, xzr\n\tldr x0, [x0]\n\tb .\n\t.cfi_endproc\n\t.size crash_no_stack, .-crash_no_stack\n\n\n.globl crash_bti\n.type crash_bti, %function\ncrash_bti:\n\t.cfi_startproc\n\tadr x16, 1f\n\tbr x16\n1:\t// Deliberatly not a bti instruction so we crash here.\n\tb .\n\t.cfi_endproc\n\t.size crash_bti, .-crash_bti\n\n\n.globl crash_pac\n.type crash_pac, %function\ncrash_pac:\n\t.cfi_startproc\n\tpaciasp\n\t// Since sp is a pac input, this ensures a mismatch.\n\tsub sp, sp, #16\n\tautiasp\n\tb .\n\t.cfi_endproc\n\t.size crash_pac, .-crash_pac\n\n// Set the PAC and BTI bits for this object file.\n.section .note.gnu.property, \"a\"\n.balign 8\n.long 4\n.long 0x10\n.long 0x5\n.asciz \"GNU\"\n.long 0xc0000000\n.long 4\n.long 0x3\n.long 0\n"
  },
  {
    "path": "debuggerd/crasher/crasher.cpp",
    "content": "/*\n * Copyright 2006, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"crasher\"\n\n#include <assert.h>\n#include <dirent.h>\n#include <errno.h>\n#include <error.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/prctl.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/strings.h>\n\n// We test both kinds of logging.\n#include <android-base/logging.h>\n#include <log/log.h>\n\n#include \"seccomp_policy.h\"\n\n#if defined(STATIC_CRASHER)\n#include \"debuggerd/handler.h\"\n#endif\n\nextern \"C\" void android_set_abort_message(const char* msg);\n\n#if defined(__arm__)\n// See https://www.kernel.org/doc/Documentation/arm/kernel_user_helpers.txt for details.\n#define __kuser_helper_version (*(int32_t*) 0xffff0ffc)\ntypedef void * (__kuser_get_tls_t)(void);\n#define __kuser_get_tls (*(__kuser_get_tls_t*) 0xffff0fe0)\ntypedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);\n#define __kuser_cmpxchg (*(__kuser_cmpxchg_t*) 0xffff0fc0)\ntypedef void (__kuser_dmb_t)(void);\n#define __kuser_dmb (*(__kuser_dmb_t*) 0xffff0fa0)\ntypedef int (__kuser_cmpxchg64_t)(const int64_t*, const int64_t*, volatile int64_t*);\n#define __kuser_cmpxchg64 (*(__kuser_cmpxchg64_t*) 0xffff0f60)\n#endif\n\n#define noinline __attribute__((__noinline__))\n\n// Avoid name mangling so that stacks are more readable.\nextern \"C\" {\n\nvoid crash1();\nvoid crash_no_stack();\nvoid crash_bti();\nvoid crash_pac();\n\nint do_action(const char* arg);\n\nnoinline void maybe_abort() {\n    if (time(0) != 42) {\n        abort();\n    }\n}\n\nchar* smash_stack_dummy_buf;\nnoinline void smash_stack_dummy_function(volatile int* plen) {\n  smash_stack_dummy_buf[*plen] = 0;\n}\n\n// This must be marked with \"__attribute__ ((noinline))\", to ensure the\n// compiler generates the proper stack guards around this function.\n// Assign local array address to global variable to force stack guards.\n// Use another noinline function to corrupt the stack.\nnoinline int smash_stack(volatile int* plen) {\n    printf(\"%s: deliberately corrupting stack...\\n\", getprogname());\n\n    char buf[128];\n    smash_stack_dummy_buf = buf;\n    // This should corrupt stack guards and make process abort.\n    smash_stack_dummy_function(plen);\n    return 0;\n}\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Winfinite-recursion\"\n\nvoid* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.\n\nnoinline void overflow_stack(void* p) {\n    void* buf[1];\n    buf[0] = p;\n    global = buf;\n    overflow_stack(&buf);\n}\n\n#pragma clang diagnostic pop\n\nnoinline void* thread_callback(void* raw_arg) {\n    const char* arg = reinterpret_cast<const char*>(raw_arg);\n    return reinterpret_cast<void*>(static_cast<uintptr_t>(do_action(arg)));\n}\n\nnoinline int do_action_on_thread(const char* arg) {\n    pthread_t t;\n    pthread_create(&t, nullptr, thread_callback, const_cast<char*>(arg));\n    void* result = nullptr;\n    pthread_join(t, &result);\n    return reinterpret_cast<uintptr_t>(result);\n}\n\nnoinline int crash_null() {\n  int (*null_func)() = nullptr;\n  return null_func();\n}\n\nnoinline int crash3(int a) {\n    *reinterpret_cast<int*>(0xdead) = a;\n    return a*4;\n}\n\nnoinline int crash2(int a) {\n    a = crash3(a) + 2;\n    return a*3;\n}\n\nnoinline int crash(int a) {\n    a = crash2(a) + 1;\n    return a*2;\n}\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wfree-nonheap-object\"\n\nnoinline void abuse_heap() {\n    char buf[16];\n    free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.\n}\n#pragma clang diagnostic pop\n\nnoinline void leak() {\n    while (true) {\n        void* mapping =\n            mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n        static_cast<volatile char*>(mapping)[0] = 'a';\n    }\n}\n\nnoinline void sigsegv_non_null() {\n    int* a = (int *)(&do_action);\n    *a = 42;\n}\n\nnoinline void fprintf_null() {\n    FILE* sneaky_null = nullptr;\n    fprintf(sneaky_null, \"oops\");\n}\n\nnoinline void readdir_null() {\n    DIR* sneaky_null = nullptr;\n    readdir(sneaky_null);\n}\n\nnoinline int strlen_null() {\n    char* sneaky_null = nullptr;\n    return strlen(sneaky_null);\n}\n\nstatic int usage() {\n    fprintf(stderr, \"usage: %s KIND\\n\", getprogname());\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"where KIND is:\\n\");\n    fprintf(stderr, \"  smash-stack           overwrite a -fstack-protector guard\\n\");\n    fprintf(stderr, \"  stack-overflow        recurse until the stack overflows\\n\");\n    fprintf(stderr, \"  nostack               crash with a NULL stack pointer\\n\");\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"  heap-usage            cause a libc abort by abusing a heap function\\n\");\n    fprintf(stderr, \"  call-null             cause a crash by calling through a nullptr\\n\");\n    fprintf(stderr, \"  leak                  leak memory until we get OOM-killed\\n\");\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"  abort                 call abort()\\n\");\n    fprintf(stderr, \"  abort_with_msg        call abort() setting an abort message\\n\");\n    fprintf(stderr, \"  abort_with_null_msg   call abort() setting a null abort message\\n\");\n    fprintf(stderr, \"  assert                call assert() without a function\\n\");\n    fprintf(stderr, \"  assert2               call assert() with a function\\n\");\n    fprintf(stderr, \"  exit                  call exit(1)\\n\");\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"  fortify               fail a _FORTIFY_SOURCE check\\n\");\n    fprintf(stderr, \"  fdsan_file            close a file descriptor that's owned by a FILE*\\n\");\n    fprintf(stderr, \"  fdsan_dir             close a file descriptor that's owned by a DIR*\\n\");\n    fprintf(stderr, \"  seccomp               fail a seccomp check\\n\");\n#if defined(__LP64__)\n    fprintf(stderr, \"  xom                   read execute-only memory\\n\");\n#endif\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"  LOG_ALWAYS_FATAL      call liblog LOG_ALWAYS_FATAL\\n\");\n    fprintf(stderr, \"  LOG_ALWAYS_FATAL_IF   call liblog LOG_ALWAYS_FATAL_IF\\n\");\n    fprintf(stderr, \"  LOG-FATAL             call libbase LOG(FATAL)\\n\");\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"  SIGFPE                cause a SIGFPE\\n\");\n    fprintf(stderr, \"  SIGILL                cause a SIGILL\\n\");\n    fprintf(stderr, \"  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\\n\");\n    fprintf(stderr, \"  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\\n\");\n    fprintf(stderr, \"  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\\n\");\n    fprintf(stderr, \"  SIGTRAP               cause a SIGTRAP\\n\");\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"  fprintf-NULL          pass a null pointer to fprintf\\n\");\n    fprintf(stderr, \"  readdir-NULL          pass a null pointer to readdir\\n\");\n    fprintf(stderr, \"  strlen-NULL           pass a null pointer to strlen\\n\");\n    fprintf(stderr, \"  pthread_join-NULL     pass a null pointer to pthread_join\\n\");\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"  no_new_privs          set PR_SET_NO_NEW_PRIVS and then abort\\n\");\n    fprintf(stderr, \"\\n\");\n#if defined(__arm__)\n    fprintf(stderr, \"Also, since this is an arm32 binary:\\n\");\n    fprintf(stderr, \"  kuser_helper_version  call kuser_helper_version\\n\");\n    fprintf(stderr, \"  kuser_get_tls         call kuser_get_tls\\n\");\n    fprintf(stderr, \"  kuser_cmpxchg         call kuser_cmpxchg\\n\");\n    fprintf(stderr, \"  kuser_memory_barrier  call kuser_memory_barrier\\n\");\n    fprintf(stderr, \"  kuser_cmpxchg64       call kuser_cmpxchg64\\n\");\n#endif\n#if defined(__aarch64__)\n    fprintf(stderr, \"Also, since this is an arm64 binary:\\n\");\n    fprintf(stderr, \"  bti                   fail a branch target identification (BTI) check\\n\");\n    fprintf(stderr, \"  pac                   fail a pointer authentication (PAC) check\\n\");\n#endif\n    fprintf(stderr, \"\\n\");\n    fprintf(stderr, \"prefix any of the above with 'thread-' to run on a new thread\\n\");\n    fprintf(stderr, \"prefix any of the above with 'exhaustfd-' to exhaust\\n\");\n    fprintf(stderr, \"all available file descriptors before crashing.\\n\");\n    fprintf(stderr, \"prefix any of the above with 'wait-' to wait until input is received on stdin\\n\");\n\n    return EXIT_FAILURE;\n}\n\n[[maybe_unused]] static void CheckCpuFeature(const std::string& name) {\n    std::string cpuinfo;\n    if (!android::base::ReadFileToString(\"/proc/cpuinfo\", &cpuinfo)) {\n        error(1, errno, \"couldn't read /proc/cpuinfo\");\n    }\n    std::vector<std::string> lines = android::base::Split(cpuinfo, \"\\n\");\n    for (std::string_view line : lines) {\n        if (!android::base::ConsumePrefix(&line, \"Features\\t:\")) continue;\n        std::vector<std::string> features = android::base::Split(std::string(line), \" \");\n        if (std::find(features.begin(), features.end(), name) == features.end()) {\n          error(1, 0, \"/proc/cpuinfo does not report feature '%s'\", name.c_str());\n        }\n    }\n}\n\nnoinline int do_action(const char* arg) {\n    // Prefixes.\n    if (!strncmp(arg, \"wait-\", strlen(\"wait-\"))) {\n      char buf[1];\n      UNUSED(TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf))));\n      return do_action(arg + strlen(\"wait-\"));\n    } else if (!strncmp(arg, \"exhaustfd-\", strlen(\"exhaustfd-\"))) {\n      errno = 0;\n      while (errno != EMFILE) {\n        open(\"/dev/null\", O_RDONLY);\n      }\n      return do_action(arg + strlen(\"exhaustfd-\"));\n    } else if (!strncmp(arg, \"thread-\", strlen(\"thread-\"))) {\n        return do_action_on_thread(arg + strlen(\"thread-\"));\n    }\n\n    // Actions.\n    if (!strcasecmp(arg, \"SIGSEGV-non-null\")) {\n      sigsegv_non_null();\n    } else if (!strcasecmp(arg, \"smash-stack\")) {\n      volatile int len = 128;\n      return smash_stack(&len);\n    } else if (!strcasecmp(arg, \"stack-overflow\")) {\n      overflow_stack(nullptr);\n    } else if (!strcasecmp(arg, \"nostack\")) {\n      crash_no_stack();\n    } else if (!strcasecmp(arg, \"exit\")) {\n      exit(1);\n    } else if (!strcasecmp(arg, \"call-null\")) {\n      return crash_null();\n    } else if (!strcasecmp(arg, \"crash\") || !strcmp(arg, \"SIGSEGV\")) {\n      return crash(42);\n    } else if (!strcasecmp(arg, \"abort\")) {\n      maybe_abort();\n    } else if (!strcasecmp(arg, \"abort_with_msg\")) {\n      android_set_abort_message(\"Aborting due to crasher\");\n      maybe_abort();\n    } else if (!strcasecmp(arg, \"abort_with_null\")) {\n      android_set_abort_message(nullptr);\n      maybe_abort();\n    } else if (!strcasecmp(arg, \"assert\")) {\n      __assert(\"some_file.c\", 123, \"false\");\n    } else if (!strcasecmp(arg, \"assert2\")) {\n      __assert2(\"some_file.c\", 123, \"some_function\", \"false\");\n#if !defined(__clang_analyzer__)\n    } else if (!strcasecmp(arg, \"fortify\")) {\n      // FORTIFY is disabled when running clang-tidy and other tools, so this\n      // shouldn't depend on internal implementation details of it.\n      char buf[10];\n      __read_chk(-1, buf, 32, 10);\n      while (true) pause();\n#endif\n    } else if (!strcasecmp(arg, \"fdsan_file\")) {\n      FILE* f = fopen(\"/dev/null\", \"r\");\n      close(fileno(f));\n    } else if (!strcasecmp(arg, \"fdsan_dir\")) {\n      DIR* d = opendir(\"/dev/\");\n      close(dirfd(d));\n    } else if (!strcasecmp(arg, \"LOG(FATAL)\")) {\n      LOG(FATAL) << \"hello \" << 123;\n    } else if (!strcasecmp(arg, \"LOG_ALWAYS_FATAL\")) {\n      LOG_ALWAYS_FATAL(\"hello %s\", \"world\");\n    } else if (!strcasecmp(arg, \"LOG_ALWAYS_FATAL_IF\")) {\n      LOG_ALWAYS_FATAL_IF(true, \"hello %s\", \"world\");\n    } else if (!strcasecmp(arg, \"SIGFPE\")) {\n      raise(SIGFPE);\n      return EXIT_SUCCESS;\n    } else if (!strcasecmp(arg, \"SIGILL\")) {\n#if defined(__aarch64__)\n      __asm__ volatile(\".word 0\\n\");\n#elif defined(__arm__)\n      __asm__ volatile(\".word 0xe7f0def0\\n\");\n#elif defined(__i386__) || defined(__x86_64__)\n      __asm__ volatile(\"ud2\\n\");\n#elif defined(__riscv)\n      __asm__ volatile(\"unimp\\n\");\n#else\n#error\n#endif\n    } else if (!strcasecmp(arg, \"SIGTRAP\")) {\n      raise(SIGTRAP);\n      return EXIT_SUCCESS;\n    } else if (!strcasecmp(arg, \"fprintf-NULL\")) {\n      fprintf_null();\n    } else if (!strcasecmp(arg, \"readdir-NULL\")) {\n      readdir_null();\n    } else if (!strcasecmp(arg, \"strlen-NULL\")) {\n      return strlen_null();\n    } else if (!strcasecmp(arg, \"pthread_join-NULL\")) {\n      return pthread_join(0, nullptr);\n    } else if (!strcasecmp(arg, \"heap-usage\")) {\n      abuse_heap();\n    } else if (!strcasecmp(arg, \"leak\")) {\n      leak();\n    } else if (!strcasecmp(arg, \"SIGSEGV-unmapped\")) {\n      char* map = reinterpret_cast<char*>(\n          mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));\n      munmap(map, sizeof(int));\n      map[0] = '8';\n    } else if (!strcasecmp(arg, \"seccomp\")) {\n      set_system_seccomp_filter();\n      syscall(99999);\n#if defined(__LP64__)\n    } else if (!strcasecmp(arg, \"xom\")) {\n      // Try to read part of our code, which will fail if XOM is active.\n      printf(\"*%lx = %lx\\n\", reinterpret_cast<long>(usage), *reinterpret_cast<long*>(usage));\n#endif\n#if defined(__arm__)\n    } else if (!strcasecmp(arg, \"kuser_helper_version\")) {\n        return __kuser_helper_version;\n    } else if (!strcasecmp(arg, \"kuser_get_tls\")) {\n        return !__kuser_get_tls();\n    } else if (!strcasecmp(arg, \"kuser_cmpxchg\")) {\n        return __kuser_cmpxchg(0, 0, 0);\n    } else if (!strcasecmp(arg, \"kuser_memory_barrier\")) {\n        __kuser_dmb();\n    } else if (!strcasecmp(arg, \"kuser_cmpxchg64\")) {\n        return __kuser_cmpxchg64(0, 0, 0);\n#endif\n#if defined(__aarch64__)\n    } else if (!strcasecmp(arg, \"bti\")) {\n        CheckCpuFeature(\"bti\");\n        crash_bti();\n    } else if (!strcasecmp(arg, \"pac\")) {\n        CheckCpuFeature(\"paca\");\n        crash_pac();\n#endif\n    } else if (!strcasecmp(arg, \"no_new_privs\")) {\n        if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) {\n          fprintf(stderr, \"prctl(PR_SET_NO_NEW_PRIVS, 1) failed: %s\\n\", strerror(errno));\n          return EXIT_SUCCESS;\n        }\n        abort();\n    } else {\n        return usage();\n    }\n\n    fprintf(stderr, \"%s: exiting normally!\\n\", getprogname());\n    return EXIT_SUCCESS;\n}\n\n}  // extern \"C\"\n\nint main(int argc, char** argv) {\n#if defined(STATIC_CRASHER)\n    debuggerd_callbacks_t callbacks = {\n      .get_process_info = []() {\n        static struct {\n          size_t size;\n          char msg[32];\n        } msg;\n\n        msg.size = strlen(\"dummy abort message\");\n        memcpy(msg.msg, \"dummy abort message\", strlen(\"dummy abort message\"));\n        return debugger_process_info{\n            .abort_msg = reinterpret_cast<void*>(&msg),\n        };\n      },\n      .post_dump = nullptr\n    };\n    debuggerd_init(&callbacks);\n#endif\n\n    if (argc == 1) crash1();\n    else if (argc == 2) return do_action(argv[1]);\n\n    return usage();\n}\n"
  },
  {
    "path": "debuggerd/crasher/riscv64/crashglue.S",
    "content": "/*\n * Copyright 2022, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.globl crash1\ncrash1:\n\t.cfi_startproc\n\taddi sp, sp, -16\n\t.cfi_def_cfa_offset 16\n\tsd ra, 8(sp)\n\t.cfi_offset ra, -8\n\n\tli\tx0,0xa5a50000\n\tli\tx1,0xa5a50001\n\tli\tx2,0xa5a50002\n\tli\tx3,0xa5a50003\n\tli\tx4,0xa5a50004\n\tli\tx5,0xa5a50005\n\tli\tx6,0xa5a50006\n\tli\tx7,0xa5a50007\n\tli\tx8,0xa5a50008\n\tli\tx9,0xa5a50009\n\tli\tx10,0xa5a50010\n\tli\tx11,0xa5a50011\n\tli\tx12,0xa5a50012\n\tli\tx13,0xa5a50013\n\tli\tx14,0xa5a50014\n\tli\tx15,0xa5a50015\n\tli\tx16,0xa5a50016\n\tli\tx17,0xa5a50017\n\tli\tx18,0xa5a50018\n\tli\tx19,0xa5a50019\n\tli\tx20,0xa5a50020\n\tli\tx21,0xa5a50021\n\tli\tx22,0xa5a50022\n\tli\tx23,0xa5a50023\n\tli\tx24,0xa5a50024\n\tli\tx25,0xa5a50025\n\tli\tx26,0xa5a50026\n\tli\tx27,0xa5a50027\n\tli\tx28,0xa5a50028\n\tli\tx29,0xa5a50029\n\tli\tx30,0xa5a50030\n\tli\tx31,0xa5a50031\n\n\tli sp, 0\n\tld t2, 0(zero)\n\tj .\n\t.cfi_endproc\n\t.size crash1, .-crash1\n\n\n.globl crash_no_stack\ncrash_no_stack:\n\t.cfi_startproc\n\tmv t1, sp\n\t.cfi_def_cfa_register t1\n\tli sp, 0\n\tld t2, 0(zero)\n\tj .\n\t.cfi_endproc\n\t.size crash_no_stack, .-crash_no_stack\n"
  },
  {
    "path": "debuggerd/crasher/x86/crashglue.S",
    "content": "/*\n * Copyright 2010, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.globl crash1\ncrash1:\n\tmovl $0xa5a50000, %eax\n\tmovl $0xa5a50001, %ebx\n\tmovl $0xa5a50002, %ecx\n\n\tmovl $0, %edx\n\tjmp *%edx\n\t.size crash1, .-crash1\n\n\n.globl crash_no_stack\ncrash_no_stack:\n\t.cfi_startproc\n\tmovl %esp, %eax\n\t.cfi_def_cfa_register %eax\n\tmovl $0, %esp\n\tmovl (%esp), %ebx\n\t.cfi_endproc\n\t.size crash_no_stack, .-crash_no_stack\n"
  },
  {
    "path": "debuggerd/crasher/x86_64/crashglue.S",
    "content": "/*\n * Copyright 2010, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n.globl crash1\ncrash1:\n\tmovl $0xa5a50000, %eax\n\tmovl $0xa5a50001, %ebx\n\tmovl $0xa5a50002, %ecx\n\n\tmovl $0, %edx\n\tjmp *%rdx\n\t.size crash1, .-crash1\n\n\n.globl crash_no_stack\ncrash_no_stack:\n\t.cfi_startproc\n\tmovq %rsp, %rax\n\t.cfi_def_cfa_register %rax\n\tmovq $0, %rsp\n\tmovq (%rsp), %rbx\n\t.cfi_endproc\n\t.size crash_no_stack, .-crash_no_stack\n"
  },
  {
    "path": "debuggerd/debuggerd.cpp",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <err.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <limits>\n#include <string_view>\n#include <thread>\n\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/unique_fd.h>\n#include <debuggerd/client.h>\n#include <processgroup/processgroup.h>\n#include <procinfo/process.h>\n#include \"util.h\"\n\nusing android::base::unique_fd;\n\nstatic void usage(int exit_code) {\n  fprintf(stderr, \"usage: debuggerd [-bj] PID\\n\");\n  fprintf(stderr, \"\\n\");\n  fprintf(stderr, \"-b, --backtrace    just a backtrace rather than a full tombstone\\n\");\n  fprintf(stderr, \"-j                 collect java traces\\n\");\n  _exit(exit_code);\n}\n\nint main(int argc, char* argv[]) {\n  if (argc <= 1) usage(0);\n  if (argc > 3) usage(1);\n\n  DebuggerdDumpType dump_type = kDebuggerdTombstone;\n\n  if (argc == 3) {\n    std::string_view flag = argv[1];\n    if (flag == \"-b\" || flag == \"--backtrace\") {\n      dump_type = kDebuggerdNativeBacktrace;\n    } else if (flag == \"-j\") {\n      dump_type = kDebuggerdJavaBacktrace;\n    } else {\n      usage(1);\n    }\n  }\n\n  pid_t pid;\n  if (!android::base::ParseInt(argv[argc - 1], &pid, 1, std::numeric_limits<pid_t>::max())) {\n    usage(1);\n  }\n\n  if (getuid() != 0) {\n    errx(1, \"root is required\");\n  }\n\n  // Check to see if the process exists and that we can actually send a signal to it.\n  android::procinfo::ProcessInfo proc_info;\n  if (!android::procinfo::GetProcessInfo(pid, &proc_info)) {\n    err(1, \"failed to fetch info for process %d\", pid);\n  }\n\n  if (proc_info.state == android::procinfo::kProcessStateZombie) {\n    errx(1, \"process %d is a zombie\", pid);\n  }\n\n  // Send a signal to the main thread pid, not a side thread. The signal\n  // handler always sets the crashing tid to the main thread pid when sent this\n  // signal. This is to avoid a problem where the signal is sent to a process,\n  // but happens on a side thread and the intercept mismatches since it\n  // is looking for the main thread pid, not the tid of this random thread.\n  // See b/194346289 for extra details.\n  if (kill(proc_info.pid, 0) != 0) {\n    if (pid == proc_info.pid) {\n      err(1, \"cannot send signal to process %d\", pid);\n    } else {\n      err(1, \"cannot send signal to main thread %d (requested thread %d)\", proc_info.pid, pid);\n    }\n  }\n\n  // unfreeze if pid is frozen.\n  SetProcessProfiles(proc_info.uid, proc_info.pid, {\"Unfrozen\"});\n  // we don't restore the frozen state as this is considered a benign change.\n\n  unique_fd output_fd(fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0));\n  if (output_fd.get() == -1) {\n    err(1, \"failed to fcntl dup stdout\");\n  }\n  if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(output_fd))) {\n    if (pid == proc_info.pid) {\n      errx(1, \"failed to dump process %d\", pid);\n    } else {\n      errx(1, \"failed to dump main thread %d (requested thread %d)\", proc_info.pid, pid);\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "debuggerd/debuggerd_benchmark.cpp",
    "content": "/*\n * Copyright 2017, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <err.h>\n#include <errno.h>\n#include <sched.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <thread>\n\n#include <benchmark/benchmark.h>\n#include <debuggerd/client.h>\n\nusing namespace std::chrono_literals;\n\nstatic_assert(std::chrono::high_resolution_clock::is_steady);\n\nenum class ThreadState { Starting, Started, Stopping };\n\nstatic void SetScheduler() {\n  struct sched_param param {\n    .sched_priority = 1,\n  };\n\n  if (sched_setscheduler(getpid(), SCHED_FIFO, &param) != 0) {\n    fprintf(stderr, \"failed to set scheduler to SCHED_FIFO: %s\", strerror(errno));\n  }\n}\n\nstatic std::chrono::duration<double> GetMaximumPause(std::atomic<ThreadState>& state) {\n  std::chrono::duration<double> max_diff(0);\n\n  const auto begin = std::chrono::high_resolution_clock::now();\n  auto last = begin;\n  state.store(ThreadState::Started);\n  while (state.load() != ThreadState::Stopping) {\n    auto now = std::chrono::high_resolution_clock::now();\n\n    auto diff = now - last;\n    if (diff > max_diff) {\n      max_diff = diff;\n    }\n\n    last = now;\n  }\n\n  return max_diff;\n}\n\nstatic void PerformDump() {\n  pid_t target = getpid();\n  pid_t forkpid = fork();\n  if (forkpid == -1) {\n    err(1, \"fork failed\");\n  } else if (forkpid != 0) {\n    int status;\n    pid_t pid = waitpid(forkpid, &status, 0);\n    if (pid == -1) {\n      err(1, \"waitpid failed\");\n    } else if (!WIFEXITED(status)) {\n      err(1, \"child didn't exit\");\n    } else if (WEXITSTATUS(status) != 0) {\n      errx(1, \"child exited with non-zero status %d\", WEXITSTATUS(status));\n    }\n  } else {\n    android::base::unique_fd output_fd(open(\"/dev/null\", O_WRONLY | O_CLOEXEC));\n    if (output_fd == -1) {\n      err(1, \"failed to open /dev/null\");\n    }\n\n    if (!debuggerd_trigger_dump(target, kDebuggerdNativeBacktrace, 1000, std::move(output_fd))) {\n      errx(1, \"failed to trigger dump\");\n    }\n\n    _exit(0);\n  }\n}\n\ntemplate <typename Fn>\nstatic void BM_maximum_pause_impl(benchmark::State& state, const Fn& function) {\n  SetScheduler();\n\n  for (auto _ : state) {\n    std::chrono::duration<double> max_pause;\n    std::atomic<ThreadState> thread_state(ThreadState::Starting);\n    auto thread = std::thread([&]() { max_pause = GetMaximumPause(thread_state); });\n\n    while (thread_state != ThreadState::Started) {\n      std::this_thread::sleep_for(1ms);\n    }\n\n    function();\n\n    thread_state = ThreadState::Stopping;\n    thread.join();\n\n    state.SetIterationTime(max_pause.count());\n  }\n}\n\nstatic void BM_maximum_pause_noop(benchmark::State& state) {\n  BM_maximum_pause_impl(state, []() {});\n}\n\nstatic void BM_maximum_pause_debuggerd(benchmark::State& state) {\n  BM_maximum_pause_impl(state, []() { PerformDump(); });\n}\n\nBENCHMARK(BM_maximum_pause_noop)->Iterations(128)->UseManualTime();\nBENCHMARK(BM_maximum_pause_debuggerd)->Iterations(128)->UseManualTime();\n\nBENCHMARK_MAIN();\n"
  },
  {
    "path": "debuggerd/debuggerd_test.cpp",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <dirent.h>\n#include <dlfcn.h>\n#include <err.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <linux/prctl.h>\n#include <malloc.h>\n#include <pthread.h>\n#include <setjmp.h>\n#include <stdlib.h>\n#include <sys/capability.h>\n#include <sys/mman.h>\n#include <sys/prctl.h>\n#include <sys/ptrace.h>\n#include <sys/resource.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <regex>\n#include <set>\n#include <string>\n#include <thread>\n\n#include <android/crash_detail.h>\n#include <android/dlext.h>\n#include <android/fdsan.h>\n#include <android/set_abort_message.h>\n#include <bionic/malloc.h>\n#include <bionic/mte.h>\n#include <bionic/reserved_signals.h>\n\n#include <android-base/cmsg.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/test_utils.h>\n#include <android-base/unique_fd.h>\n#include <cutils/sockets.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n\n#include <unwindstack/Elf.h>\n#include <unwindstack/Memory.h>\n\n#include <libminijail.h>\n#include <scoped_minijail.h>\n\n#include \"crash_test.h\"\n#include \"debuggerd/handler.h\"\n#include \"gtest/gtest.h\"\n#include \"libdebuggerd/utility_host.h\"\n#include \"protocol.h\"\n#include \"tombstoned/tombstoned.h\"\n#include \"util.h\"\n\nusing namespace std::chrono_literals;\n\nusing android::base::SendFileDescriptors;\nusing android::base::unique_fd;\nusing ::testing::HasSubstr;\n\n#if defined(__LP64__)\n#define ARCH_SUFFIX \"64\"\n#else\n#define ARCH_SUFFIX \"\"\n#endif\n\nconstexpr char kWaitForDebuggerKey[] = \"debug.debuggerd.wait_for_debugger\";\n\n#define TIMEOUT(seconds, expr)                                     \\\n  [&]() {                                                          \\\n    struct sigaction old_sigaction;                                \\\n    struct sigaction new_sigaction = {};                           \\\n    new_sigaction.sa_handler = [](int) {};                         \\\n    if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \\\n      err(1, \"sigaction failed\");                                  \\\n    }                                                              \\\n    alarm(seconds * android::base::HwTimeoutMultiplier());         \\\n    auto value = expr;                                             \\\n    int saved_errno = errno;                                       \\\n    if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) {        \\\n      err(1, \"sigaction failed\");                                  \\\n    }                                                              \\\n    alarm(0);                                                      \\\n    errno = saved_errno;                                           \\\n    return value;                                                  \\\n  }()\n\n// Backtrace frame dump could contain:\n//   #01 pc 0001cded  /data/tmp/debuggerd_test32 (raise_debugger_signal+80)\n// or\n//   #01 pc 00022a09  /data/tmp/debuggerd_test32 (offset 0x12000) (raise_debugger_signal+80)\n#define ASSERT_BACKTRACE_FRAME(result, frame_name) \\\n  ASSERT_MATCH(result,                             \\\n               R\"(#\\d\\d pc [0-9a-f]+\\s+ \\S+ (\\(offset 0x[0-9a-f]+\\) )?\\()\" frame_name R\"(\\+)\");\n\nstatic void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd,\n                                 InterceptResponse* response, DebuggerdDumpType intercept_type) {\n  intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,\n                                          ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));\n  if (intercept_fd->get() == -1) {\n    FAIL() << \"failed to contact tombstoned: \" << strerror(errno);\n  }\n\n  InterceptRequest req = {\n      .dump_type = intercept_type,\n      .pid = target_pid,\n  };\n\n  unique_fd output_pipe_write;\n  if (!Pipe(output_fd, &output_pipe_write)) {\n    FAIL() << \"failed to create output pipe: \" << strerror(errno);\n  }\n\n  std::string pipe_size_str;\n  int pipe_buffer_size;\n  if (!android::base::ReadFileToString(\"/proc/sys/fs/pipe-max-size\", &pipe_size_str)) {\n    FAIL() << \"failed to read /proc/sys/fs/pipe-max-size: \" << strerror(errno);\n  }\n\n  pipe_size_str = android::base::Trim(pipe_size_str);\n\n  if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {\n    FAIL() << \"failed to parse pipe max size\";\n  }\n\n  if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {\n    FAIL() << \"failed to set pipe size: \" << strerror(errno);\n  }\n\n  ASSERT_GE(pipe_buffer_size, 1024 * 1024);\n\n  ssize_t rc = SendFileDescriptors(intercept_fd->get(), &req, sizeof(req), output_pipe_write.get());\n  output_pipe_write.reset();\n  if (rc != sizeof(req)) {\n    FAIL() << \"failed to send output fd to tombstoned: \" << strerror(errno);\n  }\n\n  rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), response, sizeof(*response)));\n  if (rc == -1) {\n    FAIL() << \"failed to read response from tombstoned: \" << strerror(errno);\n  } else if (rc == 0) {\n    FAIL() << \"failed to read response from tombstoned (EOF)\";\n  } else if (rc != sizeof(*response)) {\n    FAIL() << \"received packet of unexpected length from tombstoned: expected \" << sizeof(*response)\n           << \", received \" << rc;\n  }\n}\n\nstatic bool pac_supported() {\n#if defined(__aarch64__)\n  return getauxval(AT_HWCAP) & HWCAP_PACA;\n#else\n  return false;\n#endif\n}\n\nclass CrasherTest : public ::testing::Test {\n public:\n  pid_t crasher_pid = -1;\n  bool previous_wait_for_debugger;\n  unique_fd crasher_pipe;\n  unique_fd intercept_fd;\n\n  CrasherTest();\n  ~CrasherTest();\n\n  void StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type = kDebuggerdTombstone);\n\n  // Returns -1 if we fail to read a response from tombstoned, otherwise the received return code.\n  void FinishIntercept(int* result);\n\n  void StartProcess(std::function<void()> function, std::function<pid_t()> forker = fork);\n  void StartCrasher(const std::string& crash_type);\n  void FinishCrasher();\n  void AssertDeath(int signo);\n\n  static void Trap(void* ptr);\n};\n\nCrasherTest::CrasherTest() {\n  previous_wait_for_debugger = android::base::GetBoolProperty(kWaitForDebuggerKey, false);\n  android::base::SetProperty(kWaitForDebuggerKey, \"0\");\n\n  // Clear the old property too, just in case someone's been using it\n  // on this device. (We only document the new name, but we still support\n  // the old name so we don't break anyone's existing setups.)\n  android::base::SetProperty(\"debug.debuggerd.wait_for_gdb\", \"0\");\n}\n\nCrasherTest::~CrasherTest() {\n  if (crasher_pid != -1) {\n    kill(crasher_pid, SIGKILL);\n    int status;\n    TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, WUNTRACED));\n  }\n\n  android::base::SetProperty(kWaitForDebuggerKey, previous_wait_for_debugger ? \"1\" : \"0\");\n}\n\nvoid CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType intercept_type) {\n  if (crasher_pid == -1) {\n    FAIL() << \"crasher hasn't been started\";\n  }\n\n  InterceptResponse response = {};\n  tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &response, intercept_type);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n}\n\nvoid CrasherTest::FinishIntercept(int* result) {\n  InterceptResponse response;\n\n  ssize_t rc = TIMEOUT(30, read(intercept_fd.get(), &response, sizeof(response)));\n  if (rc == -1) {\n    FAIL() << \"failed to read response from tombstoned: \" << strerror(errno);\n  } else if (rc == 0) {\n    *result = -1;\n  } else if (rc != sizeof(response)) {\n    FAIL() << \"received packet of unexpected length from tombstoned: expected \" << sizeof(response)\n           << \", received \" << rc;\n  } else {\n    *result = response.status == InterceptStatus::kStarted ? 1 : 0;\n  }\n}\n\nvoid CrasherTest::StartProcess(std::function<void()> function, std::function<pid_t()> forker) {\n  unique_fd read_pipe;\n  unique_fd crasher_read_pipe;\n  if (!Pipe(&crasher_read_pipe, &crasher_pipe)) {\n    FAIL() << \"failed to create pipe: \" << strerror(errno);\n  }\n\n  crasher_pid = forker();\n  if (crasher_pid == -1) {\n    FAIL() << \"fork failed: \" << strerror(errno);\n  } else if (crasher_pid == 0) {\n    char dummy;\n    crasher_pipe.reset();\n    TEMP_FAILURE_RETRY(read(crasher_read_pipe.get(), &dummy, 1));\n    function();\n    _exit(0);\n  }\n}\n\nvoid CrasherTest::FinishCrasher() {\n  if (crasher_pipe == -1) {\n    FAIL() << \"crasher pipe uninitialized\";\n  }\n\n  ssize_t rc = TEMP_FAILURE_RETRY(write(crasher_pipe.get(), \"\\n\", 1));\n  if (rc == -1) {\n    FAIL() << \"failed to write to crasher pipe: \" << strerror(errno);\n  } else if (rc == 0) {\n    FAIL() << \"crasher pipe was closed\";\n  }\n}\n\nvoid CrasherTest::AssertDeath(int signo) {\n  int status;\n  pid_t pid = TIMEOUT(30, waitpid(crasher_pid, &status, 0));\n  if (pid != crasher_pid) {\n    printf(\"failed to wait for crasher (expected pid %d, return value %d): %s\\n\", crasher_pid, pid,\n           strerror(errno));\n    sleep(100);\n    FAIL() << \"failed to wait for crasher: \" << strerror(errno);\n  }\n\n  if (signo == 0) {\n    ASSERT_TRUE(WIFEXITED(status)) << \"Terminated due to unexpected signal \" << WTERMSIG(status);\n    ASSERT_EQ(0, WEXITSTATUS(signo));\n  } else {\n    ASSERT_FALSE(WIFEXITED(status));\n    ASSERT_TRUE(WIFSIGNALED(status)) << \"crasher didn't terminate via a signal\";\n    ASSERT_EQ(signo, WTERMSIG(status));\n  }\n  crasher_pid = -1;\n}\n\nstatic void ConsumeFd(unique_fd fd, std::string* output) {\n  ASSERT_TRUE(android::base::ReadFdToString(fd, output));\n}\n\nclass LogcatCollector {\n public:\n  LogcatCollector() { system(\"logcat -c\"); }\n\n  void Collect(std::string* output) {\n    FILE* cmd_stdout = popen(\"logcat -d '*:S DEBUG'\", \"r\");\n    ASSERT_NE(cmd_stdout, nullptr);\n    unique_fd tmp_fd(TEMP_FAILURE_RETRY(dup(fileno(cmd_stdout))));\n    ConsumeFd(std::move(tmp_fd), output);\n    pclose(cmd_stdout);\n  }\n};\n\nTEST_F(CrasherTest, smoke) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    *reinterpret_cast<volatile char*>(0xdead) = '1';\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\), code 1 \\(SEGV_MAPERR\\), fault addr 0x0+dead)\");\n\n  if (mte_supported() && mte_enabled()) {\n    // Test that the default TAGGED_ADDR_CTRL value is set.\n    ASSERT_MATCH(result, R\"(tagged_addr_ctrl: 000000000007fff3)\"\n                         R\"( \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe\\))\");\n  }\n\n  if (pac_supported()) {\n    // Test that the default PAC_ENABLED_KEYS value is set.\n    ASSERT_MATCH(result, R\"(pac_enabled_keys: 000000000000000f)\"\n                         R\"( \\(PR_PAC_APIAKEY, PR_PAC_APIBKEY, PR_PAC_APDAKEY, PR_PAC_APDBKEY\\))\");\n  }\n}\n\nTEST_F(CrasherTest, tagged_fault_addr) {\n#if !defined(__aarch64__)\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n  // HWASan crashes with SIGABRT on tag mismatch.\n  SKIP_WITH_HWASAN;\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    *reinterpret_cast<volatile char*>(0x100000000000dead) = '1';\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  // The address can either be tagged (new kernels) or untagged (old kernels).\n  ASSERT_MATCH(\n      result, R\"(signal 11 \\(SIGSEGV\\), code 1 \\(SEGV_MAPERR\\), fault addr 0x[01]00000000000dead)\");\n}\n\nvoid CrasherTest::Trap(void* ptr) {\n  void (*volatile f)(void*) = nullptr;\n  __asm__ __volatile__(\"\" : : \"r\"(f) : \"memory\");\n  f(ptr);\n}\n\nTEST_F(CrasherTest, heap_addr_in_register) {\n#if defined(__i386__)\n  GTEST_SKIP() << \"architecture does not pass arguments in registers\";\n#endif\n  // The memory dump in HWASan crashes sadly shows the memory near the registers\n  // in the HWASan dump function, rather the faulting context. This is a known\n  // issue.\n  SKIP_WITH_HWASAN;\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    // Crash with a heap pointer in the first argument register.\n    Trap(malloc(1));\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  int status;\n  ASSERT_EQ(crasher_pid, TIMEOUT(30, waitpid(crasher_pid, &status, 0)));\n  ASSERT_TRUE(WIFSIGNALED(status)) << \"crasher didn't terminate via a signal\";\n  // Don't test the signal number because different architectures use different signals for\n  // __builtin_trap().\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n#if defined(__aarch64__)\n  ASSERT_MATCH(result, \"memory near x0 \\\\(\\\\[anon:\");\n#elif defined(__arm__)\n  ASSERT_MATCH(result, \"memory near r0 \\\\(\\\\[anon:\");\n#elif defined(__riscv)\n  ASSERT_MATCH(result, \"memory near a0 \\\\(\\\\[anon:\");\n#elif defined(__x86_64__)\n  ASSERT_MATCH(result, \"memory near rdi \\\\(\\\\[anon:\");\n#else\n  ASSERT_TRUE(false) << \"unsupported architecture\";\n#endif\n}\n\n#if defined(__aarch64__)\nstatic void SetTagCheckingLevelSync() {\n  if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_SYNC) == 0) {\n    abort();\n  }\n}\n\nstatic void SetTagCheckingLevelAsync() {\n  if (mallopt(M_BIONIC_SET_HEAP_TAGGING_LEVEL, M_HEAP_TAGGING_LEVEL_ASYNC) == 0) {\n    abort();\n  }\n}\n#endif\n\nstruct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};\n\nINSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(0, 16, 131072));\n\nTEST_P(SizeParamCrasherTest, mte_uaf) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  // Any UAF on a zero-sized allocation will be out-of-bounds so it won't be reported.\n  if (GetParam() == 0) {\n    return;\n  }\n\n  LogcatCollector logcat_collector;\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    volatile int* p = (volatile int*)malloc(GetParam());\n    free((void *)p);\n    p[0] = 42;\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::vector<std::string> log_sources(2);\n  ConsumeFd(std::move(output_fd), &log_sources[0]);\n  logcat_collector.Collect(&log_sources[1]);\n  // Tag dump only available in the tombstone, not logcat.\n  ASSERT_MATCH(log_sources[0], \"Memory tags around the fault address\");\n\n  for (const auto& result : log_sources) {\n    ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\))\");\n    ASSERT_MATCH(result, R\"(Cause: \\[MTE\\]: Use After Free, 0 bytes into a )\" +\n                             std::to_string(GetParam()) + R\"(-byte allocation)\");\n    ASSERT_MATCH(result, R\"(deallocated by thread .*?\\n.*#00 pc)\");\n    ASSERT_MATCH(result, R\"((^|\\s)allocated by thread .*?\\n.*#00 pc)\");\n  }\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_P(SizeParamCrasherTest, mte_oob_uaf) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    volatile int* p = (volatile int*)malloc(GetParam());\n    free((void *)p);\n    p[-1] = 42;\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\))\");\n  ASSERT_NOT_MATCH(result, R\"(Cause: \\[MTE\\]: Use After Free, 4 bytes left)\");\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_P(SizeParamCrasherTest, mte_overflow) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  LogcatCollector logcat_collector;\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    volatile char* p = (volatile char*)malloc(GetParam());\n    p[GetParam()] = 42;\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::vector<std::string> log_sources(2);\n  ConsumeFd(std::move(output_fd), &log_sources[0]);\n  logcat_collector.Collect(&log_sources[1]);\n\n  // Tag dump only in tombstone, not logcat, and tagging is not used for\n  // overflow protection in the scudo secondary (guard pages are used instead).\n  if (GetParam() < 0x10000) {\n    ASSERT_MATCH(log_sources[0], \"Memory tags around the fault address\");\n  }\n\n  for (const auto& result : log_sources) {\n    ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\))\");\n    ASSERT_MATCH(result, R\"(Cause: \\[MTE\\]: Buffer Overflow, 0 bytes right of a )\" +\n                             std::to_string(GetParam()) + R\"(-byte allocation)\");\n    ASSERT_MATCH(result, R\"((^|\\s)allocated by thread .*?\\n.*#00 pc)\");\n  }\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_P(SizeParamCrasherTest, mte_underflow) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    volatile int* p = (volatile int*)malloc(GetParam());\n    p[-1] = 42;\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\), code 9 \\(SEGV_MTESERR\\))\");\n  ASSERT_MATCH(result, R\"(Cause: \\[MTE\\]: Buffer Underflow, 4 bytes left of a )\" +\n                           std::to_string(GetParam()) + R\"(-byte allocation)\");\n  ASSERT_MATCH(result, R\"((^|\\s)allocated by thread .*\n      #00 pc)\");\n  ASSERT_MATCH(result, \"Memory tags around the fault address\");\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\n__attribute__((noinline)) void mte_illegal_setjmp_helper(jmp_buf& jump_buf) {\n  // This frame is at least 8 bytes for storing and restoring the LR before the\n  // setjmp below. So this can never get an empty stack frame, even if we omit\n  // the frame pointer. So, the SP of this is always less (numerically) than the\n  // calling function frame.\n  setjmp(jump_buf);\n}\n\nTEST_F(CrasherTest, DISABLED_mte_illegal_setjmp) {\n  // This setjmp is illegal because it jumps back into a function that already returned.\n  // Quoting man 3 setjmp:\n  //     If the function which called setjmp() returns before longjmp() is\n  //     called, the behavior is undefined.  Some kind of subtle or\n  //     unsubtle chaos is sure to result.\n  // https://man7.org/linux/man-pages/man3/longjmp.3.html\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    jmp_buf jump_buf;\n    mte_illegal_setjmp_helper(jump_buf);\n    longjmp(jump_buf, 1);\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  // In our test-case, we have a NEGATIVE stack adjustment, which is being\n  // interpreted as unsigned integer, and thus is \"too large\".\n  // TODO(fmayer): fix the error message for this\n  ASSERT_MATCH(result, R\"(memtag_handle_longjmp: stack adjustment too large)\");\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_F(CrasherTest, mte_async) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelAsync();\n    volatile int* p = (volatile int*)malloc(16);\n    p[-1] = 42;\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\), code [89] \\(SEGV_MTE[AS]ERR\\), fault addr)\");\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_F(CrasherTest, mte_multiple_causes) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  LogcatCollector logcat_collector;\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    SetTagCheckingLevelSync();\n\n    // Make two allocations with the same tag and close to one another. Check for both properties\n    // with a bounds check -- this relies on the fact that only if the allocations have the same tag\n    // would they be measured as closer than 128 bytes to each other. Otherwise they would be about\n    // (some non-zero value << 56) apart.\n    //\n    // The out-of-bounds access will be considered either an overflow of one or an underflow of the\n    // other.\n    std::set<uintptr_t> allocs;\n    for (int i = 0; i != 4096; ++i) {\n      uintptr_t alloc = reinterpret_cast<uintptr_t>(malloc(16));\n      auto it = allocs.insert(alloc).first;\n      if (it != allocs.begin() && *std::prev(it) + 128 > alloc) {\n        *reinterpret_cast<int*>(*std::prev(it) + 16) = 42;\n      }\n      if (std::next(it) != allocs.end() && alloc + 128 > *std::next(it)) {\n        *reinterpret_cast<int*>(alloc + 16) = 42;\n      }\n    }\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::vector<std::string> log_sources(2);\n  ConsumeFd(std::move(output_fd), &log_sources[0]);\n  logcat_collector.Collect(&log_sources[1]);\n\n  // Tag dump only in the tombstone, not logcat.\n  ASSERT_MATCH(log_sources[0], \"Memory tags around the fault address\");\n\n  for (const auto& result : log_sources) {\n    ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\))\");\n    ASSERT_THAT(result, HasSubstr(\"Note: multiple potential causes for this crash were detected, \"\n                                  \"listing them in decreasing order of likelihood.\"));\n    // Adjacent untracked allocations may cause us to see the wrong underflow here (or only\n    // overflows), so we can't match explicitly for an underflow message.\n    ASSERT_MATCH(result,\n                 R\"(Cause: \\[MTE\\]: Buffer Overflow, 0 bytes right of a 16-byte allocation)\");\n    // Ensure there's at least two allocation traces (one for each cause).\n    ASSERT_MATCH(\n        result,\n        R\"((^|\\s)allocated by thread .*?\\n.*#00 pc(.|\\n)*?(^|\\s)allocated by thread .*?\\n.*#00 pc)\");\n  }\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\n#if defined(__aarch64__)\nstatic uintptr_t CreateTagMapping() {\n  // Some of the MTE tag dump tests assert that there is an inaccessible page to the left and right\n  // of the PROT_MTE page, so map three pages and set the two guard pages to PROT_NONE.\n  size_t page_size = getpagesize();\n  void* mapping = mmap(nullptr, page_size * 3, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n  uintptr_t mapping_uptr = reinterpret_cast<uintptr_t>(mapping);\n  if (mapping == MAP_FAILED) {\n    return 0;\n  }\n  mprotect(reinterpret_cast<void*>(mapping_uptr + page_size), page_size,\n           PROT_READ | PROT_WRITE | PROT_MTE);\n  // Stripe the mapping, where even granules get tag '1', and odd granules get tag '0'.\n  for (uintptr_t offset = 0; offset < page_size; offset += 2 * kTagGranuleSize) {\n    uintptr_t tagged_addr = mapping_uptr + page_size + offset + (1ULL << 56);\n    __asm__ __volatile__(\".arch_extension mte; stg %0, [%0]\" : : \"r\"(tagged_addr) : \"memory\");\n  }\n  return mapping_uptr + page_size;\n}\n#endif\n\nTEST_F(CrasherTest, mte_register_tag_dump) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    Trap(reinterpret_cast<void *>(CreateTagMapping()));\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  ASSERT_MATCH(result, R\"(memory near x0:\n.*\n.*\n    01.............0 0000000000000000 0000000000000000  ................\n    00.............0)\");\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    volatile char* p = reinterpret_cast<char*>(CreateTagMapping());\n    p[0] = 0;  // Untagged pointer, tagged memory.\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  ASSERT_MATCH(result, R\"(Memory tags around the fault address.*\n\\s*=>0x[0-9a-f]+000:\\[1\\] 0  1  0)\");\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_F(CrasherTest, mte_fault_tag_dump) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    volatile char* p = reinterpret_cast<char*>(CreateTagMapping());\n    p[320] = 0;  // Untagged pointer, tagged memory.\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  ASSERT_MATCH(result, R\"(Memory tags around the fault address.*\n\\s*0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0\n\\s*=>0x[0-9a-f]+: 1  0  1  0 \\[1\\] 0  1  0  1  0  1  0  1  0  1  0\n\\s*0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0\n)\");\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) {\n#if defined(__aarch64__)\n  if (!mte_supported() || !mte_enabled()) {\n    GTEST_SKIP() << \"Requires MTE\";\n  }\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&]() {\n    SetTagCheckingLevelSync();\n    size_t page_size = getpagesize();\n    volatile char* p = reinterpret_cast<char*>(CreateTagMapping());\n    p[page_size - kTagGranuleSize * 2] = 0;  // Untagged pointer, tagged memory.\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  ASSERT_MATCH(result, R\"(Memory tags around the fault address)\");\n  ASSERT_MATCH(result,\n               R\"(\\s*0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0  1  0\n\\s*=>0x[0-9a-f]+: 1  0  1  0  1  0  1  0  1  0  1  0  1  0 \\[1\\] 0\n\n)\");  // Ensure truncation happened and there's a newline after the tag fault.\n#else\n  GTEST_SKIP() << \"Requires aarch64\";\n#endif\n}\n\nTEST_F(CrasherTest, LD_PRELOAD) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    setenv(\"LD_PRELOAD\", \"nonexistent.so\", 1);\n    *reinterpret_cast<volatile char*>(0xdead) = '1';\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\), code 1 \\(SEGV_MAPERR\\), fault addr 0x0+dead)\");\n}\n\nTEST_F(CrasherTest, abort) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"abort\");\n}\n\nTEST_F(CrasherTest, signal) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    while (true) {\n      sleep(1);\n    }\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  ASSERT_EQ(0, kill(crasher_pid, SIGSEGV));\n\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(\n      result,\n      R\"(signal 11 \\(SIGSEGV\\), code 0 \\(SI_USER from pid \\d+, uid \\d+\\), fault addr --------)\");\n  ASSERT_MATCH(result, R\"(backtrace:)\");\n}\n\nTEST_F(CrasherTest, abort_message) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    // Arrived at experimentally;\n    // logd truncates at 4062.\n    // strlen(\"Abort message: ''\") is 17.\n    // That's 4045, but we also want a NUL.\n    char buf[4045 + 1];\n    memset(buf, 'x', sizeof(buf));\n    buf[sizeof(buf) - 1] = '\\0';\n    android_set_abort_message(buf);\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(Abort message: 'x{4045}')\");\n}\n\nstatic char g_crash_detail_value_changes[] = \"crash_detail_value\";\nstatic char g_crash_detail_value[] = \"crash_detail_value\";\nstatic char g_crash_detail_value2[] = \"crash_detail_value2\";\n\ninline crash_detail_t* _Nullable android_register_crash_detail_strs(const char* _Nonnull name,\n                                                                    const char* _Nonnull data) {\n  return android_crash_detail_register(name, strlen(name), data, strlen(data));\n}\n\nTEST_F(CrasherTest, crash_detail_single) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_register_crash_detail_strs(\"CRASH_DETAIL_NAME\", g_crash_detail_value);\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME: 'crash_detail_value')\");\n}\n\nTEST_F(CrasherTest, crash_detail_replace_data) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    auto *cd = android_register_crash_detail_strs(\"CRASH_DETAIL_NAME\", \"original_data\");\n    android_crash_detail_replace_data(cd, \"new_data\", strlen(\"new_data\"));\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME: 'new_data')\");\n  // Ensure the old one no longer shows up, i.e. that we actually replaced\n  // it, not added a new one.\n  ASSERT_NOT_MATCH(result, R\"(CRASH_DETAIL_NAME: 'original_data')\");\n}\n\nTEST_F(CrasherTest, crash_detail_replace_name) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    auto *cd = android_register_crash_detail_strs(\"old_name\", g_crash_detail_value);\n    android_crash_detail_replace_name(cd, \"new_name\", strlen(\"new_name\"));\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(new_name: 'crash_detail_value')\");\n  // Ensure the old one no longer shows up, i.e. that we actually replaced\n  // it, not added a new one.\n  ASSERT_NOT_MATCH(result, R\"(old_name: 'crash_detail_value')\");\n}\n\nTEST_F(CrasherTest, crash_detail_single_byte_name) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_register_crash_detail_strs(\"CRASH_DETAIL_NAME\\1\", g_crash_detail_value);\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME\\\\1: 'crash_detail_value')\");\n}\n\n\nTEST_F(CrasherTest, crash_detail_single_bytes) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_crash_detail_register(\"CRASH_DETAIL_NAME\", strlen(\"CRASH_DETAIL_NAME\"), \"\\1\",\n                                  sizeof(\"\\1\"));\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME: '\\\\1\\\\0')\");\n}\n\nTEST_F(CrasherTest, crash_detail_mixed) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    const char data[] = \"helloworld\\1\\255\\3\";\n    android_register_crash_detail_strs(\"CRASH_DETAIL_NAME\", data);\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME: 'helloworld\\\\1\\\\255\\\\3')\");\n}\n\nTEST_F(CrasherTest, crash_detail_many) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    for (int i = 0; i < 1000; ++i) {\n      std::string name = \"CRASH_DETAIL_NAME\" + std::to_string(i);\n      std::string value = \"CRASH_DETAIL_VALUE\" + std::to_string(i);\n      auto* h = android_register_crash_detail_strs(name.data(), value.data());\n      android_crash_detail_unregister(h);\n    }\n\n    android_register_crash_detail_strs(\"FINAL_NAME\", \"FINAL_VALUE\");\n    android_register_crash_detail_strs(\"FINAL_NAME2\", \"FINAL_VALUE2\");\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_NOT_MATCH(result, \"CRASH_DETAIL_NAME\");\n  ASSERT_NOT_MATCH(result, \"CRASH_DETAIL_VALUE\");\n  ASSERT_MATCH(result, R\"(FINAL_NAME: 'FINAL_VALUE')\");\n  ASSERT_MATCH(result, R\"(FINAL_NAME2: 'FINAL_VALUE2')\");\n}\n\nTEST_F(CrasherTest, crash_detail_single_changes) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_register_crash_detail_strs(\"CRASH_DETAIL_NAME\", g_crash_detail_value_changes);\n    g_crash_detail_value_changes[0] = 'C';\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME: 'Crash_detail_value')\");\n}\n\nTEST_F(CrasherTest, crash_detail_multiple) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_register_crash_detail_strs(\"CRASH_DETAIL_NAME\", g_crash_detail_value);\n    android_register_crash_detail_strs(\"CRASH_DETAIL_NAME2\", g_crash_detail_value2);\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME: 'crash_detail_value')\");\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME2: 'crash_detail_value2')\");\n}\n\nTEST_F(CrasherTest, crash_detail_remove) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    auto* detail1 = android_register_crash_detail_strs(\"CRASH_DETAIL_NAME\", g_crash_detail_value);\n    android_crash_detail_unregister(detail1);\n    android_register_crash_detail_strs(\"CRASH_DETAIL_NAME2\", g_crash_detail_value2);\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_NOT_MATCH(result, R\"(CRASH_DETAIL_NAME: 'crash_detail_value')\");\n  ASSERT_MATCH(result, R\"(CRASH_DETAIL_NAME2: 'crash_detail_value2')\");\n}\n\nTEST_F(CrasherTest, abort_message_newline_trimmed) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_set_abort_message(\"Message with a newline.\\n\");\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(Abort message: 'Message with a newline.')\");\n}\n\nTEST_F(CrasherTest, abort_message_multiple_newlines_trimmed) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_set_abort_message(\"Message with multiple newlines.\\n\\n\\n\\n\\n\");\n    abort();\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(Abort message: 'Message with multiple newlines.')\");\n}\n\nTEST_F(CrasherTest, abort_message_backtrace) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    android_set_abort_message(\"not actually aborting\");\n    raise(BIONIC_SIGNAL_DEBUGGER);\n    exit(0);\n  });\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(0);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_NOT_MATCH(result, R\"(Abort message:)\");\n}\n\nTEST_F(CrasherTest, intercept_timeout) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    abort();\n  });\n  StartIntercept(&output_fd);\n\n  // Don't let crasher finish until we timeout.\n  FinishIntercept(&intercept_result);\n\n  ASSERT_NE(1, intercept_result) << \"tombstoned reported success? (intercept_result = \"\n                                 << intercept_result << \")\";\n\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n}\n\nTEST_F(CrasherTest, wait_for_debugger) {\n  if (!android::base::SetProperty(kWaitForDebuggerKey, \"1\")) {\n    FAIL() << \"failed to enable wait_for_debugger\";\n  }\n  sleep(1);\n\n  StartProcess([]() {\n    abort();\n  });\n  FinishCrasher();\n\n  int status;\n  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, WUNTRACED)));\n  ASSERT_TRUE(WIFSTOPPED(status));\n  ASSERT_EQ(SIGSTOP, WSTOPSIG(status));\n\n  ASSERT_EQ(0, kill(crasher_pid, SIGCONT));\n\n  AssertDeath(SIGABRT);\n}\n\nTEST_F(CrasherTest, backtrace) {\n  std::string result;\n  int intercept_result;\n  unique_fd output_fd;\n\n  StartProcess([]() {\n    abort();\n  });\n  StartIntercept(&output_fd, kDebuggerdNativeBacktrace);\n\n  std::this_thread::sleep_for(500ms);\n\n  sigval val;\n  val.sival_int = 1;\n  ASSERT_EQ(0, sigqueue(crasher_pid, BIONIC_SIGNAL_DEBUGGER, val)) << strerror(errno);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"read\");\n\n  int status;\n  ASSERT_EQ(0, waitpid(crasher_pid, &status, WNOHANG | WUNTRACED));\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"abort\");\n}\n\nTEST_F(CrasherTest, PR_SET_DUMPABLE_0_crash) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    prctl(PR_SET_DUMPABLE, 0);\n    abort();\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"abort\");\n}\n\nTEST_F(CrasherTest, capabilities) {\n  ASSERT_EQ(0U, getuid()) << \"capability test requires root\";\n\n  StartProcess([]() {\n    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) {\n      err(1, \"failed to set PR_SET_KEEPCAPS\");\n    }\n\n    if (setresuid(1, 1, 1) != 0) {\n      err(1, \"setresuid failed\");\n    }\n\n    __user_cap_header_struct capheader;\n    __user_cap_data_struct capdata[2];\n    memset(&capheader, 0, sizeof(capheader));\n    memset(&capdata, 0, sizeof(capdata));\n\n    capheader.version = _LINUX_CAPABILITY_VERSION_3;\n    capheader.pid = 0;\n\n    // Turn on every third capability.\n    static_assert(CAP_LAST_CAP > 33, \"CAP_LAST_CAP <= 32\");\n    for (int i = 0; i < CAP_LAST_CAP; i += 3) {\n      capdata[CAP_TO_INDEX(i)].permitted |= CAP_TO_MASK(i);\n      capdata[CAP_TO_INDEX(i)].effective |= CAP_TO_MASK(i);\n    }\n\n    // Make sure CAP_SYS_PTRACE is off.\n    capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].permitted &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));\n    capdata[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective &= ~(CAP_TO_MASK(CAP_SYS_PTRACE));\n\n    if (capset(&capheader, &capdata[0]) != 0) {\n      err(1, \"capset failed\");\n    }\n\n    if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0) != 0) {\n      err(1, \"failed to drop ambient capabilities\");\n    }\n\n    pthread_setname_np(pthread_self(), \"thread_name\");\n    raise(SIGSYS);\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSYS);\n\n  std::string result;\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(name: thread_name\\s+>>> .+debuggerd_test(32|64) <<<)\");\n  ASSERT_BACKTRACE_FRAME(result, \"tgkill\");\n}\n\nTEST_F(CrasherTest, fake_pid) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  // Prime the getpid/gettid caches.\n  UNUSED(getpid());\n  UNUSED(gettid());\n\n  std::function<pid_t()> clone_fn = []() {\n    return syscall(__NR_clone, SIGCHLD, nullptr, nullptr, nullptr, nullptr);\n  };\n  StartProcess(\n      []() {\n        ASSERT_NE(getpid(), syscall(__NR_getpid));\n        ASSERT_NE(gettid(), syscall(__NR_gettid));\n        raise(SIGSEGV);\n      },\n      clone_fn);\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"tgkill\");\n}\n\nstatic const char* const kDebuggerdSeccompPolicy =\n    \"/system/etc/seccomp_policy/crash_dump.\" ABI_STRING \".policy\";\n\nstatic void setup_jail(minijail* jail) {\n  if (!jail) {\n    LOG(FATAL) << \"failed to create minijail\";\n  }\n\n  std::string policy;\n  if (!android::base::ReadFileToString(kDebuggerdSeccompPolicy, &policy)) {\n    PLOG(FATAL) << \"failed to read policy file\";\n  }\n\n  // Allow a bunch of syscalls used by the tests.\n  policy += \"\\nclone: 1\";\n  policy += \"\\nsigaltstack: 1\";\n  policy += \"\\nnanosleep: 1\";\n  policy += \"\\ngetrlimit: 1\";\n  policy += \"\\nugetrlimit: 1\";\n\n  FILE* tmp_file = tmpfile();\n  if (!tmp_file) {\n    PLOG(FATAL) << \"tmpfile failed\";\n  }\n\n  unique_fd tmp_fd(TEMP_FAILURE_RETRY(dup(fileno(tmp_file))));\n  if (!android::base::WriteStringToFd(policy, tmp_fd.get())) {\n    PLOG(FATAL) << \"failed to write policy to tmpfile\";\n  }\n\n  if (lseek(tmp_fd.get(), 0, SEEK_SET) != 0) {\n    PLOG(FATAL) << \"failed to seek tmp_fd\";\n  }\n\n  minijail_no_new_privs(jail);\n  minijail_log_seccomp_filter_failures(jail);\n  minijail_use_seccomp_filter(jail);\n  minijail_parse_seccomp_filters_from_fd(jail, tmp_fd.release());\n}\n\nstatic pid_t seccomp_fork_impl(void (*prejail)()) {\n  ScopedMinijail jail{minijail_new()};\n  setup_jail(jail.get());\n\n  pid_t result = fork();\n  if (result == -1) {\n    return result;\n  } else if (result != 0) {\n    return result;\n  }\n\n  // Spawn and detach a thread that spins forever.\n  std::atomic<bool> thread_ready(false);\n  std::thread thread([&jail, &thread_ready]() {\n    minijail_enter(jail.get());\n    thread_ready = true;\n    for (;;)\n      ;\n  });\n  thread.detach();\n\n  while (!thread_ready) {\n    continue;\n  }\n\n  if (prejail) {\n    prejail();\n  }\n\n  minijail_enter(jail.get());\n  return result;\n}\n\nstatic pid_t seccomp_fork() {\n  return seccomp_fork_impl(nullptr);\n}\n\nTEST_F(CrasherTest, seccomp_crash) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  StartProcess([]() { abort(); }, &seccomp_fork);\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"abort\");\n}\n\nstatic pid_t seccomp_fork_rlimit() {\n  return seccomp_fork_impl([]() {\n    struct rlimit rlim = {\n        .rlim_cur = 512 * 1024 * 1024,\n        .rlim_max = 512 * 1024 * 1024,\n    };\n\n    if (setrlimit(RLIMIT_AS, &rlim) != 0) {\n      raise(SIGINT);\n    }\n  });\n}\n\nTEST_F(CrasherTest, seccomp_crash_oom) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  StartProcess(\n      []() {\n        std::vector<void*> vec;\n        for (int i = 0; i < 512; ++i) {\n          char* buf = static_cast<char*>(malloc(1024 * 1024));\n          if (!buf) {\n            abort();\n          }\n          memset(buf, 0xff, 1024 * 1024);\n          vec.push_back(buf);\n        }\n      },\n      &seccomp_fork_rlimit);\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  // We can't actually generate a backtrace, just make sure that the process terminates.\n}\n\n__attribute__((__noinline__)) extern \"C\" bool raise_debugger_signal(DebuggerdDumpType dump_type) {\n  siginfo_t siginfo;\n  siginfo.si_code = SI_QUEUE;\n  siginfo.si_pid = getpid();\n  siginfo.si_uid = getuid();\n\n  if (dump_type != kDebuggerdNativeBacktrace && dump_type != kDebuggerdTombstone) {\n    PLOG(FATAL) << \"invalid dump type\";\n  }\n\n  siginfo.si_value.sival_int = dump_type == kDebuggerdNativeBacktrace;\n\n  if (syscall(__NR_rt_tgsigqueueinfo, getpid(), gettid(), BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {\n    PLOG(ERROR) << \"libdebuggerd_client: failed to send signal to self\";\n    return false;\n  }\n\n  return true;\n}\n\nextern \"C\" void foo() {\n  LOG(INFO) << \"foo\";\n  std::this_thread::sleep_for(1s);\n}\n\nextern \"C\" void bar() {\n  LOG(INFO) << \"bar\";\n  std::this_thread::sleep_for(1s);\n}\n\nTEST_F(CrasherTest, seccomp_tombstone) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  static const auto dump_type = kDebuggerdTombstone;\n  StartProcess(\n      []() {\n        std::thread a(foo);\n        std::thread b(bar);\n\n        std::this_thread::sleep_for(100ms);\n\n        raise_debugger_signal(dump_type);\n        _exit(0);\n      },\n      &seccomp_fork);\n\n  StartIntercept(&output_fd, dump_type);\n  FinishCrasher();\n  AssertDeath(0);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"raise_debugger_signal\");\n  ASSERT_BACKTRACE_FRAME(result, \"foo\");\n  ASSERT_BACKTRACE_FRAME(result, \"bar\");\n}\n\nTEST_F(CrasherTest, seccomp_tombstone_thread_abort) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  static const auto dump_type = kDebuggerdTombstone;\n  StartProcess(\n      []() {\n        std::thread abort_thread([] { abort(); });\n        abort_thread.join();\n      },\n      &seccomp_fork);\n\n  StartIntercept(&output_fd, dump_type);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(\n      result,\n      R\"(signal 6 \\(SIGABRT\\))\");\n  ASSERT_BACKTRACE_FRAME(result, \"abort\");\n}\n\nTEST_F(CrasherTest, seccomp_tombstone_multiple_threads_abort) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  static const auto dump_type = kDebuggerdTombstone;\n  StartProcess(\n      []() {\n        std::thread a(foo);\n        std::thread b(bar);\n\n        std::this_thread::sleep_for(100ms);\n\n        std::thread abort_thread([] { abort(); });\n        abort_thread.join();\n      },\n      &seccomp_fork);\n\n  StartIntercept(&output_fd, dump_type);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"abort\");\n  ASSERT_BACKTRACE_FRAME(result, \"foo\");\n  ASSERT_BACKTRACE_FRAME(result, \"bar\");\n  ASSERT_BACKTRACE_FRAME(result, \"main\");\n}\n\nTEST_F(CrasherTest, seccomp_backtrace) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  static const auto dump_type = kDebuggerdNativeBacktrace;\n  StartProcess(\n      []() {\n        std::thread a(foo);\n        std::thread b(bar);\n\n        std::this_thread::sleep_for(100ms);\n\n        raise_debugger_signal(dump_type);\n        _exit(0);\n      },\n      &seccomp_fork);\n\n  StartIntercept(&output_fd, dump_type);\n  FinishCrasher();\n  AssertDeath(0);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"raise_debugger_signal\");\n  ASSERT_BACKTRACE_FRAME(result, \"foo\");\n  ASSERT_BACKTRACE_FRAME(result, \"bar\");\n}\n\nTEST_F(CrasherTest, seccomp_backtrace_from_thread) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  static const auto dump_type = kDebuggerdNativeBacktrace;\n  StartProcess(\n      []() {\n        std::thread a(foo);\n        std::thread b(bar);\n\n        std::this_thread::sleep_for(100ms);\n\n        std::thread raise_thread([] {\n          raise_debugger_signal(dump_type);\n          _exit(0);\n        });\n        raise_thread.join();\n      },\n      &seccomp_fork);\n\n  StartIntercept(&output_fd, dump_type);\n  FinishCrasher();\n  AssertDeath(0);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"raise_debugger_signal\");\n  ASSERT_BACKTRACE_FRAME(result, \"foo\");\n  ASSERT_BACKTRACE_FRAME(result, \"bar\");\n  ASSERT_BACKTRACE_FRAME(result, \"main\");\n}\n\nTEST_F(CrasherTest, seccomp_crash_logcat) {\n  StartProcess([]() { abort(); }, &seccomp_fork);\n  FinishCrasher();\n\n  // Make sure we don't get SIGSYS when trying to dump a crash to logcat.\n  AssertDeath(SIGABRT);\n}\n\nextern \"C\" void malloc_enable();\nextern \"C\" void malloc_disable();\n\nTEST_F(CrasherTest, seccomp_tombstone_no_allocation) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  static const auto dump_type = kDebuggerdTombstone;\n  StartProcess(\n      []() {\n        std::thread a(foo);\n        std::thread b(bar);\n\n        std::this_thread::sleep_for(100ms);\n\n        // Disable allocations to verify that nothing in the fallback\n        // signal handler does an allocation.\n        malloc_disable();\n        raise_debugger_signal(dump_type);\n        _exit(0);\n      },\n      &seccomp_fork);\n\n  StartIntercept(&output_fd, dump_type);\n  FinishCrasher();\n  AssertDeath(0);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"raise_debugger_signal\");\n  ASSERT_BACKTRACE_FRAME(result, \"foo\");\n  ASSERT_BACKTRACE_FRAME(result, \"bar\");\n}\n\nTEST_F(CrasherTest, seccomp_backtrace_no_allocation) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  static const auto dump_type = kDebuggerdNativeBacktrace;\n  StartProcess(\n      []() {\n        std::thread a(foo);\n        std::thread b(bar);\n\n        std::this_thread::sleep_for(100ms);\n\n        // Disable allocations to verify that nothing in the fallback\n        // signal handler does an allocation.\n        malloc_disable();\n        raise_debugger_signal(dump_type);\n        _exit(0);\n      },\n      &seccomp_fork);\n\n  StartIntercept(&output_fd, dump_type);\n  FinishCrasher();\n  AssertDeath(0);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"raise_debugger_signal\");\n  ASSERT_BACKTRACE_FRAME(result, \"foo\");\n  ASSERT_BACKTRACE_FRAME(result, \"bar\");\n}\n\nTEST_F(CrasherTest, competing_tracer) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() {\n    raise(SIGABRT);\n  });\n\n  StartIntercept(&output_fd);\n\n  ASSERT_EQ(0, ptrace(PTRACE_SEIZE, crasher_pid, 0, 0));\n  FinishCrasher();\n\n  int status;\n  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, 0)));\n  ASSERT_TRUE(WIFSTOPPED(status));\n  ASSERT_EQ(SIGABRT, WSTOPSIG(status));\n\n  ASSERT_EQ(0, ptrace(PTRACE_CONT, crasher_pid, 0, SIGABRT));\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  std::string regex = R\"(failed to attach to thread \\d+, already traced by )\";\n  regex += std::to_string(gettid());\n  regex += R\"( \\(.+debuggerd_test)\";\n  ASSERT_MATCH(result, regex.c_str());\n\n  ASSERT_EQ(crasher_pid, TEMP_FAILURE_RETRY(waitpid(crasher_pid, &status, 0)));\n  ASSERT_TRUE(WIFSTOPPED(status));\n  ASSERT_EQ(SIGABRT, WSTOPSIG(status));\n\n  ASSERT_EQ(0, ptrace(PTRACE_DETACH, crasher_pid, 0, SIGABRT));\n  AssertDeath(SIGABRT);\n}\n\nstruct GwpAsanTestParameters {\n  size_t alloc_size;\n  bool free_before_access;\n  int access_offset;\n  std::string cause_needle;  // Needle to be found in the \"Cause: [GWP-ASan]\" line.\n};\n\nstruct GwpAsanCrasherTest\n    : CrasherTest,\n      testing::WithParamInterface<\n          std::tuple<GwpAsanTestParameters, /* recoverable */ bool, /* seccomp */ bool>> {};\n\nGwpAsanTestParameters gwp_asan_tests[] = {\n    {/* alloc_size */ 7, /* free_before_access */ true, /* access_offset */ 0,\n     \"Use After Free, 0 bytes into a 7-byte allocation\"},\n    {/* alloc_size */ 15, /* free_before_access */ true, /* access_offset */ 1,\n     \"Use After Free, 1 byte into a 15-byte allocation\"},\n    {/* alloc_size */ static_cast<size_t>(getpagesize()), /* free_before_access */ false,\n     /* access_offset */ getpagesize() + 2,\n     android::base::StringPrintf(\"Buffer Overflow, 2 bytes right of a %d-byte allocation\",\n                                 getpagesize())},\n    {/* alloc_size */ static_cast<size_t>(getpagesize()), /* free_before_access */ false,\n     /* access_offset */ -1,\n     android::base::StringPrintf(\"Buffer Underflow, 1 byte left of a %d-byte allocation\",\n                                 getpagesize())},\n};\n\nINSTANTIATE_TEST_SUITE_P(\n    GwpAsanTests, GwpAsanCrasherTest,\n    testing::Combine(testing::ValuesIn(gwp_asan_tests),\n                     /* recoverable */ testing::Bool(),\n                     /* seccomp */ testing::Bool()),\n    [](const testing::TestParamInfo<\n        std::tuple<GwpAsanTestParameters, /* recoverable */ bool, /* seccomp */ bool>>& info) {\n      const GwpAsanTestParameters& params = std::get<0>(info.param);\n      std::string name = params.free_before_access ? \"UseAfterFree\" : \"Overflow\";\n      name += testing::PrintToString(params.alloc_size);\n      name += \"Alloc\";\n      if (params.access_offset < 0) {\n        name += \"Left\";\n        name += testing::PrintToString(params.access_offset * -1);\n      } else {\n        name += \"Right\";\n        name += testing::PrintToString(params.access_offset);\n      }\n      name += \"Bytes\";\n      if (std::get<1>(info.param)) name += \"Recoverable\";\n      if (std::get<2>(info.param)) name += \"Seccomp\";\n      return name;\n    });\n\nTEST_P(GwpAsanCrasherTest, run_gwp_asan_test) {\n  if (mte_supported()) {\n    // Skip this test on MTE hardware, as MTE will reliably catch these errors\n    // instead of GWP-ASan.\n    GTEST_SKIP() << \"Skipped on MTE.\";\n  }\n  // Skip this test on HWASan, which will reliably catch test errors as well.\n  SKIP_WITH_HWASAN;\n\n  GwpAsanTestParameters params = std::get<0>(GetParam());\n  bool recoverable = std::get<1>(GetParam());\n  LogcatCollector logcat_collector;\n\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([&recoverable]() {\n    const char* env[] = {\"GWP_ASAN_SAMPLE_RATE=1\", \"GWP_ASAN_PROCESS_SAMPLING=1\",\n                         \"GWP_ASAN_MAX_ALLOCS=40000\", nullptr, nullptr};\n    if (!recoverable) {\n      env[3] = \"GWP_ASAN_RECOVERABLE=false\";\n    }\n    std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name();\n    test_name = std::regex_replace(test_name, std::regex(\"run_gwp_asan_test\"),\n                                   \"DISABLED_run_gwp_asan_test\");\n    std::string test_filter = \"--gtest_filter=*\";\n    test_filter += test_name;\n    std::string this_binary = android::base::GetExecutablePath();\n    const char* args[] = {this_binary.c_str(), \"--gtest_also_run_disabled_tests\",\n                          test_filter.c_str(), nullptr};\n    // We check the crash report from a debuggerd handler and from logcat. The\n    // echo from stdout/stderr of the subprocess trips up atest, because it\n    // doesn't like that two tests started in a row without the first one\n    // finishing (even though the second one is in a subprocess).\n    close(STDOUT_FILENO);\n    close(STDERR_FILENO);\n    execve(this_binary.c_str(), const_cast<char**>(args), const_cast<char**>(env));\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  if (recoverable) {\n    AssertDeath(0);\n  } else {\n    AssertDeath(SIGSEGV);\n  }\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::vector<std::string> log_sources(2);\n  ConsumeFd(std::move(output_fd), &log_sources[0]);\n  logcat_collector.Collect(&log_sources[1]);\n\n  // seccomp forces the fallback handler, which doesn't print GWP-ASan debugging\n  // information. Make sure the recovery still works, but the report won't be\n  // hugely useful, it looks like a regular SEGV.\n  bool seccomp = std::get<2>(GetParam());\n  if (!seccomp) {\n    for (const auto& result : log_sources) {\n      ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\), code 2 \\(SEGV_ACCERR\\))\");\n      ASSERT_MATCH(result, R\"(Cause: \\[GWP-ASan\\]: )\" + params.cause_needle);\n      if (params.free_before_access) {\n        ASSERT_MATCH(result, R\"(deallocated by thread .*\\n.*#00 pc)\");\n      }\n      ASSERT_MATCH(result, R\"((^|\\s)allocated by thread .*\\n.*#00 pc)\");\n    }\n  }\n}\n\nTEST_P(GwpAsanCrasherTest, DISABLED_run_gwp_asan_test) {\n  GwpAsanTestParameters params = std::get<0>(GetParam());\n  bool seccomp = std::get<2>(GetParam());\n  if (seccomp) {\n    ScopedMinijail jail{minijail_new()};\n    setup_jail(jail.get());\n    minijail_enter(jail.get());\n  }\n\n  // Use 'volatile' to prevent a very clever compiler eliminating the store.\n  char* volatile p = reinterpret_cast<char* volatile>(malloc(params.alloc_size));\n  if (params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));\n  p[params.access_offset] = 42;\n  if (!params.free_before_access) free(static_cast<void*>(const_cast<char*>(p)));\n\n  bool recoverable = std::get<1>(GetParam());\n  ASSERT_TRUE(recoverable);  // Non-recoverable should have crashed.\n\n  // As we're in recoverable mode, trigger another 2x use-after-frees (ensuring\n  // we end with at least one in a different slot), make sure the process still\n  // doesn't crash.\n  p = reinterpret_cast<char* volatile>(malloc(params.alloc_size));\n  char* volatile p2 = reinterpret_cast<char* volatile>(malloc(params.alloc_size));\n  free(static_cast<void*>(const_cast<char*>(p)));\n  free(static_cast<void*>(const_cast<char*>(p2)));\n  *p = 42;\n  *p2 = 42;\n\n  // Under clang coverage (which is a default TEST_MAPPING presubmit target), the\n  // recoverable+seccomp tests fail because the minijail prevents some atexit syscalls that clang\n  // coverage does. Thus, skip the atexit handlers.\n  _exit(0);\n}\n\nTEST_F(CrasherTest, fdsan_warning_abort_message) {\n  int intercept_result;\n  unique_fd output_fd;\n\n  StartProcess([]() {\n    android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);\n    unique_fd fd(TEMP_FAILURE_RETRY(open(\"/dev/null\", O_RDONLY | O_CLOEXEC)));\n    if (fd == -1) {\n      abort();\n    }\n    close(fd.get());\n    _exit(0);\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(0);\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, \"Abort message: 'attempted to close\");\n}\n\nTEST(crash_dump, zombie) {\n  pid_t forkpid = fork();\n\n  pid_t rc;\n  int status;\n\n  if (forkpid == 0) {\n    errno = 0;\n    rc = waitpid(-1, &status, WNOHANG | __WALL | __WNOTHREAD);\n    if (rc != -1 || errno != ECHILD) {\n      errx(2, \"first waitpid returned %d (%s), expected failure with ECHILD\", rc, strerror(errno));\n    }\n\n    raise(BIONIC_SIGNAL_DEBUGGER);\n\n    errno = 0;\n    rc = TEMP_FAILURE_RETRY(waitpid(-1, &status, __WALL | __WNOTHREAD));\n    if (rc != -1 || errno != ECHILD) {\n      errx(2, \"second waitpid returned %d (%s), expected failure with ECHILD\", rc, strerror(errno));\n    }\n    _exit(0);\n  } else {\n    rc = TEMP_FAILURE_RETRY(waitpid(forkpid, &status, 0));\n    ASSERT_EQ(forkpid, rc);\n    ASSERT_TRUE(WIFEXITED(status));\n    ASSERT_EQ(0, WEXITSTATUS(status));\n  }\n}\n\nTEST(tombstoned, no_notify) {\n  // Do this a few times.\n  for (int i = 0; i < 3; ++i) {\n    pid_t pid = 123'456'789 + i;\n\n    unique_fd intercept_fd, output_fd;\n    InterceptResponse response = {};\n    tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);\n    ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n        << \"Error message: \" << response.error_message;\n\n    {\n      unique_fd tombstoned_socket, input_fd;\n      ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));\n      ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));\n    }\n\n    pid_t read_pid;\n    ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));\n    ASSERT_EQ(read_pid, pid);\n  }\n}\n\nTEST(tombstoned, stress) {\n  // Spawn threads to simultaneously do a bunch of failing dumps and a bunch of successful dumps.\n  static constexpr int kDumpCount = 100;\n\n  std::atomic<bool> start(false);\n  std::vector<std::thread> threads;\n  threads.emplace_back([&start]() {\n    while (!start) {\n      continue;\n    }\n\n    // Use a way out of range pid, to avoid stomping on an actual process.\n    pid_t pid_base = 1'000'000;\n\n    for (int dump = 0; dump < kDumpCount; ++dump) {\n      pid_t pid = pid_base + dump;\n\n      unique_fd intercept_fd, output_fd;\n      InterceptResponse response = {};\n      tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);\n      ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n          << \"Error messeage: \" << response.error_message;\n\n      // Pretend to crash, and then immediately close the socket.\n      unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,\n                                           ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));\n      if (sockfd == -1) {\n        FAIL() << \"failed to connect to tombstoned: \" << strerror(errno);\n      }\n      TombstonedCrashPacket packet = {};\n      packet.packet_type = CrashPacketType::kDumpRequest;\n      packet.packet.dump_request.pid = pid;\n      if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {\n        FAIL() << \"failed to write to tombstoned: \" << strerror(errno);\n      }\n\n      continue;\n    }\n  });\n\n  threads.emplace_back([&start]() {\n    while (!start) {\n      continue;\n    }\n\n    // Use a way out of range pid, to avoid stomping on an actual process.\n    pid_t pid_base = 2'000'000;\n\n    for (int dump = 0; dump < kDumpCount; ++dump) {\n      pid_t pid = pid_base + dump;\n\n      unique_fd intercept_fd, output_fd;\n      InterceptResponse response = {};\n      tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);\n      ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n          << \"Error message: \" << response.error_message;\n\n      {\n        unique_fd tombstoned_socket, input_fd;\n        ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd, kDebuggerdTombstone));\n        ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));\n        tombstoned_notify_completion(tombstoned_socket.get());\n      }\n\n      // TODO: Fix the race that requires this sleep.\n      std::this_thread::sleep_for(50ms);\n\n      pid_t read_pid;\n      ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));\n      ASSERT_EQ(read_pid, pid);\n    }\n  });\n\n  start = true;\n\n  for (std::thread& thread : threads) {\n    thread.join();\n  }\n}\n\nTEST(tombstoned, intercept_java_trace_smoke) {\n  // Using a \"real\" PID is a little dangerous here - if the test fails\n  // or crashes, we might end up getting a bogus / unreliable stack\n  // trace.\n  const pid_t self = getpid();\n\n  unique_fd intercept_fd, output_fd;\n  InterceptResponse response = {};\n  tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  // First connect to tombstoned requesting a native tombstone. This\n  // should result in a \"regular\" FD and not the installed intercept.\n  const char native[] = \"native\";\n  unique_fd tombstoned_socket, input_fd;\n  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdTombstone));\n  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), native, sizeof(native)));\n  tombstoned_notify_completion(tombstoned_socket.get());\n\n  // Then, connect to tombstoned asking for a java backtrace. This *should*\n  // trigger the intercept.\n  const char java[] = \"java\";\n  ASSERT_TRUE(tombstoned_connect(self, &tombstoned_socket, &input_fd, kDebuggerdJavaBacktrace));\n  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), java, sizeof(java)));\n  tombstoned_notify_completion(tombstoned_socket.get());\n\n  char outbuf[sizeof(java)];\n  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));\n  ASSERT_STREQ(\"java\", outbuf);\n}\n\nTEST(tombstoned, intercept_multiple_dump_types) {\n  const pid_t fake_pid = 1'234'567;\n  unique_fd intercept_fd, output_fd;\n  InterceptResponse response = {};\n  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  unique_fd intercept_fd_2, output_fd_2;\n  tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &response,\n                       kDebuggerdNativeBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n}\n\nTEST(tombstoned, intercept_bad_pid) {\n  const pid_t fake_pid = -1;\n  unique_fd intercept_fd, output_fd;\n  InterceptResponse response = {};\n  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace);\n  ASSERT_EQ(InterceptStatus::kFailed, response.status)\n      << \"Error message: \" << response.error_message;\n  ASSERT_MATCH(response.error_message, \"bad pid\");\n}\n\nTEST(tombstoned, intercept_bad_dump_types) {\n  const pid_t fake_pid = 1'234'567;\n  unique_fd intercept_fd, output_fd;\n  InterceptResponse response = {};\n  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response,\n                       static_cast<DebuggerdDumpType>(20));\n  ASSERT_EQ(InterceptStatus::kFailed, response.status)\n      << \"Error message: \" << response.error_message;\n  ASSERT_MATCH(response.error_message, \"bad dump type \\\\[unknown\\\\]\");\n\n  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdAnyIntercept);\n  ASSERT_EQ(InterceptStatus::kFailed, response.status)\n      << \"Error message: \" << response.error_message;\n  ASSERT_MATCH(response.error_message, \"bad dump type kDebuggerdAnyIntercept\");\n\n  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstoneProto);\n  ASSERT_EQ(InterceptStatus::kFailed, response.status)\n      << \"Error message: \" << response.error_message;\n  ASSERT_MATCH(response.error_message, \"bad dump type kDebuggerdTombstoneProto\");\n}\n\nTEST(tombstoned, intercept_already_registered) {\n  const pid_t fake_pid = 1'234'567;\n  unique_fd intercept_fd1, output_fd1;\n  InterceptResponse response = {};\n  tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdTombstone);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  unique_fd intercept_fd2, output_fd2;\n  tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdTombstone);\n  ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n  ASSERT_MATCH(response.error_message, \"already registered, type kDebuggerdTombstone\");\n}\n\nTEST(tombstoned, intercept_tombstone_proto_matched_to_tombstone) {\n  const pid_t fake_pid = 1'234'567;\n\n  unique_fd intercept_fd, output_fd;\n  InterceptResponse response = {};\n  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  const char data[] = \"tombstone_proto\";\n  unique_fd tombstoned_socket, input_fd;\n  ASSERT_TRUE(\n      tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdTombstoneProto));\n  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), data, sizeof(data)));\n  tombstoned_notify_completion(tombstoned_socket.get());\n\n  char outbuf[sizeof(data)];\n  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));\n  ASSERT_STREQ(\"tombstone_proto\", outbuf);\n}\n\nTEST(tombstoned, intercept_any) {\n  const pid_t fake_pid = 1'234'567;\n\n  unique_fd intercept_fd, output_fd;\n  InterceptResponse response = {};\n  tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  const char any[] = \"any\";\n  unique_fd tombstoned_socket, input_fd;\n  ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));\n  ASSERT_TRUE(android::base::WriteFully(input_fd.get(), any, sizeof(any)));\n  tombstoned_notify_completion(tombstoned_socket.get());\n\n  char outbuf[sizeof(any)];\n  ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf)));\n  ASSERT_STREQ(\"any\", outbuf);\n}\n\nTEST(tombstoned, intercept_any_failed_with_multiple_intercepts) {\n  const pid_t fake_pid = 1'234'567;\n\n  InterceptResponse response = {};\n  unique_fd intercept_fd1, output_fd1;\n  tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  unique_fd intercept_fd2, output_fd2;\n  tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  unique_fd tombstoned_socket, input_fd;\n  ASSERT_FALSE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept));\n}\n\nTEST(tombstoned, intercept_multiple_verify_intercept) {\n  // Need to use our pid for java since that will verify the pid.\n  const pid_t fake_pid = getpid();\n\n  InterceptResponse response = {};\n  unique_fd intercept_fd1, output_fd1;\n  tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  unique_fd intercept_fd2, output_fd2;\n  tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  unique_fd intercept_fd3, output_fd3;\n  tombstoned_intercept(fake_pid, &intercept_fd3, &output_fd3, &response, kDebuggerdTombstone);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  const char native_data[] = \"native\";\n  unique_fd tombstoned_socket1, input_fd1;\n  ASSERT_TRUE(\n      tombstoned_connect(fake_pid, &tombstoned_socket1, &input_fd1, kDebuggerdNativeBacktrace));\n  ASSERT_TRUE(android::base::WriteFully(input_fd1.get(), native_data, sizeof(native_data)));\n  tombstoned_notify_completion(tombstoned_socket1.get());\n\n  char native_outbuf[sizeof(native_data)];\n  ASSERT_TRUE(android::base::ReadFully(output_fd1.get(), native_outbuf, sizeof(native_outbuf)));\n  ASSERT_STREQ(\"native\", native_outbuf);\n\n  const char java_data[] = \"java\";\n  unique_fd tombstoned_socket2, input_fd2;\n  ASSERT_TRUE(\n      tombstoned_connect(fake_pid, &tombstoned_socket2, &input_fd2, kDebuggerdJavaBacktrace));\n  ASSERT_TRUE(android::base::WriteFully(input_fd2.get(), java_data, sizeof(java_data)));\n  tombstoned_notify_completion(tombstoned_socket2.get());\n\n  char java_outbuf[sizeof(java_data)];\n  ASSERT_TRUE(android::base::ReadFully(output_fd2.get(), java_outbuf, sizeof(java_outbuf)));\n  ASSERT_STREQ(\"java\", java_outbuf);\n\n  const char tomb_data[] = \"tombstone\";\n  unique_fd tombstoned_socket3, input_fd3;\n  ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket3, &input_fd3, kDebuggerdTombstone));\n  ASSERT_TRUE(android::base::WriteFully(input_fd3.get(), tomb_data, sizeof(tomb_data)));\n  tombstoned_notify_completion(tombstoned_socket3.get());\n\n  char tomb_outbuf[sizeof(tomb_data)];\n  ASSERT_TRUE(android::base::ReadFully(output_fd3.get(), tomb_outbuf, sizeof(tomb_outbuf)));\n  ASSERT_STREQ(\"tombstone\", tomb_outbuf);\n}\n\nTEST(tombstoned, interceptless_backtrace) {\n  // Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones.\n  auto get_tombstone_timestamps = []() -> std::map<int, time_t> {\n    std::map<int, time_t> result;\n    for (int i = 0; i < 99; ++i) {\n      std::string path = android::base::StringPrintf(\"/data/tombstones/tombstone_%02d\", i);\n      struct stat st;\n      if (stat(path.c_str(), &st) == 0) {\n        result[i] = st.st_mtim.tv_sec;\n      }\n    }\n    return result;\n  };\n\n  auto before = get_tombstone_timestamps();\n  for (int i = 0; i < 50; ++i) {\n    raise_debugger_signal(kDebuggerdNativeBacktrace);\n  }\n  auto after = get_tombstone_timestamps();\n\n  int diff = 0;\n  for (int i = 0; i < 99; ++i) {\n    if (after.count(i) == 0) {\n      continue;\n    }\n    if (before.count(i) == 0) {\n      ++diff;\n      continue;\n    }\n    if (before[i] != after[i]) {\n      ++diff;\n    }\n  }\n\n  // We can't be sure that nothing's crash looping in the background.\n  // This should be good enough, though...\n  ASSERT_LT(diff, 10) << \"too many new tombstones; is something crashing in the background?\";\n}\n\nstatic __attribute__((__noinline__)) void overflow_stack(void* p) {\n  void* buf[1];\n  buf[0] = p;\n  static volatile void* global = buf;\n  if (global) {\n    global = buf;\n    overflow_stack(&buf);\n  }\n}\n\nTEST_F(CrasherTest, stack_overflow) {\n  int intercept_result;\n  unique_fd output_fd;\n  StartProcess([]() { overflow_stack(nullptr); });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(Cause: stack pointer[^\\n]*stack overflow.\\n)\");\n}\n\nstatic std::string GetTestLibraryPath() {\n  std::string test_lib(testing::internal::GetArgvs()[0]);\n  auto const value = test_lib.find_last_of('/');\n  if (value == std::string::npos) {\n    test_lib = \"./\";\n  } else {\n    test_lib = test_lib.substr(0, value + 1) + \"./\";\n  }\n  return test_lib + \"libcrash_test.so\";\n}\n\nstatic void CreateEmbeddedLibrary(int out_fd) {\n  std::string test_lib(GetTestLibraryPath());\n  android::base::unique_fd fd(open(test_lib.c_str(), O_RDONLY | O_CLOEXEC));\n  ASSERT_NE(fd.get(), -1);\n  off_t file_size = lseek(fd, 0, SEEK_END);\n  ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);\n  std::vector<uint8_t> contents(file_size);\n  ASSERT_TRUE(android::base::ReadFully(fd, contents.data(), contents.size()));\n\n  // Put the shared library data at a pagesize() offset.\n  ASSERT_EQ(lseek(out_fd, 4 * getpagesize(), SEEK_CUR), 4 * getpagesize());\n  ASSERT_EQ(static_cast<size_t>(write(out_fd, contents.data(), contents.size())), contents.size());\n}\n\nTEST_F(CrasherTest, non_zero_offset_in_library) {\n  int intercept_result;\n  unique_fd output_fd;\n  TemporaryFile tf;\n  CreateEmbeddedLibrary(tf.fd);\n  StartProcess([&tf]() {\n    android_dlextinfo extinfo{};\n    extinfo.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;\n    extinfo.library_fd = tf.fd;\n    extinfo.library_fd_offset = 4 * getpagesize();\n    void* handle = android_dlopen_ext(tf.path, RTLD_NOW, &extinfo);\n    if (handle == nullptr) {\n      _exit(1);\n    }\n    void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, \"crash\"));\n    if (crash_func == nullptr) {\n      _exit(1);\n    }\n    crash_func();\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  // Verify the crash includes an offset value in the backtrace.\n  std::string match_str = android::base::StringPrintf(\"%s\\\\!libcrash_test.so \\\\(offset 0x%x\\\\)\",\n                                                      tf.path, 4 * getpagesize());\n  ASSERT_MATCH(result, match_str);\n}\n\nstatic bool CopySharedLibrary(const char* tmp_dir, std::string* tmp_so_name) {\n  std::string test_lib(GetTestLibraryPath());\n\n  *tmp_so_name = std::string(tmp_dir) + \"/libcrash_test.so\";\n  std::string cp_cmd = android::base::StringPrintf(\"cp %s %s\", test_lib.c_str(), tmp_dir);\n\n  // Copy the shared so to a tempory directory.\n  return system(cp_cmd.c_str()) == 0;\n}\n\nTEST_F(CrasherTest, unreadable_elf) {\n  int intercept_result;\n  unique_fd output_fd;\n  std::string tmp_so_name;\n  StartProcess([&tmp_so_name]() {\n    TemporaryDir td;\n    if (!CopySharedLibrary(td.path, &tmp_so_name)) {\n      _exit(1);\n    }\n    void* handle = dlopen(tmp_so_name.c_str(), RTLD_NOW);\n    if (handle == nullptr) {\n      _exit(1);\n    }\n    // Delete the original shared library so that we get the warning\n    // about unreadable elf files.\n    if (unlink(tmp_so_name.c_str()) == -1) {\n      _exit(1);\n    }\n    void (*crash_func)() = reinterpret_cast<void (*)()>(dlsym(handle, \"crash\"));\n    if (crash_func == nullptr) {\n      _exit(1);\n    }\n    crash_func();\n  });\n\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(NOTE: Function names and BuildId information is missing )\");\n  std::string match_str = \"NOTE:   \" + tmp_so_name;\n  ASSERT_MATCH(result, match_str);\n}\n\nvoid CheckForTombstone(const struct stat& text_st, std::optional<std::string>& tombstone_file) {\n  static std::regex tombstone_re(\"tombstone_\\\\d+\");\n  std::unique_ptr<DIR, decltype(&closedir)> dir_h(opendir(\"/data/tombstones\"), closedir);\n  ASSERT_TRUE(dir_h != nullptr);\n  dirent* entry;\n  while ((entry = readdir(dir_h.get())) != nullptr) {\n    if (!std::regex_match(entry->d_name, tombstone_re)) {\n      continue;\n    }\n    std::string path = android::base::StringPrintf(\"/data/tombstones/%s\", entry->d_name);\n\n    struct stat st;\n    if (TEMP_FAILURE_RETRY(stat(path.c_str(), &st)) != 0) {\n      continue;\n    }\n\n    if (st.st_dev == text_st.st_dev && st.st_ino == text_st.st_ino) {\n      tombstone_file = path;\n      break;\n    }\n  }\n}\n\nTEST(tombstoned, proto) {\n  const pid_t self = getpid();\n  unique_fd tombstoned_socket, text_fd, proto_fd;\n  ASSERT_TRUE(\n      tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto));\n\n  tombstoned_notify_completion(tombstoned_socket.get());\n\n  ASSERT_NE(-1, text_fd.get());\n  ASSERT_NE(-1, proto_fd.get());\n\n  struct stat text_st;\n  ASSERT_EQ(0, fstat(text_fd.get(), &text_st));\n\n  std::optional<std::string> tombstone_file;\n  // Allow up to 5 seconds for the tombstone to be written to the system.\n  const auto max_wait_time = std::chrono::seconds(5) * android::base::HwTimeoutMultiplier();\n  const auto start = std::chrono::high_resolution_clock::now();\n  while (true) {\n    std::this_thread::sleep_for(100ms);\n    CheckForTombstone(text_st, tombstone_file);\n    if (tombstone_file) {\n      break;\n    }\n    if (std::chrono::high_resolution_clock::now() - start > max_wait_time) {\n      break;\n    }\n  }\n\n  ASSERT_TRUE(tombstone_file) << \"Timed out trying to find tombstone file.\";\n  std::string proto_path = tombstone_file.value() + \".pb\";\n\n  struct stat proto_fd_st;\n  struct stat proto_file_st;\n  ASSERT_EQ(0, fstat(proto_fd.get(), &proto_fd_st));\n  ASSERT_EQ(0, stat(proto_path.c_str(), &proto_file_st));\n\n  ASSERT_EQ(proto_fd_st.st_dev, proto_file_st.st_dev);\n  ASSERT_EQ(proto_fd_st.st_ino, proto_file_st.st_ino);\n}\n\nTEST(tombstoned, proto_intercept) {\n  const pid_t self = getpid();\n  unique_fd intercept_fd, output_fd;\n\n  InterceptResponse response = {};\n  tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdTombstone);\n  ASSERT_EQ(InterceptStatus::kRegistered, response.status)\n      << \"Error message: \" << response.error_message;\n\n  unique_fd tombstoned_socket, text_fd, proto_fd;\n  ASSERT_TRUE(\n      tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto));\n  ASSERT_TRUE(android::base::WriteStringToFd(\"foo\", text_fd.get()));\n  tombstoned_notify_completion(tombstoned_socket.get());\n\n  text_fd.reset();\n\n  std::string output;\n  ASSERT_TRUE(android::base::ReadFdToString(output_fd, &output));\n  ASSERT_EQ(\"foo\", output);\n}\n\n// Verify that when an intercept is present for the main thread, and the signal\n// is received on a different thread, the intercept still works.\nTEST_F(CrasherTest, intercept_for_main_thread_signal_on_side_thread) {\n  StartProcess([]() {\n    std::thread thread([]() {\n      // Raise the signal on the side thread.\n      raise_debugger_signal(kDebuggerdNativeBacktrace);\n    });\n    thread.join();\n    _exit(0);\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd, kDebuggerdNativeBacktrace);\n  FinishCrasher();\n  AssertDeath(0);\n\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"raise_debugger_signal\");\n}\n\nstatic std::string format_pointer(uintptr_t ptr) {\n#if defined(__LP64__)\n  return android::base::StringPrintf(\"%08x'%08x\", static_cast<uint32_t>(ptr >> 32),\n                                     static_cast<uint32_t>(ptr & 0xffffffff));\n#else\n  return android::base::StringPrintf(\"%08x\", static_cast<uint32_t>(ptr & 0xffffffff));\n#endif\n}\n\nstatic std::string format_pointer(void* ptr) {\n  return format_pointer(reinterpret_cast<uintptr_t>(ptr));\n}\n\nstatic std::string format_full_pointer(uintptr_t ptr) {\n#if defined(__LP64__)\n  return android::base::StringPrintf(\"%016\" PRIx64, ptr);\n#else\n  return android::base::StringPrintf(\"%08x\", ptr);\n#endif\n}\n\nstatic std::string format_full_pointer(void* ptr) {\n  return format_full_pointer(reinterpret_cast<uintptr_t>(ptr));\n}\n\n__attribute__((__noinline__)) int crash_call(uintptr_t ptr) {\n  int* crash_ptr = reinterpret_cast<int*>(ptr);\n  *crash_ptr = 1;\n  return *crash_ptr;\n}\n\n// Verify that a fault address before the first map is properly handled.\nTEST_F(CrasherTest, fault_address_before_first_map) {\n  StartProcess([]() {\n    ASSERT_EQ(0, crash_call(0x1024));\n    _exit(0);\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\), code 1 \\(SEGV_MAPERR\\), fault addr 0x0+1024)\");\n\n  ASSERT_MATCH(result, R\"(\\nmemory map \\(.*\\):\\n)\");\n\n  std::string match_str = android::base::StringPrintf(\n      R\"(memory map .*:\\n--->Fault address falls at %s before any mapped regions\\n    )\",\n      format_pointer(0x1024).c_str());\n  ASSERT_MATCH(result, match_str);\n}\n\n// Verify that a fault address after the last map is properly handled.\nTEST_F(CrasherTest, fault_address_after_last_map) {\n  // This makes assumptions about the memory layout that are not true in HWASan\n  // processes.\n  SKIP_WITH_HWASAN;\n  uintptr_t crash_uptr = untag_address(UINTPTR_MAX - 15);\n  StartProcess([crash_uptr]() {\n    ASSERT_EQ(0, crash_call(crash_uptr));\n    _exit(0);\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  std::string match_str = R\"(signal 11 \\(SIGSEGV\\), code 1 \\(SEGV_MAPERR\\), fault addr 0x)\";\n  match_str += format_full_pointer(crash_uptr);\n  ASSERT_MATCH(result, match_str);\n\n  ASSERT_MATCH(result, R\"(\\nmemory map \\(.*\\): \\(fault address prefixed with --->\\)\\n)\");\n\n  // Verifies that the fault address error message is at the end of the\n  // maps section. To do this, the check below looks for the start of the\n  // open files section or the start of the log file section. It's possible\n  // for either of these sections to be present after the maps section right\n  // now.\n  // If the sections move around, this check might need to be modified.\n  match_str = android::base::StringPrintf(\n      R\"(\\n--->Fault address falls at %s after any mapped regions\\n(---------|\\nopen files:))\",\n      format_pointer(crash_uptr).c_str());\n  ASSERT_MATCH(result, match_str);\n}\n\n// Verify that a fault address between maps is properly handled.\nTEST_F(CrasherTest, fault_address_between_maps) {\n  // Create a map before the fork so it will be present in the child.\n  void* start_ptr =\n      mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n  ASSERT_NE(MAP_FAILED, start_ptr);\n  // Add a name to guarantee that this map is distinct and not combined in the map listing.\n  EXPECT_EQ(\n      prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start_ptr, 3 * getpagesize(), \"debuggerd map start\"),\n      0);\n  // Unmap the page in the middle.\n  void* middle_ptr =\n      reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + getpagesize());\n  ASSERT_EQ(0, munmap(middle_ptr, getpagesize()));\n\n  StartProcess([middle_ptr]() {\n    ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(middle_ptr)));\n    _exit(0);\n  });\n\n  // Unmap the two maps.\n  ASSERT_EQ(0, munmap(start_ptr, getpagesize()));\n  void* end_ptr =\n      reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start_ptr) + 2 * getpagesize());\n  ASSERT_EQ(0, munmap(end_ptr, getpagesize()));\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  std::string match_str = R\"(signal 11 \\(SIGSEGV\\), code 1 \\(SEGV_MAPERR\\), fault addr 0x)\";\n  match_str += format_full_pointer(reinterpret_cast<uintptr_t>(middle_ptr));\n  ASSERT_MATCH(result, match_str);\n\n  ASSERT_MATCH(result, R\"(\\nmemory map \\(.*\\): \\(fault address prefixed with --->\\)\\n)\");\n\n  match_str = android::base::StringPrintf(\n      R\"(    %s.*\\n--->Fault address falls at %s between mapped regions\\n    %s)\",\n      format_pointer(start_ptr).c_str(), format_pointer(middle_ptr).c_str(),\n      format_pointer(end_ptr).c_str());\n  ASSERT_MATCH(result, match_str);\n}\n\n// Verify that a fault address happens in the correct map.\nTEST_F(CrasherTest, fault_address_in_map) {\n  // Create a map before the fork so it will be present in the child.\n  void* ptr = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n  ASSERT_NE(MAP_FAILED, ptr);\n  // Add a name to guarantee that this map is distinct and not combined in the map listing.\n  EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, getpagesize(), \"debuggerd map\"), 0);\n\n  StartProcess([ptr]() {\n    ASSERT_EQ(0, crash_call(reinterpret_cast<uintptr_t>(ptr)));\n    _exit(0);\n  });\n\n  ASSERT_EQ(0, munmap(ptr, getpagesize()));\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  std::string match_str = R\"(signal 11 \\(SIGSEGV\\), code 2 \\(SEGV_ACCERR\\), fault addr 0x)\";\n  match_str += format_full_pointer(reinterpret_cast<uintptr_t>(ptr));\n  ASSERT_MATCH(result, match_str);\n\n  ASSERT_MATCH(result, R\"(\\nmemory map \\(.*\\): \\(fault address prefixed with --->\\)\\n)\");\n\n  match_str = android::base::StringPrintf(R\"(\\n--->%s.*\\n)\", format_pointer(ptr).c_str());\n  ASSERT_MATCH(result, match_str);\n}\n\nstatic constexpr uint32_t kDexData[] = {\n    0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,\n    0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,\n    0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,\n    0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,\n    0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,\n    0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,\n    0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,\n    0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,\n    0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,\n    0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,\n    0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,\n    0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,\n    0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,\n    0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,\n    0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,\n    0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,\n    0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,\n};\n\nTEST_F(CrasherTest, verify_dex_pc_with_function_name) {\n  StartProcess([]() {\n    TemporaryDir td;\n    std::string tmp_so_name;\n    if (!CopySharedLibrary(td.path, &tmp_so_name)) {\n      _exit(1);\n    }\n\n    // In order to cause libunwindstack to look for this __dex_debug_descriptor\n    // move the library to which has a basename of libart.so.\n    std::string art_so_name = android::base::Dirname(tmp_so_name) + \"/libart.so\";\n    ASSERT_EQ(0, rename(tmp_so_name.c_str(), art_so_name.c_str()));\n    void* handle = dlopen(art_so_name.c_str(), RTLD_NOW | RTLD_LOCAL);\n    if (handle == nullptr) {\n      _exit(1);\n    }\n\n    void* ptr =\n        mmap(nullptr, sizeof(kDexData), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n    ASSERT_TRUE(ptr != MAP_FAILED);\n    memcpy(ptr, kDexData, sizeof(kDexData));\n    EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, sizeof(kDexData), \"dex\"), 0);\n\n    JITCodeEntry dex_entry = {.symfile_addr = reinterpret_cast<uintptr_t>(ptr),\n                              .symfile_size = sizeof(kDexData)};\n\n    JITDescriptor* dex_debug =\n        reinterpret_cast<JITDescriptor*>(dlsym(handle, \"__dex_debug_descriptor\"));\n    ASSERT_TRUE(dex_debug != nullptr);\n    dex_debug->version = 1;\n    dex_debug->action_flag = 0;\n    dex_debug->relevant_entry = 0;\n    dex_debug->first_entry = reinterpret_cast<uintptr_t>(&dex_entry);\n\n    // This sets the magic dex pc value for register 0, using the value\n    // of register 1 + 0x102.\n    asm(\".cfi_escape \"\n        \"0x16 /* DW_CFA_val_expression */, 0, 0x0a /* size */,\"\n        \"0x0c /* DW_OP_const4u */, 0x44, 0x45, 0x58, 0x31, /* magic = 'DEX1' */\"\n        \"0x13 /* DW_OP_drop */,\"\n        \"0x92 /* DW_OP_bregx */, 1, 0x82, 0x02 /* 2-byte SLEB128 */\");\n\n    // For each different architecture, set register one to the dex ptr mmap\n    // created above. Then do a nullptr dereference to force a crash.\n#if defined(__arm__)\n    asm volatile(\n        \"mov r1, %[base]\\n\"\n        \"mov r2, #0\\n\"\n        \"str r2, [r2]\\n\"\n        : [base] \"+r\"(ptr)\n        :\n        : \"r1\", \"r2\", \"memory\");\n#elif defined(__aarch64__)\n    asm volatile(\n        \"mov x1, %[base]\\n\"\n        \"mov x2, #0\\n\"\n        \"str xzr, [x2]\\n\"\n        : [base] \"+r\"(ptr)\n        :\n        : \"x1\", \"x2\", \"memory\");\n#elif defined(__riscv)\n    // TODO: x1 is ra (the link register) on riscv64, so this might have\n    // unintended consequences, but we'll need to change the .cfi_escape if so.\n    asm volatile(\n        \"mv x1, %[base]\\n\"\n        \"sw zero, 0(zero)\\n\"\n        : [base] \"+r\"(ptr)\n        :\n        : \"x1\", \"memory\");\n#elif defined(__i386__)\n    asm volatile(\n        \"mov %[base], %%ecx\\n\"\n        \"movl $0, 0\\n\"\n        : [base] \"+r\"(ptr)\n        :\n        : \"ecx\", \"memory\");\n#elif defined(__x86_64__)\n    asm volatile(\n        \"mov %[base], %%rdx\\n\"\n        \"movq $0, 0\\n\"\n        : [base] \"+r\"(ptr)\n        :\n        : \"rdx\", \"memory\");\n#else\n#error \"Unsupported architecture\"\n#endif\n    _exit(0);\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGSEGV);\n\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  // Verify the process crashed properly.\n  ASSERT_MATCH(result, R\"(signal 11 \\(SIGSEGV\\), code 1 \\(SEGV_MAPERR\\), fault addr 0x0*)\");\n\n  // Now verify that the dex_pc frame includes a proper function name.\n  ASSERT_MATCH(result, R\"( \\[anon:dex\\] \\(Main\\.\\<init\\>\\+2)\");\n}\n\nstatic std::string format_map_pointer(uintptr_t ptr) {\n#if defined(__LP64__)\n  return android::base::StringPrintf(\"%08x'%08x\", static_cast<uint32_t>(ptr >> 32),\n                                     static_cast<uint32_t>(ptr & 0xffffffff));\n#else\n  return android::base::StringPrintf(\"%08x\", ptr);\n#endif\n}\n\n// Verify that map data is properly formatted.\nTEST_F(CrasherTest, verify_map_format) {\n  // Create multiple maps to make sure that the map data is formatted properly.\n  void* none_map = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n  ASSERT_NE(MAP_FAILED, none_map);\n  // Add names to guarantee that the maps are distinct and not combined in the map listing.\n  EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, none_map, getpagesize(), \"debuggerd map none\"),\n            0);\n  void* r_map = mmap(nullptr, getpagesize(), PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n  ASSERT_NE(MAP_FAILED, r_map);\n  EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, r_map, getpagesize(), \"debuggerd map r\"), 0);\n  void* w_map = mmap(nullptr, getpagesize(), PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n  EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, w_map, getpagesize(), \"debuggerd map w\"), 0);\n  ASSERT_NE(MAP_FAILED, w_map);\n  void* x_map = mmap(nullptr, getpagesize(), PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n  ASSERT_NE(MAP_FAILED, x_map);\n  EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, x_map, getpagesize(), \"debuggerd map x\"), 0);\n\n  TemporaryFile tf;\n  ASSERT_EQ(0x2000, lseek(tf.fd, 0x2000, SEEK_SET));\n  char c = 'f';\n  ASSERT_EQ(1, write(tf.fd, &c, 1));\n  ASSERT_EQ(0x5000, lseek(tf.fd, 0x5000, SEEK_SET));\n  ASSERT_EQ(1, write(tf.fd, &c, 1));\n  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));\n  void* file_map = mmap(nullptr, 0x3001, PROT_READ, MAP_PRIVATE, tf.fd, 0x2000);\n  ASSERT_NE(MAP_FAILED, file_map);\n\n  StartProcess([]() { abort(); });\n\n  ASSERT_EQ(0, munmap(none_map, getpagesize()));\n  ASSERT_EQ(0, munmap(r_map, getpagesize()));\n  ASSERT_EQ(0, munmap(w_map, getpagesize()));\n  ASSERT_EQ(0, munmap(x_map, getpagesize()));\n  ASSERT_EQ(0, munmap(file_map, 0x3001));\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  std::string match_str;\n  // Verify none.\n  match_str = android::base::StringPrintf(\n      \"    %s-%s ---         0      %x  \\\\[anon:debuggerd map none\\\\]\\\\n\",\n      format_map_pointer(reinterpret_cast<uintptr_t>(none_map)).c_str(),\n      format_map_pointer(reinterpret_cast<uintptr_t>(none_map) + getpagesize() - 1).c_str(),\n      getpagesize());\n  ASSERT_MATCH(result, match_str);\n\n  // Verify read-only.\n  match_str = android::base::StringPrintf(\n      \"    %s-%s r--         0      %x  \\\\[anon:debuggerd map r\\\\]\\\\n\",\n      format_map_pointer(reinterpret_cast<uintptr_t>(r_map)).c_str(),\n      format_map_pointer(reinterpret_cast<uintptr_t>(r_map) + getpagesize() - 1).c_str(),\n      getpagesize());\n  ASSERT_MATCH(result, match_str);\n\n  // Verify write-only.\n  match_str = android::base::StringPrintf(\n      \"    %s-%s -w-         0      %x  \\\\[anon:debuggerd map w\\\\]\\\\n\",\n      format_map_pointer(reinterpret_cast<uintptr_t>(w_map)).c_str(),\n      format_map_pointer(reinterpret_cast<uintptr_t>(w_map) + getpagesize() - 1).c_str(),\n      getpagesize());\n  ASSERT_MATCH(result, match_str);\n\n  // Verify exec-only.\n  match_str = android::base::StringPrintf(\n      \"    %s-%s --x         0      %x  \\\\[anon:debuggerd map x\\\\]\\\\n\",\n      format_map_pointer(reinterpret_cast<uintptr_t>(x_map)).c_str(),\n      format_map_pointer(reinterpret_cast<uintptr_t>(x_map) + getpagesize() - 1).c_str(),\n      getpagesize());\n  ASSERT_MATCH(result, match_str);\n\n  // Verify file map with non-zero offset and a name.\n  match_str = android::base::StringPrintf(\n      \"    %s-%s r--      2000      4000  %s\\\\n\",\n      format_map_pointer(reinterpret_cast<uintptr_t>(file_map)).c_str(),\n      format_map_pointer(reinterpret_cast<uintptr_t>(file_map) + 0x3fff).c_str(), tf.path);\n  ASSERT_MATCH(result, match_str);\n}\n\n// Verify that the tombstone map data is correct.\nTEST_F(CrasherTest, verify_header) {\n  StartProcess([]() { abort(); });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  std::string match_str = android::base::StringPrintf(\n      \"Build fingerprint: '%s'\\\\nRevision: '%s'\\\\n\",\n      android::base::GetProperty(\"ro.build.fingerprint\", \"unknown\").c_str(),\n      android::base::GetProperty(\"ro.revision\", \"unknown\").c_str());\n  match_str += android::base::StringPrintf(\"ABI: '%s'\\n\", ABI_STRING);\n  ASSERT_MATCH(result, match_str);\n}\n\n// Verify that the thread header is formatted properly.\nTEST_F(CrasherTest, verify_thread_header) {\n  void* shared_map =\n      mmap(nullptr, sizeof(pid_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);\n  ASSERT_NE(MAP_FAILED, shared_map);\n  memset(shared_map, 0, sizeof(pid_t));\n\n  StartProcess([&shared_map]() {\n    std::atomic_bool tid_written;\n    std::thread thread([&tid_written, &shared_map]() {\n      pid_t tid = gettid();\n      memcpy(shared_map, &tid, sizeof(pid_t));\n      tid_written = true;\n      volatile bool done = false;\n      while (!done)\n        ;\n    });\n    thread.detach();\n    while (!tid_written.load(std::memory_order_acquire))\n      ;\n    abort();\n  });\n\n  pid_t primary_pid = crasher_pid;\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  // Read the tid data out.\n  pid_t tid;\n  memcpy(&tid, shared_map, sizeof(pid_t));\n  ASSERT_NE(0, tid);\n\n  ASSERT_EQ(0, munmap(shared_map, sizeof(pid_t)));\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  // Verify that there are two headers, one where the tid is \"primary_pid\"\n  // and the other where the tid is \"tid\".\n  std::string match_str = android::base::StringPrintf(\"pid: %d, tid: %d, name: .*  >>> .* <<<\\\\n\",\n                                                      primary_pid, primary_pid);\n  ASSERT_MATCH(result, match_str);\n\n  match_str =\n      android::base::StringPrintf(\"pid: %d, tid: %d, name: .*  >>> .* <<<\\\\n\", primary_pid, tid);\n  ASSERT_MATCH(result, match_str);\n}\n\n// Verify that there is a BuildID present in the map section and set properly.\nTEST_F(CrasherTest, verify_build_id) {\n  StartProcess([]() { abort(); });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n\n  // Find every /system or /apex lib and verify the BuildID is displayed\n  // properly.\n  bool found_valid_elf = false;\n  std::smatch match;\n  std::regex build_id_regex(R\"(  ((/system/|/apex/)\\S+) \\(BuildId: ([^\\)]+)\\))\");\n  for (std::string prev_file; std::regex_search(result, match, build_id_regex);\n       result = match.suffix()) {\n    if (prev_file == match[1]) {\n      // Already checked this file.\n      continue;\n    }\n\n    prev_file = match[1];\n    auto elf_memory = unwindstack::Memory::CreateFileMemory(prev_file, 0);\n    unwindstack::Elf elf(elf_memory);\n    if (!elf.Init() || !elf.valid()) {\n      // Skipping invalid elf files.\n      continue;\n    }\n    ASSERT_EQ(match[3], elf.GetPrintableBuildID());\n\n    found_valid_elf = true;\n  }\n  ASSERT_TRUE(found_valid_elf) << \"Did not find any elf files with valid BuildIDs to check.\";\n}\n\nconst char kLogMessage[] = \"Should not see this log message.\";\n\n// Verify that the logd process does not read the log.\nTEST_F(CrasherTest, logd_skips_reading_logs) {\n  StartProcess([]() {\n    pthread_setname_np(pthread_self(), \"logd\");\n    LOG(INFO) << kLogMessage;\n    abort();\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  // logd should not contain our log message.\n  ASSERT_NOT_MATCH(result, kLogMessage);\n}\n\n// Verify that the logd process does not read the log when the non-main\n// thread crashes.\nTEST_F(CrasherTest, logd_skips_reading_logs_not_main_thread) {\n  StartProcess([]() {\n    pthread_setname_np(pthread_self(), \"logd\");\n    LOG(INFO) << kLogMessage;\n\n    std::thread thread([]() {\n      pthread_setname_np(pthread_self(), \"not_logd_thread\");\n      // Raise the signal on the side thread.\n      raise_debugger_signal(kDebuggerdTombstone);\n    });\n    thread.join();\n    _exit(0);\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd, kDebuggerdTombstone);\n  FinishCrasher();\n  AssertDeath(0);\n\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_BACKTRACE_FRAME(result, \"raise_debugger_signal\");\n  ASSERT_NOT_MATCH(result, kLogMessage);\n}\n\n// Disable this test since there is a high liklihood that this would\n// be flaky since it requires 500 messages being in the log.\nTEST_F(CrasherTest, DISABLED_max_log_messages) {\n  StartProcess([]() {\n    for (size_t i = 0; i < 600; i++) {\n      LOG(INFO) << \"Message number \" << i;\n    }\n    abort();\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_NOT_MATCH(result, \"Message number 99\");\n  ASSERT_MATCH(result, \"Message number 100\");\n  ASSERT_MATCH(result, \"Message number 599\");\n}\n\nTEST_F(CrasherTest, log_with_newline) {\n  StartProcess([]() {\n    LOG(INFO) << \"This line has a newline.\\nThis is on the next line.\";\n    abort();\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  ASSERT_MATCH(result, \":\\\\s*This line has a newline.\");\n  ASSERT_MATCH(result, \":\\\\s*This is on the next line.\");\n}\n\nTEST_F(CrasherTest, log_with_non_printable_ascii_verify_encoded) {\n  static const std::string kEncodedStr =\n      \"\\x5C\\x31\"\n      \"\\x5C\\x32\"\n      \"\\x5C\\x33\"\n      \"\\x5C\\x34\"\n      \"\\x5C\\x35\"\n      \"\\x5C\\x36\"\n      \"\\x5C\\x37\"\n      \"\\x5C\\x31\\x30\"\n      \"\\x5C\\x31\\x36\"\n      \"\\x5C\\x31\\x37\"\n      \"\\x5C\\x32\\x30\"\n      \"\\x5C\\x32\\x31\"\n      \"\\x5C\\x32\\x32\"\n      \"\\x5C\\x32\\x33\"\n      \"\\x5C\\x32\\x34\"\n      \"\\x5C\\x32\\x35\"\n      \"\\x5C\\x32\\x36\"\n      \"\\x5C\\x32\\x37\"\n      \"\\x5C\\x33\\x30\"\n      \"\\x5C\\x33\\x31\"\n      \"\\x5C\\x33\\x32\"\n      \"\\x5C\\x33\\x33\"\n      \"\\x5C\\x33\\x34\"\n      \"\\x5C\\x33\\x35\"\n      \"\\x5C\\x33\\x36\"\n      \"\\x5C\\x33\\x37\"\n      \"\\x5C\\x31\\x37\\x37\"\n      \"\\x5C\\x32\\x34\\x30\"\n      \"\\x5C\\x32\\x36\\x30\"\n      \"\\x5C\\x33\\x30\\x30\"\n      \"\\x5C\\x33\\x32\\x30\";\n  StartProcess([]() {\n    LOG(FATAL) << \"Encoded: \"\n                  \"\\x1\\x2\\x3\\x4\\x5\\x6\\x7\\x8\\xe\\xf\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\"\n                  \"\\x1c\\x1d\\x1e\\x1f\\x7f\\xA0\\xB0\\xC0\\xD0 after\";\n  });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  // Verify the abort message is sanitized properly.\n  size_t pos = result.find(std::string(\"Abort message: 'Encoded: \") + kEncodedStr + \" after'\");\n  EXPECT_TRUE(pos != std::string::npos) << \"Couldn't find sanitized abort message: \" << result;\n\n  // Make sure that the log message is sanitized properly too.\n  EXPECT_TRUE(result.find(std::string(\"Encoded: \") + kEncodedStr + \" after\", pos + 1) !=\n              std::string::npos)\n      << \"Couldn't find sanitized log message: \" << result;\n}\n\nTEST_F(CrasherTest, log_with_with_special_printable_ascii) {\n  static const std::string kMsg = \"Not encoded: \\t\\v\\f\\r\\n after\";\n  StartProcess([]() { LOG(FATAL) << kMsg; });\n\n  unique_fd output_fd;\n  StartIntercept(&output_fd);\n  FinishCrasher();\n  AssertDeath(SIGABRT);\n  int intercept_result;\n  FinishIntercept(&intercept_result);\n  ASSERT_EQ(1, intercept_result) << \"tombstoned reported failure\";\n\n  std::string result;\n  ConsumeFd(std::move(output_fd), &result);\n  // Verify the abort message does not remove characters that are UTF8 but\n  // are, technically, not printable.\n  size_t pos = result.find(std::string(\"Abort message: '\") + kMsg + \"'\");\n  EXPECT_TRUE(pos != std::string::npos) << \"Couldn't find abort message: \" << result;\n\n  // Make sure that the log message is handled properly too.\n  // The logger automatically splits a newline message into two pieces.\n  pos = result.find(\"Not encoded: \\t\\v\\f\\r\", pos + kMsg.size());\n  EXPECT_TRUE(pos != std::string::npos) << \"Couldn't find log message: \" << result;\n  EXPECT_TRUE(result.find(\" after\", pos + 1) != std::string::npos)\n      << \"Couldn't find sanitized log message: \" << result;\n}\n"
  },
  {
    "path": "debuggerd/handler/debuggerd_fallback.cpp",
    "content": "/*\n * Copyright 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <poll.h>\n#include <pthread.h>\n#include <stddef.h>\n#include <sys/ucontext.h>\n#include <syscall.h>\n#include <unistd.h>\n\n#include <atomic>\n#include <memory>\n#include <mutex>\n\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n#include <async_safe/log.h>\n#include <bionic/reserved_signals.h>\n#include <unwindstack/AndroidUnwinder.h>\n#include <unwindstack/Memory.h>\n#include <unwindstack/Regs.h>\n\n#include \"debuggerd/handler.h\"\n#include \"handler/fallback.h\"\n#include \"tombstoned/tombstoned.h\"\n#include \"util.h\"\n\n#include \"libdebuggerd/backtrace.h\"\n#include \"libdebuggerd/tombstone.h\"\n\nusing android::base::unique_fd;\n\nextern \"C\" bool __linker_enable_fallback_allocator();\nextern \"C\" void __linker_disable_fallback_allocator();\n\n// This file implements a fallback path for processes that do not allow the\n// normal fork and exec of crash_dump to handle crashes/unwinds.\n// The issue is that all of this happens from within a signal handler, which\n// can cause problems since this code uses the linker allocator which is not\n// thread safe. In order to avoid any problems allocating, the code calls\n// a function to switch to use a fallback allocator in the linker that will\n// only be used for the current thread. All of the libunwindstack code does\n// allocations using C++ stl, but should be fine since the code runs in the\n// linker and should use the fallback handler.\n\n// This method can still fail if the virtual space is exhausted on a 32 bit\n// process or mmap failing due to hitting the maximum number of maps (65535\n// total maps) on a 64 bit process.\n\n// Class to handle automatically turning on and off the fallback allocator.\nclass ScopedUseFallbackAllocator {\n public:\n  ScopedUseFallbackAllocator() { Enable(); }\n\n  ~ScopedUseFallbackAllocator() { Disable(); }\n\n  bool Enable() {\n    if (!enabled_) {\n      enabled_ = __linker_enable_fallback_allocator();\n      if (!enabled_) {\n        async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                              \"Unable to enable fallback allocator, already in use.\");\n      }\n    }\n    return enabled_;\n  }\n\n  void Disable() {\n    if (enabled_) {\n      __linker_disable_fallback_allocator();\n      enabled_ = false;\n    }\n  }\n\n  bool enabled() { return enabled_; }\n\n private:\n  bool enabled_ = false;\n};\n\nstatic void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) {\n  std::unique_ptr<unwindstack::Regs> regs;\n\n  ThreadInfo thread;\n  thread.pid = getpid();\n  thread.tid = gettid();\n  thread.thread_name = get_thread_name(gettid());\n  thread.registers.reset(\n      unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));\n\n  // Do not use the thread cache here because it will call pthread_key_create\n  // which doesn't work in linker code. See b/189803009.\n  // Use a normal cached object because the thread is stopped, and there\n  // is no chance of data changing between reads.\n  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());\n  // TODO: Create this once and store it in a global?\n  unwindstack::AndroidLocalUnwinder unwinder(process_memory);\n  dump_backtrace_thread(output_fd, &unwinder, thread);\n}\n\nstatic bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {\n  // Make sure the thread actually got the signal.\n  struct pollfd pfd = {\n    .fd = src_fd, .events = POLLIN,\n  };\n\n  // Wait for up to a second for output to start flowing.\n  if (poll(&pfd, 1, 1000) != 1) {\n    return false;\n  }\n\n  pid_t tid;\n  if (TEMP_FAILURE_RETRY(read(src_fd, &tid, sizeof(tid))) != sizeof(tid)) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"failed to read tid\");\n    return false;\n  }\n\n  if (tid != expected_tid) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"received tid %d, expected %d\", tid,\n                          expected_tid);\n    return false;\n  }\n\n  while (true) {\n    char buf[512];\n    ssize_t rc = TEMP_FAILURE_RETRY(read(src_fd, buf, sizeof(buf)));\n    if (rc == 0) {\n      return true;\n    } else if (rc < 0) {\n      return false;\n    }\n\n    if (!android::base::WriteFully(dst_fd, buf, rc)) {\n      // We failed to write to tombstoned, but there's not much we can do.\n      // Keep reading from src_fd to keep things going.\n      continue;\n    }\n  }\n}\n\nstruct __attribute__((__packed__)) packed_thread_output {\n  int32_t tid;\n  int32_t fd;\n};\n\nstatic uint64_t pack_thread_fd(pid_t tid, int fd) {\n  packed_thread_output packed = {.tid = tid, .fd = fd};\n  uint64_t result;\n  static_assert(sizeof(packed) == sizeof(result));\n  memcpy(&result, &packed, sizeof(packed));\n  return result;\n}\n\nstatic std::pair<pid_t, int> unpack_thread_fd(uint64_t value) {\n  packed_thread_output result;\n  memcpy(&result, &value, sizeof(value));\n  return std::make_pair(result.tid, result.fd);\n}\n\nstatic void trace_handler(siginfo_t* info, ucontext_t* ucontext) {\n  ScopedUseFallbackAllocator allocator;\n  if (!allocator.enabled()) {\n    return;\n  }\n\n  static std::atomic<uint64_t> trace_output(pack_thread_fd(-1, -1));\n\n  if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) {\n    // Asked to dump by the original signal recipient.\n    uint64_t val = trace_output.load();\n    auto [tid, fd] = unpack_thread_fd(val);\n    if (tid != gettid()) {\n      // We received some other thread's info request?\n      async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                            \"thread %d received output fd for thread %d?\", gettid(), tid);\n      return;\n    }\n\n    if (!trace_output.compare_exchange_strong(val, pack_thread_fd(-1, -1))) {\n      // Presumably, the timeout in forward_output expired, and the main thread moved on.\n      // If this happened, the main thread closed our fd for us, so just return.\n      async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"cmpxchg for thread %d failed\", gettid());\n      return;\n    }\n\n    // Write our tid to the output fd to let the main thread know that we're working.\n    if (TEMP_FAILURE_RETRY(write(fd, &tid, sizeof(tid))) == sizeof(tid)) {\n      debuggerd_fallback_trace(fd, ucontext);\n    } else {\n      async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"failed to write to output fd\");\n    }\n\n    // Stop using the fallback allocator before the close. This will prevent\n    // a race condition where the thread backtracing all of the threads tries\n    // to re-acquire the fallback allocator.\n    allocator.Disable();\n\n    close(fd);\n    return;\n  }\n\n  // Only allow one thread to perform a trace at a time.\n  static std::mutex trace_mutex;\n  if (!trace_mutex.try_lock()) {\n    async_safe_format_log(ANDROID_LOG_INFO, \"libc\", \"trace lock failed\");\n    return;\n  }\n\n  std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);\n\n  // Fetch output fd from tombstoned.\n  unique_fd tombstone_socket, output_fd;\n  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,\n                          kDebuggerdNativeBacktrace)) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                          \"missing crash_dump_fallback() in selinux policy?\");\n    return;\n  }\n\n  dump_backtrace_header(output_fd.get());\n\n  // Dump our own stack.\n  debuggerd_fallback_trace(output_fd.get(), ucontext);\n\n  // Send a signal to all of our siblings, asking them to dump their stack.\n  pid_t current_tid = gettid();\n  if (!iterate_tids(current_tid, [&allocator, &output_fd, &current_tid](pid_t tid) {\n        if (current_tid == tid) {\n          return;\n        }\n\n        if (!allocator.enabled()) {\n          return;\n        }\n\n        // Use a pipe, to be able to detect situations where the thread gracefully exits before\n        // receiving our signal.\n        unique_fd pipe_read, pipe_write;\n        if (!Pipe(&pipe_read, &pipe_write)) {\n          async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"failed to create pipe: %s\",\n                                strerror(errno));\n          return;\n        }\n\n        uint64_t expected = pack_thread_fd(-1, -1);\n        int sent_fd = pipe_write.release();\n        if (!trace_output.compare_exchange_strong(expected, pack_thread_fd(tid, sent_fd))) {\n          auto [tid, fd] = unpack_thread_fd(expected);\n          async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                                \"thread %d is already outputting to fd %d?\", tid, fd);\n          close(sent_fd);\n          return;\n        }\n\n        // Disable our use of the fallback allocator while the target thread\n        // is getting the backtrace.\n        allocator.Disable();\n\n        siginfo_t siginfo = {};\n        siginfo.si_code = SI_QUEUE;\n        siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump;\n        siginfo.si_pid = getpid();\n        siginfo.si_uid = getuid();\n\n        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) == 0) {\n          if (!forward_output(pipe_read.get(), output_fd.get(), tid)) {\n            async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                                  \"timeout expired while waiting for thread %d to dump\", tid);\n          }\n        } else {\n          async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"failed to send trace signal to %d: %s\",\n                                tid, strerror(errno));\n        }\n\n        // The thread should be finished now, so try and re-enable the fallback allocator.\n        if (!allocator.Enable()) {\n          return;\n        }\n\n        // Regardless of whether the poll succeeds, check to see if the thread took fd ownership.\n        uint64_t post_wait = trace_output.exchange(pack_thread_fd(-1, -1));\n        if (post_wait != pack_thread_fd(-1, -1)) {\n          auto [tid, fd] = unpack_thread_fd(post_wait);\n          if (fd != -1) {\n            async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"closing fd %d for thread %d\", fd, tid);\n            close(fd);\n          }\n        }\n      })) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"failed to open /proc/%d/task: %s\",\n                          current_tid, strerror(errno));\n  }\n\n  if (allocator.enabled()) {\n    dump_backtrace_footer(output_fd.get());\n  }\n\n  tombstoned_notify_completion(tombstone_socket.get());\n}\n\nstatic void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {\n  // Only allow one thread to handle a crash at a time (this can happen multiple times without\n  // exit, since tombstones can be requested without a real crash happening.)\n  static std::recursive_mutex crash_mutex;\n  static int lock_count;\n\n  crash_mutex.lock();\n  if (lock_count++ > 0) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"recursed signal handler call, aborting\");\n    signal(SIGABRT, SIG_DFL);\n    raise(SIGABRT);\n    sigset_t sigset;\n    sigemptyset(&sigset);\n    sigaddset(&sigset, SIGABRT);\n    sigprocmask(SIG_UNBLOCK, &sigset, nullptr);\n\n    // Just in case...\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"abort didn't exit, exiting\");\n    _exit(1);\n  }\n\n  unique_fd tombstone_socket, output_fd, proto_fd;\n  bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd,\n                                                 kDebuggerdTombstoneProto);\n  {\n    ScopedUseFallbackAllocator allocator;\n    if (allocator.enabled()) {\n      engrave_tombstone_ucontext(output_fd.get(), proto_fd.get(),\n                                 reinterpret_cast<uintptr_t>(abort_message), info, ucontext);\n    }\n  }\n  if (tombstoned_connected) {\n    tombstoned_notify_completion(tombstone_socket.get());\n  }\n\n  --lock_count;\n  crash_mutex.unlock();\n}\n\nextern \"C\" void debuggerd_fallback_handler(siginfo_t* info, ucontext_t* ucontext,\n                                           void* abort_message) {\n  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER && info->si_value.sival_ptr != nullptr) {\n    return trace_handler(info, ucontext);\n  } else {\n    return crash_handler(info, ucontext, abort_message);\n  }\n}\n"
  },
  {
    "path": "debuggerd/handler/debuggerd_fallback_nop.cpp",
    "content": "/*\n * Copyright 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nextern \"C\" void debuggerd_fallback_handler(struct siginfo_t*, struct ucontext_t*, void*) {\n}\n"
  },
  {
    "path": "debuggerd/handler/debuggerd_handler.cpp",
    "content": "/*\n * Copyright 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"debuggerd/handler.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <linux/futex.h>\n#include <pthread.h>\n#include <sched.h>\n#include <signal.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/capability.h>\n#include <sys/mman.h>\n#include <sys/prctl.h>\n#include <sys/socket.h>\n#include <sys/syscall.h>\n#include <sys/uio.h>\n#include <sys/un.h>\n#include <sys/wait.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <android-base/macros.h>\n#include <android-base/parsebool.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n#include <async_safe/log.h>\n#include <bionic/reserved_signals.h>\n\n#include <libdebuggerd/utility.h>\n\n#include \"dump_type.h\"\n#include \"protocol.h\"\n\n#include \"handler/fallback.h\"\n\nusing ::android::base::ParseBool;\nusing ::android::base::ParseBoolResult;\nusing ::android::base::Pipe;\n\n// We muck with our fds in a 'thread' that doesn't share the same fd table.\n// Close fds in that thread with a raw close syscall instead of going through libc.\nstruct FdsanBypassCloser {\n  static void Close(int fd) {\n    syscall(__NR_close, fd);\n  }\n};\n\nusing unique_fd = android::base::unique_fd_impl<FdsanBypassCloser>;\n\n// see man(2) prctl, specifically the section about PR_GET_NAME\n#define MAX_TASK_NAME_LEN (16)\n\n#if defined(__LP64__)\n#define CRASH_DUMP_NAME \"crash_dump64\"\n#else\n#define CRASH_DUMP_NAME \"crash_dump32\"\n#endif\n\n#define CRASH_DUMP_PATH \"/apex/com.android.runtime/bin/\" CRASH_DUMP_NAME\n\n// Wrappers that directly invoke the respective syscalls, in case the cached values are invalid.\n#pragma GCC poison getpid gettid\nstatic pid_t __getpid() {\n  return syscall(__NR_getpid);\n}\n\nstatic pid_t __gettid() {\n  return syscall(__NR_gettid);\n}\n\nstatic bool property_parse_bool(const char* name) {\n  const prop_info* pi = __system_property_find(name);\n  if (!pi) return false;\n  bool cookie = false;\n  __system_property_read_callback(\n      pi,\n      [](void* cookie, const char*, const char* value, uint32_t) {\n        *reinterpret_cast<bool*>(cookie) = ParseBool(value) == ParseBoolResult::kTrue;\n      },\n      &cookie);\n  return cookie;\n}\n\nstatic bool is_permissive_mte() {\n  // Environment variable for testing or local use from shell.\n  char* permissive_env = getenv(\"MTE_PERMISSIVE\");\n  char process_sysprop_name[512];\n  async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name),\n                           \"persist.device_config.memory_safety_native.permissive.process.%s\",\n                           getprogname());\n  // DO NOT REPLACE this with GetBoolProperty. That uses std::string which allocates, so it is\n  // not async-safe, and this function gets used in a signal handler.\n  return property_parse_bool(\"persist.sys.mte.permissive\") ||\n         property_parse_bool(\"persist.device_config.memory_safety_native.permissive.default\") ||\n         property_parse_bool(process_sysprop_name) ||\n         (permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue);\n}\n\nstatic bool parse_uint_with_error_reporting(const char* s, const char* name, int* v) {\n  if (android::base::ParseInt(s, v) && *v >= 0) {\n    return true;\n  }\n  async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"invalid %s: %s\", name, s);\n  return false;\n}\n\n// We cannot use base::GetIntProperty, because that internally uses\n// std::string, which allocates.\nstatic bool property_parse_int(const char* name, int* out) {\n  const prop_info* pi = __system_property_find(name);\n  if (!pi) return false;\n  struct cookie_t {\n    int* out;\n    bool empty;\n  } cookie{out, true};\n  __system_property_read_callback(\n      pi,\n      [](void* raw_cookie, const char* name, const char* value, uint32_t) {\n        // Property is set to empty value, ignoring.\n        if (!*value) return;\n        cookie_t* cookie = reinterpret_cast<cookie_t*>(raw_cookie);\n        if (parse_uint_with_error_reporting(value, name, cookie->out)) cookie->empty = false;\n      },\n      &cookie);\n  return !cookie.empty;\n}\n\nstatic int permissive_mte_renable_timer() {\n  if (char* env = getenv(\"MTE_PERMISSIVE_REENABLE_TIME_CPUMS\")) {\n    int v;\n    if (parse_uint_with_error_reporting(env, \"MTE_PERMISSIVE_REENABLE_TIME_CPUMS\", &v)) return v;\n  }\n\n  char process_sysprop_name[512];\n  async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name),\n                           \"persist.sys.mte.permissive_reenable_timer.process.%s\", getprogname());\n  int v;\n  if (property_parse_int(process_sysprop_name, &v)) return v;\n  if (property_parse_int(\"persist.sys.mte.permissive_reenable_timer.default\", &v)) return v;\n  char process_deviceconf_sysprop_name[512];\n  async_safe_format_buffer(\n      process_deviceconf_sysprop_name, sizeof(process_deviceconf_sysprop_name),\n      \"persist.device_config.memory_safety_native.permissive_reenable_timer.process.%s\",\n      getprogname());\n  if (property_parse_int(process_deviceconf_sysprop_name, &v)) return v;\n  if (property_parse_int(\n          \"persist.device_config.memory_safety_native.permissive_reenable_timer.default\", &v))\n    return v;\n  return 0;\n}\n\nstatic inline void futex_wait(volatile void* ftx, int value) {\n  syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0);\n}\n\nclass ErrnoRestorer {\n public:\n  ErrnoRestorer() : saved_errno_(errno) {\n  }\n\n  ~ErrnoRestorer() {\n    errno = saved_errno_;\n  }\n\n private:\n  int saved_errno_;\n};\n\nextern \"C\" void* android_fdsan_get_fd_table();\nextern \"C\" void debuggerd_fallback_handler(siginfo_t*, ucontext_t*, void*);\n\nstatic debuggerd_callbacks_t g_callbacks;\n\n// Mutex to ensure only one crashing thread dumps itself.\nstatic pthread_mutex_t crash_mutex = PTHREAD_MUTEX_INITIALIZER;\n\n// Don't use async_safe_fatal because it exits via abort, which might put us back into\n// a signal handler.\nstatic void __noreturn __printflike(1, 2) fatal(const char* fmt, ...) {\n  va_list args;\n  va_start(args, fmt);\n  async_safe_format_log_va_list(ANDROID_LOG_FATAL, \"libc\", fmt, args);\n  _exit(1);\n}\n\nstatic void __noreturn __printflike(1, 2) fatal_errno(const char* fmt, ...) {\n  int err = errno;\n  va_list args;\n  va_start(args, fmt);\n\n  char buf[256];\n  async_safe_format_buffer_va_list(buf, sizeof(buf), fmt, args);\n  fatal(\"%s: %s\", buf, strerror(err));\n}\n\nstatic bool get_main_thread_name(char* buf, size_t len) {\n  unique_fd fd(open(\"/proc/self/comm\", O_RDONLY | O_CLOEXEC));\n  if (fd == -1) {\n    return false;\n  }\n\n  ssize_t rc = read(fd, buf, len);\n  if (rc == -1) {\n    return false;\n  } else if (rc == 0) {\n    // Should never happen?\n    return false;\n  }\n\n  // There's a trailing newline, replace it with a NUL.\n  buf[rc - 1] = '\\0';\n  return true;\n}\n\n/*\n * Writes a summary of the signal to the log file.  We do this so that, if\n * for some reason we're not able to contact debuggerd, there is still some\n * indication of the failure in the log.\n *\n * We could be here as a result of native heap corruption, or while a\n * mutex is being held, so we don't want to use any libc functions that\n * could allocate memory or hold a lock.\n */\nstatic void log_signal_summary(const siginfo_t* si) {\n  char main_thread_name[MAX_TASK_NAME_LEN + 1];\n  if (!get_main_thread_name(main_thread_name, sizeof(main_thread_name))) {\n    strncpy(main_thread_name, \"<unknown>\", sizeof(main_thread_name));\n  }\n\n  if (si->si_signo == BIONIC_SIGNAL_DEBUGGER) {\n    async_safe_format_log(ANDROID_LOG_INFO, \"libc\", \"Requested dump for pid %d (%s)\", __getpid(),\n                          main_thread_name);\n    return;\n  }\n\n  // Many signals don't have a sender or extra detail, but some do...\n  pid_t self_pid = __getpid();\n  char sender_desc[32] = {};  // \" from pid 1234, uid 666\"\n  if (signal_has_sender(si, self_pid)) {\n    get_signal_sender(sender_desc, sizeof(sender_desc), si);\n  }\n  char extra_desc[32] = {};  // \", fault addr 0x1234\" or \", syscall 1234\"\n  if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {\n    async_safe_format_buffer(extra_desc, sizeof(extra_desc), \", syscall %d\", si->si_syscall);\n  } else if (signal_has_si_addr(si)) {\n    async_safe_format_buffer(extra_desc, sizeof(extra_desc), \", fault addr %p\", si->si_addr);\n  }\n\n  char thread_name[MAX_TASK_NAME_LEN + 1];  // one more for termination\n  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {\n    strcpy(thread_name, \"<name unknown>\");\n  } else {\n    // short names are null terminated by prctl, but the man page\n    // implies that 16 byte names are not.\n    thread_name[MAX_TASK_NAME_LEN] = 0;\n  }\n\n  async_safe_format_log(ANDROID_LOG_FATAL, \"libc\",\n                        \"Fatal signal %d (%s), code %d (%s%s)%s in tid %d (%s), pid %d (%s)\",\n                        si->si_signo, get_signame(si), si->si_code, get_sigcode(si), sender_desc,\n                        extra_desc, __gettid(), thread_name, self_pid, main_thread_name);\n}\n\n/*\n * Returns true if the handler for signal \"signum\" has SA_SIGINFO set.\n */\nstatic bool have_siginfo(int signum) {\n  struct sigaction old_action;\n  if (sigaction(signum, nullptr, &old_action) < 0) {\n    async_safe_format_log(ANDROID_LOG_WARN, \"libc\", \"Failed testing for SA_SIGINFO: %s\",\n                          strerror(errno));\n    return false;\n  }\n  return (old_action.sa_flags & SA_SIGINFO) != 0;\n}\n\nstatic void raise_caps() {\n  // Raise CapInh to match CapPrm, so that we can set the ambient bits.\n  __user_cap_header_struct capheader;\n  memset(&capheader, 0, sizeof(capheader));\n  capheader.version = _LINUX_CAPABILITY_VERSION_3;\n  capheader.pid = 0;\n\n  __user_cap_data_struct capdata[2];\n  if (capget(&capheader, &capdata[0]) == -1) {\n    fatal_errno(\"capget failed\");\n  }\n\n  if (capdata[0].permitted != capdata[0].inheritable ||\n      capdata[1].permitted != capdata[1].inheritable) {\n    capdata[0].inheritable = capdata[0].permitted;\n    capdata[1].inheritable = capdata[1].permitted;\n\n    if (capset(&capheader, &capdata[0]) == -1) {\n      async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"capset failed: %s\", strerror(errno));\n    }\n  }\n\n  // Set the ambient capability bits so that crash_dump gets all of our caps and can ptrace us.\n  uint64_t capmask = capdata[0].inheritable;\n  capmask |= static_cast<uint64_t>(capdata[1].inheritable) << 32;\n  for (unsigned long i = 0; i < 64; ++i) {\n    if (capmask & (1ULL << i)) {\n      if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, i, 0, 0) != 0) {\n        async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                              \"failed to raise ambient capability %lu: %s\", i, strerror(errno));\n      }\n    }\n  }\n}\n\n// Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation.\n// Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure.\nstatic void create_vm_process() {\n  pid_t first = clone(nullptr, nullptr, CLONE_FILES, nullptr);\n  if (first == -1) {\n    fatal_errno(\"failed to clone vm process\");\n  } else if (first == 0) {\n    drop_capabilities();\n\n    if (clone(nullptr, nullptr, CLONE_FILES, nullptr) == -1) {\n      _exit(errno);\n    }\n\n    // crash_dump is ptracing both sides of the fork; it'll let the parent exit,\n    // but keep the orphan stopped to peek at its memory.\n\n    // There appears to be a bug in the kernel where our death causes SIGHUP to\n    // be sent to our process group if we exit while it has stopped jobs (e.g.\n    // because of wait_for_debugger). Use setsid to create a new process group to\n    // avoid hitting this.\n    setsid();\n\n    _exit(0);\n  }\n\n  int status;\n  if (TEMP_FAILURE_RETRY(waitpid(first, &status, __WCLONE)) != first) {\n    fatal_errno(\"failed to waitpid in double fork\");\n  } else if (!WIFEXITED(status)) {\n    fatal(\"intermediate process didn't exit cleanly in double fork (status = %d)\", status);\n  } else if (WEXITSTATUS(status)) {\n    fatal(\"second clone failed: %s\", strerror(WEXITSTATUS(status)));\n  }\n}\n\nstruct debugger_thread_info {\n  pid_t crashing_tid;\n  pid_t pseudothread_tid;\n  siginfo_t* siginfo;\n  void* ucontext;\n  debugger_process_info process_info;\n};\n\n// Logging and contacting debuggerd requires free file descriptors, which we might not have.\n// Work around this by spawning a \"thread\" that shares its parent's address space, but not its file\n// descriptor table, so that we can close random file descriptors without affecting the original\n// process. Note that this doesn't go through pthread_create, so TLS is shared with the spawning\n// process.\nstatic void* pseudothread_stack;\n\nstatic DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) {\n  if (thread_info->siginfo->si_signo == BIONIC_SIGNAL_DEBUGGER &&\n      thread_info->siginfo->si_value.sival_int) {\n    return kDebuggerdNativeBacktrace;\n  }\n\n  return kDebuggerdTombstoneProto;\n}\n\nstatic const char* get_unwind_type(const debugger_thread_info* thread_info) {\n  if (thread_info->siginfo->si_signo == BIONIC_SIGNAL_DEBUGGER) {\n    return \"Unwind request\";\n  }\n  return \"Crash due to signal\";\n}\n\nstatic int debuggerd_dispatch_pseudothread(void* arg) {\n  debugger_thread_info* thread_info = static_cast<debugger_thread_info*>(arg);\n\n  for (int i = 0; i < 1024; ++i) {\n    // Don't use close to avoid bionic's file descriptor ownership checks.\n    syscall(__NR_close, i);\n  }\n\n  int devnull = TEMP_FAILURE_RETRY(open(\"/dev/null\", O_RDWR));\n  if (devnull == -1) {\n    fatal_errno(\"failed to open /dev/null\");\n  } else if (devnull != 0) {\n    fatal_errno(\"expected /dev/null fd to be 0, actually %d\", devnull);\n  }\n\n  // devnull will be 0.\n  TEMP_FAILURE_RETRY(dup2(devnull, 1));\n  TEMP_FAILURE_RETRY(dup2(devnull, 2));\n\n  unique_fd input_read, input_write;\n  unique_fd output_read, output_write;\n  if (!Pipe(&input_read, &input_write) != 0 || !Pipe(&output_read, &output_write)) {\n    fatal_errno(\"failed to create pipe\");\n  }\n\n  uint32_t version;\n  ssize_t expected;\n\n  // ucontext_t is absurdly large on AArch64, so piece it together manually with writev.\n  struct iovec iovs[4] = {\n      {.iov_base = &version, .iov_len = sizeof(version)},\n      {.iov_base = thread_info->siginfo, .iov_len = sizeof(siginfo_t)},\n      {.iov_base = thread_info->ucontext, .iov_len = sizeof(ucontext_t)},\n  };\n\n  constexpr size_t kHeaderSize = sizeof(version) + sizeof(siginfo_t) + sizeof(ucontext_t);\n\n  if (thread_info->process_info.fdsan_table) {\n    // Dynamic executables always use version 4. There is no need to increment the version number if\n    // the format changes, because the sender (linker) and receiver (crash_dump) are version locked.\n    version = 4;\n    expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic);\n\n    static_assert(sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic) ==\n                      kHeaderSize + sizeof(thread_info->process_info),\n                  \"Wire protocol structs do not match the data sent.\");\n#define ASSERT_SAME_OFFSET(MEMBER1, MEMBER2) \\\n    static_assert(sizeof(CrashInfoHeader) + offsetof(CrashInfoDataDynamic, MEMBER1) == \\\n                      kHeaderSize + offsetof(debugger_process_info, MEMBER2), \\\n                  \"Wire protocol offset does not match data sent: \" #MEMBER1);\n    ASSERT_SAME_OFFSET(fdsan_table_address, fdsan_table);\n    ASSERT_SAME_OFFSET(gwp_asan_state, gwp_asan_state);\n    ASSERT_SAME_OFFSET(gwp_asan_metadata, gwp_asan_metadata);\n    ASSERT_SAME_OFFSET(scudo_stack_depot, scudo_stack_depot);\n    ASSERT_SAME_OFFSET(scudo_region_info, scudo_region_info);\n    ASSERT_SAME_OFFSET(scudo_ring_buffer, scudo_ring_buffer);\n    ASSERT_SAME_OFFSET(scudo_ring_buffer_size, scudo_ring_buffer_size);\n    ASSERT_SAME_OFFSET(scudo_stack_depot_size, scudo_stack_depot_size);\n    ASSERT_SAME_OFFSET(recoverable_crash, recoverable_crash);\n    ASSERT_SAME_OFFSET(crash_detail_page, crash_detail_page);\n#undef ASSERT_SAME_OFFSET\n\n    iovs[3] = {.iov_base = &thread_info->process_info,\n               .iov_len = sizeof(thread_info->process_info)};\n  } else {\n    // Static executables always use version 1.\n    version = 1;\n    expected = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic);\n\n    static_assert(\n        sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic) == kHeaderSize + sizeof(uintptr_t),\n        \"Wire protocol structs do not match the data sent.\");\n\n    iovs[3] = {.iov_base = &thread_info->process_info.abort_msg, .iov_len = sizeof(uintptr_t)};\n  }\n  errno = 0;\n  if (fcntl(output_write.get(), F_SETPIPE_SZ, expected) < static_cast<int>(expected)) {\n    fatal_errno(\"failed to set pipe buffer size\");\n  }\n\n  ssize_t rc = TEMP_FAILURE_RETRY(writev(output_write.get(), iovs, arraysize(iovs)));\n  if (rc == -1) {\n    fatal_errno(\"failed to write crash info\");\n  } else if (rc != expected) {\n    fatal(\"failed to write crash info, wrote %zd bytes, expected %zd\", rc, expected);\n  }\n\n  // Don't use fork(2) to avoid calling pthread_atfork handlers.\n  pid_t crash_dump_pid = _Fork();\n  if (crash_dump_pid == -1) {\n    async_safe_format_log(ANDROID_LOG_FATAL, \"libc\",\n                          \"failed to fork in debuggerd signal handler: %s\", strerror(errno));\n  } else if (crash_dump_pid == 0) {\n    TEMP_FAILURE_RETRY(dup2(input_write.get(), STDOUT_FILENO));\n    TEMP_FAILURE_RETRY(dup2(output_read.get(), STDIN_FILENO));\n    input_read.reset();\n    input_write.reset();\n    output_read.reset();\n    output_write.reset();\n\n    raise_caps();\n\n    char main_tid[10];\n    char pseudothread_tid[10];\n    char debuggerd_dump_type[10];\n    async_safe_format_buffer(main_tid, sizeof(main_tid), \"%d\", thread_info->crashing_tid);\n    async_safe_format_buffer(pseudothread_tid, sizeof(pseudothread_tid), \"%d\",\n                             thread_info->pseudothread_tid);\n    async_safe_format_buffer(debuggerd_dump_type, sizeof(debuggerd_dump_type), \"%d\",\n                             get_dump_type(thread_info));\n\n    execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type,\n           nullptr, nullptr);\n    async_safe_format_log(ANDROID_LOG_FATAL, \"libc\", \"%s: failed to exec crash_dump helper: %s\",\n                          get_unwind_type(thread_info), strerror(errno));\n    return 1;\n  }\n\n  input_write.reset();\n  output_read.reset();\n\n  // crash_dump will ptrace and pause all of our threads, and then write to the pipe to tell\n  // us to fork off a process to read memory from.\n  char buf[4];\n  rc = TEMP_FAILURE_RETRY(read(input_read.get(), &buf, sizeof(buf)));\n\n  bool success = false;\n  if (rc == 1 && buf[0] == '\\1') {\n    // crash_dump successfully started, and is ptracing us.\n    // Fork off a copy of our address space for it to use.\n    create_vm_process();\n    success = true;\n  } else {\n    // Something went wrong, log it.\n    if (rc == -1) {\n      async_safe_format_log(ANDROID_LOG_FATAL, \"libc\", \"%s: read of IPC pipe failed: %s\",\n                            get_unwind_type(thread_info), strerror(errno));\n    } else if (rc == 0) {\n      async_safe_format_log(ANDROID_LOG_FATAL, \"libc\",\n                            \"%s: crash_dump helper failed to exec, or was killed\",\n                            get_unwind_type(thread_info));\n    } else if (rc != 1) {\n      async_safe_format_log(ANDROID_LOG_FATAL, \"libc\",\n                            \"%s: read of IPC pipe returned unexpected value: %zd\",\n                            get_unwind_type(thread_info), rc);\n    } else if (buf[0] != '\\1') {\n      async_safe_format_log(ANDROID_LOG_FATAL, \"libc\", \"%s: crash_dump helper reported failure\",\n                            get_unwind_type(thread_info));\n    }\n  }\n\n  // Don't leave a zombie child.\n  int status;\n  if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) {\n    async_safe_format_log(ANDROID_LOG_FATAL, \"libc\", \"%s: failed to wait for crash_dump helper: %s\",\n                          get_unwind_type(thread_info), strerror(errno));\n  } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) {\n    async_safe_format_log(ANDROID_LOG_FATAL, \"libc\", \"%s: crash_dump helper crashed or stopped\",\n                          get_unwind_type(thread_info));\n  }\n\n  if (success) {\n    if (thread_info->siginfo->si_signo != BIONIC_SIGNAL_DEBUGGER) {\n      // For crashes, we don't need to minimize pause latency.\n      // Wait for the dump to complete before having the process exit, to avoid being murdered by\n      // ActivityManager or init.\n      TEMP_FAILURE_RETRY(read(input_read, &buf, sizeof(buf)));\n    }\n  }\n\n  return success ? 0 : 1;\n}\n\nstatic void resend_signal(siginfo_t* info) {\n  // Signals can either be fatal or nonfatal.\n  // For fatal signals, crash_dump will send us the signal we crashed with\n  // before resuming us, so that processes using waitpid on us will see that we\n  // exited with the correct exit status (e.g. so that sh will report\n  // \"Segmentation fault\" instead of \"Killed\"). For this to work, we need\n  // to deregister our signal handler for that signal before continuing.\n  if (info->si_signo != BIONIC_SIGNAL_DEBUGGER) {\n    signal(info->si_signo, SIG_DFL);\n    int rc = syscall(SYS_rt_tgsigqueueinfo, __getpid(), __gettid(), info->si_signo, info);\n    if (rc != 0) {\n      fatal_errno(\"failed to resend signal during crash\");\n    }\n  }\n}\n\n// Handler that does crash dumping by forking and doing the processing in the child.\n// Do this by ptracing the relevant thread, and then execing debuggerd to do the actual dump.\nstatic void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* context) {\n  // Make sure we don't change the value of errno, in case a signal comes in between the process\n  // making a syscall and checking errno.\n  ErrnoRestorer restorer;\n\n  auto *ucontext = static_cast<ucontext_t*>(context);\n\n  // It's possible somebody cleared the SA_SIGINFO flag, which would mean\n  // our \"info\" arg holds an undefined value.\n  if (!have_siginfo(signal_number)) {\n    info = nullptr;\n  }\n\n  struct siginfo dummy_info = {};\n  if (!info) {\n    memset(&dummy_info, 0, sizeof(dummy_info));\n    dummy_info.si_signo = signal_number;\n    dummy_info.si_code = SI_USER;\n    dummy_info.si_pid = __getpid();\n    dummy_info.si_uid = getuid();\n    info = &dummy_info;\n  } else if (info->si_code >= 0 || info->si_code == SI_TKILL) {\n    // rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels\n    // that contain commit 66dd34a (3.9+). The manpage claims to only allow\n    // negative si_code values that are not SI_TKILL, but 66dd34a changed the\n    // check to allow all si_code values in calls coming from inside the house.\n  }\n\n  debugger_process_info process_info = {};\n  if (g_callbacks.get_process_info) {\n    process_info = g_callbacks.get_process_info();\n  }\n  uintptr_t si_val = reinterpret_cast<uintptr_t>(info->si_ptr);\n  if (signal_number == BIONIC_SIGNAL_DEBUGGER) {\n    // Applications can set abort messages via android_set_abort_message without\n    // actually aborting; ignore those messages in non-fatal dumps.\n    process_info.abort_msg = nullptr;\n    if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {\n      // Allow for the abort message to be explicitly specified via the sigqueue value.\n      // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.\n      if (si_val != kDebuggerdFallbackSivalUintptrRequestDump) {\n        process_info.abort_msg = reinterpret_cast<void*>(si_val & ~1);\n        info->si_ptr = reinterpret_cast<void*>(si_val & 1);\n      }\n    }\n  }\n\n  gwp_asan_callbacks_t gwp_asan_callbacks = {};\n  bool recoverable_gwp_asan_crash = false;\n  if (g_callbacks.get_gwp_asan_callbacks != nullptr) {\n    // GWP-ASan catches use-after-free and heap-buffer-overflow by using PROT_NONE\n    // guard pages, which lead to SEGV. Normally, debuggerd prints a bug report\n    // and the process terminates, but in some cases, we actually want to print\n    // the bug report and let the signal handler return, and restart the process.\n    // In order to do that, we need to disable GWP-ASan's guard pages. The\n    // following callbacks handle this case.\n    gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks();\n    if (signal_number == SIGSEGV && signal_has_si_addr(info) &&\n        gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery &&\n        gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report &&\n        gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report &&\n        gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) {\n      gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr);\n      recoverable_gwp_asan_crash = true;\n      process_info.recoverable_crash = true;\n    }\n  }\n\n  if (info->si_signo == SIGSEGV &&\n      (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) && is_permissive_mte()) {\n    process_info.recoverable_crash = true;\n    // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread,\n    // and then let the failing instruction be retried. The second time should work (except\n    // if there is another non-MTE fault).\n    int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);\n    if (tagged_addr_ctrl < 0) {\n      fatal_errno(\"failed to PR_GET_TAGGED_ADDR_CTRL\");\n    }\n    int previous = tagged_addr_ctrl & PR_MTE_TCF_MASK;\n    tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE;\n    if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) {\n      fatal_errno(\"failed to PR_SET_TAGGED_ADDR_CTRL\");\n    }\n    if (int reenable_timer = permissive_mte_renable_timer()) {\n      async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                            \"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH \"\n                            \"MTE DISABLED FOR %d MS OF CPU TIME.\",\n                            reenable_timer);\n      timer_t timerid{};\n      struct sigevent sev {};\n      sev.sigev_signo = BIONIC_ENABLE_MTE;\n      sev.sigev_notify = SIGEV_THREAD_ID;\n      sev.sigev_value.sival_int = previous;\n      sev.sigev_notify_thread_id = __gettid();\n      // This MUST be CLOCK_THREAD_CPUTIME_ID. If we used CLOCK_MONOTONIC we could get stuck\n      // in an endless loop of re-running the same instruction, calling this signal handler,\n      // and re-enabling MTE before we had a chance to re-run the instruction.\n      if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timerid) == -1) {\n        fatal_errno(\"timer_create() failed\");\n      }\n      struct itimerspec its {};\n      its.it_value.tv_sec = reenable_timer / 1000;\n      its.it_value.tv_nsec = (reenable_timer % 1000) * 1000000;\n\n      if (timer_settime(timerid, 0, &its, nullptr) == -1) {\n        fatal_errno(\"timer_settime() failed\");\n      }\n    } else {\n      async_safe_format_log(\n          ANDROID_LOG_ERROR, \"libc\",\n          \"MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH MTE DISABLED.\");\n    }\n    pthread_mutex_unlock(&crash_mutex);\n  }\n\n  // If sival_int is ~0, it means that the fallback handler has been called\n  // once before and this function is being called again to dump the stack\n  // of a specific thread. It is possible that the prctl call might return 1,\n  // then return 0 in subsequent calls, so check the sival_int to determine if\n  // the fallback handler should be called first.\n  bool no_new_privs = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0) == 1;\n  if (si_val == kDebuggerdFallbackSivalUintptrRequestDump || no_new_privs) {\n    // This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,\n    // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing\n    // ANR trace.\n    debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);\n    if (no_new_privs && recoverable_gwp_asan_crash) {\n      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);\n      return;\n    }\n    resend_signal(info);\n    return;\n  }\n\n  // Only allow one thread to handle a signal at a time.\n  int ret = pthread_mutex_lock(&crash_mutex);\n  if (ret != 0) {\n    async_safe_format_log(ANDROID_LOG_INFO, \"libc\", \"pthread_mutex_lock failed: %s\", strerror(ret));\n    return;\n  }\n\n  log_signal_summary(info);\n\n  // If we got here due to the signal BIONIC_SIGNAL_DEBUGGER, it's possible\n  // this is not the main thread, which can cause the intercept logic to fail\n  // since the intercept is only looking for the main thread. In this case,\n  // setting crashing_tid to pid instead of the current thread's tid avoids\n  // the problem.\n  debugger_thread_info thread_info = {\n      .crashing_tid = (signal_number == BIONIC_SIGNAL_DEBUGGER) ? __getpid() : __gettid(),\n      .pseudothread_tid = -1,\n      .siginfo = info,\n      .ucontext = context,\n      .process_info = process_info,\n  };\n\n  // Set PR_SET_DUMPABLE to 1, so that crash_dump can ptrace us.\n  int orig_dumpable = prctl(PR_GET_DUMPABLE);\n  if (prctl(PR_SET_DUMPABLE, 1) != 0) {\n    fatal_errno(\"failed to set dumpable\");\n  }\n\n  // On kernels with yama_ptrace enabled, also allow any process to attach.\n  bool restore_orig_ptracer = true;\n  if (prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY) != 0) {\n    if (errno == EINVAL) {\n      // This kernel does not support PR_SET_PTRACER_ANY, or Yama is not enabled.\n      restore_orig_ptracer = false;\n    } else {\n      fatal_errno(\"failed to set traceable\");\n    }\n  }\n\n  // Essentially pthread_create without CLONE_FILES, so we still work during file descriptor\n  // exhaustion.\n  pid_t child_pid =\n    clone(debuggerd_dispatch_pseudothread, pseudothread_stack,\n          CLONE_THREAD | CLONE_SIGHAND | CLONE_VM | CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID,\n          &thread_info, nullptr, nullptr, &thread_info.pseudothread_tid);\n  if (child_pid == -1) {\n    fatal_errno(\"failed to spawn debuggerd dispatch thread\");\n  }\n\n  // Wait for the child to start...\n  futex_wait(&thread_info.pseudothread_tid, -1);\n\n  // and then wait for it to terminate.\n  futex_wait(&thread_info.pseudothread_tid, child_pid);\n\n  // Restore PR_SET_DUMPABLE to its original value.\n  if (prctl(PR_SET_DUMPABLE, orig_dumpable) != 0) {\n    fatal_errno(\"failed to restore dumpable\");\n  }\n\n  // Restore PR_SET_PTRACER to its original value.\n  if (restore_orig_ptracer && prctl(PR_SET_PTRACER, 0) != 0) {\n    fatal_errno(\"failed to restore traceable\");\n  }\n\n  if (info->si_signo == BIONIC_SIGNAL_DEBUGGER) {\n    // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from\n    // starting to dump right before our death.\n    pthread_mutex_unlock(&crash_mutex);\n  } else if (process_info.recoverable_crash) {\n    if (recoverable_gwp_asan_crash) {\n      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);\n    }\n    pthread_mutex_unlock(&crash_mutex);\n  }\n#ifdef __aarch64__\n  else if (info->si_signo == SIGSEGV && info->si_code == SEGV_MTEAERR && getppid() == 1) {\n    // Back channel to init (see system/core/init/service.cpp) to signal that\n    // this process crashed due to an ASYNC MTE fault and should be considered\n    // for upgrade to SYNC mode. We are re-using the ART profiler signal, which\n    // is always handled (ignored in native processes, handled for generating a\n    // dump in ART processes), so a process will never crash from this signal\n    // except from here.\n    // The kernel is not particularly receptive to adding this information:\n    // https://lore.kernel.org/all/20220909180617.374238-1-fmayer@google.com/, so we work around\n    // like this.\n    info->si_signo = BIONIC_SIGNAL_ART_PROFILER;\n    resend_signal(info);\n  }\n#endif\n  else {\n    // Resend the signal, so that either the debugger or the parent's waitpid sees it.\n    resend_signal(info);\n  }\n}\n\nvoid debuggerd_init(debuggerd_callbacks_t* callbacks) {\n  if (callbacks) {\n    g_callbacks = *callbacks;\n  }\n\n  size_t thread_stack_pages = 8;\n  void* thread_stack_allocation = mmap(nullptr, getpagesize() * (thread_stack_pages + 2), PROT_NONE,\n                                       MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);\n  if (thread_stack_allocation == MAP_FAILED) {\n    fatal_errno(\"failed to allocate debuggerd thread stack\");\n  }\n\n  char* stack = static_cast<char*>(thread_stack_allocation) + getpagesize();\n  if (mprotect(stack, getpagesize() * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) {\n    fatal_errno(\"failed to mprotect debuggerd thread stack\");\n  }\n\n  // Stack grows negatively, set it to the last byte in the page...\n  stack = (stack + thread_stack_pages * getpagesize() - 1);\n  // and align it.\n  stack -= 15;\n  pseudothread_stack = stack;\n\n  struct sigaction action;\n  memset(&action, 0, sizeof(action));\n  sigfillset(&action.sa_mask);\n  action.sa_sigaction = debuggerd_signal_handler;\n  action.sa_flags = SA_RESTART | SA_SIGINFO;\n\n  // Use the alternate signal stack if available so we can catch stack overflows.\n  action.sa_flags |= SA_ONSTACK;\n\n  // Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE\n  // faults.\n  action.sa_flags |= SA_EXPOSE_TAGBITS;\n\n  debuggerd_register_handlers(&action);\n}\n\nbool debuggerd_handle_gwp_asan_signal(int signal_number, siginfo_t* info, void* context) {\n  if (g_callbacks.get_gwp_asan_callbacks == nullptr) return false;\n  gwp_asan_callbacks_t gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks();\n  if (gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery == nullptr ||\n      gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report == nullptr ||\n      gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report == nullptr ||\n      !gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) {\n    return false;\n  }\n\n  // Only dump a crash report for the first GWP-ASan crash. ActivityManager\n  // doesn't like it when an app crashes multiple times, and is even more strict\n  // about an app crashing multiple times in a short time period. While the app\n  // won't crash fully when we do GWP-ASan recovery, ActivityManager still gets\n  // the information about the crash through the DropBoxManager service. If an\n  // app has multiple back-to-back GWP-ASan crashes, this would lead to the app\n  // being killed, which defeats the purpose of having the recoverable mode. To\n  // mitigate against this, only generate a debuggerd crash report for the first\n  // GWP-ASan crash encountered. We still need to do the patching up of the\n  // allocator though, so do that.\n  static pthread_mutex_t first_crash_mutex = PTHREAD_MUTEX_INITIALIZER;\n  pthread_mutex_lock(&first_crash_mutex);\n  static bool first_crash = true;\n\n  if (first_crash) {\n    // `debuggerd_signal_handler` will call\n    // `debuggerd_gwp_asan_(pre|post)_crash_report`, so no need to manually call\n    // them here.\n    debuggerd_signal_handler(signal_number, info, context);\n    first_crash = false;\n  } else {\n    gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr);\n    gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr);\n  }\n\n  pthread_mutex_unlock(&first_crash_mutex);\n  return true;\n}\n\n// When debuggerd's signal handler is the first handler called, it's great at\n// handling the recoverable GWP-ASan and permissive MTE modes. For apps,\n// sigchain (from libart) is always the first signal handler, and so the\n// following function is what sigchain must call before processing the signal.\n// This allows for processing of a potentially recoverable GWP-ASan or MTE\n// crash. If the signal requires recovery, then dump a report (via the regular\n// debuggerd hanndler), and patch up the allocator (in the case of GWP-ASan) or\n// disable MTE on the thread, and allow the process to continue (indicated by\n// returning 'true'). If the crash has nothing to do with GWP-ASan/MTE, or\n// recovery isn't possible, return 'false'.\nbool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) {\n  if (signal_number != SIGSEGV) return false;\n  if (info->si_code == SEGV_MTEAERR || info->si_code == SEGV_MTESERR) {\n    if (!is_permissive_mte()) return false;\n    // Because permissive MTE disables MTE for the entire thread, we're less\n    // worried about getting a whole bunch of crashes in a row. ActivityManager\n    // doesn't like multiple native crashes for an app in a short period of time\n    // (see the comment about recoverable GWP-ASan in\n    // `debuggerd_handle_gwp_asan_signal`), but that shouldn't happen if MTE is\n    // disabled for the entire thread. This might need to be changed if there's\n    // some low-hanging bug that happens across multiple threads in quick\n    // succession.\n    debuggerd_signal_handler(signal_number, info, context);\n    return true;\n  }\n\n  if (!signal_has_si_addr(info)) return false;\n  return debuggerd_handle_gwp_asan_signal(signal_number, info, context);\n}\n"
  },
  {
    "path": "debuggerd/handler/fallback.h",
    "content": "/*\n * Copyright 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\nstatic void* const kDebuggerdFallbackSivalPtrRequestDump = reinterpret_cast<void*>(~0UL);\nstatic const uintptr_t kDebuggerdFallbackSivalUintptrRequestDump = ~0UL;\n"
  },
  {
    "path": "debuggerd/include/debuggerd/client.h",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <sys/cdefs.h>\n#include <unistd.h>\n\n#include <android-base/unique_fd.h>\n\n#include \"dump_type.h\"\n\n// Trigger a dump of specified process to output_fd.\n// output_fd is consumed, timeout of 0 will wait forever.\nbool debuggerd_trigger_dump(pid_t tid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms,\n                            android::base::unique_fd output_fd);\n\nint dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd);\nint dump_backtrace_to_file_timeout(pid_t tid, enum DebuggerdDumpType dump_type, int timeout_secs,\n                                   int output_fd);\n"
  },
  {
    "path": "debuggerd/include/debuggerd/handler.h",
    "content": "/*\n * Copyright 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <bionic/reserved_signals.h>\n#include <signal.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/cdefs.h>\n#include <sys/system_properties.h>\n#include <sys/types.h>\n\n__BEGIN_DECLS\n\n// Forward declare these classes so not everyone has to include GWP-ASan\n// headers.\nnamespace gwp_asan {\nstruct AllocatorState;\nstruct AllocationMetadata;\n};  // namespace gwp_asan\n\nstruct crash_detail_page_t;\n\n// When updating this data structure, CrashInfoDataDynamic and the code in\n// ReadCrashInfo() must also be updated.\nstruct __attribute__((packed)) debugger_process_info {\n  void* abort_msg;\n  void* fdsan_table;\n  const gwp_asan::AllocatorState* gwp_asan_state;\n  const gwp_asan::AllocationMetadata* gwp_asan_metadata;\n  const char* scudo_stack_depot;\n  const char* scudo_region_info;\n  const char* scudo_ring_buffer;\n  size_t scudo_ring_buffer_size;\n  size_t scudo_stack_depot_size;\n  bool recoverable_crash;\n  struct crash_detail_page_t* crash_detail_page;\n};\n\n// GWP-ASan calbacks to support the recoverable mode. Separate from the\n// debuggerd_callbacks_t because these values aren't available at debuggerd_init\n// time, and have to be synthesized on request.\ntypedef struct {\n  bool (*debuggerd_needs_gwp_asan_recovery)(void* fault_addr);\n  void (*debuggerd_gwp_asan_pre_crash_report)(void* fault_addr);\n  void (*debuggerd_gwp_asan_post_crash_report)(void* fault_addr);\n} gwp_asan_callbacks_t;\n\n// These callbacks are called in a signal handler, and thus must be async signal safe.\n// If null, the callbacks will not be called.\ntypedef struct {\n  debugger_process_info (*get_process_info)();\n  gwp_asan_callbacks_t (*get_gwp_asan_callbacks)();\n  void (*post_dump)();\n} debuggerd_callbacks_t;\n\nvoid debuggerd_init(debuggerd_callbacks_t* callbacks);\nbool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context);\n\n// DEBUGGER_ACTION_DUMP_TOMBSTONE and DEBUGGER_ACTION_DUMP_BACKTRACE are both\n// triggered via BIONIC_SIGNAL_DEBUGGER. The debugger_action_t is sent via si_value\n// using sigqueue(2) or equivalent. If no si_value is specified (e.g. if the\n// signal is sent by kill(2)), the default behavior is to print the backtrace\n// to the log.\n#define DEBUGGER_SIGNAL BIONIC_SIGNAL_DEBUGGER\n\nstatic void __attribute__((__unused__)) debuggerd_register_handlers(struct sigaction* action) {\n  bool enabled = true;\n#if ANDROID_DEBUGGABLE\n  char value[PROP_VALUE_MAX] = \"\";\n  enabled = !(__system_property_get(\"debug.debuggerd.disable\", value) > 0 && !strcmp(value, \"1\"));\n#endif\n  if (enabled) {\n    sigaction(SIGABRT, action, nullptr);\n    sigaction(SIGBUS, action, nullptr);\n    sigaction(SIGFPE, action, nullptr);\n    sigaction(SIGILL, action, nullptr);\n    sigaction(SIGSEGV, action, nullptr);\n    sigaction(SIGSTKFLT, action, nullptr);\n    sigaction(SIGSYS, action, nullptr);\n    sigaction(SIGTRAP, action, nullptr);\n  }\n\n  sigaction(BIONIC_SIGNAL_DEBUGGER, action, nullptr);\n}\n\n__END_DECLS\n"
  },
  {
    "path": "debuggerd/libdebuggerd/backtrace.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"DEBUG\"\n\n#include \"libdebuggerd/backtrace.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ptrace.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <map>\n#include <memory>\n#include <string>\n\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <log/log.h>\n#include <unwindstack/AndroidUnwinder.h>\n#include <unwindstack/Unwinder.h>\n\n#include \"libdebuggerd/types.h\"\n#include \"libdebuggerd/utility.h\"\n#include \"util.h\"\n\nstatic void dump_process_header(log_t* log, pid_t pid,\n                                const std::vector<std::string>& command_line) {\n  _LOG(log, logtype::BACKTRACE, \"\\n\\n----- pid %d at %s -----\\n\", pid, get_timestamp().c_str());\n\n  if (!command_line.empty()) {\n    _LOG(log, logtype::BACKTRACE, \"Cmd line: %s\\n\", android::base::Join(command_line, \" \").c_str());\n  }\n  _LOG(log, logtype::BACKTRACE, \"ABI: '%s'\\n\", ABI_STRING);\n}\n\nstatic void dump_process_footer(log_t* log, pid_t pid) {\n  _LOG(log, logtype::BACKTRACE, \"\\n----- end %d -----\\n\", pid);\n}\n\nvoid dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,\n                           const ThreadInfo& thread) {\n  log_t log;\n  log.tfd = output_fd;\n  log.amfd_data = nullptr;\n\n  _LOG(&log, logtype::BACKTRACE, \"\\n\\\"%s\\\" sysTid=%d\\n\", thread.thread_name.c_str(), thread.tid);\n\n  unwindstack::AndroidUnwinderData data;\n  if (!unwinder->Unwind(thread.registers.get(), data)) {\n    _LOG(&log, logtype::THREAD, \"Unwind failed: tid = %d: Error %s\\n\", thread.tid,\n         data.GetErrorString().c_str());\n    return;\n  }\n\n  log_backtrace(&log, unwinder, data, \"  \");\n}\n\nvoid dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,\n                    const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread) {\n  log_t log;\n  log.tfd = output_fd.get();\n  log.amfd_data = nullptr;\n\n  auto target = thread_info.find(target_thread);\n  if (target == thread_info.end()) {\n    ALOGE(\"failed to find target thread in thread info\");\n    return;\n  }\n\n  dump_process_header(&log, target->second.pid, target->second.command_line);\n\n  dump_backtrace_thread(output_fd.get(), unwinder, target->second);\n  for (const auto& [tid, info] : thread_info) {\n    if (tid != target_thread) {\n      dump_backtrace_thread(output_fd.get(), unwinder, info);\n    }\n  }\n\n  dump_process_footer(&log, target->second.pid);\n}\n\nvoid dump_backtrace_header(int output_fd) {\n  log_t log;\n  log.tfd = output_fd;\n  log.amfd_data = nullptr;\n\n  pid_t pid = getpid();\n  dump_process_header(&log, pid, get_command_line(pid));\n}\n\nvoid dump_backtrace_footer(int output_fd) {\n  log_t log;\n  log.tfd = output_fd;\n  log.amfd_data = nullptr;\n\n  dump_process_footer(&log, getpid());\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/gwp_asan.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libdebuggerd/gwp_asan.h\"\n#include \"libdebuggerd/tombstone.h\"\n#include \"libdebuggerd/utility.h\"\n\n#include \"gwp_asan/common.h\"\n#include \"gwp_asan/crash_handler.h\"\n\n#include <unwindstack/AndroidUnwinder.h>\n#include <unwindstack/Memory.h>\n#include <unwindstack/Unwinder.h>\n\n#include \"tombstone.pb.h\"\n\n// Retrieve GWP-ASan state from `state_addr` inside the process at\n// `process_memory`. Place the state into `*state`.\nstatic bool retrieve_gwp_asan_state(unwindstack::Memory* process_memory, uintptr_t state_addr,\n                                    gwp_asan::AllocatorState* state) {\n  return process_memory->ReadFully(state_addr, state, sizeof(*state));\n}\n\n// Retrieve the GWP-ASan metadata pool from `metadata_addr` inside the process\n// at `process_memory`. The number of metadata slots is retrieved from the\n// allocator state provided. This function returns a heap-allocated copy of the\n// metadata pool whose ownership should be managed by the caller. Returns\n// nullptr on failure.\nstatic const gwp_asan::AllocationMetadata* retrieve_gwp_asan_metadata(\n    unwindstack::Memory* process_memory, const gwp_asan::AllocatorState& state,\n    uintptr_t metadata_addr) {\n  // 1 million GWP-ASan slots would take 4.1GiB of space. Thankfully, copying\n  // the metadata for that amount of slots is only 532MiB, and this really will\n  // only be used with some ridiculous torture-tests.\n  if (state.MaxSimultaneousAllocations > 1000000) {\n    ALOGE(\n        \"Error when retrieving GWP-ASan metadata, MSA from state (%zu) \"\n        \"exceeds maximum allowed (1,000,000).\",\n        state.MaxSimultaneousAllocations);\n    return nullptr;\n  }\n\n  gwp_asan::AllocationMetadata* meta =\n      new gwp_asan::AllocationMetadata[state.MaxSimultaneousAllocations];\n  if (!process_memory->ReadFully(metadata_addr, meta,\n                                 sizeof(*meta) * state.MaxSimultaneousAllocations)) {\n    ALOGE(\n        \"Error when retrieving GWP-ASan metadata, could not retrieve %zu \"\n        \"pieces of metadata.\",\n        state.MaxSimultaneousAllocations);\n    delete[] meta;\n    meta = nullptr;\n  }\n  return meta;\n}\n\nGwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,\n                                   const ProcessInfo& process_info, const ThreadInfo& thread_info) {\n  if (!process_memory || !process_info.gwp_asan_metadata || !process_info.gwp_asan_state) return;\n  // Extract the GWP-ASan regions from the dead process.\n  if (!retrieve_gwp_asan_state(process_memory, process_info.gwp_asan_state, &state_)) return;\n  metadata_.reset(retrieve_gwp_asan_metadata(process_memory, state_, process_info.gwp_asan_metadata));\n  if (!metadata_.get()) return;\n\n  // Get the external crash address from the thread info.\n  crash_address_ = 0u;\n  if (process_info.has_fault_address) {\n    crash_address_ = process_info.untagged_fault_address;\n  }\n\n  // Ensure the error belongs to GWP-ASan.\n  if (!__gwp_asan_error_is_mine(&state_, crash_address_)) return;\n\n  is_gwp_asan_responsible_ = true;\n  thread_id_ = thread_info.tid;\n\n  // Grab the internal error address, if it exists.\n  uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_, crash_address_);\n  if (internal_crash_address) {\n    crash_address_ = internal_crash_address;\n  }\n\n  // Get other information from the internal state.\n  error_ = __gwp_asan_diagnose_error(&state_, metadata_.get(), crash_address_);\n  error_string_ = gwp_asan::ErrorToString(error_);\n  responsible_allocation_ = __gwp_asan_get_metadata(&state_, metadata_.get(), crash_address_);\n}\n\nbool GwpAsanCrashData::CrashIsMine() const {\n  return is_gwp_asan_responsible_;\n}\n\nconstexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;\n\nvoid GwpAsanCrashData::AddCauseProtos(Tombstone* tombstone,\n                                      unwindstack::AndroidUnwinder* unwinder) const {\n  if (!CrashIsMine()) {\n    ALOGE(\"Internal Error: AddCauseProtos() on a non-GWP-ASan crash.\");\n    return;\n  }\n\n  Cause* cause = tombstone->add_causes();\n  MemoryError* memory_error = cause->mutable_memory_error();\n  HeapObject* heap_object = memory_error->mutable_heap();\n\n  memory_error->set_tool(MemoryError_Tool_GWP_ASAN);\n  switch (error_) {\n    case gwp_asan::Error::USE_AFTER_FREE:\n      memory_error->set_type(MemoryError_Type_USE_AFTER_FREE);\n      break;\n    case gwp_asan::Error::DOUBLE_FREE:\n      memory_error->set_type(MemoryError_Type_DOUBLE_FREE);\n      break;\n    case gwp_asan::Error::INVALID_FREE:\n      memory_error->set_type(MemoryError_Type_INVALID_FREE);\n      break;\n    case gwp_asan::Error::BUFFER_OVERFLOW:\n      memory_error->set_type(MemoryError_Type_BUFFER_OVERFLOW);\n      break;\n    case gwp_asan::Error::BUFFER_UNDERFLOW:\n      memory_error->set_type(MemoryError_Type_BUFFER_UNDERFLOW);\n      break;\n    default:\n      memory_error->set_type(MemoryError_Type_UNKNOWN);\n      break;\n  }\n\n  heap_object->set_address(__gwp_asan_get_allocation_address(responsible_allocation_));\n  heap_object->set_size(__gwp_asan_get_allocation_size(responsible_allocation_));\n\n  std::unique_ptr<uintptr_t[]> frames(new uintptr_t[kMaxTraceLength]);\n\n  heap_object->set_allocation_tid(__gwp_asan_get_allocation_thread_id(responsible_allocation_));\n  size_t num_frames =\n      __gwp_asan_get_allocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);\n  for (size_t i = 0; i != num_frames; ++i) {\n    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);\n    BacktraceFrame* f = heap_object->add_allocation_backtrace();\n    fill_in_backtrace_frame(f, frame_data);\n  }\n\n  heap_object->set_deallocation_tid(__gwp_asan_get_deallocation_thread_id(responsible_allocation_));\n  num_frames =\n      __gwp_asan_get_deallocation_trace(responsible_allocation_, frames.get(), kMaxTraceLength);\n  for (size_t i = 0; i != num_frames; ++i) {\n    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames[i]);\n    BacktraceFrame* f = heap_object->add_deallocation_backtrace();\n    fill_in_backtrace_frame(f, frame_data);\n  }\n\n  set_human_readable_cause(cause, crash_address_);\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/backtrace.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _DEBUGGERD_BACKTRACE_H\n#define _DEBUGGERD_BACKTRACE_H\n\n#include <sys/types.h>\n#include <sys/ucontext.h>\n\n#include <map>\n#include <string>\n\n#include <android-base/unique_fd.h>\n\n#include \"types.h\"\n#include \"utility.h\"\n\n// Forward delcaration\nnamespace unwindstack {\nclass AndroidUnwinder;\n}\n\n// Dumps a backtrace using a format similar to what Dalvik uses so that the result\n// can be intermixed in a bug report.\nvoid dump_backtrace(android::base::unique_fd output_fd, unwindstack::AndroidUnwinder* unwinder,\n                    const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread);\n\nvoid dump_backtrace_header(int output_fd);\nvoid dump_backtrace_thread(int output_fd, unwindstack::AndroidUnwinder* unwinder,\n                           const ThreadInfo& thread);\nvoid dump_backtrace_footer(int output_fd);\n\n#endif // _DEBUGGERD_BACKTRACE_H\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/gwp_asan.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <log/log.h>\n#include <unwindstack/Memory.h>\n\n#include \"gwp_asan/common.h\"\n#include \"types.h\"\n#include \"utility.h\"\n\n// Forward delcarations\nclass Cause;\nclass Tombstone;\n\nnamespace unwindstack {\nclass AndroidUnwinder;\nclass Memory;\n}  // namespace unwindstack\n\nclass GwpAsanCrashData {\n public:\n  GwpAsanCrashData() = delete;\n  ~GwpAsanCrashData() = default;\n\n  // Construct the crash data object. Takes a handle to the object that can\n  // supply the memory of the dead process, and pointers to the GWP-ASan state\n  // and metadata regions within that process. Also takes the thread information\n  // of the crashed process. If the process didn't crash via SEGV, GWP-ASan may\n  // still be responsible, as it terminates when it detects an internal error\n  // (double free, invalid free). In these cases, we will retrieve the fault\n  // address from the GWP-ASan allocator's state.\n  GwpAsanCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info,\n                   const ThreadInfo& thread_info);\n\n  // Is GWP-ASan responsible for this crash.\n  bool CrashIsMine() const;\n\n  // Returns the fault address. The fault address may be the same as provided\n  // during construction, or it may have been retrieved from GWP-ASan's internal\n  // allocator crash state.\n  uintptr_t GetFaultAddress() const;\n\n  void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;\n\n protected:\n  // Is GWP-ASan responsible for this crash.\n  bool is_gwp_asan_responsible_ = false;\n\n  // Thread ID of the crash.\n  size_t thread_id_;\n\n  // The type of error that GWP-ASan caused (and the stringified version),\n  // Undefined if GWP-ASan isn't responsible for the crash.\n  gwp_asan::Error error_;\n  const char* error_string_;\n\n  // Pointer to the crash address. Holds the internal crash address if it\n  // exists, otherwise the address provided at construction.\n  uintptr_t crash_address_ = 0u;\n\n  // Pointer to the metadata for the responsible allocation, nullptr if it\n  // doesn't exist.\n  const gwp_asan::AllocationMetadata* responsible_allocation_ = nullptr;\n\n  // Internal state.\n  gwp_asan::AllocatorState state_;\n  std::unique_ptr<const gwp_asan::AllocationMetadata> metadata_;\n};\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n\n#include \"utility.h\"\n\nstruct FDInfo {\n  std::optional<std::string> path;\n  std::optional<uint64_t> fdsan_owner;\n};\n\nusing OpenFilesList = std::map<int, FDInfo>;\n\n// Populates the given list with open files for the given process.\nvoid populate_open_files_list(OpenFilesList* list, pid_t pid);\n\n// Populates the given list with the target process's fdsan table.\nvoid populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,\n                          uint64_t fdsan_table_address);\n\n// Dumps the open files list to the log.\nvoid dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix);\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/scudo.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#if defined(USE_SCUDO)\n\n#include \"types.h\"\n#include \"utility.h\"\n\n#include <memory.h>\n\n#include \"scudo/interface.h\"\n\n// Forward delcarations\nclass Cause;\nclass Tombstone;\n\nnamespace unwindstack {\nclass AndroidUnwinder;\nclass Memory;\n}  // namespace unwindstack\n\nclass ScudoCrashData {\n public:\n  ScudoCrashData() = delete;\n  ~ScudoCrashData() = default;\n  ScudoCrashData(unwindstack::Memory* process_memory, const ProcessInfo& process_info);\n\n  bool CrashIsMine() const;\n\n  void AddCauseProtos(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder) const;\n\n private:\n  scudo_error_info error_info_ = {};\n  uintptr_t untagged_fault_addr_;\n\n  void FillInCause(Cause* cause, const scudo_error_report* report,\n                   unwindstack::AndroidUnwinder* unwinder) const;\n};\n\n#endif  // USE_SCUDO\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _DEBUGGERD_TOMBSTONE_H\n#define _DEBUGGERD_TOMBSTONE_H\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <sys/types.h>\n\n#include <functional>\n#include <map>\n#include <string>\n\n#include <android-base/unique_fd.h>\n\n#include \"open_files_list.h\"\n#include \"tombstone.pb.h\"\n#include \"types.h\"\n\n// Forward declarations\nclass BacktraceFrame;\nclass Cause;\nclass Tombstone;\n\nnamespace unwindstack {\nstruct FrameData;\nclass AndroidUnwinder;\n}\n\n// The maximum number of frames to save when unwinding.\nconstexpr size_t kMaxFrames = 256;\n\n/* Create and open a tombstone file for writing.\n * Returns a writable file descriptor, or -1 with errno set appropriately.\n * If out_path is non-null, *out_path is set to the path of the tombstone file.\n */\nint open_tombstone(std::string* path);\n\n/* Creates a tombstone file and writes the crash dump to it. */\nvoid engrave_tombstone(android::base::unique_fd output_fd, android::base::unique_fd proto_fd,\n                       unwindstack::AndroidUnwinder* unwinder,\n                       const std::map<pid_t, ThreadInfo>& thread_info, pid_t target_thread,\n                       const ProcessInfo& process_info, OpenFilesList* open_files,\n                       std::string* amfd_data, const Architecture* guest_arch = nullptr,\n                       unwindstack::AndroidUnwinder* guest_unwinder = nullptr);\n\nvoid engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,\n                                siginfo_t* siginfo, ucontext_t* ucontext);\n\nvoid engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,\n                             const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,\n                             const ProcessInfo& process_info, const OpenFilesList* open_files,\n                             const Architecture* guest_arch,\n                             unwindstack::AndroidUnwinder* guest_unwinder);\n\nvoid fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame);\nvoid set_human_readable_cause(Cause* cause, uint64_t fault_addr);\n#if defined(__aarch64__)\nvoid dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,\n                        StackHistoryBuffer& shb_ob, bool nounwind = false);\n#endif\n#endif  // _DEBUGGERD_TOMBSTONE_H\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/tombstone_proto_to_text.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <string>\n\nclass BacktraceFrame;\nclass Tombstone;\n\nbool tombstone_proto_to_text(\n    const Tombstone& tombstone,\n    std::function<void(const std::string& line, bool should_log)> callback,\n    std::function<void(const BacktraceFrame& frame)> symbolize);\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/types.h",
    "content": "#pragma once\n\n/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <unwindstack/Regs.h>\n\nstruct ThreadInfo {\n  std::unique_ptr<unwindstack::Regs> registers;\n  long tagged_addr_ctrl = -1;\n  long pac_enabled_keys = -1;\n\n  pid_t uid;\n\n  pid_t tid;\n  std::string thread_name;\n\n  pid_t pid;\n\n  std::vector<std::string> command_line;\n  std::string selinux_label;\n\n  int signo = 0;\n  siginfo_t* siginfo = nullptr;\n\n  std::unique_ptr<unwindstack::Regs> guest_registers;\n#if defined(__aarch64__)\n  uintptr_t tls;  // This is currently used for MTE stack history buffer.\n#endif\n};\n\n// This struct is written into a pipe from inside the crashing process.\nstruct ProcessInfo {\n  uintptr_t abort_msg_address = 0;\n  uintptr_t fdsan_table_address = 0;\n  uintptr_t gwp_asan_state = 0;\n  uintptr_t gwp_asan_metadata = 0;\n  uintptr_t scudo_stack_depot = 0;\n  uintptr_t scudo_region_info = 0;\n  uintptr_t scudo_ring_buffer = 0;\n  size_t scudo_ring_buffer_size = 0;\n  size_t scudo_stack_depot_size = 0;\n\n  bool has_fault_address = false;\n  uintptr_t untagged_fault_address = 0;\n  uintptr_t maybe_tagged_fault_address = 0;\n  uintptr_t crash_detail_page = 0;\n};\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/utility.h",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <inttypes.h>\n#include <signal.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include <android-base/macros.h>\n\nstruct log_t {\n  // Tombstone file descriptor.\n  int tfd;\n  // Data to be sent to the Activity Manager.\n  std::string* amfd_data;\n  // The tid of the thread that crashed.\n  pid_t crashed_tid;\n  // The tid of the thread we are currently working with.\n  pid_t current_tid;\n  // logd daemon crash, can block asking for logcat data, allow suppression.\n  bool should_retrieve_logcat;\n\n  log_t()\n      : tfd(-1),\n        amfd_data(nullptr),\n        crashed_tid(-1),\n        current_tid(-1),\n        should_retrieve_logcat(true) {}\n};\n\n// List of types of logs to simplify the logging decision in _LOG\nenum logtype {\n  HEADER,\n  THREAD,\n  REGISTERS,\n  BACKTRACE,\n  MAPS,\n  MEMORY,\n  STACK,\n  LOGS,\n  OPEN_FILES\n};\n\n#if defined(__LP64__)\n#define PRIPTR \"016\" PRIx64\ntypedef uint64_t word_t;\n#else\n#define PRIPTR \"08\" PRIx64\ntypedef uint32_t word_t;\n#endif\n\n// Log information onto the tombstone.\nvoid _LOG(log_t* log, logtype ltype, const char* fmt, ...) __attribute__((format(printf, 3, 4)));\nvoid _VLOG(log_t* log, logtype ltype, const char* fmt, va_list ap);\n\nnamespace unwindstack {\nclass AndroidUnwinder;\nclass Memory;\nstruct AndroidUnwinderData;\n}\n\nvoid log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,\n                   unwindstack::AndroidUnwinderData& data, const char* prefix);\n\nssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,\n                    unwindstack::Memory* memory);\nvoid dump_memory(log_t* log, unwindstack::Memory* backtrace, uint64_t addr, const std::string&);\n\nvoid drop_capabilities();\n\nbool signal_has_sender(const siginfo_t*, pid_t caller_pid);\nbool signal_has_si_addr(const siginfo_t*);\nvoid get_signal_sender(char* buf, size_t n, const siginfo_t*);\nconst char* get_signame(const siginfo_t*);\nconst char* get_sigcode(const siginfo_t*);\n"
  },
  {
    "path": "debuggerd/libdebuggerd/include/libdebuggerd/utility_host.h",
    "content": "/*\n * Copyright 2024, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <stddef.h>\n\nstd::string describe_tagged_addr_ctrl(long ctrl);\nstd::string describe_pac_enabled_keys(long keys);\n\n// Number of bytes per MTE granule.\nconstexpr size_t kTagGranuleSize = 16;\n\n// Number of rows and columns to display in an MTE tag dump.\nconstexpr size_t kNumTagColumns = 16;\nconstexpr size_t kNumTagRows = 16;\n\n// Encode all non-ascii values and also ascii values that are not printable.\nstd::string oct_encode_non_ascii_printable(const std::string& data);\n// Encode any value that fails isprint(), includes encoding chars like '\\n' and '\\t'.\nstd::string oct_encode_non_printable(const std::string& data);\n"
  },
  {
    "path": "debuggerd/libdebuggerd/open_files_list.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"DEBUG\"\n\n#include \"libdebuggerd/open_files_list.h\"\n\n#include <android/fdsan.h>\n#include <dirent.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <android-base/file.h>\n#include <log/log.h>\n#include <unwindstack/Memory.h>\n\n#include \"libdebuggerd/utility.h\"\n#include \"private/bionic_fdsan.h\"\n\nvoid populate_open_files_list(OpenFilesList* list, pid_t pid) {\n  std::string fd_dir_name = \"/proc/\" + std::to_string(pid) + \"/fd\";\n  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(fd_dir_name.c_str()), closedir);\n  if (dir == nullptr) {\n    ALOGE(\"failed to open directory %s: %s\", fd_dir_name.c_str(), strerror(errno));\n    return;\n  }\n\n  struct dirent* de;\n  while ((de = readdir(dir.get())) != nullptr) {\n    if (*de->d_name == '.') {\n      continue;\n    }\n\n    int fd = atoi(de->d_name);\n    std::string path = fd_dir_name + \"/\" + std::string(de->d_name);\n    std::string target;\n    if (android::base::Readlink(path, &target)) {\n      (*list)[fd].path = target;\n    } else {\n      (*list)[fd].path = \"???\";\n      ALOGE(\"failed to readlink %s: %s\", path.c_str(), strerror(errno));\n    }\n  }\n}\n\nvoid populate_fdsan_table(OpenFilesList* list, std::shared_ptr<unwindstack::Memory> memory,\n                          uint64_t fdsan_table_address) {\n  constexpr size_t inline_fds = sizeof(FdTable::entries) / sizeof(*FdTable::entries);\n  static_assert(inline_fds == 128);\n  size_t entry_offset = offsetof(FdTable, entries);\n  for (size_t i = 0; i < inline_fds; ++i) {\n    uint64_t address = fdsan_table_address + entry_offset + sizeof(FdEntry) * i;\n    FdEntry entry;\n    if (!memory->Read(address, &entry, sizeof(entry))) {\n      ALOGE(\"failed to read fdsan table entry %zu: %s\", i, strerror(errno));\n      return;\n    }\n    if (entry.close_tag) {\n      (*list)[i].fdsan_owner = entry.close_tag.load();\n    }\n  }\n\n  size_t overflow_offset = offsetof(FdTable, overflow);\n  uintptr_t overflow = 0;\n  if (!memory->Read(fdsan_table_address + overflow_offset, &overflow, sizeof(overflow))) {\n    ALOGE(\"failed to read fdsan table overflow pointer: %s\", strerror(errno));\n    return;\n  }\n\n  if (!overflow) {\n    return;\n  }\n\n  size_t overflow_length;\n  if (!memory->Read(overflow, &overflow_length, sizeof(overflow_length))) {\n    ALOGE(\"failed to read fdsan overflow table length: %s\", strerror(errno));\n    return;\n  }\n\n  if (overflow_length > 131072) {\n    ALOGE(\"unreasonable large fdsan overflow table size %zu, bailing out\", overflow_length);\n    return;\n  }\n\n  for (size_t i = 0; i < overflow_length; ++i) {\n    int fd = i + inline_fds;\n    uint64_t address = overflow + offsetof(FdTableOverflow, entries) + i * sizeof(FdEntry);\n    FdEntry entry;\n    if (!memory->Read(address, &entry, sizeof(entry))) {\n      ALOGE(\"failed to read fdsan overflow entry for fd %d: %s\", fd, strerror(errno));\n      return;\n    }\n    if (entry.close_tag) {\n      (*list)[fd].fdsan_owner = entry.close_tag;\n    }\n  }\n  return;\n}\n\nvoid dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) {\n  for (auto& [fd, entry] : files) {\n    const std::optional<std::string>& path = entry.path;\n    const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;\n    if (path && fdsan_owner) {\n      const char* type = android_fdsan_get_tag_type(*fdsan_owner);\n      uint64_t value = android_fdsan_get_tag_value(*fdsan_owner);\n      _LOG(log, logtype::OPEN_FILES, \"%sfd %i: %s (owned by %s %#\" PRIx64 \")\\n\", prefix, fd,\n           path->c_str(), type, value);\n    } else if (path && !fdsan_owner) {\n      _LOG(log, logtype::OPEN_FILES, \"%sfd %i: %s (unowned)\\n\", prefix, fd, path->c_str());\n    } else if (!path && fdsan_owner) {\n      _LOG(log, logtype::OPEN_FILES, \"%sfd %i: <MISSING> (owned by %#\" PRIx64 \")\\n\", prefix, fd,\n           *fdsan_owner);\n    } else {\n      ALOGE(\"OpenFilesList contains an entry (fd %d) with no path or owner\", fd);\n    }\n  }\n}\n\n"
  },
  {
    "path": "debuggerd/libdebuggerd/scudo.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#if defined(USE_SCUDO)\n\n#include \"libdebuggerd/scudo.h\"\n#include \"libdebuggerd/tombstone.h\"\n#include \"libdebuggerd/utility_host.h\"\n\n#include \"unwindstack/AndroidUnwinder.h\"\n#include \"unwindstack/Memory.h\"\n\n#include <android-base/macros.h>\n#include <bionic/macros.h>\n#include <unistd.h>\n\n#include \"tombstone.pb.h\"\n\nstd::unique_ptr<char[]> AllocAndReadFully(unwindstack::Memory* process_memory, uint64_t addr,\n                                          size_t size) {\n  auto buf = std::make_unique<char[]>(size);\n  if (!process_memory->ReadFully(addr, buf.get(), size)) {\n    return std::unique_ptr<char[]>();\n  }\n  return buf;\n}\n\nScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,\n                               const ProcessInfo& process_info) {\n  if (!process_info.has_fault_address) {\n    return;\n  }\n\n  auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,\n                                       __scudo_get_region_info_size());\n  std::unique_ptr<char[]> ring_buffer;\n  if (process_info.scudo_ring_buffer_size != 0) {\n    ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,\n                                    process_info.scudo_ring_buffer_size);\n  }\n  std::unique_ptr<char[]> stack_depot;\n  if (process_info.scudo_stack_depot_size != 0) {\n    stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot,\n                                    process_info.scudo_stack_depot_size);\n  }\n  if (!region_info) {\n    return;\n  }\n\n  untagged_fault_addr_ = process_info.untagged_fault_address;\n  uintptr_t fault_page = untagged_fault_addr_ & ~(getpagesize() - 1);\n\n  uintptr_t memory_begin = fault_page - getpagesize() * 16;\n  if (memory_begin > fault_page) {\n    return;\n  }\n\n  uintptr_t memory_end = fault_page + getpagesize() * 16;\n  if (memory_end < fault_page) {\n    return;\n  }\n\n  auto memory = std::make_unique<char[]>(memory_end - memory_begin);\n  for (auto i = memory_begin; i != memory_end; i += getpagesize()) {\n    process_memory->ReadFully(i, memory.get() + i - memory_begin, getpagesize());\n  }\n\n  auto memory_tags = std::make_unique<char[]>((memory_end - memory_begin) / kTagGranuleSize);\n  for (auto i = memory_begin; i != memory_end; i += kTagGranuleSize) {\n    memory_tags[(i - memory_begin) / kTagGranuleSize] = process_memory->ReadTag(i);\n  }\n\n  __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(),\n                         process_info.scudo_stack_depot_size, region_info.get(), ring_buffer.get(),\n                         process_info.scudo_ring_buffer_size, memory.get(), memory_tags.get(),\n                         memory_begin, memory_end - memory_begin);\n}\n\nbool ScudoCrashData::CrashIsMine() const {\n  return error_info_.reports[0].error_type != UNKNOWN;\n}\n\nvoid ScudoCrashData::FillInCause(Cause* cause, const scudo_error_report* report,\n                                 unwindstack::AndroidUnwinder* unwinder) const {\n  MemoryError* memory_error = cause->mutable_memory_error();\n  HeapObject* heap_object = memory_error->mutable_heap();\n\n  memory_error->set_tool(MemoryError_Tool_SCUDO);\n  switch (report->error_type) {\n    case USE_AFTER_FREE:\n      memory_error->set_type(MemoryError_Type_USE_AFTER_FREE);\n      break;\n    case BUFFER_OVERFLOW:\n      memory_error->set_type(MemoryError_Type_BUFFER_OVERFLOW);\n      break;\n    case BUFFER_UNDERFLOW:\n      memory_error->set_type(MemoryError_Type_BUFFER_UNDERFLOW);\n      break;\n    default:\n      memory_error->set_type(MemoryError_Type_UNKNOWN);\n      break;\n  }\n\n  heap_object->set_address(report->allocation_address);\n  heap_object->set_size(report->allocation_size);\n\n  heap_object->set_allocation_tid(report->allocation_tid);\n  for (size_t i = 0; i < arraysize(report->allocation_trace) && report->allocation_trace[i]; ++i) {\n    unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(report->allocation_trace[i]);\n    BacktraceFrame* f = heap_object->add_allocation_backtrace();\n    fill_in_backtrace_frame(f, frame_data);\n  }\n\n  heap_object->set_deallocation_tid(report->deallocation_tid);\n  for (size_t i = 0; i < arraysize(report->deallocation_trace) && report->deallocation_trace[i];\n       ++i) {\n    unwindstack::FrameData frame_data =\n        unwinder->BuildFrameFromPcOnly(report->deallocation_trace[i]);\n    BacktraceFrame* f = heap_object->add_deallocation_backtrace();\n    fill_in_backtrace_frame(f, frame_data);\n  }\n\n  set_human_readable_cause(cause, untagged_fault_addr_);\n}\n\nvoid ScudoCrashData::AddCauseProtos(Tombstone* tombstone,\n                                    unwindstack::AndroidUnwinder* unwinder) const {\n  size_t report_num = 0;\n  while (report_num < sizeof(error_info_.reports) / sizeof(error_info_.reports[0]) &&\n         error_info_.reports[report_num].error_type != UNKNOWN) {\n    FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder);\n  }\n}\n\n#endif  // USE_SCUDO\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/UnwinderMock.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <unwindstack/MapInfo.h>\n#include <unwindstack/Maps.h>\n#include <unwindstack/Unwinder.h>\n\nclass UnwinderMock : public unwindstack::Unwinder {\n public:\n  UnwinderMock() : Unwinder(128, new unwindstack::Maps, nullptr) {}\n  virtual ~UnwinderMock() { delete GetMaps(); }\n\n  void MockAddMap(uint64_t start, uint64_t end, uint64_t offset, uint64_t flags, std::string name,\n                  uint64_t load_bias) {\n    GetMaps()->Add(start, end, offset, flags, name, load_bias);\n  }\n\n  void MockSetBuildID(uint64_t offset, const std::string& build_id) {\n    std::shared_ptr<unwindstack::MapInfo> map_info = GetMaps()->Find(offset);\n    if (map_info != nullptr) {\n      map_info->SetBuildID(std::string(build_id));\n    }\n  }\n};\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/dump_memory_test.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdlib.h>\n\n#include <memory>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <gtest/gtest.h>\n#include <unwindstack/Memory.h>\n\n#include \"libdebuggerd/utility.h\"\n\n#include \"log_fake.h\"\n\nstd::string GetMemoryString(uintptr_t addr, const std::vector<uint64_t>& data) {\n  // Must be even number of data values.\n  CHECK((data.size() & 1) == 0);\n\n  std::string str;\n  for (size_t i = 0; i < data.size(); i += 2) {\n    str += \"    \";\n    std::string ascii_str = \"\";\n    for (size_t j = 0; j < 2; j++) {\n      for (size_t k = 0; k < 8; k++) {\n        uint8_t c = (data[i + j] >> (k * 8)) & 0xff;\n        if (c >= 0x20 && c < 0x7f) {\n          ascii_str += c;\n        } else {\n          ascii_str += '.';\n        }\n      }\n    }\n#if defined(__LP64__)\n    str += android::base::StringPrintf(\"%016zx %016zx %016zx  \", addr, data[i], data[i + 1]);\n#else\n    str += android::base::StringPrintf(\n        \"%08zx %08zx %08zx %08zx %08zx  \", addr, static_cast<uintptr_t>(data[i] & 0xffffffff),\n        static_cast<uintptr_t>(data[i] >> 32), static_cast<uintptr_t>(data[i + 1] & 0xffffffff),\n        static_cast<uintptr_t>(data[i + 1] >> 32));\n#endif\n    str += ascii_str + \"\\n\";\n    addr += 0x10;\n  }\n  return str;\n}\n\nconst std::vector<uint64_t>& GetDefaultData() {\n  static std::vector<uint64_t> data(\n      {0x0706050403020100UL, 0x0f0e0d0c0b0a0908UL, 0x1716151413121110UL, 0x1f1e1d1c1b1a1918UL,\n       0x2726252423222120UL, 0x2f2e2d2c2b2a2928UL, 0x3736353433323130UL, 0x3f3e3d3c3b3a3938UL,\n       0x4746454443424140UL, 0x4f4e4d4c4b4a4948UL, 0x5756555453525150UL, 0x5f5e5d5c5b5a5958UL,\n       0x6766656463626160UL, 0x6f6e6d6c6b6a6968UL, 0x7776757473727170UL, 0x7f7e7d7c7b7a7978UL,\n       0x8786858483828180UL, 0x8f8e8d8c8b8a8988UL, 0x9796959493929190UL, 0x9f9e9d9c9b9a9998UL,\n       0xa7a6a5a4a3a2a1a0UL, 0xafaeadacabaaa9a8UL, 0xb7b6b5b4b3b2b1b0UL, 0xbfbebdbcbbbab9b8UL,\n       0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL, 0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL,\n       0xe7e6e5e4e3e2e1e0UL, 0xefeeedecebeae9e8UL, 0xf7f6f5f4f3f2f1f0UL, 0xfffefdfcfbfaf9f8UL});\n  return data;\n}\n\nstd::string GetFullDumpString() {\n  std::string str = \"\\nmemory near r1:\\n\";\n  str += GetMemoryString(0x12345650U, GetDefaultData());\n  return str;\n}\n\nstd::string GetPartialDumpString() {\n  std::string str = \"\\nmemory near pc:\\n\";\n  std::vector<uint64_t> data = GetDefaultData();\n  data.resize(12);\n  str += GetMemoryString(0x123455e0U, data);\n  return str;\n}\n\nclass MemoryMock : public unwindstack::Memory {\n public:\n  virtual ~MemoryMock() = default;\n\n  virtual size_t Read(uint64_t addr, void* buffer, size_t bytes) override {\n    size_t offset = 0;\n    if (last_read_addr_ > 0) {\n      offset = addr - last_read_addr_;\n    }\n    size_t bytes_available = 0;\n    if (offset < buffer_.size()) {\n      bytes_available = buffer_.size() - offset;\n    }\n\n    if (partial_read_) {\n      bytes = std::min(bytes, bytes_partial_read_);\n      bytes_partial_read_ -= bytes;\n      partial_read_ = bytes_partial_read_;\n    } else if (bytes > bytes_available) {\n      bytes = bytes_available;\n    }\n\n    if (bytes > 0) {\n      memcpy(buffer, buffer_.data() + offset, bytes);\n    }\n\n    last_read_addr_ = addr;\n    return bytes;\n  }\n\n  void SetReadData(uint8_t* buffer, size_t bytes) {\n    buffer_.resize(bytes);\n    memcpy(buffer_.data(), buffer, bytes);\n    bytes_partial_read_ = 0;\n    last_read_addr_ = 0;\n  }\n\n  void SetPartialReadAmount(size_t bytes) {\n    if (bytes > buffer_.size()) {\n      abort();\n    }\n    partial_read_ = true;\n    bytes_partial_read_ = bytes;\n  }\n\n private:\n  std::vector<uint8_t> buffer_;\n  bool partial_read_ = false;\n  size_t bytes_partial_read_ = 0;\n  uintptr_t last_read_addr_ = 0;\n};\n\nclass DumpMemoryTest : public ::testing::Test {\n protected:\n  virtual void SetUp() {\n    memory_mock_ = std::make_unique<MemoryMock>();\n\n    char tmp_file[256];\n    const char data_template[] = \"/data/local/tmp/debuggerd_memory_testXXXXXX\";\n    memcpy(tmp_file, data_template, sizeof(data_template));\n    int tombstone_fd = mkstemp(tmp_file);\n    if (tombstone_fd == -1) {\n      const char tmp_template[] = \"/tmp/debuggerd_memory_testXXXXXX\";\n      memcpy(tmp_file, tmp_template, sizeof(tmp_template));\n      tombstone_fd = mkstemp(tmp_file);\n      if (tombstone_fd == -1) {\n        abort();\n      }\n    }\n    if (unlink(tmp_file) == -1) {\n      abort();\n    }\n\n    log_.tfd = tombstone_fd;\n    log_.amfd_data = nullptr;\n    log_.crashed_tid = 12;\n    log_.current_tid = 12;\n    log_.should_retrieve_logcat = false;\n\n    resetLogs();\n  }\n\n  virtual void TearDown() {\n    if (log_.tfd >= 0) {\n      close(log_.tfd);\n    }\n    memory_mock_.reset();\n  }\n\n  std::unique_ptr<MemoryMock> memory_mock_;\n\n  log_t log_;\n};\n\nTEST_F(DumpMemoryTest, aligned_addr) {\n  uint8_t buffer[256];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n\n  dump_memory(&log_, memory_mock_.get(), 0x12345678, \"memory near r1\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_EQ(GetFullDumpString(), tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, partial_read) {\n  uint8_t buffer[256];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n  memory_mock_->SetPartialReadAmount(96);\n\n  dump_memory(&log_, memory_mock_.get(), 0x12345679, \"memory near r1\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_EQ(GetFullDumpString(), tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, unaligned_addr) {\n  uint8_t buffer[256];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n\n  dump_memory(&log_, memory_mock_.get(), 0x12345679, \"memory near r1\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_EQ(GetFullDumpString(), tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, memory_unreadable) {\n  dump_memory(&log_, memory_mock_.get(), 0xa2345678, \"memory near pc\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_STREQ(\"\", tombstone_contents.c_str());\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, memory_partially_unreadable) {\n  uint8_t buffer[104];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n\n  dump_memory(&log_, memory_mock_.get(), 0x12345600, \"memory near pc\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_EQ(GetPartialDumpString(), tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) {\n  uint8_t buffer[104];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n  memory_mock_->SetPartialReadAmount(102);\n\n  dump_memory(&log_, memory_mock_.get(), 0x12345600, \"memory near pc\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_EQ(GetPartialDumpString(), tombstone_contents);\n\n#if defined(__LP64__)\n  ASSERT_STREQ(\"6 DEBUG Bytes read 102, is not a multiple of 8\\n\", getFakeLogPrint().c_str());\n#else\n  ASSERT_STREQ(\"6 DEBUG Bytes read 102, is not a multiple of 4\\n\", getFakeLogPrint().c_str());\n#endif\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n}\n\nTEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) {\n  uint8_t buffer[106];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n  memory_mock_->SetPartialReadAmount(45);\n\n  dump_memory(&log_, memory_mock_.get(), 0x12345600, \"memory near pc\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_EQ(GetPartialDumpString(), tombstone_contents);\n\n#if defined(__LP64__)\n  ASSERT_STREQ(\"6 DEBUG Bytes read 45, is not a multiple of 8\\n\"\n               \"6 DEBUG Bytes after second read 106, is not a multiple of 8\\n\",\n               getFakeLogPrint().c_str());\n#else\n  ASSERT_STREQ(\"6 DEBUG Bytes read 45, is not a multiple of 4\\n\"\n               \"6 DEBUG Bytes after second read 106, is not a multiple of 4\\n\",\n               getFakeLogPrint().c_str());\n#endif\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n}\n\nTEST_F(DumpMemoryTest, address_low_fence) {\n  uint8_t buffer[256];\n  memset(buffer, 0, sizeof(buffer));\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n\n  dump_memory(&log_, memory_mock_.get(), 0x1000, \"memory near r1\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  std::string expected_dump = \"\\nmemory near r1:\\n\";\n  expected_dump += GetMemoryString(0x1000, std::vector<uint64_t>(32, 0UL));\n  ASSERT_EQ(expected_dump, tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, memory_address_too_high) {\n  uint8_t buffer[256];\n  memset(buffer, 0, sizeof(buffer));\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n\n#if defined(__LP64__)\n  dump_memory(&log_, memory_mock_.get(), -32, \"memory near r1\");\n  dump_memory(&log_, memory_mock_.get(), -208, \"memory near r1\");\n#else\n  dump_memory(&log_, memory_mock_.get(), 0x100000000 - 32, \"memory near r1\");\n  dump_memory(&log_, memory_mock_.get(), 0x100000000 - 208, \"memory near r1\");\n#endif\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_STREQ(\"\", tombstone_contents.c_str());\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, memory_address_nearly_too_high) {\n  uint8_t buffer[256];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n\n#if defined(__LP64__)\n  dump_memory(&log_, memory_mock_.get(), -224, \"memory near r4\");\n#else\n  dump_memory(&log_, memory_mock_.get(), 0x100000000 - 224, \"memory near r4\");\n#endif\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  std::string expected_dump = \"\\nmemory near r4:\\n\";\n  uintptr_t addr;\n#if defined(__aarch64__)\n  addr = 0x00ffffffffffff00UL;\n#elif defined(__LP64__)\n  addr = 0xffffffffffffff00UL;\n#else\n  addr = 0xffffff00UL;\n#endif\n  expected_dump += GetMemoryString(addr, GetDefaultData());\n  ASSERT_EQ(expected_dump, tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, first_read_empty) {\n  uint8_t buffer[256];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n  memory_mock_->SetPartialReadAmount(0);\n\n  size_t page_size = sysconf(_SC_PAGE_SIZE);\n  uintptr_t addr = 0x10000020 + page_size - 120;\n  dump_memory(&log_, memory_mock_.get(), addr, \"memory near r4\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  std::string expected_dump = \"\\nmemory near r4:\\n\";\n  expected_dump += GetMemoryString(\n      0x10000000 + page_size,\n      std::vector<uint64_t>{\n          0x8786858483828180UL, 0x8f8e8d8c8b8a8988UL, 0x9796959493929190UL, 0x9f9e9d9c9b9a9998UL,\n          0xa7a6a5a4a3a2a1a0UL, 0xafaeadacabaaa9a8UL, 0xb7b6b5b4b3b2b1b0UL, 0xbfbebdbcbbbab9b8UL,\n          0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL, 0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL,\n          0xe7e6e5e4e3e2e1e0UL, 0xefeeedecebeae9e8UL, 0xf7f6f5f4f3f2f1f0UL, 0xfffefdfcfbfaf9f8UL});\n  ASSERT_EQ(expected_dump, tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {\n  uint8_t buffer[224];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n  memory_mock_->SetPartialReadAmount(0);\n\n  size_t page_size = sysconf(_SC_PAGE_SIZE);\n  uintptr_t addr = 0x10000020 + page_size - 192;\n  dump_memory(&log_, memory_mock_.get(), addr, \"memory near r4\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  std::string expected_dump = \"\\nmemory near r4:\\n\";\n  expected_dump += GetMemoryString(\n      0x10000000 + page_size, std::vector<uint64_t>{0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL,\n                                                    0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL});\n  ASSERT_EQ(expected_dump, tombstone_contents);\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {\n  uint8_t buffer[256];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n  memory_mock_->SetPartialReadAmount(0);\n\n  uintptr_t addr = 0x10000020;\n  dump_memory(&log_, memory_mock_.get(), addr, \"memory near r4\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_STREQ(\"\", tombstone_contents.c_str());\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n\nTEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {\n  uint8_t buffer[256];\n  for (size_t i = 0; i < sizeof(buffer); i++) {\n    buffer[i] = i;\n  }\n  memory_mock_->SetReadData(buffer, sizeof(buffer));\n  memory_mock_->SetPartialReadAmount(0);\n\n  size_t page_size = sysconf(_SC_PAGE_SIZE);\n  uintptr_t addr = 0x10000020 + page_size - 256;\n\n  dump_memory(&log_, memory_mock_.get(), addr, \"memory near r4\");\n\n  std::string tombstone_contents;\n  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);\n  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));\n  ASSERT_STREQ(\"\", tombstone_contents.c_str());\n\n  // Verify that the log buf is empty, and no error messages.\n  ASSERT_STREQ(\"\", getFakeLogBuf().c_str());\n  ASSERT_STREQ(\"\", getFakeLogPrint().c_str());\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/elf_fake.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"elf_fake.h\"\n\n#include <stdint.h>\n\n#include <string>\n\nnamespace unwindstack {\nclass Memory;\n}\n\nstd::string g_build_id;\n\nvoid elf_set_fake_build_id(const std::string& build_id) {\n  g_build_id = build_id;\n}\n\nbool elf_get_build_id(unwindstack::Memory*, uintptr_t, std::string* build_id) {\n  if (g_build_id != \"\") {\n    *build_id = g_build_id;\n    return true;\n  }\n  return false;\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/elf_fake.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _DEBUGGERD_TEST_ELF_FAKE_H\n#define _DEBUGGERD_TEST_ELF_FAKE_H\n\n#include <string>\n\nvoid elf_set_fake_build_id(const std::string&);\n\n#endif // _DEBUGGERD_TEST_ELF_FAKE_H\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/log_fake.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"log_fake.h\"\n\n#include <errno.h>\n#include <stdarg.h>\n\n#include <string>\n\n#include <android-base/stringprintf.h>\n#include <log/log.h>\n\n// Forward declarations.\nclass Backtrace;\nstruct EventTagMap;\nstruct AndroidLogEntry;\n\nstd::string g_fake_log_buf;\n\nstd::string g_fake_log_print;\n\nvoid resetLogs() {\n  g_fake_log_buf = \"\";\n  g_fake_log_print = \"\";\n}\n\nstd::string getFakeLogBuf() {\n  return g_fake_log_buf;\n}\n\nstd::string getFakeLogPrint() {\n  return g_fake_log_print;\n}\n\nextern \"C\" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {\n  g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';\n  g_fake_log_buf += tag;\n  g_fake_log_buf += ' ';\n  g_fake_log_buf += msg;\n  return 1;\n}\n\nextern \"C\" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {\n  g_fake_log_print += std::to_string(prio) + ' ';\n  g_fake_log_print += tag;\n  g_fake_log_print += ' ';\n\n  va_list ap;\n  va_start(ap, fmt);\n  android::base::StringAppendV(&g_fake_log_print, fmt, ap);\n  va_end(ap);\n\n  g_fake_log_print += '\\n';\n\n  return 1;\n}\n\nextern \"C\" log_id_t android_name_to_log_id(const char*) {\n  return LOG_ID_SYSTEM;\n}\n\nextern \"C\" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {\n  errno = EACCES;\n  return nullptr;\n}\n\nextern \"C\" int android_logger_list_read(struct logger_list*, struct log_msg*) {\n  return 0;\n}\n\nextern \"C\" EventTagMap* android_openEventTagMap(const char*) {\n  return nullptr;\n}\n\nextern \"C\" int android_log_processBinaryLogBuffer(\n    struct logger_entry*,\n    AndroidLogEntry*, const EventTagMap*, char*, int) {\n  return 0;\n}\n\nextern \"C\" void android_logger_list_free(struct logger_list*) {\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/log_fake.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _DEBUGGERD_TEST_LOG_FAKE_H\n#define _DEBUGGERD_TEST_LOG_FAKE_H\n\n#include <string>\n\nvoid resetLogs();\nstd::string getFakeLogBuf();\nstd::string getFakeLogPrint();\n\n#endif // _DEBUGGERD_TEST_LOG_FAKE_H\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/mte_stack_record_test.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#if defined(__aarch64__)\n\n#include <stdint.h>\n#include <sys/mman.h>\n\n#include <optional>\n\n#include \"bionic/mte.h\"\n#include \"bionic/page.h\"\n#include \"unwindstack/AndroidUnwinder.h\"\n#include \"unwindstack/Memory.h\"\n\n#include <android-base/test_utils.h>\n#include <procinfo/process_map.h>\n\n#include \"gtest/gtest.h\"\n\n#include \"libdebuggerd/tombstone.h\"\n\nstruct ScopedUnmap {\n  void* ptr;\n  size_t size;\n  ~ScopedUnmap() { munmap(ptr, size); }\n};\n\nclass MteStackHistoryTest : public ::testing::TestWithParam<int> {\n  void SetUp() override {\n#if !defined(__aarch64__)\n    GTEST_SKIP();\n#endif\n    SKIP_WITH_HWASAN;\n    unwinder.emplace();\n    unwindstack::ErrorData E;\n    ASSERT_TRUE(unwinder->Initialize(E));\n  }\n\n protected:\n  // std::optional so we don't construct it for the SKIP cases.\n  std::optional<unwindstack::AndroidLocalUnwinder> unwinder;\n};\n\nTEST(MteStackHistoryUnwindTest, TestOne) {\n#if !defined(__aarch64__)\n  GTEST_SKIP();\n#endif\n  SKIP_WITH_HWASAN;\n  size_t size = stack_mte_ringbuffer_size(0);\n  char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(0, nullptr));\n  ScopedUnmap s{data, size};\n\n  uintptr_t taggedfp = (1ULL << 56) | 1;\n  uintptr_t pc = reinterpret_cast<uintptr_t>(&memcpy);\n  memcpy(data, &pc, sizeof(pc));\n  memcpy(data + 8, &taggedfp, sizeof(taggedfp));\n\n  // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.\n  void* tls[4] = {data + 16};\n\n  unwindstack::AndroidLocalUnwinder unwinder;\n  unwindstack::ErrorData E;\n  unwinder.Initialize(E);\n  StackHistoryBuffer shb;\n  dump_stack_history(&unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ false);\n  ASSERT_EQ(shb.entries_size(), 1);\n  const StackHistoryBufferEntry& e = shb.entries(0);\n  EXPECT_EQ(e.addr().pc(), pc);\n  EXPECT_EQ(e.addr().file_name(), \"/apex/com.android.runtime/lib64/bionic/libc.so\");\n  EXPECT_EQ(e.fp(), 1ULL);\n  EXPECT_EQ(e.tag(), 1ULL);\n}\n\nstatic std::optional<android::procinfo::MapInfo> FindMapping(void* data) {\n  std::optional<android::procinfo::MapInfo> result;\n  android::procinfo::ReadMapFile(\n      \"/proc/self/maps\", [&result, data](const android::procinfo::MapInfo& info) {\n        auto data_int = reinterpret_cast<uint64_t>(data) & ((1ULL << 56ULL) - 1ULL);\n        if (info.start <= data_int && data_int < info.end) {\n          result = info;\n        }\n      });\n  return result;\n}\n\nTEST_P(MteStackHistoryTest, TestFree) {\n  int size_cls = GetParam();\n  size_t size = stack_mte_ringbuffer_size(size_cls);\n  void* data = stack_mte_ringbuffer_allocate(size_cls, nullptr);\n  EXPECT_EQ(stack_mte_ringbuffer_size_from_pointer(reinterpret_cast<uintptr_t>(data)), size);\n  auto before = FindMapping(data);\n  ASSERT_TRUE(before.has_value());\n  EXPECT_EQ(before->end - before->start, size);\n  stack_mte_free_ringbuffer(reinterpret_cast<uintptr_t>(data));\n  for (size_t i = 0; i < size; i += page_size()) {\n    auto after = FindMapping(static_cast<char*>(data) + i);\n    EXPECT_TRUE(!after.has_value() || after->name != before->name);\n  }\n}\n\nTEST_P(MteStackHistoryTest, TestEmpty) {\n  int size_cls = GetParam();\n  size_t size = stack_mte_ringbuffer_size(size_cls);\n  void* data = stack_mte_ringbuffer_allocate(size_cls, nullptr);\n  ScopedUnmap s{data, size};\n  // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.\n  void* tls[4] = {data};\n\n  StackHistoryBuffer shb;\n  dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);\n  EXPECT_EQ(shb.entries_size(), 0);\n}\n\nTEST_P(MteStackHistoryTest, TestFull) {\n  int size_cls = GetParam();\n  size_t size = stack_mte_ringbuffer_size(size_cls);\n  char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(size_cls, nullptr));\n  ScopedUnmap s{data, size};\n  uintptr_t itr = 1;\n  for (char* d = data; d < &data[size]; d += 16) {\n    uintptr_t taggedfp = ((itr & 15) << 56) | itr;\n    uintptr_t pc = itr;\n    memcpy(d, &pc, sizeof(pc));\n    memcpy(d + 8, &taggedfp, sizeof(taggedfp));\n    ++itr;\n  }\n  // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.\n  // Because the buffer is full, and we point at one past the last inserted element,\n  // due to wrap-around we point at the beginning of the buffer.\n  void* tls[4] = {data};\n\n  StackHistoryBuffer shb;\n  dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);\n  EXPECT_EQ(static_cast<size_t>(shb.entries_size()), size / 16);\n  for (const auto& entry : shb.entries()) {\n    EXPECT_EQ(entry.addr().pc(), --itr);\n    EXPECT_EQ(entry.addr().pc(), entry.fp());\n    EXPECT_EQ(entry.addr().pc() & 15, entry.tag());\n  }\n}\n\nTEST_P(MteStackHistoryTest, TestHalfFull) {\n  int size_cls = GetParam();\n  size_t size = stack_mte_ringbuffer_size(size_cls);\n  size_t half_size = size / 2;\n\n  char* data = static_cast<char*>(stack_mte_ringbuffer_allocate(size_cls, nullptr));\n  ScopedUnmap s{data, size};\n\n  uintptr_t itr = 1;\n  for (char* d = data; d < &data[half_size]; d += 16) {\n    uintptr_t taggedfp = ((itr & 15) << 56) | itr;\n    uintptr_t pc = itr;\n    memcpy(d, &pc, sizeof(pc));\n    memcpy(d + 8, &taggedfp, sizeof(taggedfp));\n    ++itr;\n  }\n  // The MTE TLS is at TLS - 3, so we allocate 3 placeholders.\n  void* tls[4] = {&data[half_size]};\n\n  StackHistoryBuffer shb;\n  dump_stack_history(&*unwinder, reinterpret_cast<uintptr_t>(&tls[3]), shb, /* nounwind= */ true);\n  EXPECT_EQ(static_cast<size_t>(shb.entries_size()), half_size / 16);\n  for (const auto& entry : shb.entries()) {\n    EXPECT_EQ(entry.addr().pc(), --itr);\n    EXPECT_EQ(entry.addr().pc(), entry.fp());\n    EXPECT_EQ(entry.addr().pc() & 15, entry.tag());\n  }\n}\n\nINSTANTIATE_TEST_SUITE_P(MteStackHistoryTestInstance, MteStackHistoryTest, testing::Range(0, 8));\n\n#endif\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/open_files_list_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <string>\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n\n#include \"libdebuggerd/open_files_list.h\"\n\n// Check that we can produce a list of open files for the current process, and\n// that it includes a known open file.\nTEST(OpenFilesListTest, BasicTest) {\n  // Open a temporary file that we can check for in the list of open files.\n  TemporaryFile tf;\n\n  // Get the list of open files for this process.\n  OpenFilesList list;\n  populate_open_files_list(&list, getpid());\n\n  // Verify our open file is in the list.\n  bool found = false;\n  for (auto& file : list) {\n    if (file.first == tf.fd) {\n      EXPECT_EQ(file.second.path.value_or(\"\"), std::string(tf.path));\n      found = true;\n      break;\n    }\n  }\n  EXPECT_TRUE(found);\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n#include <sys/prctl.h>\n\n#include <string>\n\n#include <android-base/test_utils.h>\n\n#include \"libdebuggerd/tombstone.h\"\n#include \"libdebuggerd/tombstone_proto_to_text.h\"\n#include \"tombstone.pb.h\"\n\nusing CallbackType = std::function<void(const std::string& line, bool should_log)>;\n\nclass TombstoneProtoToTextTest : public ::testing::Test {\n public:\n  void SetUp() {\n    tombstone_.reset(new Tombstone);\n\n    tombstone_->set_arch(Architecture::ARM64);\n    tombstone_->set_build_fingerprint(\"Test fingerprint\");\n    tombstone_->set_timestamp(\"1970-01-01 00:00:00\");\n    tombstone_->set_pid(100);\n    tombstone_->set_tid(100);\n    tombstone_->set_uid(0);\n    tombstone_->set_selinux_label(\"none\");\n\n    Signal signal;\n    signal.set_number(SIGSEGV);\n    signal.set_name(\"SIGSEGV\");\n    signal.set_code(0);\n    signal.set_code_name(\"none\");\n\n    *tombstone_->mutable_signal_info() = signal;\n\n    Thread thread;\n    thread.set_id(100);\n    thread.set_name(\"main\");\n    thread.set_tagged_addr_ctrl(0);\n    thread.set_pac_enabled_keys(0);\n\n    auto& threads = *tombstone_->mutable_threads();\n    threads[100] = thread;\n    main_thread_ = &threads[100];\n  }\n\n  void ProtoToString() {\n    text_ = \"\";\n    EXPECT_TRUE(tombstone_proto_to_text(\n        *tombstone_,\n        [this](const std::string& line, bool should_log) {\n          if (should_log) {\n            text_ += \"LOG \";\n          }\n          text_ += line + '\\n';\n        },\n        [&](const BacktraceFrame& frame) {\n          text_ += \"SYMBOLIZE \" + frame.build_id() + \" \" + std::to_string(frame.pc()) + \"\\n\";\n        }));\n  }\n\n  Thread* main_thread_;\n  std::string text_;\n  std::unique_ptr<Tombstone> tombstone_;\n};\n\nTEST_F(TombstoneProtoToTextTest, tagged_addr_ctrl) {\n  main_thread_->set_tagged_addr_ctrl(0);\n  ProtoToString();\n  EXPECT_MATCH(text_, \"LOG tagged_addr_ctrl: 0000000000000000\\\\n\");\n\n  main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE);\n  ProtoToString();\n  EXPECT_MATCH(text_, \"LOG tagged_addr_ctrl: 0000000000000001 \\\\(PR_TAGGED_ADDR_ENABLE\\\\)\\\\n\");\n\n  main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |\n                                     (0xfffe << PR_MTE_TAG_SHIFT));\n  ProtoToString();\n  EXPECT_MATCH(text_,\n               \"LOG tagged_addr_ctrl: 000000000007fff3 \\\\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, \"\n               \"mask 0xfffe\\\\)\\\\n\");\n\n  main_thread_->set_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC |\n                                     PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT));\n  ProtoToString();\n  EXPECT_MATCH(text_,\n               \"LOG tagged_addr_ctrl: 00000000f007fff7 \\\\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, \"\n               \"PR_MTE_TCF_ASYNC, mask 0xfffe, unknown 0xf0000000\\\\)\\\\n\");\n}\n\nTEST_F(TombstoneProtoToTextTest, pac_enabled_keys) {\n  main_thread_->set_pac_enabled_keys(0);\n  ProtoToString();\n  EXPECT_MATCH(text_, \"LOG pac_enabled_keys: 0000000000000000\\\\n\");\n\n  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY);\n  ProtoToString();\n  EXPECT_MATCH(text_, \"LOG pac_enabled_keys: 0000000000000001 \\\\(PR_PAC_APIAKEY\\\\)\\\\n\");\n\n  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY);\n  ProtoToString();\n  EXPECT_MATCH(text_,\n               \"LOG pac_enabled_keys: 0000000000000009 \\\\(PR_PAC_APIAKEY, PR_PAC_APDBKEY\\\\)\\\\n\");\n\n  main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000);\n  ProtoToString();\n  EXPECT_MATCH(text_,\n               \"LOG pac_enabled_keys: 0000000000001009 \\\\(PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown \"\n               \"0x1000\\\\)\\\\n\");\n}\n\nTEST_F(TombstoneProtoToTextTest, crash_detail_string) {\n  auto* crash_detail = tombstone_->add_crash_details();\n  crash_detail->set_name(\"CRASH_DETAIL_NAME\");\n  crash_detail->set_data(\"crash_detail_value\");\n  ProtoToString();\n  EXPECT_MATCH(text_, \"(CRASH_DETAIL_NAME: 'crash_detail_value')\");\n}\n\nTEST_F(TombstoneProtoToTextTest, crash_detail_bytes) {\n  auto* crash_detail = tombstone_->add_crash_details();\n  crash_detail->set_name(\"CRASH_DETAIL_NAME\");\n  crash_detail->set_data(\"helloworld\\1\\255\\3\");\n  ProtoToString();\n  EXPECT_MATCH(text_, R\"(CRASH_DETAIL_NAME: 'helloworld\\\\1\\\\255\\\\3')\");\n}\n\nTEST_F(TombstoneProtoToTextTest, stack_record) {\n  auto* cause = tombstone_->add_causes();\n  cause->set_human_readable(\"stack tag-mismatch on thread 123\");\n  auto* stack = tombstone_->mutable_stack_history_buffer();\n  stack->set_tid(123);\n  {\n    auto* shb_entry = stack->add_entries();\n    shb_entry->set_fp(0x1);\n    shb_entry->set_tag(0xb);\n    auto* addr = shb_entry->mutable_addr();\n    addr->set_rel_pc(0x567);\n    addr->set_file_name(\"foo.so\");\n    addr->set_build_id(\"ABC123\");\n  }\n  {\n    auto* shb_entry = stack->add_entries();\n    shb_entry->set_fp(0x2);\n    shb_entry->set_tag(0xc);\n    auto* addr = shb_entry->mutable_addr();\n    addr->set_rel_pc(0x678);\n    addr->set_file_name(\"bar.so\");\n  }\n  ProtoToString();\n  EXPECT_MATCH(text_, \"stack tag-mismatch on thread 123\");\n  EXPECT_MATCH(text_, \"stack_record fp:0x1 tag:0xb pc:foo\\\\.so\\\\+0x567 \\\\(BuildId: ABC123\\\\)\");\n  EXPECT_MATCH(text_, \"stack_record fp:0x2 tag:0xc pc:bar\\\\.so\\\\+0x678\");\n}\n\nTEST_F(TombstoneProtoToTextTest, symbolize) {\n  BacktraceFrame* frame = main_thread_->add_current_backtrace();\n  frame->set_pc(12345);\n  frame->set_build_id(\"0123456789abcdef\");\n  ProtoToString();\n  EXPECT_MATCH(text_, \"\\\\(BuildId: 0123456789abcdef\\\\)\\\\nSYMBOLIZE 0123456789abcdef 12345\\\\n\");\n}\n\nTEST_F(TombstoneProtoToTextTest, uid) {\n  ProtoToString();\n  EXPECT_MATCH(text_, \"\\\\nLOG uid: 0\\\\n\");\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/tombstone.cpp",
    "content": "/*\n * Copyright (C) 2012-2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"DEBUG\"\n\n#include \"libdebuggerd/tombstone.h\"\n#include \"libdebuggerd/tombstone_proto_to_text.h\"\n\n#include <errno.h>\n#include <signal.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/prctl.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <memory>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n#include <android/log.h>\n#include <async_safe/log.h>\n#include <log/log.h>\n#include <private/android_filesystem_config.h>\n#include <unwindstack/AndroidUnwinder.h>\n#include <unwindstack/Error.h>\n#include <unwindstack/Regs.h>\n\n#include \"libdebuggerd/backtrace.h\"\n#include \"libdebuggerd/open_files_list.h\"\n#include \"libdebuggerd/utility.h\"\n#include \"util.h\"\n\n#include \"tombstone.pb.h\"\n\nusing android::base::unique_fd;\n\nusing namespace std::literals::string_literals;\n\nvoid engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address,\n                                siginfo_t* siginfo, ucontext_t* ucontext) {\n  pid_t uid = getuid();\n  pid_t pid = getpid();\n  pid_t target_tid = gettid();\n\n  log_t log;\n  log.current_tid = target_tid;\n  log.crashed_tid = target_tid;\n  log.tfd = tombstone_fd;\n  log.amfd_data = nullptr;\n\n  std::string thread_name = get_thread_name(target_tid);\n  std::vector<std::string> command_line = get_command_line(pid);\n\n  std::unique_ptr<unwindstack::Regs> regs(\n      unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext));\n\n  std::string selinux_label;\n  android::base::ReadFileToString(\"/proc/self/attr/current\", &selinux_label);\n\n  std::map<pid_t, ThreadInfo> threads;\n  threads[target_tid] = ThreadInfo {\n    .registers = std::move(regs), .uid = uid, .tid = target_tid,\n    .thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line),\n    .selinux_label = std::move(selinux_label), .siginfo = siginfo, .signo = siginfo->si_signo,\n    // Only supported on aarch64 for now.\n#if defined(__aarch64__)\n    .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),\n    .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),\n#endif\n  };\n  const ThreadInfo& thread = threads[pid];\n  if (!iterate_tids(pid, [&threads, &thread, &target_tid](pid_t tid) {\n        if (target_tid == tid) {\n          return;\n        }\n        threads[tid] = ThreadInfo{\n            .uid = thread.uid,\n            .tid = tid,\n            .pid = thread.pid,\n            .command_line = thread.command_line,\n            .thread_name = get_thread_name(tid),\n            .tagged_addr_ctrl = thread.tagged_addr_ctrl,\n            .pac_enabled_keys = thread.pac_enabled_keys,\n        };\n      })) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"failed to open /proc/%d/task: %s\", pid,\n                          strerror(errno));\n  }\n\n  // Do not use the thread cache here because it will call pthread_key_create\n  // which doesn't work in linker code. See b/189803009.\n  // Use a normal cached object because the thread is stopped, and there\n  // is no chance of data changing between reads.\n  auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid());\n  unwindstack::AndroidLocalUnwinder unwinder(process_memory);\n  unwindstack::ErrorData error;\n  if (!unwinder.Initialize(error)) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"failed to init unwinder object: %s\",\n                          unwindstack::GetErrorCodeString(error.code));\n    return;\n  }\n\n  ProcessInfo process_info;\n  process_info.abort_msg_address = abort_msg_address;\n  engrave_tombstone(unique_fd(dup(tombstone_fd)), unique_fd(dup(proto_fd)), &unwinder, threads,\n                    target_tid, process_info, nullptr, nullptr);\n}\n\nvoid engrave_tombstone(unique_fd output_fd, unique_fd proto_fd,\n                       unwindstack::AndroidUnwinder* unwinder,\n                       const std::map<pid_t, ThreadInfo>& threads, pid_t target_thread,\n                       const ProcessInfo& process_info, OpenFilesList* open_files,\n                       std::string* amfd_data, const Architecture* guest_arch,\n                       unwindstack::AndroidUnwinder* guest_unwinder) {\n  // Don't copy log messages to tombstone unless this is a development device.\n  Tombstone tombstone;\n  engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files,\n                          guest_arch, guest_unwinder);\n\n  if (proto_fd != -1) {\n    if (!tombstone.SerializeToFileDescriptor(proto_fd.get())) {\n      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"failed to write proto tombstone: %s\",\n                            strerror(errno));\n    }\n  }\n\n  log_t log;\n  log.current_tid = target_thread;\n  log.crashed_tid = target_thread;\n  log.tfd = output_fd.get();\n  log.amfd_data = amfd_data;\n\n  tombstone_proto_to_text(\n      tombstone,\n      [&log](const std::string& line, bool should_log) {\n        _LOG(&log, should_log ? logtype::HEADER : logtype::LOGS, \"%s\\n\", line.c_str());\n      },\n      [](const BacktraceFrame&) {});\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/tombstone_proto.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"DEBUG\"\n\n#include \"libdebuggerd/tombstone.h\"\n#include \"libdebuggerd/gwp_asan.h\"\n#if defined(USE_SCUDO)\n#include \"libdebuggerd/scudo.h\"\n#endif\n\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <signal.h>\n#include <stddef.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/sysinfo.h>\n#include <time.h>\n\n#include <map>\n#include <memory>\n#include <optional>\n#include <set>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <async_safe/log.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include <android/log.h>\n#include <android/set_abort_message.h>\n#include <bionic/crash_detail_internal.h>\n#include <bionic/macros.h>\n#include <bionic/mte.h>\n#include <bionic/reserved_signals.h>\n#include <bionic/tls_defines.h>\n#include <log/log.h>\n#include <log/log_read.h>\n#include <log/logprint.h>\n#include <private/android_filesystem_config.h>\n\n#include <procinfo/process.h>\n#include <unwindstack/AndroidUnwinder.h>\n#include <unwindstack/Error.h>\n#include <unwindstack/MapInfo.h>\n#include <unwindstack/Maps.h>\n#include <unwindstack/Regs.h>\n\n#include \"libdebuggerd/open_files_list.h\"\n#include \"libdebuggerd/utility.h\"\n#include \"libdebuggerd/utility_host.h\"\n#include \"util.h\"\n\n#include \"tombstone.pb.h\"\n\nusing android::base::StringPrintf;\n\n// The maximum number of messages to save in the protobuf per file.\nstatic constexpr size_t kMaxLogMessages = 500;\n\n// Use the demangler from libc++.\nextern \"C\" char* __cxa_demangle(const char*, char*, size_t*, int* status);\n\nstatic Architecture get_arch() {\n#if defined(__arm__)\n  return Architecture::ARM32;\n#elif defined(__aarch64__)\n  return Architecture::ARM64;\n#elif defined(__i386__)\n  return Architecture::X86;\n#elif defined(__x86_64__)\n  return Architecture::X86_64;\n#elif defined(__riscv) && (__riscv_xlen == 64)\n  return Architecture::RISCV64;\n#else\n#error Unknown architecture!\n#endif\n}\n\nstatic std::optional<std::string> get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp,\n                                                           unwindstack::Maps* maps) {\n  // Under stack MTE the stack pointer and/or the fault address can be tagged.\n  // In order to calculate deltas between them, strip off the tags off both\n  // addresses.\n  fault_addr = untag_address(fault_addr);\n  sp = untag_address(sp);\n  static constexpr uint64_t kMaxDifferenceBytes = 256;\n  uint64_t difference;\n  if (sp >= fault_addr) {\n    difference = sp - fault_addr;\n  } else {\n    difference = fault_addr - sp;\n  }\n  if (difference <= kMaxDifferenceBytes) {\n    // The faulting address is close to the current sp, check if the sp\n    // indicates a stack overflow.\n    // On arm, the sp does not get updated when the instruction faults.\n    // In this case, the sp will still be in a valid map, which is the\n    // last case below.\n    // On aarch64, the sp does get updated when the instruction faults.\n    // In this case, the sp will be in either an invalid map if triggered\n    // on the main thread, or in a guard map if in another thread, which\n    // will be the first case or second case from below.\n    std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(sp);\n    if (map_info == nullptr) {\n      return \"stack pointer is in a non-existent map; likely due to stack overflow.\";\n    } else if ((map_info->flags() & (PROT_READ | PROT_WRITE)) != (PROT_READ | PROT_WRITE)) {\n      return \"stack pointer is not in a rw map; likely due to stack overflow.\";\n    } else if ((sp - map_info->start()) <= kMaxDifferenceBytes) {\n      return \"stack pointer is close to top of stack; likely stack overflow.\";\n    }\n  }\n  return {};\n}\n\nvoid set_human_readable_cause(Cause* cause, uint64_t fault_addr) {\n  if (!cause->has_memory_error() || !cause->memory_error().has_heap()) {\n    return;\n  }\n\n  const MemoryError& memory_error = cause->memory_error();\n  const HeapObject& heap_object = memory_error.heap();\n\n  const char *tool_str;\n  switch (memory_error.tool()) {\n    case MemoryError_Tool_GWP_ASAN:\n      tool_str = \"GWP-ASan\";\n      break;\n    case MemoryError_Tool_SCUDO:\n      tool_str = \"MTE\";\n      break;\n    default:\n      tool_str = \"Unknown\";\n      break;\n  }\n\n  const char *error_type_str;\n  switch (memory_error.type()) {\n    case MemoryError_Type_USE_AFTER_FREE:\n      error_type_str = \"Use After Free\";\n      break;\n    case MemoryError_Type_DOUBLE_FREE:\n      error_type_str = \"Double Free\";\n      break;\n    case MemoryError_Type_INVALID_FREE:\n      error_type_str = \"Invalid (Wild) Free\";\n      break;\n    case MemoryError_Type_BUFFER_OVERFLOW:\n      error_type_str = \"Buffer Overflow\";\n      break;\n    case MemoryError_Type_BUFFER_UNDERFLOW:\n      error_type_str = \"Buffer Underflow\";\n      break;\n    default:\n      cause->set_human_readable(\n          StringPrintf(\"[%s]: Unknown error occurred at 0x%\" PRIx64 \".\", tool_str, fault_addr));\n      return;\n  }\n\n  uint64_t diff;\n  const char* location_str;\n\n  if (fault_addr < heap_object.address()) {\n    // Buffer Underflow, 6 bytes left of a 41-byte allocation at 0xdeadbeef.\n    location_str = \"left of\";\n    diff = heap_object.address() - fault_addr;\n  } else if (fault_addr - heap_object.address() < heap_object.size()) {\n    // Use After Free, 40 bytes into a 41-byte allocation at 0xdeadbeef.\n    location_str = \"into\";\n    diff = fault_addr - heap_object.address();\n  } else {\n    // Buffer Overflow, 6 bytes right of a 41-byte allocation at 0xdeadbeef.\n    location_str = \"right of\";\n    diff = fault_addr - heap_object.address() - heap_object.size();\n  }\n\n  // Suffix of 'bytes', i.e. 4 bytes' vs. '1 byte'.\n  const char* byte_suffix = \"s\";\n  if (diff == 1) {\n    byte_suffix = \"\";\n  }\n\n  cause->set_human_readable(StringPrintf(\n      \"[%s]: %s, %\" PRIu64 \" byte%s %s a %\" PRIu64 \"-byte allocation at 0x%\" PRIx64, tool_str,\n      error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address()));\n}\n\n#if defined(__aarch64__)\nvoid dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls,\n                        StackHistoryBuffer& shb_obj, bool nounwind) {\n  auto process_memory = unwinder->GetProcessMemory();\n  target_tls += sizeof(void*) * TLS_SLOT_STACK_MTE;\n  uintptr_t stack_mte;\n  if (!process_memory->ReadFully(target_tls, &stack_mte, sizeof(stack_mte))) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,\n                          \"dump_stack_history: failed to read TLS_SLOT_STACK_MTE: %m\");\n    return;\n  }\n  if (stack_mte == 0) {\n    async_safe_format_log(ANDROID_LOG_DEBUG, LOG_TAG,\n                          \"dump_stack_history: stack history buffer is null\");\n    return;\n  }\n  uintptr_t untagged_stack_mte = untag_address(stack_mte);\n  uintptr_t buf_size = stack_mte_ringbuffer_size_from_pointer(stack_mte);\n  if (buf_size == 0) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"dump_stack_history: empty size\");\n    return;\n  }\n  uintptr_t buf_start = untagged_stack_mte & ~(buf_size - 1ULL);\n  std::vector<char> buf(buf_size);\n  if (!process_memory->ReadFully(buf_start, buf.data(), buf.size())) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,\n                          \"dump_stack_history: failed to read stack history: %m\");\n    return;\n  }\n  uintptr_t original_off = untagged_stack_mte - buf_start;\n  if (original_off % 16 || original_off > buf_size) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,\n                          \"dump_stack_history: invalid offset: %\" PRIuPTR, original_off);\n    return;\n  }\n\n  // The original_off is the next slot that would have been written, so the last\n  // slot that was written is the previous one.\n  for (uintptr_t idx = 16; idx <= buf_size; idx += 16) {\n    int64_t off = original_off - idx;\n    if (off < 0) off += buf_size;\n    uintptr_t pc, taggedfp;\n    memcpy(&pc, &(buf[off]), sizeof(pc));\n    memcpy(&taggedfp, &(buf[off + sizeof(pc)]), sizeof(taggedfp));\n\n    if (pc == 0) break;\n    uintptr_t fp = untag_address(taggedfp);\n    uintptr_t tag = taggedfp >> 56;\n\n    unwindstack::FrameData frame_data;\n\n    if (nounwind) {\n      frame_data.pc = pc;\n    } else {\n      // +4 is to counteract the \"pc adjustment\" in BuildFrameFromPcOnly.\n      // BuildFrameFromPcOnly assumes we are unwinding, so it needs to correct for that\n      // the PC is the return address. That is not the case here.\n      // It doesn't really matter, because either should be in the correct function, but\n      // this is more correct (and consistent with the nounwind case).\n      frame_data = unwinder->BuildFrameFromPcOnly(pc);\n      frame_data.pc += 4;\n      frame_data.rel_pc += 4;\n    }\n\n    StackHistoryBufferEntry* entry = shb_obj.add_entries();\n    fill_in_backtrace_frame(entry->mutable_addr(), frame_data);\n    entry->set_fp(fp);\n    entry->set_tag(tag);\n  }\n}\n\nstatic pid_t get_containing_thread(unwindstack::MapInfo* map_info, pid_t main_tid) {\n  if (map_info == nullptr) return 0;\n\n  std::string name = map_info->name();\n  if (name == \"[stack]\") {\n    return main_tid;\n  }\n  int tid;\n  if (sscanf(name.c_str(), \"[anon:stack_and_tls:%d\", &tid) != 1) {\n    return 0;\n  }\n  return tid;\n}\n\nstatic std::optional<std::string> maybe_stack_mte_cause(\n    Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, const ThreadInfo& target_thread,\n    [[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads, uint64_t fault_addr) {\n  unwindstack::Maps* maps = unwinder->GetMaps();\n  auto map_info = maps->Find(untag_address(fault_addr));\n  pid_t tid = get_containing_thread(map_info.get(), target_thread.tid);\n  if (!tid) {\n    return std::nullopt;\n  }\n  auto it = threads.find(tid);\n  if (it != threads.end()) {\n    StackHistoryBuffer* shb = tombstone->mutable_stack_history_buffer();\n    shb->set_tid(tid);\n    dump_stack_history(unwinder, it->second.tls, *shb);\n  } else {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,\n                          \"dump_probable_cause: unknown target thread %d\", tid);\n  }\n  return StringPrintf(\"stack tag-mismatch on thread %u\", tid);\n}\n\n#endif\n\nstatic void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,\n                                const ProcessInfo& process_info, const ThreadInfo& target_thread,\n                                [[maybe_unused]] const std::map<pid_t, ThreadInfo>& threads) {\n#if defined(USE_SCUDO)\n  ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info);\n  if (scudo_crash_data.CrashIsMine()) {\n    scudo_crash_data.AddCauseProtos(tombstone, unwinder);\n    return;\n  }\n#endif\n\n  GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info,\n                                       target_thread);\n  if (gwp_asan_crash_data.CrashIsMine()) {\n    gwp_asan_crash_data.AddCauseProtos(tombstone, unwinder);\n    return;\n  }\n\n  const siginfo *si = target_thread.siginfo;\n  auto fault_addr = reinterpret_cast<uint64_t>(si->si_addr);\n  unwindstack::Maps* maps = unwinder->GetMaps();\n\n  std::optional<std::string> cause;\n  if (si->si_signo == SIGSEGV && si->si_code == SEGV_MAPERR) {\n    if (fault_addr < 4096) {\n      cause = \"null pointer dereference\";\n    } else if (fault_addr == 0xffff0ffc) {\n      cause = \"call to kuser_helper_version\";\n    } else if (fault_addr == 0xffff0fe0) {\n      cause = \"call to kuser_get_tls\";\n    } else if (fault_addr == 0xffff0fc0) {\n      cause = \"call to kuser_cmpxchg\";\n    } else if (fault_addr == 0xffff0fa0) {\n      cause = \"call to kuser_memory_barrier\";\n    } else if (fault_addr == 0xffff0f60) {\n      cause = \"call to kuser_cmpxchg64\";\n    } else {\n      cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps);\n    }\n  } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) {\n    auto map_info = maps->Find(fault_addr);\n    if (map_info != nullptr && map_info->flags() == PROT_EXEC) {\n      cause = \"execute-only (no-read) memory access error; likely due to data in .text.\";\n    } else if (fault_addr == target_thread.registers->pc() &&\n               map_info != nullptr && (map_info->flags() & PROT_EXEC) == 0) {\n      cause = \"trying to execute non-executable memory.\";\n    } else {\n      cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps);\n    }\n  }\n#if defined(__aarch64__) && defined(SEGV_MTESERR)\n  else if (si->si_signo == SIGSEGV && si->si_code == SEGV_MTESERR) {\n    // If this was a heap MTE crash, it would have been handled by scudo. Checking whether it\n    // is a stack one.\n    cause = maybe_stack_mte_cause(tombstone, unwinder, target_thread, threads, fault_addr);\n  }\n#endif\n  else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) {\n    cause = StringPrintf(\"seccomp prevented call to disallowed %s system call %d\", ABI_STRING,\n                         si->si_syscall);\n  }\n\n  if (cause) {\n    Cause *cause_proto = tombstone->add_causes();\n    cause_proto->set_human_readable(*cause);\n  }\n}\n\nstatic void dump_crash_details(Tombstone* tombstone,\n                               std::shared_ptr<unwindstack::Memory>& process_memory,\n                               const ProcessInfo& process_info) {\n  uintptr_t address = process_info.crash_detail_page;\n  while (address) {\n    struct crash_detail_page_t page;\n    if (!process_memory->ReadFully(address, &page, sizeof(page))) {\n      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"failed to read crash detail page: %m\");\n      break;\n    }\n    if (page.used > kNumCrashDetails) {\n      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"crash detail: page corrupted\");\n      break;\n    }\n    for (size_t i = 0; i < page.used; ++i) {\n      const crash_detail_t& crash_detail = page.crash_details[i];\n      if (!crash_detail.data) {\n        continue;\n      }\n      std::string name(crash_detail.name_size, '\\0');\n      if (!process_memory->ReadFully(reinterpret_cast<uintptr_t>(crash_detail.name), name.data(),\n                                     crash_detail.name_size)) {\n        async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"crash detail: failed to read name: %m\");\n        continue;\n      }\n      std::string data(crash_detail.data_size, '\\0');\n      if (!process_memory->ReadFully(reinterpret_cast<uintptr_t>(crash_detail.data), data.data(),\n                                     crash_detail.data_size)) {\n        async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,\n                              \"crash detail: failed to read data for %s: %m\", name.c_str());\n        continue;\n      }\n      auto* proto_detail = tombstone->add_crash_details();\n      proto_detail->set_name(name);\n      proto_detail->set_data(data);\n    }\n    address = reinterpret_cast<uintptr_t>(page.prev);\n  }\n}\n\nstatic void dump_abort_message(Tombstone* tombstone,\n                               std::shared_ptr<unwindstack::Memory>& process_memory,\n                               const ProcessInfo& process_info) {\n  uintptr_t address = process_info.abort_msg_address;\n  if (address == 0) {\n    return;\n  }\n\n  size_t length;\n  if (!process_memory->ReadFully(address, &length, sizeof(length))) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"failed to read abort message header: %s\",\n                          strerror(errno));\n    return;\n  }\n\n  // The length field includes the length of the length field itself.\n  if (length < sizeof(size_t)) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,\n                          \"abort message header malformed: claimed length = %zu\", length);\n    return;\n  }\n\n  length -= sizeof(size_t);\n\n  // The abort message should be null terminated already, but reserve a spot for NUL just in case.\n  std::string msg;\n  msg.resize(length);\n\n  if (!process_memory->ReadFully(address + sizeof(length), &msg[0], length)) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"failed to read abort message header: %s\",\n                          strerror(errno));\n    return;\n  }\n\n  // Remove any trailing newlines.\n  size_t index = msg.size();\n  while (index > 0 && (msg[index - 1] == '\\0' || msg[index - 1] == '\\n')) {\n    --index;\n  }\n  msg.resize(index);\n\n  // Make sure only UTF8 characters are present since abort_message is a string.\n  tombstone->set_abort_message(oct_encode_non_ascii_printable(msg));\n}\n\nstatic void dump_open_fds(Tombstone* tombstone, const OpenFilesList* open_files) {\n  if (open_files) {\n    for (auto& [fd, entry] : *open_files) {\n      FD f;\n\n      f.set_fd(fd);\n\n      const std::optional<std::string>& path = entry.path;\n      if (path) {\n        f.set_path(*path);\n      }\n\n      const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;\n      if (fdsan_owner) {\n        const char* type = android_fdsan_get_tag_type(*fdsan_owner);\n        uint64_t value = android_fdsan_get_tag_value(*fdsan_owner);\n        f.set_owner(type);\n        f.set_tag(value);\n      }\n\n      *tombstone->add_open_fds() = f;\n    }\n  }\n}\n\nvoid fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame) {\n  f->set_rel_pc(frame.rel_pc);\n  f->set_pc(frame.pc);\n  f->set_sp(frame.sp);\n\n  if (!frame.function_name.empty()) {\n    // TODO: Should this happen here, or on the display side?\n    char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr, nullptr);\n    if (demangled_name) {\n      f->set_function_name(demangled_name);\n      free(demangled_name);\n    } else {\n      f->set_function_name(frame.function_name);\n    }\n  }\n\n  f->set_function_offset(frame.function_offset);\n\n  if (frame.map_info == nullptr) {\n    // No valid map associated with this frame.\n    f->set_file_name(\"<unknown>\");\n    return;\n  }\n\n  if (!frame.map_info->name().empty()) {\n    f->set_file_name(frame.map_info->GetFullName());\n  } else {\n    f->set_file_name(StringPrintf(\"<anonymous:%\" PRIx64 \">\", frame.map_info->start()));\n  }\n  f->set_file_map_offset(frame.map_info->elf_start_offset());\n\n  f->set_build_id(frame.map_info->GetPrintableBuildID());\n}\n\nstatic void dump_registers(unwindstack::AndroidUnwinder* unwinder,\n                           const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,\n                           bool memory_dump) {\n  if (regs == nullptr) {\n    return;\n  }\n\n  unwindstack::Maps* maps = unwinder->GetMaps();\n  unwindstack::Memory* memory = unwinder->GetProcessMemory().get();\n\n  regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {\n    Register r;\n    r.set_name(name);\n    r.set_u64(value);\n    *thread.add_registers() = r;\n\n    if (memory_dump) {\n      MemoryDump dump;\n\n      dump.set_register_name(name);\n      std::shared_ptr<unwindstack::MapInfo> map_info = maps->Find(untag_address(value));\n      if (map_info) {\n        dump.set_mapping_name(map_info->name());\n      }\n\n      constexpr size_t kNumBytesAroundRegister = 256;\n      constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;\n      char buf[kNumBytesAroundRegister];\n      uint8_t tags[kNumTagsAroundRegister];\n      ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);\n      if (bytes == -1) {\n        return;\n      }\n      dump.set_begin_address(value);\n      dump.set_memory(buf, bytes);\n\n      bool has_tags = false;\n#if defined(__aarch64__)\n      for (size_t i = 0; i < kNumTagsAroundRegister; ++i) {\n        if (tags[i] != 0) {\n          has_tags = true;\n        }\n      }\n#endif  // defined(__aarch64__)\n\n      if (has_tags) {\n        dump.mutable_arm_mte_metadata()->set_memory_tags(tags, kNumTagsAroundRegister);\n      }\n\n      *thread.add_memory_dump() = std::move(dump);\n    }\n  });\n}\n\nstatic void dump_thread_backtrace(std::vector<unwindstack::FrameData>& frames, Thread& thread) {\n  std::set<std::string> unreadable_elf_files;\n  for (const auto& frame : frames) {\n    BacktraceFrame* f = thread.add_current_backtrace();\n    fill_in_backtrace_frame(f, frame);\n    if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {\n      unreadable_elf_files.emplace(frame.map_info->name());\n    }\n  }\n\n  if (!unreadable_elf_files.empty()) {\n    auto unreadable_elf_files_proto = thread.mutable_unreadable_elf_files();\n    auto backtrace_note = thread.mutable_backtrace_note();\n    *backtrace_note->Add() =\n        \"Function names and BuildId information is missing for some frames due\";\n    *backtrace_note->Add() = \"to unreadable libraries. For unwinds of apps, only shared libraries\";\n    *backtrace_note->Add() = \"found under the lib/ directory are readable.\";\n    *backtrace_note->Add() = \"On this device, run setenforce 0 to make the libraries readable.\";\n    *backtrace_note->Add() = \"Unreadable libraries:\";\n    for (auto& name : unreadable_elf_files) {\n      *backtrace_note->Add() = \"  \" + name;\n      *unreadable_elf_files_proto->Add() = name;\n    }\n  }\n}\n\nstatic void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,\n                        const ThreadInfo& thread_info, bool memory_dump = false,\n                        unwindstack::AndroidUnwinder* guest_unwinder = nullptr) {\n  Thread thread;\n\n  thread.set_id(thread_info.tid);\n  thread.set_name(thread_info.thread_name);\n  thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);\n  thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);\n\n  unwindstack::AndroidUnwinderData data;\n  // Indicate we want a copy of the initial registers.\n  data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();\n  bool unwind_ret;\n  if (thread_info.registers != nullptr) {\n    unwind_ret = unwinder->Unwind(thread_info.registers.get(), data);\n  } else {\n    unwind_ret = unwinder->Unwind(thread_info.tid, data);\n  }\n  if (!unwind_ret) {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"Unwind failed for tid %d: Error %s\",\n                          thread_info.tid, data.GetErrorString().c_str());\n  } else {\n    dump_thread_backtrace(data.frames, thread);\n  }\n  dump_registers(unwinder, *data.saved_initial_regs, thread, memory_dump);\n\n  auto& threads = *tombstone->mutable_threads();\n  threads[thread_info.tid] = thread;\n\n  if (guest_unwinder) {\n    if (!thread_info.guest_registers) {\n      async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,\n                            \"No guest state registers information for tid %d\", thread_info.tid);\n      return;\n    }\n    Thread guest_thread;\n    unwindstack::AndroidUnwinderData guest_data;\n    guest_data.saved_initial_regs = std::make_optional<std::unique_ptr<unwindstack::Regs>>();\n    if (guest_unwinder->Unwind(thread_info.guest_registers.get(), guest_data)) {\n      dump_thread_backtrace(guest_data.frames, guest_thread);\n    } else {\n      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,\n                            \"Unwind guest state registers failed for tid %d: Error %s\",\n                            thread_info.tid, guest_data.GetErrorString().c_str());\n    }\n    dump_registers(guest_unwinder, *guest_data.saved_initial_regs, guest_thread, memory_dump);\n    auto& guest_threads = *tombstone->mutable_guest_threads();\n    guest_threads[thread_info.tid] = guest_thread;\n  }\n}\n\nstatic void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps,\n                          std::shared_ptr<unwindstack::Memory>& process_memory) {\n  for (const auto& map_info : *maps) {\n    auto* map = tombstone->add_memory_mappings();\n    map->set_begin_address(map_info->start());\n    map->set_end_address(map_info->end());\n    map->set_offset(map_info->offset());\n\n    if (map_info->flags() & PROT_READ) {\n      map->set_read(true);\n    }\n    if (map_info->flags() & PROT_WRITE) {\n      map->set_write(true);\n    }\n    if (map_info->flags() & PROT_EXEC) {\n      map->set_execute(true);\n    }\n\n    map->set_mapping_name(map_info->name());\n\n    std::string build_id = map_info->GetPrintableBuildID();\n    if (!build_id.empty()) {\n      map->set_build_id(build_id);\n    }\n\n    map->set_load_bias(map_info->GetLoadBias(process_memory));\n  }\n}\n\n// This creates a fake log message that indicates an error occurred when\n// reading the log.\nstatic void add_error_log_msg(Tombstone* tombstone, const std::string&& error_msg) {\n  LogBuffer buffer;\n  buffer.set_name(\"ERROR\");\n\n  LogMessage* log_msg = buffer.add_logs();\n  log_msg->set_timestamp(\"00-00 00:00:00.000\");\n  log_msg->set_pid(0);\n  log_msg->set_tid(0);\n  log_msg->set_priority(ANDROID_LOG_ERROR);\n  log_msg->set_tag(\"\");\n  log_msg->set_message(error_msg);\n\n  *tombstone->add_log_buffers() = std::move(buffer);\n\n  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"%s\", error_msg.c_str());\n}\n\nstatic void dump_log_file(Tombstone* tombstone, const char* logger, pid_t pid) {\n  logger_list* logger_list = android_logger_list_open(android_name_to_log_id(logger),\n                                                      ANDROID_LOG_NONBLOCK, kMaxLogMessages, pid);\n  if (logger_list == nullptr) {\n    add_error_log_msg(tombstone, android::base::StringPrintf(\"Cannot open log file %s\", logger));\n    return;\n  }\n\n  LogBuffer buffer;\n  while (true) {\n    log_msg log_entry;\n    ssize_t actual = android_logger_list_read(logger_list, &log_entry);\n    if (actual < 0) {\n      if (actual == -EINTR) {\n        // interrupted by signal, retry\n        continue;\n      }\n      // Don't consider EAGAIN an error since this is a non-blocking call.\n      if (actual != -EAGAIN) {\n        add_error_log_msg(tombstone, android::base::StringPrintf(\"reading log %s failed (%s)\",\n                                                                 logger, strerror(-actual)));\n      }\n      break;\n    } else if (actual == 0) {\n      break;\n    }\n\n    char timestamp_secs[32];\n    time_t sec = static_cast<time_t>(log_entry.entry.sec);\n    tm tm;\n    localtime_r(&sec, &tm);\n    strftime(timestamp_secs, sizeof(timestamp_secs), \"%m-%d %H:%M:%S\", &tm);\n    std::string timestamp =\n        StringPrintf(\"%s.%03d\", timestamp_secs, log_entry.entry.nsec / 1'000'000);\n\n    // Msg format is: <priority:1><tag:N>\\0<message:N>\\0\n    char* msg = log_entry.msg();\n    if (msg == nullptr) {\n      continue;\n    }\n\n    unsigned char prio = msg[0];\n    char* tag = msg + 1;\n    msg = tag + strlen(tag) + 1;\n\n    // consume any trailing newlines\n    char* nl = msg + strlen(msg) - 1;\n    while (nl >= msg && *nl == '\\n') {\n      *nl-- = '\\0';\n    }\n\n    // Look for line breaks ('\\n') and display each text line\n    // on a separate line, prefixed with the header, like logcat does.\n    do {\n      nl = strchr(msg, '\\n');\n      if (nl != nullptr) {\n        *nl = '\\0';\n        ++nl;\n      }\n\n      LogMessage* log_msg = buffer.add_logs();\n      log_msg->set_timestamp(timestamp);\n      log_msg->set_pid(log_entry.entry.pid);\n      log_msg->set_tid(log_entry.entry.tid);\n      log_msg->set_priority(prio);\n      log_msg->set_tag(tag);\n      // Make sure only UTF8 characters are present since message is a string.\n      log_msg->set_message(oct_encode_non_ascii_printable(msg));\n    } while ((msg = nl));\n  }\n  android_logger_list_free(logger_list);\n\n  if (!buffer.logs().empty()) {\n    buffer.set_name(logger);\n    *tombstone->add_log_buffers() = std::move(buffer);\n  }\n}\n\nstatic void dump_logcat(Tombstone* tombstone, pid_t pid) {\n  dump_log_file(tombstone, \"system\", pid);\n  dump_log_file(tombstone, \"main\", pid);\n}\n\nstatic void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombstone,\n                                        std::shared_ptr<unwindstack::Memory>& process_memory,\n                                        uintptr_t fault_addr) {\n  if (tombstone.arch() != Architecture::ARM64) return;\n\n  fault_addr = untag_address(fault_addr);\n  constexpr size_t kNumGranules = kNumTagRows * kNumTagColumns;\n  constexpr size_t kBytesToRead = kNumGranules * kTagGranuleSize;\n\n  // If the low part of the tag dump would underflow to the high address space, it's probably not\n  // a valid address for us to dump tags from.\n  if (fault_addr < kBytesToRead / 2) return;\n\n  constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);\n  size_t start_address = (fault_addr & kRowStartMask) - kBytesToRead / 2;\n  MemoryDump tag_dump;\n  size_t granules_to_read = kNumGranules;\n\n  // Attempt to read the first tag. If reading fails, this likely indicates the\n  // lowest touched page is inaccessible or not marked with PROT_MTE.\n  // Fast-forward over pages until one has tags, or we exhaust the search range.\n  while (process_memory->ReadTag(start_address) < 0) {\n    size_t page_size = sysconf(_SC_PAGE_SIZE);\n    size_t bytes_to_next_page = page_size - (start_address % page_size);\n    if (bytes_to_next_page >= granules_to_read * kTagGranuleSize) return;\n    start_address += bytes_to_next_page;\n    granules_to_read -= bytes_to_next_page / kTagGranuleSize;\n  }\n  tag_dump.set_begin_address(start_address);\n\n  std::string* mte_tags = tag_dump.mutable_arm_mte_metadata()->mutable_memory_tags();\n\n  for (size_t i = 0; i < granules_to_read; ++i) {\n    long tag = process_memory->ReadTag(start_address + i * kTagGranuleSize);\n    if (tag < 0) break;\n    mte_tags->push_back(static_cast<uint8_t>(tag));\n  }\n\n  if (!mte_tags->empty()) {\n    *signal->mutable_fault_adjacent_metadata() = tag_dump;\n  }\n}\n\nvoid engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder,\n                             const std::map<pid_t, ThreadInfo>& threads, pid_t target_tid,\n                             const ProcessInfo& process_info, const OpenFilesList* open_files,\n                             const Architecture* guest_arch,\n                             unwindstack::AndroidUnwinder* guest_unwinder) {\n  Tombstone result;\n\n  result.set_arch(get_arch());\n  if (guest_arch != nullptr) {\n    result.set_guest_arch(*guest_arch);\n  } else {\n    result.set_guest_arch(Architecture::NONE);\n  }\n  result.set_build_fingerprint(android::base::GetProperty(\"ro.build.fingerprint\", \"unknown\"));\n  result.set_revision(android::base::GetProperty(\"ro.revision\", \"unknown\"));\n  result.set_timestamp(get_timestamp());\n\n  const ThreadInfo& target_thread = threads.at(target_tid);\n  result.set_pid(target_thread.pid);\n  result.set_tid(target_thread.tid);\n  result.set_uid(target_thread.uid);\n  result.set_selinux_label(target_thread.selinux_label);\n  // The main thread must have a valid siginfo.\n  CHECK(target_thread.siginfo != nullptr);\n\n  struct sysinfo si;\n  sysinfo(&si);\n  android::procinfo::ProcessInfo proc_info;\n  std::string error;\n  if (android::procinfo::GetProcessInfo(target_thread.pid, &proc_info, &error)) {\n    uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK);\n    result.set_process_uptime(si.uptime - starttime);\n  } else {\n    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, \"failed to read process info: %s\",\n                          error.c_str());\n  }\n\n  result.set_page_size(getpagesize());\n  result.set_has_been_16kb_mode(android::base::GetBoolProperty(\"ro.misctrl.16kb_before\", false));\n\n  auto cmd_line = result.mutable_command_line();\n  for (const auto& arg : target_thread.command_line) {\n    *cmd_line->Add() = arg;\n  }\n\n  if (!target_thread.siginfo) {\n    async_safe_fatal(\"siginfo missing\");\n  }\n\n  Signal sig;\n  sig.set_number(target_thread.signo);\n  sig.set_name(get_signame(target_thread.siginfo));\n  sig.set_code(target_thread.siginfo->si_code);\n  sig.set_code_name(get_sigcode(target_thread.siginfo));\n\n  if (signal_has_sender(target_thread.siginfo, target_thread.pid)) {\n    sig.set_has_sender(true);\n    sig.set_sender_uid(target_thread.siginfo->si_uid);\n    sig.set_sender_pid(target_thread.siginfo->si_pid);\n  }\n\n  if (process_info.has_fault_address) {\n    sig.set_has_fault_address(true);\n    uintptr_t fault_addr = process_info.maybe_tagged_fault_address;\n    sig.set_fault_address(fault_addr);\n    dump_tags_around_fault_addr(&sig, result, unwinder->GetProcessMemory(), fault_addr);\n  }\n\n  *result.mutable_signal_info() = sig;\n\n  dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);\n  dump_crash_details(&result, unwinder->GetProcessMemory(), process_info);\n  // Dump the target thread, but save the memory around the registers.\n  dump_thread(&result, unwinder, target_thread, /* memory_dump */ true, guest_unwinder);\n\n  for (const auto& [tid, thread_info] : threads) {\n    if (tid != target_tid) {\n      dump_thread(&result, unwinder, thread_info, /* memory_dump */ false, guest_unwinder);\n    }\n  }\n\n  dump_probable_cause(&result, unwinder, process_info, target_thread, threads);\n\n  dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory());\n\n  // Only dump logs on debuggable devices.\n  if (android::base::GetBoolProperty(\"ro.debuggable\", false)) {\n    // Get the thread that corresponds to the main pid of the process.\n    const ThreadInfo& thread = threads.at(target_thread.pid);\n\n    // Do not attempt to dump logs of the logd process because the gathering\n    // of logs can hang until a timeout occurs.\n    if (thread.thread_name != \"logd\") {\n      dump_logcat(&result, target_thread.pid);\n    }\n  }\n\n  dump_open_fds(&result, open_files);\n\n  *tombstone = std::move(result);\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/tombstone_proto_to_text.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <libdebuggerd/tombstone_proto_to_text.h>\n#include <libdebuggerd/utility_host.h>\n\n#include <ctype.h>\n#include <inttypes.h>\n\n#include <algorithm>\n#include <functional>\n#include <optional>\n#include <set>\n#include <string>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"libdebuggerd/utility_host.h\"\n#include \"tombstone.pb.h\"\n\nusing android::base::StringAppendF;\nusing android::base::StringPrintf;\n\n#define CB(log, ...) callback(StringPrintf(__VA_ARGS__), log)\n#define CBL(...) CB(true, __VA_ARGS__)\n#define CBS(...) CB(false, __VA_ARGS__)\nusing CallbackType = std::function<void(const std::string& line, bool should_log)>;\nusing SymbolizeCallbackType = std::function<void(const BacktraceFrame& frame)>;\n\n#define DESCRIBE_FLAG(flag) \\\n  if (value & flag) {       \\\n    desc += \", \";           \\\n    desc += #flag;          \\\n    value &= ~flag;         \\\n  }\n\nstatic std::string describe_end(long value, std::string& desc) {\n  if (value) {\n    desc += StringPrintf(\", unknown 0x%lx\", value);\n  }\n  return desc.empty() ? \"\" : \" (\" + desc.substr(2) + \")\";\n}\n\nstatic const char* abi_string(const Architecture& arch) {\n  switch (arch) {\n    case Architecture::ARM32:\n      return \"arm\";\n    case Architecture::ARM64:\n      return \"arm64\";\n    case Architecture::RISCV64:\n      return \"riscv64\";\n    case Architecture::X86:\n      return \"x86\";\n    case Architecture::X86_64:\n      return \"x86_64\";\n    default:\n      return \"<unknown>\";\n  }\n}\n\nstatic int pointer_width(const Tombstone& tombstone) {\n  switch (tombstone.arch()) {\n    case Architecture::ARM32:\n      return 4;\n    case Architecture::ARM64:\n      return 8;\n    case Architecture::RISCV64:\n      return 8;\n    case Architecture::X86:\n      return 4;\n    case Architecture::X86_64:\n      return 8;\n    default:\n      return 8;\n  }\n}\n\nstatic uint64_t untag_address(Architecture arch, uint64_t addr) {\n  if (arch == Architecture::ARM64) {\n    return addr & ((1ULL << 56) - 1);\n  }\n  return addr;\n}\n\nstatic void print_thread_header(CallbackType callback, const Tombstone& tombstone,\n                                const Thread& thread, bool should_log) {\n  const char* process_name = \"<unknown>\";\n  if (!tombstone.command_line().empty()) {\n    process_name = tombstone.command_line()[0].c_str();\n    CB(should_log, \"Cmdline: %s\", android::base::Join(tombstone.command_line(), \" \").c_str());\n  } else {\n    CB(should_log, \"Cmdline: <unknown>\");\n  }\n  CB(should_log, \"pid: %d, tid: %d, name: %s  >>> %s <<<\", tombstone.pid(), thread.id(),\n     thread.name().c_str(), process_name);\n  CB(should_log, \"uid: %d\", tombstone.uid());\n  if (thread.tagged_addr_ctrl() != -1) {\n    CB(should_log, \"tagged_addr_ctrl: %016\" PRIx64 \"%s\", thread.tagged_addr_ctrl(),\n       describe_tagged_addr_ctrl(thread.tagged_addr_ctrl()).c_str());\n  }\n  if (thread.pac_enabled_keys() != -1) {\n    CB(should_log, \"pac_enabled_keys: %016\" PRIx64 \"%s\", thread.pac_enabled_keys(),\n       describe_pac_enabled_keys(thread.pac_enabled_keys()).c_str());\n  }\n}\n\nstatic void print_register_row(CallbackType callback, int word_size,\n                               std::vector<std::pair<std::string, uint64_t>> row, bool should_log) {\n  std::string output = \"  \";\n  for (const auto& [name, value] : row) {\n    output += android::base::StringPrintf(\"  %-3s %0*\" PRIx64, name.c_str(), 2 * word_size,\n                                          static_cast<uint64_t>(value));\n  }\n  callback(output, should_log);\n}\n\nstatic void print_thread_registers(CallbackType callback, const Tombstone& tombstone,\n                                   const Thread& thread, bool should_log) {\n  static constexpr size_t column_count = 4;\n  std::vector<std::pair<std::string, uint64_t>> current_row;\n  std::vector<std::pair<std::string, uint64_t>> special_row;\n  std::unordered_set<std::string> special_registers;\n\n  int word_size = pointer_width(tombstone);\n\n  switch (tombstone.arch()) {\n    case Architecture::ARM32:\n      special_registers = {\"ip\", \"lr\", \"sp\", \"pc\", \"pst\"};\n      break;\n\n    case Architecture::ARM64:\n      special_registers = {\"ip\", \"lr\", \"sp\", \"pc\", \"pst\"};\n      break;\n\n    case Architecture::RISCV64:\n      special_registers = {\"ra\", \"sp\", \"pc\"};\n      break;\n\n    case Architecture::X86:\n      special_registers = {\"ebp\", \"esp\", \"eip\"};\n      break;\n\n    case Architecture::X86_64:\n      special_registers = {\"rbp\", \"rsp\", \"rip\"};\n      break;\n\n    default:\n      CBL(\"Unknown architecture %d printing thread registers\", tombstone.arch());\n      return;\n  }\n\n  for (const auto& reg : thread.registers()) {\n    auto row = &current_row;\n    if (special_registers.count(reg.name()) == 1) {\n      row = &special_row;\n    }\n\n    row->emplace_back(reg.name(), reg.u64());\n    if (current_row.size() == column_count) {\n      print_register_row(callback, word_size, current_row, should_log);\n      current_row.clear();\n    }\n  }\n\n  if (!current_row.empty()) {\n    print_register_row(callback, word_size, current_row, should_log);\n  }\n\n  print_register_row(callback, word_size, special_row, should_log);\n}\n\nstatic void print_backtrace(CallbackType callback, SymbolizeCallbackType symbolize,\n                            const Tombstone& tombstone,\n                            const google::protobuf::RepeatedPtrField<BacktraceFrame>& backtrace,\n                            bool should_log) {\n  int index = 0;\n  for (const auto& frame : backtrace) {\n    std::string function;\n\n    if (!frame.function_name().empty()) {\n      function =\n          StringPrintf(\" (%s+%\" PRId64 \")\", frame.function_name().c_str(), frame.function_offset());\n    }\n\n    std::string build_id;\n    if (!frame.build_id().empty()) {\n      build_id = StringPrintf(\" (BuildId: %s)\", frame.build_id().c_str());\n    }\n\n    std::string line =\n        StringPrintf(\"      #%02d pc %0*\" PRIx64 \"  %s\", index++, pointer_width(tombstone) * 2,\n                     frame.rel_pc(), frame.file_name().c_str());\n    if (frame.file_map_offset() != 0) {\n      line += StringPrintf(\" (offset 0x%\" PRIx64 \")\", frame.file_map_offset());\n    }\n    line += function + build_id;\n    CB(should_log, \"%s\", line.c_str());\n\n    symbolize(frame);\n  }\n}\n\nstatic void print_thread_backtrace(CallbackType callback, SymbolizeCallbackType symbolize,\n                                   const Tombstone& tombstone, const Thread& thread,\n                                   bool should_log) {\n  CBS(\"\");\n  CB(should_log, \"%d total frames\", thread.current_backtrace().size());\n  CB(should_log, \"backtrace:\");\n  if (!thread.backtrace_note().empty()) {\n    CB(should_log, \"  NOTE: %s\",\n       android::base::Join(thread.backtrace_note(), \"\\n  NOTE: \").c_str());\n  }\n  print_backtrace(callback, symbolize, tombstone, thread.current_backtrace(), should_log);\n}\n\nstatic void print_thread_memory_dump(CallbackType callback, const Tombstone& tombstone,\n                                     const Thread& thread) {\n  static constexpr size_t bytes_per_line = 16;\n  static_assert(bytes_per_line == kTagGranuleSize);\n  int word_size = pointer_width(tombstone);\n  for (const auto& mem : thread.memory_dump()) {\n    CBS(\"\");\n    if (mem.mapping_name().empty()) {\n      CBS(\"memory near %s:\", mem.register_name().c_str());\n    } else {\n      CBS(\"memory near %s (%s):\", mem.register_name().c_str(), mem.mapping_name().c_str());\n    }\n    uint64_t addr = mem.begin_address();\n    for (size_t offset = 0; offset < mem.memory().size(); offset += bytes_per_line) {\n      uint64_t tagged_addr = addr;\n      if (mem.has_arm_mte_metadata() &&\n          mem.arm_mte_metadata().memory_tags().size() > offset / kTagGranuleSize) {\n        tagged_addr |=\n            static_cast<uint64_t>(mem.arm_mte_metadata().memory_tags()[offset / kTagGranuleSize])\n            << 56;\n      }\n      std::string line = StringPrintf(\"    %0*\" PRIx64, word_size * 2, tagged_addr + offset);\n\n      size_t bytes = std::min(bytes_per_line, mem.memory().size() - offset);\n      for (size_t i = 0; i < bytes; i += word_size) {\n        uint64_t word = 0;\n\n        // Assumes little-endian, but what doesn't?\n        memcpy(&word, mem.memory().data() + offset + i, word_size);\n\n        StringAppendF(&line, \" %0*\" PRIx64, word_size * 2, word);\n      }\n\n      char ascii[bytes_per_line + 1];\n\n      memset(ascii, '.', sizeof(ascii));\n      ascii[bytes_per_line] = '\\0';\n\n      for (size_t i = 0; i < bytes; ++i) {\n        uint8_t byte = mem.memory()[offset + i];\n        if (byte >= 0x20 && byte < 0x7f) {\n          ascii[i] = byte;\n        }\n      }\n\n      CBS(\"%s  %s\", line.c_str(), ascii);\n    }\n  }\n}\n\nstatic void print_thread(CallbackType callback, SymbolizeCallbackType symbolize,\n                         const Tombstone& tombstone, const Thread& thread) {\n  print_thread_header(callback, tombstone, thread, false);\n  print_thread_registers(callback, tombstone, thread, false);\n  print_thread_backtrace(callback, symbolize, tombstone, thread, false);\n  print_thread_memory_dump(callback, tombstone, thread);\n}\n\nstatic void print_tag_dump(CallbackType callback, const Tombstone& tombstone) {\n  if (!tombstone.has_signal_info()) return;\n\n  const Signal& signal = tombstone.signal_info();\n\n  if (!signal.has_fault_address() || !signal.has_fault_adjacent_metadata()) {\n    return;\n  }\n\n  const MemoryDump& memory_dump = signal.fault_adjacent_metadata();\n\n  if (!memory_dump.has_arm_mte_metadata() || memory_dump.arm_mte_metadata().memory_tags().empty()) {\n    return;\n  }\n\n  const std::string& tags = memory_dump.arm_mte_metadata().memory_tags();\n\n  CBS(\"\");\n  CBS(\"Memory tags around the fault address (0x%\" PRIx64 \"), one tag per %zu bytes:\",\n      signal.fault_address(), kTagGranuleSize);\n  constexpr uintptr_t kRowStartMask = ~(kNumTagColumns * kTagGranuleSize - 1);\n\n  size_t tag_index = 0;\n  size_t num_tags = tags.length();\n  uintptr_t fault_granule =\n      untag_address(tombstone.arch(), signal.fault_address()) & ~(kTagGranuleSize - 1);\n  for (size_t row = 0; tag_index < num_tags; ++row) {\n    uintptr_t row_addr =\n        (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask;\n    std::string row_contents;\n    bool row_has_fault = false;\n\n    for (size_t column = 0; column < kNumTagColumns; ++column) {\n      uintptr_t granule_addr = row_addr + column * kTagGranuleSize;\n      if (granule_addr < memory_dump.begin_address() ||\n          granule_addr >= memory_dump.begin_address() + num_tags * kTagGranuleSize) {\n        row_contents += \" . \";\n      } else if (granule_addr == fault_granule) {\n        row_contents += StringPrintf(\"[%1hhx]\", tags[tag_index++]);\n        row_has_fault = true;\n      } else {\n        row_contents += StringPrintf(\" %1hhx \", tags[tag_index++]);\n      }\n    }\n\n    if (row_contents.back() == ' ') row_contents.pop_back();\n\n    if (row_has_fault) {\n      CBS(\"    =>0x%\" PRIxPTR \":%s\", row_addr, row_contents.c_str());\n    } else {\n      CBS(\"      0x%\" PRIxPTR \":%s\", row_addr, row_contents.c_str());\n    }\n  }\n}\n\nstatic void print_memory_maps(CallbackType callback, const Tombstone& tombstone) {\n  int word_size = pointer_width(tombstone);\n  const auto format_pointer = [word_size](uint64_t ptr) -> std::string {\n    if (word_size == 8) {\n      uint64_t top = ptr >> 32;\n      uint64_t bottom = ptr & 0xFFFFFFFF;\n      return StringPrintf(\"%08\" PRIx64 \"'%08\" PRIx64, top, bottom);\n    }\n\n    return StringPrintf(\"%0*\" PRIx64, word_size * 2, ptr);\n  };\n\n  std::string memory_map_header =\n      StringPrintf(\"memory map (%d %s):\", tombstone.memory_mappings().size(),\n                   tombstone.memory_mappings().size() == 1 ? \"entry\" : \"entries\");\n\n  const Signal& signal_info = tombstone.signal_info();\n  bool has_fault_address = signal_info.has_fault_address();\n  uint64_t fault_address = untag_address(tombstone.arch(), signal_info.fault_address());\n  bool preamble_printed = false;\n  bool printed_fault_address_marker = false;\n  for (const auto& map : tombstone.memory_mappings()) {\n    if (!preamble_printed) {\n      preamble_printed = true;\n      if (has_fault_address) {\n        if (fault_address < map.begin_address()) {\n          memory_map_header +=\n              StringPrintf(\"\\n--->Fault address falls at %s before any mapped regions\",\n                           format_pointer(fault_address).c_str());\n          printed_fault_address_marker = true;\n        } else {\n          memory_map_header += \" (fault address prefixed with --->)\";\n        }\n      }\n      CBS(\"%s\", memory_map_header.c_str());\n    }\n\n    std::string line = \"    \";\n    if (has_fault_address && !printed_fault_address_marker) {\n      if (fault_address < map.begin_address()) {\n        printed_fault_address_marker = true;\n        CBS(\"--->Fault address falls at %s between mapped regions\",\n            format_pointer(fault_address).c_str());\n      } else if (fault_address >= map.begin_address() && fault_address < map.end_address()) {\n        printed_fault_address_marker = true;\n        line = \"--->\";\n      }\n    }\n    StringAppendF(&line, \"%s-%s\", format_pointer(map.begin_address()).c_str(),\n                  format_pointer(map.end_address() - 1).c_str());\n    StringAppendF(&line, \" %s%s%s\", map.read() ? \"r\" : \"-\", map.write() ? \"w\" : \"-\",\n                  map.execute() ? \"x\" : \"-\");\n    StringAppendF(&line, \"  %8\" PRIx64 \"  %8\" PRIx64, map.offset(),\n                  map.end_address() - map.begin_address());\n\n    if (!map.mapping_name().empty()) {\n      StringAppendF(&line, \"  %s\", map.mapping_name().c_str());\n\n      if (!map.build_id().empty()) {\n        StringAppendF(&line, \" (BuildId: %s)\", map.build_id().c_str());\n      }\n\n      if (map.load_bias() != 0) {\n        StringAppendF(&line, \" (load bias 0x%\" PRIx64 \")\", map.load_bias());\n      }\n    }\n\n    CBS(\"%s\", line.c_str());\n  }\n\n  if (has_fault_address && !printed_fault_address_marker) {\n    CBS(\"--->Fault address falls at %s after any mapped regions\",\n        format_pointer(fault_address).c_str());\n  }\n}\n\nstatic void print_main_thread(CallbackType callback, SymbolizeCallbackType symbolize,\n                              const Tombstone& tombstone, const Thread& thread) {\n  print_thread_header(callback, tombstone, thread, true);\n\n  const Signal& signal_info = tombstone.signal_info();\n  std::string sender_desc;\n\n  if (signal_info.has_sender()) {\n    sender_desc =\n        StringPrintf(\" from pid %d, uid %d\", signal_info.sender_pid(), signal_info.sender_uid());\n  }\n\n  bool is_async_mte_crash = false;\n  bool is_mte_crash = false;\n  if (!tombstone.has_signal_info()) {\n    CBL(\"signal information missing\");\n  } else {\n    std::string fault_addr_desc;\n    if (signal_info.has_fault_address()) {\n      fault_addr_desc =\n          StringPrintf(\"0x%0*\" PRIx64, 2 * pointer_width(tombstone), signal_info.fault_address());\n    } else {\n      fault_addr_desc = \"--------\";\n    }\n\n    CBL(\"signal %d (%s), code %d (%s%s), fault addr %s\", signal_info.number(),\n        signal_info.name().c_str(), signal_info.code(), signal_info.code_name().c_str(),\n        sender_desc.c_str(), fault_addr_desc.c_str());\n#ifdef SEGV_MTEAERR\n    is_async_mte_crash = signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTEAERR;\n    is_mte_crash = is_async_mte_crash ||\n                   (signal_info.number() == SIGSEGV && signal_info.code() == SEGV_MTESERR);\n#endif\n  }\n\n  if (tombstone.causes_size() == 1) {\n    CBL(\"Cause: %s\", tombstone.causes(0).human_readable().c_str());\n  }\n\n  if (!tombstone.abort_message().empty()) {\n    CBL(\"Abort message: '%s'\", tombstone.abort_message().c_str());\n  }\n\n  for (const auto& crash_detail : tombstone.crash_details()) {\n    std::string oct_encoded_name = oct_encode_non_printable(crash_detail.name());\n    std::string oct_encoded_data = oct_encode_non_printable(crash_detail.data());\n    CBL(\"Extra crash detail: %s: '%s'\", oct_encoded_name.c_str(), oct_encoded_data.c_str());\n  }\n\n  print_thread_registers(callback, tombstone, thread, true);\n  if (is_async_mte_crash) {\n    CBL(\"Note: This crash is a delayed async MTE crash. Memory corruption has occurred\");\n    CBL(\"      in this process. The stack trace below is the first system call or context\");\n    CBL(\"      switch that was executed after the memory corruption happened.\");\n  }\n  print_thread_backtrace(callback, symbolize, tombstone, thread, true);\n\n  if (tombstone.causes_size() > 1) {\n    CBS(\"\");\n    CBL(\"Note: multiple potential causes for this crash were detected, listing them in decreasing \"\n        \"order of likelihood.\");\n  }\n\n  if (tombstone.has_stack_history_buffer()) {\n    for (const StackHistoryBufferEntry& shbe : tombstone.stack_history_buffer().entries()) {\n      std::string stack_record_str = StringPrintf(\n          \"stack_record fp:0x%\" PRIx64 \" tag:0x%\" PRIx64 \" pc:%s+0x%\" PRIx64, shbe.fp(), shbe.tag(),\n          shbe.addr().file_name().c_str(), shbe.addr().rel_pc());\n      if (!shbe.addr().build_id().empty()) {\n        StringAppendF(&stack_record_str, \" (BuildId: %s)\", shbe.addr().build_id().c_str());\n      }\n\n      CBL(\"%s\", stack_record_str.c_str());\n    }\n  }\n\n  for (const Cause& cause : tombstone.causes()) {\n    if (tombstone.causes_size() > 1) {\n      CBS(\"\");\n      CBL(\"Cause: %s\", cause.human_readable().c_str());\n    }\n\n    if (cause.has_memory_error() && cause.memory_error().has_heap()) {\n      const HeapObject& heap_object = cause.memory_error().heap();\n\n      if (heap_object.deallocation_backtrace_size() != 0) {\n        CBS(\"\");\n        CBL(\"deallocated by thread %\" PRIu64 \":\", heap_object.deallocation_tid());\n        print_backtrace(callback, symbolize, tombstone, heap_object.deallocation_backtrace(), true);\n      }\n\n      if (heap_object.allocation_backtrace_size() != 0) {\n        CBS(\"\");\n        CBL(\"allocated by thread %\" PRIu64 \":\", heap_object.allocation_tid());\n        print_backtrace(callback, symbolize, tombstone, heap_object.allocation_backtrace(), true);\n      }\n    }\n  }\n\n  print_tag_dump(callback, tombstone);\n\n  if (is_mte_crash) {\n    CBS(\"\");\n    CBL(\"Learn more about MTE reports: \"\n        \"https://source.android.com/docs/security/test/memory-safety/mte-reports\");\n  }\n\n  print_thread_memory_dump(callback, tombstone, thread);\n\n  CBS(\"\");\n\n  // No memory maps to print.\n  if (!tombstone.memory_mappings().empty()) {\n    print_memory_maps(callback, tombstone);\n  } else {\n    CBS(\"No memory maps found\");\n  }\n}\n\nvoid print_logs(CallbackType callback, const Tombstone& tombstone, int tail) {\n  for (const auto& buffer : tombstone.log_buffers()) {\n    if (tail) {\n      CBS(\"--------- tail end of log %s\", buffer.name().c_str());\n    } else {\n      CBS(\"--------- log %s\", buffer.name().c_str());\n    }\n\n    int begin = 0;\n    if (tail != 0) {\n      begin = std::max(0, buffer.logs().size() - tail);\n    }\n\n    for (int i = begin; i < buffer.logs().size(); ++i) {\n      const LogMessage& msg = buffer.logs(i);\n\n      static const char* kPrioChars = \"!.VDIWEFS\";\n      char priority = (msg.priority() < strlen(kPrioChars) ? kPrioChars[msg.priority()] : '?');\n      CBS(\"%s %5u %5u %c %-8s: %s\", msg.timestamp().c_str(), msg.pid(), msg.tid(), priority,\n          msg.tag().c_str(), msg.message().c_str());\n    }\n  }\n}\n\nstatic void print_guest_thread(CallbackType callback, SymbolizeCallbackType symbolize,\n                               const Tombstone& tombstone, const Thread& guest_thread, pid_t tid,\n                               bool should_log) {\n  CBS(\"--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\");\n  CBS(\"Guest thread information for tid: %d\", tid);\n  print_thread_registers(callback, tombstone, guest_thread, should_log);\n\n  CBS(\"\");\n  CB(true, \"%d total frames\", guest_thread.current_backtrace().size());\n  CB(true, \"backtrace:\");\n  print_backtrace(callback, symbolize, tombstone, guest_thread.current_backtrace(), should_log);\n\n  print_thread_memory_dump(callback, tombstone, guest_thread);\n}\n\nbool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback,\n                             SymbolizeCallbackType symbolize) {\n  CBL(\"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\");\n  CBL(\"Build fingerprint: '%s'\", tombstone.build_fingerprint().c_str());\n  CBL(\"Revision: '%s'\", tombstone.revision().c_str());\n  CBL(\"ABI: '%s'\", abi_string(tombstone.arch()));\n  if (tombstone.guest_arch() != Architecture::NONE) {\n    CBL(\"Guest architecture: '%s'\", abi_string(tombstone.guest_arch()));\n  }\n  CBL(\"Timestamp: %s\", tombstone.timestamp().c_str());\n  CBL(\"Process uptime: %ds\", tombstone.process_uptime());\n\n  // only print this info if the page size is not 4k or has been in 16k mode\n  if (tombstone.page_size() != 4096) {\n    CBL(\"Page size: %d bytes\", tombstone.page_size());\n  } else if (tombstone.has_been_16kb_mode()) {\n    CBL(\"Has been in 16 KB mode before: yes\");\n  }\n\n  // Process header\n  const auto& threads = tombstone.threads();\n  auto main_thread_it = threads.find(tombstone.tid());\n  if (main_thread_it == threads.end()) {\n    CBL(\"failed to find entry for main thread in tombstone\");\n    return false;\n  }\n\n  const auto& main_thread = main_thread_it->second;\n\n  print_main_thread(callback, symbolize, tombstone, main_thread);\n\n  print_logs(callback, tombstone, 50);\n\n  const auto& guest_threads = tombstone.guest_threads();\n  auto main_guest_thread_it = guest_threads.find(tombstone.tid());\n  if (main_guest_thread_it != threads.end()) {\n    print_guest_thread(callback, symbolize, tombstone, main_guest_thread_it->second,\n                       tombstone.tid(), true);\n  }\n\n  // protobuf's map is unordered, so sort the keys first.\n  std::set<int> thread_ids;\n  for (const auto& [tid, _] : threads) {\n    if (tid != tombstone.tid()) {\n      thread_ids.insert(tid);\n    }\n  }\n\n  for (const auto& tid : thread_ids) {\n    CBS(\"--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\");\n    print_thread(callback, symbolize, tombstone, threads.find(tid)->second);\n    auto guest_thread_it = guest_threads.find(tid);\n    if (guest_thread_it != guest_threads.end()) {\n      print_guest_thread(callback, symbolize, tombstone, guest_thread_it->second, tid, false);\n    }\n  }\n\n  if (tombstone.open_fds().size() > 0) {\n    CBS(\"\");\n    CBS(\"open files:\");\n    for (const auto& fd : tombstone.open_fds()) {\n      std::optional<std::string> owner;\n      if (!fd.owner().empty()) {\n        owner = StringPrintf(\"owned by %s 0x%\" PRIx64, fd.owner().c_str(), fd.tag());\n      }\n\n      CBS(\"    fd %d: %s (%s)\", fd.fd(), fd.path().c_str(), owner ? owner->c_str() : \"unowned\");\n    }\n  }\n\n  print_logs(callback, tombstone, 0);\n\n  return true;\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/utility.cpp",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"DEBUG\"\n\n#include \"libdebuggerd/utility.h\"\n#include \"libdebuggerd/utility_host.h\"\n\n#include <errno.h>\n#include <signal.h>\n#include <string.h>\n#include <sys/capability.h>\n#include <sys/prctl.h>\n#include <sys/ptrace.h>\n#include <sys/uio.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <set>\n#include <string>\n\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <async_safe/log.h>\n#include <bionic/reserved_signals.h>\n#include <debuggerd/handler.h>\n#include <log/log.h>\n#include <unwindstack/AndroidUnwinder.h>\n#include <unwindstack/Memory.h>\n#include <unwindstack/Unwinder.h>\n\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\n\nbool is_allowed_in_logcat(enum logtype ltype) {\n  return (ltype == HEADER) || (ltype == REGISTERS) || (ltype == BACKTRACE);\n}\n\nstatic bool should_write_to_kmsg() {\n  // Write to kmsg if tombstoned isn't up, and we're able to do so.\n  if (!android::base::GetBoolProperty(\"ro.debuggable\", false)) {\n    return false;\n  }\n\n  if (android::base::GetProperty(\"init.svc.tombstoned\", \"\") == \"running\") {\n    return false;\n  }\n\n  return true;\n}\n\n__attribute__((__weak__, visibility(\"default\")))\nvoid _LOG(log_t* log, enum logtype ltype, const char* fmt, ...) {\n  va_list ap;\n  va_start(ap, fmt);\n  _VLOG(log, ltype, fmt, ap);\n  va_end(ap);\n}\n\n__attribute__((__weak__, visibility(\"default\")))\nvoid _VLOG(log_t* log, enum logtype ltype, const char* fmt, va_list ap) {\n  bool write_to_tombstone = (log->tfd != -1);\n  bool write_to_logcat = is_allowed_in_logcat(ltype)\n                      && log->crashed_tid != -1\n                      && log->current_tid != -1\n                      && (log->crashed_tid == log->current_tid);\n  static bool write_to_kmsg = should_write_to_kmsg();\n\n  std::string msg;\n  android::base::StringAppendV(&msg, fmt, ap);\n\n  if (msg.empty()) return;\n\n  if (write_to_tombstone) {\n    TEMP_FAILURE_RETRY(write(log->tfd, msg.c_str(), msg.size()));\n  }\n\n  if (write_to_logcat) {\n    __android_log_buf_write(LOG_ID_CRASH, ANDROID_LOG_FATAL, LOG_TAG, msg.c_str());\n    if (log->amfd_data != nullptr) {\n      *log->amfd_data += msg;\n    }\n\n    if (write_to_kmsg) {\n      unique_fd kmsg_fd(open(\"/dev/kmsg_debug\", O_WRONLY | O_APPEND | O_CLOEXEC));\n      if (kmsg_fd.get() >= 0) {\n        // Our output might contain newlines which would otherwise be handled by the android logger.\n        // Split the lines up ourselves before sending to the kernel logger.\n        if (msg.back() == '\\n') {\n          msg.back() = '\\0';\n        }\n\n        std::vector<std::string> fragments = android::base::Split(msg, \"\\n\");\n        for (const std::string& fragment : fragments) {\n          static constexpr char prefix[] = \"<3>DEBUG: \";\n          struct iovec iov[3];\n          iov[0].iov_base = const_cast<char*>(prefix);\n          iov[0].iov_len = strlen(prefix);\n          iov[1].iov_base = const_cast<char*>(fragment.c_str());\n          iov[1].iov_len = fragment.length();\n          iov[2].iov_base = const_cast<char*>(\"\\n\");\n          iov[2].iov_len = 1;\n          TEMP_FAILURE_RETRY(writev(kmsg_fd.get(), iov, 3));\n        }\n      }\n    }\n  }\n}\n\n#define MEMORY_BYTES_TO_DUMP 256\n#define MEMORY_BYTES_PER_LINE 16\nstatic_assert(MEMORY_BYTES_PER_LINE == kTagGranuleSize);\n\nssize_t dump_memory(void* out, size_t len, uint8_t* tags, size_t tags_len, uint64_t* addr,\n                    unwindstack::Memory* memory) {\n  // Align the address to the number of bytes per line to avoid confusing memory tag output if\n  // memory is tagged and we start from a misaligned address. Start 32 bytes before the address.\n  *addr &= ~(MEMORY_BYTES_PER_LINE - 1);\n  if (*addr >= 4128) {\n    *addr -= 32;\n  }\n\n  // We don't want the address tag to appear in the addresses in the memory dump.\n  *addr = untag_address(*addr);\n\n  // Don't bother if the address would overflow, taking tag bits into account. Note that\n  // untag_address truncates to 32 bits on 32-bit platforms as a side effect of returning a\n  // uintptr_t, so this also checks for 32-bit overflow.\n  if (untag_address(*addr + MEMORY_BYTES_TO_DUMP - 1) < *addr) {\n    return -1;\n  }\n\n  memset(out, 0, len);\n\n  size_t bytes = memory->Read(*addr, reinterpret_cast<uint8_t*>(out), len);\n  if (bytes % sizeof(uintptr_t) != 0) {\n    // This should never happen, but just in case.\n    ALOGE(\"Bytes read %zu, is not a multiple of %zu\", bytes, sizeof(uintptr_t));\n    bytes &= ~(sizeof(uintptr_t) - 1);\n  }\n\n  bool skip_2nd_read = false;\n  if (bytes == 0) {\n    // In this case, we might want to try another read at the beginning of\n    // the next page only if it's within the amount of memory we would have\n    // read.\n    size_t page_size = sysconf(_SC_PAGE_SIZE);\n    uint64_t next_page = (*addr + (page_size - 1)) & ~(page_size - 1);\n    if (next_page == *addr || next_page >= *addr + len) {\n      skip_2nd_read = true;\n    }\n    *addr = next_page;\n  }\n\n  if (bytes < len && !skip_2nd_read) {\n    // Try to do one more read. This could happen if a read crosses a map,\n    // but the maps do not have any break between them. Or it could happen\n    // if reading from an unreadable map, but the read would cross back\n    // into a readable map. Only requires one extra read because a map has\n    // to contain at least one page, and the total number of bytes to dump\n    // is smaller than a page.\n    size_t bytes2 = memory->Read(*addr + bytes, static_cast<uint8_t*>(out) + bytes, len - bytes);\n    bytes += bytes2;\n    if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {\n      // This should never happen, but we'll try and continue any way.\n      ALOGE(\"Bytes after second read %zu, is not a multiple of %zu\", bytes, sizeof(uintptr_t));\n      bytes &= ~(sizeof(uintptr_t) - 1);\n    }\n  }\n\n  // If we were unable to read anything, it probably means that the register doesn't contain a\n  // valid pointer.\n  if (bytes == 0) {\n    return -1;\n  }\n\n  for (uint64_t tag_granule = 0; tag_granule < bytes / kTagGranuleSize; ++tag_granule) {\n    long tag = memory->ReadTag(*addr + kTagGranuleSize * tag_granule);\n    if (tag_granule < tags_len) {\n      tags[tag_granule] = tag >= 0 ? tag : 0;\n    } else {\n      ALOGE(\"Insufficient space for tags\");\n    }\n  }\n\n  return bytes;\n}\n\nvoid dump_memory(log_t* log, unwindstack::Memory* memory, uint64_t addr, const std::string& label) {\n  // Dump 256 bytes\n  uintptr_t data[MEMORY_BYTES_TO_DUMP / sizeof(uintptr_t)];\n  uint8_t tags[MEMORY_BYTES_TO_DUMP / kTagGranuleSize];\n\n  ssize_t bytes = dump_memory(data, sizeof(data), tags, sizeof(tags), &addr, memory);\n  if (bytes == -1) {\n    return;\n  }\n\n  _LOG(log, logtype::MEMORY, \"\\n%s:\\n\", label.c_str());\n\n  // Dump the code around memory as:\n  //  addr             contents                           ascii\n  //  0000000000008d34 ef000000e8bd0090 e1b00000512fff1e  ............../Q\n  //  0000000000008d44 ea00b1f9e92d0090 e3a070fcef000000  ......-..p......\n  // On 32-bit machines, there are still 16 bytes per line but addresses and\n  // words are of course presented differently.\n  uintptr_t* data_ptr = data;\n  uint8_t* tags_ptr = tags;\n  for (size_t line = 0; line < static_cast<size_t>(bytes) / MEMORY_BYTES_PER_LINE; line++) {\n    uint64_t tagged_addr = addr | static_cast<uint64_t>(*tags_ptr++) << 56;\n    std::string logline;\n    android::base::StringAppendF(&logline, \"    %\" PRIPTR, tagged_addr);\n\n    addr += MEMORY_BYTES_PER_LINE;\n    std::string ascii;\n    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {\n      android::base::StringAppendF(&logline, \" %\" PRIPTR, static_cast<uint64_t>(*data_ptr));\n\n      // Fill out the ascii string from the data.\n      uint8_t* ptr = reinterpret_cast<uint8_t*>(data_ptr);\n      for (size_t val = 0; val < sizeof(uintptr_t); val++, ptr++) {\n        if (*ptr >= 0x20 && *ptr < 0x7f) {\n          ascii += *ptr;\n        } else {\n          ascii += '.';\n        }\n      }\n      data_ptr++;\n    }\n    _LOG(log, logtype::MEMORY, \"%s  %s\\n\", logline.c_str(), ascii.c_str());\n  }\n}\n\nvoid drop_capabilities() {\n  __user_cap_header_struct capheader;\n  memset(&capheader, 0, sizeof(capheader));\n  capheader.version = _LINUX_CAPABILITY_VERSION_3;\n  capheader.pid = 0;\n\n  __user_cap_data_struct capdata[2];\n  memset(&capdata, 0, sizeof(capdata));\n\n  if (capset(&capheader, &capdata[0]) == -1) {\n    async_safe_fatal(\"failed to drop capabilities: %s\", strerror(errno));\n  }\n\n  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {\n    async_safe_fatal(\"failed to set PR_SET_NO_NEW_PRIVS: %s\", strerror(errno));\n  }\n}\n\nbool signal_has_si_addr(const siginfo_t* si) {\n  // Manually sent signals won't have si_addr.\n  if (si->si_code == SI_USER || si->si_code == SI_QUEUE || si->si_code == SI_TKILL) {\n    return false;\n  }\n\n  switch (si->si_signo) {\n    case SIGBUS:\n    case SIGFPE:\n    case SIGILL:\n    case SIGTRAP:\n      return true;\n    case SIGSEGV:\n      return si->si_code != SEGV_MTEAERR;\n    default:\n      return false;\n  }\n}\n\nbool signal_has_sender(const siginfo_t* si, pid_t caller_pid) {\n  return SI_FROMUSER(si) && (si->si_pid != 0) && (si->si_pid != caller_pid);\n}\n\nvoid get_signal_sender(char* buf, size_t n, const siginfo_t* si) {\n  snprintf(buf, n, \" from pid %d, uid %d\", si->si_pid, si->si_uid);\n}\n\nconst char* get_signame(const siginfo_t* si) {\n  switch (si->si_signo) {\n    case SIGABRT: return \"SIGABRT\";\n    case SIGBUS: return \"SIGBUS\";\n    case SIGFPE: return \"SIGFPE\";\n    case SIGILL: return \"SIGILL\";\n    case SIGSEGV: return \"SIGSEGV\";\n    case SIGSTKFLT: return \"SIGSTKFLT\";\n    case SIGSTOP: return \"SIGSTOP\";\n    case SIGSYS: return \"SIGSYS\";\n    case SIGTRAP: return \"SIGTRAP\";\n    case BIONIC_SIGNAL_DEBUGGER:\n      return \"<debuggerd signal>\";\n    default: return \"?\";\n  }\n}\n\nconst char* get_sigcode(const siginfo_t* si) {\n  // Try the signal-specific codes...\n  switch (si->si_signo) {\n    case SIGILL:\n      switch (si->si_code) {\n        case ILL_ILLOPC: return \"ILL_ILLOPC\";\n        case ILL_ILLOPN: return \"ILL_ILLOPN\";\n        case ILL_ILLADR: return \"ILL_ILLADR\";\n        case ILL_ILLTRP: return \"ILL_ILLTRP\";\n        case ILL_PRVOPC: return \"ILL_PRVOPC\";\n        case ILL_PRVREG: return \"ILL_PRVREG\";\n        case ILL_COPROC: return \"ILL_COPROC\";\n        case ILL_BADSTK: return \"ILL_BADSTK\";\n        case ILL_BADIADDR:\n          return \"ILL_BADIADDR\";\n        case __ILL_BREAK:\n          return \"ILL_BREAK\";\n        case __ILL_BNDMOD:\n          return \"ILL_BNDMOD\";\n      }\n      static_assert(NSIGILL == __ILL_BNDMOD, \"missing ILL_* si_code\");\n      break;\n    case SIGBUS:\n      switch (si->si_code) {\n        case BUS_ADRALN: return \"BUS_ADRALN\";\n        case BUS_ADRERR: return \"BUS_ADRERR\";\n        case BUS_OBJERR: return \"BUS_OBJERR\";\n        case BUS_MCEERR_AR: return \"BUS_MCEERR_AR\";\n        case BUS_MCEERR_AO: return \"BUS_MCEERR_AO\";\n      }\n      static_assert(NSIGBUS == BUS_MCEERR_AO, \"missing BUS_* si_code\");\n      break;\n    case SIGFPE:\n      switch (si->si_code) {\n        case FPE_INTDIV: return \"FPE_INTDIV\";\n        case FPE_INTOVF: return \"FPE_INTOVF\";\n        case FPE_FLTDIV: return \"FPE_FLTDIV\";\n        case FPE_FLTOVF: return \"FPE_FLTOVF\";\n        case FPE_FLTUND: return \"FPE_FLTUND\";\n        case FPE_FLTRES: return \"FPE_FLTRES\";\n        case FPE_FLTINV: return \"FPE_FLTINV\";\n        case FPE_FLTSUB: return \"FPE_FLTSUB\";\n        case __FPE_DECOVF:\n          return \"FPE_DECOVF\";\n        case __FPE_DECDIV:\n          return \"FPE_DECDIV\";\n        case __FPE_DECERR:\n          return \"FPE_DECERR\";\n        case __FPE_INVASC:\n          return \"FPE_INVASC\";\n        case __FPE_INVDEC:\n          return \"FPE_INVDEC\";\n        case FPE_FLTUNK:\n          return \"FPE_FLTUNK\";\n        case FPE_CONDTRAP:\n          return \"FPE_CONDTRAP\";\n      }\n      static_assert(NSIGFPE == FPE_CONDTRAP, \"missing FPE_* si_code\");\n      break;\n    case SIGSEGV:\n      switch (si->si_code) {\n        case SEGV_MAPERR: return \"SEGV_MAPERR\";\n        case SEGV_ACCERR: return \"SEGV_ACCERR\";\n        case SEGV_BNDERR: return \"SEGV_BNDERR\";\n        case SEGV_PKUERR: return \"SEGV_PKUERR\";\n        case SEGV_ACCADI:\n          return \"SEGV_ACCADI\";\n        case SEGV_ADIDERR:\n          return \"SEGV_ADIDERR\";\n        case SEGV_ADIPERR:\n          return \"SEGV_ADIPERR\";\n        case SEGV_MTEAERR:\n          return \"SEGV_MTEAERR\";\n        case SEGV_MTESERR:\n          return \"SEGV_MTESERR\";\n        case SEGV_CPERR:\n          return \"SEGV_CPERR\";\n      }\n      static_assert(NSIGSEGV == SEGV_CPERR, \"missing SEGV_* si_code\");\n      break;\n    case SIGSYS:\n      switch (si->si_code) {\n        case SYS_SECCOMP: return \"SYS_SECCOMP\";\n        case SYS_USER_DISPATCH:\n          return \"SYS_USER_DISPATCH\";\n      }\n      static_assert(NSIGSYS == SYS_USER_DISPATCH, \"missing SYS_* si_code\");\n      break;\n    case SIGTRAP:\n      switch (si->si_code) {\n        case TRAP_BRKPT: return \"TRAP_BRKPT\";\n        case TRAP_TRACE: return \"TRAP_TRACE\";\n        case TRAP_BRANCH: return \"TRAP_BRANCH\";\n        case TRAP_HWBKPT: return \"TRAP_HWBKPT\";\n        case TRAP_UNK:\n          return \"TRAP_UNDIAGNOSED\";\n        case TRAP_PERF:\n          return \"TRAP_PERF\";\n      }\n      if ((si->si_code & 0xff) == SIGTRAP) {\n        switch ((si->si_code >> 8) & 0xff) {\n          case PTRACE_EVENT_FORK:\n            return \"PTRACE_EVENT_FORK\";\n          case PTRACE_EVENT_VFORK:\n            return \"PTRACE_EVENT_VFORK\";\n          case PTRACE_EVENT_CLONE:\n            return \"PTRACE_EVENT_CLONE\";\n          case PTRACE_EVENT_EXEC:\n            return \"PTRACE_EVENT_EXEC\";\n          case PTRACE_EVENT_VFORK_DONE:\n            return \"PTRACE_EVENT_VFORK_DONE\";\n          case PTRACE_EVENT_EXIT:\n            return \"PTRACE_EVENT_EXIT\";\n          case PTRACE_EVENT_SECCOMP:\n            return \"PTRACE_EVENT_SECCOMP\";\n          case PTRACE_EVENT_STOP:\n            return \"PTRACE_EVENT_STOP\";\n        }\n      }\n      static_assert(NSIGTRAP == TRAP_PERF, \"missing TRAP_* si_code\");\n      break;\n  }\n  // Then the other codes...\n  switch (si->si_code) {\n    case SI_USER: return \"SI_USER\";\n    case SI_KERNEL: return \"SI_KERNEL\";\n    case SI_QUEUE: return \"SI_QUEUE\";\n    case SI_TIMER: return \"SI_TIMER\";\n    case SI_MESGQ: return \"SI_MESGQ\";\n    case SI_ASYNCIO: return \"SI_ASYNCIO\";\n    case SI_SIGIO: return \"SI_SIGIO\";\n    case SI_TKILL: return \"SI_TKILL\";\n    case SI_DETHREAD: return \"SI_DETHREAD\";\n  }\n  // Then give up...\n  return \"?\";\n}\n\nvoid log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder,\n                   unwindstack::AndroidUnwinderData& data, const char* prefix) {\n  std::set<std::string> unreadable_elf_files;\n  for (const auto& frame : data.frames) {\n    if (frame.map_info != nullptr && frame.map_info->ElfFileNotReadable()) {\n      unreadable_elf_files.emplace(frame.map_info->name());\n    }\n  }\n\n  // Put the preamble ahead of the backtrace.\n  if (!unreadable_elf_files.empty()) {\n    _LOG(log, logtype::BACKTRACE,\n         \"%sNOTE: Function names and BuildId information is missing for some frames due\\n\", prefix);\n    _LOG(log, logtype::BACKTRACE,\n         \"%sNOTE: to unreadable libraries. For unwinds of apps, only shared libraries\\n\", prefix);\n    _LOG(log, logtype::BACKTRACE, \"%sNOTE: found under the lib/ directory are readable.\\n\", prefix);\n#if defined(ROOT_POSSIBLE)\n    _LOG(log, logtype::BACKTRACE,\n         \"%sNOTE: On this device, run setenforce 0 to make the libraries readable.\\n\", prefix);\n#endif\n    _LOG(log, logtype::BACKTRACE, \"%sNOTE: Unreadable libraries:\\n\", prefix);\n    for (auto& name : unreadable_elf_files) {\n      _LOG(log, logtype::BACKTRACE, \"%sNOTE:   %s\\n\", prefix, name.c_str());\n    }\n  }\n\n  for (const auto& frame : data.frames) {\n    _LOG(log, logtype::BACKTRACE, \"%s%s\\n\", prefix, unwinder->FormatFrame(frame).c_str());\n  }\n}\n"
  },
  {
    "path": "debuggerd/libdebuggerd/utility_host.cpp",
    "content": "/*\n * Copyright 2024, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libdebuggerd/utility_host.h\"\n\n#include <ctype.h>\n#include <sys/prctl.h>\n\n#include <charconv>\n#include <limits>\n#include <string>\n\n#include <android-base/stringprintf.h>\n\nusing android::base::StringPrintf;\n\n#ifndef PR_MTE_TAG_SHIFT\n#define PR_MTE_TAG_SHIFT 3\n#endif\n\n#ifndef PR_MTE_TAG_MASK\n#define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT)\n#endif\n\n#ifndef PR_MTE_TCF_ASYNC\n#define PR_MTE_TCF_ASYNC (1UL << 2)\n#endif\n\n#ifndef PR_MTE_TCF_SYNC\n#define PR_MTE_TCF_SYNC (1UL << 1)\n#endif\n\n#ifndef PR_PAC_APIAKEY\n#define PR_PAC_APIAKEY (1UL << 0)\n#endif\n\n#ifndef PR_PAC_APIBKEY\n#define PR_PAC_APIBKEY (1UL << 1)\n#endif\n\n#ifndef PR_PAC_APDAKEY\n#define PR_PAC_APDAKEY (1UL << 2)\n#endif\n\n#ifndef PR_PAC_APDBKEY\n#define PR_PAC_APDBKEY (1UL << 3)\n#endif\n\n#ifndef PR_PAC_APGAKEY\n#define PR_PAC_APGAKEY (1UL << 4)\n#endif\n\n#ifndef PR_TAGGED_ADDR_ENABLE\n#define PR_TAGGED_ADDR_ENABLE (1UL << 0)\n#endif\n\n#define DESCRIBE_FLAG(flag) \\\n  if (value & flag) {       \\\n    desc += \", \";           \\\n    desc += #flag;          \\\n    value &= ~flag;         \\\n  }\n\nstatic std::string describe_end(long value, std::string& desc) {\n  if (value) {\n    desc += StringPrintf(\", unknown 0x%lx\", value);\n  }\n  return desc.empty() ? \"\" : \" (\" + desc.substr(2) + \")\";\n}\n\nstd::string describe_tagged_addr_ctrl(long value) {\n  std::string desc;\n  DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE);\n  DESCRIBE_FLAG(PR_MTE_TCF_SYNC);\n  DESCRIBE_FLAG(PR_MTE_TCF_ASYNC);\n  if (value & PR_MTE_TAG_MASK) {\n    desc += StringPrintf(\", mask 0x%04lx\", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT);\n    value &= ~PR_MTE_TAG_MASK;\n  }\n  return describe_end(value, desc);\n}\n\nstd::string describe_pac_enabled_keys(long value) {\n  std::string desc;\n  DESCRIBE_FLAG(PR_PAC_APIAKEY);\n  DESCRIBE_FLAG(PR_PAC_APIBKEY);\n  DESCRIBE_FLAG(PR_PAC_APDAKEY);\n  DESCRIBE_FLAG(PR_PAC_APDBKEY);\n  DESCRIBE_FLAG(PR_PAC_APGAKEY);\n  return describe_end(value, desc);\n}\n\nstatic std::string oct_encode(const std::string& data, bool (*should_encode_func)(int)) {\n  std::string oct_encoded;\n  oct_encoded.reserve(data.size());\n\n  // N.B. the unsigned here is very important, otherwise e.g. \\255 would render as\n  // \\-123 (and overflow our buffer).\n  for (unsigned char c : data) {\n    if (should_encode_func(c)) {\n      std::string oct_digits(\"\\\\\\0\\0\\0\", 4);\n      // char is encodable in 3 oct digits\n      static_assert(std::numeric_limits<unsigned char>::max() <= 8 * 8 * 8);\n      auto [ptr, ec] = std::to_chars(oct_digits.data() + 1, oct_digits.data() + 4, c, 8);\n      oct_digits.resize(ptr - oct_digits.data());\n      oct_encoded += oct_digits;\n    } else {\n      oct_encoded += c;\n    }\n  }\n  return oct_encoded;\n}\n\nstd::string oct_encode_non_ascii_printable(const std::string& data) {\n  return oct_encode(data, [](int c) { return !isgraph(c) && !isspace(c); });\n}\n\nstd::string oct_encode_non_printable(const std::string& data) {\n  return oct_encode(data, [](int c) { return !isprint(c); });\n}\n"
  },
  {
    "path": "debuggerd/pbtombstone.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <err.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <libdebuggerd/tombstone_proto_to_text.h>\n\n#include \"tombstone.pb.h\"\n#include \"tombstone_symbolize.h\"\n\nusing android::base::unique_fd;\n\n[[noreturn]] void usage(bool error) {\n  fprintf(stderr, \"usage: pbtombstone [OPTION] TOMBSTONE.PB\\n\");\n  fprintf(stderr, \"Convert a protobuf tombstone to text.\\n\");\n  fprintf(stderr, \"Arguments:\\n\");\n  fprintf(stderr, \"  -h, --help                   print this message\\n\");\n  fprintf(stderr, \"  --debug-file-directory PATH  specify the path to a symbols directory\\n\");\n  exit(error);\n}\n\nint main(int argc, char* argv[]) {\n  std::vector<std::string> debug_file_directories;\n  static struct option long_options[] = {\n      {\"debug-file-directory\", required_argument, 0, 0},\n      {\"help\", no_argument, 0, 'h'},\n      {},\n  };\n  int c;\n  while ((c = getopt_long(argc, argv, \"h\", long_options, 0)) != -1) {\n    switch (c) {\n      case 0:\n        debug_file_directories.push_back(optarg);\n        break;\n\n      case 'h':\n        usage(false);\n        break;\n    }\n  }\n\n  if (optind != argc-1) {\n    usage(true);\n  }\n\n  unique_fd fd(open(argv[optind], O_RDONLY | O_CLOEXEC));\n  if (fd == -1) {\n    err(1, \"failed to open tombstone '%s'\", argv[1]);\n  }\n\n  Tombstone tombstone;\n  if (!tombstone.ParseFromFileDescriptor(fd.get())) {\n    err(1, \"failed to parse tombstone\");\n  }\n\n  Symbolizer sym;\n  sym.Start(debug_file_directories);\n  bool result = tombstone_proto_to_text(\n      tombstone, [](const std::string& line, bool) { printf(\"%s\\n\", line.c_str()); },\n      [&](const BacktraceFrame& frame) { symbolize_backtrace_frame(frame, sym); });\n\n  if (!result) {\n    errx(1, \"tombstone was malformed\");\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "debuggerd/proto/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nfilegroup {\n    name: \"libtombstone_proto-src\",\n    srcs: [\"tombstone.proto\"],\n}\n\ncc_library_static {\n    name: \"libtombstone_proto\",\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Wthread-safety\",\n        \"-Werror\",\n    ],\n\n    compile_multilib: \"both\",\n\n    proto: {\n        export_proto_headers: true,\n        type: \"lite\",\n    },\n\n    srcs: [\":libtombstone_proto-src\"],\n\n    // b/155341058: Soong doesn't automatically add libprotobuf if there aren't any explicitly\n    // listed protos in srcs.\n    static_libs: [\"libprotobuf-cpp-lite\"],\n\n    stl: \"libc++_static\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.runtime\",\n    ],\n\n    ramdisk_available: true,\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    host_supported: true,\n}\n\njava_library_static {\n    name: \"libtombstone_proto_java\",\n    proto: {\n        type: \"lite\",\n    },\n    srcs: [\n        \"tombstone.proto\",\n    ],\n    jarjar_rules: \"jarjar-rules.txt\",\n    sdk_version: \"current\",\n    static_libs: [\"libprotobuf-java-lite\"],\n}\n"
  },
  {
    "path": "debuggerd/proto/jarjar-rules.txt",
    "content": "rule com.google.protobuf.** com.android.server.os.protobuf.@1\n"
  },
  {
    "path": "debuggerd/proto/tombstone.proto",
    "content": "//\n// Protobuf definition for Android tombstones.\n//\n// An app can get hold of these for any `REASON_CRASH_NATIVE` instance of\n// `android.app.ApplicationExitInfo`.\n//\n// https://developer.android.com/reference/android/app/ApplicationExitInfo#getTraceInputStream()\n//\n\nsyntax = \"proto3\";\n\noption java_package = \"com.android.server.os\";\noption java_outer_classname = \"TombstoneProtos\";\n\n// NOTE TO OEMS:\n// If you add custom fields to this proto, do not use numbers in the reserved range.\n\n// NOTE TO CONSUMERS:\n// With proto3 -- unlike proto2 -- HasValue is unreliable for any field\n// where the default value for that type is also a valid value for the field.\n// This means, for example, that a boolean that is false or an integer that\n// is zero will appear to be missing --- but because they're not actually\n// marked as `optional` in this schema, consumers should just use values\n// without first checking whether or not they're \"present\".\n// https://protobuf.dev/programming-guides/proto3/#default\n\nmessage CrashDetail {\n  bytes name = 1;\n  bytes data = 2;\n\n  reserved 3 to 999;\n}\n\nmessage StackHistoryBufferEntry {\n  BacktraceFrame addr = 1;\n  uint64 fp = 2;\n  uint64 tag = 3;\n\n  reserved 4 to 999;\n}\n\nmessage StackHistoryBuffer {\n  uint64 tid = 1;\n  repeated StackHistoryBufferEntry entries = 2;\n\n  reserved 3 to 999;\n}\n\nmessage Tombstone {\n  Architecture arch = 1;\n  Architecture guest_arch = 24;\n  string build_fingerprint = 2;\n  string revision = 3;\n  string timestamp = 4;\n\n  uint32 pid = 5;\n  uint32 tid = 6;\n  uint32 uid = 7;\n  string selinux_label = 8;\n\n  repeated string command_line = 9;\n\n  // Process uptime in seconds.\n  uint32 process_uptime = 20;\n\n  Signal signal_info = 10;\n  string abort_message = 14;\n  repeated CrashDetail crash_details = 21;\n  repeated Cause causes = 15;\n\n  map<uint32, Thread> threads = 16;\n  map<uint32, Thread> guest_threads = 25;\n  repeated MemoryMapping memory_mappings = 17;\n  repeated LogBuffer log_buffers = 18;\n  repeated FD open_fds = 19;\n\n  uint32 page_size = 22;\n  bool has_been_16kb_mode = 23;\n\n  StackHistoryBuffer stack_history_buffer = 26;\n\n  reserved 27 to 999;\n}\n\nenum Architecture {\n  ARM32 = 0;\n  ARM64 = 1;\n  X86 = 2;\n  X86_64 = 3;\n  RISCV64 = 4;\n  NONE = 5;\n\n  reserved 6 to 999;\n}\n\nmessage Signal {\n  int32 number = 1;\n  string name = 2;\n\n  int32 code = 3;\n  string code_name = 4;\n\n  bool has_sender = 5;\n  int32 sender_uid = 6;\n  int32 sender_pid = 7;\n\n  bool has_fault_address = 8;\n  uint64 fault_address = 9;\n  // Note, may or may not contain the dump of the actual memory contents. Currently, on arm64, we\n  // only include metadata, and not the contents.\n  MemoryDump fault_adjacent_metadata = 10;\n\n  reserved 11 to 999;\n}\n\nmessage HeapObject {\n  uint64 address = 1;\n  uint64 size = 2;\n\n  uint64 allocation_tid = 3;\n  repeated BacktraceFrame allocation_backtrace = 4;\n\n  uint64 deallocation_tid = 5;\n  repeated BacktraceFrame deallocation_backtrace = 6;\n}\n\nmessage MemoryError {\n  enum Tool {\n    GWP_ASAN = 0;\n    SCUDO = 1;\n\n    reserved 2 to 999;\n  }\n  Tool tool = 1;\n\n  enum Type {\n    UNKNOWN = 0;\n    USE_AFTER_FREE = 1;\n    DOUBLE_FREE = 2;\n    INVALID_FREE = 3;\n    BUFFER_OVERFLOW = 4;\n    BUFFER_UNDERFLOW = 5;\n\n    reserved 6 to 999;\n  }\n  Type type = 2;\n\n  oneof location {\n    HeapObject heap = 3;\n  }\n\n  reserved 4 to 999;\n}\n\nmessage Cause {\n  string human_readable = 1;\n  oneof details {\n    MemoryError memory_error = 2;\n  }\n\n  reserved 3 to 999;\n}\n\nmessage Register {\n  string name = 1;\n  uint64 u64 = 2;\n\n  reserved 3 to 999;\n}\n\nmessage Thread {\n  int32 id = 1;\n  string name = 2;\n  repeated Register registers = 3;\n  repeated string backtrace_note = 7;\n  repeated string unreadable_elf_files = 9;\n  repeated BacktraceFrame current_backtrace = 4;\n  repeated MemoryDump memory_dump = 5;\n  int64 tagged_addr_ctrl = 6;\n  int64 pac_enabled_keys = 8;\n\n  reserved 10 to 999;\n}\n\nmessage BacktraceFrame {\n  uint64 rel_pc = 1;\n  uint64 pc = 2;\n  uint64 sp = 3;\n\n  string function_name = 4;\n  uint64 function_offset = 5;\n\n  string file_name = 6;\n  uint64 file_map_offset = 7;\n  string build_id = 8;\n\n  reserved 9 to 999;\n}\n\nmessage ArmMTEMetadata {\n  // One memory tag per granule (e.g. every 16 bytes) of regular memory.\n  bytes memory_tags = 1;\n  reserved 2 to 999;\n}\n\nmessage MemoryDump {\n  string register_name = 1;\n  string mapping_name = 2;\n  uint64 begin_address = 3;\n  bytes memory = 4;\n  oneof metadata {\n    ArmMTEMetadata arm_mte_metadata = 6;\n  }\n\n  reserved 5, 7 to 999;\n}\n\nmessage MemoryMapping {\n  uint64 begin_address = 1;\n  uint64 end_address = 2;\n  uint64 offset = 3;\n\n  bool read = 4;\n  bool write = 5;\n  bool execute = 6;\n\n  string mapping_name = 7;\n  string build_id = 8;\n  uint64 load_bias = 9;\n\n  reserved 10 to 999;\n}\n\nmessage FD {\n  int32 fd = 1;\n  string path = 2;\n  string owner = 3;\n  uint64 tag = 4;\n\n  reserved 5 to 999;\n}\n\nmessage LogBuffer {\n  string name = 1;\n  repeated LogMessage logs = 2;\n\n  reserved 3 to 999;\n}\n\nmessage LogMessage {\n  string timestamp = 1;\n  uint32 pid = 2;\n  uint32 tid = 3;\n  uint32 priority = 4;\n  string tag = 5;\n  string message = 6;\n\n  reserved 7 to 999;\n}\n"
  },
  {
    "path": "debuggerd/protocol.h",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <signal.h>\n#include <stdint.h>\n#include <sys/ucontext.h>\n#include <unistd.h>\n\n#include \"dump_type.h\"\n\n// Sockets in the ANDROID_SOCKET_NAMESPACE_RESERVED namespace.\n// Both sockets are SOCK_SEQPACKET sockets, so no explicit length field is needed.\nconstexpr char kTombstonedCrashSocketName[] = \"tombstoned_crash\";\nconstexpr char kTombstonedJavaTraceSocketName[] = \"tombstoned_java_trace\";\nconstexpr char kTombstonedInterceptSocketName[] = \"tombstoned_intercept\";\n\nenum class CrashPacketType : uint8_t {\n  // Initial request from crash_dump.\n  kDumpRequest = 0,\n\n  // Notification of a completed crash dump.\n  // Sent after a dump is completed and the process has been untraced, but\n  // before it has been resumed with SIGCONT.\n  kCompletedDump,\n\n  // Responses to kRequest.\n  // kPerformDump sends along an output fd via cmsg(3).\n  kPerformDump = 128,\n  kAbortDump,\n};\n\nstruct DumpRequest {\n  DebuggerdDumpType dump_type;\n  int32_t pid;\n};\n\n// The full packet must always be written, regardless of whether the union is used.\nstruct TombstonedCrashPacket {\n  CrashPacketType packet_type;\n  union {\n    DumpRequest dump_request;\n  } packet;\n};\n\n// Comes with a file descriptor via SCM_RIGHTS.\n// This packet should be sent before an actual dump happens.\nstruct InterceptRequest {\n  DebuggerdDumpType dump_type;\n  int32_t pid;\n};\n\nenum class InterceptStatus : uint8_t {\n  // Returned when an intercept of the same type has already been\n  // registered (and is active) for a given PID.\n  kFailedAlreadyRegistered,\n  // Returned in all other failure cases.\n  kFailed,\n  kStarted,\n  kRegistered,\n};\n\n// Sent either immediately upon failure, or when the intercept has been used.\nstruct InterceptResponse {\n  InterceptStatus status;\n  char error_message[127];  // always null-terminated\n};\n\n// Sent from handler to crash_dump via pipe.\nstruct __attribute__((__packed__)) CrashInfoHeader {\n  uint32_t version;\n};\n\nstruct __attribute__((__packed__)) CrashInfoDataStatic {\n  siginfo_t siginfo;\n  ucontext_t ucontext;\n  uintptr_t abort_msg_address;\n};\n\nstruct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataStatic {\n  uintptr_t fdsan_table_address;\n  uintptr_t gwp_asan_state;\n  uintptr_t gwp_asan_metadata;\n  uintptr_t scudo_stack_depot;\n  uintptr_t scudo_region_info;\n  uintptr_t scudo_ring_buffer;\n  size_t scudo_ring_buffer_size;\n  size_t scudo_stack_depot_size;\n  bool recoverable_crash;\n  uintptr_t crash_detail_page;\n};\n\nstruct __attribute__((__packed__)) CrashInfo {\n  CrashInfoHeader header;\n  union {\n    CrashInfoDataStatic s;\n    CrashInfoDataDynamic d;\n  } data;\n};\n"
  },
  {
    "path": "debuggerd/rust/tombstoned_client/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libtombstoned_client_wrapper\",\n    srcs: [\n        \"wrapper.cpp\",\n    ],\n    generated_sources: [\n        \"libtombstoned_client_rust_bridge_code\",\n    ],\n    header_libs: [\n        \"libbase_headers\",\n        \"libdebuggerd_common_headers\",\n    ],\n    shared_libs: [\n        \"libtombstoned_client\",\n    ],\n    apex_available: [\"com.android.virt\"],\n}\n\nrust_defaults {\n    name: \"libtombstoned_client_rust_defaults\",\n    crate_name: \"tombstoned_client\",\n    srcs: [\"src/lib.rs\"],\n    edition: \"2021\",\n    rustlibs: [\n        \"libcxx\",\n        \"libthiserror\",\n    ],\n    static_libs: [\n        \"libtombstoned_client_wrapper\",\n    ],\n    shared_libs: [\n        \"libtombstoned_client\",\n    ],\n}\n\nrust_library {\n    name: \"libtombstoned_client_rust\",\n    defaults: [\"libtombstoned_client_rust_defaults\"],\n    apex_available: [\"com.android.virt\"],\n}\n\nrust_test {\n    name: \"libtombstoned_client_rust_test\",\n    defaults: [\"libtombstoned_client_rust_defaults\"],\n    require_root: true,\n    test_suites: [\"device-tests\"],\n}\n\ngenrule {\n    name: \"libtombstoned_client_rust_bridge_code\",\n    tools: [\"cxxbridge\"],\n    cmd: \"$(location cxxbridge) $(in) >> $(out)\",\n    srcs: [\"src/lib.rs\"],\n    out: [\"libtombstoned_client_cxx_generated.cc\"],\n}\n"
  },
  {
    "path": "debuggerd/rust/tombstoned_client/src/lib.rs",
    "content": "// Copyright 2022, The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! Rust wrapper for tombstoned client.\n\npub use ffi::DebuggerdDumpType;\nuse std::fs::File;\nuse std::os::unix::io::{AsRawFd, FromRawFd, RawFd};\nuse thiserror::Error;\n\n/// Error communicating with tombstoned.\n#[derive(Clone, Debug, Error, Eq, PartialEq)]\n#[error(\"Error communicating with tombstoned\")]\npub struct Error;\n\n/// File descriptors for communicating with tombstoned.\npub struct TombstonedConnection {\n    /// The socket connection to tombstoned.\n    ///\n    /// This is actually a Unix SOCK_SEQPACKET socket not a file, but the Rust standard library\n    /// doesn't have an appropriate type and it's not really worth bringing in a dependency on `uds`\n    /// or something when all we do is pass it back to C++ or close it.\n    tombstoned_socket: File,\n    /// The file descriptor for text output.\n    pub text_output: Option<File>,\n    /// The file descriptor for proto output.\n    pub proto_output: Option<File>,\n}\n\nimpl TombstonedConnection {\n    /// # Safety\n    ///\n    /// The file descriptors must be valid and open.\n    unsafe fn from_raw_fds(\n        tombstoned_socket: RawFd,\n        text_output_fd: RawFd,\n        proto_output_fd: RawFd,\n    ) -> Self {\n        Self {\n            // SAFETY: The caller guarantees that the file descriptor is valid and open.\n            tombstoned_socket: unsafe { File::from_raw_fd(tombstoned_socket) },\n            text_output: if text_output_fd >= 0 {\n                // SAFETY: The caller guarantees that the file descriptor is valid and open.\n                Some(unsafe { File::from_raw_fd(text_output_fd) })\n            } else {\n                None\n            },\n            proto_output: if proto_output_fd >= 0 {\n                // SAFETY: The caller guarantees that the file descriptor is valid and open.\n                Some(unsafe { File::from_raw_fd(proto_output_fd) })\n            } else {\n                None\n            },\n        }\n    }\n\n    /// Connects to tombstoned.\n    pub fn connect(pid: i32, dump_type: DebuggerdDumpType) -> Result<Self, Error> {\n        let mut tombstoned_socket = -1;\n        let mut text_output_fd = -1;\n        let mut proto_output_fd = -1;\n        if ffi::tombstoned_connect_files(\n            pid,\n            &mut tombstoned_socket,\n            &mut text_output_fd,\n            &mut proto_output_fd,\n            dump_type,\n        ) {\n            // SAFETY: If tombstoned_connect_files returns successfully then they file descriptors\n            // are valid and open.\n            Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) })\n        } else {\n            Err(Error)\n        }\n    }\n\n    /// Notifies tombstoned that the dump is complete.\n    pub fn notify_completion(&self) -> Result<(), Error> {\n        if ffi::tombstoned_notify_completion(self.tombstoned_socket.as_raw_fd()) {\n            Ok(())\n        } else {\n            Err(Error)\n        }\n    }\n}\n\n#[cxx::bridge]\nmod ffi {\n    /// The type of dump.\n    enum DebuggerdDumpType {\n        /// A native backtrace.\n        #[cxx_name = \"kDebuggerdNativeBacktrace\"]\n        NativeBacktrace,\n        /// A tombstone.\n        #[cxx_name = \"kDebuggerdTombstone\"]\n        Tombstone,\n        /// A Java backtrace.\n        #[cxx_name = \"kDebuggerdJavaBacktrace\"]\n        JavaBacktrace,\n        /// Any intercept.\n        #[cxx_name = \"kDebuggerdAnyIntercept\"]\n        AnyIntercept,\n        /// A tombstone proto.\n        #[cxx_name = \"kDebuggerdTombstoneProto\"]\n        TombstoneProto,\n    }\n\n    unsafe extern \"C++\" {\n        include!(\"wrapper.hpp\");\n\n        type DebuggerdDumpType;\n\n        fn tombstoned_connect_files(\n            pid: i32,\n            tombstoned_socket: &mut i32,\n            text_output_fd: &mut i32,\n            proto_output_fd: &mut i32,\n            dump_type: DebuggerdDumpType,\n        ) -> bool;\n\n        fn tombstoned_notify_completion(tombstoned_socket: i32) -> bool;\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n    use std::{io::Write, process};\n\n    // Verify that we can connect to tombstoned, write something to the file descriptor it returns,\n    // and notify completion, without any errors.\n    #[test]\n    fn test() {\n        let connection =\n            TombstonedConnection::connect(process::id() as i32, DebuggerdDumpType::Tombstone)\n                .expect(\"Failed to connect to tombstoned.\");\n\n        assert!(connection.proto_output.is_none());\n        connection\n            .text_output\n            .as_ref()\n            .expect(\"No text output FD returned.\")\n            .write_all(b\"test data\")\n            .expect(\"Failed to write to text output FD.\");\n\n        connection.notify_completion().expect(\"Failed to notify completion.\");\n    }\n}\n"
  },
  {
    "path": "debuggerd/rust/tombstoned_client/wrapper.cpp",
    "content": "/*\n * Copyright 2022, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"wrapper.hpp\"\n\n#include <android-base/unique_fd.h>\n\n#include \"tombstoned/tombstoned.h\"\n\nusing android::base::unique_fd;\n\nbool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,\n                              int& proto_output_fd, DebuggerdDumpType dump_type) {\n  unique_fd tombstoned_socket_unique, text_output_unique, proto_output_unique;\n\n  bool result = tombstoned_connect(pid, &tombstoned_socket_unique, &text_output_unique,\n                                   &proto_output_unique, dump_type);\n  if (result) {\n    tombstoned_socket = tombstoned_socket_unique.release();\n    text_output_fd = text_output_unique.release();\n    proto_output_fd = proto_output_unique.release();\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "debuggerd/rust/tombstoned_client/wrapper.hpp",
    "content": "/*\n * Copyright 2022, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n#include \"tombstoned/tombstoned.h\"\n\nbool tombstoned_connect_files(pid_t pid, int& tombstoned_socket, int& text_output_fd,\n                              int& proto_output_fd, DebuggerdDumpType dump_type);\n"
  },
  {
    "path": "debuggerd/seccomp_policy/crash_dump.arm.policy",
    "content": "read: 1\nwrite: 1\nexit: 1\nrt_sigreturn: 1\nsigreturn: 1\nexit_group: 1\nclock_gettime: 1\ngettimeofday: 1\nfutex: 1\ngetrandom: 1\ngetpid: 1\ngettid: 1\nppoll: 1\npipe2: 1\nopenat: 1\ndup: 1\nclose: 1\nlseek: 1\ngetdents64: 1\nfaccessat: 1\nrecvmsg: 1\nrecvfrom: 1\nsetsockopt: 1\nsysinfo: 1\nprocess_vm_readv: 1\ntgkill: 1\nrt_sigprocmask: 1\nrt_sigaction: 1\nrt_tgsigqueueinfo: 1\nprctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41\nmadvise: 1\nmprotect: arg2 in 0x1|0x2\nmunmap: 1\ngetuid32: 1\nfstat64: 1\nmmap2: arg2 in 0x1|0x2\ngeteuid32: 1\ngetgid32: 1\ngetegid32: 1\ngetgroups32: 1\n"
  },
  {
    "path": "debuggerd/seccomp_policy/crash_dump.arm64.policy",
    "content": "read: 1\nwrite: 1\nexit: 1\nrt_sigreturn: 1\nexit_group: 1\nclock_gettime: 1\ngettimeofday: 1\nfutex: 1\ngetrandom: 1\ngetpid: 1\ngettid: 1\nppoll: 1\npipe2: 1\nopenat: 1\ndup: 1\nclose: 1\nlseek: 1\ngetdents64: 1\nfaccessat: 1\nrecvmsg: 1\nrecvfrom: 1\nsetsockopt: 1\nsysinfo: 1\nprocess_vm_readv: 1\ntgkill: 1\nrt_sigprocmask: 1\nrt_sigaction: 1\nrt_tgsigqueueinfo: 1\nprctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == 56 || arg0 == 61\nmadvise: 1\nmprotect: arg2 in 0x1|0x2|0x20\nmunmap: 1\ngetuid: 1\nfstat: 1\nmmap: arg2 in 0x1|0x2|0x20\ngeteuid: 1\ngetgid: 1\ngetegid: 1\ngetgroups: 1\n"
  },
  {
    "path": "debuggerd/seccomp_policy/crash_dump.policy.def",
    "content": "// SECCOMP_MODE_STRICT\nread: 1\nwrite: 1\nexit: 1\nrt_sigreturn: 1\n#if !defined(__LP64__)\nsigreturn: 1\n#endif\n\nexit_group: 1\nclock_gettime: 1\ngettimeofday: 1\nfutex: 1\ngetrandom: 1\ngetpid: 1\ngettid: 1\n\nppoll: 1\npipe2: 1\nopenat: 1\ndup: 1\nclose: 1\nlseek: 1\ngetdents64: 1\nfaccessat: 1\nrecvmsg: 1\nrecvfrom: 1\nsetsockopt: 1\nsysinfo: 1\n\nprocess_vm_readv: 1\n\ntgkill: 1\nrt_sigprocmask: 1\nrt_sigaction: 1\nrt_tgsigqueueinfo: 1\n\n// this is referenced from mainline modules running on Q devices, where not all\n// of the constants used here are defined in headers, so minijail rejects them.\n// we define them here to avoid those errors.\n        // constants introduced in R\n#define PR_SET_VMA 0x53564d41\n#define PR_GET_TAGGED_ADDR_CTRL 56\n        // constants introduced in S\n#define PR_PAC_GET_ENABLED_KEYS 61\n\n#if defined(__aarch64__)\n// PR_PAC_RESET_KEYS happens on aarch64 in pthread_create path.\nprctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS\n#else\nprctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA\n#endif\n\n#if 0\nlibminijail on vendor partitions older than P does not have constants from <sys/mman.h>.\nDefine values for PROT_READ, PROT_WRITE and PROT_MTE ourselves to maintain backwards compatibility.\n#else\n#define PROT_READ 0x1\n#define PROT_WRITE 0x2\n#define PROT_MTE 0x20\n#endif\n\nmadvise: 1\n#if defined(__aarch64__)\nmprotect: arg2 in PROT_READ|PROT_WRITE|PROT_MTE\n#else\nmprotect: arg2 in PROT_READ|PROT_WRITE\n#endif\nmunmap: 1\n\n#if defined(__LP64__)\ngetuid: 1\nfstat: 1\n#if defined(__aarch64__)\nmmap: arg2 in PROT_READ|PROT_WRITE|PROT_MTE\n#else\nmmap: arg2 in PROT_READ|PROT_WRITE\n#endif\n#else\ngetuid32: 1\nfstat64: 1\nmmap2: arg2 in PROT_READ|PROT_WRITE\n#endif\n\n// Needed for logging.\n#if defined(__LP64__)\ngeteuid: 1\ngetgid: 1\ngetegid: 1\ngetgroups: 1\n#else\ngeteuid32: 1\ngetgid32: 1\ngetegid32: 1\ngetgroups32: 1\n#endif\n"
  },
  {
    "path": "debuggerd/seccomp_policy/crash_dump.riscv64.policy",
    "content": "read: 1\nwrite: 1\nexit: 1\nrt_sigreturn: 1\nexit_group: 1\nclock_gettime: 1\ngettimeofday: 1\nfutex: 1\ngetrandom: 1\ngetpid: 1\ngettid: 1\nppoll: 1\npipe2: 1\nopenat: 1\ndup: 1\nclose: 1\nlseek: 1\ngetdents64: 1\nfaccessat: 1\nrecvmsg: 1\nrecvfrom: 1\nsetsockopt: 1\nsysinfo: 1\nprocess_vm_readv: 1\ntgkill: 1\nrt_sigprocmask: 1\nrt_sigaction: 1\nrt_tgsigqueueinfo: 1\nprctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41\nmadvise: 1\nmprotect: arg2 in 0x1|0x2\nmunmap: 1\ngetuid: 1\nfstat: 1\nmmap: arg2 in 0x1|0x2\ngeteuid: 1\ngetgid: 1\ngetegid: 1\ngetgroups: 1\n"
  },
  {
    "path": "debuggerd/seccomp_policy/crash_dump.x86.policy",
    "content": "read: 1\nwrite: 1\nexit: 1\nrt_sigreturn: 1\nsigreturn: 1\nexit_group: 1\nclock_gettime: 1\ngettimeofday: 1\nfutex: 1\ngetrandom: 1\ngetpid: 1\ngettid: 1\nppoll: 1\npipe2: 1\nopenat: 1\ndup: 1\nclose: 1\nlseek: 1\ngetdents64: 1\nfaccessat: 1\nrecvmsg: 1\nrecvfrom: 1\nsetsockopt: 1\nsysinfo: 1\nprocess_vm_readv: 1\ntgkill: 1\nrt_sigprocmask: 1\nrt_sigaction: 1\nrt_tgsigqueueinfo: 1\nprctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41\nmadvise: 1\nmprotect: arg2 in 0x1|0x2\nmunmap: 1\ngetuid32: 1\nfstat64: 1\nmmap2: arg2 in 0x1|0x2\ngeteuid32: 1\ngetgid32: 1\ngetegid32: 1\ngetgroups32: 1\n"
  },
  {
    "path": "debuggerd/seccomp_policy/crash_dump.x86_64.policy",
    "content": "read: 1\nwrite: 1\nexit: 1\nrt_sigreturn: 1\nexit_group: 1\nclock_gettime: 1\ngettimeofday: 1\nfutex: 1\ngetrandom: 1\ngetpid: 1\ngettid: 1\nppoll: 1\npipe2: 1\nopenat: 1\ndup: 1\nclose: 1\nlseek: 1\ngetdents64: 1\nfaccessat: 1\nrecvmsg: 1\nrecvfrom: 1\nsetsockopt: 1\nsysinfo: 1\nprocess_vm_readv: 1\ntgkill: 1\nrt_sigprocmask: 1\nrt_sigaction: 1\nrt_tgsigqueueinfo: 1\nprctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41\nmadvise: 1\nmprotect: arg2 in 0x1|0x2\nmunmap: 1\ngetuid: 1\nfstat: 1\nmmap: arg2 in 0x1|0x2\ngeteuid: 1\ngetgid: 1\ngetegid: 1\ngetgroups: 1\n"
  },
  {
    "path": "debuggerd/seccomp_policy/generate.sh",
    "content": "#!/bin/bash\n\nset -ex\n\ncd \"$(dirname \"$0\")\"\nCPP='cpp -undef -E -P crash_dump.policy.def'\n$CPP -D__arm__ -o crash_dump.arm.policy\n$CPP -D__aarch64__ -D__LP64__ -o crash_dump.arm64.policy\n$CPP -D__riscv -D__LP64__ -o crash_dump.riscv64.policy\n$CPP -D__i386__ -o crash_dump.x86.policy\n$CPP -D__x86_64__ -D__LP64__ -o crash_dump.x86_64.policy\n"
  },
  {
    "path": "debuggerd/test_permissive_mte/Android.bp",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"mte_crash\",\n    srcs: [\"mte_crash.cpp\"],\n    sanitize: {\n        memtag_heap: true,\n        diag: {\n            memtag_heap: true,\n        },\n    },\n}\n\njava_test_host {\n    name: \"permissive_mte_test\",\n    libs: [\"tradefed\"],\n    static_libs: [\n        \"frameworks-base-hostutils\",\n        \"cts-install-lib-host\",\n    ],\n    srcs: [\n        \"src/**/PermissiveMteTest.java\",\n        \":libtombstone_proto-src\",\n    ],\n    device_first_data: [\":mte_crash\"],\n    test_config: \"AndroidTest.xml\",\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "debuggerd/test_permissive_mte/AndroidTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2022 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Runs the permissive MTE tests\">\n    <option name=\"test-suite-tag\" value=\"init_test_upgrade_mte\" />\n    <option name=\"test-suite-tag\" value=\"apct\" />\n\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n      <option name=\"cleanup\" value=\"true\" />\n      <option name=\"push\" value=\"mte_crash->/data/local/tmp/mte_crash\" />\n    </target_preparer>\n    <test class=\"com.android.tradefed.testtype.HostTest\" >\n        <option name=\"jar\" value=\"permissive_mte_test.jar\" />\n    </test>\n</configuration>"
  },
  {
    "path": "debuggerd/test_permissive_mte/mte_crash.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n\nint main(int, char**) {\n  volatile char* f = (char*)malloc(1);\n  printf(\"%c\\n\", f[17]);\n#ifdef __aarch64__\n  if (getenv(\"MTE_PERMISSIVE_REENABLE_TIME_CPUMS\")) {\n    // Burn some cycles because the MTE_PERMISSIVE_REENABLE_TIME_CPUMS is based on CPU clock.\n    for (int i = 0; i < 1000000000; ++i) {\n      asm(\"isb\");\n    }\n    printf(\"%c\\n\", f[17]);\n  }\n#endif\n  return 0;\n}\n"
  },
  {
    "path": "debuggerd/test_permissive_mte/src/com/android/tests/debuggerd/PermissiveMteTest.java",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.tests.init;\n\nimport static com.google.common.truth.Truth.assertThat;\nimport static org.junit.Assume.assumeTrue;\n\nimport com.android.server.os.TombstoneProtos.Tombstone;\nimport com.android.tradefed.testtype.DeviceJUnit4ClassRunner;\nimport com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;\nimport com.android.tradefed.util.CommandResult;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.util.Arrays;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n@RunWith(DeviceJUnit4ClassRunner.class)\npublic class PermissiveMteTest extends BaseHostJUnit4Test {\n  String mUUID;\n\n  @Before\n  public void setUp() throws Exception {\n    mUUID = java.util.UUID.randomUUID().toString();\n    CommandResult result =\n        getDevice().executeShellV2Command(\"/data/local/tmp/mte_crash setUp \" + mUUID);\n    assumeTrue(\"mte_crash needs to segfault\", result.getExitCode() == 139);\n  }\n\n  Tombstone parseTombstone(String tombstonePath) throws Exception {\n    File tombstoneFile = getDevice().pullFile(tombstonePath);\n    InputStream istr = new FileInputStream(tombstoneFile);\n    Tombstone tombstoneProto;\n    try {\n      tombstoneProto = Tombstone.parseFrom(istr);\n    } finally {\n      istr.close();\n    }\n    return tombstoneProto;\n  }\n\n  @After\n  public void tearDown() throws Exception {\n    String[] tombstones = getDevice().getChildren(\"/data/tombstones\");\n    for (String tombstone : tombstones) {\n      if (!tombstone.endsWith(\".pb\")) {\n        continue;\n      }\n      String tombstonePath = \"/data/tombstones/\" + tombstone;\n      Tombstone tombstoneProto = parseTombstone(tombstonePath);\n      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {\n        continue;\n      }\n      getDevice().deleteFile(tombstonePath);\n      // remove the non .pb file as well.\n      getDevice().deleteFile(tombstonePath.substring(0, tombstonePath.length() - 3));\n    }\n  }\n\n  @Test\n  public void testCrash() throws Exception {\n    CommandResult result = getDevice().executeShellV2Command(\n        \"MTE_PERMISSIVE=1 /data/local/tmp/mte_crash testCrash \" + mUUID);\n    assertThat(result.getExitCode()).isEqualTo(0);\n    int numberTombstones = 0;\n    String[] tombstones = getDevice().getChildren(\"/data/tombstones\");\n    for (String tombstone : tombstones) {\n      if (!tombstone.endsWith(\".pb\")) {\n        continue;\n      }\n      String tombstonePath = \"/data/tombstones/\" + tombstone;\n      Tombstone tombstoneProto = parseTombstone(tombstonePath);\n      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {\n        continue;\n      }\n      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(\"testCrash\"))) {\n        continue;\n      }\n      numberTombstones++;\n    }\n    assertThat(numberTombstones).isEqualTo(1);\n  }\n\n  @Test\n  public void testReenableCrash() throws Exception {\n    CommandResult result =\n        getDevice().executeShellV2Command(\"MTE_PERMISSIVE=1 MTE_PERMISSIVE_REENABLE_TIME_CPUMS=1 \"\n                                          + \"/data/local/tmp/mte_crash testReenableCrash \"\n                                          + mUUID);\n    assertThat(result.getExitCode()).isEqualTo(0);\n    int numberTombstones = 0;\n    String[] tombstones = getDevice().getChildren(\"/data/tombstones\");\n    for (String tombstone : tombstones) {\n      if (!tombstone.endsWith(\".pb\")) {\n        continue;\n      }\n      String tombstonePath = \"/data/tombstones/\" + tombstone;\n      Tombstone tombstoneProto = parseTombstone(tombstonePath);\n      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {\n        continue;\n      }\n      if (!tombstoneProto.getCommandLineList().stream().anyMatch(\n              x -> x.contains(\"testReenableCrash\"))) {\n        continue;\n      }\n      numberTombstones++;\n    }\n    assertThat(numberTombstones).isEqualTo(2);\n  }\n\n  @Test\n  public void testCrashProperty() throws Exception {\n    String prevValue = getDevice().getProperty(\"persist.sys.mte.permissive\");\n    if (prevValue == null) {\n      prevValue = \"\";\n    }\n    assertThat(getDevice().setProperty(\"persist.sys.mte.permissive\", \"1\")).isTrue();\n    CommandResult result =\n        getDevice().executeShellV2Command(\"/data/local/tmp/mte_crash testCrash \" + mUUID);\n    assertThat(result.getExitCode()).isEqualTo(0);\n    int numberTombstones = 0;\n    String[] tombstones = getDevice().getChildren(\"/data/tombstones\");\n    for (String tombstone : tombstones) {\n      if (!tombstone.endsWith(\".pb\")) {\n        continue;\n      }\n      String tombstonePath = \"/data/tombstones/\" + tombstone;\n      Tombstone tombstoneProto = parseTombstone(tombstonePath);\n      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(mUUID))) {\n        continue;\n      }\n      if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(\"testCrash\"))) {\n        continue;\n      }\n      numberTombstones++;\n    }\n    assertThat(numberTombstones).isEqualTo(1);\n    assertThat(getDevice().setProperty(\"persist.sys.mte.permissive\", prevValue)).isTrue();\n  }\n}\n"
  },
  {
    "path": "debuggerd/tombstone_handler.cpp",
    "content": "/*\n * Copyright 2023, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"tombstoned/tombstoned.h\"\n\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n#include <cutils/sockets.h>\n#include <linux/vm_sockets.h>\n#include \"util.h\"\n\nusing android::base::unique_fd;\n\n/*\n  Port number that VirtualMachineService listens on connections from the guest VMs.\n  Kep in sync with IVirtualMachineService.aidl\n*/\nconst unsigned int VM_TOMBSTONES_SERVICE_PORT = 2000;\n\nstatic bool is_microdroid() {\n  return android::base::GetProperty(\"ro.hardware\", \"\") == \"microdroid\";\n}\n\nstatic bool connect_tombstone_server_microdroid(unique_fd* text_output_fd,\n                                                unique_fd* proto_output_fd,\n                                                DebuggerdDumpType dump_type) {\n  // We do not wait for the property to be set, the default behaviour is not export tombstones.\n  if (!android::base::GetBoolProperty(\"microdroid_manager.export_tombstones.enabled\", false)) {\n    LOG(WARNING) << \"exporting tombstones is not enabled\";\n    return false;\n  }\n\n  // Microdroid supports handling requests originating from crash_dump which\n  // supports limited dump types. Java traces and incept management are not supported.\n  switch (dump_type) {\n    case kDebuggerdNativeBacktrace:\n    case kDebuggerdTombstone:\n    case kDebuggerdTombstoneProto:\n      break;\n\n    default:\n      LOG(WARNING) << \"Requested dump type: \" << dump_type << \" \"\n                   << \"not supported\";\n  }\n\n  int fd1 = TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM, 0));\n  int fd2 = TEMP_FAILURE_RETRY(socket(AF_VSOCK, SOCK_STREAM, 0));\n  if (fd1 < 0 || fd2 < 0) {\n    LOG(WARNING) << \"Unable to create virtual socket for writing tombstones\";\n    return false;\n  }\n\n  unique_fd vsock_output_fd(fd1), vsock_proto_fd(fd2);\n\n  struct sockaddr_vm sa = (struct sockaddr_vm){\n      .svm_family = AF_VSOCK,\n      .svm_port = VM_TOMBSTONES_SERVICE_PORT,\n      .svm_cid = VMADDR_CID_HOST,\n  };\n\n  if (TEMP_FAILURE_RETRY(connect(vsock_output_fd, (struct sockaddr*)&sa, sizeof(sa))) < 0) {\n    PLOG(WARNING) << \"Unable to connect to tombstone service in host\";\n    return false;\n  }\n\n  if (dump_type == kDebuggerdTombstoneProto) {\n    if (TEMP_FAILURE_RETRY(connect(vsock_proto_fd, (struct sockaddr*)&sa, sizeof(sa))) < 0) {\n      PLOG(WARNING) << \"Unable to connect to tombstone service in host\";\n      return false;\n    }\n  }\n\n  *text_output_fd = std::move(vsock_output_fd);\n  if (proto_output_fd) {\n    *proto_output_fd = std::move(vsock_proto_fd);\n  }\n  return true;\n}\n\nstatic bool notify_completion_microdroid(int vsock_out, int vsock_proto) {\n  if (shutdown(vsock_out, SHUT_WR) || shutdown(vsock_proto, SHUT_WR)) return false;\n  return true;\n}\nbool connect_tombstone_server(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text_output_fd,\n                              unique_fd* proto_output_fd, DebuggerdDumpType dump_type) {\n  if (is_microdroid()) {\n    return connect_tombstone_server_microdroid(text_output_fd, proto_output_fd, dump_type);\n  }\n  return tombstoned_connect(pid, tombstoned_socket, text_output_fd, proto_output_fd, dump_type);\n}\n\nbool notify_completion(int tombstoned_socket, int vsock_out, int vsock_proto) {\n  if (is_microdroid()) {\n    return notify_completion_microdroid(vsock_out, vsock_proto);\n  }\n  return tombstoned_notify_completion(tombstoned_socket);\n}\n"
  },
  {
    "path": "debuggerd/tombstone_handler.h",
    "content": "/*\n * Copyright 2023, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/unique_fd.h>\n#include \"dump_type.h\"\n\nbool connect_tombstone_server(pid_t pid, android::base::unique_fd* tombstoned_socket,\n                              android::base::unique_fd* text_output_fd,\n                              android::base::unique_fd* proto_output_fd,\n                              DebuggerdDumpType dump_type);\n\nbool notify_completion(int tombstoned_socket, int vsock_out, int vsock_proto);\n"
  },
  {
    "path": "debuggerd/tombstone_symbolize.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"tombstone_symbolize.h\"\n\n#include <fcntl.h>\n#include <inttypes.h>\n#include <unistd.h>\n\n#include <string>\n#include <vector>\n\n#include \"android-base/stringprintf.h\"\n#include \"android-base/unique_fd.h\"\n\n#include \"tombstone.pb.h\"\n\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\n\nbool Symbolizer::Start(const std::vector<std::string>& debug_file_directories) {\n  unique_fd parent_in, parent_out, child_in, child_out;\n  if (!Pipe(&parent_in, &child_out) || !Pipe(&child_in, &parent_out)) {\n    return false;\n  }\n\n  std::vector<const char *> args;\n  args.push_back(\"llvm-symbolizer\");\n  for (const std::string &dir : debug_file_directories) {\n    args.push_back(\"--debug-file-directory\");\n    args.push_back(dir.c_str());\n  }\n  args.push_back(0);\n\n  int pid = fork();\n  if (pid == -1) {\n    return false;\n  } else if (pid == 0) {\n    parent_in.reset();\n    parent_out.reset();\n\n    dup2(child_in.get(), STDIN_FILENO);\n    dup2(child_out.get(), STDOUT_FILENO);\n\n    execvp(\"llvm-symbolizer\", const_cast<char *const *>(args.data()));\n\n    fprintf(stderr, \"unable to start llvm-symbolizer: %s\\n\", strerror(errno));\n    _exit(1);\n  } else {\n    child_in.reset();\n    child_out.reset();\n\n    // TODO: Check that llvm-symbolizer started up successfully.\n    // There used to be an easy way to do this, but it was removed in:\n    // https://github.com/llvm/llvm-project/commit/1792852f86dc75efa1f44d46b1a0daf386d64afa\n\n    in_fd = std::move(parent_in);\n    out_fd = std::move(parent_out);\n    return true;\n  }\n}\n\nstd::string Symbolizer::read_response() {\n  std::string resp;\n\n  while (resp.size() < 2 || resp[resp.size() - 2] != '\\n' || resp[resp.size() - 1] != '\\n') {\n    char buf[4096];\n    ssize_t size = read(in_fd, buf, 4096);\n    if (size <= 0) {\n      return \"\";\n    }\n    resp.append(buf, size);\n  }\n\n  return resp;\n}\n\nstd::vector<Symbolizer::Frame> Symbolizer::SymbolizeCode(std::string path, uint64_t rel_pc) {\n  std::string request = StringPrintf(\"CODE %s 0x%\" PRIx64 \"\\n\", path.c_str(), rel_pc);\n  if (write(out_fd, request.c_str(), request.size()) != static_cast<ssize_t>(request.size())) {\n    return {};\n  }\n\n  std::string response = read_response();\n  if (response.empty()) {\n    return {};\n  }\n\n  std::vector<Symbolizer::Frame> frames;\n\n  size_t frame_start = 0;\n  while (frame_start < response.size() - 1) {\n    Symbolizer::Frame frame;\n\n    size_t second_line_start = response.find('\\n', frame_start) + 1;\n    if (second_line_start == std::string::npos + 1) {\n      return {};\n    }\n\n    size_t third_line_start = response.find('\\n', second_line_start) + 1;\n    if (third_line_start == std::string::npos + 1) {\n      return {};\n    }\n\n    frame.function_name = response.substr(frame_start, second_line_start - frame_start - 1);\n\n    size_t column_number_start = response.rfind(':', third_line_start);\n    if (column_number_start == std::string::npos) {\n      return {};\n    }\n\n    size_t line_number_start = response.rfind(':', column_number_start - 1);\n    if (line_number_start == std::string::npos) {\n      return {};\n    }\n\n    frame.file = response.substr(second_line_start, line_number_start - second_line_start);\n\n    errno = 0;\n    frame.line = strtoull(response.c_str() + line_number_start + 1, 0, 10);\n    frame.column = strtoull(response.c_str() + column_number_start + 1, 0, 10);\n    if (errno != 0) {\n      return {};\n    }\n\n    frames.push_back(frame);\n\n    frame_start = third_line_start;\n  }\n\n  if (frames.size() == 1 && frames[0].file == \"??\") {\n    return {};\n  }\n\n  return frames;\n}\n\nvoid symbolize_backtrace_frame(const BacktraceFrame& frame, Symbolizer& sym) {\n  if (frame.build_id().empty()) {\n    return;\n  }\n\n  for (Symbolizer::Frame f : sym.SymbolizeCode(\"BUILDID:\" + frame.build_id(), frame.rel_pc())) {\n    printf(\"          %s:%\" PRId64 \":%\" PRId64 \" (%s)\\n\", f.file.c_str(), f.line, f.column,\n           f.function_name.c_str());\n  }\n}\n"
  },
  {
    "path": "debuggerd/tombstone_symbolize.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"android-base/unique_fd.h\"\n\nclass BacktraceFrame;\n\nclass Symbolizer {\n  android::base::unique_fd in_fd, out_fd;\n\n  std::string read_response();\n\n public:\n  bool Start(const std::vector<std::string>& debug_file_directories);\n\n  struct Frame {\n    std::string function_name, file;\n    uint64_t line, column;\n  };\n\n  std::vector<Frame> SymbolizeCode(std::string path, uint64_t rel_pc);\n};\n\nvoid symbolize_backtrace_frame(const BacktraceFrame& frame, Symbolizer& sym);\n"
  },
  {
    "path": "debuggerd/tombstoned/include/tombstoned/tombstoned.h",
    "content": "#pragma once\n\n/*\n * Copyright 2017, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/types.h>\n\n#include <android-base/unique_fd.h>\n\n#include \"dump_type.h\"\n\nbool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,\n                        android::base::unique_fd* text_output_fd,\n                        android::base::unique_fd* proto_output_fd, DebuggerdDumpType dump_type);\n\nbool tombstoned_connect(pid_t pid, android::base::unique_fd* tombstoned_socket,\n                        android::base::unique_fd* text_output_fd, DebuggerdDumpType dump_type);\n\nbool tombstoned_notify_completion(int tombstoned_socket);\n"
  },
  {
    "path": "debuggerd/tombstoned/intercept_manager.cpp",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"intercept_manager.h\"\n\n#include <inttypes.h>\n#include <sys/types.h>\n\n#include <limits>\n#include <memory>\n#include <unordered_map>\n#include <utility>\n\n#include <event2/event.h>\n#include <event2/listener.h>\n\n#include <android-base/cmsg.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n\n#include \"protocol.h\"\n#include \"util.h\"\n\nusing android::base::ReceiveFileDescriptors;\nusing android::base::unique_fd;\n\nstatic void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {\n  std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));\n\n  CHECK_EQ(sockfd, intercept->sockfd.get());\n\n  // If we can read, either we received unexpected data from the other side, or the other side\n  // closed their end of the socket. Either way, kill the intercept.\n\n  // Ownership of intercept differs based on whether we've registered it with InterceptManager.\n  if (!intercept->registered) {\n    LOG(WARNING) << \"intercept for pid \" << intercept->pid << \" and type \" << intercept->dump_type\n                 << \" closed before being registered.\";\n    return;\n  }\n\n  const char* reason = (event & EV_TIMEOUT) ? \"due to timeout\" : \"due to input\";\n  LOG(INFO) << \"intercept for pid \" << intercept->pid << \" and type \" << intercept->dump_type\n            << \" terminated: \" << reason;\n}\n\nvoid InterceptManager::Unregister(Intercept* intercept) {\n  CHECK(intercept->registered);\n  auto pid_entry = intercepts.find(intercept->pid);\n  if (pid_entry == intercepts.end()) {\n    LOG(FATAL) << \"No intercepts found for pid \" << intercept->pid;\n  }\n  auto& dump_type_hash = pid_entry->second;\n  auto dump_type_entry = dump_type_hash.find(intercept->dump_type);\n  if (dump_type_entry == dump_type_hash.end()) {\n    LOG(FATAL) << \"Unknown intercept \" << intercept->pid << \" \" << intercept->dump_type;\n  }\n  if (intercept != dump_type_entry->second) {\n    LOG(FATAL) << \"Mismatch pointer trying to unregister intercept \" << intercept->pid << \" \"\n               << intercept->dump_type;\n  }\n\n  dump_type_hash.erase(dump_type_entry);\n  if (dump_type_hash.empty()) {\n    intercepts.erase(pid_entry);\n  }\n}\n\nstatic void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {\n  std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));\n  InterceptManager* intercept_manager = intercept->intercept_manager;\n\n  CHECK_EQ(sockfd, intercept->sockfd.get());\n\n  if ((ev & EV_TIMEOUT) != 0) {\n    LOG(WARNING) << \"tombstoned didn't receive InterceptRequest before timeout\";\n    return;\n  } else if ((ev & EV_READ) == 0) {\n    LOG(WARNING) << \"tombstoned received unexpected event on intercept socket\";\n    return;\n  }\n\n  unique_fd rcv_fd;\n  InterceptRequest intercept_request;\n  ssize_t result =\n      ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);\n\n  if (result == -1) {\n    PLOG(WARNING) << \"failed to read from intercept socket\";\n    return;\n  }\n  if (result != sizeof(intercept_request)) {\n    LOG(WARNING) << \"intercept socket received short read of length \" << result << \" (expected \"\n                 << sizeof(intercept_request) << \")\";\n    return;\n  }\n\n  // Move the received FD to the upper half, in order to more easily notice FD leaks.\n  int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);\n  if (moved_fd == -1) {\n    LOG(WARNING) << \"failed to move received fd (\" << rcv_fd.get() << \")\";\n    return;\n  }\n  rcv_fd.reset(moved_fd);\n\n  // See if we can properly register the intercept.\n  InterceptResponse response = {};\n  if (!intercept_manager->CanRegister(intercept_request, response)) {\n    TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));\n    LOG(WARNING) << response.error_message;\n    return;\n  }\n\n  // Let the other side know that the intercept has been registered, now that we know we can't\n  // fail. tombstoned is single threaded, so this isn't racy.\n  response.status = InterceptStatus::kRegistered;\n  if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {\n    PLOG(WARNING) << \"failed to notify interceptor of registration\";\n    return;\n  }\n\n  intercept->pid = intercept_request.pid;\n  intercept->dump_type = intercept_request.dump_type;\n  intercept->output_fd = std::move(rcv_fd);\n  intercept_manager->Register(intercept.get());\n\n  LOG(INFO) << \"registered intercept for pid \" << intercept_request.pid << \" and type \"\n            << intercept_request.dump_type;\n\n  // Register a different read event on the socket so that we can remove intercepts if the socket\n  // closes (e.g. if a user CTRL-C's the process that requested the intercept).\n  event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,\n               intercept_close_cb, arg);\n\n  // If no request comes in, then this will close the intercept and free the pointer.\n  struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};\n  event_add(intercept->intercept_event, &timeout);\n  intercept.release();\n}\n\nstatic void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,\n                                void* arg) {\n  Intercept* intercept = new Intercept();\n  intercept->intercept_manager = static_cast<InterceptManager*>(arg);\n  intercept->sockfd.reset(sockfd);\n\n  struct timeval timeout = {1 * android::base::HwTimeoutMultiplier(), 0};\n  event_base* base = evconnlistener_get_base(listener);\n  event* intercept_event =\n    event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);\n  intercept->intercept_event = intercept_event;\n  event_add(intercept_event, &timeout);\n}\n\nIntercept::~Intercept() {\n  event_free(intercept_event);\n  if (registered) {\n    CHECK(intercept_manager != nullptr);\n    intercept_manager->Unregister(this);\n  }\n}\n\nInterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {\n  this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,\n                                      /* backlog */ -1, intercept_socket);\n}\n\nstatic DebuggerdDumpType canonical_dump_type(const DebuggerdDumpType dump_type) {\n  // kDebuggerdTombstone and kDebuggerdTombstoneProto should be treated as\n  // a single dump_type for intercepts (kDebuggerdTombstone).\n  if (dump_type == kDebuggerdTombstoneProto) {\n    return kDebuggerdTombstone;\n  }\n  return dump_type;\n}\n\nIntercept* InterceptManager::Get(const pid_t pid, const DebuggerdDumpType dump_type) {\n  auto pid_entry = intercepts.find(pid);\n  if (pid_entry == intercepts.end()) {\n    return nullptr;\n  }\n\n  const auto& dump_type_hash = pid_entry->second;\n  auto dump_type_entry = dump_type_hash.find(canonical_dump_type(dump_type));\n  if (dump_type_entry == dump_type_hash.end()) {\n    if (dump_type != kDebuggerdAnyIntercept) {\n      return nullptr;\n    }\n    // If doing a dump with an any intercept, only allow an any to match\n    // a single intercept. If there are multiple dump types with intercepts\n    // then there would be no way to figure out which to use.\n    if (dump_type_hash.size() != 1) {\n      LOG(WARNING) << \"Cannot intercept using kDebuggerdAnyIntercept: there is more than one \"\n                      \"intercept registered for pid \"\n                   << pid;\n      return nullptr;\n    }\n    dump_type_entry = dump_type_hash.begin();\n  }\n  return dump_type_entry->second;\n}\n\nbool InterceptManager::CanRegister(const InterceptRequest& request, InterceptResponse& response) {\n  if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {\n    response.status = InterceptStatus::kFailed;\n    snprintf(response.error_message, sizeof(response.error_message),\n             \"invalid intercept request: bad pid %\" PRId32, request.pid);\n    return false;\n  }\n  if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {\n    response.status = InterceptStatus::kFailed;\n    snprintf(response.error_message, sizeof(response.error_message),\n             \"invalid intercept request: bad dump type %s\", get_dump_type_name(request.dump_type));\n    return false;\n  }\n\n  if (Get(request.pid, request.dump_type) != nullptr) {\n    response.status = InterceptStatus::kFailedAlreadyRegistered;\n    snprintf(response.error_message, sizeof(response.error_message),\n             \"pid %\" PRId32 \" already registered, type %s\", request.pid,\n             get_dump_type_name(request.dump_type));\n    return false;\n  }\n\n  return true;\n}\n\nvoid InterceptManager::Register(Intercept* intercept) {\n  CHECK(!intercept->registered);\n  auto& dump_type_hash = intercepts[intercept->pid];\n  dump_type_hash[canonical_dump_type(intercept->dump_type)] = intercept;\n  intercept->registered = true;\n}\n\nbool InterceptManager::FindIntercept(pid_t pid, DebuggerdDumpType dump_type,\n                                     android::base::unique_fd* out_fd) {\n  Intercept* intercept = Get(pid, dump_type);\n  if (intercept == nullptr) {\n    return false;\n  }\n\n  if (dump_type != intercept->dump_type) {\n    LOG(INFO) << \"found registered intercept of type \" << intercept->dump_type\n              << \" for requested type \" << dump_type;\n  }\n\n  LOG(INFO) << \"found intercept fd \" << intercept->output_fd.get() << \" for pid \" << pid\n            << \" and type \" << intercept->dump_type;\n  InterceptResponse response = {};\n  response.status = InterceptStatus::kStarted;\n  TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));\n  *out_fd = std::move(intercept->output_fd);\n\n  // Delete the intercept data, which will unregister the intercept and remove the timeout event.\n  delete intercept;\n\n  return true;\n}\n"
  },
  {
    "path": "debuggerd/tombstoned/intercept_manager.h",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <unordered_map>\n\n#include <event2/event.h>\n#include <event2/listener.h>\n\n#include <android-base/unique_fd.h>\n\n#include \"dump_type.h\"\n\nstruct InterceptManager;\nstruct InterceptRequest;\nstruct InterceptResponse;\n\nstruct Intercept {\n  ~Intercept();\n\n  InterceptManager* intercept_manager = nullptr;\n  event* intercept_event = nullptr;\n  android::base::unique_fd sockfd;\n\n  pid_t pid = -1;\n  android::base::unique_fd output_fd;\n  bool registered = false;\n  DebuggerdDumpType dump_type = kDebuggerdNativeBacktrace;\n};\n\nstruct InterceptManager {\n  event_base* base;\n  std::unordered_map<pid_t, std::unordered_map<DebuggerdDumpType, Intercept*>> intercepts;\n  evconnlistener* listener = nullptr;\n\n  InterceptManager(event_base* _Nonnull base, int intercept_socket);\n  InterceptManager(InterceptManager& copy) = delete;\n  InterceptManager(InterceptManager&& move) = delete;\n\n  bool CanRegister(const InterceptRequest& request, InterceptResponse& response);\n  Intercept* Get(const pid_t pid, const DebuggerdDumpType dump_type);\n  void Register(Intercept* intercept);\n  void Unregister(Intercept* intercept);\n\n  bool FindIntercept(pid_t pid, DebuggerdDumpType dump_type, android::base::unique_fd* out_fd);\n};\n"
  },
  {
    "path": "debuggerd/tombstoned/tombstoned.cpp",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <array>\n#include <deque>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include <event2/event.h>\n#include <event2/listener.h>\n#include <event2/thread.h>\n\n#include <android-base/cmsg.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n#include <cutils/sockets.h>\n\n#include \"debuggerd/handler.h\"\n#include \"dump_type.h\"\n#include \"protocol.h\"\n#include \"util.h\"\n\n#include \"intercept_manager.h\"\n\nusing android::base::GetIntProperty;\nusing android::base::SendFileDescriptors;\nusing android::base::StringPrintf;\n\nusing android::base::borrowed_fd;\nusing android::base::unique_fd;\n\nstatic InterceptManager* intercept_manager;\n\nenum CrashStatus {\n  kCrashStatusRunning,\n  kCrashStatusQueued,\n};\n\nstruct CrashArtifact {\n  unique_fd fd;\n\n  static CrashArtifact devnull() {\n    CrashArtifact result;\n    result.fd.reset(open(\"/dev/null\", O_WRONLY | O_CLOEXEC));\n    return result;\n  }\n};\n\nstruct CrashArtifactPaths {\n  std::string text;\n  std::optional<std::string> proto;\n};\n\nstruct CrashOutput {\n  CrashArtifact text;\n  std::optional<CrashArtifact> proto;\n};\n\n// Ownership of Crash is a bit messy.\n// It's either owned by an active event that must have a timeout, or owned by\n// queued_requests, in the case that multiple crashes come in at the same time.\nstruct Crash {\n  ~Crash() { event_free(crash_event); }\n\n  CrashOutput output;\n  unique_fd crash_socket_fd;\n  pid_t crash_pid;\n  event* crash_event = nullptr;\n\n  DebuggerdDumpType crash_type;\n};\n\nclass CrashQueue {\n public:\n  CrashQueue(const std::string& dir_path, const std::string& file_name_prefix, size_t max_artifacts,\n             size_t max_concurrent_dumps, bool supports_proto, bool world_readable)\n      : file_name_prefix_(file_name_prefix),\n        dir_path_(dir_path),\n        dir_fd_(open(dir_path.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)),\n        max_artifacts_(max_artifacts),\n        next_artifact_(0),\n        max_concurrent_dumps_(max_concurrent_dumps),\n        num_concurrent_dumps_(0),\n        supports_proto_(supports_proto),\n        world_readable_(world_readable) {\n    if (dir_fd_ == -1) {\n      PLOG(FATAL) << \"failed to open directory: \" << dir_path;\n    }\n\n    // NOTE: If max_artifacts_ <= max_concurrent_dumps_, then theoretically the\n    // same filename could be handed out to multiple processes.\n    CHECK(max_artifacts_ > max_concurrent_dumps_);\n\n    find_oldest_artifact();\n  }\n\n  static CrashQueue* for_crash(const Crash* crash) {\n    return (crash->crash_type == kDebuggerdJavaBacktrace) ? for_anrs() : for_tombstones();\n  }\n\n  static CrashQueue* for_crash(const std::unique_ptr<Crash>& crash) {\n    return for_crash(crash.get());\n  }\n\n  static CrashQueue* for_tombstones() {\n    static CrashQueue queue(\"/data/tombstones\", \"tombstone_\" /* file_name_prefix */,\n                            GetIntProperty(\"tombstoned.max_tombstone_count\", 32),\n                            1 /* max_concurrent_dumps */, true /* supports_proto */,\n                            true /* world_readable */);\n    return &queue;\n  }\n\n  static CrashQueue* for_anrs() {\n    static CrashQueue queue(\"/data/anr\", \"trace_\" /* file_name_prefix */,\n                            GetIntProperty(\"tombstoned.max_anr_count\", 64),\n                            4 /* max_concurrent_dumps */, false /* supports_proto */,\n                            false /* world_readable */);\n    return &queue;\n  }\n\n  CrashArtifact create_temporary_file() const {\n    CrashArtifact result;\n\n    result.fd.reset(openat(dir_fd_, \".\", O_WRONLY | O_APPEND | O_TMPFILE | O_CLOEXEC, 0660));\n    if (result.fd == -1) {\n      PLOG(FATAL) << \"failed to create temporary tombstone in \" << dir_path_;\n    }\n\n    if (world_readable_) {\n      // We need to fchmodat after creating to avoid getting the umask applied.\n      std::string fd_path = StringPrintf(\"/proc/self/fd/%d\", result.fd.get());\n      if (fchmodat(dir_fd_, fd_path.c_str(), 0664, 0) != 0) {\n        PLOG(ERROR) << \"Failed to make tombstone world-readable\";\n      }\n    }\n\n    return result;\n  }\n\n  std::optional<CrashOutput> get_output(DebuggerdDumpType dump_type) {\n    CrashOutput result;\n\n    switch (dump_type) {\n      case kDebuggerdNativeBacktrace:\n        // Don't generate tombstones for native backtrace requests.\n        return {};\n\n      case kDebuggerdTombstoneProto:\n        if (!supports_proto_) {\n          LOG(ERROR) << \"received kDebuggerdTombstoneProto on a queue that doesn't support proto\";\n          return {};\n        }\n        result.proto = create_temporary_file();\n        result.text = create_temporary_file();\n        break;\n\n      case kDebuggerdJavaBacktrace:\n      case kDebuggerdTombstone:\n        result.text = create_temporary_file();\n        break;\n\n      default:\n        LOG(ERROR) << \"unexpected dump type: \" << dump_type;\n        return {};\n    }\n\n    return result;\n  }\n\n  borrowed_fd dir_fd() { return dir_fd_; }\n\n  CrashArtifactPaths get_next_artifact_paths() {\n    CrashArtifactPaths result;\n    result.text = StringPrintf(\"%s%02d\", file_name_prefix_.c_str(), next_artifact_);\n\n    if (supports_proto_) {\n      result.proto = StringPrintf(\"%s%02d.pb\", file_name_prefix_.c_str(), next_artifact_);\n    }\n\n    next_artifact_ = (next_artifact_ + 1) % max_artifacts_;\n    return result;\n  }\n\n  // Consumes crash if it returns true, otherwise leaves it untouched.\n  bool maybe_enqueue_crash(std::unique_ptr<Crash>&& crash) {\n    if (num_concurrent_dumps_ == max_concurrent_dumps_) {\n      queued_requests_.emplace_back(std::move(crash));\n      return true;\n    }\n\n    return false;\n  }\n\n  void maybe_dequeue_crashes(void (*handler)(std::unique_ptr<Crash> crash)) {\n    while (!queued_requests_.empty() && num_concurrent_dumps_ < max_concurrent_dumps_) {\n      std::unique_ptr<Crash> next_crash = std::move(queued_requests_.front());\n      queued_requests_.pop_front();\n      handler(std::move(next_crash));\n    }\n  }\n\n  void on_crash_started() { ++num_concurrent_dumps_; }\n\n  void on_crash_completed() { --num_concurrent_dumps_; }\n\n private:\n  void find_oldest_artifact() {\n    size_t oldest_tombstone = 0;\n    time_t oldest_time = std::numeric_limits<time_t>::max();\n\n    for (size_t i = 0; i < max_artifacts_; ++i) {\n      std::string path =\n          StringPrintf(\"%s/%s%02zu\", dir_path_.c_str(), file_name_prefix_.c_str(), i);\n      struct stat st;\n      if (stat(path.c_str(), &st) != 0) {\n        if (errno == ENOENT) {\n          oldest_tombstone = i;\n          break;\n        } else {\n          PLOG(ERROR) << \"failed to stat \" << path;\n          continue;\n        }\n      }\n\n      if (st.st_mtime < oldest_time) {\n        oldest_tombstone = i;\n        oldest_time = st.st_mtime;\n      }\n    }\n\n    next_artifact_ = oldest_tombstone;\n  }\n\n  const std::string file_name_prefix_;\n\n  const std::string dir_path_;\n  const int dir_fd_;\n\n  const size_t max_artifacts_;\n  int next_artifact_;\n\n  const size_t max_concurrent_dumps_;\n  size_t num_concurrent_dumps_;\n\n  bool supports_proto_;\n  bool world_readable_;\n\n  std::deque<std::unique_ptr<Crash>> queued_requests_;\n\n  DISALLOW_COPY_AND_ASSIGN(CrashQueue);\n};\n\n// Whether java trace dumps are produced via tombstoned.\nstatic constexpr bool kJavaTraceDumpsEnabled = true;\n\n// Forward declare the callbacks so they can be placed in a sensible order.\nstatic void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,\n                            void*);\nstatic void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg);\nstatic void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg);\n\nstatic void perform_request(std::unique_ptr<Crash> crash) {\n  unique_fd output_fd;\n  if (intercept_manager->FindIntercept(crash->crash_pid, crash->crash_type, &output_fd)) {\n    if (crash->crash_type == kDebuggerdTombstoneProto) {\n      crash->output.proto = CrashArtifact::devnull();\n    }\n  } else {\n    if (auto o = CrashQueue::for_crash(crash.get())->get_output(crash->crash_type); o) {\n      crash->output = std::move(*o);\n      output_fd.reset(dup(crash->output.text.fd));\n    } else {\n      LOG(ERROR) << \"failed to get crash output for type \" << crash->crash_type;\n      return;\n    }\n  }\n\n  TombstonedCrashPacket response = {.packet_type = CrashPacketType::kPerformDump};\n\n  ssize_t rc = -1;\n  if (crash->output.proto) {\n    rc = SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get(),\n                             crash->output.proto->fd.get());\n  } else {\n    rc = SendFileDescriptors(crash->crash_socket_fd, &response, sizeof(response), output_fd.get());\n  }\n\n  output_fd.reset();\n\n  if (rc == -1) {\n    PLOG(WARNING) << \"failed to send response to CrashRequest\";\n    return;\n  } else if (rc != sizeof(response)) {\n    PLOG(WARNING) << \"crash socket write returned short\";\n    return;\n  }\n\n  // TODO: Make this configurable by the interceptor?\n  struct timeval timeout = {10 * android::base::HwTimeoutMultiplier(), 0};\n\n  event_base* base = event_get_base(crash->crash_event);\n\n  event_assign(crash->crash_event, base, crash->crash_socket_fd, EV_TIMEOUT | EV_READ,\n               crash_completed_cb, crash.get());\n  event_add(crash->crash_event, &timeout);\n  CrashQueue::for_crash(crash)->on_crash_started();\n\n  // The crash is now owned by the event loop.\n  crash.release();\n}\n\nstatic void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,\n                            void*) {\n  event_base* base = evconnlistener_get_base(listener);\n  Crash* crash = new Crash();\n\n  // TODO: Make sure that only java crashes come in on the java socket\n  // and only native crashes on the native socket.\n  struct timeval timeout = {1 * android::base::HwTimeoutMultiplier(), 0};\n  event* crash_event = event_new(base, sockfd, EV_TIMEOUT | EV_READ, crash_request_cb, crash);\n  crash->crash_socket_fd.reset(sockfd);\n  crash->crash_event = crash_event;\n  event_add(crash_event, &timeout);\n}\n\nstatic void crash_request_cb(evutil_socket_t sockfd, short ev, void* arg) {\n  std::unique_ptr<Crash> crash(static_cast<Crash*>(arg));\n  TombstonedCrashPacket request = {};\n\n  if ((ev & EV_TIMEOUT) != 0) {\n    LOG(WARNING) << \"crash request timed out\";\n    return;\n  } else if ((ev & EV_READ) == 0) {\n    LOG(WARNING) << \"tombstoned received unexpected event from crash socket\";\n    return;\n  }\n\n  ssize_t rc = TEMP_FAILURE_RETRY(read(sockfd, &request, sizeof(request)));\n  if (rc == -1) {\n    PLOG(WARNING) << \"failed to read from crash socket\";\n    return;\n  } else if (rc != sizeof(request)) {\n    LOG(WARNING) << \"crash socket received short read of length \" << rc << \" (expected \"\n                 << sizeof(request) << \")\";\n    return;\n  }\n\n  if (request.packet_type != CrashPacketType::kDumpRequest) {\n    LOG(WARNING) << \"unexpected crash packet type, expected kDumpRequest, received  \"\n                 << StringPrintf(\"%#2hhX\", request.packet_type);\n    return;\n  }\n\n  crash->crash_type = request.packet.dump_request.dump_type;\n  if (crash->crash_type < 0 || crash->crash_type > kDebuggerdTombstoneProto) {\n    LOG(WARNING) << \"unexpected crash dump type: \" << crash->crash_type;\n    return;\n  }\n\n  if (crash->crash_type != kDebuggerdJavaBacktrace) {\n    crash->crash_pid = request.packet.dump_request.pid;\n  } else {\n    // Requests for java traces are sent from untrusted processes, so we\n    // must not trust the PID sent down with the request. Instead, we ask the\n    // kernel.\n    ucred cr = {};\n    socklen_t len = sizeof(cr);\n    int ret = getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len);\n    if (ret != 0) {\n      PLOG(ERROR) << \"Failed to getsockopt(..SO_PEERCRED)\";\n      return;\n    }\n\n    crash->crash_pid = cr.pid;\n  }\n\n  pid_t crash_pid = crash->crash_pid;\n  LOG(INFO) << \"received crash request for pid \" << crash_pid;\n\n  if (CrashQueue::for_crash(crash)->maybe_enqueue_crash(std::move(crash))) {\n    LOG(INFO) << \"enqueueing crash request for pid \" << crash_pid;\n  } else {\n    perform_request(std::move(crash));\n  }\n}\n\nstatic bool rename_tombstone_fd(borrowed_fd fd, borrowed_fd dirfd, const std::string& path) {\n  // Always try to unlink the tombstone file.\n  // linkat doesn't let us replace a file, so we need to unlink before linking\n  // our results onto disk, and if we fail for some reason, we should delete\n  // stale tombstones to avoid confusing inconsistency.\n  int rc = unlinkat(dirfd.get(), path.c_str(), 0);\n  if (rc != 0 && errno != ENOENT) {\n    PLOG(ERROR) << \"failed to unlink tombstone at \" << path;\n    return false;\n  }\n\n  // This fd is created inside of dirfd in CrashQueue::create_temporary_file.\n  std::string fd_path = StringPrintf(\"/proc/self/fd/%d\", fd.get());\n  rc = linkat(AT_FDCWD, fd_path.c_str(), dirfd.get(), path.c_str(), AT_SYMLINK_FOLLOW);\n  if (rc != 0) {\n    PLOG(ERROR) << \"failed to link tombstone at \" << path;\n    return false;\n  }\n  return true;\n}\n\nstatic void crash_completed(borrowed_fd sockfd, std::unique_ptr<Crash> crash) {\n  TombstonedCrashPacket request = {};\n  CrashQueue* queue = CrashQueue::for_crash(crash);\n\n  ssize_t rc = TEMP_FAILURE_RETRY(read(sockfd.get(), &request, sizeof(request)));\n  if (rc == -1) {\n    PLOG(WARNING) << \"failed to read from crash socket\";\n    return;\n  } else if (rc != sizeof(request)) {\n    LOG(WARNING) << \"crash socket received short read of length \" << rc << \" (expected \"\n                 << sizeof(request) << \")\";\n    return;\n  }\n\n  if (request.packet_type != CrashPacketType::kCompletedDump) {\n    LOG(WARNING) << \"unexpected crash packet type, expected kCompletedDump, received \"\n                 << uint32_t(request.packet_type);\n    return;\n  }\n\n  if (crash->output.text.fd == -1) {\n    LOG(WARNING) << \"skipping tombstone file creation due to intercept\";\n    return;\n  }\n\n  CrashArtifactPaths paths = queue->get_next_artifact_paths();\n\n  if (crash->output.proto && crash->output.proto->fd != -1) {\n    if (!paths.proto) {\n      LOG(ERROR) << \"missing path for proto tombstone\";\n    } else {\n      rename_tombstone_fd(crash->output.proto->fd, queue->dir_fd(), *paths.proto);\n    }\n  }\n\n  if (rename_tombstone_fd(crash->output.text.fd, queue->dir_fd(), paths.text)) {\n    if (crash->crash_type == kDebuggerdJavaBacktrace) {\n      LOG(ERROR) << \"Traces for pid \" << crash->crash_pid << \" written to: \" << paths.text;\n    } else {\n      // NOTE: Several tools parse this log message to figure out where the\n      // tombstone associated with a given native crash was written. Any changes\n      // to this message must be carefully considered.\n      LOG(ERROR) << \"Tombstone written to: \" << paths.text;\n    }\n  }\n}\n\nstatic void crash_completed_cb(evutil_socket_t sockfd, short ev, void* arg) {\n  std::unique_ptr<Crash> crash(static_cast<Crash*>(arg));\n  CrashQueue* queue = CrashQueue::for_crash(crash);\n\n  queue->on_crash_completed();\n\n  if ((ev & EV_READ) == EV_READ) {\n    crash_completed(sockfd, std::move(crash));\n  }\n\n  // If there's something queued up, let them proceed.\n  queue->maybe_dequeue_crashes(perform_request);\n}\n\nint main(int, char* []) {\n  umask(0117);\n\n  // Don't try to connect to ourselves if we crash.\n  struct sigaction action = {};\n  action.sa_handler = [](int signal) {\n    LOG(ERROR) << \"received fatal signal \" << signal;\n    _exit(1);\n  };\n  debuggerd_register_handlers(&action);\n\n  int intercept_socket = android_get_control_socket(kTombstonedInterceptSocketName);\n  int crash_socket = android_get_control_socket(kTombstonedCrashSocketName);\n\n  if (intercept_socket == -1 || crash_socket == -1) {\n    PLOG(FATAL) << \"failed to get socket from init\";\n  }\n\n  evutil_make_socket_nonblocking(intercept_socket);\n  evutil_make_socket_nonblocking(crash_socket);\n\n  event_base* base = event_base_new();\n  if (!base) {\n    LOG(FATAL) << \"failed to create event_base\";\n  }\n\n  intercept_manager = new InterceptManager(base, intercept_socket);\n\n  evconnlistener* tombstone_listener =\n      evconnlistener_new(base, crash_accept_cb, CrashQueue::for_tombstones(), LEV_OPT_CLOSE_ON_FREE,\n                         -1 /* backlog */, crash_socket);\n  if (!tombstone_listener) {\n    LOG(FATAL) << \"failed to create evconnlistener for tombstones.\";\n  }\n\n  if (kJavaTraceDumpsEnabled) {\n    const int java_trace_socket = android_get_control_socket(kTombstonedJavaTraceSocketName);\n    if (java_trace_socket == -1) {\n      PLOG(FATAL) << \"failed to get socket from init\";\n    }\n\n    evutil_make_socket_nonblocking(java_trace_socket);\n    evconnlistener* java_trace_listener =\n        evconnlistener_new(base, crash_accept_cb, CrashQueue::for_anrs(), LEV_OPT_CLOSE_ON_FREE,\n                           -1 /* backlog */, java_trace_socket);\n    if (!java_trace_listener) {\n      LOG(FATAL) << \"failed to create evconnlistener for java traces.\";\n    }\n  }\n\n  LOG(INFO) << \"tombstoned successfully initialized\";\n  event_base_dispatch(base);\n}\n"
  },
  {
    "path": "debuggerd/tombstoned/tombstoned.microdroid.rc",
    "content": "service tombstoned /system/bin/tombstoned.microdroid\n    user tombstoned\n    group system\n\n    socket tombstoned_crash seqpacket 0666 system system\n    socket tombstoned_intercept seqpacket 0666 system system\n    socket tombstoned_java_trace seqpacket 0666 system system\n"
  },
  {
    "path": "debuggerd/tombstoned/tombstoned.rc",
    "content": "service tombstoned /system/bin/tombstoned\n    user tombstoned\n    group system\n\n    socket tombstoned_crash seqpacket 0666 system system\n    socket tombstoned_intercept seqpacket 0666 system system\n    socket tombstoned_java_trace seqpacket 0666 system system\n    task_profiles ServiceCapacityLow\n"
  },
  {
    "path": "debuggerd/tombstoned/tombstoned_client.cpp",
    "content": "/*\n * Copyright 2017, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"tombstoned/tombstoned.h\"\n\n#include <fcntl.h>\n#include <unistd.h>\n\n#include <utility>\n\n#include <android-base/cmsg.h>\n#include <android-base/unique_fd.h>\n#include <async_safe/log.h>\n#include <cutils/sockets.h>\n\n#include \"protocol.h\"\n#include \"util.h\"\n\nusing android::base::ReceiveFileDescriptors;\nusing android::base::unique_fd;\n\nbool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text_output_fd,\n                        DebuggerdDumpType dump_type) {\n  return tombstoned_connect(pid, tombstoned_socket, text_output_fd, nullptr, dump_type);\n}\n\nbool tombstoned_connect(pid_t pid, unique_fd* tombstoned_socket, unique_fd* text_output_fd,\n                        unique_fd* proto_output_fd, DebuggerdDumpType dump_type) {\n  unique_fd sockfd(\n      socket_local_client((dump_type != kDebuggerdJavaBacktrace ? kTombstonedCrashSocketName\n                                                                : kTombstonedJavaTraceSocketName),\n                          ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));\n  if (sockfd == -1) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"failed to connect to tombstoned: %s\",\n                          strerror(errno));\n    return false;\n  }\n\n  TombstonedCrashPacket packet = {};\n  packet.packet_type = CrashPacketType::kDumpRequest;\n  packet.packet.dump_request.pid = pid;\n  packet.packet.dump_request.dump_type = dump_type;\n  if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\", \"failed to write DumpRequest packet: %s\",\n                          strerror(errno));\n    return false;\n  }\n\n  unique_fd tmp_output_fd, tmp_proto_fd;\n  ssize_t rc = -1;\n\n  if (dump_type == kDebuggerdTombstoneProto) {\n    rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd, &tmp_proto_fd);\n  } else {\n    rc = ReceiveFileDescriptors(sockfd, &packet, sizeof(packet), &tmp_output_fd);\n  }\n\n  if (rc == -1) {\n    async_safe_format_log(ANDROID_LOG_ERROR, \"libc\",\n                          \"failed to read response to DumpRequest packet: %s\", strerror(errno));\n    return false;\n  } else if (rc != sizeof(packet)) {\n    async_safe_format_log(\n        ANDROID_LOG_ERROR, \"libc\",\n        \"received DumpRequest response packet of incorrect length (expected %zu, got %zd)\",\n        sizeof(packet), rc);\n    return false;\n  }\n\n  // Make the fd O_APPEND so that our output is guaranteed to be at the end of a file.\n  // (This also makes selinux rules consistent, because selinux distinguishes between writing to\n  // a regular fd, and writing to an fd with O_APPEND).\n  int flags = fcntl(tmp_output_fd.get(), F_GETFL);\n  if (fcntl(tmp_output_fd.get(), F_SETFL, flags | O_APPEND) != 0) {\n    async_safe_format_log(ANDROID_LOG_WARN, \"libc\", \"failed to set output fd flags: %s\",\n                          strerror(errno));\n  }\n\n  *tombstoned_socket = std::move(sockfd);\n  *text_output_fd = std::move(tmp_output_fd);\n  if (proto_output_fd) {\n    *proto_output_fd = std::move(tmp_proto_fd);\n  }\n  return true;\n}\n\nbool tombstoned_notify_completion(int tombstoned_socket) {\n  TombstonedCrashPacket packet = {};\n  packet.packet_type = CrashPacketType::kCompletedDump;\n  if (TEMP_FAILURE_RETRY(write(tombstoned_socket, &packet, sizeof(packet))) != sizeof(packet)) {\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "debuggerd/util.cpp",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"util.h\"\n\n#include <time.h>\n\n#include <functional>\n#include <string>\n#include <utility>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include \"protocol.h\"\n\nstd::vector<std::string> get_command_line(pid_t pid) {\n  std::vector<std::string> result;\n\n  std::string cmdline;\n  android::base::ReadFileToString(android::base::StringPrintf(\"/proc/%d/cmdline\", pid), &cmdline);\n\n  auto it = cmdline.cbegin();\n  while (it != cmdline.cend()) {\n    // string::iterator is a wrapped type, not a raw char*.\n    auto terminator = std::find(it, cmdline.cend(), '\\0');\n    result.emplace_back(it, terminator);\n    it = std::find_if(terminator, cmdline.cend(), [](char c) { return c != '\\0'; });\n  }\n  if (result.empty()) {\n    result.emplace_back(\"<unknown>\");\n  }\n\n  return result;\n}\n\nstd::string get_process_name(pid_t pid) {\n  std::string result = \"<unknown>\";\n  android::base::ReadFileToString(android::base::StringPrintf(\"/proc/%d/cmdline\", pid), &result);\n  // We only want the name, not the whole command line, so truncate at the first NUL.\n  return result.c_str();\n}\n\nstd::string get_thread_name(pid_t tid) {\n  std::string result = \"<unknown>\";\n  android::base::ReadFileToString(android::base::StringPrintf(\"/proc/%d/comm\", tid), &result);\n  return android::base::Trim(result);\n}\n\nstd::string get_timestamp() {\n  timespec ts;\n  clock_gettime(CLOCK_REALTIME, &ts);\n\n  tm tm;\n  localtime_r(&ts.tv_sec, &tm);\n\n  char buf[strlen(\"1970-01-01 00:00:00.123456789+0830\") + 1];\n  char* s = buf;\n  size_t sz = sizeof(buf), n;\n  n = strftime(s, sz, \"%F %H:%M\", &tm), s += n, sz -= n;\n  n = snprintf(s, sz, \":%02d.%09ld\", tm.tm_sec, ts.tv_nsec), s += n, sz -= n;\n  n = strftime(s, sz, \"%z\", &tm), s += n, sz -= n;\n  return buf;\n}\n\nbool iterate_tids(pid_t pid, std::function<void(pid_t)> callback) {\n  char buf[BUFSIZ];\n  snprintf(buf, sizeof(buf), \"/proc/%d/task\", pid);\n  std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(buf), closedir);\n  if (dir == nullptr) {\n    return false;\n  }\n\n  struct dirent* entry;\n  while ((entry = readdir(dir.get())) != nullptr) {\n    pid_t tid = atoi(entry->d_name);\n    if (tid == 0) {\n      continue;\n    }\n    callback(tid);\n  }\n  return true;\n}\n"
  },
  {
    "path": "debuggerd/util.h",
    "content": "/*\n * Copyright 2016, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <string>\n#include <vector>\n\n#include <sys/cdefs.h>\n#include <sys/types.h>\n\nstd::vector<std::string> get_command_line(pid_t pid);\nstd::string get_process_name(pid_t pid);\nstd::string get_thread_name(pid_t tid);\n\nstd::string get_timestamp();\nbool iterate_tids(pid_t, std::function<void(pid_t)>);\n"
  },
  {
    "path": "diagnose_usb/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libdiagnose_usb\",\n    cflags: [\"-Wall\", \"-Wextra\", \"-Werror\"],\n    host_supported: true,\n    recovery_available: true,\n    min_sdk_version: \"apex_inherit\",\n    apex_available: [\n        \"com.android.adbd\",\n        // TODO(b/151398197) remove the below\n        \"//apex_available:platform\",\n    ],\n    target: {\n        windows: {\n            enabled: true,\n        },\n    },\n    srcs: [\"diagnose_usb.cpp\"],\n    export_include_dirs: [\"include\"],\n    static_libs: [\"libbase\"],\n}\n"
  },
  {
    "path": "diagnose_usb/OWNERS",
    "content": "yabinc@google.com\n"
  },
  {
    "path": "diagnose_usb/diagnose_usb.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"diagnose_usb.h\"\n\n#include <errno.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <string>\n#include <vector>\n\n#include <android-base/stringprintf.h>\n\n#if defined(__linux__)\n#include <grp.h>\n#include <pwd.h>\n#endif\n\nstatic const char kPermissionsHelpUrl[] = \"http://developer.android.com/tools/device.html\";\n\n// Returns a message describing any potential problems we find with udev, or an empty string if we\n// can't find plugdev information (i.e. udev is not installed).\nstatic std::string GetUdevProblem() {\n#if defined(__linux__) && !defined(__BIONIC__)\n    errno = 0;\n    group* plugdev_group = getgrnam(\"plugdev\");\n\n    if (plugdev_group == nullptr) {\n        if (errno != 0) {\n            perror(\"failed to read plugdev group info\");\n        }\n        // We can't give any generally useful advice here, just let the caller print the help URL.\n        return \"\";\n    }\n\n    int ngroups = getgroups(0, nullptr);\n    if (ngroups < 0) {\n        perror(\"failed to get groups list size\");\n        return \"\";\n    }\n\n    std::vector<gid_t> groups(ngroups);\n    ngroups = getgroups(groups.size(), groups.data());\n    if (ngroups < 0) {\n        perror(\"failed to get groups list\");\n        return \"\";\n    }\n\n    groups.resize(ngroups);\n\n    // getgroups(2) indicates that the egid may not be included so we check it additionally just\n    // to be sure.\n    if (std::find(groups.begin(), groups.end(), plugdev_group->gr_gid) != groups.end() ||\n        getegid() == plugdev_group->gr_gid) {\n        // The user is in plugdev so the problem is likely with the udev rules.\n        return \"missing udev rules? user is in the plugdev group\";\n    }\n    passwd* pwd = getpwuid(getuid());\n    return android::base::StringPrintf(\"user %s is not in the plugdev group\",\n                                       pwd ? pwd->pw_name : \"?\");\n#else\n    return \"\";\n#endif\n}\n\n// Short help text must be a single line, and will look something like:\n//\n//   no permissions (reason); see [URL]\nstd::string UsbNoPermissionsShortHelpText() {\n    std::string help_text = \"no permissions\";\n\n    std::string problem(GetUdevProblem());\n    if (!problem.empty()) help_text += \" (\" + problem + \")\";\n\n    return android::base::StringPrintf(\"%s; see [%s]\", help_text.c_str(), kPermissionsHelpUrl);\n}\n\n// Long help text can span multiple lines but doesn't currently provide more detailed information:\n//\n//   insufficient permissions for device: reason\n//   See [URL] for more information\nstd::string UsbNoPermissionsLongHelpText() {\n    std::string header = \"insufficient permissions for device\";\n\n    std::string problem(GetUdevProblem());\n    if (!problem.empty()) header += \": \" + problem;\n\n    return android::base::StringPrintf(\"%s\\nSee [%s] for more information\", header.c_str(),\n                                       kPermissionsHelpUrl);\n}\n"
  },
  {
    "path": "diagnose_usb/include/diagnose_usb.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __DIAGNOSE_LINUX_USB_H\n#define __DIAGNOSE_LINUX_USB_H\n\n#include <string>\n\n// USB permission error help text. The short version will be one line, long may be multi-line.\n// Returns a string message to print, or an empty string if no problems could be found.\nstd::string UsbNoPermissionsShortHelpText();\nstd::string UsbNoPermissionsLongHelpText();\n\n#endif\n"
  },
  {
    "path": "fastboot/Android.bp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// This is required because no Android.bp can include a library defined in an\n// Android.mk. Eventually should kill libfastboot (defined in Android.mk)\npackage {\n    default_applicable_licenses: [\n        \"system_core_fastboot_license\",\n        \"Android-Apache-2.0\",\n    ],\n}\n\n// Added automatically by a large-scale-change that took the approach of\n// 'apply every license found to every target'. While this makes sure we respect\n// every license restriction, it may not be entirely correct.\n//\n// e.g. GPL in an MIT project might only apply to the contrib/ directory.\n//\n// Please consider splitting the single license below into multiple licenses,\n// taking care not to lose any license_kind information, and overriding the\n// default license using the 'licenses: [...]' property on targets as needed.\n//\n// For unused files, consider creating a 'fileGroup' with \"//visibility:private\"\n// to attach the license to, and including a comment whether the files may be\n// used in the current project.\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_fastboot_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-BSD\",\n    ],\n    license_text: [\"LICENSE\"],\n}\n\ncc_library_host_static {\n    name: \"libfastboot2\",\n\n    //host_supported: true,\n\n    compile_multilib: \"first\",\n    srcs: [\n        \"bootimg_utils.cpp\",\n        \"fs.cpp\",\n        \"socket.cpp\",\n        \"tcp.cpp\",\n        \"udp.cpp\",\n        \"util.cpp\",\n        \"vendor_boot_img_utils.cpp\",\n        \"fastboot_driver.cpp\",\n    ],\n\n    static_libs: [\n        \"libziparchive\",\n        \"libsparse\",\n        \"libutils\",\n        \"liblog\",\n        \"libz\",\n        \"libdiagnose_usb\",\n        \"libbase\",\n        \"libcutils\",\n        \"libgtest\",\n        \"libgtest_main\",\n        \"libbase\",\n        \"libadb_host\",\n        \"liblp\",\n    ],\n\n    header_libs: [\n        \"avb_headers\",\n        \"bootimg_headers\",\n        \"libstorage_literals_headers\",\n    ],\n\n    export_header_lib_headers: [\n        \"bootimg_headers\",\n    ],\n\n    target: {\n        linux: {\n            srcs: [\"usb_linux.cpp\"],\n        },\n\n        darwin: {\n            srcs: [\"usb_osx.cpp\"],\n\n            host_ldlibs: [\n                \"-framework CoreFoundation\",\n                \"-framework IOKit\",\n            ],\n        },\n\n        windows: {\n            srcs: [\"usb_windows.cpp\"],\n\n            host_ldlibs: [\n                \"-lws2_32\",\n            ],\n        },\n    },\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n        \"-Wunreachable-code\",\n    ],\n\n    export_include_dirs: [\".\"],\n\n}\n\ncc_defaults {\n    name: \"fastboot_defaults\",\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n        \"-Wvla\",\n        \"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION\",\n        \"-Wthread-safety\",\n    ],\n    rtti: true,\n\n}\n\ncc_binary {\n    name: \"fastbootd\",\n    defaults: [\"fastboot_defaults\"],\n\n    recovery: true,\n\n    product_variables: {\n        debuggable: {\n            cppflags: [\"-DFB_ENABLE_FETCH\"],\n        },\n    },\n\n    srcs: [\n        \"device/commands.cpp\",\n        \"device/fastboot_device.cpp\",\n        \"device/flashing.cpp\",\n        \"device/main.cpp\",\n        \"device/usb.cpp\",\n        \"device/usb_iouring.cpp\",\n        \"device/usb_client.cpp\",\n        \"device/tcp_client.cpp\",\n        \"device/utility.cpp\",\n        \"device/variables.cpp\",\n        \"socket.cpp\",\n    ],\n\n    shared_libs: [\n        \"android.hardware.boot@1.0\",\n        \"android.hardware.boot@1.1\",\n        \"android.hardware.boot-V1-ndk\",\n        \"libboot_control_client\",\n        \"android.hardware.fastboot@1.1\",\n        \"android.hardware.fastboot-V1-ndk\",\n        \"android.hardware.health@2.0\",\n        \"android.hardware.health-V4-ndk\",\n        \"libasyncio\",\n        \"libbase\",\n        \"libbinder_ndk\",\n        \"libbootloader_message\",\n        \"libcutils\",\n        \"libext2_uuid\",\n        \"libext4_utils\",\n        \"libfs_mgr\",\n        \"libgsi\",\n        \"libhidlbase\",\n        \"liblog\",\n        \"liblp\",\n        \"libprotobuf-cpp-lite\",\n        \"libsparse\",\n        \"libutils\",\n        \"libselinux\",\n    ],\n\n    static_libs: [\n        \"android.hardware.health-translate-ndk\",\n        \"libhealthhalutils\",\n        \"libhealthshim\",\n        \"libfastbootshim\",\n        \"libsnapshot_cow\",\n        \"liblz4\",\n        \"libzstd\",\n        \"libsnapshot_nobinder\",\n        \"update_metadata-protos\",\n        \"liburing\",\n    ],\n\n    header_libs: [\n        \"avb_headers\",\n        \"libgtest_prod_headers\",\n        \"libsnapshot_headers\",\n        \"libstorage_literals_headers\",\n    ],\n}\n\ncc_defaults {\n    name: \"fastboot_host_defaults\",\n\n    use_version_lib: true,\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n        \"-Wunreachable-code\",\n        \"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION\",\n        \"-D_FILE_OFFSET_BITS=64\",\n    ],\n\n    target: {\n        darwin: {\n            cflags: [\"-Wno-unused-parameter\"],\n            host_ldlibs: [\n                \"-lpthread\",\n                \"-framework CoreFoundation\",\n                \"-framework IOKit\",\n            ],\n        },\n        windows: {\n            enabled: true,\n\n            host_ldlibs: [\"-lws2_32\"],\n        },\n        not_windows: {\n            static_libs: [\n                \"libext4_utils\",\n            ],\n        },\n    },\n\n    stl: \"libc++_static\",\n\n    // Don't add anything here, we don't want additional shared dependencies\n    // on the host fastboot tool, and shared libraries that link against libc++\n    // will violate ODR.\n    shared_libs: [],\n\n    header_libs: [\n        \"avb_headers\",\n        \"bootimg_headers\",\n    ],\n\n    static_libs: [\n        \"libziparchive\",\n        \"libsparse\",\n        \"libutils\",\n        \"liblog\",\n        \"liblz4\",\n        \"libz\",\n        \"libdiagnose_usb\",\n        \"libbase\",\n        \"libcutils\",\n        \"libgtest_host\",\n        \"liblp\",\n        \"libcrypto\",\n    ],\n}\n\n//\n// Build host libfastboot.\n//\n\ncc_library_host_static {\n    name: \"libfastboot\",\n    defaults: [\"fastboot_host_defaults\"],\n\n    srcs: [\n        \"bootimg_utils.cpp\",\n        \"fastboot_driver.cpp\",\n        \"fastboot.cpp\",\n        \"filesystem.cpp\",\n        \"fs.cpp\",\n        \"socket.cpp\",\n        \"storage.cpp\",\n        \"super_flash_helper.cpp\",\n        \"tcp.cpp\",\n        \"udp.cpp\",\n        \"util.cpp\",\n        \"vendor_boot_img_utils.cpp\",\n        \"task.cpp\",\n    ],\n\n    // Only version the final binaries\n    use_version_lib: false,\n    static_libs: [\"libbuildversion\"],\n    header_libs: [\n        \"avb_headers\",\n        \"libstorage_literals_headers\",\n    ],\n\n    generated_headers: [\"platform_tools_version\"],\n\n    target: {\n        windows: {\n            srcs: [\"usb_windows.cpp\"],\n\n            include_dirs: [\"development/host/windows/usb/api\"],\n        },\n        darwin: {\n            srcs: [\"usb_osx.cpp\"],\n        },\n        linux: {\n            srcs: [\"usb_linux.cpp\"],\n        },\n    },\n}\n\n//\n// Build host fastboot / fastboot.exe\n//\n\ncc_binary_host {\n    name: \"fastboot\",\n    defaults: [\"fastboot_host_defaults\"],\n\n    srcs: [\"main.cpp\"],\n    static_libs: [\"libfastboot\"],\n\n    required: [\n        \"mke2fs\",\n        \"make_f2fs\",\n        \"make_f2fs_casefold\",\n    ],\n    dist: {\n        targets: [\n            \"dist_files\",\n            \"sdk\",\n            \"sdk-repo-platform-tools\",\n            \"sdk_repo\",\n            \"win_sdk\",\n        ],\n    },\n\n    target: {\n        not_windows: {\n            required: [\n                \"mke2fs.conf\",\n            ],\n        },\n        windows: {\n            required: [\"AdbWinUsbApi\"],\n            shared_libs: [\"AdbWinApi\"],\n        },\n    },\n}\n\n//\n// Build host fastboot_test.\n//\n\ncc_test_host {\n    name: \"fastboot_test\",\n    defaults: [\"fastboot_host_defaults\"],\n\n    srcs: [\n        \"fastboot_driver_test.cpp\",\n        \"fastboot_test.cpp\",\n        \"socket_mock.cpp\",\n        \"socket_test.cpp\",\n        \"super_flash_helper_test.cpp\",\n        \"task_test.cpp\",\n        \"tcp_test.cpp\",\n        \"udp_test.cpp\",\n    ],\n\n    static_libs: [\n        \"libfastboot\",\n        \"libgmock\",\n    ],\n\n    target: {\n        windows: {\n            shared_libs: [\"AdbWinApi\"],\n        },\n        windows_x86_64: {\n            // Avoid trying to build for win64\n            enabled: false,\n        },\n    },\n\n    test_suites: [\"general-tests\"],\n\n    data: [\n        \"testdata/super.img\",\n        \"testdata/super_empty.img\",\n        \"testdata/system.img\",\n    ],\n}\n\ncc_test_host {\n    name: \"fastboot_vendor_boot_img_utils_test\",\n    srcs: [\"vendor_boot_img_utils_test.cpp\"],\n    static_libs: [\n        \"libbase\",\n        \"libfastboot\",\n        \"libgmock\",\n        \"liblog\",\n    ],\n    header_libs: [\n        \"avb_headers\",\n        \"bootimg_headers\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    data: [\n        \":fastboot_test_dtb\",\n        \":fastboot_test_dtb_replace\",\n        \":fastboot_test_bootconfig\",\n        \":fastboot_test_vendor_ramdisk_none\",\n        \":fastboot_test_vendor_ramdisk_platform\",\n        \":fastboot_test_vendor_ramdisk_replace\",\n        \":fastboot_test_vendor_boot_v3\",\n        \":fastboot_test_vendor_boot_v4_without_frag\",\n        \":fastboot_test_vendor_boot_v4_with_frag\",\n    ],\n}\n\ncc_library_headers {\n    name: \"fastboot_headers\",\n    host_supported: true,\n    export_include_dirs: [\".\"],\n}\n\npython_test_host {\n    name: \"fastboot_integration_test\",\n    main: \"test_fastboot.py\",\n    srcs: [\"test_fastboot.py\"],\n    data: [\":fastboot\"],\n    test_config: \"fastboot_integration_test.xml\",\n    test_options: {\n        unit_test: false,\n    },\n}\n"
  },
  {
    "path": "fastboot/LICENSE",
    "content": " Redistribution and use in source and binary forms, with or without\n modification, are permitted provided that the following conditions\n are met:\n  * Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n  * Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in\n    the documentation and/or other materials provided with the\n    distribution.\n\n THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n SUCH DAMAGE.\n\n"
  },
  {
    "path": "fastboot/OWNERS",
    "content": "dvander@google.com\nelsk@google.com\nenh@google.com\nsanglardf@google.com\nzhangkelvin@google.com\n\n"
  },
  {
    "path": "fastboot/README.md",
    "content": "Fastboot\n--------\n\nThe fastboot protocol is a mechanism for communicating with bootloaders\nover USB or ethernet.  It is designed to be very straightforward to implement,\nto allow it to be used across a wide range of devices and from hosts running\nLinux, macOS, or Windows.\n\n\n## Basic Requirements\n\n* USB\n  * Two bulk endpoints (in, out) are required\n  * Max packet size must be 64 bytes for full-speed, 512 bytes for\n    high-speed and 1024 bytes for Super Speed USB.\n  * The protocol is entirely host-driven and synchronous (unlike the\n    multi-channel, bi-directional, asynchronous ADB protocol)\n\n* TCP or UDP\n  * Device must be reachable via IP.\n  * Device will act as the server, fastboot will be the client.\n  * Fastboot data is wrapped in a simple protocol; see below for details.\n\n\n## Transport and Framing\n\n1. Host sends a command, which is an ascii string in a single\n   packet no greater than 4096 bytes.\n\n2. Client response with a single packet no greater than 256 bytes.\n   The first four bytes of the response are \"OKAY\", \"FAIL\", \"DATA\",\n   \"INFO\" or \"TEXT\".  Additional bytes may contain an (ascii) informative\n   message.\n\n   a. INFO -> the remaining 252 bytes are an informative message\n      (providing progress or diagnostic messages).  They should\n      be displayed and then step #2 repeats. The print format is:\n      \"(bootloader) \" + InfoMessagePayload + '\\n'\n\n   b. TEXT -> the remaining 252 bytes are arbitrary. They should\n      be displayed and then step #2 repeats.\n      It differs from info in that no formatting is applied.\n      The payload is printed as-is with no newline at the end.\n      Payload is expected to be NULL terminated.\n\n   c. FAIL -> the requested command failed.  The remaining 252 bytes\n      of the response (if present) provide a textual failure message\n      to present to the user.  Stop.\n\n   d. OKAY -> the requested command completed successfully.  Go to #5\n\n   e. DATA -> the requested command is ready for the data phase.\n      A DATA response packet will be 12 bytes long, in the form of\n      DATA00000000 where the 8 digit hexadecimal number represents\n      the total data size to transfer.\n\n3. Data phase.  Depending on the command, the host or client will\n   send the indicated amount of data.  Short packets are always\n   acceptable and zero-length packets are ignored.  This phase continues\n   until the client has sent or received the number of bytes indicated\n   in the \"DATA\" response above.\n\n4. Client responds with a single packet no greater than 256 bytes.\n   The first four bytes of the response are \"OKAY\", \"FAIL\",\n   \"INFO\" or \"TEXT\". Similar to #2:\n\n   a. INFO -> display the formatted remaining 252 bytes and return to #4\n\n   b. TEXT -> display the unformatted remaining 252 bytes and return to #4\n\n   c. FAIL -> display the remaining 252 bytes (if present) as a failure\n      reason and consider the command failed.  Stop.\n\n   d. OKAY -> success.  Go to #5\n\n5. Success.  Stop.\n\n\n## Example Session\n\n    Host:    \"getvar:version\"        request version variable\n\n    Client:  \"OKAY0.4\"               return version \"0.4\"\n\n    Host:    \"getvar:nonexistant\"    request some undefined variable\n\n    Client:  \"FAILUnknown variable\"  getvar failure; see getvar details below\n\n    Host:    \"download:00001234\"     request to send 0x1234 bytes of data\n\n    Client:  \"DATA00001234\"          ready to accept data\n\n    Host:    < 0x1234 bytes >        send data\n\n    Client:  \"OKAY\"                  success\n\n    Host:    \"flash:bootloader\"      request to flash the data to the bootloader\n\n    Client:  \"INFOerasing flash\"     indicate status / progress\n             \"INFOwriting flash\"\n             \"OKAY\"                  indicate success\n\n    Host:    \"powerdown\"             send a command\n\n    Client:  \"FAILunknown command\"   indicate failure\n\n\n## Command Reference\n\n* Command parameters are indicated by printf-style escape sequences.\n\n* Commands are ascii strings and sent without the quotes (which are\n  for illustration only here) and without a trailing 0 byte.\n\n* Commands that begin with a lowercase letter are reserved for this\n  specification.  OEM-specific commands should not begin with a\n  lowercase letter, to prevent incompatibilities with future specs.\n\nThe various currently defined commands are:\n\n    getvar:%s          Read a config/version variable from the bootloader.\n                       The variable contents will be returned after the\n                       OKAY response. If the variable is unknown, the bootloader\n                       should return a FAIL response, optionally with an error\n                       message.\n\n                       Previous versions of this document indicated that getvar\n                       should return an empty OKAY response for unknown\n                       variables, so older devices might exhibit this behavior,\n                       but new implementations should return FAIL instead.\n\n    download:%08x      Write data to memory which will be later used\n                       by \"boot\", \"ramdisk\", \"flash\", etc.  The client\n                       will reply with \"DATA%08x\" if it has enough\n                       space in RAM or \"FAIL\" if not.  The size of\n                       the download is remembered.\n\n    upload             Read data from memory which was staged by the last\n                       command, e.g. an oem command.  The client will reply\n                       with \"DATA%08x\" if it is ready to send %08x bytes of\n                       data.  If no data was staged in the last command,\n                       the client must reply with \"FAIL\".  After the client\n                       successfully sends %08x bytes, the client shall send\n                       a single packet starting with \"OKAY\".  Clients\n                       should not support \"upload\" unless it supports an\n                       oem command that requires \"upload\" capabilities.\n\n    flash:%s           Write the previously downloaded image to the\n                       named partition (if possible).\n\n    erase:%s           Erase the indicated partition (clear to 0xFFs)\n\n    boot               The previously downloaded data is a boot.img\n                       and should be booted according to the normal\n                       procedure for a boot.img\n\n    continue           Continue booting as normal (if possible)\n\n    reboot             Reboot the device.\n\n    reboot-bootloader\n                       Reboot back into the bootloader.\n                       Useful for upgrade processes that require upgrading\n                       the bootloader and then upgrading other partitions\n                       using the new bootloader.\n\n\n## Flashing Logic\n\nFastboot binary will follow directions listed out fastboot-info.txt\nbuild artifact for fastboot flashall && fastboot update comamnds.\nThis build artifact will live inside of ANDROID_PRODUCT_OUT &&\ntarget_files_package && updatepackage.\n\n\nThe currently defined commands are:\n\n    flash %s           Flash a given partition. Optional arguments include\n                       --slot-other, {filename_path}, --apply-vbmeta\n\n    reboot %s          Reboot to either bootloader or fastbootd\n\n    update-super       Updates the super partition\n\n    if-wipe            Conditionally run some other functionality if\n                       wipe is specified\n\n    erase %s           Erase a given partition (can only be used in conjunction)\n                       with if-wipe -> eg. if-wipe erase cache\n\nFlashing Optimization:\n\n    After generating the list of tasks to execute, Fastboot will try and\n    optimize the flashing of the dynamic partitions by constructing an\n    optimized flash super task. Fastboot will explicitly pattern match the\n    following commands and try and concatenate it into this task. (doing so\n    will allow us to avoid the reboot into userspace fastbootd which takes\n    significant time)\n\n    //Optimizable Block\n    reboot fastboot\n    update-super                        ---> generate optimized flash super task\n    $FOR EACH {dynamic partition}\n        flash {dynamic partition}\n\n## Client Variables\n\nThe \"getvar:%s\" command is used to read client variables which\nrepresent various information about the device and the software\non it.\n\nThe various currently defined names are:\n\n    version             Version of FastBoot protocol supported.\n                        It should be \"0.4\" for this document.\n\n    version-bootloader  Version string for the Bootloader.\n\n    version-baseband    Version string of the Baseband Software\n\n    product             Name of the product\n\n    serialno            Product serial number\n\n    secure              If the value is \"yes\", this is a secure\n                        bootloader requiring a signature before\n                        it will install or boot images.\n\n    is-userspace        If the value is \"yes\", the device is running\n                        fastbootd. Otherwise, it is running fastboot\n                        in the bootloader.\n\nNames starting with a lowercase character are reserved by this\nspecification.  OEM-specific names should not start with lowercase\ncharacters.\n\n## Logical Partitions\n\nThere are a number of commands to interact with logical partitions:\n\n    update-super:%s:%s  Write the previously downloaded image to a super\n                        partition. Unlike the \"flash\" command, this has\n                        special rules. The image must have been created by\n                        the lpmake command, and must not be a sparse image.\n                        If the last argument is \"wipe\", then all existing\n                        logical partitions are deleted. If no final argument\n                        is specified, the partition tables are merged. Any\n                        partition in the new image that does not exist in the\n                        old image is created with a zero size.\n\n                        In all cases, this will cause the temporary \"scratch\"\n                        partition to be deleted if it exists.\n\n    create-logical-partition:%s:%d\n                        Create a logical partition with the given name and\n                        size, in the super partition.\n\n    delete-logical-partition:%s\n                        Delete a logical partition with the given name.\n\n    resize-logical-partition:%s:%d\n                        Change the size of the named logical partition.\n\nIn addition, there is a variable to test whether a partition is logical:\n\n    is-logical:%s       If the value is \"yes\", the partition is logical.\n                        Otherwise the partition is physical.\n\n## TCP Protocol v1\n\nThe TCP protocol is designed to be a simple way to use the fastboot protocol\nover ethernet if USB is not available.\n\nThe device will open a TCP server on port 5554 and wait for a fastboot client\nto connect.\n\n### Handshake\nUpon connecting, both sides will send a 4-byte handshake message to ensure they\nare speaking the same protocol. This consists of the ASCII characters \"FB\"\nfollowed by a 2-digit base-10 ASCII version number. For example, the version 1\nhandshake message will be [FB01].\n\nIf either side detects a malformed handshake, it should disconnect.\n\nThe protocol version to use must be the minimum of the versions sent by each\nside; if either side cannot speak this protocol version, it should disconnect.\n\n### Fastboot Data\nOnce the handshake is complete, fastboot data will be sent as follows:\n\n    [data_size][data]\n\nWhere data\\_size is an unsigned 8-byte big-endian binary value, and data is the\nfastboot packet. The 8-byte length is intended to provide future-proofing even\nthough currently fastboot packets have a 4-byte maximum length.\n\n### Example\nIn this example the fastboot host queries the device for two variables,\n\"version\" and \"none\".\n\n    Host    <connect to the device on port 5555>\n    Host    FB01\n    Device  FB01\n    Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version\n    Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4\n    Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none\n    Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable\n    Host    <disconnect>\n\n\n## UDP Protocol v1\n\nThe UDP protocol is more complex than TCP since we must implement reliability\nto ensure no packets are lost, but the general concept of wrapping the fastboot\nprotocol is the same.\n\nOverview:\n  1. As with TCP, the device will listen on UDP port 5554.\n  2. Maximum UDP packet size is negotiated during initialization.\n  3. The host drives all communication; the device may only send a packet as a\n     response to a host packet.\n  4. If the host does not receive a response in 500ms it will re-transmit.\n\n### UDP Packet format\n\n    +----------+----+-------+-------+--------------------+\n    | Byte #   | 0  |   1   | 2 - 3 |  4+                |\n    +----------+----+-------+-------+--------------------+\n    | Contents | ID | Flags | Seq # | Data               |\n    +----------+----+-------+-------+--------------------+\n\n    ID      Packet ID:\n              0x00: Error.\n              0x01: Query.\n              0x02: Initialization.\n              0x03: Fastboot.\n\n            Packet types are described in more detail below.\n\n    Flags   Packet flags: 0 0 0 0 0 0 0 C\n              C=1 indicates a continuation packet; the data is too large and will\n                  continue in the next packet.\n\n              Remaining bits are reserved for future use and must be set to 0.\n\n    Seq #   2-byte packet sequence number (big-endian). The host will increment\n            this by 1 with each new packet, and the device must provide the\n            corresponding sequence number in the response packets.\n\n    Data    Packet data, not present in all packets.\n\n### Packet Types\n\n    Query\n          The host sends a query packet once on startup to sync with the device.\n          The host will not know the current sequence number, so the device must\n          respond to all query packets regardless of sequence number.\n\n          The response data field should contain a 2-byte big-endian value\n          giving the next expected sequence number.\n\n    Init\n          The host sends an init packet once the query response is returned. The\n          device must abort any in-progress operation and prepare for a new\n          fastboot session. This message is meant to allow recovery if a\n          previous session failed, e.g. due to network error or user Ctrl+C.\n\n          The data field contains two big-endian 2-byte values, a protocol\n          version and the max UDP packet size (including the 4-byte header).\n          Both the host and device will send these values, and in each case\n          the minimum of the sent values must be used.\n\n    Fastboot\n          These packets wrap the fastboot protocol. To write, the host will\n          send a packet with fastboot data, and the device will reply with an\n          empty packet as an ACK. To read, the host will send an empty packet,\n          and the device will reply with fastboot data. The device may not give\n          any data in the ACK packet.\n\n    Error\n          The device may respond to any packet with an error packet to indicate\n          a UDP protocol error. The data field should contain an ASCII string\n          describing the error. This is the only case where a device is allowed\n          to return a packet ID other than the one sent by the host.\n\n### Packet Size\nThe maximum packet size is negotiated by the host and device in the Init packet.\nDevices must support at least 512-byte packets, but packet size has a direct\ncorrelation with download speed, so devices are strongly suggested to support at\nleast 1024-byte packets. On a local network with 0.5ms round-trip time this will\nprovide transfer rates of ~2MB/s. Over WiFi it will likely be significantly\nless.\n\nQuery and Initialization packets, which are sent before size negotiation is\ncomplete, must always be 512 bytes or less.\n\n### Packet Re-Transmission\nThe host will re-transmit any packet that does not receive a response. The\nrequirement of exactly one device response packet per host packet is how we\nachieve reliability and in-order delivery of packets.\n\nFor simplicity of implementation, there is no windowing of multiple\nunacknowledged packets in this version of the protocol. The host will continue\nto send the same packet until a response is received. Windowing functionality\nmay be implemented in future versions if necessary to increase performance.\n\nThe first Query packet will only be attempted a small number of times, but\nsubsequent packets will attempt to retransmit for at least 1 minute before\ngiving up. This means a device may safely ignore host UDP packets for up to 1\nminute during long operations, e.g. writing to flash.\n\n### Continuation Packets\nAny packet may set the continuation flag to indicate that the data is\nincomplete. Large data such as downloading an image may require many\ncontinuation packets. The receiver should respond to a continuation packet with\nan empty packet to acknowledge receipt. See examples below.\n\n### Summary\nThe host starts with a Query packet, then an Initialization packet, after\nwhich only Fastboot packets are sent. Fastboot packets may contain data from\nthe host for writes, or from the device for reads, but not both.\n\nGiven a next expected sequence number S and a received packet P, the device\nbehavior should be:\n\n    if P is a Query packet:\n      * respond with a Query packet with S in the data field\n    else if P has sequence == S:\n      * process P and take any required action\n      * create a response packet R with the same ID and sequence as P, containing\n        any response data required.\n      * transmit R and save it in case of re-transmission\n      * increment S\n    else if P has sequence == S - 1:\n      * re-transmit the saved response packet R from above\n    else:\n      * ignore the packet\n\n### Examples\n\nIn the examples below, S indicates the starting client sequence number.\n\n    Host                                    Client\n    ======================================================================\n    [Initialization, S = 0x55AA]\n    [Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]\n    [Resulting values to use: version = 1, max packet size = 1024]\n    ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data\n    ----------------------------------------------------------------------\n    0x01 0x00 0x00 0x00\n                                            0x01 0x00 0x00 0x00 0x55 0xAA\n    0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00\n                                            0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00\n\n    ----------------------------------------------------------------------\n    [fastboot \"getvar\" commands, S = 0x0001]\n    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data\n    ----------------------------------------------------------------------\n    0x03  0x00  0x00  0x01  getvar:version\n                                            0x03  0x00  0x00  0x01\n    0x03  0x00  0x00  0x02\n                                            0x03  0x00  0x00  0x02  OKAY0.4\n    0x03  0x00  0x00  0x03  getvar:none\n                                            0x03  0x00  0x00  0x03\n    0x03  0x00  0x00  0x04\n                                            0x03  0x00  0x00  0x04  FAILUnknown var\n\n    ----------------------------------------------------------------------\n    [fastboot \"INFO\" responses, S = 0x0000]\n    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data\n    ----------------------------------------------------------------------\n    0x03  0x00  0x00  0x00  <command>\n                                            0x03  0x00  0x00  0x00\n    0x03  0x00  0x00  0x01\n                                            0x03  0x00  0x00  0x01  INFOWait1\n    0x03  0x00  0x00  0x02\n                                            0x03  0x00  0x00  0x02  INFOWait2\n    0x03  0x00  0x00  0x03\n                                            0x03  0x00  0x00  0x03  OKAY\n\n    ----------------------------------------------------------------------\n    [Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]\n    ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data\n    ----------------------------------------------------------------------\n    0x03 0x00 0xFF 0xFF download:0000834\n                                            0x03 0x00 0xFF 0xFF\n    0x03 0x00 0x00 0x00\n                                            0x03 0x00 0x00 0x00 DATA0000834\n    0x03 0x01 0x00 0x01 <1020 bytes>\n                                            0x03 0x00 0x00 0x01\n    0x03 0x01 0x00 0x02 <1020 bytes>\n                                            0x03 0x00 0x00 0x02\n    0x03 0x00 0x00 0x03 <60 bytes>\n                                            0x03 0x00 0x00 0x03\n    0x03 0x00 0x00 0x04\n                                            0x03 0x00 0x00 0x04 OKAY\n\n    ----------------------------------------------------------------------\n    [Unknown ID error, S = 0x0000]\n    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data\n    ----------------------------------------------------------------------\n    0x10  0x00  0x00  0x00\n                                            0x00  0x00  0x00  0x00  <error message>\n\n    ----------------------------------------------------------------------\n    [Host packet loss and retransmission, S = 0x0000]\n    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data\n    ----------------------------------------------------------------------\n    0x03  0x00  0x00  0x00  getvar:version [lost]\n    0x03  0x00  0x00  0x00  getvar:version [lost]\n    0x03  0x00  0x00  0x00  getvar:version\n                                            0x03  0x00  0x00  0x00\n    0x03  0x00  0x00  0x01\n                                            0x03  0x00  0x00  0x01  OKAY0.4\n\n    ----------------------------------------------------------------------\n    [Client packet loss and retransmission, S = 0x0000]\n    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data\n    ----------------------------------------------------------------------\n    0x03  0x00  0x00  0x00  getvar:version\n                                            0x03  0x00  0x00  0x00 [lost]\n    0x03  0x00  0x00  0x00  getvar:version\n                                            0x03  0x00  0x00  0x00 [lost]\n    0x03  0x00  0x00  0x00  getvar:version\n                                            0x03  0x00  0x00  0x00\n    0x03  0x00  0x00  0x01\n                                            0x03  0x00  0x00  0x01  OKAY0.4\n\n    ----------------------------------------------------------------------\n    [Host packet delayed, S = 0x0000]\n    ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data\n    ----------------------------------------------------------------------\n    0x03  0x00  0x00  0x00  getvar:version [delayed]\n    0x03  0x00  0x00  0x00  getvar:version\n                                            0x03  0x00  0x00  0x00\n    0x03  0x00  0x00  0x01\n                                            0x03  0x00  0x00  0x01  OKAY0.4\n    0x03  0x00  0x00  0x00  getvar:version [arrives late with old seq#, is ignored]\n"
  },
  {
    "path": "fastboot/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"fastboot_test\"\n    }\n  ]\n}\n"
  },
  {
    "path": "fastboot/bootimg_utils.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"bootimg_utils.h\"\n\n#include \"util.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\nstatic void bootimg_set_cmdline_v3_and_above(boot_img_hdr_v3* h, const std::string& cmdline) {\n    if (cmdline.size() >= sizeof(h->cmdline)) die(\"command line too large: %zu\", cmdline.size());\n    strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());\n}\n\nvoid bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline) {\n    if (h->header_version >= 3) {\n        return bootimg_set_cmdline_v3_and_above(reinterpret_cast<boot_img_hdr_v3*>(h), cmdline);\n    }\n    if (cmdline.size() >= sizeof(h->cmdline)) die(\"command line too large: %zu\", cmdline.size());\n    strcpy(reinterpret_cast<char*>(h->cmdline), cmdline.c_str());\n}\n\nstatic void mkbootimg_v3_and_above(const std::vector<char>& kernel,\n                                   const std::vector<char>& ramdisk, const boot_img_hdr_v2& src,\n                                   std::vector<char>* out) {\n#define V3_PAGE_SIZE 4096\n    const size_t page_mask = V3_PAGE_SIZE - 1;\n    int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);\n    int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);\n\n    int64_t bootimg_size = V3_PAGE_SIZE + kernel_actual + ramdisk_actual;\n    out->resize(bootimg_size);\n\n    boot_img_hdr_v3* hdr = reinterpret_cast<boot_img_hdr_v3*>(out->data());\n\n    memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);\n    hdr->kernel_size = kernel.size();\n    hdr->ramdisk_size = ramdisk.size();\n    hdr->os_version = src.os_version;\n    hdr->header_size = sizeof(boot_img_hdr_v3);\n    hdr->header_version = src.header_version;\n\n    if (src.header_version >= 4) {\n        auto hdr_v4 = reinterpret_cast<boot_img_hdr_v4*>(hdr);\n        hdr_v4->signature_size = 0;\n    }\n\n    memcpy(hdr->magic + V3_PAGE_SIZE, kernel.data(), kernel.size());\n    memcpy(hdr->magic + V3_PAGE_SIZE + kernel_actual, ramdisk.data(), ramdisk.size());\n}\n\nvoid mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,\n               const std::vector<char>& second, const std::vector<char>& dtb, size_t base,\n               const boot_img_hdr_v2& src, std::vector<char>* out) {\n    if (src.header_version >= 3) {\n        if (!second.empty() || !dtb.empty()) {\n            die(\"Second stage bootloader and dtb not supported in v%d boot image\\n\",\n                src.header_version);\n        }\n        mkbootimg_v3_and_above(kernel, ramdisk, src, out);\n        return;\n    }\n    const size_t page_mask = src.page_size - 1;\n\n    int64_t header_actual = (sizeof(boot_img_hdr_v1) + page_mask) & (~page_mask);\n    int64_t kernel_actual = (kernel.size() + page_mask) & (~page_mask);\n    int64_t ramdisk_actual = (ramdisk.size() + page_mask) & (~page_mask);\n    int64_t second_actual = (second.size() + page_mask) & (~page_mask);\n    int64_t dtb_actual = (dtb.size() + page_mask) & (~page_mask);\n\n    int64_t bootimg_size =\n            header_actual + kernel_actual + ramdisk_actual + second_actual + dtb_actual;\n    out->resize(bootimg_size);\n\n    boot_img_hdr_v2* hdr = reinterpret_cast<boot_img_hdr_v2*>(out->data());\n\n    *hdr = src;\n    memcpy(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE);\n\n    hdr->kernel_size = kernel.size();\n    hdr->ramdisk_size = ramdisk.size();\n    hdr->second_size = second.size();\n\n    hdr->kernel_addr += base;\n    hdr->ramdisk_addr += base;\n    hdr->second_addr += base;\n    hdr->tags_addr += base;\n\n    if (hdr->header_version == 1) {\n        hdr->header_size = sizeof(boot_img_hdr_v1);\n    } else if (hdr->header_version == 2) {\n        hdr->header_size = sizeof(boot_img_hdr_v2);\n        hdr->dtb_size = dtb.size();\n        hdr->dtb_addr += base;\n    }\n\n    memcpy(hdr->magic + hdr->page_size, kernel.data(), kernel.size());\n    memcpy(hdr->magic + hdr->page_size + kernel_actual, ramdisk.data(), ramdisk.size());\n    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual, second.data(),\n           second.size());\n    memcpy(hdr->magic + hdr->page_size + kernel_actual + ramdisk_actual + second_actual, dtb.data(),\n           dtb.size());\n}\n"
  },
  {
    "path": "fastboot/bootimg_utils.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#pragma once\n\n#include <bootimg.h>\n#include <inttypes.h>\n#include <sys/types.h>\n\n#include <string>\n#include <vector>\n\nvoid mkbootimg(const std::vector<char>& kernel, const std::vector<char>& ramdisk,\n               const std::vector<char>& second, const std::vector<char>& dtb, size_t base,\n               const boot_img_hdr_v2& src, std::vector<char>* out);\n\nvoid bootimg_set_cmdline(boot_img_hdr_v2* h, const std::string& cmdline);\n"
  },
  {
    "path": "fastboot/constants.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#define FB_CMD_GETVAR \"getvar\"\n#define FB_CMD_DOWNLOAD \"download\"\n#define FB_CMD_UPLOAD \"upload\"\n#define FB_CMD_FLASH \"flash\"\n#define FB_CMD_ERASE \"erase\"\n#define FB_CMD_BOOT \"boot\"\n#define FB_CMD_SET_ACTIVE \"set_active\"\n#define FB_CMD_CONTINUE \"continue\"\n#define FB_CMD_REBOOT \"reboot\"\n#define FB_CMD_SHUTDOWN \"shutdown\"\n#define FB_CMD_REBOOT_BOOTLOADER \"reboot-bootloader\"\n#define FB_CMD_REBOOT_RECOVERY \"reboot-recovery\"\n#define FB_CMD_REBOOT_FASTBOOT \"reboot-fastboot\"\n#define FB_CMD_CREATE_PARTITION \"create-logical-partition\"\n#define FB_CMD_DELETE_PARTITION \"delete-logical-partition\"\n#define FB_CMD_RESIZE_PARTITION \"resize-logical-partition\"\n#define FB_CMD_UPDATE_SUPER \"update-super\"\n#define FB_CMD_OEM \"oem\"\n#define FB_CMD_GSI \"gsi\"\n#define FB_CMD_SNAPSHOT_UPDATE \"snapshot-update\"\n#define FB_CMD_FETCH \"fetch\"\n\n#define RESPONSE_OKAY \"OKAY\"\n#define RESPONSE_FAIL \"FAIL\"\n#define RESPONSE_DATA \"DATA\"\n#define RESPONSE_INFO \"INFO\"\n\n#define FB_COMMAND_SZ 4096\n#define FB_RESPONSE_SZ 256\n\n#define FB_VAR_VERSION \"version\"\n#define FB_VAR_VERSION_BOOTLOADER \"version-bootloader\"\n#define FB_VAR_VERSION_BASEBAND \"version-baseband\"\n#define FB_VAR_VERSION_OS \"version-os\"\n#define FB_VAR_VERSION_VNDK \"version-vndk\"\n#define FB_VAR_PRODUCT \"product\"\n#define FB_VAR_SERIALNO \"serialno\"\n#define FB_VAR_SECURE \"secure\"\n#define FB_VAR_UNLOCKED \"unlocked\"\n#define FB_VAR_CURRENT_SLOT \"current-slot\"\n#define FB_VAR_MAX_DOWNLOAD_SIZE \"max-download-size\"\n#define FB_VAR_HAS_SLOT \"has-slot\"\n#define FB_VAR_SLOT_COUNT \"slot-count\"\n#define FB_VAR_PARTITION_SIZE \"partition-size\"\n#define FB_VAR_PARTITION_TYPE \"partition-type\"\n#define FB_VAR_SLOT_SUCCESSFUL \"slot-successful\"\n#define FB_VAR_SLOT_UNBOOTABLE \"slot-unbootable\"\n#define FB_VAR_IS_LOGICAL \"is-logical\"\n#define FB_VAR_IS_USERSPACE \"is-userspace\"\n#define FB_VAR_IS_FORCE_DEBUGGABLE \"is-force-debuggable\"\n#define FB_VAR_HW_REVISION \"hw-revision\"\n#define FB_VAR_VARIANT \"variant\"\n#define FB_VAR_OFF_MODE_CHARGE_STATE \"off-mode-charge\"\n#define FB_VAR_BATTERY_VOLTAGE \"battery-voltage\"\n#define FB_VAR_BATTERY_SOC \"battery-soc\"\n#define FB_VAR_BATTERY_SOC_OK \"battery-soc-ok\"\n#define FB_VAR_SUPER_PARTITION_NAME \"super-partition-name\"\n#define FB_VAR_SNAPSHOT_UPDATE_STATUS \"snapshot-update-status\"\n#define FB_VAR_CPU_ABI \"cpu-abi\"\n#define FB_VAR_SYSTEM_FINGERPRINT \"system-fingerprint\"\n#define FB_VAR_VENDOR_FINGERPRINT \"vendor-fingerprint\"\n#define FB_VAR_DYNAMIC_PARTITION \"dynamic-partition\"\n#define FB_VAR_FIRST_API_LEVEL \"first-api-level\"\n#define FB_VAR_SECURITY_PATCH_LEVEL \"security-patch-level\"\n#define FB_VAR_TREBLE_ENABLED \"treble-enabled\"\n#define FB_VAR_MAX_FETCH_SIZE \"max-fetch-size\"\n#define FB_VAR_DMESG \"dmesg\"\n#define FB_VAR_BATTERY_SERIAL_NUMBER \"battery-serial-number\"\n#define FB_VAR_BATTERY_PART_STATUS \"battery-part-status\"\n"
  },
  {
    "path": "fastboot/device/commands.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"commands.h\"\n\n#include <inttypes.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n\n#include <unordered_set>\n\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <android/hardware/boot/1.1/IBootControl.h>\n#include <cutils/android_reboot.h>\n#include <ext4_utils/wipe.h>\n#include <fs_mgr.h>\n#include <fs_mgr/roots.h>\n#include <libgsi/libgsi.h>\n#include <liblp/builder.h>\n#include <liblp/liblp.h>\n#include <libsnapshot/snapshot.h>\n#include <storage_literals/storage_literals.h>\n#include <uuid/uuid.h>\n\n#include <bootloader_message/bootloader_message.h>\n\n#include \"BootControlClient.h\"\n#include \"constants.h\"\n#include \"fastboot_device.h\"\n#include \"flashing.h\"\n#include \"utility.h\"\n\n#ifdef FB_ENABLE_FETCH\nstatic constexpr bool kEnableFetch = true;\n#else\nstatic constexpr bool kEnableFetch = false;\n#endif\n\nusing android::fs_mgr::MetadataBuilder;\nusing android::hal::CommandResult;\nusing ::android::hardware::hidl_string;\nusing android::snapshot::SnapshotManager;\nusing MergeStatus = android::hal::BootControlClient::MergeStatus;\n\nusing namespace android::storage_literals;\n\nstruct VariableHandlers {\n    // Callback to retrieve the value of a single variable.\n    std::function<bool(FastbootDevice*, const std::vector<std::string>&, std::string*)> get;\n    // Callback to retrieve all possible argument combinations, for getvar all.\n    std::function<std::vector<std::vector<std::string>>(FastbootDevice*)> get_all_args;\n};\n\nstatic bool IsSnapshotUpdateInProgress(FastbootDevice* device) {\n    auto hal = device->boot1_1();\n    if (!hal) {\n        return false;\n    }\n    auto merge_status = hal->getSnapshotMergeStatus();\n    return merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;\n}\n\nstatic bool IsProtectedPartitionDuringMerge(FastbootDevice* device, const std::string& name) {\n    static const std::unordered_set<std::string> ProtectedPartitionsDuringMerge = {\n            \"userdata\", \"metadata\", \"misc\"};\n    if (ProtectedPartitionsDuringMerge.count(name) == 0) {\n        return false;\n    }\n    return IsSnapshotUpdateInProgress(device);\n}\n\nstatic void GetAllVars(FastbootDevice* device, const std::string& name,\n                       const VariableHandlers& handlers) {\n    if (!handlers.get_all_args) {\n        std::string message;\n        if (!handlers.get(device, std::vector<std::string>(), &message)) {\n            return;\n        }\n        device->WriteInfo(android::base::StringPrintf(\"%s:%s\", name.c_str(), message.c_str()));\n        return;\n    }\n\n    auto all_args = handlers.get_all_args(device);\n    for (const auto& args : all_args) {\n        std::string message;\n        if (!handlers.get(device, args, &message)) {\n            continue;\n        }\n        std::string arg_string = android::base::Join(args, \":\");\n        device->WriteInfo(android::base::StringPrintf(\"%s:%s:%s\", name.c_str(), arg_string.c_str(),\n                                                      message.c_str()));\n    }\n}\n\nconst std::unordered_map<std::string, VariableHandlers> kVariableMap = {\n        {FB_VAR_VERSION, {GetVersion, nullptr}},\n        {FB_VAR_VERSION_BOOTLOADER, {GetBootloaderVersion, nullptr}},\n        {FB_VAR_VERSION_BASEBAND, {GetBasebandVersion, nullptr}},\n        {FB_VAR_VERSION_OS, {GetOsVersion, nullptr}},\n        {FB_VAR_VERSION_VNDK, {GetVndkVersion, nullptr}},\n        {FB_VAR_PRODUCT, {GetProduct, nullptr}},\n        {FB_VAR_SERIALNO, {GetSerial, nullptr}},\n        {FB_VAR_VARIANT, {GetVariant, nullptr}},\n        {FB_VAR_SECURE, {GetSecure, nullptr}},\n        {FB_VAR_UNLOCKED, {GetUnlocked, nullptr}},\n        {FB_VAR_MAX_DOWNLOAD_SIZE, {GetMaxDownloadSize, nullptr}},\n        {FB_VAR_CURRENT_SLOT, {::GetCurrentSlot, nullptr}},\n        {FB_VAR_SLOT_COUNT, {GetSlotCount, nullptr}},\n        {FB_VAR_HAS_SLOT, {GetHasSlot, GetAllPartitionArgsNoSlot}},\n        {FB_VAR_SLOT_SUCCESSFUL, {GetSlotSuccessful, nullptr}},\n        {FB_VAR_SLOT_UNBOOTABLE, {GetSlotUnbootable, nullptr}},\n        {FB_VAR_PARTITION_SIZE, {GetPartitionSize, GetAllPartitionArgsWithSlot}},\n        {FB_VAR_PARTITION_TYPE, {GetPartitionType, GetAllPartitionArgsWithSlot}},\n        {FB_VAR_IS_LOGICAL, {GetPartitionIsLogical, GetAllPartitionArgsWithSlot}},\n        {FB_VAR_IS_USERSPACE, {GetIsUserspace, nullptr}},\n        {FB_VAR_IS_FORCE_DEBUGGABLE, {GetIsForceDebuggable, nullptr}},\n        {FB_VAR_OFF_MODE_CHARGE_STATE, {GetOffModeChargeState, nullptr}},\n        {FB_VAR_BATTERY_VOLTAGE, {GetBatteryVoltage, nullptr}},\n        {FB_VAR_BATTERY_SOC, {GetBatterySoC, nullptr}},\n        {FB_VAR_BATTERY_SOC_OK, {GetBatterySoCOk, nullptr}},\n        {FB_VAR_HW_REVISION, {GetHardwareRevision, nullptr}},\n        {FB_VAR_SUPER_PARTITION_NAME, {GetSuperPartitionName, nullptr}},\n        {FB_VAR_SNAPSHOT_UPDATE_STATUS, {GetSnapshotUpdateStatus, nullptr}},\n        {FB_VAR_CPU_ABI, {GetCpuAbi, nullptr}},\n        {FB_VAR_SYSTEM_FINGERPRINT, {GetSystemFingerprint, nullptr}},\n        {FB_VAR_VENDOR_FINGERPRINT, {GetVendorFingerprint, nullptr}},\n        {FB_VAR_DYNAMIC_PARTITION, {GetDynamicPartition, nullptr}},\n        {FB_VAR_FIRST_API_LEVEL, {GetFirstApiLevel, nullptr}},\n        {FB_VAR_SECURITY_PATCH_LEVEL, {GetSecurityPatchLevel, nullptr}},\n        {FB_VAR_TREBLE_ENABLED, {GetTrebleEnabled, nullptr}},\n        {FB_VAR_MAX_FETCH_SIZE, {GetMaxFetchSize, nullptr}},\n        {FB_VAR_BATTERY_SERIAL_NUMBER, {GetBatterySerialNumber, nullptr}},\n        {FB_VAR_BATTERY_PART_STATUS, {GetBatteryPartStatus, nullptr}},\n};\n\nstatic bool GetVarAll(FastbootDevice* device) {\n    for (const auto& [name, handlers] : kVariableMap) {\n        GetAllVars(device, name, handlers);\n    }\n    return true;\n}\n\nstatic void PostWipeData() {\n    std::string err;\n    // Reset mte state of device.\n    if (!WriteMiscMemtagMessage({}, &err)) {\n        LOG(ERROR) << \"Failed to reset MTE state: \" << err;\n    }\n}\n\nconst std::unordered_map<std::string, std::function<bool(FastbootDevice*)>> kSpecialVars = {\n        {\"all\", GetVarAll},\n        {\"dmesg\", GetDmesg},\n};\n\nbool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 2) {\n        return device->WriteFail(\"Missing argument\");\n    }\n\n    // \"all\" and \"dmesg\" are multiline and handled specially.\n    auto found_special = kSpecialVars.find(args[1]);\n    if (found_special != kSpecialVars.end()) {\n        if (!found_special->second(device)) {\n            return false;\n        }\n        return device->WriteOkay(\"\");\n    }\n\n    // args[0] is command name, args[1] is variable.\n    auto found_variable = kVariableMap.find(args[1]);\n    if (found_variable == kVariableMap.end()) {\n        return device->WriteFail(\"Unknown variable\");\n    }\n\n    std::string message;\n    std::vector<std::string> getvar_args(args.begin() + 2, args.end());\n    if (!found_variable->second.get(device, getvar_args, &message)) {\n        return device->WriteFail(message);\n    }\n    return device->WriteOkay(message);\n}\n\nbool OemPostWipeData(FastbootDevice* device) {\n    auto fastboot_hal = device->fastboot_hal();\n    if (!fastboot_hal) {\n        return false;\n    }\n\n    auto status = fastboot_hal->doOemSpecificErase();\n    if (status.isOk()) {\n        device->WriteStatus(FastbootResult::OKAY, \"Erasing succeeded\");\n        return true;\n    }\n    switch (status.getExceptionCode()) {\n        case EX_UNSUPPORTED_OPERATION:\n            return false;\n        case EX_SERVICE_SPECIFIC:\n            device->WriteStatus(FastbootResult::FAIL, status.getDescription());\n            return false;\n        default:\n            LOG(ERROR) << \"Erase operation failed\" << status.getDescription();\n            return false;\n    }\n}\n\nbool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 2) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Invalid arguments\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Erase is not allowed on locked devices\");\n    }\n\n    const auto& partition_name = args[1];\n    if (IsProtectedPartitionDuringMerge(device, partition_name)) {\n        auto message = \"Cannot erase \" + partition_name + \" while a snapshot update is in progress\";\n        return device->WriteFail(message);\n    }\n\n    PartitionHandle handle;\n    if (!OpenPartition(device, partition_name, &handle)) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Partition doesn't exist\");\n    }\n    if (wipe_block_device(handle.fd(), get_block_device_size(handle.fd())) == 0) {\n        //Perform oem PostWipeData if Android userdata partition has been erased\n        bool support_oem_postwipedata = false;\n        if (partition_name == \"userdata\") {\n            PostWipeData();\n            support_oem_postwipedata = OemPostWipeData(device);\n        }\n\n        if (!support_oem_postwipedata) {\n            return device->WriteStatus(FastbootResult::OKAY, \"Erasing succeeded\");\n        } else {\n            //Write device status in OemPostWipeData(), so just return true\n            return true;\n        }\n    }\n    return device->WriteStatus(FastbootResult::FAIL, \"Erasing failed\");\n}\n\nbool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    auto fastboot_hal = device->fastboot_hal();\n    if (!fastboot_hal) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Unable to open fastboot HAL\");\n    }\n\n    //Disable \"oem postwipedata userdata\" to prevent user wipe oem userdata only.\n    if (args[0] == \"oem postwipedata userdata\") {\n        return device->WriteStatus(FastbootResult::FAIL, \"Unable to do oem postwipedata userdata\");\n    }\n    std::string message;\n    auto status = fastboot_hal->doOemCommand(args[0], &message);\n    if (!status.isOk()) {\n        LOG(ERROR) << \"Unable to do OEM command \" << args[0].c_str() << status.getDescription();\n        return device->WriteStatus(FastbootResult::FAIL,\n                                   \"Unable to do OEM command \" + status.getDescription());\n    }\n\n    device->WriteInfo(message);\n    return device->WriteStatus(FastbootResult::OKAY, message);\n}\n\nbool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 2) {\n        return device->WriteStatus(FastbootResult::FAIL, \"size argument unspecified\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL,\n                                   \"Download is not allowed on locked devices\");\n    }\n\n    // arg[0] is the command name, arg[1] contains size of data to be downloaded\n    // which should always be 8 bytes\n    if (args[1].length() != 8) {\n        return device->WriteStatus(FastbootResult::FAIL,\n                                   \"Invalid size (length of size != 8)\");\n    }\n    unsigned int size;\n    if (!android::base::ParseUint(\"0x\" + args[1], &size, kMaxDownloadSizeDefault)) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Invalid size\");\n    }\n    if (size == 0) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Invalid size (0)\");\n    }\n    device->download_data().resize(size);\n    if (!device->WriteStatus(FastbootResult::DATA, android::base::StringPrintf(\"%08x\", size))) {\n        return false;\n    }\n\n    if (device->HandleData(true, &device->download_data())) {\n        return device->WriteStatus(FastbootResult::OKAY, \"\");\n    }\n\n    PLOG(ERROR) << \"Couldn't download data\";\n    return device->WriteStatus(FastbootResult::FAIL, \"Couldn't download data\");\n}\n\nbool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 2) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Missing slot argument\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL,\n                                   \"set_active command is not allowed on locked devices\");\n    }\n\n    int32_t slot = 0;\n    if (!GetSlotNumber(args[1], &slot)) {\n        // Slot suffix needs to be between 'a' and 'z'.\n        return device->WriteStatus(FastbootResult::FAIL, \"Bad slot suffix\");\n    }\n\n    // Non-A/B devices will not have a boot control HAL.\n    auto boot_control_hal = device->boot_control_hal();\n    if (!boot_control_hal) {\n        return device->WriteStatus(FastbootResult::FAIL,\n                                   \"Cannot set slot: boot control HAL absent\");\n    }\n    if (slot >= boot_control_hal->GetNumSlots()) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Slot out of range\");\n    }\n\n    // If the slot is not changing, do nothing.\n    if (args[1] == device->GetCurrentSlot()) {\n        return device->WriteOkay(\"\");\n    }\n\n    // Check how to handle the current snapshot state.\n    if (auto hal11 = device->boot1_1()) {\n        auto merge_status = hal11->getSnapshotMergeStatus();\n        if (merge_status == MergeStatus::MERGING) {\n            return device->WriteFail(\"Cannot change slots while a snapshot update is in progress\");\n        }\n        // Note: we allow the slot change if the state is SNAPSHOTTED. First-\n        // stage init does not have access to the HAL, and uses the slot number\n        // and /metadata OTA state to determine whether a slot change occurred.\n        // Booting into the old slot would erase the OTA, and switching A->B->A\n        // would simply resume it if no boots occur in between. Re-flashing\n        // partitions implicitly cancels the OTA, so leaving the state as-is is\n        // safe.\n        if (merge_status == MergeStatus::SNAPSHOTTED) {\n            device->WriteInfo(\n                    \"Changing the active slot with a snapshot applied may cancel the\"\n                    \" update.\");\n        }\n    }\n\n    CommandResult ret = boot_control_hal->SetActiveBootSlot(slot);\n    if (ret.success) {\n        // Save as slot suffix to match the suffix format as returned from\n        // the boot control HAL.\n        auto current_slot = \"_\" + args[1];\n        device->set_active_slot(current_slot);\n        return device->WriteStatus(FastbootResult::OKAY, \"\");\n    }\n    return device->WriteStatus(FastbootResult::FAIL, \"Unable to set slot\");\n}\n\nbool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {\n    auto result = device->WriteStatus(FastbootResult::OKAY, \"Shutting down\");\n    android::base::SetProperty(ANDROID_RB_PROPERTY, \"shutdown,fastboot\");\n    device->CloseDevice();\n    TEMP_FAILURE_RETRY(pause());\n    return result;\n}\n\nbool RebootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {\n    auto result = device->WriteStatus(FastbootResult::OKAY, \"Rebooting\");\n    android::base::SetProperty(ANDROID_RB_PROPERTY, \"reboot,from_fastboot\");\n    device->CloseDevice();\n    TEMP_FAILURE_RETRY(pause());\n    return result;\n}\n\nbool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {\n    auto result = device->WriteStatus(FastbootResult::OKAY, \"Rebooting bootloader\");\n    android::base::SetProperty(ANDROID_RB_PROPERTY, \"reboot,bootloader\");\n    device->CloseDevice();\n    TEMP_FAILURE_RETRY(pause());\n    return result;\n}\n\nbool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {\n    auto result = device->WriteStatus(FastbootResult::OKAY, \"Rebooting fastboot\");\n    android::base::SetProperty(ANDROID_RB_PROPERTY, \"reboot,fastboot\");\n    device->CloseDevice();\n    TEMP_FAILURE_RETRY(pause());\n    return result;\n}\n\nstatic bool EnterRecovery() {\n    const char msg_switch_to_recovery = 'r';\n\n    android::base::unique_fd sock(socket(AF_UNIX, SOCK_STREAM, 0));\n    if (sock < 0) {\n        PLOG(ERROR) << \"Couldn't create sock\";\n        return false;\n    }\n\n    struct sockaddr_un addr = {.sun_family = AF_UNIX};\n    strncpy(addr.sun_path, \"/dev/socket/recovery\", sizeof(addr.sun_path) - 1);\n    if (connect(sock.get(), (struct sockaddr*)&addr, sizeof(addr)) < 0) {\n        PLOG(ERROR) << \"Couldn't connect to recovery\";\n        return false;\n    }\n    // Switch to recovery will not update the boot reason since it does not\n    // require a reboot.\n    auto ret = write(sock.get(), &msg_switch_to_recovery, sizeof(msg_switch_to_recovery));\n    if (ret != sizeof(msg_switch_to_recovery)) {\n        PLOG(ERROR) << \"Couldn't write message to switch to recovery\";\n        return false;\n    }\n\n    return true;\n}\n\nbool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& /* args */) {\n    auto status = true;\n    if (EnterRecovery()) {\n        status = device->WriteStatus(FastbootResult::OKAY, \"Rebooting to recovery\");\n    } else {\n        status = device->WriteStatus(FastbootResult::FAIL, \"Unable to reboot to recovery\");\n    }\n    device->CloseDevice();\n    TEMP_FAILURE_RETRY(pause());\n    return status;\n}\n\n// Helper class for opening a handle to a MetadataBuilder and writing the new\n// partition table to the same place it was read.\nclass PartitionBuilder {\n  public:\n    explicit PartitionBuilder(FastbootDevice* device, const std::string& partition_name);\n\n    bool Write();\n    bool Valid() const { return !!builder_; }\n    MetadataBuilder* operator->() const { return builder_.get(); }\n\n  private:\n    FastbootDevice* device_;\n    std::string super_device_;\n    uint32_t slot_number_;\n    std::unique_ptr<MetadataBuilder> builder_;\n};\n\nPartitionBuilder::PartitionBuilder(FastbootDevice* device, const std::string& partition_name)\n    : device_(device) {\n    std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);\n    slot_number_ = android::fs_mgr::SlotNumberForSlotSuffix(slot_suffix);\n    auto super_device = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number_));\n    if (!super_device) {\n        return;\n    }\n    super_device_ = *super_device;\n    builder_ = MetadataBuilder::New(super_device_, slot_number_);\n}\n\nbool PartitionBuilder::Write() {\n    auto metadata = builder_->Export();\n    if (!metadata) {\n        return false;\n    }\n    return UpdateAllPartitionMetadata(device_, super_device_, *metadata.get());\n}\n\nbool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 3) {\n        return device->WriteFail(\"Invalid partition name and size\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Command not available on locked devices\");\n    }\n\n    uint64_t partition_size;\n    std::string partition_name = args[1];\n    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {\n        return device->WriteFail(\"Invalid partition size\");\n    }\n\n    PartitionBuilder builder(device, partition_name);\n    if (!builder.Valid()) {\n        return device->WriteFail(\"Could not open super partition\");\n    }\n    // TODO(112433293) Disallow if the name is in the physical table as well.\n    if (builder->FindPartition(partition_name)) {\n        return device->WriteFail(\"Partition already exists\");\n    }\n\n    auto partition = builder->AddPartition(partition_name, 0);\n    if (!partition) {\n        return device->WriteFail(\"Failed to add partition\");\n    }\n    if (!builder->ResizePartition(partition, partition_size)) {\n        builder->RemovePartition(partition_name);\n        return device->WriteFail(\"Not enough space for partition\");\n    }\n    if (!builder.Write()) {\n        return device->WriteFail(\"Failed to write partition table\");\n    }\n    return device->WriteOkay(\"Partition created\");\n}\n\nbool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 2) {\n        return device->WriteFail(\"Invalid partition name and size\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Command not available on locked devices\");\n    }\n\n    std::string partition_name = args[1];\n\n    PartitionBuilder builder(device, partition_name);\n    if (!builder.Valid()) {\n        return device->WriteFail(\"Could not open super partition\");\n    }\n    builder->RemovePartition(partition_name);\n    if (!builder.Write()) {\n        return device->WriteFail(\"Failed to write partition table\");\n    }\n    return device->WriteOkay(\"Partition deleted\");\n}\n\nbool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 3) {\n        return device->WriteFail(\"Invalid partition name and size\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Command not available on locked devices\");\n    }\n\n    uint64_t partition_size;\n    std::string partition_name = args[1];\n    if (!android::base::ParseUint(args[2].c_str(), &partition_size)) {\n        return device->WriteFail(\"Invalid partition size\");\n    }\n\n    PartitionBuilder builder(device, partition_name);\n    if (!builder.Valid()) {\n        return device->WriteFail(\"Could not open super partition\");\n    }\n\n    auto partition = builder->FindPartition(partition_name);\n    if (!partition) {\n        return device->WriteFail(\"Partition does not exist\");\n    }\n\n    // Remove the updated flag to cancel any snapshots.\n    uint32_t attrs = partition->attributes();\n    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);\n\n    if (!builder->ResizePartition(partition, partition_size)) {\n        return device->WriteFail(\"Not enough space to resize partition\");\n    }\n    if (!builder.Write()) {\n        return device->WriteFail(\"Failed to write partition table\");\n    }\n    return device->WriteOkay(\"Partition resized\");\n}\n\nvoid CancelPartitionSnapshot(FastbootDevice* device, const std::string& partition_name) {\n    PartitionBuilder builder(device, partition_name);\n    if (!builder.Valid()) return;\n\n    auto partition = builder->FindPartition(partition_name);\n    if (!partition) return;\n\n    // Remove the updated flag to cancel any snapshots.\n    uint32_t attrs = partition->attributes();\n    partition->set_attributes(attrs & ~LP_PARTITION_ATTR_UPDATED);\n\n    builder.Write();\n}\n\nbool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 2) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Invalid arguments\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL,\n                                   \"Flashing is not allowed on locked devices\");\n    }\n\n    const auto& partition_name = args[1];\n    if (IsProtectedPartitionDuringMerge(device, partition_name)) {\n        auto message = \"Cannot flash \" + partition_name + \" while a snapshot update is in progress\";\n        return device->WriteFail(message);\n    }\n\n    if (LogicalPartitionExists(device, partition_name)) {\n        CancelPartitionSnapshot(device, partition_name);\n    }\n\n    int ret = Flash(device, partition_name);\n    if (ret < 0) {\n        return device->WriteStatus(FastbootResult::FAIL, strerror(-ret));\n    }\n    if (partition_name == \"userdata\") {\n        PostWipeData();\n    }\n\n    return device->WriteStatus(FastbootResult::OKAY, \"Flashing succeeded\");\n}\n\nbool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() < 2) {\n        return device->WriteFail(\"Invalid arguments\");\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteStatus(FastbootResult::FAIL, \"Command not available on locked devices\");\n    }\n\n    bool wipe = (args.size() >= 3 && args[2] == \"wipe\");\n    return UpdateSuper(device, args[1], wipe);\n}\n\nstatic bool IsLockedDsu() {\n    std::string active_dsu;\n    android::gsi::GetActiveDsu(&active_dsu);\n    return android::base::EndsWith(active_dsu, \".lock\");\n}\n\nbool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    if (args.size() != 2) {\n        return device->WriteFail(\"Invalid arguments\");\n    }\n\n    AutoMountMetadata mount_metadata;\n    if (!mount_metadata) {\n        return device->WriteFail(\"Could not find GSI install\");\n    }\n\n    if (!android::gsi::IsGsiInstalled()) {\n        return device->WriteStatus(FastbootResult::FAIL, \"No GSI is installed\");\n    }\n\n    if ((args[1] == \"wipe\" || args[1] == \"disable\") && GetDeviceLockStatus() && IsLockedDsu()) {\n        // Block commands that modify the states of locked DSU\n        return device->WriteFail(\"Command not available on locked DSU/devices\");\n    }\n\n    if (args[1] == \"wipe\") {\n        if (!android::gsi::UninstallGsi()) {\n            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));\n        }\n    } else if (args[1] == \"disable\") {\n        if (!android::gsi::DisableGsi()) {\n            return device->WriteStatus(FastbootResult::FAIL, strerror(errno));\n        }\n    } else if (args[1] == \"status\") {\n        std::string active_dsu;\n        if (!android::gsi::IsGsiRunning()) {\n            device->WriteInfo(\"Not running\");\n        } else if (!android::gsi::GetActiveDsu(&active_dsu)) {\n            return device->WriteFail(strerror(errno));\n        } else {\n            device->WriteInfo(\"Running active DSU: \" + active_dsu);\n        }\n    } else {\n        return device->WriteFail(\"Invalid arguments\");\n    }\n    return device->WriteStatus(FastbootResult::OKAY, \"Success\");\n}\n\nbool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    // Note that we use the HAL rather than mounting /metadata, since we want\n    // our results to match the bootloader.\n    auto hal = device->boot1_1();\n    if (!hal) return device->WriteFail(\"Not supported\");\n\n    // If no arguments, return the same thing as a getvar. Note that we get the\n    // HAL first so we can return \"not supported\" before we return the less\n    // specific error message below.\n    if (args.size() < 2 || args[1].empty()) {\n        std::string message;\n        if (!GetSnapshotUpdateStatus(device, {}, &message)) {\n            return device->WriteFail(\"Could not determine update status\");\n        }\n        device->WriteInfo(message);\n        return device->WriteOkay(\"\");\n    }\n\n    MergeStatus status = hal->getSnapshotMergeStatus();\n\n    if (args.size() != 2) {\n        return device->WriteFail(\"Invalid arguments\");\n    }\n    if (args[1] == \"cancel\") {\n        switch (status) {\n            case MergeStatus::SNAPSHOTTED:\n            case MergeStatus::MERGING: {\n                const auto ret = hal->SetSnapshotMergeStatus(MergeStatus::CANCELLED);\n                if (!ret.success) {\n                    device->WriteFail(\"Failed to SetSnapshotMergeStatus(MergeStatus::CANCELLED) \" +\n                                      ret.errMsg);\n                }\n                break;\n            }\n            default:\n                break;\n        }\n    } else if (args[1] == \"merge\") {\n        if (status != MergeStatus::MERGING) {\n            return device->WriteFail(\"No snapshot merge is in progress\");\n        }\n\n        auto sm = SnapshotManager::New();\n        if (!sm) {\n            return device->WriteFail(\"Unable to create SnapshotManager\");\n        }\n        if (!sm->FinishMergeInRecovery()) {\n            return device->WriteFail(\"Unable to finish snapshot merge\");\n        }\n    } else {\n        return device->WriteFail(\"Invalid parameter to snapshot-update\");\n    }\n    return device->WriteStatus(FastbootResult::OKAY, \"Success\");\n}\n\nnamespace {\n// Helper of FetchHandler.\nclass PartitionFetcher {\n  public:\n    static bool Fetch(FastbootDevice* device, const std::vector<std::string>& args) {\n        if constexpr (!kEnableFetch) {\n            return device->WriteFail(\"Fetch is not allowed on user build\");\n        }\n\n        if (GetDeviceLockStatus()) {\n            return device->WriteFail(\"Fetch is not allowed on locked devices\");\n        }\n\n        PartitionFetcher fetcher(device, args);\n        if (fetcher.Open()) {\n            fetcher.Fetch();\n        }\n        CHECK(fetcher.ret_.has_value());\n        return *fetcher.ret_;\n    }\n\n  private:\n    PartitionFetcher(FastbootDevice* device, const std::vector<std::string>& args)\n        : device_(device), args_(&args) {}\n    // Return whether the partition is successfully opened.\n    // If successfully opened, ret_ is left untouched. Otherwise, ret_ is set to the value\n    // that FetchHandler should return.\n    bool Open() {\n        if (args_->size() < 2) {\n            ret_ = device_->WriteFail(\"Missing partition arg\");\n            return false;\n        }\n\n        partition_name_ = args_->at(1);\n        if (std::find(kAllowedPartitions.begin(), kAllowedPartitions.end(), partition_name_) ==\n            kAllowedPartitions.end()) {\n            ret_ = device_->WriteFail(\"Fetch is only allowed on [\" +\n                                      android::base::Join(kAllowedPartitions, \", \") + \"]\");\n            return false;\n        }\n\n        if (!OpenPartition(device_, partition_name_, &handle_, O_RDONLY)) {\n            ret_ = device_->WriteFail(\n                    android::base::StringPrintf(\"Cannot open %s\", partition_name_.c_str()));\n            return false;\n        }\n\n        partition_size_ = get_block_device_size(handle_.fd());\n        if (partition_size_ == 0) {\n            ret_ = device_->WriteOkay(android::base::StringPrintf(\"Partition %s has size 0\",\n                                                                  partition_name_.c_str()));\n            return false;\n        }\n\n        start_offset_ = 0;\n        if (args_->size() >= 3) {\n            if (!android::base::ParseUint(args_->at(2), &start_offset_)) {\n                ret_ = device_->WriteFail(\"Invalid offset, must be integer\");\n                return false;\n            }\n            if (start_offset_ > std::numeric_limits<off64_t>::max()) {\n                ret_ = device_->WriteFail(\n                        android::base::StringPrintf(\"Offset overflows: %\" PRIx64, start_offset_));\n                return false;\n            }\n        }\n        if (start_offset_ > partition_size_) {\n            ret_ = device_->WriteFail(android::base::StringPrintf(\n                    \"Invalid offset 0x%\" PRIx64 \", partition %s has size 0x%\" PRIx64, start_offset_,\n                    partition_name_.c_str(), partition_size_));\n            return false;\n        }\n        uint64_t maximum_total_size_to_read = partition_size_ - start_offset_;\n        total_size_to_read_ = maximum_total_size_to_read;\n        if (args_->size() >= 4) {\n            if (!android::base::ParseUint(args_->at(3), &total_size_to_read_)) {\n                ret_ = device_->WriteStatus(FastbootResult::FAIL, \"Invalid size, must be integer\");\n                return false;\n            }\n        }\n        if (total_size_to_read_ == 0) {\n            ret_ = device_->WriteOkay(\"Read 0 bytes\");\n            return false;\n        }\n        if (total_size_to_read_ > maximum_total_size_to_read) {\n            ret_ = device_->WriteFail(android::base::StringPrintf(\n                    \"Invalid size to read 0x%\" PRIx64 \", partition %s has size 0x%\" PRIx64\n                    \" and fetching from offset 0x%\" PRIx64,\n                    total_size_to_read_, partition_name_.c_str(), partition_size_, start_offset_));\n            return false;\n        }\n\n        if (total_size_to_read_ > kMaxFetchSizeDefault) {\n            ret_ = device_->WriteFail(android::base::StringPrintf(\n                    \"Cannot fetch 0x%\" PRIx64\n                    \" bytes because it exceeds maximum transport size 0x%x\",\n                    partition_size_, kMaxDownloadSizeDefault));\n            return false;\n        }\n\n        return true;\n    }\n\n    // Assume Open() returns true.\n    // After execution, ret_ is set to the value that FetchHandler should return.\n    void Fetch() {\n        CHECK(start_offset_ <= std::numeric_limits<off64_t>::max());\n        if (lseek64(handle_.fd(), start_offset_, SEEK_SET) != static_cast<off64_t>(start_offset_)) {\n            ret_ = device_->WriteFail(android::base::StringPrintf(\n                    \"On partition %s, unable to lseek(0x%\" PRIx64 \": %s\", partition_name_.c_str(),\n                    start_offset_, strerror(errno)));\n            return;\n        }\n\n        if (!device_->WriteStatus(FastbootResult::DATA,\n                                  android::base::StringPrintf(\n                                          \"%08x\", static_cast<uint32_t>(total_size_to_read_)))) {\n            ret_ = false;\n            return;\n        }\n        uint64_t end_offset = start_offset_ + total_size_to_read_;\n        std::vector<char> buf(1_MiB);\n        uint64_t current_offset = start_offset_;\n        while (current_offset < end_offset) {\n            // On any error, exit. We can't return a status message to the driver because\n            // we are in the middle of writing data, so just let the driver guess what's wrong\n            // by ending the data stream prematurely.\n            uint64_t remaining = end_offset - current_offset;\n            uint64_t chunk_size = std::min<uint64_t>(buf.size(), remaining);\n            if (!android::base::ReadFully(handle_.fd(), buf.data(), chunk_size)) {\n                PLOG(ERROR) << std::hex << \"Unable to read 0x\" << chunk_size << \" bytes from \"\n                            << partition_name_ << \" @ offset 0x\" << current_offset;\n                ret_ = false;\n                return;\n            }\n            if (!device_->HandleData(false /* is read */, buf.data(), chunk_size)) {\n                PLOG(ERROR) << std::hex << \"Unable to send 0x\" << chunk_size << \" bytes of \"\n                            << partition_name_ << \" @ offset 0x\" << current_offset;\n                ret_ = false;\n                return;\n            }\n            current_offset += chunk_size;\n        }\n\n        ret_ = device_->WriteOkay(android::base::StringPrintf(\n                \"Fetched %s (offset=0x%\" PRIx64 \", size=0x%\" PRIx64, partition_name_.c_str(),\n                start_offset_, total_size_to_read_));\n    }\n\n    static constexpr std::array<const char*, 3> kAllowedPartitions{\n            \"vendor_boot\",\n            \"vendor_boot_a\",\n            \"vendor_boot_b\",\n    };\n\n    FastbootDevice* device_;\n    const std::vector<std::string>* args_ = nullptr;\n    std::string partition_name_;\n    PartitionHandle handle_;\n    uint64_t partition_size_ = 0;\n    uint64_t start_offset_ = 0;\n    uint64_t total_size_to_read_ = 0;\n\n    // What FetchHandler should return.\n    std::optional<bool> ret_ = std::nullopt;\n};\n}  // namespace\n\nbool FetchHandler(FastbootDevice* device, const std::vector<std::string>& args) {\n    return PartitionFetcher::Fetch(device, args);\n}\n"
  },
  {
    "path": "fastboot/device/commands.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <functional>\n#include <string>\n#include <vector>\n\nconstexpr unsigned int kMaxDownloadSizeDefault = 0x10000000;\nconstexpr unsigned int kMaxFetchSizeDefault = 0x10000000;\n\nclass FastbootDevice;\n\nenum class FastbootResult {\n    OKAY,\n    FAIL,\n    INFO,\n    DATA,\n};\n\n// Execute a command with the given arguments (possibly empty).\nusing CommandHandler = std::function<bool(FastbootDevice*, const std::vector<std::string>&)>;\n\nbool DownloadHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool SetActiveHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool ShutDownHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool RebootHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool RebootBootloaderHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool RebootFastbootHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool RebootRecoveryHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool GetVarHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool EraseHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool FlashHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool CreatePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool DeletePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool ResizePartitionHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool UpdateSuperHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool OemCmdHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool GsiHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool SnapshotUpdateHandler(FastbootDevice* device, const std::vector<std::string>& args);\nbool FetchHandler(FastbootDevice* device, const std::vector<std::string>& args);\n"
  },
  {
    "path": "fastboot/device/fastboot_device.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fastboot_device.h\"\n\n#include <algorithm>\n\n#include <BootControlClient.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android/binder_manager.h>\n#include <android/hardware/boot/1.0/IBootControl.h>\n#include <android/hardware/fastboot/1.1/IFastboot.h>\n#include <fastbootshim.h>\n#include <fs_mgr.h>\n#include <fs_mgr/roots.h>\n#include <health-shim/shim.h>\n#include <healthhalutils/HealthHalUtils.h>\n\n#include \"constants.h\"\n#include \"flashing.h\"\n#include \"tcp_client.h\"\n#include \"usb_client.h\"\n\nusing std::string_literals::operator\"\"s;\nusing android::fs_mgr::EnsurePathUnmounted;\nusing android::fs_mgr::Fstab;\nusing ::android::hardware::hidl_string;\nusing ::android::hardware::fastboot::V1_1::IFastboot;\nusing BootControlClient = FastbootDevice::BootControlClient;\n\nnamespace sph = std::placeholders;\n\nstd::shared_ptr<aidl::android::hardware::health::IHealth> get_health_service() {\n    using aidl::android::hardware::health::IHealth;\n    using HidlHealth = android::hardware::health::V2_0::IHealth;\n    using aidl::android::hardware::health::HealthShim;\n    auto service_name = IHealth::descriptor + \"/default\"s;\n    if (AServiceManager_isDeclared(service_name.c_str())) {\n        ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));\n        std::shared_ptr<IHealth> health = IHealth::fromBinder(binder);\n        if (health != nullptr) return health;\n        LOG(WARNING) << \"AIDL health service is declared, but it cannot be retrieved.\";\n    }\n    LOG(INFO) << \"Unable to get AIDL health service, trying HIDL...\";\n    android::sp<HidlHealth> hidl_health = android::hardware::health::V2_0::get_health_service();\n    if (hidl_health != nullptr) {\n        return ndk::SharedRefBase::make<HealthShim>(hidl_health);\n    }\n    LOG(WARNING) << \"No health implementation is found.\";\n    return nullptr;\n}\n\nstd::shared_ptr<aidl::android::hardware::fastboot::IFastboot> get_fastboot_service() {\n    using aidl::android::hardware::fastboot::IFastboot;\n    using HidlFastboot = android::hardware::fastboot::V1_1::IFastboot;\n    using aidl::android::hardware::fastboot::FastbootShim;\n    auto service_name = IFastboot::descriptor + \"/default\"s;\n    if (AServiceManager_isDeclared(service_name.c_str())) {\n        ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));\n        std::shared_ptr<IFastboot> fastboot = IFastboot::fromBinder(binder);\n        if (fastboot != nullptr) {\n            LOG(INFO) << \"Found and using AIDL fastboot service\";\n            return fastboot;\n        }\n        LOG(WARNING) << \"AIDL fastboot service is declared, but it cannot be retrieved.\";\n    }\n    LOG(INFO) << \"Unable to get AIDL fastboot service, trying HIDL...\";\n    android::sp<HidlFastboot> hidl_fastboot = HidlFastboot::getService();\n    if (hidl_fastboot != nullptr) {\n        LOG(INFO) << \"Found and now using fastboot HIDL implementation\";\n        return ndk::SharedRefBase::make<FastbootShim>(hidl_fastboot);\n    }\n    LOG(WARNING) << \"No fastboot implementation is found.\";\n    return nullptr;\n}\n\nFastbootDevice::FastbootDevice()\n    : kCommandMap({\n              {FB_CMD_SET_ACTIVE, SetActiveHandler},\n              {FB_CMD_DOWNLOAD, DownloadHandler},\n              {FB_CMD_GETVAR, GetVarHandler},\n              {FB_CMD_SHUTDOWN, ShutDownHandler},\n              {FB_CMD_REBOOT, RebootHandler},\n              {FB_CMD_REBOOT_BOOTLOADER, RebootBootloaderHandler},\n              {FB_CMD_REBOOT_FASTBOOT, RebootFastbootHandler},\n              {FB_CMD_REBOOT_RECOVERY, RebootRecoveryHandler},\n              {FB_CMD_ERASE, EraseHandler},\n              {FB_CMD_FLASH, FlashHandler},\n              {FB_CMD_CREATE_PARTITION, CreatePartitionHandler},\n              {FB_CMD_DELETE_PARTITION, DeletePartitionHandler},\n              {FB_CMD_RESIZE_PARTITION, ResizePartitionHandler},\n              {FB_CMD_UPDATE_SUPER, UpdateSuperHandler},\n              {FB_CMD_OEM, OemCmdHandler},\n              {FB_CMD_GSI, GsiHandler},\n              {FB_CMD_SNAPSHOT_UPDATE, SnapshotUpdateHandler},\n              {FB_CMD_FETCH, FetchHandler},\n      }),\n      boot_control_hal_(BootControlClient::WaitForService()),\n      health_hal_(get_health_service()),\n      fastboot_hal_(get_fastboot_service()),\n      active_slot_(\"\") {\n    if (android::base::GetProperty(\"fastbootd.protocol\", \"usb\") == \"tcp\") {\n        transport_ = std::make_unique<ClientTcpTransport>();\n    } else {\n        transport_ = std::make_unique<ClientUsbTransport>();\n    }\n\n    // Make sure cache is unmounted, since recovery will have mounted it for\n    // logging.\n    Fstab fstab;\n    if (ReadDefaultFstab(&fstab)) {\n        EnsurePathUnmounted(&fstab, \"/cache\");\n    }\n}\n\nFastbootDevice::~FastbootDevice() {\n    CloseDevice();\n}\n\nvoid FastbootDevice::CloseDevice() {\n    transport_->Close();\n}\n\nstd::string FastbootDevice::GetCurrentSlot() {\n    // Check if a set_active ccommand was issued earlier since the boot control HAL\n    // returns the slot that is currently booted into.\n    if (!active_slot_.empty()) {\n        return active_slot_;\n    }\n    // Non-A/B devices must not have boot control HALs.\n    if (!boot_control_hal_) {\n        return \"\";\n    }\n    std::string suffix = boot_control_hal_->GetSuffix(boot_control_hal_->GetCurrentSlot());\n    return suffix;\n}\n\nBootControlClient* FastbootDevice::boot1_1() const {\n    if (boot_control_hal_ &&\n        boot_control_hal_->GetVersion() >= android::hal::BootControlVersion::BOOTCTL_V1_1) {\n        return boot_control_hal_.get();\n    }\n    return nullptr;\n}\n\nbool FastbootDevice::WriteStatus(FastbootResult result, const std::string& message) {\n    constexpr size_t kResponseReasonSize = 4;\n    constexpr size_t kNumResponseTypes = 4;  // \"FAIL\", \"OKAY\", \"INFO\", \"DATA\"\n\n    char buf[FB_RESPONSE_SZ];\n    constexpr size_t kMaxMessageSize = sizeof(buf) - kResponseReasonSize;\n    size_t msg_len = std::min(kMaxMessageSize, message.size());\n\n    constexpr const char* kResultStrings[kNumResponseTypes] = {RESPONSE_OKAY, RESPONSE_FAIL,\n                                                               RESPONSE_INFO, RESPONSE_DATA};\n\n    if (static_cast<size_t>(result) >= kNumResponseTypes) {\n        return false;\n    }\n\n    memcpy(buf, kResultStrings[static_cast<size_t>(result)], kResponseReasonSize);\n    memcpy(buf + kResponseReasonSize, message.c_str(), msg_len);\n\n    size_t response_len = kResponseReasonSize + msg_len;\n    auto write_ret = this->get_transport()->Write(buf, response_len);\n    if (write_ret != static_cast<ssize_t>(response_len)) {\n        PLOG(ERROR) << \"Failed to write \" << message;\n        return false;\n    }\n\n    return true;\n}\n\nbool FastbootDevice::HandleData(bool read, std::vector<char>* data) {\n    return HandleData(read, data->data(), data->size());\n}\n\nbool FastbootDevice::HandleData(bool read, char* data, uint64_t size) {\n    auto read_write_data_size = read ? this->get_transport()->Read(data, size)\n                                     : this->get_transport()->Write(data, size);\n    if (read_write_data_size == -1) {\n        LOG(ERROR) << (read ? \"read from\" : \"write to\") << \" transport failed\";\n        return false;\n    }\n    if (static_cast<size_t>(read_write_data_size) != size) {\n        LOG(ERROR) << (read ? \"read\" : \"write\") << \" expected \" << size << \" bytes, got \"\n                   << read_write_data_size;\n        return false;\n    }\n    return true;\n}\n\nvoid FastbootDevice::ExecuteCommands() {\n    char command[FB_RESPONSE_SZ + 1];\n    for (;;) {\n        auto bytes_read = transport_->Read(command, FB_RESPONSE_SZ);\n        if (bytes_read == -1) {\n            PLOG(ERROR) << \"Couldn't read command\";\n            return;\n        }\n        if (std::count_if(command, command + bytes_read, iscntrl) != 0) {\n            WriteStatus(FastbootResult::FAIL,\n                        \"Command contains control character\");\n            continue;\n        }\n        command[bytes_read] = '\\0';\n\n        LOG(INFO) << \"Fastboot command: \" << command;\n\n        std::vector<std::string> args;\n        std::string cmd_name;\n        if (android::base::StartsWith(command, \"oem \")) {\n            args = {command};\n            cmd_name = FB_CMD_OEM;\n        } else {\n            args = android::base::Split(command, \":\");\n            cmd_name = args[0];\n        }\n\n        auto found_command = kCommandMap.find(cmd_name);\n        if (found_command == kCommandMap.end()) {\n            WriteStatus(FastbootResult::FAIL, \"Unrecognized command \" + args[0]);\n            continue;\n        }\n        if (!found_command->second(this, args)) {\n            return;\n        }\n    }\n}\n\nbool FastbootDevice::WriteOkay(const std::string& message) {\n    return WriteStatus(FastbootResult::OKAY, message);\n}\n\nbool FastbootDevice::WriteFail(const std::string& message) {\n    return WriteStatus(FastbootResult::FAIL, message);\n}\n\nbool FastbootDevice::WriteInfo(const std::string& message) {\n    return WriteStatus(FastbootResult::INFO, message);\n}\n"
  },
  {
    "path": "fastboot/device/fastboot_device.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include <BootControlClient.h>\n#include <aidl/android/hardware/fastboot/IFastboot.h>\n#include <aidl/android/hardware/health/IHealth.h>\n\n#include \"commands.h\"\n#include \"transport.h\"\n#include \"variables.h\"\n\nclass FastbootDevice {\n  public:\n    using BootControlClient = android::hal::BootControlClient;\n    FastbootDevice();\n    ~FastbootDevice();\n\n    void CloseDevice();\n    void ExecuteCommands();\n    bool WriteStatus(FastbootResult result, const std::string& message);\n    bool HandleData(bool read, std::vector<char>* data);\n    bool HandleData(bool read, char* data, uint64_t size);\n    std::string GetCurrentSlot();\n\n    // Shortcuts for writing status results.\n    bool WriteOkay(const std::string& message);\n    bool WriteFail(const std::string& message);\n    bool WriteInfo(const std::string& message);\n\n    std::vector<char>& download_data() { return download_data_; }\n    Transport* get_transport() { return transport_.get(); }\n    BootControlClient* boot_control_hal() const { return boot_control_hal_.get(); }\n    BootControlClient* boot1_1() const;\n    std::shared_ptr<aidl::android::hardware::fastboot::IFastboot> fastboot_hal() {\n        return fastboot_hal_;\n    }\n    std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal() { return health_hal_; }\n\n    void set_active_slot(const std::string& active_slot) { active_slot_ = active_slot; }\n\n  private:\n    const std::unordered_map<std::string, CommandHandler> kCommandMap;\n\n    std::unique_ptr<Transport> transport_;\n    std::unique_ptr<BootControlClient> boot_control_hal_;\n    std::shared_ptr<aidl::android::hardware::health::IHealth> health_hal_;\n    std::shared_ptr<aidl::android::hardware::fastboot::IFastboot> fastboot_hal_;\n    std::vector<char> download_data_;\n    std::string active_slot_;\n};\n"
  },
  {
    "path": "fastboot/device/flashing.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"flashing.h\"\n\n#include <fcntl.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n#include <set>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <ext4_utils/ext4_utils.h>\n#include <fs_mgr_overlayfs.h>\n#include <fstab/fstab.h>\n#include <libavb/libavb.h>\n#include <liblp/builder.h>\n#include <liblp/liblp.h>\n#include <libsnapshot/snapshot.h>\n#include <sparse/sparse.h>\n\n#include \"fastboot_device.h\"\n#include \"utility.h\"\n\nusing namespace android::fs_mgr;\nusing namespace std::literals;\n\nnamespace {\n\nconstexpr uint32_t SPARSE_HEADER_MAGIC = 0xed26ff3a;\n\nvoid WipeOverlayfsForPartition(FastbootDevice* device, const std::string& partition_name) {\n    // May be called, in the case of sparse data, multiple times so cache/skip.\n    static std::set<std::string> wiped;\n    if (wiped.find(partition_name) != wiped.end()) return;\n    wiped.insert(partition_name);\n    // Following appears to have a first time 2% impact on flashing speeds.\n\n    // Convert partition_name to a validated mount point and wipe.\n    Fstab fstab;\n    ReadDefaultFstab(&fstab);\n\n    std::optional<AutoMountMetadata> mount_metadata;\n    for (const auto& entry : fstab) {\n        auto partition = android::base::Basename(entry.mount_point);\n        if (\"/\" == entry.mount_point) {\n            partition = \"system\";\n        }\n\n        if ((partition + device->GetCurrentSlot()) == partition_name) {\n            mount_metadata.emplace();\n            android::fs_mgr::TeardownAllOverlayForMountPoint(entry.mount_point);\n        }\n    }\n}\n\n}  // namespace\n\nint FlashRawDataChunk(PartitionHandle* handle, const char* data, size_t len) {\n    size_t ret = 0;\n    const size_t max_write_size = 1048576;\n    void* aligned_buffer;\n\n    if (posix_memalign(&aligned_buffer, 4096, max_write_size)) {\n        PLOG(ERROR) << \"Failed to allocate write buffer\";\n        return -ENOMEM;\n    }\n\n    auto aligned_buffer_unique_ptr = std::unique_ptr<void, decltype(&free)>{aligned_buffer, free};\n\n    while (ret < len) {\n        int this_len = std::min(max_write_size, len - ret);\n        memcpy(aligned_buffer_unique_ptr.get(), data, this_len);\n        // In case of non 4KB aligned writes, reopen without O_DIRECT flag\n        if (this_len & 0xFFF) {\n            if (handle->Reset(O_WRONLY) != true) {\n                PLOG(ERROR) << \"Failed to reset file descriptor\";\n                return -1;\n            }\n        }\n\n        int this_ret = write(handle->fd(), aligned_buffer_unique_ptr.get(), this_len);\n        if (this_ret < 0) {\n            PLOG(ERROR) << \"Failed to flash data of len \" << len;\n            return -1;\n        }\n        data += this_ret;\n        ret += this_ret;\n    }\n    return 0;\n}\n\nint FlashRawData(PartitionHandle* handle, const std::vector<char>& downloaded_data) {\n    int ret = FlashRawDataChunk(handle, downloaded_data.data(), downloaded_data.size());\n    if (ret < 0) {\n        return -errno;\n    }\n    return ret;\n}\n\nint WriteCallback(void* priv, const void* data, size_t len) {\n    PartitionHandle* handle = reinterpret_cast<PartitionHandle*>(priv);\n    if (!data) {\n        if (lseek64(handle->fd(), len, SEEK_CUR) < 0) {\n            int rv = -errno;\n            PLOG(ERROR) << \"lseek failed\";\n            return rv;\n        }\n        return 0;\n    }\n    return FlashRawDataChunk(handle, reinterpret_cast<const char*>(data), len);\n}\n\nint FlashSparseData(PartitionHandle* handle, std::vector<char>& downloaded_data) {\n    struct sparse_file* file = sparse_file_import_buf(downloaded_data.data(),\n                                                      downloaded_data.size(), true, false);\n    if (!file) {\n        // Invalid sparse format\n        LOG(ERROR) << \"Unable to open sparse data for flashing\";\n        return -EINVAL;\n    }\n    return sparse_file_callback(file, false, false, WriteCallback, reinterpret_cast<void*>(handle));\n}\n\nint FlashBlockDevice(PartitionHandle* handle, std::vector<char>& downloaded_data) {\n    lseek64(handle->fd(), 0, SEEK_SET);\n    if (downloaded_data.size() >= sizeof(SPARSE_HEADER_MAGIC) &&\n        *reinterpret_cast<uint32_t*>(downloaded_data.data()) == SPARSE_HEADER_MAGIC) {\n        return FlashSparseData(handle, downloaded_data);\n    } else {\n        return FlashRawData(handle, downloaded_data);\n    }\n}\n\nstatic void CopyAVBFooter(std::vector<char>* data, const uint64_t block_device_size) {\n    if (data->size() < AVB_FOOTER_SIZE) {\n        return;\n    }\n    std::string footer;\n    uint64_t footer_offset = data->size() - AVB_FOOTER_SIZE;\n    for (int idx = 0; idx < AVB_FOOTER_MAGIC_LEN; idx++) {\n        footer.push_back(data->at(footer_offset + idx));\n    }\n    if (0 != footer.compare(AVB_FOOTER_MAGIC)) {\n        return;\n    }\n\n    // copy AVB footer from end of data to end of block device\n    uint64_t original_data_size = data->size();\n    data->resize(block_device_size, 0);\n    for (int idx = 0; idx < AVB_FOOTER_SIZE; idx++) {\n        data->at(block_device_size - 1 - idx) = data->at(original_data_size - 1 - idx);\n    }\n}\n\nint Flash(FastbootDevice* device, const std::string& partition_name) {\n    PartitionHandle handle;\n    if (!OpenPartition(device, partition_name, &handle, O_WRONLY | O_DIRECT)) {\n        return -ENOENT;\n    }\n\n    std::vector<char> data = std::move(device->download_data());\n    if (data.size() == 0) {\n        LOG(ERROR) << \"Cannot flash empty data vector\";\n        return -EINVAL;\n    }\n    uint64_t block_device_size = get_block_device_size(handle.fd());\n    if (data.size() > block_device_size) {\n        LOG(ERROR) << \"Cannot flash \" << data.size() << \" bytes to block device of size \"\n                   << block_device_size;\n        return -EOVERFLOW;\n    } else if (data.size() < block_device_size &&\n               (partition_name == \"boot\" || partition_name == \"boot_a\" ||\n                partition_name == \"boot_b\" || partition_name == \"init_boot\" ||\n                partition_name == \"init_boot_a\" || partition_name == \"init_boot_b\")) {\n        CopyAVBFooter(&data, block_device_size);\n    }\n    if (android::base::GetProperty(\"ro.system.build.type\", \"\") != \"user\") {\n        WipeOverlayfsForPartition(device, partition_name);\n    }\n    int result = FlashBlockDevice(&handle, data);\n    sync();\n    return result;\n}\n\nstatic void RemoveScratchPartition() {\n    AutoMountMetadata mount_metadata;\n    android::fs_mgr::TeardownAllOverlayForMountPoint();\n}\n\nbool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe) {\n    std::vector<char> data = std::move(device->download_data());\n    if (data.empty()) {\n        return device->WriteFail(\"No data available\");\n    }\n\n    std::unique_ptr<LpMetadata> new_metadata = ReadFromImageBlob(data.data(), data.size());\n    if (!new_metadata) {\n        return device->WriteFail(\"Data is not a valid logical partition metadata image\");\n    }\n\n    if (!FindPhysicalPartition(super_name)) {\n        return device->WriteFail(\"Cannot find \" + super_name +\n                                 \", build may be missing broken or missing boot_devices\");\n    }\n\n    std::string slot_suffix = device->GetCurrentSlot();\n    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);\n\n    std::string other_slot_suffix;\n    if (!slot_suffix.empty()) {\n        other_slot_suffix = (slot_suffix == \"_a\") ? \"_b\" : \"_a\";\n    }\n\n    // If we are unable to read the existing metadata, then the super partition\n    // is corrupt. In this case we reflash the whole thing using the provided\n    // image.\n    std::unique_ptr<LpMetadata> old_metadata = ReadMetadata(super_name, slot_number);\n    if (wipe || !old_metadata) {\n        if (!FlashPartitionTable(super_name, *new_metadata.get())) {\n            return device->WriteFail(\"Unable to flash new partition table\");\n        }\n        RemoveScratchPartition();\n        sync();\n        return device->WriteOkay(\"Successfully flashed partition table\");\n    }\n\n    std::set<std::string> partitions_to_keep;\n    bool virtual_ab = android::base::GetBoolProperty(\"ro.virtual_ab.enabled\", false);\n    for (const auto& partition : old_metadata->partitions) {\n        // Preserve partitions in the other slot, but not the current slot.\n        std::string partition_name = GetPartitionName(partition);\n        if (!slot_suffix.empty()) {\n            auto part_suffix = GetPartitionSlotSuffix(partition_name);\n            if (part_suffix == slot_suffix || (part_suffix == other_slot_suffix && virtual_ab)) {\n                continue;\n            }\n        }\n        std::string group_name = GetPartitionGroupName(old_metadata->groups[partition.group_index]);\n        // Skip partitions in the COW group\n        if (group_name == android::snapshot::kCowGroupName) {\n            continue;\n        }\n        partitions_to_keep.emplace(partition_name);\n    }\n\n    // Do not preserve the scratch partition.\n    partitions_to_keep.erase(\"scratch\");\n\n    if (!partitions_to_keep.empty()) {\n        std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(*new_metadata.get());\n        if (!builder->ImportPartitions(*old_metadata.get(), partitions_to_keep)) {\n            return device->WriteFail(\n                    \"Old partitions are not compatible with the new super layout; wipe needed\");\n        }\n\n        new_metadata = builder->Export();\n        if (!new_metadata) {\n            return device->WriteFail(\"Unable to build new partition table; wipe needed\");\n        }\n    }\n\n    // Write the new table to every metadata slot.\n    if (!UpdateAllPartitionMetadata(device, super_name, *new_metadata.get())) {\n        return device->WriteFail(\"Unable to write new partition table\");\n    }\n    RemoveScratchPartition();\n    sync();\n    return device->WriteOkay(\"Successfully updated partition table\");\n}\n"
  },
  {
    "path": "fastboot/device/flashing.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\nclass FastbootDevice;\n\nint Flash(FastbootDevice* device, const std::string& partition_name);\nbool UpdateSuper(FastbootDevice* device, const std::string& super_name, bool wipe);\n"
  },
  {
    "path": "fastboot/device/main.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdarg.h>\n\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <sparse/sparse.h>\n\n#include \"fastboot_device.h\"\n\nstatic void LogSparseVerboseMessage(const char* fmt, ...) {\n    std::string message;\n\n    va_list ap;\n    va_start(ap, fmt);\n    android::base::StringAppendV(&message, fmt, ap);\n    va_end(ap);\n\n    LOG(ERROR) << \"libsparse message: \" << message;\n}\n\nint main(int /*argc*/, char* argv[]) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n\n    sparse_print_verbose = LogSparseVerboseMessage;\n\n    while (true) {\n        FastbootDevice device;\n        device.ExecuteCommands();\n    }\n}\n"
  },
  {
    "path": "fastboot/device/tcp_client.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"tcp_client.h\"\n#include \"constants.h\"\n\n#include <android-base/errors.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n\nstatic constexpr int kDefaultPort = 5554;\nstatic constexpr int kProtocolVersion = 1;\nstatic constexpr int kHandshakeTimeoutMs = 2000;\nstatic constexpr size_t kHandshakeLength = 4;\n\n// Extract the big-endian 8-byte message length into a 64-bit number.\nstatic uint64_t ExtractMessageLength(const void* buffer) {\n    uint64_t ret = 0;\n    for (int i = 0; i < 8; ++i) {\n        ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);\n    }\n    return ret;\n}\n\n// Encode the 64-bit number into a big-endian 8-byte message length.\nstatic void EncodeMessageLength(uint64_t length, void* buffer) {\n    for (int i = 0; i < 8; ++i) {\n        reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);\n    }\n}\n\nClientTcpTransport::ClientTcpTransport() {\n    service_ = Socket::NewServer(Socket::Protocol::kTcp, kDefaultPort);\n\n    // A workaround to notify recovery to continue its work.\n    android::base::SetProperty(\"sys.usb.ffs.ready\", \"1\");\n}\n\nssize_t ClientTcpTransport::Read(void* data, size_t len) {\n    if (len > SSIZE_MAX) {\n        return -1;\n    }\n\n    size_t total_read = 0;\n    do {\n        // Read a new message\n        while (message_bytes_left_ == 0) {\n            if (socket_ == nullptr) {\n                ListenFastbootSocket();\n            }\n\n            char buffer[8];\n            if (socket_->ReceiveAll(buffer, 8, 0) == 8) {\n                message_bytes_left_ = ExtractMessageLength(buffer);\n            } else {\n                // If connection is closed by host, Receive will return 0 immediately.\n                socket_.reset(nullptr);\n                // In DATA phase, return error.\n                if (downloading_) {\n                    return -1;\n                }\n            }\n        }\n\n        size_t read_length = len - total_read;\n        if (read_length > message_bytes_left_) {\n            read_length = message_bytes_left_;\n        }\n        ssize_t bytes_read =\n                socket_->ReceiveAll(reinterpret_cast<char*>(data) + total_read, read_length, 0);\n        if (bytes_read == -1) {\n            socket_.reset(nullptr);\n            return -1;\n        } else {\n            message_bytes_left_ -= bytes_read;\n            total_read += bytes_read;\n        }\n    // There are more than one DATA phases if the downloading buffer is too\n    // large, like a very big system image. All of data phases should be\n    // received until the whole buffer is filled in that case.\n    } while (downloading_ && total_read < len);\n\n    return total_read;\n}\n\nssize_t ClientTcpTransport::Write(const void* data, size_t len) {\n    if (socket_ == nullptr || len > SSIZE_MAX) {\n        return -1;\n    }\n\n    // Use multi-buffer writes for better performance.\n    char header[8];\n    EncodeMessageLength(len, header);\n\n    if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, len}})) {\n        socket_.reset(nullptr);\n        return -1;\n    }\n\n    // In DATA phase\n    if (android::base::StartsWith(reinterpret_cast<const char*>(data), RESPONSE_DATA)) {\n        downloading_ = true;\n    } else {\n        downloading_ = false;\n    }\n\n    return len;\n}\n\nint ClientTcpTransport::Close() {\n    if (socket_ == nullptr) {\n        return -1;\n    }\n    socket_.reset(nullptr);\n\n    return 0;\n}\n\nint ClientTcpTransport::Reset() {\n    return Close();\n}\n\nvoid ClientTcpTransport::ListenFastbootSocket() {\n    while (true) {\n        socket_ = service_->Accept();\n\n        // Handshake\n        char buffer[kHandshakeLength + 1];\n        buffer[kHandshakeLength] = '\\0';\n        if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) !=\n            kHandshakeLength) {\n            PLOG(ERROR) << \"No Handshake message received\";\n            socket_.reset(nullptr);\n            continue;\n        }\n\n        if (memcmp(buffer, \"FB\", 2) != 0) {\n            PLOG(ERROR) << \"Unrecognized initialization message\";\n            socket_.reset(nullptr);\n            continue;\n        }\n\n        int version = 0;\n        if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {\n            LOG(ERROR) << \"Unknown TCP protocol version \" << buffer + 2\n                       << \", our version: \" << kProtocolVersion;\n            socket_.reset(nullptr);\n            continue;\n        }\n\n        std::string handshake_message(android::base::StringPrintf(\"FB%02d\", kProtocolVersion));\n        if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {\n            PLOG(ERROR) << \"Failed to send initialization message\";\n            socket_.reset(nullptr);\n            continue;\n        }\n\n        break;\n    }\n}\n"
  },
  {
    "path": "fastboot/device/tcp_client.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <memory>\n\n#include \"socket.h\"\n#include \"transport.h\"\n\nclass ClientTcpTransport : public Transport {\n  public:\n    ClientTcpTransport();\n    ~ClientTcpTransport() override = default;\n\n    ssize_t Read(void* data, size_t len) override;\n    ssize_t Write(const void* data, size_t len) override;\n    int Close() override;\n    int Reset() override;\n\n  private:\n    void ListenFastbootSocket();\n\n    std::unique_ptr<Socket> service_;\n    std::unique_ptr<Socket> socket_;\n    uint64_t message_bytes_left_ = 0;\n    bool downloading_ = false;\n\n    DISALLOW_COPY_AND_ASSIGN(ClientTcpTransport);\n};\n"
  },
  {
    "path": "fastboot/device/usb.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"usb.h\"\n#include \"usb_iouring.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/mman.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <linux/usb/ch9.h>\n#include <linux/usb/functionfs.h>\n#include <sys/utsname.h>\n\n#include <algorithm>\n#include <atomic>\n#include <chrono>\n#include <condition_variable>\n#include <mutex>\n#include <thread>\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <liburing.h>\n\nusing namespace std::chrono_literals;\n\n#define D(...)\n#define MAX_PACKET_SIZE_FS 64\n#define MAX_PACKET_SIZE_HS 512\n#define MAX_PACKET_SIZE_SS 1024\n\n#define USB_FFS_BULK_SIZE 16384\n\n// Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs.\n#define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1)\n\nstatic void aio_block_init(aio_block* aiob, unsigned num_bufs) {\n    aiob->iocb.resize(num_bufs);\n    aiob->iocbs.resize(num_bufs);\n    aiob->events.resize(num_bufs);\n    aiob->num_submitted = 0;\n    for (unsigned i = 0; i < num_bufs; i++) {\n        aiob->iocbs[i] = &aiob->iocb[i];\n    }\n    memset(&aiob->ctx, 0, sizeof(aiob->ctx));\n    if (io_setup(num_bufs, &aiob->ctx)) {\n        D(\"[ aio: got error on io_setup (%d) ]\", errno);\n    }\n}\n\nint getMaxPacketSize(int ffs_fd) {\n    usb_endpoint_descriptor desc{};\n    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {\n        D(\"[ could not get endpoint descriptor! (%d) ]\", errno);\n        return MAX_PACKET_SIZE_HS;\n    } else {\n        return desc.wMaxPacketSize;\n    }\n}\n\nstatic int usb_ffs_write(usb_handle* h, const void* data, int len) {\n    D(\"about to write (fd=%d, len=%d)\", h->bulk_in.get(), len);\n\n    const char* buf = static_cast<const char*>(data);\n    int orig_len = len;\n    while (len > 0) {\n        int write_len = std::min(USB_FFS_BULK_SIZE, len);\n        int n = write(h->bulk_in.get(), buf, write_len);\n        if (n < 0) {\n            D(\"ERROR: fd = %d, n = %d: %s\", h->bulk_in.get(), n, strerror(errno));\n            return -1;\n        }\n        buf += n;\n        len -= n;\n    }\n\n    D(\"[ done fd=%d ]\", h->bulk_in.get());\n    return orig_len;\n}\n\nstatic int usb_ffs_read(usb_handle* h, void* data, int len, bool allow_partial) {\n    D(\"about to read (fd=%d, len=%d)\", h->bulk_out.get(), len);\n\n    char* buf = static_cast<char*>(data);\n    int orig_len = len;\n    unsigned count = 0;\n    while (len > 0) {\n        int read_len = std::min(USB_FFS_BULK_SIZE, len);\n        int n = read(h->bulk_out.get(), buf, read_len);\n        if (n < 0) {\n            D(\"ERROR: fd = %d, n = %d: %s\", h->bulk_out.get(), n, strerror(errno));\n            return -1;\n        }\n        buf += n;\n        len -= n;\n        count += n;\n\n        // For fastbootd command such as \"getvar all\", len parameter is always set 64.\n        // But what we read is actually less than 64.\n        // For example, length 10 for \"getvar all\" command.\n        // If we get less data than expected, this means there should be no more data.\n        if (allow_partial && n < read_len) {\n            orig_len = count;\n            break;\n        }\n    }\n\n    D(\"[ done fd=%d ]\", h->bulk_out.get());\n    return orig_len;\n}\n\nstatic int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) {\n    aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;\n\n    int num_bufs = len / h->io_size + (len % h->io_size == 0 ? 0 : 1);\n    const char* cur_data = reinterpret_cast<const char*>(data);\n\n    if (posix_madvise(const_cast<void*>(data), len, POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) <\n        0) {\n        D(\"[ Failed to madvise: %d ]\", errno);\n    }\n\n    for (int i = 0; i < num_bufs; i++) {\n        int buf_len = std::min(len, static_cast<int>(h->io_size));\n        io_prep(&aiob->iocb[i], aiob->fd, cur_data, buf_len, 0, read);\n\n        len -= buf_len;\n        cur_data += buf_len;\n    }\n\n    while (true) {\n        if (TEMP_FAILURE_RETRY(io_submit(aiob->ctx, num_bufs, aiob->iocbs.data())) < num_bufs) {\n            PLOG(ERROR) << \"aio: got error submitting \" << (read ? \"read\" : \"write\");\n            return -1;\n        }\n        if (TEMP_FAILURE_RETRY(io_getevents(aiob->ctx, num_bufs, num_bufs, aiob->events.data(),\n                                            nullptr)) < num_bufs) {\n            PLOG(ERROR) << \"aio: got error waiting \" << (read ? \"read\" : \"write\");\n            return -1;\n        }\n        if (num_bufs == 1 && aiob->events[0].res == -EINTR) {\n            continue;\n        }\n        if (read && aiob->events[0].res == -EPIPE) {\n            // On initial connection, some clients will send a ClearFeature(HALT) to\n            // attempt to resynchronize host and device after the fastboot server is killed.\n            // On newer device kernels, the reads we've already dispatched will be cancelled.\n            // Instead of treating this as a failure, which will tear down the interface and\n            // lead to the client doing the same thing again, just resubmit if this happens\n            // before we've actually read anything.\n            PLOG(ERROR) << \"aio: got -EPIPE on first read attempt. Re-submitting read... \";\n            continue;\n        }\n        int ret = 0;\n        for (int i = 0; i < num_bufs; i++) {\n            if (aiob->events[i].res < 0) {\n                errno = -aiob->events[i].res;\n                PLOG(ERROR) << \"aio: got error event on \" << (read ? \"read\" : \"write\")\n                            << \" total bufs \" << num_bufs;\n                return -1;\n            }\n            ret += aiob->events[i].res;\n        }\n        return ret;\n    }\n}\n\nstatic int usb_ffs_aio_read(usb_handle* h, void* data, int len, bool /* allow_partial */) {\n    return usb_ffs_do_aio(h, data, len, true);\n}\n\nstatic int usb_ffs_aio_write(usb_handle* h, const void* data, int len) {\n    return usb_ffs_do_aio(h, data, len, false);\n}\n\nstatic void usb_ffs_close(usb_handle* h) {\n    LOG(INFO) << \"closing functionfs transport\";\n\n    h->bulk_out.reset();\n    h->bulk_in.reset();\n\n    // Notify usb_adb_open_thread to open a new connection.\n    h->lock.lock();\n    h->open_new_connection = true;\n    h->lock.unlock();\n    h->notify.notify_one();\n    if (h->aio_type == AIOType::IO_URING) {\n        exit_io_uring_ffs(h);\n    }\n}\n\nbool DoesKernelSupportIouring() {\n    struct utsname uts {};\n    unsigned int major = 0, minor = 0;\n    if ((uname(&uts) != 0) || (sscanf(uts.release, \"%u.%u\", &major, &minor) != 2)) {\n        return false;\n    }\n    if (major > 5) {\n        return true;\n    }\n    // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and\n    // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel\n    return minor >= 6;\n}\n\nstd::unique_ptr<usb_handle> create_usb_handle(unsigned num_bufs, unsigned io_size) {\n    auto h = std::make_unique<usb_handle>();\n    if (DoesKernelSupportIouring() &&\n        android::base::GetBoolProperty(\"sys.usb.ffs.io_uring_enabled\", false)) {\n        init_io_uring_ffs(h.get(), num_bufs);\n        h->aio_type = AIOType::IO_URING;\n        LOG(INFO) << \"Using io_uring for usb ffs\";\n    } else if (android::base::GetBoolProperty(\"sys.usb.ffs.aio_compat\", false)) {\n        // Devices on older kernels (< 3.18) will not have aio support for ffs\n        // unless backported. Fall back on the non-aio functions instead.\n        h->write = usb_ffs_write;\n        h->read = usb_ffs_read;\n        h->aio_type = AIOType::SYNC_IO;\n        LOG(INFO) << \"Using sync io for usb ffs\";\n    } else {\n        h->write = usb_ffs_aio_write;\n        h->read = usb_ffs_aio_read;\n        aio_block_init(&h->read_aiob, num_bufs);\n        aio_block_init(&h->write_aiob, num_bufs);\n        h->aio_type = AIOType::AIO;\n        LOG(INFO) << \"Using aio for usb ffs\";\n    }\n    h->io_size = io_size;\n    h->close = usb_ffs_close;\n    return h;\n}\n"
  },
  {
    "path": "fastboot/device/usb.h",
    "content": "#pragma once\n\n/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <linux/usb/functionfs.h>\n\n#include <liburing.h>\n#include <atomic>\n#include <condition_variable>\n#include <memory>\n#include <mutex>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <asyncio/AsyncIO.h>\n\nstruct aio_block {\n    std::vector<struct iocb> iocb;\n    std::vector<struct iocb*> iocbs;\n    std::vector<struct io_event> events;\n    aio_context_t ctx;\n    int num_submitted;\n    int fd;\n};\n\nint getMaxPacketSize(int ffs_fd);\n\nenum class AIOType { SYNC_IO, AIO, IO_URING };\n\nstruct usb_handle {\n    std::condition_variable notify;\n    std::mutex lock;\n    bool open_new_connection = true;\n\n    int (*write)(usb_handle* h, const void* data, int len);\n    int (*read)(usb_handle* h, void* data, int len, bool allow_partial);\n    void (*close)(usb_handle* h);\n\n    // FunctionFS\n    android::base::unique_fd control;\n    android::base::unique_fd bulk_out;  // \"out\" from the host's perspective => source for adbd\n    android::base::unique_fd bulk_in;   // \"in\" from the host's perspective => sink for adbd\n\n    // Access to these blocks is very not thread safe. Have one block for each of the\n    // read and write threads.\n    struct aio_block read_aiob;\n    struct aio_block write_aiob;\n\n    io_uring ring;\n    size_t io_size;\n    AIOType aio_type;\n};\n\nstd::unique_ptr<usb_handle> create_usb_handle(unsigned num_bufs, unsigned io_size);\n"
  },
  {
    "path": "fastboot/device/usb_client.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"usb_client.h\"\n\n#include <endian.h>\n#include <fcntl.h>\n#include <linux/usb/ch9.h>\n#include <linux/usb/functionfs.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n\nconstexpr int kMaxPacketSizeFs = 64;\nconstexpr int kMaxPacketSizeHs = 512;\nconstexpr int kMaxPacketsizeSs = 1024;\n\nconstexpr size_t kFbFfsNumBufs = 16;\nconstexpr size_t kFbFfsBufSize = 16384;\n\nconstexpr const char* kUsbFfsFastbootEp0 = \"/dev/usb-ffs/fastboot/ep0\";\nconstexpr const char* kUsbFfsFastbootOut = \"/dev/usb-ffs/fastboot/ep1\";\nconstexpr const char* kUsbFfsFastbootIn = \"/dev/usb-ffs/fastboot/ep2\";\n\nstruct FuncDesc {\n    struct usb_interface_descriptor intf;\n    struct usb_endpoint_descriptor_no_audio source;\n    struct usb_endpoint_descriptor_no_audio sink;\n} __attribute__((packed));\n\nstruct SsFuncDesc {\n    struct usb_interface_descriptor intf;\n    struct usb_endpoint_descriptor_no_audio source;\n    struct usb_ss_ep_comp_descriptor source_comp;\n    struct usb_endpoint_descriptor_no_audio sink;\n    struct usb_ss_ep_comp_descriptor sink_comp;\n} __attribute__((packed));\n\nstruct DescV2 {\n    struct usb_functionfs_descs_head_v2 header;\n    // The rest of the structure depends on the flags in the header.\n    __le32 fs_count;\n    __le32 hs_count;\n    __le32 ss_count;\n    struct FuncDesc fs_descs, hs_descs;\n    struct SsFuncDesc ss_descs;\n} __attribute__((packed));\n\nstruct usb_interface_descriptor fastboot_interface = {\n        .bLength = USB_DT_INTERFACE_SIZE,\n        .bDescriptorType = USB_DT_INTERFACE,\n        .bInterfaceNumber = 0,\n        .bNumEndpoints = 2,\n        .bInterfaceClass = USB_CLASS_VENDOR_SPEC,\n        .bInterfaceSubClass = 66,\n        .bInterfaceProtocol = 3,\n        .iInterface = 1, /* first string from the provided table */\n};\n\nstatic struct FuncDesc fs_descriptors = {\n        .intf = fastboot_interface,\n        .source =\n                {\n                        .bLength = sizeof(fs_descriptors.source),\n                        .bDescriptorType = USB_DT_ENDPOINT,\n                        .bEndpointAddress = 1 | USB_DIR_OUT,\n                        .bmAttributes = USB_ENDPOINT_XFER_BULK,\n                        .wMaxPacketSize = kMaxPacketSizeFs,\n                },\n        .sink =\n                {\n                        .bLength = sizeof(fs_descriptors.sink),\n                        .bDescriptorType = USB_DT_ENDPOINT,\n                        .bEndpointAddress = 1 | USB_DIR_IN,\n                        .bmAttributes = USB_ENDPOINT_XFER_BULK,\n                        .wMaxPacketSize = kMaxPacketSizeFs,\n                },\n};\n\nstatic struct FuncDesc hs_descriptors = {\n        .intf = fastboot_interface,\n        .source =\n                {\n                        .bLength = sizeof(hs_descriptors.source),\n                        .bDescriptorType = USB_DT_ENDPOINT,\n                        .bEndpointAddress = 1 | USB_DIR_OUT,\n                        .bmAttributes = USB_ENDPOINT_XFER_BULK,\n                        .wMaxPacketSize = kMaxPacketSizeHs,\n                },\n        .sink =\n                {\n                        .bLength = sizeof(hs_descriptors.sink),\n                        .bDescriptorType = USB_DT_ENDPOINT,\n                        .bEndpointAddress = 1 | USB_DIR_IN,\n                        .bmAttributes = USB_ENDPOINT_XFER_BULK,\n                        .wMaxPacketSize = kMaxPacketSizeHs,\n                },\n};\n\nstatic struct SsFuncDesc ss_descriptors = {\n        .intf = fastboot_interface,\n        .source =\n                {\n                        .bLength = sizeof(ss_descriptors.source),\n                        .bDescriptorType = USB_DT_ENDPOINT,\n                        .bEndpointAddress = 1 | USB_DIR_OUT,\n                        .bmAttributes = USB_ENDPOINT_XFER_BULK,\n                        .wMaxPacketSize = kMaxPacketsizeSs,\n                },\n        .source_comp =\n                {\n                        .bLength = sizeof(ss_descriptors.source_comp),\n                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,\n                        .bMaxBurst = 15,\n                },\n        .sink =\n                {\n                        .bLength = sizeof(ss_descriptors.sink),\n                        .bDescriptorType = USB_DT_ENDPOINT,\n                        .bEndpointAddress = 1 | USB_DIR_IN,\n                        .bmAttributes = USB_ENDPOINT_XFER_BULK,\n                        .wMaxPacketSize = kMaxPacketsizeSs,\n                },\n        .sink_comp =\n                {\n                        .bLength = sizeof(ss_descriptors.sink_comp),\n                        .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,\n                        .bMaxBurst = 15,\n                },\n};\n\n#define STR_INTERFACE_ \"fastbootd\"\n\nstatic const struct {\n    struct usb_functionfs_strings_head header;\n    struct {\n        __le16 code;\n        const char str1[sizeof(STR_INTERFACE_)];\n    } __attribute__((packed)) lang0;\n} __attribute__((packed)) strings = {\n        .header =\n                {\n                        .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),\n                        .length = htole32(sizeof(strings)),\n                        .str_count = htole32(1),\n                        .lang_count = htole32(1),\n                },\n        .lang0 =\n                {\n                        htole16(0x0409), /* en-us */\n                        STR_INTERFACE_,\n                },\n};\n\nstatic struct DescV2 v2_descriptor = {\n        .header =\n                {\n                        .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),\n                        .length = htole32(sizeof(v2_descriptor)),\n                        .flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |\n                                 FUNCTIONFS_HAS_SS_DESC,\n                },\n        .fs_count = 3,\n        .hs_count = 3,\n        .ss_count = 5,\n        .fs_descs = fs_descriptors,\n        .hs_descs = hs_descriptors,\n        .ss_descs = ss_descriptors,\n};\n\n// Reimplementing since usb_ffs_close() does not close the control FD.\nstatic void CloseFunctionFs(usb_handle* h) {\n    h->bulk_in.reset();\n    h->bulk_out.reset();\n    h->control.reset();\n}\n\nstatic bool InitFunctionFs(usb_handle* h) {\n    LOG(INFO) << \"initializing functionfs\";\n\n    if (h->control < 0) {  // might have already done this before\n        LOG(INFO) << \"opening control endpoint \" << kUsbFfsFastbootEp0;\n        h->control.reset(open(kUsbFfsFastbootEp0, O_RDWR));\n        if (h->control < 0) {\n            PLOG(ERROR) << \"cannot open control endpoint \" << kUsbFfsFastbootEp0;\n            goto err;\n        }\n\n        auto ret = write(h->control.get(), &v2_descriptor, sizeof(v2_descriptor));\n        if (ret < 0) {\n            PLOG(ERROR) << \"cannot write descriptors \" << kUsbFfsFastbootEp0;\n            goto err;\n        }\n\n        ret = write(h->control.get(), &strings, sizeof(strings));\n        if (ret < 0) {\n            PLOG(ERROR) << \"cannot write strings \" << kUsbFfsFastbootEp0;\n            goto err;\n        }\n        // Signal only when writing the descriptors to ffs\n        android::base::SetProperty(\"sys.usb.ffs.ready\", \"1\");\n    }\n\n    h->bulk_out.reset(open(kUsbFfsFastbootOut, O_RDONLY));\n    if (h->bulk_out < 0) {\n        PLOG(ERROR) << \"cannot open bulk-out endpoint \" << kUsbFfsFastbootOut;\n        goto err;\n    }\n\n    h->bulk_in.reset(open(kUsbFfsFastbootIn, O_WRONLY));\n    if (h->bulk_in < 0) {\n        PLOG(ERROR) << \"cannot open bulk-in endpoint \" << kUsbFfsFastbootIn;\n        goto err;\n    }\n\n    h->read_aiob.fd = h->bulk_out.get();\n    h->write_aiob.fd = h->bulk_in.get();\n    return true;\n\nerr:\n    CloseFunctionFs(h);\n    return false;\n}\n\nClientUsbTransport::ClientUsbTransport()\n    : handle_(std::unique_ptr<usb_handle>(create_usb_handle(kFbFfsNumBufs, kFbFfsBufSize))) {\n    if (!InitFunctionFs(handle_.get())) {\n        handle_.reset(nullptr);\n    }\n}\n\nssize_t ClientUsbTransport::Read(void* data, size_t len) {\n    if (handle_ == nullptr) {\n        LOG(ERROR) << \"ClientUsbTransport: no handle\";\n        return -1;\n    }\n    if (len > SSIZE_MAX) {\n        LOG(ERROR) << \"ClientUsbTransport: maximum length exceeds bounds\";\n        return -1;\n    }\n    char* char_data = static_cast<char*>(data);\n    size_t bytes_read_total = 0;\n    while (bytes_read_total < len) {\n        auto bytes_to_read = std::min(len - bytes_read_total, kFbFfsNumBufs * kFbFfsBufSize);\n        auto bytes_read_now =\n                handle_->read(handle_.get(), char_data, bytes_to_read, true /* allow_partial */);\n        if (bytes_read_now < 0) {\n            PLOG(ERROR) << \"ClientUsbTransport: read failed\";\n            return bytes_read_total == 0 ? -1 : bytes_read_total;\n        }\n        bytes_read_total += bytes_read_now;\n        char_data += bytes_read_now;\n        if (static_cast<size_t>(bytes_read_now) < bytes_to_read) {\n            break;\n        }\n    }\n    return bytes_read_total;\n}\n\nssize_t ClientUsbTransport::Write(const void* data, size_t len) {\n    if (handle_ == nullptr || len > SSIZE_MAX) {\n        return -1;\n    }\n    const char* char_data = reinterpret_cast<const char*>(data);\n    size_t bytes_written_total = 0;\n    while (bytes_written_total < len) {\n        auto bytes_to_write = std::min(len - bytes_written_total, kFbFfsNumBufs * kFbFfsBufSize);\n        auto bytes_written_now = handle_->write(handle_.get(), char_data, bytes_to_write);\n        if (bytes_written_now < 0) {\n            return bytes_written_total == 0 ? -1 : bytes_written_total;\n        }\n        bytes_written_total += bytes_written_now;\n        char_data += bytes_written_now;\n        if (static_cast<size_t>(bytes_written_now) < bytes_to_write) {\n            break;\n        }\n    }\n    return bytes_written_total;\n}\n\nint ClientUsbTransport::Close() {\n    if (handle_ == nullptr) {\n        return -1;\n    }\n    CloseFunctionFs(handle_.get());\n    return 0;\n}\n\nint ClientUsbTransport::Reset() {\n    return 0;\n}\n"
  },
  {
    "path": "fastboot/device/usb_client.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <memory>\n\n#include \"usb.h\"\n\n#include \"transport.h\"\n\nclass ClientUsbTransport : public Transport {\n  public:\n    ClientUsbTransport();\n    ~ClientUsbTransport() override = default;\n\n    ssize_t Read(void* data, size_t len) override;\n    ssize_t Write(const void* data, size_t len) override;\n    int Close() override;\n    int Reset() override;\n\n  private:\n    std::unique_ptr<usb_handle> handle_;\n\n    DISALLOW_COPY_AND_ASSIGN(ClientUsbTransport);\n};\n"
  },
  {
    "path": "fastboot/device/usb_iouring.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n#include <liburing.h>\n#include \"liburing/io_uring.h\"\n#include \"usb.h\"\n\nstatic int prep_async_read(struct io_uring* ring, int fd, void* data, size_t len, int64_t offset) {\n    if (io_uring_sq_space_left(ring) <= 0) {\n        LOG(ERROR) << \"Submission queue run out of space.\";\n        return -1;\n    }\n    auto sqe = io_uring_get_sqe(ring);\n    if (sqe == nullptr) {\n        return -1;\n    }\n    io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK | IOSQE_ASYNC);\n    io_uring_prep_read(sqe, fd, data, len, offset);\n    return 0;\n}\n\nstatic int prep_async_write(struct io_uring* ring, int fd, const void* data, size_t len,\n                            int64_t offset) {\n    if (io_uring_sq_space_left(ring) <= 0) {\n        LOG(ERROR) << \"Submission queue run out of space.\";\n        return -1;\n    }\n    auto sqe = io_uring_get_sqe(ring);\n    if (sqe == nullptr) {\n        return -1;\n    }\n    io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK | IOSQE_ASYNC);\n    io_uring_prep_write(sqe, fd, data, len, offset);\n    return 0;\n}\n\ntemplate <bool read, typename T>\nint prep_async_io(struct io_uring* ring, int fd, T* data, size_t len, int64_t offset) {\n    if constexpr (read) {\n        return prep_async_read(ring, fd, data, len, offset);\n    } else {\n        return prep_async_write(ring, fd, data, len, offset);\n    }\n}\n\ntemplate <typename T>\nstatic constexpr T DivRoundup(T x, T y) {\n    return (x + y - 1) / y;\n}\n\nextern int getMaxPacketSize(int ffs_fd);\n\ntemplate <bool read, typename T>\nstatic int usb_ffs_do_aio(usb_handle* h, T* const data, const int len) {\n    const aio_block* aiob = read ? &h->read_aiob : &h->write_aiob;\n    const int num_requests = DivRoundup<int>(len, h->io_size);\n    auto cur_data = data;\n    const auto packet_size = getMaxPacketSize(aiob->fd);\n\n    for (int bytes_remain = len; bytes_remain > 0;) {\n        const int buf_len = std::min(bytes_remain, static_cast<int>(h->io_size));\n        const auto ret = prep_async_io<read>(&h->ring, aiob->fd, cur_data, buf_len, 0);\n        if (ret < 0) {\n            PLOG(ERROR) << \"Failed to queue io_uring request\";\n            return -1;\n        }\n\n        bytes_remain -= buf_len;\n        cur_data = reinterpret_cast<T*>(reinterpret_cast<size_t>(cur_data) + buf_len);\n    }\n    const int ret = io_uring_submit(&h->ring);\n    if (ret <= 0 || ret != num_requests) {\n        PLOG(ERROR) << \"io_uring: failed to submit SQE entries to kernel\";\n        return -1;\n    }\n    int res = 0;\n    bool success = true;\n    for (int i = 0; i < num_requests; ++i) {\n        struct io_uring_cqe* cqe{};\n        const auto ret = TEMP_FAILURE_RETRY(io_uring_wait_cqe(&h->ring, &cqe));\n        if (ret < 0 || cqe == nullptr) {\n            PLOG(ERROR) << \"Failed to get CQE from kernel\";\n            success = false;\n            continue;\n        }\n        res += cqe->res;\n        if (cqe->res < 0) {\n            LOG(ERROR) << \"io_uring request failed:, i = \" << i\n                       << \", num_requests = \" << num_requests << \", res = \" << cqe->res << \": \"\n                       << strerror(cqe->res) << (read ? \" read\" : \" write\")\n                       << \" request size: \" << len << \", io_size: \" << h->io_size\n                       << \" max packet size: \" << packet_size << \", fd: \" << aiob->fd;\n            success = false;\n            errno = -cqe->res;\n        }\n        io_uring_cqe_seen(&h->ring, cqe);\n    }\n    if (!success) {\n        return -1;\n    }\n    return res;\n}\n\nstatic int usb_ffs_io_uring_read(usb_handle* h, void* data, int len, bool /* allow_partial */) {\n    return usb_ffs_do_aio<true>(h, data, len);\n}\n\nstatic int usb_ffs_io_uring_write(usb_handle* h, const void* data, int len) {\n    return usb_ffs_do_aio<false>(h, data, len);\n}\n\nvoid exit_io_uring_ffs(usb_handle* h) {\n    io_uring_queue_exit(&h->ring);\n}\n\nbool init_io_uring_ffs(usb_handle* h, size_t queue_depth) {\n    const auto err = io_uring_queue_init(queue_depth, &h->ring, 0);\n    if (err) {\n        LOG(ERROR) << \"Failed to initialize io_uring of depth \" << queue_depth << \": \"\n                   << strerror(err);\n        return false;\n    }\n    h->write = usb_ffs_io_uring_write;\n    h->read = usb_ffs_io_uring_read;\n    return true;\n}\n"
  },
  {
    "path": "fastboot/device/usb_iouring.h",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"usb.h\"\n\nbool init_io_uring_ffs(usb_handle* h, size_t queue_depth);\n\nvoid exit_io_uring_ffs(usb_handle* h);\n"
  },
  {
    "path": "fastboot/device/utility.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"utility.h\"\n\n#include <dirent.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n#include <fs_mgr/roots.h>\n#include <fs_mgr_dm_linear.h>\n#include <liblp/builder.h>\n#include <liblp/liblp.h>\n\n#include \"fastboot_device.h\"\n\nusing namespace android::fs_mgr;\nusing namespace std::chrono_literals;\nusing android::base::unique_fd;\n\nnamespace {\n\nbool OpenPhysicalPartition(const std::string& name, PartitionHandle* handle) {\n    std::optional<std::string> path = FindPhysicalPartition(name);\n    if (!path) {\n        return false;\n    }\n    *handle = PartitionHandle(*path);\n    return true;\n}\n\nbool OpenLogicalPartition(FastbootDevice* device, const std::string& partition_name,\n                          PartitionHandle* handle) {\n    std::string slot_suffix = GetSuperSlotSuffix(device, partition_name);\n    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);\n    auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));\n    if (!path) {\n        return false;\n    }\n\n    CreateLogicalPartitionParams params = {\n            .block_device = *path,\n            .metadata_slot = slot_number,\n            .partition_name = partition_name,\n            .force_writable = true,\n            .timeout_ms = 5s,\n    };\n    std::string dm_path;\n    if (!CreateLogicalPartition(params, &dm_path)) {\n        LOG(ERROR) << \"Could not map partition: \" << partition_name;\n        return false;\n    }\n    auto closer = [partition_name]() -> void { DestroyLogicalPartition(partition_name); };\n    *handle = PartitionHandle(dm_path, std::move(closer));\n    return true;\n}\n\n}  // namespace\n\nbool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,\n                   int flags) {\n    // We prioritize logical partitions over physical ones, and do this\n    // consistently for other partition operations (like getvar:partition-size).\n    if (LogicalPartitionExists(device, name)) {\n        if (!OpenLogicalPartition(device, name, handle)) {\n            return false;\n        }\n    } else if (!OpenPhysicalPartition(name, handle)) {\n        LOG(ERROR) << \"No such partition: \" << name;\n        return false;\n    }\n\n    return handle->Open(flags);\n}\n\nstd::optional<std::string> FindPhysicalPartition(const std::string& name) {\n    // Check for an invalid file name\n    if (android::base::StartsWith(name, \"../\") || name.find(\"/../\") != std::string::npos) {\n        return {};\n    }\n    std::string path = \"/dev/block/by-name/\" + name;\n    if (access(path.c_str(), W_OK) < 0) {\n        return {};\n    }\n    return path;\n}\n\nstatic const LpMetadataPartition* FindLogicalPartition(const LpMetadata& metadata,\n                                                       const std::string& name) {\n    for (const auto& partition : metadata.partitions) {\n        if (GetPartitionName(partition) == name) {\n            return &partition;\n        }\n    }\n    return nullptr;\n}\n\nbool LogicalPartitionExists(FastbootDevice* device, const std::string& name, bool* is_zero_length) {\n    std::string slot_suffix = GetSuperSlotSuffix(device, name);\n    uint32_t slot_number = SlotNumberForSlotSuffix(slot_suffix);\n    auto path = FindPhysicalPartition(fs_mgr_get_super_partition_name(slot_number));\n    if (!path) {\n        return false;\n    }\n\n    std::unique_ptr<LpMetadata> metadata = ReadMetadata(path->c_str(), slot_number);\n    if (!metadata) {\n        return false;\n    }\n    const LpMetadataPartition* partition = FindLogicalPartition(*metadata.get(), name);\n    if (!partition) {\n        return false;\n    }\n    if (is_zero_length) {\n        *is_zero_length = (partition->num_extents == 0);\n    }\n    return true;\n}\n\nbool GetSlotNumber(const std::string& slot, int32_t* number) {\n    if (slot.size() != 1) {\n        return false;\n    }\n    if (slot[0] < 'a' || slot[0] > 'z') {\n        return false;\n    }\n    *number = slot[0] - 'a';\n    return true;\n}\n\nstd::vector<std::string> ListPartitions(FastbootDevice* device) {\n    std::vector<std::string> partitions;\n\n    // First get physical partitions.\n    struct dirent* de;\n    std::unique_ptr<DIR, decltype(&closedir)> by_name(opendir(\"/dev/block/by-name\"), closedir);\n    while ((de = readdir(by_name.get())) != nullptr) {\n        if (!strcmp(de->d_name, \".\") || !strcmp(de->d_name, \"..\")) {\n            continue;\n        }\n        struct stat s;\n        std::string path = \"/dev/block/by-name/\" + std::string(de->d_name);\n        if (!stat(path.c_str(), &s) && S_ISBLK(s.st_mode)) {\n            partitions.emplace_back(de->d_name);\n        }\n    }\n\n    // Find metadata in each super partition (on retrofit devices, there will\n    // be two).\n    std::vector<std::unique_ptr<LpMetadata>> metadata_list;\n\n    uint32_t current_slot = SlotNumberForSlotSuffix(device->GetCurrentSlot());\n    std::string super_name = fs_mgr_get_super_partition_name(current_slot);\n    if (auto metadata = ReadMetadata(super_name, current_slot)) {\n        metadata_list.emplace_back(std::move(metadata));\n    }\n\n    uint32_t other_slot = (current_slot == 0) ? 1 : 0;\n    std::string other_super = fs_mgr_get_super_partition_name(other_slot);\n    if (super_name != other_super) {\n        if (auto metadata = ReadMetadata(other_super, other_slot)) {\n            metadata_list.emplace_back(std::move(metadata));\n        }\n    }\n\n    for (const auto& metadata : metadata_list) {\n        for (const auto& partition : metadata->partitions) {\n            std::string partition_name = GetPartitionName(partition);\n            if (std::find(partitions.begin(), partitions.end(), partition_name) ==\n                partitions.end()) {\n                partitions.emplace_back(partition_name);\n            }\n        }\n    }\n    return partitions;\n}\n\nbool GetDeviceLockStatus() {\n    return android::base::GetProperty(\"ro.boot.verifiedbootstate\", \"\") != \"orange\";\n}\n\nbool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,\n                                const android::fs_mgr::LpMetadata& metadata) {\n    size_t num_slots = 1;\n    auto boot_control_hal = device->boot_control_hal();\n    if (boot_control_hal) {\n        num_slots = boot_control_hal->GetNumSlots();\n    }\n\n    bool ok = true;\n    for (size_t i = 0; i < num_slots; i++) {\n        ok &= UpdatePartitionTable(super_name, metadata, i);\n    }\n    return ok;\n}\n\nstd::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name) {\n    // If the super partition does not have a slot suffix, this is not a\n    // retrofit device, and we should take the current slot.\n    std::string current_slot_suffix = device->GetCurrentSlot();\n    uint32_t current_slot_number = SlotNumberForSlotSuffix(current_slot_suffix);\n    std::string super_partition = fs_mgr_get_super_partition_name(current_slot_number);\n    if (GetPartitionSlotSuffix(super_partition).empty()) {\n        return current_slot_suffix;\n    }\n\n    // Otherwise, infer the slot from the partition name.\n    std::string slot_suffix = GetPartitionSlotSuffix(partition_name);\n    if (!slot_suffix.empty()) {\n        return slot_suffix;\n    }\n    return current_slot_suffix;\n}\n\nAutoMountMetadata::AutoMountMetadata() {\n    android::fs_mgr::Fstab proc_mounts;\n    if (!ReadFstabFromFile(\"/proc/mounts\", &proc_mounts)) {\n        LOG(ERROR) << \"Could not read /proc/mounts\";\n        return;\n    }\n\n    if (GetEntryForMountPoint(&proc_mounts, \"/metadata\")) {\n        mounted_ = true;\n        return;\n    }\n\n    if (!ReadDefaultFstab(&fstab_)) {\n        LOG(ERROR) << \"Could not read default fstab\";\n        return;\n    }\n    mounted_ = EnsurePathMounted(&fstab_, \"/metadata\");\n    should_unmount_ = true;\n}\n\nAutoMountMetadata::~AutoMountMetadata() {\n    if (mounted_ && should_unmount_) {\n        EnsurePathUnmounted(&fstab_, \"/metadata\");\n    }\n}\n"
  },
  {
    "path": "fastboot/device/utility.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <optional>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <fstab/fstab.h>\n#include <liblp/liblp.h>\n\n// Logical partitions are only mapped to a block device as needed, and\n// immediately unmapped when no longer needed. In order to enforce this we\n// require accessing partitions through a Handle abstraction, which may perform\n// additional operations after closing its file descriptor.\nclass PartitionHandle {\n  public:\n    PartitionHandle() {}\n    explicit PartitionHandle(const std::string& path) : path_(path) {}\n    PartitionHandle(const std::string& path, std::function<void()>&& closer)\n        : path_(path), closer_(std::move(closer)) {}\n    PartitionHandle(PartitionHandle&& other) = default;\n    PartitionHandle& operator=(PartitionHandle&& other) = default;\n    ~PartitionHandle() {\n        if (closer_) {\n            // Make sure the device is closed first.\n            fd_ = {};\n            closer_();\n        }\n    }\n    const std::string& path() const { return path_; }\n    int fd() const { return fd_.get(); }\n    bool Open(int flags) {\n        flags |= (O_EXCL | O_CLOEXEC | O_BINARY);\n\n        // Attempts to open a second device can fail with EBUSY if the device is already open.\n        // Explicitly close any previously opened devices as unique_fd won't close them until\n        // after the attempt to open.\n        fd_.reset();\n\n        fd_ = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path_.c_str(), flags)));\n        if (fd_ < 0) {\n            PLOG(ERROR) << \"Failed to open block device: \" << path_;\n            return false;\n        }\n        flags_ = flags;\n\n        return true;\n    }\n    bool Reset(int flags) {\n        if (fd_.ok() && (flags | O_EXCL | O_CLOEXEC | O_BINARY) == flags_) {\n            return true;\n        }\n\n        off_t offset = fd_.ok() ? lseek(fd_.get(), 0, SEEK_CUR) : 0;\n        if (offset < 0) {\n            PLOG(ERROR) << \"Failed lseek on block device: \" << path_;\n            return false;\n        }\n\n        sync();\n\n        if (Open(flags) == false) {\n            return false;\n        }\n\n        if (lseek(fd_.get(), offset, SEEK_SET) != offset) {\n            PLOG(ERROR) << \"Failed lseek on block device: \" << path_;\n            return false;\n        }\n\n        return true;\n    }\n  private:\n    std::string path_;\n    android::base::unique_fd fd_;\n    int flags_;\n    std::function<void()> closer_;\n};\n\nclass AutoMountMetadata {\n  public:\n    AutoMountMetadata();\n    ~AutoMountMetadata();\n    explicit operator bool() const { return mounted_; }\n\n  private:\n    android::fs_mgr::Fstab fstab_;\n    bool mounted_ = false;\n    bool should_unmount_ = false;\n};\n\nclass FastbootDevice;\n\n// On normal devices, the super partition is always named \"super\". On retrofit\n// devices, the name must be derived from the partition name or current slot.\n// This helper assists in choosing the correct super for a given partition\n// name.\nstd::string GetSuperSlotSuffix(FastbootDevice* device, const std::string& partition_name);\n\nstd::optional<std::string> FindPhysicalPartition(const std::string& name);\nbool LogicalPartitionExists(FastbootDevice* device, const std::string& name,\n                            bool* is_zero_length = nullptr);\n\n// Partition is O_WRONLY by default, caller should pass O_RDONLY for reading.\n// Caller may pass additional flags if needed. (O_EXCL | O_CLOEXEC | O_BINARY)\n// will be logically ORed internally.\nbool OpenPartition(FastbootDevice* device, const std::string& name, PartitionHandle* handle,\n                   int flags = O_WRONLY);\n\nbool GetSlotNumber(const std::string& slot, int32_t* number);\nstd::vector<std::string> ListPartitions(FastbootDevice* device);\nbool GetDeviceLockStatus();\n\n// Update all copies of metadata.\nbool UpdateAllPartitionMetadata(FastbootDevice* device, const std::string& super_name,\n                                const android::fs_mgr::LpMetadata& metadata);\n"
  },
  {
    "path": "fastboot/device/variables.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"variables.h\"\n\n#include <inttypes.h>\n#include <stdio.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android/hardware/boot/1.1/IBootControl.h>\n#include <ext4_utils/ext4_utils.h>\n#include <fs_mgr.h>\n#include <liblp/liblp.h>\n\n#include \"BootControlClient.h\"\n#include \"fastboot_device.h\"\n#include \"flashing.h\"\n#include \"utility.h\"\n\n#ifdef FB_ENABLE_FETCH\nstatic constexpr bool kEnableFetch = true;\n#else\nstatic constexpr bool kEnableFetch = false;\n#endif\n\nusing MergeStatus = android::hal::BootControlClient::MergeStatus;\nusing aidl::android::hardware::fastboot::FileSystemType;\nusing namespace android::fs_mgr;\nusing namespace std::string_literals;\n\nconstexpr char kFastbootProtocolVersion[] = \"0.4\";\n\nbool GetVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                std::string* message) {\n    *message = kFastbootProtocolVersion;\n    return true;\n}\n\nbool GetBootloaderVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                          std::string* message) {\n    *message = android::base::GetProperty(\"ro.bootloader\", \"\");\n    return true;\n}\n\nbool GetBasebandVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                        std::string* message) {\n    *message = android::base::GetProperty(\"ro.build.expect.baseband\", \"\");\n    return true;\n}\n\nbool GetOsVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                  std::string* message) {\n    *message = android::base::GetProperty(\"ro.build.version.release\", \"\");\n    return true;\n}\n\nbool GetVndkVersion(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                    std::string* message) {\n    *message = android::base::GetProperty(\"ro.vndk.version\", \"\");\n    return true;\n}\n\nbool GetProduct(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                std::string* message) {\n    *message = android::base::GetProperty(\"ro.product.device\", \"\");\n    return true;\n}\n\nbool GetSerial(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n               std::string* message) {\n    *message = android::base::GetProperty(\"ro.serialno\", \"\");\n    return true;\n}\n\nbool GetSecure(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n               std::string* message) {\n    *message = android::base::GetBoolProperty(\"ro.secure\", \"\") ? \"yes\" : \"no\";\n    return true;\n}\n\nbool GetVariant(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                std::string* message) {\n    auto fastboot_hal = device->fastboot_hal();\n    if (!fastboot_hal) {\n        *message = \"Fastboot HAL not found\";\n        return false;\n    }\n    std::string device_variant = \"\";\n    auto status = fastboot_hal->getVariant(&device_variant);\n\n    if (!status.isOk()) {\n        *message = \"Unable to get device variant\";\n        LOG(ERROR) << message->c_str() << status.getDescription();\n        return false;\n    }\n\n    *message = device_variant;\n    return true;\n}\n\nbool GetBatteryVoltageHelper(FastbootDevice* device, int32_t* battery_voltage) {\n    using aidl::android::hardware::health::HealthInfo;\n\n    auto health_hal = device->health_hal();\n    if (!health_hal) {\n        return false;\n    }\n\n    HealthInfo health_info;\n    auto res = health_hal->getHealthInfo(&health_info);\n    if (!res.isOk()) return false;\n    *battery_voltage = health_info.batteryVoltageMillivolts;\n    return true;\n}\n\nbool GetBatterySoCHelper(FastbootDevice* device, int32_t* battery_soc) {\n    using aidl::android::hardware::health::HealthInfo;\n\n    auto health_hal = device->health_hal();\n    if (!health_hal) {\n        return false;\n    }\n\n    HealthInfo health_info;\n    auto res = health_hal->getHealthInfo(&health_info);\n    if (!res.isOk()) return false;\n    *battery_soc = health_info.batteryLevel;\n    return true;\n}\n\nbool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                     std::string* message) {\n    int32_t battery_voltage = 0;\n    if (!GetBatteryVoltageHelper(device, &battery_voltage)) {\n        *message = \"Unable to read battery voltage\";\n        return false;\n    }\n\n    auto fastboot_hal = device->fastboot_hal();\n    if (!fastboot_hal) {\n        *message = \"Fastboot HAL not found\";\n        return false;\n    }\n\n    auto voltage_threshold = 0;\n    auto status = fastboot_hal->getBatteryVoltageFlashingThreshold(&voltage_threshold);\n    if (!status.isOk()) {\n        *message = \"Unable to get battery voltage flashing threshold\";\n        LOG(ERROR) << message->c_str() << status.getDescription();\n        return false;\n    }\n    *message = battery_voltage >= voltage_threshold ? \"yes\" : \"no\";\n\n    return true;\n}\n\nbool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                           std::string* message) {\n    auto fastboot_hal = device->fastboot_hal();\n    if (!fastboot_hal) {\n        *message = \"Fastboot HAL not found\";\n        return false;\n    }\n    bool off_mode_charging_state = false;\n    auto status = fastboot_hal->getOffModeChargeState(&off_mode_charging_state);\n    if (!status.isOk()) {\n        *message = \"Unable to get off mode charge state\";\n        LOG(ERROR) << message->c_str() << status.getDescription();\n        return false;\n    }\n    *message = off_mode_charging_state ? \"1\" : \"0\";\n    return true;\n}\n\nbool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                       std::string* message) {\n    int32_t battery_voltage = 0;\n    if (GetBatteryVoltageHelper(device, &battery_voltage)) {\n        *message = std::to_string(battery_voltage);\n        return true;\n    }\n    *message = \"Unable to get battery voltage\";\n    return false;\n}\n\nbool GetBatterySoC(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                   std::string* message) {\n    int32_t battery_soc = 0;\n    if (GetBatterySoCHelper(device, &battery_soc)) {\n        *message = std::to_string(battery_soc);\n        return true;\n    }\n    *message = \"Unable to get battery soc\";\n    return false;\n}\n\nbool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                    std::string* message) {\n    std::string suffix = device->GetCurrentSlot();\n    *message = suffix.size() == 2 ? suffix.substr(1) : suffix;\n    return true;\n}\n\nbool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                  std::string* message) {\n    auto boot_control_hal = device->boot_control_hal();\n    if (!boot_control_hal) {\n        *message = \"0\";\n    } else {\n        *message = std::to_string(boot_control_hal->GetNumSlots());\n    }\n    return true;\n}\n\nbool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,\n                       std::string* message) {\n    if (args.empty()) {\n        *message = \"Missing argument\";\n        return false;\n    }\n    int32_t slot = -1;\n    if (!GetSlotNumber(args[0], &slot)) {\n        *message = \"Invalid slot\";\n        return false;\n    }\n    auto boot_control_hal = device->boot_control_hal();\n    if (!boot_control_hal) {\n        *message = \"Device has no slots\";\n        return false;\n    }\n    if (boot_control_hal->IsSlotMarkedSuccessful(slot).value_or(false)) {\n        *message = \"no\";\n    } else {\n        *message = \"yes\";\n    }\n    return true;\n}\n\nbool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,\n                       std::string* message) {\n    if (args.empty()) {\n        *message = \"Missing argument\";\n        return false;\n    }\n    int32_t slot = -1;\n    if (!GetSlotNumber(args[0], &slot)) {\n        *message = \"Invalid slot\";\n        return false;\n    }\n    auto boot_control_hal = device->boot_control_hal();\n    if (!boot_control_hal) {\n        *message = \"Device has no slots\";\n        return false;\n    }\n    if (!boot_control_hal->IsSlotBootable(slot).value_or(false)) {\n        *message = \"yes\";\n    } else {\n        *message = \"no\";\n    }\n    return true;\n}\n\nbool GetMaxDownloadSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                        std::string* message) {\n    *message = android::base::StringPrintf(\"0x%X\", kMaxDownloadSizeDefault);\n    return true;\n}\n\nbool GetUnlocked(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                 std::string* message) {\n    *message = GetDeviceLockStatus() ? \"no\" : \"yes\";\n    return true;\n}\n\nbool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args,\n                std::string* message) {\n    if (args.empty()) {\n        *message = \"Missing argument\";\n        return false;\n    }\n    std::string slot_suffix = device->GetCurrentSlot();\n    if (slot_suffix.empty()) {\n        *message = \"no\";\n        return true;\n    }\n    std::string partition_name = args[0] + slot_suffix;\n    if (FindPhysicalPartition(partition_name) || LogicalPartitionExists(device, partition_name)) {\n        *message = \"yes\";\n    } else {\n        *message = \"no\";\n    }\n    return true;\n}\n\nbool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,\n                      std::string* message) {\n    if (args.size() < 1) {\n        *message = \"Missing argument\";\n        return false;\n    }\n    // Zero-length partitions cannot be created through device-mapper, so we\n    // special case them here.\n    bool is_zero_length;\n    if (LogicalPartitionExists(device, args[0], &is_zero_length) && is_zero_length) {\n        *message = \"0x0\";\n        return true;\n    }\n    // Otherwise, open the partition as normal.\n    PartitionHandle handle;\n    if (!OpenPartition(device, args[0], &handle)) {\n        *message = \"Could not open partition\";\n        return false;\n    }\n    uint64_t size = get_block_device_size(handle.fd());\n    *message = android::base::StringPrintf(\"0x%\" PRIX64, size);\n    return true;\n}\n\nbool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,\n                      std::string* message) {\n    if (args.size() < 1) {\n        *message = \"Missing argument\";\n        return false;\n    }\n\n    std::string partition_name = args[0];\n    if (!FindPhysicalPartition(partition_name) && !LogicalPartitionExists(device, partition_name)) {\n        *message = \"Invalid partition\";\n        return false;\n    }\n\n    auto fastboot_hal = device->fastboot_hal();\n    if (!fastboot_hal) {\n        *message = \"raw\";\n        return true;\n    }\n\n    FileSystemType type;\n    auto status = fastboot_hal->getPartitionType(args[0], &type);\n\n    if (!status.isOk()) {\n        *message = \"Unable to retrieve partition type\";\n        LOG(ERROR) << message->c_str() << status.getDescription();\n    } else {\n        switch (type) {\n            case FileSystemType::RAW:\n                *message = \"raw\";\n                return true;\n            case FileSystemType::EXT4:\n                *message = \"ext4\";\n                return true;\n            case FileSystemType::F2FS:\n                *message = \"f2fs\";\n                return true;\n            default:\n                *message = \"Unknown file system type\";\n        }\n    }\n\n    return false;\n}\n\nbool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,\n                           std::string* message) {\n    if (args.size() < 1) {\n        *message = \"Missing argument\";\n        return false;\n    }\n    // Note: if a partition name is in both the GPT and the super partition, we\n    // return \"true\", to be consistent with prefering to flash logical partitions\n    // over physical ones.\n    std::string partition_name = args[0];\n    if (LogicalPartitionExists(device, partition_name)) {\n        *message = \"yes\";\n        return true;\n    }\n    if (FindPhysicalPartition(partition_name)) {\n        *message = \"no\";\n        return true;\n    }\n    *message = \"Partition not found\";\n    return false;\n}\n\nbool GetIsUserspace(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                    std::string* message) {\n    *message = \"yes\";\n    return true;\n}\n\nbool GetIsForceDebuggable(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                          std::string* message) {\n    *message = android::base::GetBoolProperty(\"ro.force.debuggable\", false) ? \"yes\" : \"no\";\n    return true;\n}\n\nstd::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device) {\n    std::vector<std::vector<std::string>> args;\n    auto partitions = ListPartitions(device);\n    for (const auto& partition : partitions) {\n        args.emplace_back(std::initializer_list<std::string>{partition});\n    }\n    return args;\n}\n\nstd::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device) {\n    auto partitions = ListPartitions(device);\n\n    std::string slot_suffix = device->GetCurrentSlot();\n    if (!slot_suffix.empty()) {\n        auto names = std::move(partitions);\n        for (const auto& name : names) {\n            std::string slotless_name = name;\n            if (android::base::EndsWith(name, \"_a\") || android::base::EndsWith(name, \"_b\")) {\n                slotless_name = name.substr(0, name.rfind(\"_\"));\n            }\n            if (std::find(partitions.begin(), partitions.end(), slotless_name) ==\n                partitions.end()) {\n                partitions.emplace_back(slotless_name);\n            }\n        }\n    }\n\n    std::vector<std::vector<std::string>> args;\n    for (const auto& partition : partitions) {\n        args.emplace_back(std::initializer_list<std::string>{partition});\n    }\n    return args;\n}\n\nbool GetHardwareRevision(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                         std::string* message) {\n    *message = android::base::GetProperty(\"ro.revision\", \"\");\n    return true;\n}\n\nbool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                           std::string* message) {\n    uint32_t slot_number = SlotNumberForSlotSuffix(device->GetCurrentSlot());\n    *message = fs_mgr_get_super_partition_name(slot_number);\n    return true;\n}\n\nbool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& /* args */,\n                             std::string* message) {\n    // Note that we use the HAL rather than mounting /metadata, since we want\n    // our results to match the bootloader.\n    auto hal = device->boot1_1();\n    if (!hal) {\n        *message = \"not supported\";\n        return false;\n    }\n\n    MergeStatus status = hal->getSnapshotMergeStatus();\n    switch (status) {\n        case MergeStatus::SNAPSHOTTED:\n            *message = \"snapshotted\";\n            break;\n        case MergeStatus::MERGING:\n            *message = \"merging\";\n            break;\n        default:\n            *message = \"none\";\n            break;\n    }\n    return true;\n}\n\nbool GetCpuAbi(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n               std::string* message) {\n    *message = android::base::GetProperty(\"ro.product.cpu.abi\", \"\");\n    return true;\n}\n\nbool GetSystemFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                          std::string* message) {\n    *message = android::base::GetProperty(\"ro.system.build.fingerprint\", \"\");\n    if (message->empty()) {\n        *message = android::base::GetProperty(\"ro.build.fingerprint\", \"\");\n    }\n    return true;\n}\n\nbool GetVendorFingerprint(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                          std::string* message) {\n    *message = android::base::GetProperty(\"ro.vendor.build.fingerprint\", \"\");\n    return true;\n}\n\nbool GetDynamicPartition(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                         std::string* message) {\n    *message = android::base::GetProperty(\"ro.boot.dynamic_partitions\", \"\");\n    return true;\n}\n\nbool GetFirstApiLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                      std::string* message) {\n    *message = android::base::GetProperty(\"ro.product.first_api_level\", \"\");\n    return true;\n}\n\nbool GetSecurityPatchLevel(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                           std::string* message) {\n    *message = android::base::GetProperty(\"ro.build.version.security_patch\", \"\");\n    return true;\n}\n\nbool GetTrebleEnabled(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                      std::string* message) {\n    *message = android::base::GetProperty(\"ro.treble.enabled\", \"\");\n    return true;\n}\n\nbool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                     std::string* message) {\n    if (!kEnableFetch) {\n        *message = \"fetch not supported on user builds\";\n        return false;\n    }\n    *message = android::base::StringPrintf(\"0x%X\", kMaxFetchSizeDefault);\n    return true;\n}\n\nbool GetDmesg(FastbootDevice* device) {\n    if (GetDeviceLockStatus()) {\n        return device->WriteFail(\"Cannot use when device flashing is locked\");\n    }\n\n    std::unique_ptr<FILE, decltype(&::fclose)> fp(popen(\"/system/bin/dmesg\", \"re\"), ::fclose);\n    if (!fp) {\n        PLOG(ERROR) << \"popen /system/bin/dmesg\";\n        return device->WriteFail(\"Unable to run dmesg: \"s + strerror(errno));\n    }\n\n    ssize_t rv;\n    size_t n = 0;\n    char* str = nullptr;\n    while ((rv = ::getline(&str, &n, fp.get())) > 0) {\n        if (str[rv - 1] == '\\n') {\n            rv--;\n        }\n        device->WriteInfo(std::string(str, rv));\n    }\n\n    int saved_errno = errno;\n    ::free(str);\n\n    if (rv < 0 && saved_errno) {\n        LOG(ERROR) << \"dmesg getline: \" << strerror(saved_errno);\n        device->WriteFail(\"Unable to read dmesg: \"s + strerror(saved_errno));\n        return false;\n    }\n\n    return true;\n}\n\nbool GetBatterySerialNumber(FastbootDevice* device, const std::vector<std::string>&,\n                            std::string* message) {\n    auto health_hal = device->health_hal();\n    if (!health_hal) {\n        return false;\n    }\n\n    if (GetDeviceLockStatus()) {\n        return device->WriteFail(\"Device is locked\");\n    }\n\n    *message = \"unsupported\";\n\n    int32_t version = 0;\n    auto res = health_hal->getInterfaceVersion(&version);\n    if (!res.isOk()) {\n        return device->WriteFail(\"Unable to query battery data\");\n    }\n    if (version >= 3) {\n        using aidl::android::hardware::health::BatteryHealthData;\n\n        BatteryHealthData data;\n        auto res = health_hal->getBatteryHealthData(&data);\n        if (!res.isOk()) {\n            return device->WriteFail(\"Unable to query battery data\");\n        }\n        if (data.batterySerialNumber) {\n            *message = *data.batterySerialNumber;\n        }\n    }\n    return true;\n}\n\nbool GetBatteryPartStatus(FastbootDevice* device, const std::vector<std::string>&,\n                          std::string* message) {\n    auto health_hal = device->health_hal();\n    if (!health_hal) {\n        return false;\n    }\n\n    using aidl::android::hardware::health::BatteryPartStatus;\n\n    BatteryPartStatus status = BatteryPartStatus::UNSUPPORTED;\n\n    int32_t version = 0;\n    auto res = health_hal->getInterfaceVersion(&version);\n    if (!res.isOk()) {\n        return device->WriteFail(\"Unable to query battery data\");\n    }\n    if (version >= 3) {\n        using aidl::android::hardware::health::BatteryHealthData;\n\n        BatteryHealthData data;\n        auto res = health_hal->getBatteryHealthData(&data);\n        if (!res.isOk()) {\n            return device->WriteFail(\"Unable to query battery data\");\n        }\n        status = data.batteryPartStatus;\n    }\n    switch (status) {\n        case BatteryPartStatus::UNSUPPORTED:\n            *message = \"unsupported\";\n            break;\n        case BatteryPartStatus::ORIGINAL:\n            *message = \"original\";\n            break;\n        case BatteryPartStatus::REPLACED:\n            *message = \"replaced\";\n            break;\n        default:\n            *message = \"unknown\";\n            break;\n    }\n    return true;\n}\n"
  },
  {
    "path": "fastboot/device/variables.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <functional>\n#include <string>\n#include <vector>\n\nclass FastbootDevice;\n\nbool GetVersion(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);\nbool GetBootloaderVersion(FastbootDevice* device, const std::vector<std::string>& args,\n                          std::string* message);\nbool GetBasebandVersion(FastbootDevice* device, const std::vector<std::string>& args,\n                        std::string* message);\nbool GetOsVersion(FastbootDevice* device, const std::vector<std::string>& args,\n                  std::string* message);\nbool GetVndkVersion(FastbootDevice* device, const std::vector<std::string>& args,\n                    std::string* message);\nbool GetProduct(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);\nbool GetSerial(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);\nbool GetSecure(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);\nbool GetCurrentSlot(FastbootDevice* device, const std::vector<std::string>& args,\n                    std::string* message);\nbool GetSlotCount(FastbootDevice* device, const std::vector<std::string>& args,\n                  std::string* message);\nbool GetSlotSuccessful(FastbootDevice* device, const std::vector<std::string>& args,\n                       std::string* message);\nbool GetSlotUnbootable(FastbootDevice* device, const std::vector<std::string>& args,\n                       std::string* message);\nbool GetMaxDownloadSize(FastbootDevice* device, const std::vector<std::string>& args,\n                        std::string* message);\nbool GetUnlocked(FastbootDevice* device, const std::vector<std::string>& args,\n                 std::string* message);\nbool GetHasSlot(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);\nbool GetPartitionSize(FastbootDevice* device, const std::vector<std::string>& args,\n                      std::string* message);\nbool GetPartitionType(FastbootDevice* device, const std::vector<std::string>& args,\n                      std::string* message);\nbool GetPartitionIsLogical(FastbootDevice* device, const std::vector<std::string>& args,\n                           std::string* message);\nbool GetIsUserspace(FastbootDevice* device, const std::vector<std::string>& args,\n                    std::string* message);\nbool GetIsForceDebuggable(FastbootDevice* device, const std::vector<std::string>& args,\n                          std::string* message);\nbool GetHardwareRevision(FastbootDevice* device, const std::vector<std::string>& args,\n                         std::string* message);\nbool GetVariant(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);\nbool GetOffModeChargeState(FastbootDevice* device, const std::vector<std::string>& args,\n                           std::string* message);\nbool GetBatteryVoltage(FastbootDevice* device, const std::vector<std::string>& args,\n                       std::string* message);\nbool GetBatterySoC(FastbootDevice* device, const std::vector<std::string>& args,\n                   std::string* message);\nbool GetBatterySoCOk(FastbootDevice* device, const std::vector<std::string>& args,\n                     std::string* message);\nbool GetBatterySerialNumber(FastbootDevice* device, const std::vector<std::string>& args,\n                            std::string* message);\nbool GetBatteryPartStatus(FastbootDevice* device, const std::vector<std::string>& args,\n                          std::string* message);\nbool GetSuperPartitionName(FastbootDevice* device, const std::vector<std::string>& args,\n                           std::string* message);\nbool GetSnapshotUpdateStatus(FastbootDevice* device, const std::vector<std::string>& args,\n                             std::string* message);\nbool GetCpuAbi(FastbootDevice* device, const std::vector<std::string>& args, std::string* message);\nbool GetSystemFingerprint(FastbootDevice* device, const std::vector<std::string>& args,\n                          std::string* message);\nbool GetVendorFingerprint(FastbootDevice* device, const std::vector<std::string>& args,\n                          std::string* message);\nbool GetDynamicPartition(FastbootDevice* device, const std::vector<std::string>& args,\n                         std::string* message);\nbool GetFirstApiLevel(FastbootDevice* device, const std::vector<std::string>& args,\n                      std::string* message);\nbool GetSecurityPatchLevel(FastbootDevice* device, const std::vector<std::string>& args,\n                           std::string* message);\nbool GetTrebleEnabled(FastbootDevice* device, const std::vector<std::string>& args,\n                      std::string* message);\nbool GetMaxFetchSize(FastbootDevice* /* device */, const std::vector<std::string>& /* args */,\n                     std::string* message);\n\n// Complex cases.\nbool GetDmesg(FastbootDevice* device);\n\n// Helpers for getvar all.\nstd::vector<std::vector<std::string>> GetAllPartitionArgsWithSlot(FastbootDevice* device);\nstd::vector<std::vector<std::string>> GetAllPartitionArgsNoSlot(FastbootDevice* device);\n"
  },
  {
    "path": "fastboot/fastboot.bash",
    "content": "# /* vim: set ai ts=4 ft=sh: */\n#\n# Copyright 2017, The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n_fastboot() {\n    if ! check_type \"$1\" >/dev/null; then\n        return\n    fi\n\n    if check_type _init_completion >/dev/null; then\n        _init_completion || return\n    fi\n\n    local where i cur serial\n    COMPREPLY=()\n\n    serial=\"${ANDROID_SERIAL:-none}\"\n    where=OPTIONS\n    for ((i=1; i <= COMP_CWORD; i++)); do\n        cur=\"${COMP_WORDS[i]}\"\n        case \"${cur}\" in\n            -s)\n                where=OPT_SERIAL\n                ;;\n            --slot)\n                where=OPT_SLOT\n                ;;\n            -*)\n                where=OPTIONS\n                ;;\n            *)\n                if [[ $where == OPT_SERIAL ]]; then\n                    where=OPT_SERIAL_ARG\n                    serial=${cur}\n                elif [[ $where == OPT_SLOT ]]; then\n                    where=OPT_SLOT_ARG\n                else\n                    where=COMMAND\n                    break\n                fi\n                ;;\n        esac\n    done\n\n    if [[ $where == COMMAND && $i -ge $COMP_CWORD ]]; then\n        where=OPTIONS\n    fi\n\n    OPTIONS=\"-a -c --disable-verification --disable-verity -h --help -s --set-active --skip-secondary --skip-reboot --slot -u --version -w\"\n    COMMAND=\"continue devices erase flash flashall flashing format getvar get_staged help oem reboot stage update\"\n\n    case $where in\n        OPTIONS|OPT_SERIAL)\n            COMPREPLY=( $(compgen -W \"$OPTIONS $COMMAND\" -- \"$cur\") )\n            ;;\n        OPT_SERIAL_ARG)\n            local devices=$(command fastboot devices 2> /dev/null | awk '{ print $1 }')\n            COMPREPLY=( $(compgen -W \"${devices}\" -- ${cur}) )\n            ;;\n        OPT_SLOT_ARG)\n            local slots=\"a all b other\"\n            COMPREPLY=( $(compgen -W \"${slots}\" -- ${cur}) )\n            ;;\n        COMMAND)\n            if [[ $i -eq $COMP_CWORD ]]; then\n                COMPREPLY=( $(compgen -W \"$COMMAND\" -- \"$cur\") )\n            else\n                i=$((i+1))\n                case \"${cur}\" in\n                    flash)\n                        _fastboot_cmd_flash \"$serial\" $i\n                        ;;\n                    reboot)\n                        if [[ $COMP_CWORD == $i ]]; then\n                            args=\"bootloader\"\n                            COMPREPLY=( $(compgen -W \"${args}\" -- \"${COMP_WORDS[i]}\") )\n                        fi\n                        ;;\n                    update)\n                        _fastboot_cmd_update \"$serial\" $i\n                        ;;\n                esac\n            fi\n            ;;\n    esac\n\n    return 0\n}\n\n_fastboot_cmd_flash() {\n    local serial i cur\n    local partitions\n\n    serial=$1\n    i=$2\n\n    cur=\"${COMP_WORDS[COMP_CWORD]}\"\n    if [[ $i -eq $COMP_CWORD ]]; then\n        partitions=\"boot bootloader dtbo init_boot modem odm odm_dlkm oem product pvmfw radio recovery system system_dlkm vbmeta vendor vendor_dlkm vendor_kernel_boot\"\n        COMPREPLY=( $(compgen -W \"$partitions\" -- $cur) )\n    else\n        _fastboot_util_complete_local_file \"${cur}\" '!*.img'\n    fi\n}\n\n_fastboot_cmd_update() {\n    local serial i cur\n\n    serial=$1\n    i=$2\n\n    cur=\"${COMP_WORDS[COMP_CWORD]}\"\n\n    _fastboot_util_complete_local_file \"${cur}\" '!*.zip'\n}\n\n_fastboot_util_complete_local_file() {\n    local file xspec i j IFS=$'\\n'\n    local -a dirs files\n\n    file=$1\n    xspec=$2\n\n    # Since we're probably doing file completion here, don't add a space after.\n    if [[ $(check_type compopt) == \"builtin\" ]]; then\n        compopt -o plusdirs\n        if [[ \"${xspec}\" == \"\" ]]; then\n            COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -- \"${cur}\") )\n        else\n            compopt +o filenames\n            COMPREPLY=( ${COMPREPLY[@]:-} $(compgen -f -X \"${xspec}\" -- \"${cur}\") )\n        fi\n    else\n        # Work-around for shells with no compopt\n\n        dirs=( $(compgen -d -- \"${cur}\" ) )\n\n        if [[ \"${xspec}\" == \"\" ]]; then\n            files=( ${COMPREPLY[@]:-} $(compgen -f -- \"${cur}\") )\n        else\n            files=( ${COMPREPLY[@]:-} $(compgen -f -X \"${xspec}\" -- \"${cur}\") )\n        fi\n\n        COMPREPLY=( $(\n            for i in \"${files[@]}\"; do\n                local skip=\n                for j in \"${dirs[@]}\"; do\n                    if [[ $i == $j ]]; then\n                        skip=1\n                        break\n                    fi\n                done\n                [[ -n $skip ]] || printf \"%s\\n\" \"$i\"\n            done\n        ))\n\n        COMPREPLY=( ${COMPREPLY[@]:-} $(\n            for i in \"${dirs[@]}\"; do\n                printf \"%s/\\n\" \"$i\"\n            done\n        ))\n    fi\n}\n\nif [[ $(check_type compopt) == \"builtin\" ]]; then\n    complete -F _fastboot fastboot\nelse\n    complete -o nospace -F _fastboot fastboot\nfi\n"
  },
  {
    "path": "fastboot/fastboot.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"fastboot.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <functional>\n#include <iostream>\n#include <memory>\n#include <regex>\n#include <string>\n#include <thread>\n#include <utility>\n#include <vector>\n\n#include <android-base/endian.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/parseint.h>\n#include <android-base/parsenetaddress.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <build/version.h>\n#include <libavb/libavb.h>\n#include <liblp/liblp.h>\n#include <liblp/super_layout_builder.h>\n#include <platform_tools_version.h>\n#include <sparse/sparse.h>\n#include <ziparchive/zip_archive.h>\n\n#include \"bootimg_utils.h\"\n#include \"constants.h\"\n#include \"diagnose_usb.h\"\n#include \"fastboot_driver.h\"\n#include \"fastboot_driver_interface.h\"\n#include \"fs.h\"\n#include \"storage.h\"\n#include \"task.h\"\n#include \"tcp.h\"\n#include \"transport.h\"\n#include \"udp.h\"\n#include \"usb.h\"\n#include \"util.h\"\n#include \"vendor_boot_img_utils.h\"\n\nusing android::base::borrowed_fd;\nusing android::base::ReadFully;\nusing android::base::Split;\nusing android::base::Trim;\nusing android::base::unique_fd;\nusing namespace std::placeholders;\n\n#define FASTBOOT_INFO_VERSION 1\n\nstatic const char* serial = nullptr;\n\nstatic bool g_long_listing = false;\n// Don't resparse files in too-big chunks.\n// libsparse will support INT_MAX, but this results in large allocations, so\n// let's keep it at 1GB to avoid memory pressure on the host.\nstatic constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024;\nstatic int64_t target_sparse_limit = -1;\n\nstatic unsigned g_base_addr = 0x10000000;\nstatic boot_img_hdr_v2 g_boot_img_hdr = {};\nstatic std::string g_cmdline;\nstatic std::string g_dtb_path;\n\nstatic bool g_disable_verity = false;\nstatic bool g_disable_verification = false;\n\nfastboot::FastBootDriver* fb = nullptr;\n\nstatic std::vector<Image> images = {\n        // clang-format off\n    { \"boot\",     \"boot.img\",         \"boot.sig\",     \"boot\",     false, ImageType::BootCritical },\n    { \"bootloader\",\n                  \"bootloader.img\",   \"\",             \"bootloader\",\n                                                                  true,  ImageType::Extra },\n    { \"init_boot\",\n                  \"init_boot.img\",    \"init_boot.sig\",\n                                                      \"init_boot\",\n                                                                  true,  ImageType::BootCritical },\n    { \"\",    \"boot_other.img\",   \"boot.sig\",     \"boot\",     true,  ImageType::Normal },\n    { \"cache\",    \"cache.img\",        \"cache.sig\",    \"cache\",    true,  ImageType::Extra },\n    { \"dtbo\",     \"dtbo.img\",         \"dtbo.sig\",     \"dtbo\",     true,  ImageType::BootCritical },\n    { \"dts\",      \"dt.img\",           \"dt.sig\",       \"dts\",      true,  ImageType::BootCritical },\n    { \"odm\",      \"odm.img\",          \"odm.sig\",      \"odm\",      true,  ImageType::Normal },\n    { \"odm_dlkm\", \"odm_dlkm.img\",     \"odm_dlkm.sig\", \"odm_dlkm\", true,  ImageType::Normal },\n    { \"product\",  \"product.img\",      \"product.sig\",  \"product\",  true,  ImageType::Normal },\n    { \"pvmfw\",    \"pvmfw.img\",        \"pvmfw.sig\",    \"pvmfw\",    true,  ImageType::BootCritical },\n    { \"radio\",    \"radio.img\",        \"\",             \"radio\",    true,  ImageType::Extra },\n    { \"recovery\", \"recovery.img\",     \"recovery.sig\", \"recovery\", true,  ImageType::BootCritical },\n    { \"super\",    \"super.img\",        \"super.sig\",    \"super\",    true,  ImageType::Extra },\n    { \"system\",   \"system.img\",       \"system.sig\",   \"system\",   false, ImageType::Normal },\n    { \"system_dlkm\",\n                  \"system_dlkm.img\",  \"system_dlkm.sig\",\n                                                      \"system_dlkm\",\n                                                                  true,  ImageType::Normal },\n    { \"system_ext\",\n                  \"system_ext.img\",   \"system_ext.sig\",\n                                                      \"system_ext\",\n                                                                  true,  ImageType::Normal },\n    { \"\",    \"system_other.img\", \"system.sig\",   \"system\",   true,  ImageType::Normal },\n    { \"userdata\", \"userdata.img\",     \"userdata.sig\", \"userdata\", true,  ImageType::Extra },\n    { \"vbmeta\",   \"vbmeta.img\",       \"vbmeta.sig\",   \"vbmeta\",   true,  ImageType::BootCritical },\n    { \"vbmeta_system\",\n                  \"vbmeta_system.img\",\n                                      \"vbmeta_system.sig\",\n                                                      \"vbmeta_system\",\n                                                                  true,  ImageType::BootCritical },\n    { \"vbmeta_vendor\",\n                  \"vbmeta_vendor.img\",\n                                      \"vbmeta_vendor.sig\",\n                                                      \"vbmeta_vendor\",\n                                                                  true,  ImageType::BootCritical },\n    { \"vendor\",   \"vendor.img\",       \"vendor.sig\",   \"vendor\",   true,  ImageType::Normal },\n    { \"vendor_boot\",\n                  \"vendor_boot.img\",  \"vendor_boot.sig\",\n                                                      \"vendor_boot\",\n                                                                  true,  ImageType::BootCritical },\n    { \"vendor_dlkm\",\n                  \"vendor_dlkm.img\",  \"vendor_dlkm.sig\",\n                                                      \"vendor_dlkm\",\n                                                                  true,  ImageType::Normal },\n    { \"vendor_kernel_boot\",\n                  \"vendor_kernel_boot.img\",\n                                      \"vendor_kernel_boot.sig\",\n                                                      \"vendor_kernel_boot\",\n                                                                  true,  ImageType::BootCritical },\n    { \"\",    \"vendor_other.img\", \"vendor.sig\",   \"vendor\",   true,  ImageType::Normal },\n        // clang-format on\n};\n\nchar* get_android_product_out() {\n    char* dir = getenv(\"ANDROID_PRODUCT_OUT\");\n    if (dir == nullptr || dir[0] == '\\0') {\n        return nullptr;\n    }\n    return dir;\n}\n\nstatic std::string find_item_given_name(const std::string& img_name) {\n    char* dir = get_android_product_out();\n    if (!dir) {\n        die(\"ANDROID_PRODUCT_OUT not set\");\n    }\n    return std::string(dir) + \"/\" + img_name;\n}\n\nstd::string find_item(const std::string& item) {\n    for (size_t i = 0; i < images.size(); ++i) {\n        if (!images[i].nickname.empty() && item == images[i].nickname) {\n            return find_item_given_name(images[i].img_name);\n        }\n    }\n\n    fprintf(stderr, \"unknown partition '%s'\\n\", item.c_str());\n    return \"\";\n}\n\ndouble last_start_time;\n\nstatic void Status(const std::string& message) {\n    if (!message.empty()) {\n        static constexpr char kStatusFormat[] = \"%-50s \";\n        fprintf(stderr, kStatusFormat, message.c_str());\n    }\n    last_start_time = now();\n}\n\nstatic void Epilog(int status) {\n    if (status) {\n        fprintf(stderr, \"FAILED (%s)\\n\", fb->Error().c_str());\n        die(\"Command failed\");\n    } else {\n        double split = now();\n        fprintf(stderr, \"OKAY [%7.3fs]\\n\", (split - last_start_time));\n    }\n}\n\nstatic void InfoMessage(const std::string& info) {\n    fprintf(stderr, \"(bootloader) %s\\n\", info.c_str());\n}\n\nstatic void TextMessage(const std::string& text) {\n    fprintf(stderr, \"%s\", text.c_str());\n}\n\nbool ReadFileToVector(const std::string& file, std::vector<char>* out) {\n    out->clear();\n\n    unique_fd fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY)));\n    if (fd == -1) {\n        return false;\n    }\n\n    out->resize(get_file_size(fd));\n    return ReadFully(fd, out->data(), out->size());\n}\n\nstatic int match_fastboot_with_serial(usb_ifc_info* info, const char* local_serial) {\n    if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {\n        return -1;\n    }\n\n    // require matching serial number or device path if requested\n    // at the command line with the -s option.\n    if (local_serial && (strcmp(local_serial, info->serial_number) != 0 &&\n                         strcmp(local_serial, info->device_path) != 0))\n        return -1;\n    return 0;\n}\n\nstatic ifc_match_func match_fastboot(const char* local_serial = serial) {\n    return [local_serial](usb_ifc_info* info) -> int {\n        return match_fastboot_with_serial(info, local_serial);\n    };\n}\n\n// output compatible with \"adb devices\"\nstatic void PrintDevice(const char* local_serial, const char* status = nullptr,\n                        const char* details = nullptr) {\n    if (local_serial == nullptr || strlen(local_serial) == 0) {\n        return;\n    }\n\n    if (g_long_listing) {\n        printf(\"%-22s\", local_serial);\n    } else {\n        printf(\"%s\\t\", local_serial);\n    }\n\n    if (status != nullptr && strlen(status) > 0) {\n        printf(\" %s\", status);\n    }\n\n    if (g_long_listing) {\n        if (details != nullptr && strlen(details) > 0) {\n            printf(\" %s\", details);\n        }\n    }\n\n    putchar('\\n');\n}\n\nstatic int list_devices_callback(usb_ifc_info* info) {\n    if (match_fastboot_with_serial(info, nullptr) == 0) {\n        std::string serial = info->serial_number;\n        std::string interface = info->interface;\n        if (interface.empty()) {\n            interface = \"fastboot\";\n        }\n        if (!info->writable) {\n            serial = UsbNoPermissionsShortHelpText();\n        }\n        if (!serial[0]) {\n            serial = \"????????????\";\n        }\n\n        PrintDevice(serial.c_str(), interface.c_str(), info->device_path);\n    }\n\n    return -1;\n}\n\nResult<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial) {\n    Socket::Protocol protocol;\n    const char* net_address = nullptr;\n    int port = 0;\n\n    if (android::base::StartsWith(serial, \"tcp:\")) {\n        protocol = Socket::Protocol::kTcp;\n        net_address = serial.c_str() + strlen(\"tcp:\");\n        port = tcp::kDefaultPort;\n    } else if (android::base::StartsWith(serial, \"udp:\")) {\n        protocol = Socket::Protocol::kUdp;\n        net_address = serial.c_str() + strlen(\"udp:\");\n        port = udp::kDefaultPort;\n    } else {\n        return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX)\n               << \"protocol prefix ('tcp:' or 'udp:') is missed: \" << serial << \". \"\n               << \"Expected address format:\\n\"\n               << \"<protocol>:<address>:<port> (tcp:localhost:5554)\";\n    }\n\n    std::string error;\n    std::string host;\n    if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {\n        return Error<FastbootError>(FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS)\n               << \"invalid network address '\" << net_address << \"': \" << error;\n    }\n\n    return NetworkSerial{protocol, host, port};\n}\n\n// Opens a new Transport connected to the particular device.\n// arguments:\n//\n// local_serial - device to connect (can be a network or usb serial name)\n// wait_for_device - flag indicates whether we need to wait for device\n// announce - flag indicates whether we need to print error to stdout in case\n// we cannot connect to the device\n//\n// The returned Transport is a singleton, so multiple calls to this function will return the same\n// object, and the caller should not attempt to delete the returned Transport.\nstatic std::unique_ptr<Transport> open_device(const char* local_serial, bool wait_for_device = true,\n                                              bool announce = true) {\n    const Result<NetworkSerial, FastbootError> network_serial = ParseNetworkSerial(local_serial);\n\n    std::unique_ptr<Transport> transport;\n    while (true) {\n        if (network_serial.ok()) {\n            std::string error;\n            if (network_serial->protocol == Socket::Protocol::kTcp) {\n                transport = tcp::Connect(network_serial->address, network_serial->port, &error);\n            } else if (network_serial->protocol == Socket::Protocol::kUdp) {\n                transport = udp::Connect(network_serial->address, network_serial->port, &error);\n            }\n\n            if (!transport && announce) {\n                LOG(ERROR) << \"error: \" << error;\n            }\n        } else if (network_serial.error().code() ==\n                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX) {\n            // WRONG_PREFIX is special because it happens when user wants to communicate with USB\n            // device\n            transport = usb_open(match_fastboot(local_serial));\n        } else {\n            Expect(network_serial);\n        }\n\n        if (transport) {\n            return transport;\n        }\n\n        if (!wait_for_device) {\n            return transport;\n        }\n\n        if (announce) {\n            announce = false;\n            LOG(ERROR) << \"< waiting for \" << local_serial << \">\";\n        }\n        std::this_thread::sleep_for(std::chrono::seconds(1));\n    }\n}\n\nstatic std::unique_ptr<Transport> NetworkDeviceConnected(bool print = false) {\n    std::unique_ptr<Transport> transport;\n    std::unique_ptr<Transport> result;\n\n    ConnectedDevicesStorage storage;\n    std::set<std::string> devices;\n    if (storage.Exists()) {\n        FileLock lock = storage.Lock();\n        devices = storage.ReadDevices(lock);\n    }\n\n    for (const std::string& device : devices) {\n        transport = open_device(device.c_str(), false, false);\n\n        if (print) {\n            PrintDevice(device.c_str(), transport ? \"fastboot\" : \"offline\");\n        }\n\n        if (transport) {\n            result = std::move(transport);\n        }\n    }\n\n    return result;\n}\n\n// Detects the fastboot connected device to open a new Transport.\n// Detecting logic:\n//\n// if serial is provided - try to connect to this particular usb/network device\n// othervise:\n// 1. Check connected usb devices and return the last connected one\n// 2. Check connected network devices and return the last connected one\n// 2. If nothing is connected - wait for any device by repeating p. 1 and 2\n//\n// The returned Transport is a singleton, so multiple calls to this function will return the same\n// object, and the caller should not attempt to delete the returned Transport.\nstatic std::unique_ptr<Transport> open_device() {\n    if (serial != nullptr) {\n        return open_device(serial);\n    }\n\n    bool announce = true;\n    std::unique_ptr<Transport> transport;\n    while (true) {\n        transport = usb_open(match_fastboot(nullptr));\n        if (transport) {\n            return transport;\n        }\n\n        transport = NetworkDeviceConnected();\n        if (transport) {\n            return transport;\n        }\n\n        if (announce) {\n            announce = false;\n            LOG(ERROR) << \"< waiting for any device >\";\n        }\n        std::this_thread::sleep_for(std::chrono::seconds(1));\n    }\n\n    return transport;\n}\n\nstatic int Connect(int argc, char* argv[]) {\n    if (argc != 1) {\n        LOG(FATAL) << \"connect command requires to receive only 1 argument. Usage:\" << std::endl\n                   << \"fastboot connect [tcp:|udp:host:port]\";\n    }\n\n    const char* local_serial = *argv;\n    Expect(ParseNetworkSerial(local_serial));\n\n    if (!open_device(local_serial, false)) {\n        return 1;\n    }\n\n    ConnectedDevicesStorage storage;\n    {\n        FileLock lock = storage.Lock();\n        std::set<std::string> devices = storage.ReadDevices(lock);\n        devices.insert(local_serial);\n        storage.WriteDevices(lock, devices);\n    }\n\n    return 0;\n}\n\nstatic int Disconnect(const char* local_serial) {\n    Expect(ParseNetworkSerial(local_serial));\n\n    ConnectedDevicesStorage storage;\n    {\n        FileLock lock = storage.Lock();\n        std::set<std::string> devices = storage.ReadDevices(lock);\n        devices.erase(local_serial);\n        storage.WriteDevices(lock, devices);\n    }\n\n    return 0;\n}\n\nstatic int Disconnect() {\n    ConnectedDevicesStorage storage;\n    {\n        FileLock lock = storage.Lock();\n        storage.Clear(lock);\n    }\n\n    return 0;\n}\n\nstatic int Disconnect(int argc, char* argv[]) {\n    switch (argc) {\n        case 0: {\n            return Disconnect();\n        }\n        case 1: {\n            return Disconnect(*argv);\n        }\n        default:\n            LOG(FATAL) << \"disconnect command can receive only 0 or 1 arguments. Usage:\"\n                       << std::endl\n                       << \"fastboot disconnect # disconnect all devices\" << std::endl\n                       << \"fastboot disconnect [tcp:|udp:host:port] # disconnect device\";\n    }\n\n    return 0;\n}\n\nstatic void list_devices() {\n    // We don't actually open a USB device here,\n    // just getting our callback called so we can\n    // list all the connected devices.\n    usb_open(list_devices_callback);\n    NetworkDeviceConnected(/* print */ true);\n}\n\nvoid syntax_error(const char* fmt, ...) {\n    fprintf(stderr, \"fastboot: usage: \");\n\n    va_list ap;\n    va_start(ap, fmt);\n    vfprintf(stderr, fmt, ap);\n    va_end(ap);\n\n    fprintf(stderr, \"\\n\");\n    exit(1);\n}\n\nstatic int show_help() {\n    // clang-format off\n    fprintf(stdout,\n//                    1         2         3         4         5         6         7         8\n//           12345678901234567890123456789012345678901234567890123456789012345678901234567890\n            \"usage: fastboot [OPTION...] COMMAND...\\n\"\n            \"\\n\"\n            \"flashing:\\n\"\n            \" update ZIP                 Flash all partitions from an update.zip package.\\n\"\n            \" flashall                   Flash all partitions from $ANDROID_PRODUCT_OUT.\\n\"\n            \"                            On A/B devices, flashed slot is set as active.\\n\"\n            \"                            Secondary images may be flashed to inactive slot.\\n\"\n            \" flash PARTITION [FILENAME] Flash given partition, using the image from\\n\"\n            \"                            $ANDROID_PRODUCT_OUT if no filename is given.\\n\"\n            \" flash vendor_boot:RAMDISK [FILENAME]\\n\"\n            \"                            Flash vendor_boot ramdisk, fetching the existing\\n\"\n            \"                            vendor_boot image and repackaging it with the new\\n\"\n            \"                            ramdisk.\\n\"\n            \" --dtb DTB                  If set with flash vendor_boot:RAMDISK, then\\n\"\n            \"                            update the vendor_boot image with provided DTB.\\n\"\n            \"\\n\"\n            \"basics:\\n\"\n            \" devices [-l]               List devices in bootloader (-l: with device paths).\\n\"\n            \" getvar NAME                Display given bootloader variable.\\n\"\n            \" reboot [bootloader]        Reboot device.\\n\"\n            \"\\n\"\n            \"locking/unlocking:\\n\"\n            \" flashing lock|unlock       Lock/unlock partitions for flashing\\n\"\n            \" flashing lock_critical|unlock_critical\\n\"\n            \"                            Lock/unlock 'critical' bootloader partitions.\\n\"\n            \" flashing get_unlock_ability\\n\"\n            \"                            Check whether unlocking is allowed (1) or not(0).\\n\"\n            \"\\n\"\n            \"advanced:\\n\"\n            \" erase PARTITION            Erase a flash partition.\\n\"\n            \" format[:FS_TYPE[:SIZE]] PARTITION\\n\"\n            \"                            Format a flash partition.\\n\"\n            \" set_active SLOT            Set the active slot.\\n\"\n            \" oem [COMMAND...]           Execute OEM-specific command.\\n\"\n            \" gsi wipe|disable|status    Wipe, disable or show status of a GSI installation\\n\"\n            \"                            (fastbootd only).\\n\"\n            \" wipe-super [SUPER_EMPTY]   Wipe the super partition. This will reset it to\\n\"\n            \"                            contain an empty set of default dynamic partitions.\\n\"\n            \" create-logical-partition NAME SIZE\\n\"\n            \"                            Create a logical partition with the given name and\\n\"\n            \"                            size, in the super partition.\\n\"\n            \" delete-logical-partition NAME\\n\"\n            \"                            Delete a logical partition with the given name.\\n\"\n            \" resize-logical-partition NAME SIZE\\n\"\n            \"                            Change the size of the named logical partition.\\n\"\n            \" snapshot-update cancel     On devices that support snapshot-based updates, cancel\\n\"\n            \"                            an in-progress update. This may make the device\\n\"\n            \"                            unbootable until it is reflashed.\\n\"\n            \" snapshot-update merge      On devices that support snapshot-based updates, finish\\n\"\n            \"                            an in-progress update if it is in the \\\"merging\\\"\\n\"\n            \"                            phase.\\n\"\n            \" fetch PARTITION OUT_FILE   Fetch a partition image from the device.\"\n            \"\\n\"\n            \"boot image:\\n\"\n            \" boot KERNEL [RAMDISK [SECOND]]\\n\"\n            \"                            Download and boot kernel from RAM.\\n\"\n            \" flash:raw PARTITION KERNEL [RAMDISK [SECOND]]\\n\"\n            \"                            Create boot image and flash it.\\n\"\n            \" --dtb DTB                  Specify path to DTB for boot image header version 2.\\n\"\n            \" --cmdline CMDLINE          Override kernel command line.\\n\"\n            \" --base ADDRESS             Set kernel base address (default: 0x10000000).\\n\"\n            \" --kernel-offset            Set kernel offset (default: 0x00008000).\\n\"\n            \" --ramdisk-offset           Set ramdisk offset (default: 0x01000000).\\n\"\n            \" --tags-offset              Set tags offset (default: 0x00000100).\\n\"\n            \" --dtb-offset               Set dtb offset (default: 0x01100000).\\n\"\n            \" --page-size BYTES          Set flash page size (default: 2048).\\n\"\n            \" --header-version VERSION   Set boot image header version.\\n\"\n            \" --os-version MAJOR[.MINOR[.PATCH]]\\n\"\n            \"                            Set boot image OS version (default: 0.0.0).\\n\"\n            \" --os-patch-level YYYY-MM-DD\\n\"\n            \"                            Set boot image OS security patch level.\\n\"\n            // TODO: still missing: `second_addr`, `name`, `id`, `recovery_dtbo_*`.\n            \"\\n\"\n            // TODO: what device(s) used this? is there any documentation?\n            //\" continue                               Continue with autoboot.\\n\"\n            //\"\\n\"\n            \"Android Things:\\n\"\n            \" stage IN_FILE              Sends given file to stage for the next command.\\n\"\n            \" get_staged OUT_FILE        Writes data staged by the last command to a file.\\n\"\n            \"\\n\"\n            \"options:\\n\"\n            \" -w                         Wipe userdata.\\n\"\n            \" -s SERIAL                  Specify a USB device.\\n\"\n            \" -s tcp|udp:HOST[:PORT]     Specify a network device.\\n\"\n            \" -S SIZE[K|M|G]             Break into sparse files no larger than SIZE.\\n\"\n            \" --force                    Force a flash operation that may be unsafe.\\n\"\n            \" --slot SLOT                Use SLOT; 'all' for both slots, 'other' for\\n\"\n            \"                            non-current slot (default: current active slot).\\n\"\n            \" --set-active[=SLOT]        Sets the active slot before rebooting.\\n\"\n            \" --skip-secondary           Don't flash secondary slots in flashall/update.\\n\"\n            \" --skip-reboot              Don't reboot device after flashing.\\n\"\n            \" --disable-verity           Sets disable-verity when flashing vbmeta.\\n\"\n            \" --disable-verification     Sets disable-verification when flashing vbmeta.\\n\"\n            \" --disable-super-optimization\\n\"\n            \"                            Disables optimizations on flashing super partition.\\n\"\n            \" --exclude-dynamic-partitions\\n\"\n            \"                            Excludes flashing of dynamic partitions.\\n\"\n            \" --disable-fastboot-info    Will collects tasks from image list rather than \\n\"\n            \"                            $OUT/fastboot-info.txt.\\n\"\n            \" --fs-options=OPTION[,OPTION]\\n\"\n            \"                            Enable filesystem features. OPTION supports casefold, projid, compress\\n\"\n            // TODO: remove --unbuffered?\n            \" --unbuffered               Don't buffer input or output.\\n\"\n            \" --verbose, -v              Verbose output.\\n\"\n            \" --version                  Display version.\\n\"\n            \" --help, -h                 Show this message.\\n\"\n        );\n    // clang-format on\n    return 0;\n}\n\nstatic std::vector<char> LoadBootableImage(const std::string& kernel, const std::string& ramdisk,\n                                           const std::string& second_stage) {\n    std::vector<char> kernel_data;\n    if (!ReadFileToVector(kernel, &kernel_data)) {\n        die(\"cannot load '%s': %s\", kernel.c_str(), strerror(errno));\n    }\n\n    // Is this actually a boot image?\n    if (kernel_data.size() < sizeof(boot_img_hdr_v3)) {\n        die(\"cannot load '%s': too short\", kernel.c_str());\n    }\n    if (!memcmp(kernel_data.data(), BOOT_MAGIC, BOOT_MAGIC_SIZE)) {\n        if (!g_cmdline.empty()) {\n            bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v2*>(kernel_data.data()), g_cmdline);\n        }\n\n        if (!ramdisk.empty()) die(\"cannot boot a boot.img *and* ramdisk\");\n\n        return kernel_data;\n    }\n\n    std::vector<char> ramdisk_data;\n    if (!ramdisk.empty()) {\n        if (!ReadFileToVector(ramdisk, &ramdisk_data)) {\n            die(\"cannot load '%s': %s\", ramdisk.c_str(), strerror(errno));\n        }\n    }\n\n    std::vector<char> second_stage_data;\n    if (!second_stage.empty()) {\n        if (!ReadFileToVector(second_stage, &second_stage_data)) {\n            die(\"cannot load '%s': %s\", second_stage.c_str(), strerror(errno));\n        }\n    }\n\n    std::vector<char> dtb_data;\n    if (!g_dtb_path.empty()) {\n        if (g_boot_img_hdr.header_version != 2) {\n            die(\"Argument dtb not supported for boot image header version %d\\n\",\n                g_boot_img_hdr.header_version);\n        }\n        if (!ReadFileToVector(g_dtb_path, &dtb_data)) {\n            die(\"cannot load '%s': %s\", g_dtb_path.c_str(), strerror(errno));\n        }\n    }\n\n    fprintf(stderr, \"creating boot image...\\n\");\n\n    std::vector<char> out;\n    mkbootimg(kernel_data, ramdisk_data, second_stage_data, dtb_data, g_base_addr, g_boot_img_hdr,\n              &out);\n\n    if (!g_cmdline.empty()) {\n        bootimg_set_cmdline(reinterpret_cast<boot_img_hdr_v2*>(out.data()), g_cmdline);\n    }\n    fprintf(stderr, \"creating boot image - %zu bytes\\n\", out.size());\n    return out;\n}\n\nstatic bool UnzipToMemory(ZipArchiveHandle zip, const std::string& entry_name,\n                          std::vector<char>* out) {\n    ZipEntry64 zip_entry;\n    if (FindEntry(zip, entry_name, &zip_entry) != 0) {\n        fprintf(stderr, \"archive does not contain '%s'\\n\", entry_name.c_str());\n        return false;\n    }\n\n    if (zip_entry.uncompressed_length > std::numeric_limits<size_t>::max()) {\n        die(\"entry '%s' is too large: %\" PRIu64, entry_name.c_str(), zip_entry.uncompressed_length);\n    }\n    out->resize(zip_entry.uncompressed_length);\n\n    fprintf(stderr, \"extracting %s (%zu MB) to RAM...\\n\", entry_name.c_str(),\n            out->size() / 1024 / 1024);\n\n    int error =\n            ExtractToMemory(zip, &zip_entry, reinterpret_cast<uint8_t*>(out->data()), out->size());\n    if (error != 0) die(\"failed to extract '%s': %s\", entry_name.c_str(), ErrorCodeString(error));\n\n    return true;\n}\n\n#if defined(_WIN32)\n\n// TODO: move this to somewhere it can be shared.\n\n#include <windows.h>\n\n// Windows' tmpfile(3) requires administrator rights because\n// it creates temporary files in the root directory.\nstatic FILE* win32_tmpfile() {\n    char temp_path[PATH_MAX];\n    DWORD nchars = GetTempPath(sizeof(temp_path), temp_path);\n    if (nchars == 0 || nchars >= sizeof(temp_path)) {\n        die(\"GetTempPath failed, error %ld\", GetLastError());\n    }\n\n    char filename[PATH_MAX];\n    if (GetTempFileName(temp_path, \"fastboot\", 0, filename) == 0) {\n        die(\"GetTempFileName failed, error %ld\", GetLastError());\n    }\n\n    return fopen(filename, \"w+bTD\");\n}\n\n#define tmpfile win32_tmpfile\n\nstatic int make_temporary_fd(const char* /*what*/) {\n    // TODO: reimplement to avoid leaking a FILE*.\n    return fileno(tmpfile());\n}\n\n#else\n\nstatic std::string make_temporary_template() {\n    const char* tmpdir = getenv(\"TMPDIR\");\n    if (tmpdir == nullptr) tmpdir = P_tmpdir;\n    return std::string(tmpdir) + \"/fastboot_userdata_XXXXXX\";\n}\n\nstatic int make_temporary_fd(const char* what) {\n    std::string path_template(make_temporary_template());\n    int fd = mkstemp(&path_template[0]);\n    if (fd == -1) {\n        die(\"failed to create temporary file for %s with template %s: %s\\n\", path_template.c_str(),\n            what, strerror(errno));\n    }\n    unlink(path_template.c_str());\n    return fd;\n}\n\n#endif\n\nstatic unique_fd UnzipToFile(ZipArchiveHandle zip, const char* entry_name) {\n    unique_fd fd(make_temporary_fd(entry_name));\n\n    ZipEntry64 zip_entry;\n    if (FindEntry(zip, entry_name, &zip_entry) != 0) {\n        fprintf(stderr, \"archive does not contain '%s'\\n\", entry_name);\n        errno = ENOENT;\n        return unique_fd();\n    }\n\n    fprintf(stderr, \"extracting %s (%\" PRIu64 \" MB) to disk...\", entry_name,\n            zip_entry.uncompressed_length / 1024 / 1024);\n    double start = now();\n    int error = ExtractEntryToFile(zip, &zip_entry, fd.get());\n    if (error != 0) {\n        die(\"\\nfailed to extract '%s': %s\", entry_name, ErrorCodeString(error));\n    }\n\n    if (lseek(fd.get(), 0, SEEK_SET) != 0) {\n        die(\"\\nlseek on extracted file '%s' failed: %s\", entry_name, strerror(errno));\n    }\n\n    fprintf(stderr, \" took %.3fs\\n\", now() - start);\n\n    return fd;\n}\n\nstatic bool CheckRequirement(const std::string& cur_product, const std::string& var,\n                             const std::string& product, bool invert,\n                             const std::vector<std::string>& options) {\n    Status(\"Checking '\" + var + \"'\");\n\n    double start = now();\n\n    if (!product.empty()) {\n        if (product != cur_product) {\n            double split = now();\n            fprintf(stderr, \"IGNORE, product is %s required only for %s [%7.3fs]\\n\",\n                    cur_product.c_str(), product.c_str(), (split - start));\n            return true;\n        }\n    }\n\n    std::string var_value;\n    if (fb->GetVar(var, &var_value) != fastboot::SUCCESS) {\n        fprintf(stderr, \"FAILED\\n\\n\");\n        fprintf(stderr, \"Could not getvar for '%s' (%s)\\n\\n\", var.c_str(), fb->Error().c_str());\n        return false;\n    }\n\n    bool match = false;\n    for (const auto& option : options) {\n        if (option == var_value ||\n            (option.back() == '*' &&\n             !var_value.compare(0, option.length() - 1, option, 0, option.length() - 1))) {\n            match = true;\n            break;\n        }\n    }\n\n    if (invert) {\n        match = !match;\n    }\n\n    if (match) {\n        double split = now();\n        fprintf(stderr, \"OKAY [%7.3fs]\\n\", (split - start));\n        return true;\n    }\n\n    fprintf(stderr, \"FAILED\\n\\n\");\n    fprintf(stderr, \"Device %s is '%s'.\\n\", var.c_str(), var_value.c_str());\n    fprintf(stderr, \"Update %s '%s'\", invert ? \"rejects\" : \"requires\", options[0].c_str());\n    for (auto it = std::next(options.begin()); it != options.end(); ++it) {\n        fprintf(stderr, \" or '%s'\", it->c_str());\n    }\n    fprintf(stderr, \".\\n\\n\");\n    return false;\n}\n\nbool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,\n                          bool* invert, std::vector<std::string>* options) {\n    // \"require product=alpha|beta|gamma\"\n    // \"require version-bootloader=1234\"\n    // \"require-for-product:gamma version-bootloader=istanbul|constantinople\"\n    // \"require partition-exists=vendor\"\n    *product = \"\";\n    *invert = false;\n\n    auto require_reject_regex = std::regex{\"(require\\\\s+|reject\\\\s+)?\\\\s*(\\\\S+)\\\\s*=\\\\s*(.*)\"};\n    auto require_product_regex =\n            std::regex{\"require-for-product:\\\\s*(\\\\S+)\\\\s+(\\\\S+)\\\\s*=\\\\s*(.*)\"};\n    std::smatch match_results;\n\n    if (std::regex_match(line, match_results, require_reject_regex)) {\n        *invert = Trim(match_results[1]) == \"reject\";\n    } else if (std::regex_match(line, match_results, require_product_regex)) {\n        *product = match_results[1];\n    } else {\n        return false;\n    }\n\n    *name = match_results[2];\n    // Work around an unfortunate name mismatch.\n    if (*name == \"board\") {\n        *name = \"product\";\n    }\n\n    auto raw_options = Split(match_results[3], \"|\");\n    for (const auto& option : raw_options) {\n        auto trimmed_option = Trim(option);\n        options->emplace_back(trimmed_option);\n    }\n\n    return true;\n}\n\n// \"require partition-exists=x\" is a special case, added because of the trouble we had when\n// Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,\n// missing out new partitions. A device with new partitions can use \"partition-exists\" to\n// override the fields `optional_if_no_image` in the `images` array.\nstatic void HandlePartitionExists(const std::vector<std::string>& options) {\n    const std::string& partition_name = options[0];\n    std::string has_slot;\n    if (fb->GetVar(\"has-slot:\" + partition_name, &has_slot) != fastboot::SUCCESS ||\n        (has_slot != \"yes\" && has_slot != \"no\")) {\n        die(\"device doesn't have required partition %s!\", partition_name.c_str());\n    }\n    bool known_partition = false;\n    for (size_t i = 0; i < images.size(); ++i) {\n        if (!images[i].nickname.empty() && images[i].nickname == partition_name) {\n            images[i].optional_if_no_image = false;\n            known_partition = true;\n        }\n    }\n    if (!known_partition) {\n        die(\"device requires partition %s which is not known to this version of fastboot\",\n            partition_name.c_str());\n    }\n}\n\nstatic void CheckRequirements(const std::string& data, bool force_flash) {\n    std::string cur_product;\n    if (fb->GetVar(\"product\", &cur_product) != fastboot::SUCCESS) {\n        fprintf(stderr, \"getvar:product FAILED (%s)\\n\", fb->Error().c_str());\n    }\n\n    auto lines = Split(data, \"\\n\");\n    for (const auto& line : lines) {\n        if (line.empty()) {\n            continue;\n        }\n\n        std::string name;\n        std::string product;\n        bool invert;\n        std::vector<std::string> options;\n\n        if (!ParseRequirementLine(line, &name, &product, &invert, &options)) {\n            fprintf(stderr, \"android-info.txt syntax error: %s\\n\", line.c_str());\n            continue;\n        }\n        if (name == \"partition-exists\") {\n            HandlePartitionExists(options);\n        } else {\n            bool met = CheckRequirement(cur_product, name, product, invert, options);\n            if (!met) {\n                if (!force_flash) {\n                    die(\"requirements not met!\");\n                } else {\n                    fprintf(stderr, \"requirements not met! but proceeding due to --force\\n\");\n                }\n            }\n        }\n    }\n}\n\nstatic void DisplayVarOrError(const std::string& label, const std::string& var) {\n    std::string value;\n\n    if (fb->GetVar(var, &value) != fastboot::SUCCESS) {\n        Status(\"getvar:\" + var);\n        fprintf(stderr, \"FAILED (%s)\\n\", fb->Error().c_str());\n        return;\n    }\n    fprintf(stderr, \"%s: %s\\n\", label.c_str(), value.c_str());\n}\n\nstatic void DumpInfo() {\n    fprintf(stderr, \"--------------------------------------------\\n\");\n    DisplayVarOrError(\"Bootloader Version...\", \"version-bootloader\");\n    DisplayVarOrError(\"Baseband Version.....\", \"version-baseband\");\n    DisplayVarOrError(\"Serial Number........\", \"serialno\");\n    fprintf(stderr, \"--------------------------------------------\\n\");\n}\n\nstd::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size) {\n    if (max_size <= 0 || max_size > std::numeric_limits<uint32_t>::max()) {\n        die(\"invalid max size %\" PRId64, max_size);\n    }\n\n    const int files = sparse_file_resparse(s, max_size, nullptr, 0);\n    if (files < 0) die(\"Failed to compute resparse boundaries\");\n\n    auto temp = std::make_unique<sparse_file*[]>(files);\n    const int rv = sparse_file_resparse(s, max_size, temp.get(), files);\n    if (rv < 0) die(\"Failed to resparse\");\n\n    std::vector<SparsePtr> out_s;\n    for (int i = 0; i < files; i++) {\n        out_s.emplace_back(temp[i], sparse_file_destroy);\n    }\n    return out_s;\n}\n\nstatic std::vector<SparsePtr> load_sparse_files(int fd, int64_t max_size) {\n    SparsePtr s(sparse_file_import_auto(fd, false, true), sparse_file_destroy);\n    if (!s) die(\"cannot sparse read file\");\n\n    return resparse_file(s.get(), max_size);\n}\n\nstatic uint64_t get_uint_var(const char* var_name, fastboot::IFastBootDriver* fb) {\n    std::string value_str;\n    if (fb->GetVar(var_name, &value_str) != fastboot::SUCCESS || value_str.empty()) {\n        verbose(\"target didn't report %s\", var_name);\n        return 0;\n    }\n\n    // Some bootloaders (angler, for example) send spurious whitespace too.\n    value_str = android::base::Trim(value_str);\n\n    uint64_t value;\n    if (!android::base::ParseUint(value_str, &value)) {\n        fprintf(stderr, \"couldn't parse %s '%s'\\n\", var_name, value_str.c_str());\n        return 0;\n    }\n    if (value > 0) verbose(\"target reported %s of %\" PRId64 \" bytes\", var_name, value);\n    return value;\n}\n\nint64_t get_sparse_limit(int64_t size, const FlashingPlan* fp) {\n    if (!fp) return 0;\n\n    int64_t limit = int64_t(fp->sparse_limit);\n    if (limit == 0) {\n        // Unlimited, so see what the target device's limit is.\n        // TODO: shouldn't we apply this limit even if you've used -S?\n        if (target_sparse_limit == -1) {\n            target_sparse_limit = static_cast<int64_t>(get_uint_var(\"max-download-size\", fp->fb));\n        }\n        if (target_sparse_limit > 0) {\n            limit = target_sparse_limit;\n        } else {\n            return 0;\n        }\n    }\n\n    if (size > limit) {\n        return std::min(limit, RESPARSE_LIMIT);\n    }\n\n    return 0;\n}\n\nstatic bool load_buf_fd(unique_fd fd, struct fastboot_buffer* buf, const FlashingPlan* fp) {\n    int64_t sz = get_file_size(fd);\n    if (sz == -1) {\n        return false;\n    }\n\n    if (sparse_file* s = sparse_file_import(fd.get(), false, false)) {\n        buf->image_size = sparse_file_len(s, false, false);\n        if (buf->image_size < 0) {\n            LOG(ERROR) << \"Could not compute length of sparse file\";\n            return false;\n        }\n        sparse_file_destroy(s);\n        buf->file_type = FB_BUFFER_SPARSE;\n    } else {\n        buf->image_size = sz;\n        buf->file_type = FB_BUFFER_FD;\n    }\n\n    lseek(fd.get(), 0, SEEK_SET);\n    int64_t limit = get_sparse_limit(sz, fp);\n    buf->fd = std::move(fd);\n    if (limit) {\n        buf->files = load_sparse_files(buf->fd.get(), limit);\n        if (buf->files.empty()) {\n            return false;\n        }\n        buf->type = FB_BUFFER_SPARSE;\n    } else {\n        buf->type = FB_BUFFER_FD;\n        buf->sz = sz;\n    }\n\n    return true;\n}\n\nstatic bool load_buf(const char* fname, struct fastboot_buffer* buf, const FlashingPlan* fp) {\n    unique_fd fd(TEMP_FAILURE_RETRY(open(fname, O_RDONLY | O_BINARY)));\n\n    if (fd == -1) {\n        return false;\n    }\n\n    struct stat s;\n    if (fstat(fd.get(), &s)) {\n        return false;\n    }\n    if (!S_ISREG(s.st_mode)) {\n        errno = S_ISDIR(s.st_mode) ? EISDIR : EINVAL;\n        return false;\n    }\n\n    return load_buf_fd(std::move(fd), buf, fp);\n}\n\nstatic void rewrite_vbmeta_buffer(struct fastboot_buffer* buf, bool vbmeta_in_boot) {\n    // Buffer needs to be at least the size of the VBMeta struct which\n    // is 256 bytes.\n    if (buf->sz < 256) {\n        return;\n    }\n\n    std::string data;\n    if (!android::base::ReadFdToString(buf->fd, &data)) {\n        die(\"Failed reading from vbmeta\");\n    }\n\n    uint64_t vbmeta_offset = 0;\n    if (vbmeta_in_boot) {\n        // Tries to locate top-level vbmeta from boot.img footer.\n        uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;\n        if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {\n            die(\"Failed to find AVB_FOOTER at offset: %\" PRId64 \", is BOARD_AVB_ENABLE true?\",\n                footer_offset);\n        }\n        const AvbFooter* footer = reinterpret_cast<const AvbFooter*>(data.c_str() + footer_offset);\n        vbmeta_offset = be64toh(footer->vbmeta_offset);\n    }\n    // Ensures there is AVB_MAGIC at vbmeta_offset.\n    if (0 != data.compare(vbmeta_offset, AVB_MAGIC_LEN, AVB_MAGIC)) {\n        die(\"Failed to find AVB_MAGIC at offset: %\" PRId64, vbmeta_offset);\n    }\n\n    fprintf(stderr, \"Rewriting vbmeta struct at offset: %\" PRId64 \"\\n\", vbmeta_offset);\n\n    // There's a 32-bit big endian |flags| field at offset 120 where\n    // bit 0 corresponds to disable-verity and bit 1 corresponds to\n    // disable-verification.\n    //\n    // See external/avb/libavb/avb_vbmeta_image.h for the layout of\n    // the VBMeta struct.\n    uint64_t flags_offset = 123 + vbmeta_offset;\n    if (g_disable_verity) {\n        data[flags_offset] |= 0x01;\n    }\n    if (g_disable_verification) {\n        data[flags_offset] |= 0x02;\n    }\n\n    unique_fd fd(make_temporary_fd(\"vbmeta rewriting\"));\n    if (!android::base::WriteStringToFd(data, fd)) {\n        die(\"Failed writing to modified vbmeta\");\n    }\n    buf->fd = std::move(fd);\n    lseek(buf->fd.get(), 0, SEEK_SET);\n}\n\nstatic bool has_vbmeta_partition() {\n    std::string partition_type;\n    return fb->GetVar(\"partition-type:vbmeta\", &partition_type) == fastboot::SUCCESS ||\n           fb->GetVar(\"partition-type:vbmeta_a\", &partition_type) == fastboot::SUCCESS ||\n           fb->GetVar(\"partition-type:vbmeta_b\", &partition_type) == fastboot::SUCCESS;\n}\n\nstatic bool is_vbmeta_partition(const std::string& partition) {\n    return android::base::EndsWith(partition, \"vbmeta\") ||\n           android::base::EndsWith(partition, \"vbmeta_a\") ||\n           android::base::EndsWith(partition, \"vbmeta_b\");\n}\n\n// Note: this only works in userspace fastboot. In the bootloader, use\n// should_flash_in_userspace().\nbool is_logical(const std::string& partition) {\n    std::string value;\n    return fb->GetVar(\"is-logical:\" + partition, &value) == fastboot::SUCCESS && value == \"yes\";\n}\n\nstatic uint64_t get_partition_size(const std::string& partition) {\n    std::string partition_size_str;\n    if (fb->GetVar(\"partition-size:\" + partition, &partition_size_str) != fastboot::SUCCESS) {\n        if (!is_logical(partition)) {\n            return 0;\n        }\n        die(\"cannot get partition size for %s\", partition.c_str());\n    }\n\n    partition_size_str = fb_fix_numeric_var(partition_size_str);\n    uint64_t partition_size;\n    if (!android::base::ParseUint(partition_size_str, &partition_size)) {\n        if (!is_logical(partition)) {\n            return 0;\n        }\n        die(\"Couldn't parse partition size '%s'.\", partition_size_str.c_str());\n    }\n    return partition_size;\n}\n\nstatic void copy_avb_footer(const ImageSource* source, const std::string& partition,\n                            struct fastboot_buffer* buf) {\n    if (buf->sz < AVB_FOOTER_SIZE || is_logical(partition) ||\n        should_flash_in_userspace(source, partition)) {\n        return;\n    }\n\n    // If the image is sparse, moving the footer will simply corrupt the sparse\n    // format, so currently we don't support moving the footer on sparse files.\n    if (buf->file_type == FB_BUFFER_SPARSE) {\n        LOG(ERROR) << \"Warning: skip copying \" << partition << \" image avb footer due to sparse \"\n                   << \"image.\";\n        return;\n    }\n\n    // If overflows and negative, it should be < buf->sz.\n    int64_t partition_size = static_cast<int64_t>(get_partition_size(partition));\n\n    if (partition_size == buf->sz) {\n        return;\n    }\n    // Some device bootloaders might not implement `fastboot getvar partition-size:boot[_a|_b]`.\n    // In this case, partition_size will be zero.\n    if (partition_size < buf->sz) {\n        fprintf(stderr,\n                \"Warning: skip copying %s image avb footer\"\n                \" (%s partition size: %\" PRId64 \", %s image size: %\" PRId64 \").\\n\",\n                partition.c_str(), partition.c_str(), partition_size, partition.c_str(), buf->sz);\n        return;\n    }\n\n    // IMPORTANT: after the following read, we need to reset buf->fd before return (if not die).\n    // Because buf->fd will still be used afterwards.\n    std::string data;\n    if (!android::base::ReadFdToString(buf->fd, &data)) {\n        die(\"Failed reading from %s\", partition.c_str());\n    }\n\n    uint64_t footer_offset = buf->sz - AVB_FOOTER_SIZE;\n    if (0 != data.compare(footer_offset, AVB_FOOTER_MAGIC_LEN, AVB_FOOTER_MAGIC)) {\n        lseek(buf->fd.get(), 0, SEEK_SET);  // IMPORTANT: resets buf->fd before return.\n        return;\n    }\n\n    const std::string tmp_fd_template = partition + \" rewriting\";\n    unique_fd fd(make_temporary_fd(tmp_fd_template.c_str()));\n    if (!android::base::WriteStringToFd(data, fd)) {\n        die(\"Failed writing to modified %s\", partition.c_str());\n    }\n    lseek(fd.get(), partition_size - AVB_FOOTER_SIZE, SEEK_SET);\n    if (!android::base::WriteStringToFd(data.substr(footer_offset), fd)) {\n        die(\"Failed copying AVB footer in %s\", partition.c_str());\n    }\n    buf->fd = std::move(fd);\n    buf->sz = partition_size;\n    lseek(buf->fd.get(), 0, SEEK_SET);\n}\n\nvoid flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files) {\n    for (size_t i = 0; i < files.size(); i++) {\n        sparse_file* s = files[i].get();\n        int64_t sz = sparse_file_len(s, true, false);\n        if (sz < 0) {\n            LOG(FATAL) << \"Could not compute length of sparse image for \" << partition;\n        }\n        fb->FlashPartition(partition, s, sz, i + 1, files.size());\n    }\n}\n\nstatic void flash_buf(const ImageSource* source, const std::string& partition,\n                      struct fastboot_buffer* buf, const bool apply_vbmeta) {\n    copy_avb_footer(source, partition, buf);\n\n    // Rewrite vbmeta if that's what we're flashing and modification has been requested.\n    if (g_disable_verity || g_disable_verification) {\n        // The vbmeta partition might have additional prefix if running in virtual machine\n        // e.g., guest_vbmeta_a.\n        if (apply_vbmeta) {\n            rewrite_vbmeta_buffer(buf, false /* vbmeta_in_boot */);\n        } else if (!has_vbmeta_partition() &&\n                   (partition == \"boot\" || partition == \"boot_a\" || partition == \"boot_b\")) {\n            rewrite_vbmeta_buffer(buf, true /* vbmeta_in_boot */);\n        }\n    }\n\n    switch (buf->type) {\n        case FB_BUFFER_SPARSE: {\n            flash_partition_files(partition, buf->files);\n            break;\n        }\n        case FB_BUFFER_FD:\n            fb->FlashPartition(partition, buf->fd, buf->sz);\n            break;\n        default:\n            die(\"unknown buffer type: %d\", buf->type);\n    }\n}\n\nstd::string get_current_slot() {\n    std::string current_slot;\n    if (fb->GetVar(\"current-slot\", &current_slot) != fastboot::SUCCESS) return \"\";\n    if (current_slot[0] == '_') current_slot.erase(0, 1);\n    return current_slot;\n}\n\nstatic int get_slot_count(fastboot::IFastBootDriver* fb) {\n    std::string var;\n    int count = 0;\n    if (fb->GetVar(\"slot-count\", &var) != fastboot::SUCCESS ||\n        !android::base::ParseInt(var, &count)) {\n        return 0;\n    }\n    return count;\n}\n\nbool supports_AB(fastboot::IFastBootDriver* fb) {\n    return get_slot_count(fb) >= 2;\n}\n\n// Given a current slot, this returns what the 'other' slot is.\nstatic std::string get_other_slot(const std::string& current_slot, int count) {\n    if (count == 0) return \"\";\n\n    char next = (current_slot[0] - 'a' + 1) % count + 'a';\n    return std::string(1, next);\n}\n\nstatic std::string get_other_slot(const std::string& current_slot) {\n    return get_other_slot(current_slot, get_slot_count(fb));\n}\n\nstatic std::string get_other_slot(int count) {\n    return get_other_slot(get_current_slot(), count);\n}\n\nstatic std::string get_other_slot() {\n    return get_other_slot(get_current_slot(), get_slot_count(fb));\n}\n\nstatic std::string verify_slot(const std::string& slot_name, bool allow_all) {\n    std::string slot = slot_name;\n    if (slot == \"all\") {\n        if (allow_all) {\n            return \"all\";\n        } else {\n            int count = get_slot_count(fb);\n            if (count > 0) {\n                return \"a\";\n            } else {\n                die(\"No known slots\");\n            }\n        }\n    }\n\n    int count = get_slot_count(fb);\n    if (count == 0) die(\"Device does not support slots\");\n\n    if (slot == \"other\") {\n        std::string other = get_other_slot(count);\n        if (other == \"\") {\n            die(\"No known slots\");\n        }\n        return other;\n    }\n\n    if (slot.size() == 1 && (slot[0] - 'a' >= 0 && slot[0] - 'a' < count)) return slot;\n\n    fprintf(stderr, \"Slot %s does not exist. supported slots are:\\n\", slot.c_str());\n    for (int i = 0; i < count; i++) {\n        fprintf(stderr, \"%c\\n\", (char)(i + 'a'));\n    }\n\n    exit(1);\n}\n\nstatic std::string verify_slot(const std::string& slot) {\n    return verify_slot(slot, true);\n}\n\nstatic void do_for_partition(const std::string& part, const std::string& slot,\n                             const std::function<void(const std::string&)>& func, bool force_slot) {\n    std::string has_slot;\n    std::string current_slot;\n    // |part| can be vendor_boot:default. Append slot to the first token.\n    auto part_tokens = android::base::Split(part, \":\");\n\n    if (fb->GetVar(\"has-slot:\" + part_tokens[0], &has_slot) != fastboot::SUCCESS) {\n        /* If has-slot is not supported, the answer is no. */\n        has_slot = \"no\";\n    }\n    if (has_slot == \"yes\") {\n        if (slot == \"\") {\n            current_slot = get_current_slot();\n            if (current_slot == \"\") {\n                die(\"Failed to identify current slot\");\n            }\n            part_tokens[0] += \"_\" + current_slot;\n        } else {\n            part_tokens[0] += \"_\" + slot;\n        }\n        func(android::base::Join(part_tokens, \":\"));\n    } else {\n        if (force_slot && slot != \"\") {\n            fprintf(stderr, \"Warning: %s does not support slots, and slot %s was requested.\\n\",\n                    part_tokens[0].c_str(), slot.c_str());\n        }\n        func(part);\n    }\n}\n\n/* This function will find the real partition name given a base name, and a slot. If slot is NULL or\n * empty, it will use the current slot. If slot is \"all\", it will return a list of all possible\n * partition names. If force_slot is true, it will fail if a slot is specified, and the given\n * partition does not support slots.\n */\nvoid do_for_partitions(const std::string& part, const std::string& slot,\n                       const std::function<void(const std::string&)>& func, bool force_slot) {\n    std::string has_slot;\n    // |part| can be vendor_boot:default. Query has-slot on the first token only.\n    auto part_tokens = android::base::Split(part, \":\");\n\n    if (slot == \"all\") {\n        if (fb->GetVar(\"has-slot:\" + part_tokens[0], &has_slot) != fastboot::SUCCESS) {\n            die(\"Could not check if partition %s has slot %s\", part_tokens[0].c_str(),\n                slot.c_str());\n        }\n        if (has_slot == \"yes\") {\n            for (int i = 0; i < get_slot_count(fb); i++) {\n                do_for_partition(part, std::string(1, (char)(i + 'a')), func, force_slot);\n            }\n        } else {\n            do_for_partition(part, \"\", func, force_slot);\n        }\n    } else {\n        do_for_partition(part, slot, func, force_slot);\n    }\n}\n\n// Fetch a partition from the device to a given fd. This is a wrapper over FetchToFd to fetch\n// the full image.\nstatic uint64_t fetch_partition(const std::string& partition, borrowed_fd fd,\n                                fastboot::IFastBootDriver* fb) {\n    uint64_t fetch_size = get_uint_var(FB_VAR_MAX_FETCH_SIZE, fb);\n    if (fetch_size == 0) {\n        die(\"Unable to get %s. Device does not support fetch command.\", FB_VAR_MAX_FETCH_SIZE);\n    }\n    uint64_t partition_size = get_partition_size(partition);\n    if (partition_size <= 0) {\n        die(\"Invalid partition size for partition %s: %\" PRId64, partition.c_str(), partition_size);\n    }\n\n    uint64_t offset = 0;\n    while (offset < partition_size) {\n        uint64_t chunk_size = std::min(fetch_size, partition_size - offset);\n        if (fb->FetchToFd(partition, fd, offset, chunk_size) != fastboot::RetCode::SUCCESS) {\n            die(\"Unable to fetch %s (offset=%\" PRIx64 \", size=%\" PRIx64 \")\", partition.c_str(),\n                offset, chunk_size);\n        }\n        offset += chunk_size;\n    }\n    return partition_size;\n}\n\nstatic void do_fetch(const std::string& partition, const std::string& slot_override,\n                     const std::string& outfile, fastboot::IFastBootDriver* fb) {\n    unique_fd fd(TEMP_FAILURE_RETRY(\n            open(outfile.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, 0644)));\n    auto fetch = std::bind(fetch_partition, _1, borrowed_fd(fd), fb);\n    do_for_partitions(partition, slot_override, fetch, false /* force slot */);\n}\n\n// Return immediately if not flashing a vendor boot image. If flashing a vendor boot image,\n// repack vendor_boot image with an updated ramdisk. After execution, buf is set\n// to the new image to flash, and return value is the real partition name to flash.\nstatic std::string repack_ramdisk(const char* pname, struct fastboot_buffer* buf,\n                                  fastboot::IFastBootDriver* fb) {\n    std::string_view pname_sv{pname};\n    struct fastboot_buffer dtb_buf = {.sz = 0, .fd = unique_fd(-1)};\n\n    if (!android::base::StartsWith(pname_sv, \"vendor_boot:\") &&\n        !android::base::StartsWith(pname_sv, \"vendor_boot_a:\") &&\n        !android::base::StartsWith(pname_sv, \"vendor_boot_b:\")) {\n        return std::string(pname_sv);\n    }\n    if (buf->type != FB_BUFFER_FD) {\n        die(\"Flashing sparse vendor ramdisk image is not supported.\");\n    }\n    if (buf->sz <= 0) {\n        die(\"repack_ramdisk() sees negative size: %\" PRId64, buf->sz);\n    }\n    std::string partition(pname_sv.substr(0, pname_sv.find(':')));\n    std::string ramdisk(pname_sv.substr(pname_sv.find(':') + 1));\n\n    if (!g_dtb_path.empty()) {\n        if (!load_buf(g_dtb_path.c_str(), &dtb_buf, nullptr)) {\n            die(\"cannot load '%s': %s\", g_dtb_path.c_str(), strerror(errno));\n        }\n\n        if (dtb_buf.type != FB_BUFFER_FD) {\n            die(\"Flashing sparse vendor ramdisk image with dtb is not supported.\");\n        }\n        if (dtb_buf.sz <= 0) {\n            die(\"repack_ramdisk() sees invalid dtb size: %\" PRId64, buf->sz);\n        }\n        verbose(\"Updating DTB with %s\", pname_sv.data());\n    }\n\n    unique_fd vendor_boot(make_temporary_fd(\"vendor boot repack\"));\n    uint64_t vendor_boot_size = fetch_partition(partition, vendor_boot, fb);\n    auto repack_res = replace_vendor_ramdisk(vendor_boot, vendor_boot_size, ramdisk, buf->fd,\n                                             static_cast<uint64_t>(buf->sz), dtb_buf.fd,\n                                             static_cast<uint64_t>(dtb_buf.sz));\n    if (!repack_res.ok()) {\n        die(\"%s\", repack_res.error().message().c_str());\n    }\n\n    buf->fd = std::move(vendor_boot);\n    buf->sz = vendor_boot_size;\n    buf->image_size = vendor_boot_size;\n    return partition;\n}\n\nvoid do_flash(const char* pname, const char* fname, const bool apply_vbmeta,\n              const FlashingPlan* fp) {\n    if (!fp) {\n        die(\"do flash was called without a valid flashing plan\");\n    }\n    verbose(\"Do flash %s %s\", pname, fname);\n    struct fastboot_buffer buf;\n\n    if (fp->source) {\n        unique_fd fd = fp->source->OpenFile(fname);\n        if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp)) {\n            die(\"could not load '%s': %s\", fname, strerror(errno));\n        }\n        std::vector<char> signature_data;\n        std::string file_string(fname);\n        if (fp->source->ReadFile(file_string.substr(0, file_string.find('.')) + \".sig\",\n                                 &signature_data)) {\n            fb->Download(\"signature\", signature_data);\n            fb->RawCommand(\"signature\", \"installing signature\");\n        }\n    } else if (!load_buf(fname, &buf, fp)) {\n        die(\"cannot load '%s': %s\", fname, strerror(errno));\n    }\n\n    if (is_logical(pname)) {\n        fb->ResizePartition(pname, std::to_string(buf.image_size));\n    }\n    std::string flash_pname = repack_ramdisk(pname, &buf, fp->fb);\n    flash_buf(fp->source.get(), flash_pname, &buf, apply_vbmeta);\n}\n\n// Sets slot_override as the active slot. If slot_override is blank,\n// set current slot as active instead. This clears slot-unbootable.\nstatic void set_active(const std::string& slot_override) {\n    if (!supports_AB(fb)) return;\n\n    if (slot_override != \"\") {\n        fb->SetActive(slot_override);\n    } else {\n        std::string current_slot = get_current_slot();\n        if (current_slot != \"\") {\n            fb->SetActive(current_slot);\n        }\n    }\n}\n\nbool is_userspace_fastboot() {\n    std::string value;\n    return fb->GetVar(\"is-userspace\", &value) == fastboot::SUCCESS && value == \"yes\";\n}\n\nvoid reboot_to_userspace_fastboot() {\n    fb->RebootTo(\"fastboot\");\n    if (fb->WaitForDisconnect() != fastboot::SUCCESS) {\n        die(\"Error waiting for USB disconnect.\");\n    }\n    fb->set_transport(nullptr);\n\n    // Not all platforms support WaitForDisconnect. There also isn't a great way to tell whether\n    // or not WaitForDisconnect is supported. So, just wait a bit extra for everyone, in order to\n    // make sure that the device has had time to initiate its reboot and disconnect itself.\n    std::this_thread::sleep_for(std::chrono::seconds(1));\n\n    fb->set_transport(open_device());\n\n    if (!is_userspace_fastboot()) {\n        die(\"Failed to boot into userspace fastboot; one or more components might be unbootable.\");\n    }\n\n    // Reset target_sparse_limit after reboot to userspace fastboot. Max\n    // download sizes may differ in bootloader and fastbootd.\n    target_sparse_limit = -1;\n}\n\nstatic void CancelSnapshotIfNeeded() {\n    std::string merge_status = \"none\";\n    if (fb->GetVar(FB_VAR_SNAPSHOT_UPDATE_STATUS, &merge_status) == fastboot::SUCCESS &&\n        !merge_status.empty() && merge_status != \"none\") {\n        fb->SnapshotUpdateCommand(\"cancel\");\n    }\n}\n\nstd::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot) {\n    auto slot = entry.second;\n    if (slot.empty()) {\n        slot = current_slot;\n    }\n    if (slot.empty()) {\n        return entry.first->part_name;\n    }\n    if (slot == \"all\") {\n        LOG(FATAL) << \"Cannot retrieve a singular name when using all slots\";\n    }\n    return entry.first->part_name + \"_\" + slot;\n}\n\nstd::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,\n                                             const std::vector<std::string>& parts) {\n    bool apply_vbmeta = false;\n    std::string slot = fp->slot_override;\n    std::string partition;\n    std::string img_name;\n    for (auto& part : parts) {\n        if (part == \"--apply-vbmeta\") {\n            apply_vbmeta = true;\n        } else if (part == \"--slot-other\") {\n            slot = fp->secondary_slot;\n        } else if (partition.empty()) {\n            partition = part;\n        } else if (img_name.empty()) {\n            img_name = part;\n        } else {\n            LOG(ERROR) << \"unknown argument\" << part\n                       << \" in fastboot-info.txt. parts: \" << android::base::Join(parts, \" \");\n            return nullptr;\n        }\n    }\n    if (partition.empty()) {\n        LOG(ERROR) << \"partition name not found when parsing fastboot-info.txt. parts: \"\n                   << android::base::Join(parts, \" \");\n        return nullptr;\n    }\n    if (img_name.empty()) {\n        img_name = partition + \".img\";\n    }\n    return std::make_unique<FlashTask>(slot, partition, img_name, apply_vbmeta, fp);\n}\n\nstd::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,\n                                               const std::vector<std::string>& parts) {\n    if (parts.empty()) return std::make_unique<RebootTask>(fp);\n    if (parts.size() > 1) {\n        LOG(ERROR) << \"unknown arguments in reboot {target} in fastboot-info.txt: \"\n                   << android::base::Join(parts, \" \");\n        return nullptr;\n    }\n    return std::make_unique<RebootTask>(fp, parts[0]);\n}\n\nstd::unique_ptr<WipeTask> ParseWipeCommand(const FlashingPlan* fp,\n                                           const std::vector<std::string>& parts) {\n    if (parts.size() != 1) {\n        LOG(ERROR) << \"unknown arguments in erase {partition} in fastboot-info.txt: \"\n                   << android::base::Join(parts, \" \");\n        return nullptr;\n    }\n    return std::make_unique<WipeTask>(fp, parts[0]);\n}\n\nstd::unique_ptr<Task> ParseFastbootInfoLine(const FlashingPlan* fp,\n                                            const std::vector<std::string>& command) {\n    if (command.size() == 0) {\n        return nullptr;\n    }\n    std::unique_ptr<Task> task;\n\n    if (command[0] == \"flash\") {\n        task = ParseFlashCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});\n    } else if (command[0] == \"reboot\") {\n        task = ParseRebootCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});\n    } else if (command[0] == \"update-super\" && command.size() == 1) {\n        task = std::make_unique<UpdateSuperTask>(fp);\n    } else if (command[0] == \"erase\" && command.size() == 2) {\n        task = ParseWipeCommand(fp, std::vector<std::string>{command.begin() + 1, command.end()});\n    }\n    if (!task) {\n        LOG(ERROR) << \"unknown command parsing fastboot-info.txt line: \"\n                   << android::base::Join(command, \" \");\n    }\n    return task;\n}\n\nbool AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>* tasks) {\n    // expands \"resize-partitions\" into individual commands : resize {os_partition_1}, resize\n    // {os_partition_2}, etc.\n    std::vector<std::unique_ptr<Task>> resize_tasks;\n    std::optional<size_t> loc;\n    std::vector<char> contents;\n    if (!fp->source->ReadFile(\"super_empty.img\", &contents)) {\n        return false;\n    }\n    auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());\n    if (!metadata) {\n        return false;\n    }\n    for (size_t i = 0; i < tasks->size(); i++) {\n        if (auto flash_task = tasks->at(i)->AsFlashTask()) {\n            if (FlashTask::IsDynamicPartition(fp->source.get(), flash_task)) {\n                if (!loc) {\n                    loc = i;\n                }\n                resize_tasks.emplace_back(std::make_unique<ResizeTask>(\n                        fp, flash_task->GetPartition(), \"0\", fp->slot_override));\n            }\n        }\n    }\n    // if no logical partitions (although should never happen since system will always need to be\n    // flashed)\n    if (!loc) {\n        return false;\n    }\n    tasks->insert(tasks->begin() + loc.value(), std::make_move_iterator(resize_tasks.begin()),\n                  std::make_move_iterator(resize_tasks.end()));\n    return true;\n}\n\nstatic bool IsIgnore(const std::vector<std::string>& command) {\n    if (command.size() == 0 || command[0][0] == '#') {\n        return true;\n    }\n    return false;\n}\n\nbool CheckFastbootInfoRequirements(const std::vector<std::string>& command,\n                                   uint32_t host_tool_version) {\n    if (command.size() != 2) {\n        LOG(ERROR) << \"unknown characters in version info in fastboot-info.txt -> \"\n                   << android::base::Join(command, \" \");\n        return false;\n    }\n    if (command[0] != \"version\") {\n        LOG(ERROR) << \"unknown characters in version info in fastboot-info.txt -> \"\n                   << android::base::Join(command, \" \");\n        return false;\n    }\n\n    uint32_t fastboot_info_version;\n    if (!android::base::ParseUint(command[1], &fastboot_info_version)) {\n        LOG(ERROR) << \"version number contains non-numeric characters in fastboot-info.txt -> \"\n                   << android::base::Join(command, \" \");\n        return false;\n    }\n\n    LOG(VERBOSE) << \"Checking 'fastboot-info.txt version'\";\n    if (fastboot_info_version <= host_tool_version) {\n        return true;\n    }\n\n    LOG(ERROR) << \"fasboot-info.txt version: \" << command[1]\n               << \" not compatible with host tool version --> \" << host_tool_version;\n    return false;\n}\n\nstd::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,\n                                                     const std::vector<std::string>& file) {\n    std::vector<std::unique_ptr<Task>> tasks;\n    // Get os_partitions that need to be resized\n    for (auto& text : file) {\n        std::vector<std::string> command = android::base::Tokenize(text, \" \");\n        if (IsIgnore(command)) {\n            continue;\n        }\n        if (command.size() > 1 && command[0] == \"version\") {\n            if (!CheckFastbootInfoRequirements(command, FASTBOOT_INFO_VERSION)) {\n                return {};\n            }\n            continue;\n        } else if (command.size() >= 2 && command[0] == \"if-wipe\") {\n            if (!fp->wants_wipe) {\n                continue;\n            }\n            command.erase(command.begin());\n        }\n        auto task = ParseFastbootInfoLine(fp, command);\n        if (!task) {\n            return {};\n        }\n        tasks.emplace_back(std::move(task));\n    }\n\n    if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp, tasks)) {\n        tasks.emplace_back(std::move(flash_super_task));\n    } else {\n        if (!AddResizeTasks(fp, &tasks)) {\n            LOG(WARNING) << \"Failed to add resize tasks\";\n        }\n    }\n\n    return tasks;\n}\n\nFlashAllTool::FlashAllTool(FlashingPlan* fp) : fp_(fp) {}\n\nvoid FlashAllTool::Flash() {\n    DumpInfo();\n    CheckRequirements();\n\n    // Change the slot first, so we boot into the correct recovery image when\n    // using fastbootd.\n    if (fp_->slot_override == \"all\") {\n        set_active(\"a\");\n    } else {\n        set_active(fp_->slot_override);\n    }\n\n    DetermineSlot();\n\n    CancelSnapshotIfNeeded();\n\n    tasks_ = CollectTasks();\n\n    for (auto& task : tasks_) {\n        task->Run();\n    }\n    return;\n}\n\nstd::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasks() {\n    std::vector<std::unique_ptr<Task>> tasks;\n    if (fp_->should_use_fastboot_info) {\n        tasks = CollectTasksFromFastbootInfo();\n\n    } else {\n        tasks = CollectTasksFromImageList();\n    }\n    if (fp_->exclude_dynamic_partitions) {\n        auto is_non_static_flash_task = [&](const auto& task) -> bool {\n            if (auto flash_task = task->AsFlashTask()) {\n                if (!should_flash_in_userspace(fp_->source.get(),\n                                               flash_task->GetPartitionAndSlot())) {\n                    return false;\n                }\n            }\n            return true;\n        };\n        tasks.erase(std::remove_if(tasks.begin(), tasks.end(), is_non_static_flash_task),\n                    tasks.end());\n    }\n    return tasks;\n}\n\nvoid FlashAllTool::CheckRequirements() {\n    std::vector<char> contents;\n    if (!fp_->source->ReadFile(\"android-info.txt\", &contents)) {\n        die(\"could not read android-info.txt\");\n    }\n    ::CheckRequirements({contents.data(), contents.size()}, fp_->force_flash);\n}\n\nvoid FlashAllTool::DetermineSlot() {\n    if (fp_->slot_override.empty()) {\n        fp_->current_slot = get_current_slot();\n    } else {\n        fp_->current_slot = fp_->slot_override;\n    }\n\n    if (fp_->skip_secondary) {\n        return;\n    }\n    if (fp_->slot_override != \"\" && fp_->slot_override != \"all\") {\n        fp_->secondary_slot = get_other_slot(fp_->slot_override);\n    } else {\n        fp_->secondary_slot = get_other_slot();\n    }\n    if (fp_->secondary_slot == \"\") {\n        if (supports_AB(fb)) {\n            fprintf(stderr, \"Warning: Could not determine slot for secondary images. Ignoring.\\n\");\n        }\n        fp_->skip_secondary = true;\n    }\n}\n\nvoid FlashAllTool::CollectImages() {\n    for (size_t i = 0; i < images.size(); ++i) {\n        std::string slot = fp_->slot_override;\n        if (images[i].IsSecondary()) {\n            if (fp_->skip_secondary) {\n                continue;\n            }\n            slot = fp_->secondary_slot;\n        }\n        if (images[i].type == ImageType::BootCritical) {\n            boot_images_.emplace_back(&images[i], slot);\n        } else if (images[i].type == ImageType::Normal) {\n            os_images_.emplace_back(&images[i], slot);\n        }\n    }\n}\n\nstd::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasksFromImageList() {\n    CollectImages();\n    // First flash boot partitions. We allow this to happen either in userspace\n    // or in bootloader fastboot.\n    std::vector<std::unique_ptr<Task>> tasks;\n    AddFlashTasks(boot_images_, tasks);\n\n    // Sync the super partition. This will reboot to userspace fastboot if needed.\n    tasks.emplace_back(std::make_unique<UpdateSuperTask>(fp_));\n\n    AddFlashTasks(os_images_, tasks);\n\n    if (auto flash_super_task = OptimizedFlashSuperTask::Initialize(fp_, tasks)) {\n        tasks.emplace_back(std::move(flash_super_task));\n    } else {\n        // Resize any logical partition to 0, so each partition is reset to 0\n        // extents, and will achieve more optimal allocation.\n        if (!AddResizeTasks(fp_, &tasks)) {\n            LOG(WARNING) << \"Failed to add resize tasks\";\n        }\n    }\n\n    return tasks;\n}\n\nstd::vector<std::unique_ptr<Task>> FlashAllTool::CollectTasksFromFastbootInfo() {\n    std::vector<std::unique_ptr<Task>> tasks;\n    std::vector<char> contents;\n    if (!fp_->source->ReadFile(\"fastboot-info.txt\", &contents)) {\n        LOG(VERBOSE) << \"Flashing from hardcoded images. fastboot-info.txt is empty or does not \"\n                        \"exist\";\n        return CollectTasksFromImageList();\n    }\n    tasks = ParseFastbootInfo(fp_, Split({contents.data(), contents.size()}, \"\\n\"));\n    return tasks;\n}\n\nvoid FlashAllTool::AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,\n                                 std::vector<std::unique_ptr<Task>>& tasks) {\n    for (const auto& [image, slot] : images) {\n        fastboot_buffer buf;\n        unique_fd fd = fp_->source->OpenFile(image->img_name);\n        if (fd < 0 || !load_buf_fd(std::move(fd), &buf, fp_)) {\n            if (image->optional_if_no_image) {\n                continue;\n            }\n            die(\"could not load '%s': %s\", image->img_name.c_str(), strerror(errno));\n        }\n        tasks.emplace_back(std::make_unique<FlashTask>(slot, image->part_name, image->img_name,\n                                                       is_vbmeta_partition(image->part_name), fp_));\n    }\n}\n\nbool ZipImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {\n    return UnzipToMemory(zip_, name, out);\n}\n\nunique_fd ZipImageSource::OpenFile(const std::string& name) const {\n    return UnzipToFile(zip_, name.c_str());\n}\n\nstatic void do_update(const char* filename, FlashingPlan* fp) {\n    ZipArchiveHandle zip;\n    int error = OpenArchive(filename, &zip);\n    if (error != 0) {\n        die(\"failed to open zip file '%s': %s\", filename, ErrorCodeString(error));\n    }\n    fp->source.reset(new ZipImageSource(zip));\n    FlashAllTool tool(fp);\n    tool.Flash();\n\n    CloseArchive(zip);\n}\n\nbool LocalImageSource::ReadFile(const std::string& name, std::vector<char>* out) const {\n    auto path = find_item_given_name(name);\n    if (path.empty()) {\n        return false;\n    }\n    return ReadFileToVector(path, out);\n}\n\nunique_fd LocalImageSource::OpenFile(const std::string& name) const {\n    auto path = find_item_given_name(name);\n    return unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_BINARY)));\n}\n\nstatic void do_flashall(FlashingPlan* fp) {\n    fp->source.reset(new LocalImageSource());\n    FlashAllTool tool(fp);\n    tool.Flash();\n}\n\nstatic std::string next_arg(std::vector<std::string>* args) {\n    if (args->empty()) syntax_error(\"expected argument\");\n    std::string result = args->front();\n    args->erase(args->begin());\n    return result;\n}\n\nstatic void do_oem_command(const std::string& cmd, std::vector<std::string>* args) {\n    if (args->empty()) syntax_error(\"empty oem command\");\n\n    std::string command(cmd);\n    while (!args->empty()) {\n        command += \" \" + next_arg(args);\n    }\n    fb->RawCommand(command, \"\");\n}\n\nstatic unsigned fb_get_flash_block_size(std::string name) {\n    std::string sizeString;\n    if (fb->GetVar(name, &sizeString) != fastboot::SUCCESS || sizeString.empty()) {\n        // This device does not report flash block sizes, so return 0.\n        return 0;\n    }\n    sizeString = fb_fix_numeric_var(sizeString);\n\n    unsigned size;\n    if (!android::base::ParseUint(sizeString, &size)) {\n        fprintf(stderr, \"Couldn't parse %s '%s'.\\n\", name.c_str(), sizeString.c_str());\n        return 0;\n    }\n    if ((size & (size - 1)) != 0) {\n        fprintf(stderr, \"Invalid %s %u: must be a power of 2.\\n\", name.c_str(), size);\n        return 0;\n    }\n    return size;\n}\n\nvoid fb_perform_format(const std::string& partition, int skip_if_not_supported,\n                       const std::string& type_override, const std::string& size_override,\n                       const unsigned fs_options, const FlashingPlan* fp) {\n    std::string partition_type, partition_size;\n\n    struct fastboot_buffer buf;\n    const char* errMsg = nullptr;\n    const struct fs_generator* gen = nullptr;\n    TemporaryFile output;\n    unique_fd fd;\n\n    unsigned int limit = INT_MAX;\n    if (target_sparse_limit > 0 && target_sparse_limit < limit) {\n        limit = target_sparse_limit;\n    }\n    if (fp->sparse_limit > 0 && fp->sparse_limit < limit) {\n        limit = fp->sparse_limit;\n    }\n\n    if (fb->GetVar(\"partition-type:\" + partition, &partition_type) != fastboot::SUCCESS) {\n        errMsg = \"Can't determine partition type.\\n\";\n        goto failed;\n    }\n    if (!type_override.empty()) {\n        if (partition_type != type_override) {\n            fprintf(stderr, \"Warning: %s type is %s, but %s was requested for formatting.\\n\",\n                    partition.c_str(), partition_type.c_str(), type_override.c_str());\n        }\n        partition_type = type_override;\n    }\n\n    if (fb->GetVar(\"partition-size:\" + partition, &partition_size) != fastboot::SUCCESS) {\n        errMsg = \"Unable to get partition size\\n\";\n        goto failed;\n    }\n    if (!size_override.empty()) {\n        if (partition_size != size_override) {\n            fprintf(stderr, \"Warning: %s size is %s, but %s was requested for formatting.\\n\",\n                    partition.c_str(), partition_size.c_str(), size_override.c_str());\n        }\n        partition_size = size_override;\n    }\n    partition_size = fb_fix_numeric_var(partition_size);\n\n    gen = fs_get_generator(partition_type);\n    if (!gen) {\n        if (skip_if_not_supported) {\n            fprintf(stderr, \"Erase successful, but not automatically formatting.\\n\");\n            fprintf(stderr, \"File system type %s not supported.\\n\", partition_type.c_str());\n            return;\n        }\n        die(\"Formatting is not supported for file system with type '%s'.\", partition_type.c_str());\n    }\n\n    int64_t size;\n    if (!android::base::ParseInt(partition_size, &size)) {\n        die(\"Couldn't parse partition size '%s'.\", partition_size.c_str());\n    }\n\n    unsigned eraseBlkSize, logicalBlkSize;\n    eraseBlkSize = fb_get_flash_block_size(\"erase-block-size\");\n    logicalBlkSize = fb_get_flash_block_size(\"logical-block-size\");\n\n    if (fs_generator_generate(gen, output.path, size, eraseBlkSize, logicalBlkSize, fs_options)) {\n        die(\"Cannot generate image for %s\", partition.c_str());\n    }\n\n    fd.reset(open(output.path, O_RDONLY));\n    if (fd == -1) {\n        die(\"Cannot open generated image: %s\", strerror(errno));\n    }\n    if (!load_buf_fd(std::move(fd), &buf, fp)) {\n        die(\"Cannot read image: %s\", strerror(errno));\n    }\n\n    flash_buf(fp->source.get(), partition, &buf, is_vbmeta_partition(partition));\n    return;\n\nfailed:\n    if (skip_if_not_supported) {\n        fprintf(stderr, \"Erase successful, but not automatically formatting.\\n\");\n        if (errMsg) fprintf(stderr, \"%s\", errMsg);\n    }\n    fprintf(stderr, \"FAILED (%s)\\n\", fb->Error().c_str());\n    if (!skip_if_not_supported) {\n        die(\"Command failed\");\n    }\n}\n\nbool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name) {\n    if (!source) {\n        if (!get_android_product_out()) {\n            return false;\n        }\n        auto path = find_item_given_name(\"super_empty.img\");\n        if (path.empty() || access(path.c_str(), R_OK)) {\n            return false;\n        }\n        auto metadata = android::fs_mgr::ReadFromImageFile(path);\n        if (!metadata) {\n            return false;\n        }\n        return should_flash_in_userspace(*metadata.get(), partition_name);\n    }\n    std::vector<char> contents;\n    if (!source->ReadFile(\"super_empty.img\", &contents)) {\n        return false;\n    }\n    auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());\n    return should_flash_in_userspace(*metadata.get(), partition_name);\n}\n\nstatic bool wipe_super(const android::fs_mgr::LpMetadata& metadata, const std::string& slot,\n                       std::string* message, const FlashingPlan* fp) {\n    auto super_device = GetMetadataSuperBlockDevice(metadata);\n    auto block_size = metadata.geometry.logical_block_size;\n    auto super_bdev_name = android::fs_mgr::GetBlockDevicePartitionName(*super_device);\n\n    if (super_bdev_name != \"super\") {\n        // retrofit devices do not allow flashing to the retrofit partitions,\n        // so enable it if we can.\n        fb->RawCommand(\"oem allow-flash-super\");\n    }\n\n    // Note: do not use die() in here, since we want TemporaryDir's destructor\n    // to be called.\n    TemporaryDir temp_dir;\n\n    bool ok;\n    if (metadata.block_devices.size() > 1) {\n        ok = WriteSplitImageFiles(temp_dir.path, metadata, block_size, {}, true);\n    } else {\n        auto image_path = std::string(temp_dir.path) + \"/\" + std::string(super_bdev_name) + \".img\";\n        ok = WriteToImageFile(image_path, metadata, block_size, {}, true);\n    }\n    if (!ok) {\n        *message = \"Could not generate a flashable super image file\";\n        return false;\n    }\n\n    for (const auto& block_device : metadata.block_devices) {\n        auto partition = android::fs_mgr::GetBlockDevicePartitionName(block_device);\n        bool force_slot = !!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED);\n\n        std::string image_name;\n        if (metadata.block_devices.size() > 1) {\n            image_name = \"super_\" + partition + \".img\";\n        } else {\n            image_name = partition + \".img\";\n        }\n\n        auto image_path = std::string(temp_dir.path) + \"/\" + image_name;\n        auto flash = [&](const std::string& partition_name) {\n            do_flash(partition_name.c_str(), image_path.c_str(), false, fp);\n        };\n        do_for_partitions(partition, slot, flash, force_slot);\n\n        unlink(image_path.c_str());\n    }\n    return true;\n}\n\nstatic void do_wipe_super(const std::string& image, const std::string& slot_override,\n                          const FlashingPlan* fp) {\n    if (access(image.c_str(), R_OK) != 0) {\n        die(\"Could not read image: %s\", image.c_str());\n    }\n    auto metadata = android::fs_mgr::ReadFromImageFile(image);\n    if (!metadata) {\n        die(\"Could not parse image: %s\", image.c_str());\n    }\n\n    auto slot = slot_override;\n    if (slot.empty()) {\n        slot = get_current_slot();\n    }\n\n    std::string message;\n    if (!wipe_super(*metadata.get(), slot, &message, fp)) {\n        die(message);\n    }\n}\n\nstatic void FastbootLogger(android::base::LogId /* id */, android::base::LogSeverity severity,\n                           const char* /* tag */, const char* /* file */, unsigned int /* line */,\n                           const char* message) {\n    switch (severity) {\n        case android::base::INFO:\n            fprintf(stdout, \"%s\\n\", message);\n            break;\n        case android::base::ERROR:\n            fprintf(stderr, \"%s\\n\", message);\n            break;\n        default:\n            verbose(\"%s\\n\", message);\n    }\n}\n\nstatic void FastbootAborter(const char* message) {\n    die(\"%s\", message);\n}\n\nint FastBootTool::Main(int argc, char* argv[]) {\n    android::base::InitLogging(argv, FastbootLogger, FastbootAborter);\n    std::unique_ptr<FlashingPlan> fp = std::make_unique<FlashingPlan>();\n\n    int longindex;\n    std::string next_active;\n\n    g_boot_img_hdr.kernel_addr = 0x00008000;\n    g_boot_img_hdr.ramdisk_addr = 0x01000000;\n    g_boot_img_hdr.second_addr = 0x00f00000;\n    g_boot_img_hdr.tags_addr = 0x00000100;\n    g_boot_img_hdr.page_size = 2048;\n    g_boot_img_hdr.dtb_addr = 0x01100000;\n\n    const struct option longopts[] = {{\"base\", required_argument, 0, 0},\n                                      {\"cmdline\", required_argument, 0, 0},\n                                      {\"disable-verification\", no_argument, 0, 0},\n                                      {\"disable-verity\", no_argument, 0, 0},\n                                      {\"disable-super-optimization\", no_argument, 0, 0},\n                                      {\"exclude-dynamic-partitions\", no_argument, 0, 0},\n                                      {\"disable-fastboot-info\", no_argument, 0, 0},\n                                      {\"force\", no_argument, 0, 0},\n                                      {\"fs-options\", required_argument, 0, 0},\n                                      {\"header-version\", required_argument, 0, 0},\n                                      {\"help\", no_argument, 0, 'h'},\n                                      {\"kernel-offset\", required_argument, 0, 0},\n                                      {\"os-patch-level\", required_argument, 0, 0},\n                                      {\"os-version\", required_argument, 0, 0},\n                                      {\"page-size\", required_argument, 0, 0},\n                                      {\"ramdisk-offset\", required_argument, 0, 0},\n                                      {\"set-active\", optional_argument, 0, 'a'},\n                                      {\"skip-reboot\", no_argument, 0, 0},\n                                      {\"skip-secondary\", no_argument, 0, 0},\n                                      {\"slot\", required_argument, 0, 0},\n                                      {\"tags-offset\", required_argument, 0, 0},\n                                      {\"dtb\", required_argument, 0, 0},\n                                      {\"dtb-offset\", required_argument, 0, 0},\n                                      {\"unbuffered\", no_argument, 0, 0},\n                                      {\"verbose\", no_argument, 0, 'v'},\n                                      {\"version\", no_argument, 0, 0},\n                                      {0, 0, 0, 0}};\n\n    serial = getenv(\"FASTBOOT_DEVICE\");\n    if (!serial) {\n        serial = getenv(\"ANDROID_SERIAL\");\n    }\n\n    int c;\n    while ((c = getopt_long(argc, argv, \"a::hls:S:vw\", longopts, &longindex)) != -1) {\n        if (c == 0) {\n            std::string name{longopts[longindex].name};\n            if (name == \"base\") {\n                g_base_addr = strtoul(optarg, 0, 16);\n            } else if (name == \"cmdline\") {\n                g_cmdline = optarg;\n            } else if (name == \"disable-verification\") {\n                g_disable_verification = true;\n            } else if (name == \"disable-verity\") {\n                g_disable_verity = true;\n            } else if (name == \"disable-super-optimization\") {\n                fp->should_optimize_flash_super = false;\n            } else if (name == \"exclude-dynamic-partitions\") {\n                fp->exclude_dynamic_partitions = true;\n                fp->should_optimize_flash_super = false;\n            } else if (name == \"disable-fastboot-info\") {\n                fp->should_use_fastboot_info = false;\n            } else if (name == \"force\") {\n                fp->force_flash = true;\n            } else if (name == \"fs-options\") {\n                fp->fs_options = ParseFsOption(optarg);\n            } else if (name == \"header-version\") {\n                g_boot_img_hdr.header_version = strtoul(optarg, nullptr, 0);\n            } else if (name == \"dtb\") {\n                g_dtb_path = optarg;\n            } else if (name == \"kernel-offset\") {\n                g_boot_img_hdr.kernel_addr = strtoul(optarg, 0, 16);\n            } else if (name == \"os-patch-level\") {\n                ParseOsPatchLevel(&g_boot_img_hdr, optarg);\n            } else if (name == \"os-version\") {\n                ParseOsVersion(&g_boot_img_hdr, optarg);\n            } else if (name == \"page-size\") {\n                g_boot_img_hdr.page_size = strtoul(optarg, nullptr, 0);\n                if (g_boot_img_hdr.page_size == 0) die(\"invalid page size\");\n            } else if (name == \"ramdisk-offset\") {\n                g_boot_img_hdr.ramdisk_addr = strtoul(optarg, 0, 16);\n            } else if (name == \"skip-reboot\") {\n                fp->skip_reboot = true;\n            } else if (name == \"skip-secondary\") {\n                fp->skip_secondary = true;\n            } else if (name == \"slot\") {\n                fp->slot_override = optarg;\n            } else if (name == \"dtb-offset\") {\n                g_boot_img_hdr.dtb_addr = strtoul(optarg, 0, 16);\n            } else if (name == \"tags-offset\") {\n                g_boot_img_hdr.tags_addr = strtoul(optarg, 0, 16);\n            } else if (name == \"unbuffered\") {\n                setvbuf(stdout, nullptr, _IONBF, 0);\n                setvbuf(stderr, nullptr, _IONBF, 0);\n            } else if (name == \"version\") {\n                fprintf(stdout, \"fastboot version %s-%s\\n\", PLATFORM_TOOLS_VERSION,\n                        android::build::GetBuildNumber().c_str());\n                fprintf(stdout, \"Installed as %s\\n\", android::base::GetExecutablePath().c_str());\n                return 0;\n            } else {\n                die(\"unknown option %s\", longopts[longindex].name);\n            }\n        } else {\n            switch (c) {\n                case 'a':\n                    fp->wants_set_active = true;\n                    if (optarg) next_active = optarg;\n                    break;\n                case 'h':\n                    return show_help();\n                case 'l':\n                    g_long_listing = true;\n                    break;\n                case 's':\n                    serial = optarg;\n                    break;\n                case 'S':\n                    if (!android::base::ParseByteCount(optarg, &fp->sparse_limit)) {\n                        die(\"invalid sparse limit %s\", optarg);\n                    }\n                    break;\n                case 'v':\n                    set_verbose();\n                    break;\n                case 'w':\n                    fp->wants_wipe = true;\n                    break;\n                case '?':\n                    return 1;\n                default:\n                    abort();\n            }\n        }\n    }\n\n    argc -= optind;\n    argv += optind;\n\n    if (argc == 0 && !fp->wants_wipe && !fp->wants_set_active) syntax_error(\"no command\");\n\n    if (argc > 0 && !strcmp(*argv, \"devices\")) {\n        list_devices();\n        return 0;\n    }\n\n    if (argc > 0 && !strcmp(*argv, \"connect\")) {\n        argc -= optind;\n        argv += optind;\n        return Connect(argc, argv);\n    }\n\n    if (argc > 0 && !strcmp(*argv, \"disconnect\")) {\n        argc -= optind;\n        argv += optind;\n        return Disconnect(argc, argv);\n    }\n\n    if (argc > 0 && !strcmp(*argv, \"help\")) {\n        return show_help();\n    }\n\n    std::unique_ptr<Transport> transport = open_device();\n    if (!transport) {\n        return 1;\n    }\n    fastboot::DriverCallbacks driver_callbacks = {\n            .prolog = Status,\n            .epilog = Epilog,\n            .info = InfoMessage,\n            .text = TextMessage,\n    };\n\n    fastboot::FastBootDriver fastboot_driver(std::move(transport), driver_callbacks, false);\n    fb = &fastboot_driver;\n    fp->fb = &fastboot_driver;\n\n    const double start = now();\n\n    if (fp->slot_override != \"\") fp->slot_override = verify_slot(fp->slot_override);\n    if (next_active != \"\") next_active = verify_slot(next_active, false);\n\n    if (fp->wants_set_active) {\n        if (next_active == \"\") {\n            if (fp->slot_override == \"\") {\n                std::string current_slot;\n                if (fb->GetVar(\"current-slot\", &current_slot) == fastboot::SUCCESS) {\n                    if (current_slot[0] == '_') current_slot.erase(0, 1);\n                    next_active = verify_slot(current_slot, false);\n                } else {\n                    fp->wants_set_active = false;\n                }\n            } else {\n                next_active = verify_slot(fp->slot_override, false);\n            }\n        }\n    }\n    std::vector<std::unique_ptr<Task>> tasks;\n    std::vector<std::string> args(argv, argv + argc);\n    while (!args.empty()) {\n        std::string command = next_arg(&args);\n\n        if (command == FB_CMD_GETVAR) {\n            std::string variable = next_arg(&args);\n            DisplayVarOrError(variable, variable);\n        } else if (command == FB_CMD_ERASE) {\n            std::string partition = next_arg(&args);\n            auto erase = [&](const std::string& partition) {\n                std::string partition_type;\n                if (fb->GetVar(\"partition-type:\" + partition, &partition_type) ==\n                            fastboot::SUCCESS &&\n                    fs_get_generator(partition_type) != nullptr) {\n                    fprintf(stderr, \"******** Did you mean to fastboot format this %s partition?\\n\",\n                            partition_type.c_str());\n                }\n\n                fb->Erase(partition);\n            };\n            do_for_partitions(partition, fp->slot_override, erase, true);\n        } else if (android::base::StartsWith(command, \"format\")) {\n            // Parsing for: \"format[:[type][:[size]]]\"\n            // Some valid things:\n            //  - select only the size, and leave default fs type:\n            //    format::0x4000000 userdata\n            //  - default fs type and size:\n            //    format userdata\n            //    format:: userdata\n            std::vector<std::string> pieces = android::base::Split(command, \":\");\n            std::string type_override;\n            if (pieces.size() > 1) type_override = pieces[1].c_str();\n            std::string size_override;\n            if (pieces.size() > 2) size_override = pieces[2].c_str();\n\n            std::string partition = next_arg(&args);\n\n            auto format = [&](const std::string& partition) {\n                fb_perform_format(partition, 0, type_override, size_override, fp->fs_options,\n                                  fp.get());\n            };\n            do_for_partitions(partition, fp->slot_override, format, true);\n        } else if (command == \"signature\") {\n            std::string filename = next_arg(&args);\n            std::vector<char> data;\n            if (!ReadFileToVector(filename, &data)) {\n                die(\"could not load '%s': %s\", filename.c_str(), strerror(errno));\n            }\n            if (data.size() != 256) die(\"signature must be 256 bytes (got %zu)\", data.size());\n            fb->Download(\"signature\", data);\n            fb->RawCommand(\"signature\", \"installing signature\");\n        } else if (command == FB_CMD_REBOOT) {\n            if (args.size() == 1) {\n                std::string reboot_target = next_arg(&args);\n                tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), reboot_target));\n            } else if (!fp->skip_reboot) {\n                tasks.emplace_back(std::make_unique<RebootTask>(fp.get()));\n            }\n            if (!args.empty()) syntax_error(\"junk after reboot command\");\n        } else if (command == FB_CMD_REBOOT_BOOTLOADER) {\n            tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), \"bootloader\"));\n        } else if (command == FB_CMD_REBOOT_RECOVERY) {\n            tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), \"recovery\"));\n        } else if (command == FB_CMD_REBOOT_FASTBOOT) {\n            tasks.emplace_back(std::make_unique<RebootTask>(fp.get(), \"fastboot\"));\n        } else if (command == FB_CMD_CONTINUE) {\n            fb->Continue();\n        } else if (command == FB_CMD_BOOT) {\n            std::string kernel = next_arg(&args);\n            std::string ramdisk;\n            if (!args.empty()) ramdisk = next_arg(&args);\n            std::string second_stage;\n            if (!args.empty()) second_stage = next_arg(&args);\n            auto data = LoadBootableImage(kernel, ramdisk, second_stage);\n            fb->Download(\"boot.img\", data);\n            fb->Boot();\n        } else if (command == FB_CMD_FLASH) {\n            std::string pname = next_arg(&args);\n            std::string fname;\n            if (!args.empty()) {\n                fname = next_arg(&args);\n            } else {\n                fname = find_item(pname);\n            }\n            if (fname.empty()) die(\"cannot determine image filename for '%s'\", pname.c_str());\n\n            FlashTask task(fp->slot_override, pname, fname, is_vbmeta_partition(pname), fp.get());\n            task.Run();\n        } else if (command == \"flash:raw\") {\n            std::string partition = next_arg(&args);\n            std::string kernel = next_arg(&args);\n            std::string ramdisk;\n            if (!args.empty()) ramdisk = next_arg(&args);\n            std::string second_stage;\n            if (!args.empty()) second_stage = next_arg(&args);\n\n            auto data = LoadBootableImage(kernel, ramdisk, second_stage);\n            auto flashraw = [&data](const std::string& partition) {\n                fb->FlashPartition(partition, data);\n            };\n            do_for_partitions(partition, fp->slot_override, flashraw, true);\n        } else if (command == \"flashall\") {\n            if (fp->slot_override == \"all\") {\n                fprintf(stderr,\n                        \"Warning: slot set to 'all'. Secondary slots will not be flashed.\\n\");\n                fp->skip_secondary = true;\n            }\n            do_flashall(fp.get());\n\n            if (!fp->skip_reboot) {\n                tasks.emplace_back(std::make_unique<RebootTask>(fp.get()));\n            }\n        } else if (command == \"update\") {\n            bool slot_all = (fp->slot_override == \"all\");\n            if (slot_all) {\n                fprintf(stderr,\n                        \"Warning: slot set to 'all'. Secondary slots will not be flashed.\\n\");\n            }\n            std::string filename = \"update.zip\";\n            if (!args.empty()) {\n                filename = next_arg(&args);\n            }\n            do_update(filename.c_str(), fp.get());\n            if (!fp->skip_reboot) {\n                tasks.emplace_back(std::make_unique<RebootTask>(fp.get()));\n            }\n        } else if (command == FB_CMD_SET_ACTIVE) {\n            std::string slot = verify_slot(next_arg(&args), false);\n            fb->SetActive(slot);\n        } else if (command == \"stage\") {\n            std::string filename = next_arg(&args);\n\n            struct fastboot_buffer buf;\n            if (!load_buf(filename.c_str(), &buf, fp.get()) || buf.type != FB_BUFFER_FD) {\n                die(\"cannot load '%s'\", filename.c_str());\n            }\n            fb->Download(filename, buf.fd.get(), buf.sz);\n        } else if (command == \"get_staged\") {\n            std::string filename = next_arg(&args);\n            fb->Upload(filename);\n        } else if (command == FB_CMD_OEM) {\n            do_oem_command(FB_CMD_OEM, &args);\n        } else if (command == \"flashing\") {\n            if (args.empty()) {\n                syntax_error(\"missing 'flashing' command\");\n            } else if (args.size() == 1 &&\n                       (args[0] == \"unlock\" || args[0] == \"lock\" || args[0] == \"unlock_critical\" ||\n                        args[0] == \"lock_critical\" || args[0] == \"get_unlock_ability\")) {\n                do_oem_command(\"flashing\", &args);\n            } else {\n                syntax_error(\"unknown 'flashing' command %s\", args[0].c_str());\n            }\n        } else if (command == FB_CMD_CREATE_PARTITION) {\n            std::string partition = next_arg(&args);\n            std::string size = next_arg(&args);\n            fb->CreatePartition(partition, size);\n        } else if (command == FB_CMD_DELETE_PARTITION) {\n            std::string partition = next_arg(&args);\n            tasks.emplace_back(std::make_unique<DeleteTask>(fp.get(), partition));\n        } else if (command == FB_CMD_RESIZE_PARTITION) {\n            std::string partition = next_arg(&args);\n            std::string size = next_arg(&args);\n            std::unique_ptr<ResizeTask> resize_task =\n                    std::make_unique<ResizeTask>(fp.get(), partition, size, fp->slot_override);\n            resize_task->Run();\n        } else if (command == \"gsi\") {\n            if (args.empty()) syntax_error(\"invalid gsi command\");\n            std::string cmd(\"gsi\");\n            while (!args.empty()) {\n                cmd += \":\" + next_arg(&args);\n            }\n            fb->RawCommand(cmd, \"\");\n        } else if (command == \"wipe-super\") {\n            std::string image;\n            if (args.empty()) {\n                image = find_item_given_name(\"super_empty.img\");\n            } else {\n                image = next_arg(&args);\n            }\n            do_wipe_super(image, fp->slot_override, fp.get());\n        } else if (command == \"snapshot-update\") {\n            std::string arg;\n            if (!args.empty()) {\n                arg = next_arg(&args);\n            }\n            if (!arg.empty() && (arg != \"cancel\" && arg != \"merge\")) {\n                syntax_error(\"expected: snapshot-update [cancel|merge]\");\n            }\n            fb->SnapshotUpdateCommand(arg);\n        } else if (command == FB_CMD_FETCH) {\n            std::string partition = next_arg(&args);\n            std::string outfile = next_arg(&args);\n            do_fetch(partition, fp->slot_override, outfile, fp->fb);\n        } else {\n            syntax_error(\"unknown command %s\", command.c_str());\n        }\n    }\n\n    if (fp->wants_wipe) {\n        if (fp->force_flash) {\n            CancelSnapshotIfNeeded();\n        }\n        std::vector<std::unique_ptr<Task>> wipe_tasks;\n        std::vector<std::string> partitions = {\"userdata\", \"cache\", \"metadata\"};\n        for (const auto& partition : partitions) {\n            wipe_tasks.emplace_back(std::make_unique<WipeTask>(fp.get(), partition));\n        }\n        tasks.insert(tasks.begin(), std::make_move_iterator(wipe_tasks.begin()),\n                     std::make_move_iterator(wipe_tasks.end()));\n    }\n    if (fp->wants_set_active) {\n        fb->SetActive(next_active);\n    }\n    for (auto& task : tasks) {\n        task->Run();\n    }\n    fprintf(stderr, \"Finished. Total time: %.3fs\\n\", (now() - start));\n\n    return 0;\n}\n\nvoid FastBootTool::ParseOsPatchLevel(boot_img_hdr_v1* hdr, const char* arg) {\n    unsigned year, month, day;\n    if (sscanf(arg, \"%u-%u-%u\", &year, &month, &day) != 3) {\n        syntax_error(\"OS patch level should be YYYY-MM-DD: %s\", arg);\n    }\n    if (year < 2000 || year >= 2128) syntax_error(\"year out of range: %d\", year);\n    if (month < 1 || month > 12) syntax_error(\"month out of range: %d\", month);\n    hdr->SetOsPatchLevel(year, month);\n}\n\nvoid FastBootTool::ParseOsVersion(boot_img_hdr_v1* hdr, const char* arg) {\n    unsigned major = 0, minor = 0, patch = 0;\n    std::vector<std::string> versions = android::base::Split(arg, \".\");\n    if (versions.size() < 1 || versions.size() > 3 ||\n        (versions.size() >= 1 && !android::base::ParseUint(versions[0], &major)) ||\n        (versions.size() >= 2 && !android::base::ParseUint(versions[1], &minor)) ||\n        (versions.size() == 3 && !android::base::ParseUint(versions[2], &patch)) ||\n        (major > 0x7f || minor > 0x7f || patch > 0x7f)) {\n        syntax_error(\"bad OS version: %s\", arg);\n    }\n    hdr->SetOsVersion(major, minor, patch);\n}\n\nunsigned FastBootTool::ParseFsOption(const char* arg) {\n    unsigned fsOptions = 0;\n\n    std::vector<std::string> options = android::base::Split(arg, \",\");\n    if (options.size() < 1) syntax_error(\"bad options: %s\", arg);\n\n    for (size_t i = 0; i < options.size(); ++i) {\n        if (options[i] == \"casefold\")\n            fsOptions |= (1 << FS_OPT_CASEFOLD);\n        else if (options[i] == \"projid\")\n            fsOptions |= (1 << FS_OPT_PROJID);\n        else if (options[i] == \"compress\")\n            fsOptions |= (1 << FS_OPT_COMPRESS);\n        else\n            syntax_error(\"unsupported options: %s\", options[i].c_str());\n    }\n    return fsOptions;\n}\n"
  },
  {
    "path": "fastboot/fastboot.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#pragma once\n\n#include <functional>\n#include <memory>\n#include <string>\n#include \"fastboot_driver_interface.h\"\n#include \"filesystem.h\"\n#include \"task.h\"\n#include \"util.h\"\n\n#include <bootimg.h>\n\n#include \"result.h\"\n#include \"socket.h\"\n#include \"util.h\"\n#include \"ziparchive/zip_archive.h\"\n\nclass FastBootTool {\n  public:\n    int Main(int argc, char* argv[]);\n\n    void ParseOsPatchLevel(boot_img_hdr_v1*, const char*);\n    void ParseOsVersion(boot_img_hdr_v1*, const char*);\n    unsigned ParseFsOption(const char*);\n};\n\nenum fb_buffer_type {\n    FB_BUFFER_FD,\n    FB_BUFFER_SPARSE,\n};\n\nstruct fastboot_buffer {\n    fb_buffer_type type;\n    fb_buffer_type file_type;\n    std::vector<SparsePtr> files;\n    int64_t sz;\n    unique_fd fd;\n    int64_t image_size;\n};\n\nenum class ImageType {\n    // Must be flashed for device to boot into the kernel.\n    BootCritical,\n    // Normal partition to be flashed during \"flashall\".\n    Normal,\n    // Partition that is never flashed during \"flashall\".\n    Extra\n};\n\nstruct Image {\n    std::string nickname;\n    std::string img_name;\n    std::string sig_name;\n    std::string part_name;\n    bool optional_if_no_image;\n    ImageType type;\n    bool IsSecondary() const { return nickname.empty(); }\n};\n\nusing ImageEntry = std::pair<const Image*, std::string>;\n\nstruct FlashingPlan {\n    unsigned fs_options = 0;\n    // If the image uses the default slot, or the user specified \"all\", then\n    // the paired string will be empty. If the image requests a specific slot\n    // (for example, system_other) it is specified instead.\n    std::unique_ptr<ImageSource> source;\n    bool wants_wipe = false;\n    bool skip_reboot = false;\n    bool wants_set_active = false;\n    bool skip_secondary = false;\n    bool force_flash = false;\n    bool should_optimize_flash_super = true;\n    bool should_use_fastboot_info = true;\n    bool exclude_dynamic_partitions = false;\n    uint64_t sparse_limit = 0;\n\n    std::string slot_override;\n    std::string current_slot;\n    std::string secondary_slot;\n\n    fastboot::IFastBootDriver* fb;\n};\n\nclass FlashAllTool {\n  public:\n    FlashAllTool(FlashingPlan* fp);\n\n    void Flash();\n    std::vector<std::unique_ptr<Task>> CollectTasks();\n\n  private:\n    void CheckRequirements();\n    void DetermineSlot();\n    void CollectImages();\n    void AddFlashTasks(const std::vector<std::pair<const Image*, std::string>>& images,\n                       std::vector<std::unique_ptr<Task>>& tasks);\n\n    std::vector<std::unique_ptr<Task>> CollectTasksFromFastbootInfo();\n    std::vector<std::unique_ptr<Task>> CollectTasksFromImageList();\n\n    std::vector<ImageEntry> boot_images_;\n    std::vector<ImageEntry> os_images_;\n    std::vector<std::unique_ptr<Task>> tasks_;\n\n    FlashingPlan* fp_;\n};\n\nclass ZipImageSource final : public ImageSource {\n  public:\n    explicit ZipImageSource(ZipArchiveHandle zip) : zip_(zip) {}\n    bool ReadFile(const std::string& name, std::vector<char>* out) const override;\n    unique_fd OpenFile(const std::string& name) const override;\n\n  private:\n    ZipArchiveHandle zip_;\n};\n\nclass LocalImageSource final : public ImageSource {\n  public:\n    bool ReadFile(const std::string& name, std::vector<char>* out) const override;\n    unique_fd OpenFile(const std::string& name) const override;\n};\n\nchar* get_android_product_out();\nbool should_flash_in_userspace(const ImageSource* source, const std::string& partition_name);\nbool is_userspace_fastboot();\nvoid do_flash(const char* pname, const char* fname, const bool apply_vbmeta,\n              const FlashingPlan* fp);\nvoid do_for_partitions(const std::string& part, const std::string& slot,\n                       const std::function<void(const std::string&)>& func, bool force_slot);\nstd::string find_item(const std::string& item);\nvoid reboot_to_userspace_fastboot();\nvoid syntax_error(const char* fmt, ...);\nstd::string get_current_slot();\n\n// Code for Parsing fastboot-info.txt\nbool CheckFastbootInfoRequirements(const std::vector<std::string>& command,\n                                   uint32_t host_tool_version);\nstd::unique_ptr<FlashTask> ParseFlashCommand(const FlashingPlan* fp,\n                                             const std::vector<std::string>& parts);\nstd::unique_ptr<RebootTask> ParseRebootCommand(const FlashingPlan* fp,\n                                               const std::vector<std::string>& parts);\nstd::unique_ptr<WipeTask> ParseWipeCommand(const FlashingPlan* fp,\n                                           const std::vector<std::string>& parts);\nstd::unique_ptr<Task> ParseFastbootInfoLine(const FlashingPlan* fp,\n                                            const std::vector<std::string>& command);\nbool AddResizeTasks(const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);\nstd::vector<std::unique_ptr<Task>> ParseFastbootInfo(const FlashingPlan* fp,\n                                                     const std::vector<std::string>& file);\n\nstruct NetworkSerial {\n    Socket::Protocol protocol;\n    std::string address;\n    int port;\n};\n\nResult<NetworkSerial, FastbootError> ParseNetworkSerial(const std::string& serial);\nstd::string GetPartitionName(const ImageEntry& entry, const std::string& current_slot_);\nvoid flash_partition_files(const std::string& partition, const std::vector<SparsePtr>& files);\nint64_t get_sparse_limit(int64_t size, const FlashingPlan* fp);\nstd::vector<SparsePtr> resparse_file(sparse_file* s, int64_t max_size);\n\nbool supports_AB(fastboot::IFastBootDriver* fb);\nbool is_logical(const std::string& partition);\nvoid fb_perform_format(const std::string& partition, int skip_if_not_supported,\n                       const std::string& type_override, const std::string& size_override,\n                       const unsigned fs_options, const FlashingPlan* fp);\n"
  },
  {
    "path": "fastboot/fastboot_driver.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"fastboot_driver.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <algorithm>\n#include <chrono>\n#include <fstream>\n#include <memory>\n#include <regex>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/mapped_file.h>\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"constants.h\"\n#include \"transport.h\"\n\nusing android::base::StringPrintf;\nusing namespace android::storage_literals;\n\nnamespace fastboot {\n\n/*************************** PUBLIC *******************************/\nFastBootDriver::FastBootDriver(std::unique_ptr<Transport> transport,\n                               DriverCallbacks driver_callbacks,\n                               bool no_checks)\n    : transport_(std::move(transport)),\n      prolog_(std::move(driver_callbacks.prolog)),\n      epilog_(std::move(driver_callbacks.epilog)),\n      info_(std::move(driver_callbacks.info)),\n      text_(std::move(driver_callbacks.text)),\n      disable_checks_(no_checks) {}\n\nFastBootDriver::~FastBootDriver() {\n}\n\nRetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {\n    return RawCommand(FB_CMD_BOOT, \"Booting\", response, info);\n}\n\nRetCode FastBootDriver::Continue(std::string* response, std::vector<std::string>* info) {\n    return RawCommand(FB_CMD_CONTINUE, \"Resuming boot\", response, info);\n}\n\nRetCode FastBootDriver::CreatePartition(const std::string& partition, const std::string& size) {\n    return RawCommand(FB_CMD_CREATE_PARTITION \":\" + partition + \":\" + size,\n                      \"Creating '\" + partition + \"'\");\n}\n\nRetCode FastBootDriver::DeletePartition(const std::string& partition) {\n    return RawCommand(FB_CMD_DELETE_PARTITION \":\" + partition, \"Deleting '\" + partition + \"'\");\n}\n\nRetCode FastBootDriver::Erase(const std::string& partition, std::string* response,\n                              std::vector<std::string>* info) {\n    return RawCommand(FB_CMD_ERASE \":\" + partition, \"Erasing '\" + partition + \"'\", response, info);\n}\n\nRetCode FastBootDriver::Flash(const std::string& partition, std::string* response,\n                              std::vector<std::string>* info) {\n    return RawCommand(FB_CMD_FLASH \":\" + partition, \"Writing '\" + partition + \"'\", response, info);\n}\n\nRetCode FastBootDriver::GetVar(const std::string& key, std::string* val,\n                               std::vector<std::string>* info) {\n    return RawCommand(FB_CMD_GETVAR \":\" + key, val, info);\n}\n\nRetCode FastBootDriver::GetVarAll(std::vector<std::string>* response) {\n    std::string tmp;\n    return GetVar(\"all\", &tmp, response);\n}\n\nRetCode FastBootDriver::Reboot(std::string* response, std::vector<std::string>* info) {\n    return RawCommand(FB_CMD_REBOOT, \"Rebooting\", response, info);\n}\n\nRetCode FastBootDriver::RebootTo(std::string target, std::string* response,\n                                 std::vector<std::string>* info) {\n    return RawCommand(\"reboot-\" + target, \"Rebooting into \" + target, response, info);\n}\n\nRetCode FastBootDriver::ResizePartition(const std::string& partition, const std::string& size) {\n    return RawCommand(FB_CMD_RESIZE_PARTITION \":\" + partition + \":\" + size,\n                      \"Resizing '\" + partition + \"'\");\n}\n\nRetCode FastBootDriver::SetActive(const std::string& slot, std::string* response,\n                                  std::vector<std::string>* info) {\n    return RawCommand(FB_CMD_SET_ACTIVE \":\" + slot, \"Setting current slot to '\" + slot + \"'\",\n                      response, info);\n}\n\nRetCode FastBootDriver::SnapshotUpdateCommand(const std::string& command, std::string* response,\n                                              std::vector<std::string>* info) {\n    prolog_(StringPrintf(\"Snapshot %s\", command.c_str()));\n    std::string raw = FB_CMD_SNAPSHOT_UPDATE \":\" + command;\n    auto result = RawCommand(raw, response, info);\n    epilog_(result);\n    return result;\n}\n\nRetCode FastBootDriver::FlashPartition(const std::string& partition,\n                                       const std::vector<char>& data) {\n    RetCode ret;\n    if ((ret = Download(partition, data))) {\n        return ret;\n    }\n    return Flash(partition);\n}\n\nRetCode FastBootDriver::FlashPartition(const std::string& partition, android::base::borrowed_fd fd,\n                                       uint32_t size) {\n    RetCode ret;\n    if ((ret = Download(partition, fd, size))) {\n        return ret;\n    }\n    return Flash(partition);\n}\n\nRetCode FastBootDriver::FlashPartition(const std::string& partition, sparse_file* s, uint32_t size,\n                                       size_t current, size_t total) {\n    RetCode ret;\n    if ((ret = Download(partition, s, size, current, total, false))) {\n        return ret;\n    }\n    return Flash(partition);\n}\n\nRetCode FastBootDriver::Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions) {\n    std::vector<std::string> all;\n    RetCode ret;\n    if ((ret = GetVarAll(&all))) {\n        return ret;\n    }\n\n    std::regex reg(\"partition-size[[:s:]]*:[[:s:]]*([[:w:]]+)[[:s:]]*:[[:s:]]*0x([[:xdigit:]]+)\");\n    std::smatch sm;\n\n    for (auto& s : all) {\n        if (std::regex_match(s, sm, reg)) {\n            std::string m1(sm[1]);\n            std::string m2(sm[2]);\n            uint64_t tmp = strtoll(m2.c_str(), 0, 16);\n            partitions->push_back(std::make_tuple(m1, tmp));\n        }\n    }\n    return SUCCESS;\n}\n\nRetCode FastBootDriver::Download(const std::string& name, android::base::borrowed_fd fd,\n                                 size_t size, std::string* response,\n                                 std::vector<std::string>* info) {\n    prolog_(StringPrintf(\"Sending '%s' (%zu KB)\", name.c_str(), size / 1024));\n    auto result = Download(fd, size, response, info);\n    epilog_(result);\n    return result;\n}\n\nRetCode FastBootDriver::Download(android::base::borrowed_fd fd, size_t size, std::string* response,\n                                 std::vector<std::string>* info) {\n    RetCode ret;\n\n    if ((size <= 0 || size > MAX_DOWNLOAD_SIZE) && !disable_checks_) {\n        error_ = \"File is too large to download\";\n        return BAD_ARG;\n    }\n\n    uint32_t u32size = static_cast<uint32_t>(size);\n    if ((ret = DownloadCommand(u32size, response, info))) {\n        return ret;\n    }\n\n    // Write the buffer\n    if ((ret = SendBuffer(fd, size))) {\n        return ret;\n    }\n\n    // Wait for response\n    return HandleResponse(response, info);\n}\n\nRetCode FastBootDriver::Download(const std::string& name, const std::vector<char>& buf,\n                                 std::string* response, std::vector<std::string>* info) {\n    prolog_(StringPrintf(\"Sending '%s' (%zu KB)\", name.c_str(), buf.size() / 1024));\n    auto result = Download(buf, response, info);\n    epilog_(result);\n    return result;\n}\n\nRetCode FastBootDriver::Download(const std::vector<char>& buf, std::string* response,\n                                 std::vector<std::string>* info) {\n    RetCode ret;\n    error_ = \"\";\n    if ((buf.size() == 0 || buf.size() > MAX_DOWNLOAD_SIZE) && !disable_checks_) {\n        error_ = \"Buffer is too large or 0 bytes\";\n        return BAD_ARG;\n    }\n\n    if ((ret = DownloadCommand(buf.size(), response, info))) {\n        return ret;\n    }\n\n    // Write the buffer\n    if ((ret = SendBuffer(buf))) {\n        return ret;\n    }\n\n    // Wait for response\n    return HandleResponse(response, info);\n}\n\nRetCode FastBootDriver::Download(const std::string& partition, struct sparse_file* s, uint32_t size,\n                                 size_t current, size_t total, bool use_crc, std::string* response,\n                                 std::vector<std::string>* info) {\n    prolog_(StringPrintf(\"Sending sparse '%s' %zu/%zu (%u KB)\", partition.c_str(), current, total,\n                         size / 1024));\n    auto result = Download(s, use_crc, response, info);\n    epilog_(result);\n    return result;\n}\n\nRetCode FastBootDriver::Download(sparse_file* s, bool use_crc, std::string* response,\n                                 std::vector<std::string>* info) {\n    error_ = \"\";\n    int64_t size = sparse_file_len(s, true, use_crc);\n    if (size <= 0 || size > MAX_DOWNLOAD_SIZE) {\n        error_ = \"Sparse file is too large or invalid\";\n        return BAD_ARG;\n    }\n\n    RetCode ret;\n    uint32_t u32size = static_cast<uint32_t>(size);\n    if ((ret = DownloadCommand(u32size, response, info))) {\n        return ret;\n    }\n\n    struct SparseCBPrivate {\n        FastBootDriver* self;\n        std::vector<char> tpbuf;\n    } cb_priv;\n    cb_priv.self = this;\n\n    auto cb = [](void* priv, const void* buf, size_t len) -> int {\n        SparseCBPrivate* data = static_cast<SparseCBPrivate*>(priv);\n        const char* cbuf = static_cast<const char*>(buf);\n        return data->self->SparseWriteCallback(data->tpbuf, cbuf, len);\n    };\n\n    if (sparse_file_callback(s, true, use_crc, cb, &cb_priv) < 0) {\n        error_ = \"Error reading sparse file\";\n        return IO_ERROR;\n    }\n\n    // Now flush\n    if (cb_priv.tpbuf.size() && (ret = SendBuffer(cb_priv.tpbuf))) {\n        return ret;\n    }\n\n    return HandleResponse(response, info);\n}\n\nRetCode FastBootDriver::Upload(const std::string& outfile, std::string* response,\n                               std::vector<std::string>* info) {\n    prolog_(\"Uploading '\" + outfile + \"'\");\n    auto result = UploadInner(outfile, response, info);\n    epilog_(result);\n    return result;\n}\n\n// This function executes cmd, then expect a \"DATA\" response with a number N, followed\n// by N bytes, and another response.\n// This is the common way for the device to send data to the driver used by upload and fetch.\nRetCode FastBootDriver::RunAndReadBuffer(\n        const std::string& cmd, std::string* response, std::vector<std::string>* info,\n        const std::function<RetCode(const char* data, uint64_t size)>& write_fn) {\n    RetCode ret;\n    int dsize = 0;\n    if ((ret = RawCommand(cmd, response, info, &dsize))) {\n        error_ = android::base::StringPrintf(\"%s request failed: %s\", cmd.c_str(), error_.c_str());\n        return ret;\n    }\n\n    if (dsize <= 0) {\n        error_ = android::base::StringPrintf(\"%s request failed, device reports %d bytes available\",\n                                             cmd.c_str(), dsize);\n        return BAD_DEV_RESP;\n    }\n\n    const uint64_t total_size = dsize;\n    const uint64_t buf_size = std::min<uint64_t>(total_size, 1_MiB);\n    std::vector<char> data(buf_size);\n    uint64_t current_offset = 0;\n    while (current_offset < total_size) {\n        uint64_t remaining = total_size - current_offset;\n        uint64_t chunk_size = std::min(buf_size, remaining);\n        if ((ret = ReadBuffer(data.data(), chunk_size)) != SUCCESS) {\n            return ret;\n        }\n        if ((ret = write_fn(data.data(), chunk_size)) != SUCCESS) {\n            return ret;\n        }\n        current_offset += chunk_size;\n    }\n    return HandleResponse(response, info);\n}\n\nRetCode FastBootDriver::UploadInner(const std::string& outfile, std::string* response,\n                                    std::vector<std::string>* info) {\n    std::ofstream ofs;\n    ofs.open(outfile, std::ofstream::out | std::ofstream::binary);\n    if (ofs.fail()) {\n        error_ = android::base::StringPrintf(\"Failed to open '%s'\", outfile.c_str());\n        return IO_ERROR;\n    }\n    auto write_fn = [&](const char* data, uint64_t size) {\n        ofs.write(data, size);\n        if (ofs.fail() || ofs.bad()) {\n            error_ = android::base::StringPrintf(\"Writing to '%s' failed\", outfile.c_str());\n            return IO_ERROR;\n        }\n        return SUCCESS;\n    };\n    RetCode ret = RunAndReadBuffer(FB_CMD_UPLOAD, response, info, write_fn);\n    ofs.close();\n    return ret;\n}\n\nRetCode FastBootDriver::FetchToFd(const std::string& partition, android::base::borrowed_fd fd,\n                                  int64_t offset, int64_t size, std::string* response,\n                                  std::vector<std::string>* info) {\n    prolog_(android::base::StringPrintf(\"Fetching %s (offset=%\" PRIx64 \", size=%\" PRIx64 \")\",\n                                        partition.c_str(), offset, size));\n    std::string cmd = FB_CMD_FETCH \":\" + partition;\n    if (offset >= 0) {\n        cmd += android::base::StringPrintf(\":0x%08\" PRIx64, offset);\n        if (size >= 0) {\n            cmd += android::base::StringPrintf(\":0x%08\" PRIx64, size);\n        }\n    }\n    RetCode ret = RunAndReadBuffer(cmd, response, info, [&](const char* data, uint64_t size) {\n        if (!android::base::WriteFully(fd, data, size)) {\n            error_ = android::base::StringPrintf(\"Cannot write: %s\", strerror(errno));\n            return IO_ERROR;\n        }\n        return SUCCESS;\n    });\n    epilog_(ret);\n    return ret;\n}\n\n// Helpers\nvoid FastBootDriver::SetInfoCallback(std::function<void(const std::string&)> info) {\n    info_ = info;\n}\n\nconst std::string FastBootDriver::RCString(RetCode rc) {\n    switch (rc) {\n        case SUCCESS:\n            return std::string(\"Success\");\n\n        case BAD_ARG:\n            return std::string(\"Invalid Argument\");\n\n        case IO_ERROR:\n            return std::string(\"I/O Error\");\n\n        case BAD_DEV_RESP:\n            return std::string(\"Invalid Device Response\");\n\n        case DEVICE_FAIL:\n            return std::string(\"Device Error\");\n\n        case TIMEOUT:\n            return std::string(\"Timeout\");\n\n        default:\n            return std::string(\"Unknown Error\");\n    }\n}\n\nstd::string FastBootDriver::Error() {\n    return error_;\n}\n\nRetCode FastBootDriver::WaitForDisconnect() {\n    return transport_->WaitForDisconnect() ? IO_ERROR : SUCCESS;\n}\n\n/****************************** PROTECTED *************************************/\nRetCode FastBootDriver::RawCommand(const std::string& cmd, const std::string& message,\n                                   std::string* response, std::vector<std::string>* info,\n                                   int* dsize) {\n    prolog_(message);\n    auto result = RawCommand(cmd, response, info, dsize);\n    epilog_(result);\n    return result;\n}\n\nRetCode FastBootDriver::RawCommand(const std::string& cmd, std::string* response,\n                                   std::vector<std::string>* info, int* dsize) {\n    error_ = \"\";  // Clear any pending error\n    if (cmd.size() > FB_COMMAND_SZ && !disable_checks_) {\n        error_ = \"Command length to RawCommand() is too long\";\n        return BAD_ARG;\n    }\n\n    if (transport_->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {\n        error_ = ErrnoStr(\"Write to device failed\");\n        return IO_ERROR;\n    }\n\n    // Read the response\n    return HandleResponse(response, info, dsize);\n}\n\nRetCode FastBootDriver::DownloadCommand(uint32_t size, std::string* response,\n                                        std::vector<std::string>* info) {\n    std::string cmd(android::base::StringPrintf(\"%s:%08\" PRIx32, FB_CMD_DOWNLOAD, size));\n    RetCode ret;\n    if ((ret = RawCommand(cmd, response, info))) {\n        return ret;\n    }\n    return SUCCESS;\n}\n\nRetCode FastBootDriver::HandleResponse(std::string* response, std::vector<std::string>* info,\n                                       int* dsize) {\n    char status[FB_RESPONSE_SZ + 1];\n    auto start = std::chrono::steady_clock::now();\n\n    auto set_response = [response](std::string s) {\n        if (response) *response = std::move(s);\n    };\n    auto add_info = [info](std::string s) {\n        if (info) info->push_back(std::move(s));\n    };\n\n    // erase response\n    set_response(\"\");\n    while ((std::chrono::steady_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {\n        int r = transport_->Read(status, FB_RESPONSE_SZ);\n        if (r < 0) {\n            error_ = ErrnoStr(\"Status read failed\");\n            return IO_ERROR;\n        }\n\n        status[r] = '\\0';  // Need the null terminator\n        std::string input(status);\n        if (android::base::StartsWith(input, \"INFO\")) {\n            std::string tmp = input.substr(strlen(\"INFO\"));\n            info_(tmp);\n            add_info(std::move(tmp));\n            // We may receive one or more INFO packets during long operations,\n            // e.g. flash/erase if they are back by slow media like NAND/NOR\n            // flash. In that case, reset the timer since it's not a real\n            // timeout.\n            start = std::chrono::steady_clock::now();\n        } else if (android::base::StartsWith(input, \"OKAY\")) {\n            set_response(input.substr(strlen(\"OKAY\")));\n            return SUCCESS;\n        } else if (android::base::StartsWith(input, \"FAIL\")) {\n            error_ = android::base::StringPrintf(\"remote: '%s'\", status + strlen(\"FAIL\"));\n            set_response(input.substr(strlen(\"FAIL\")));\n            return DEVICE_FAIL;\n        } else if (android::base::StartsWith(input, \"TEXT\")) {\n            text_(input.substr(strlen(\"TEXT\")));\n            // Reset timeout as many more TEXT may come\n            start = std::chrono::steady_clock::now();\n        } else if (android::base::StartsWith(input, \"DATA\")) {\n            std::string tmp = input.substr(strlen(\"DATA\"));\n            uint32_t num = strtol(tmp.c_str(), 0, 16);\n            if (num > MAX_DOWNLOAD_SIZE) {\n                error_ = android::base::StringPrintf(\"Data size too large (%d)\", num);\n                return BAD_DEV_RESP;\n            }\n            if (dsize) *dsize = num;\n            set_response(std::move(tmp));\n            return SUCCESS;\n        } else {\n            error_ = android::base::StringPrintf(\"Device sent unknown status code: %s\", status);\n            return BAD_DEV_RESP;\n        }\n\n    }  // End of while loop\n\n    return TIMEOUT;\n}\n\nstd::string FastBootDriver::ErrnoStr(const std::string& msg) {\n    return android::base::StringPrintf(\"%s (%s)\", msg.c_str(), strerror(errno));\n}\n\n/******************************* PRIVATE **************************************/\nRetCode FastBootDriver::SendBuffer(android::base::borrowed_fd fd, size_t size) {\n    static constexpr uint32_t MAX_MAP_SIZE = 512 * 1024 * 1024;\n    off64_t offset = 0;\n    uint32_t remaining = size;\n    RetCode ret;\n\n    while (remaining) {\n        // Memory map the file\n        size_t len = std::min(remaining, MAX_MAP_SIZE);\n        auto mapping{android::base::MappedFile::FromFd(fd, offset, len, PROT_READ)};\n        if (!mapping) {\n            error_ = \"Creating filemap failed\";\n            return IO_ERROR;\n        }\n\n        if ((ret = SendBuffer(mapping->data(), mapping->size()))) {\n            return ret;\n        }\n\n        remaining -= len;\n        offset += len;\n    }\n\n    return SUCCESS;\n}\n\nRetCode FastBootDriver::SendBuffer(const std::vector<char>& buf) {\n    // Write the buffer\n    return SendBuffer(buf.data(), buf.size());\n}\n\nRetCode FastBootDriver::SendBuffer(const void* buf, size_t size) {\n    // ioctl on 0-length buffer causes freezing\n    if (!size) {\n        return BAD_ARG;\n    }\n    // Write the buffer\n    ssize_t tmp = transport_->Write(buf, size);\n\n    if (tmp < 0) {\n        error_ = ErrnoStr(\"Write to device failed in SendBuffer()\");\n        return IO_ERROR;\n    } else if (static_cast<size_t>(tmp) != size) {\n        error_ = android::base::StringPrintf(\"Failed to write all %zu bytes\", size);\n\n        return IO_ERROR;\n    }\n\n    return SUCCESS;\n}\n\nRetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {\n    // Read the buffer\n    ssize_t tmp = transport_->Read(buf, size);\n\n    if (tmp < 0) {\n        error_ = ErrnoStr(\"Read from device failed in ReadBuffer()\");\n        return IO_ERROR;\n    } else if (static_cast<size_t>(tmp) != size) {\n        error_ = android::base::StringPrintf(\"Failed to read all %zu bytes\", size);\n        return IO_ERROR;\n    }\n\n    return SUCCESS;\n}\n\nint FastBootDriver::SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len) {\n    size_t total = 0;\n    size_t to_write = std::min(TRANSPORT_CHUNK_SIZE - tpbuf.size(), len);\n\n    // Handle the residual\n    tpbuf.insert(tpbuf.end(), data, data + to_write);\n    if (tpbuf.size() < TRANSPORT_CHUNK_SIZE) {  // Nothing enough to send rn\n        return 0;\n    }\n\n    if (SendBuffer(tpbuf)) {\n        error_ = ErrnoStr(\"Send failed in SparseWriteCallback()\");\n        return -1;\n    }\n    tpbuf.clear();\n    total += to_write;\n\n    // Now we need to send a multiple of chunk size\n    size_t nchunks = (len - total) / TRANSPORT_CHUNK_SIZE;\n    size_t nbytes = TRANSPORT_CHUNK_SIZE * nchunks;\n    if (nbytes && SendBuffer(data + total, nbytes)) {  // Don't send a ZLP\n        error_ = ErrnoStr(\"Send failed in SparseWriteCallback()\");\n        return -1;\n    }\n    total += nbytes;\n\n    if (len - total > 0) {  // We have residual data to save for next time\n        tpbuf.assign(data + total, data + len);\n    }\n\n    return 0;\n}\n\nvoid FastBootDriver::set_transport(std::unique_ptr<Transport> transport) {\n    transport_ = std::move(transport);\n}\n\n}  // End namespace fastboot\n"
  },
  {
    "path": "fastboot/fastboot_driver.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#pragma once\n#include <cstdlib>\n#include <functional>\n#include <limits>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/endian.h>\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n#include <bootimg.h>\n#include <sparse/sparse.h>\n\n#include \"fastboot_driver_interface.h\"\n#include \"transport.h\"\n\nclass Transport;\n\nnamespace fastboot {\n\nstruct DriverCallbacks {\n    std::function<void(const std::string&)> prolog = [](const std::string&) {};\n    std::function<void(int)> epilog = [](int) {};\n    std::function<void(const std::string&)> info = [](const std::string&) {};\n    std::function<void(const std::string&)> text = [](const std::string&) {};\n};\n\nclass FastBootDriver : public IFastBootDriver {\n    friend class FastBootTest;\n\n  public:\n    static constexpr int RESP_TIMEOUT = 30;  // 30 seconds\n    static constexpr uint32_t MAX_DOWNLOAD_SIZE = std::numeric_limits<uint32_t>::max();\n    static constexpr size_t TRANSPORT_CHUNK_SIZE = 1024;\n\n    FastBootDriver(std::unique_ptr<Transport> transport, DriverCallbacks driver_callbacks = {},\n                   bool no_checks = false);\n    ~FastBootDriver();\n\n    RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);\n    RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);\n    RetCode CreatePartition(const std::string& partition, const std::string& size);\n    RetCode DeletePartition(const std::string& partition) override;\n    RetCode Download(const std::string& name, android::base::borrowed_fd fd, size_t size,\n                     std::string* response = nullptr,\n                     std::vector<std::string>* info = nullptr) override;\n    RetCode Download(android::base::borrowed_fd fd, size_t size, std::string* response = nullptr,\n                     std::vector<std::string>* info = nullptr);\n    RetCode Download(const std::string& name, const std::vector<char>& buf,\n                     std::string* response = nullptr, std::vector<std::string>* info = nullptr);\n    RetCode Download(const std::vector<char>& buf, std::string* response = nullptr,\n                     std::vector<std::string>* info = nullptr);\n    RetCode Download(const std::string& partition, struct sparse_file* s, uint32_t sz,\n                     size_t current, size_t total, bool use_crc, std::string* response = nullptr,\n                     std::vector<std::string>* info = nullptr);\n    RetCode Download(sparse_file* s, bool use_crc = false, std::string* response = nullptr,\n                     std::vector<std::string>* info = nullptr);\n    RetCode Erase(const std::string& partition, std::string* response = nullptr,\n                  std::vector<std::string>* info = nullptr) override;\n    RetCode Flash(const std::string& partition, std::string* response = nullptr,\n                  std::vector<std::string>* info = nullptr);\n    RetCode GetVar(const std::string& key, std::string* val,\n                   std::vector<std::string>* info = nullptr) override;\n    RetCode GetVarAll(std::vector<std::string>* response);\n    RetCode Reboot(std::string* response = nullptr,\n                   std::vector<std::string>* info = nullptr) override;\n    RetCode RebootTo(std::string target, std::string* response = nullptr,\n                     std::vector<std::string>* info = nullptr) override;\n    RetCode ResizePartition(const std::string& partition, const std::string& size) override;\n    RetCode SetActive(const std::string& slot, std::string* response = nullptr,\n                      std::vector<std::string>* info = nullptr);\n    RetCode Upload(const std::string& outfile, std::string* response = nullptr,\n                   std::vector<std::string>* info = nullptr);\n    RetCode SnapshotUpdateCommand(const std::string& command, std::string* response = nullptr,\n                                  std::vector<std::string>* info = nullptr);\n    RetCode FetchToFd(const std::string& partition, android::base::borrowed_fd fd,\n                      int64_t offset = -1, int64_t size = -1, std::string* response = nullptr,\n                      std::vector<std::string>* info = nullptr) override;\n\n    /* HIGHER LEVEL COMMANDS -- Composed of the commands above */\n    RetCode FlashPartition(const std::string& partition, const std::vector<char>& data);\n    RetCode FlashPartition(const std::string& partition, android::base::borrowed_fd fd,\n                           uint32_t sz) override;\n    RetCode FlashPartition(const std::string& partition, sparse_file* s, uint32_t sz,\n                           size_t current, size_t total);\n\n    RetCode Partitions(std::vector<std::tuple<std::string, uint64_t>>* partitions);\n    RetCode Require(const std::string& var, const std::vector<std::string>& allowed, bool* reqmet,\n                    bool invert = false);\n\n    /* HELPERS */\n    void SetInfoCallback(std::function<void(const std::string&)> info);\n    static const std::string RCString(RetCode rc);\n    std::string Error();\n    RetCode WaitForDisconnect() override;\n\n    void set_transport(std::unique_ptr<Transport> transport);\n\n    RetCode RawCommand(const std::string& cmd, const std::string& message,\n                       std::string* response = nullptr, std::vector<std::string>* info = nullptr,\n                       int* dsize = nullptr);\n\n    RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,\n                       std::vector<std::string>* info = nullptr, int* dsize = nullptr);\n\n  protected:\n    RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,\n                            std::vector<std::string>* info = nullptr);\n    RetCode HandleResponse(std::string* response = nullptr,\n                           std::vector<std::string>* info = nullptr, int* dsize = nullptr);\n\n    std::string ErrnoStr(const std::string& msg);\n\n    std::unique_ptr<Transport> transport_;\n\n  private:\n    RetCode SendBuffer(android::base::borrowed_fd fd, size_t size);\n    RetCode SendBuffer(const std::vector<char>& buf);\n    RetCode SendBuffer(const void* buf, size_t size);\n\n    RetCode ReadBuffer(void* buf, size_t size);\n\n    RetCode UploadInner(const std::string& outfile, std::string* response = nullptr,\n                        std::vector<std::string>* info = nullptr);\n    RetCode RunAndReadBuffer(const std::string& cmd, std::string* response,\n                             std::vector<std::string>* info,\n                             const std::function<RetCode(const char*, uint64_t)>& write_fn);\n\n    int SparseWriteCallback(std::vector<char>& tpbuf, const char* data, size_t len);\n\n    std::string error_;\n    std::function<void(const std::string&)> prolog_;\n    std::function<void(int)> epilog_;\n    std::function<void(const std::string&)> info_;\n    std::function<void(const std::string&)> text_;\n    bool disable_checks_;\n};\n\n}  // namespace fastboot\n"
  },
  {
    "path": "fastboot/fastboot_driver_interface.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#pragma once\n\n#include <string>\n\n#include \"android-base/unique_fd.h\"\n\nclass Transport;\n\nnamespace fastboot {\n\nenum RetCode : int {\n    SUCCESS = 0,\n    BAD_ARG,\n    IO_ERROR,\n    BAD_DEV_RESP,\n    DEVICE_FAIL,\n    TIMEOUT,\n};\n\nclass IFastBootDriver {\n  public:\n    RetCode virtual FlashPartition(const std::string& partition, android::base::borrowed_fd fd,\n                                   uint32_t sz) = 0;\n    RetCode virtual DeletePartition(const std::string& partition) = 0;\n    RetCode virtual WaitForDisconnect() = 0;\n    RetCode virtual Reboot(std::string* response = nullptr,\n                           std::vector<std::string>* info = nullptr) = 0;\n\n    RetCode virtual RebootTo(std::string target, std::string* response = nullptr,\n                             std::vector<std::string>* info = nullptr) = 0;\n    RetCode virtual GetVar(const std::string& key, std::string* val,\n                           std::vector<std::string>* info = nullptr) = 0;\n    RetCode virtual FetchToFd(const std::string& partition, android::base::borrowed_fd fd,\n                              int64_t offset = -1, int64_t size = -1,\n                              std::string* response = nullptr,\n                              std::vector<std::string>* info = nullptr) = 0;\n    RetCode virtual Download(const std::string& name, android::base::borrowed_fd fd, size_t size,\n                             std::string* response = nullptr,\n                             std::vector<std::string>* info = nullptr) = 0;\n    RetCode virtual RawCommand(const std::string& cmd, const std::string& message,\n                               std::string* response = nullptr,\n                               std::vector<std::string>* info = nullptr, int* dsize = nullptr) = 0;\n    RetCode virtual ResizePartition(const std::string& partition, const std::string& size) = 0;\n    RetCode virtual Erase(const std::string& partition, std::string* response = nullptr,\n                          std::vector<std::string>* info = nullptr) = 0;\n    virtual ~IFastBootDriver() = default;\n};\n}  // namespace fastboot"
  },
  {
    "path": "fastboot/fastboot_driver_mock.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#pragma once\n\n#include <gmock/gmock.h>\n#include \"fastboot_driver_interface.h\"\n\nnamespace fastboot {\n\nclass MockFastbootDriver : public IFastBootDriver {\n  public:\n    MOCK_METHOD(RetCode, FlashPartition, (const std::string&, android::base::borrowed_fd, uint32_t),\n                (override));\n    MOCK_METHOD(RetCode, DeletePartition, (const std::string&), (override));\n    MOCK_METHOD(RetCode, Reboot, (std::string*, std::vector<std::string>*), (override));\n    MOCK_METHOD(RetCode, RebootTo, (std::string, std::string*, std::vector<std::string>*),\n                (override));\n    MOCK_METHOD(RetCode, GetVar, (const std::string&, std::string*, std::vector<std::string>*),\n                (override));\n    MOCK_METHOD(RetCode, FetchToFd,\n                (const std::string&, android::base::borrowed_fd, int64_t offset, int64_t size,\n                 std::string*, std::vector<std::string>*),\n                (override));\n    MOCK_METHOD(RetCode, Download,\n                (const std::string&, android::base::borrowed_fd, size_t, std::string*,\n                 std::vector<std::string>*),\n                (override));\n    MOCK_METHOD(RetCode, RawCommand,\n                (const std::string&, const std::string&, std::string*, std::vector<std::string>*,\n                 int*),\n                (override));\n    MOCK_METHOD(RetCode, ResizePartition, (const std::string&, const std::string&), (override));\n    MOCK_METHOD(RetCode, Erase, (const std::string&, std::string*, std::vector<std::string>*),\n                (override));\n    MOCK_METHOD(RetCode, WaitForDisconnect, (), (override));\n};\n\n}  // namespace fastboot"
  },
  {
    "path": "fastboot/fastboot_driver_test.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"fastboot_driver.h\"\n\n#include <memory>\n#include <optional>\n\n#include <gtest/gtest.h>\n#include \"mock_transport.h\"\n\nusing namespace ::testing;\nusing namespace fastboot;\n\nclass DriverTest : public ::testing::Test {\n  protected:\n    InSequence s_;\n};\n\nTEST_F(DriverTest, GetVar) {\n    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();\n    MockTransport* transport = transport_pointer.get();\n    FastBootDriver driver(std::move(transport_pointer));\n\n    EXPECT_CALL(*transport, Write(_, _))\n            .With(AllArgs(RawData(\"getvar:version\")))\n            .WillOnce(ReturnArg<1>());\n    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData(\"OKAY0.4\")));\n\n    std::string output;\n    ASSERT_EQ(driver.GetVar(\"version\", &output), SUCCESS) << driver.Error();\n    ASSERT_EQ(output, \"0.4\");\n}\n\nTEST_F(DriverTest, InfoMessage) {\n    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();\n    MockTransport* transport = transport_pointer.get();\n    FastBootDriver driver(std::move(transport_pointer));\n\n    EXPECT_CALL(*transport, Write(_, _))\n            .With(AllArgs(RawData(\"oem dmesg\")))\n            .WillOnce(ReturnArg<1>());\n    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData(\"INFOthis is an info line\")));\n    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData(\"OKAY\")));\n\n    std::vector<std::string> info;\n    ASSERT_EQ(driver.RawCommand(\"oem dmesg\", \"\", nullptr, &info), SUCCESS) << driver.Error();\n    ASSERT_EQ(info.size(), size_t(1));\n    ASSERT_EQ(info[0], \"this is an info line\");\n}\n\nTEST_F(DriverTest, TextMessage) {\n    std::string text;\n    std::unique_ptr<MockTransport> transport_pointer = std::make_unique<MockTransport>();\n    MockTransport* transport = transport_pointer.get();\n\n    DriverCallbacks callbacks{[](const std::string&) {}, [](int) {}, [](const std::string&) {},\n                              [&text](const std::string& extra_text) { text += extra_text; }};\n\n    FastBootDriver driver(std::move(transport_pointer), callbacks);\n\n    EXPECT_CALL(*transport, Write(_, _))\n            .With(AllArgs(RawData(\"oem trusty runtest trusty.hwaes.bench\")))\n            .WillOnce(ReturnArg<1>());\n    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData(\"TEXTthis is a text line\")));\n    EXPECT_CALL(*transport, Read(_, _))\n            .WillOnce(Invoke(\n                    CopyData(\"TEXT, albeit very long and split over multiple TEXT messages.\")));\n    EXPECT_CALL(*transport, Read(_, _))\n            .WillOnce(Invoke(CopyData(\"TEXT Indeed we can do that now with a TEXT message whenever \"\n                                      \"we feel like it.\")));\n    EXPECT_CALL(*transport, Read(_, _))\n            .WillOnce(Invoke(CopyData(\"TEXT Isn't that truly super cool?\")));\n\n    EXPECT_CALL(*transport, Read(_, _)).WillOnce(Invoke(CopyData(\"OKAY\")));\n\n    std::vector<std::string> info;\n    ASSERT_EQ(driver.RawCommand(\"oem trusty runtest trusty.hwaes.bench\", \"\", nullptr, &info),\n              SUCCESS)\n            << driver.Error();\n    ASSERT_EQ(text,\n              \"this is a text line\"\n              \", albeit very long and split over multiple TEXT messages.\"\n              \" Indeed we can do that now with a TEXT message whenever we feel like it.\"\n              \" Isn't that truly super cool?\");\n}\n"
  },
  {
    "path": "fastboot/fastboot_integration_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2023 The Android Open Source Project\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n          http://www.apache.org/licenses/LICENSE-2.0\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config to run fastboot integration tests\">\n    <option name=\"test-suite-tag\" value=\"fastboot_unittests\" />\n    <test class=\"com.android.tradefed.testtype.python.PythonBinaryHostTest\" >\n        <option name=\"par-file-name\" value=\"fastboot_integration_test\" />\n        <option name=\"test-timeout\" value=\"1m\" />\n    </test>\n</configuration>"
  },
  {
    "path": "fastboot/fastboot_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fastboot.h\"\n\n#include <android-base/logging.h>\n#include <gtest/gtest.h>\n\nTEST(FastBoot, ParseOsPatchLevel) {\n    FastBootTool fb;\n    boot_img_hdr_v1 hdr;\n\n    hdr = {};\n    fb.ParseOsPatchLevel(&hdr, \"2018-01-05\");\n    ASSERT_EQ(2018U, 2000U + ((hdr.os_version >> 4) & 0x7f));\n    ASSERT_EQ(1U, ((hdr.os_version >> 0) & 0xf));\n\n    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, \"2018\"), \"should be YYYY-MM-DD\");\n    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, \"2018-01\"), \"should be YYYY-MM-DD\");\n    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, \"2128-01-05\"), \"year out of range\");\n    EXPECT_DEATH(fb.ParseOsPatchLevel(&hdr, \"2018-13-05\"), \"month out of range\");\n}\n\nTEST(FastBoot, ParseOsVersion) {\n    FastBootTool fb;\n    boot_img_hdr_v1 hdr;\n\n    hdr = {};\n    fb.ParseOsVersion(&hdr, \"1.2.3\");\n    ASSERT_EQ(1U, ((hdr.os_version >> 25) & 0x7f));\n    ASSERT_EQ(2U, ((hdr.os_version >> 18) & 0x7f));\n    ASSERT_EQ(3U, ((hdr.os_version >> 11) & 0x7f));\n\n    fb.ParseOsVersion(&hdr, \"1.2\");\n    ASSERT_EQ(1U, ((hdr.os_version >> 25) & 0x7f));\n    ASSERT_EQ(2U, ((hdr.os_version >> 18) & 0x7f));\n    ASSERT_EQ(0U, ((hdr.os_version >> 11) & 0x7f));\n\n    fb.ParseOsVersion(&hdr, \"1\");\n    ASSERT_EQ(1U, ((hdr.os_version >> 25) & 0x7f));\n    ASSERT_EQ(0U, ((hdr.os_version >> 18) & 0x7f));\n    ASSERT_EQ(0U, ((hdr.os_version >> 11) & 0x7f));\n\n    EXPECT_DEATH(fb.ParseOsVersion(&hdr, \"\"), \"bad OS version\");\n    EXPECT_DEATH(fb.ParseOsVersion(&hdr, \"1.2.3.4\"), \"bad OS version\");\n    EXPECT_DEATH(fb.ParseOsVersion(&hdr, \"128.2.3\"), \"bad OS version\");\n    EXPECT_DEATH(fb.ParseOsVersion(&hdr, \"1.128.3\"), \"bad OS version\");\n    EXPECT_DEATH(fb.ParseOsVersion(&hdr, \"1.2.128\"), \"bad OS version\");\n}\n\nextern bool ParseRequirementLine(const std::string& line, std::string* name, std::string* product,\n                                 bool* invert, std::vector<std::string>* options);\n\nstatic void ParseRequirementLineTest(const std::string& line, const std::string& expected_name,\n                                     const std::string& expected_product, bool expected_invert,\n                                     const std::vector<std::string>& expected_options) {\n    std::string name;\n    std::string product;\n    bool invert;\n    std::vector<std::string> options;\n\n    EXPECT_TRUE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;\n\n    EXPECT_EQ(expected_name, name) << line;\n    EXPECT_EQ(expected_product, product) << line;\n    EXPECT_EQ(expected_invert, invert) << line;\n    EXPECT_EQ(expected_options, options) << line;\n}\n\nTEST(FastBoot, ParseRequirementLineSuccesses) {\n    // Examples provided in the code + slight variations.\n    ParseRequirementLineTest(\"require product=alpha\", \"product\", \"\", false, {\"alpha\"});\n    ParseRequirementLineTest(\"require product=alpha|beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require version-bootloader=1234\", \"version-bootloader\", \"\", false,\n                             {\"1234\"});\n    ParseRequirementLineTest(\"require-for-product:gamma version-bootloader=istanbul\",\n                             \"version-bootloader\", \"gamma\", false, {\"istanbul\"});\n    ParseRequirementLineTest(\"require-for-product:gamma version-bootloader=istanbul|constantinople\",\n                             \"version-bootloader\", \"gamma\", false, {\"istanbul\", \"constantinople\"});\n    ParseRequirementLineTest(\"require partition-exists=vendor\", \"partition-exists\", \"\", false,\n                             {\"vendor\"});\n    ParseRequirementLineTest(\"reject product=alpha\", \"product\", \"\", true, {\"alpha\"});\n    ParseRequirementLineTest(\"reject product=alpha|beta|gamma\", \"product\", \"\", true,\n                             {\"alpha\", \"beta\", \"gamma\"});\n\n    // Without any prefix, assume 'require'\n    ParseRequirementLineTest(\"product=alpha|beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    // Including if the variable name is otherwise a prefix keyword\n    ParseRequirementLineTest(\"require = alpha\", \"require\", \"\", false, {\"alpha\"});\n    ParseRequirementLineTest(\"reject = alpha\", \"reject\", \"\", false, {\"alpha\"});\n    ParseRequirementLineTest(\"require-for-product:gamma = alpha\", \"require-for-product:gamma\", \"\",\n                             false, {\"alpha\"});\n\n    // Extra spaces are allowed.\n    ParseRequirementLineTest(\"require    product=alpha|beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product    =alpha|beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=   alpha|beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product   =   alpha|beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=alpha  |beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=alpha|  beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=alpha  |  beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=alpha|beta|gamma   \", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"product  =  alpha  |  beta  |  gamma   \", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require-for-product:  gamma version-bootloader=istanbul\",\n                             \"version-bootloader\", \"gamma\", false, {\"istanbul\"});\n\n    // Extraneous ending | is okay, implies accepting an empty string.\n    ParseRequirementLineTest(\"require product=alpha|\", \"product\", \"\", false, {\"alpha\", \"\"});\n    ParseRequirementLineTest(\"require product=alpha|beta|gamma|\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"gamma\", \"\"});\n\n    // Accept empty options, double ||, etc, implies accepting an empty string.\n    ParseRequirementLineTest(\"require product=alpha||beta|   |gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"\", \"beta\", \"\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=alpha||beta|gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"\", \"beta\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=alpha|beta|   |gamma\", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"\", \"gamma\"});\n    ParseRequirementLineTest(\"require product=alpha||\", \"product\", \"\", false, {\"alpha\", \"\", \"\"});\n    ParseRequirementLineTest(\"require product=alpha|| \", \"product\", \"\", false, {\"alpha\", \"\", \"\"});\n    ParseRequirementLineTest(\"require product=alpha| \", \"product\", \"\", false, {\"alpha\", \"\"});\n    ParseRequirementLineTest(\"require product=alpha|beta| \", \"product\", \"\", false,\n                             {\"alpha\", \"beta\", \"\"});\n\n    // No option string is also treating as accepting an empty string.\n    ParseRequirementLineTest(\"require =\", \"require\", \"\", false, {\"\"});\n    ParseRequirementLineTest(\"require = |\", \"require\", \"\", false, {\"\", \"\"});\n    ParseRequirementLineTest(\"reject =\", \"reject\", \"\", false, {\"\"});\n    ParseRequirementLineTest(\"reject = |\", \"reject\", \"\", false, {\"\", \"\"});\n    ParseRequirementLineTest(\"require-for-product: =\", \"require-for-product:\", \"\", false, {\"\"});\n    ParseRequirementLineTest(\"require-for-product: = | \", \"require-for-product:\", \"\", false,\n                             {\"\", \"\"});\n    ParseRequirementLineTest(\"require product=\", \"product\", \"\", false, {\"\"});\n    ParseRequirementLineTest(\"require product = \", \"product\", \"\", false, {\"\"});\n    ParseRequirementLineTest(\"require product = | \", \"product\", \"\", false, {\"\", \"\"});\n    ParseRequirementLineTest(\"reject product=\", \"product\", \"\", true, {\"\"});\n    ParseRequirementLineTest(\"reject product = \", \"product\", \"\", true, {\"\"});\n    ParseRequirementLineTest(\"reject product = | \", \"product\", \"\", true, {\"\", \"\"});\n    ParseRequirementLineTest(\"require-for-product:gamma product=\", \"product\", \"gamma\", false, {\"\"});\n    ParseRequirementLineTest(\"require-for-product:gamma product = \", \"product\", \"gamma\", false,\n                             {\"\"});\n    ParseRequirementLineTest(\"require-for-product:gamma product = |\", \"product\", \"gamma\", false,\n                             {\"\", \"\"});\n\n    // Check for board -> product substitution.\n    ParseRequirementLineTest(\"require board=alpha\", \"product\", \"\", false, {\"alpha\"});\n    ParseRequirementLineTest(\"board=alpha\", \"product\", \"\", false, {\"alpha\"});\n}\n\nstatic void ParseRequirementLineTestMalformed(const std::string& line) {\n    std::string name;\n    std::string product;\n    bool invert;\n    std::vector<std::string> options;\n\n    EXPECT_FALSE(ParseRequirementLine(line, &name, &product, &invert, &options)) << line;\n}\n\nTEST(FastBoot, ParseRequirementLineMalformed) {\n    ParseRequirementLineTestMalformed(\"nothing\");\n    ParseRequirementLineTestMalformed(\"\");\n    ParseRequirementLineTestMalformed(\"=\");\n    ParseRequirementLineTestMalformed(\"|\");\n\n    ParseRequirementLineTestMalformed(\"require\");\n    ParseRequirementLineTestMalformed(\"require \");\n    ParseRequirementLineTestMalformed(\"reject\");\n    ParseRequirementLineTestMalformed(\"reject \");\n    ParseRequirementLineTestMalformed(\"require-for-product:\");\n    ParseRequirementLineTestMalformed(\"require-for-product: \");\n\n    ParseRequirementLineTestMalformed(\"require product\");\n    ParseRequirementLineTestMalformed(\"reject product\");\n\n    ParseRequirementLineTestMalformed(\"require-for-product:gamma\");\n    ParseRequirementLineTestMalformed(\"require-for-product:gamma product\");\n\n    // No spaces allowed before between require-for-product and :.\n    ParseRequirementLineTestMalformed(\"require-for-product :\");\n}\n\nstatic void ParseNetworkSerialTest(const std::string& description, const std::string& serial,\n                                   const std::string& expected_address,\n                                   const Socket::Protocol expected_protocol,\n                                   const int expected_port) {\n    const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);\n\n    ASSERT_RESULT_OK(parsed) << description;\n\n    const NetworkSerial network_serial = parsed.value();\n    EXPECT_EQ(network_serial.address, expected_address) << description;\n    EXPECT_EQ(network_serial.protocol, expected_protocol) << description;\n    EXPECT_EQ(network_serial.port, expected_port) << description;\n}\n\nstatic void ParseNetworkSerialNegativeTest(const std::string& description,\n                                           const std::string& serial,\n                                           const FastbootError::Type expected_error) {\n    const Result<NetworkSerial, FastbootError> parsed = ParseNetworkSerial(serial);\n\n    EXPECT_FALSE(parsed.ok()) << description;\n    EXPECT_EQ(parsed.error().code(), expected_error) << description;\n}\n\nTEST(FastBoot, ParseNetworkSerial) {\n    ParseNetworkSerialTest(\"tcp IPv4 parsed\", \"tcp:192.168.1.0\", \"192.168.1.0\",\n                           Socket::Protocol::kTcp, 5554);\n\n    ParseNetworkSerialTest(\"udp IPv4 parsed\", \"udp:192.168.1.0\", \"192.168.1.0\",\n                           Socket::Protocol::kUdp, 5554);\n\n    ParseNetworkSerialTest(\"port parsed\", \"udp:192.168.1.0:9999\", \"192.168.1.0\",\n                           Socket::Protocol::kUdp, 9999);\n\n    ParseNetworkSerialTest(\"IPv6 parsed\", \"tcp:2001:db8:3333:4444:5555:6666:7777:8888\",\n                           \"2001:db8:3333:4444:5555:6666:7777:8888\", Socket::Protocol::kTcp, 5554);\n\n    ParseNetworkSerialTest(\"empty IPv6 parsed\", \"tcp:::\", \"::\", Socket::Protocol::kTcp, 5554);\n\n    ParseNetworkSerialNegativeTest(\"wrong prefix\", \"tcpa:192.168.1.0\",\n                                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);\n\n    ParseNetworkSerialNegativeTest(\"no prefix\", \"192.168.1.0\",\n                                   FastbootError::Type::NETWORK_SERIAL_WRONG_PREFIX);\n\n    ParseNetworkSerialNegativeTest(\"wrong port\", \"tcp:192.168.1.0:-1\",\n                                   FastbootError::Type::NETWORK_SERIAL_WRONG_ADDRESS);\n}\n\nint main(int argc, char* argv[]) {\n    ::testing::InitGoogleTest(&argc, argv);\n    android::base::InitLogging(argv);\n    android::base::SetMinimumLogSeverity(android::base::VERBOSE);\n\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "fastboot/filesystem.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifdef _WIN32\n#include <android-base/utf8.h>\n#include <direct.h>\n#include <shlobj.h>\n#else\n#include <pwd.h>\n#endif\n\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <sys/file.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <vector>\n\n#include \"filesystem.h\"\n\nnamespace {\n\nint LockFile(int fd) {\n#ifdef _WIN32\n    HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));\n    OVERLAPPED overlapped = {};\n    const BOOL locked =\n            LockFileEx(handle, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &overlapped);\n    return locked ? 0 : -1;\n#else\n    return flock(fd, LOCK_EX);\n#endif\n}\n\n}  // namespace\n\n// inspired by adb implementation:\n// cs.android.com/android/platform/superproject/+/master:packages/modules/adb/adb_utils.cpp;l=275\nstd::string GetHomeDirPath() {\n#ifdef _WIN32\n    WCHAR path[MAX_PATH];\n    const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);\n    if (FAILED(hr)) {\n        return {};\n    }\n    std::string home_str;\n    if (!android::base::WideToUTF8(path, &home_str)) {\n        return {};\n    }\n    return home_str;\n#else\n    if (const char* const home = getenv(\"HOME\")) {\n        return home;\n    }\n\n    struct passwd pwent;\n    struct passwd* result;\n    int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);\n    if (pwent_max == -1) {\n        pwent_max = 16384;\n    }\n    std::vector<char> buf(pwent_max);\n    int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);\n    if (rc == 0 && result) {\n        return result->pw_dir;\n    }\n#endif\n\n    return {};\n}\n\nbool FileExists(const std::string& path) {\n    return access(path.c_str(), F_OK) == 0;\n}\n\nbool EnsureDirectoryExists(const std::string& directory_path) {\n    const int result =\n#ifdef _WIN32\n            _mkdir(directory_path.c_str());\n#else\n            mkdir(directory_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);\n#endif\n\n    return result == 0 || errno == EEXIST;\n}\n\nFileLock::FileLock(const std::string& path) : fd_(open(path.c_str(), O_CREAT | O_WRONLY, 0644)) {\n    if (LockFile(fd_.get()) != 0) {\n        LOG(FATAL) << \"Failed to acquire a lock on \" << path;\n    }\n}"
  },
  {
    "path": "fastboot/filesystem.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android-base/unique_fd.h>\n\n#include <string>\n\nusing android::base::unique_fd;\n\n// TODO(b/175635923): remove after enabling libc++fs for windows\nconst char kPathSeparator =\n#ifdef _WIN32\n        '\\\\';\n#else\n        '/';\n#endif\n\nstd::string GetHomeDirPath();\nbool FileExists(const std::string& path);\nbool EnsureDirectoryExists(const std::string& directory_path);\n\nclass FileLock {\n  public:\n    FileLock() = delete;\n    FileLock(const std::string& path);\n\n  private:\n    unique_fd fd_;\n};"
  },
  {
    "path": "fastboot/fs.cpp",
    "content": "#include \"fs.h\"\n\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#ifndef _WIN32\n#include <sys/wait.h>\n#else\n#include <tchar.h>\n#include <windows.h>\n#endif\n#include <unistd.h>\n#include <vector>\n\n#include <android-base/errors.h>\n#include <android-base/file.h>\n#include <android-base/macros.h>\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n\nusing android::base::GetExecutableDirectory;\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\n\n#ifdef _WIN32\nstatic int exec_cmd(const char* path, const char** argv, const char** envp) {\n    std::string cmd;\n    int i = 0;\n    while (argv[i] != nullptr) {\n        cmd += argv[i++];\n        cmd += \" \";\n    }\n    cmd = cmd.substr(0, cmd.size() - 1);\n\n    STARTUPINFO si;\n    PROCESS_INFORMATION pi;\n    DWORD exit_code = 0;\n\n    ZeroMemory(&si, sizeof(si));\n    si.cb = sizeof(si);\n    ZeroMemory(&pi, sizeof(pi));\n\n    std::string env_str;\n    if (envp != nullptr) {\n        while (*envp != nullptr) {\n            env_str += std::string(*envp) + std::string(\"\\0\", 1);\n            envp++;\n        }\n    }\n\n    if (!CreateProcessA(nullptr,                         // No module name (use command line)\n                        const_cast<char*>(cmd.c_str()),  // Command line\n                        nullptr,                         // Process handle not inheritable\n                        nullptr,                         // Thread handle not inheritable\n                        FALSE,                           // Set handle inheritance to FALSE\n                        0,                               // No creation flags\n                        env_str.empty() ? nullptr : LPSTR(env_str.c_str()),\n                        nullptr,  // Use parent's starting directory\n                        &si,      // Pointer to STARTUPINFO structure\n                        &pi)      // Pointer to PROCESS_INFORMATION structure\n    ) {\n        fprintf(stderr, \"CreateProcess failed: %s\\n\",\n                android::base::SystemErrorCodeToString(GetLastError()).c_str());\n        return -1;\n    }\n\n    WaitForSingleObject(pi.hProcess, INFINITE);\n\n    GetExitCodeProcess(pi.hProcess, &exit_code);\n\n    CloseHandle(pi.hProcess);\n    CloseHandle(pi.hThread);\n\n    if (exit_code != 0) {\n        fprintf(stderr, \"%s failed: %lu\\n\", path, exit_code);\n        return -1;\n    }\n    return 0;\n}\n#else\nstatic int exec_cmd(const char* path, const char** argv, const char** envp) {\n    int status;\n    pid_t child;\n    if ((child = fork()) == 0) {\n        execve(path, const_cast<char**>(argv), const_cast<char**>(envp));\n        _exit(EXIT_FAILURE);\n    }\n    if (child < 0) {\n        fprintf(stderr, \"%s failed with fork %s\\n\", path, strerror(errno));\n        return -1;\n    }\n    if (TEMP_FAILURE_RETRY(waitpid(child, &status, 0)) == -1) {\n        fprintf(stderr, \"%s failed with waitpid %s\\n\", path, strerror(errno));\n        return -1;\n    }\n    int ret = -1;\n    if (WIFEXITED(status)) {\n        ret = WEXITSTATUS(status);\n    }\n\n    if (ret != 0) {\n        fprintf(stderr, \"%s failed with status %d\\n\", path, ret);\n        return -1;\n    }\n    return 0;\n}\n#endif\n\nstatic int generate_ext4_image(const char* fileName, long long partSize, unsigned eraseBlkSize,\n                               unsigned logicalBlkSize, const unsigned fsOptions) {\n    static constexpr int block_size = 4096;\n    const std::string exec_dir = android::base::GetExecutableDirectory();\n\n    const std::string mke2fs_path = exec_dir + \"/mke2fs\";\n    std::vector<const char*> mke2fs_args = {mke2fs_path.c_str(), \"-t\", \"ext4\", \"-b\"};\n\n    std::string block_size_str = std::to_string(block_size);\n    mke2fs_args.push_back(block_size_str.c_str());\n\n    std::string ext_attr = \"android_sparse\";\n    if (eraseBlkSize != 0 && logicalBlkSize != 0) {\n        int raid_stride = logicalBlkSize / block_size;\n        int raid_stripe_width = eraseBlkSize / block_size;\n        // stride should be the max of 8kb and logical block size\n        if (logicalBlkSize != 0 && logicalBlkSize < 8192) raid_stride = 8192 / block_size;\n        // stripe width should be >= stride\n        if (raid_stripe_width < raid_stride) raid_stripe_width = raid_stride;\n        ext_attr += StringPrintf(\",stride=%d,stripe-width=%d\", raid_stride, raid_stripe_width);\n    }\n    mke2fs_args.push_back(\"-E\");\n    mke2fs_args.push_back(ext_attr.c_str());\n    mke2fs_args.push_back(\"-O\");\n    mke2fs_args.push_back(\"uninit_bg\");\n\n    if (fsOptions & (1 << FS_OPT_PROJID)) {\n        mke2fs_args.push_back(\"-I\");\n        mke2fs_args.push_back(\"512\");\n    }\n\n    if (fsOptions & (1 << FS_OPT_CASEFOLD)) {\n        mke2fs_args.push_back(\"-O\");\n        mke2fs_args.push_back(\"casefold\");\n        mke2fs_args.push_back(\"-E\");\n        mke2fs_args.push_back(\"encoding=utf8\");\n    }\n\n    mke2fs_args.push_back(fileName);\n\n    std::string size_str = std::to_string(partSize / block_size);\n    mke2fs_args.push_back(size_str.c_str());\n    mke2fs_args.push_back(nullptr);\n\n    const std::string mke2fs_env = \"MKE2FS_CONFIG=\" + GetExecutableDirectory() + \"/mke2fs.conf\";\n    std::vector<const char*> mke2fs_envp = {mke2fs_env.c_str(), nullptr};\n\n    int ret = exec_cmd(mke2fs_args[0], mke2fs_args.data(), mke2fs_envp.data());\n    if (ret != 0) {\n        return -1;\n    }\n    return 0;\n}\n\nenum {\n    // clang-format off\n    FSCK_SUCCESS                 = 0,\n    FSCK_ERROR_CORRECTED         = 1 << 0,\n    FSCK_SYSTEM_SHOULD_REBOOT    = 1 << 1,\n    FSCK_ERRORS_LEFT_UNCORRECTED = 1 << 2,\n    FSCK_OPERATIONAL_ERROR       = 1 << 3,\n    FSCK_USAGE_OR_SYNTAX_ERROR   = 1 << 4,\n    FSCK_USER_CANCELLED          = 1 << 5,\n    FSCK_SHARED_LIB_ERROR        = 1 << 7,\n    // clang-format on\n};\n\nstatic int generate_f2fs_image(const char* fileName, long long partSize, unsigned /* unused */,\n                               unsigned /* unused */, const unsigned fsOptions) {\n    const std::string exec_dir = android::base::GetExecutableDirectory();\n    const std::string mkf2fs_path = exec_dir + \"/make_f2fs\";\n    std::vector<const char*> mkf2fs_args = {mkf2fs_path.c_str()};\n\n    mkf2fs_args.push_back(\"-S\");\n    std::string size_str = std::to_string(partSize);\n    mkf2fs_args.push_back(size_str.c_str());\n    mkf2fs_args.push_back(\"-g\");\n    mkf2fs_args.push_back(\"android\");\n\n    if (fsOptions & (1 << FS_OPT_PROJID)) {\n        mkf2fs_args.push_back(\"-O\");\n        mkf2fs_args.push_back(\"project_quota,extra_attr\");\n    }\n\n    if (fsOptions & (1 << FS_OPT_CASEFOLD)) {\n        mkf2fs_args.push_back(\"-O\");\n        mkf2fs_args.push_back(\"casefold\");\n        mkf2fs_args.push_back(\"-C\");\n        mkf2fs_args.push_back(\"utf8\");\n    }\n\n    if (fsOptions & (1 << FS_OPT_COMPRESS)) {\n        mkf2fs_args.push_back(\"-O\");\n        mkf2fs_args.push_back(\"compression\");\n        mkf2fs_args.push_back(\"-O\");\n        mkf2fs_args.push_back(\"extra_attr\");\n    }\n\n    mkf2fs_args.push_back(fileName);\n    mkf2fs_args.push_back(nullptr);\n\n    int ret = exec_cmd(mkf2fs_args[0], mkf2fs_args.data(), nullptr);\n    if (ret != 0) {\n        return -1;\n    }\n    return 0;\n}\n\nstatic const struct fs_generator {\n    const char* fs_type;  //must match what fastboot reports for partition type\n\n    //returns 0 or error value\n    int (*generate)(const char* fileName, long long partSize, unsigned eraseBlkSize,\n                    unsigned logicalBlkSize, const unsigned fsOptions);\n\n} generators[] = {\n    { \"ext4\", generate_ext4_image},\n    { \"f2fs\", generate_f2fs_image},\n};\n\nconst struct fs_generator* fs_get_generator(const std::string& fs_type) {\n    for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {\n        if (fs_type == generators[i].fs_type) {\n            return generators + i;\n        }\n    }\n    return nullptr;\n}\n\nint fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,\n                          unsigned eraseBlkSize, unsigned logicalBlkSize,\n                          const unsigned fsOptions) {\n    return gen->generate(fileName, partSize, eraseBlkSize, logicalBlkSize, fsOptions);\n}\n"
  },
  {
    "path": "fastboot/fs.h",
    "content": "#pragma once\n\n#include <string>\n#include <stdint.h>\n\nstruct fs_generator;\n\nenum FS_OPTION {\n    FS_OPT_CASEFOLD,\n    FS_OPT_PROJID,\n    FS_OPT_COMPRESS,\n};\n\nconst struct fs_generator* fs_get_generator(const std::string& fs_type);\nint fs_generator_generate(const struct fs_generator* gen, const char* fileName, long long partSize,\n                          unsigned eraseBlkSize = 0, unsigned logicalBlkSize = 0,\n                          unsigned fsOptions = 0);\n"
  },
  {
    "path": "fastboot/fuzzer/Android.bp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_fuzz {\n    name: \"fastboot_fuzzer\",\n    host_supported: true,\n    device_supported: false,\n    srcs: [\n        \"fastboot_fuzzer.cpp\",\n        \"socket_mock_fuzz.cpp\",\n    ],\n    header_libs: [\n        \"bootimg_headers\",\n        \"fastboot_headers\",\n    ],\n    static_libs: [\n        \"libext4_utils\",\n        \"libcrypto\",\n        \"libfastboot\",\n        \"libbuildversion\",\n        \"libbase\",\n        \"libziparchive\",\n        \"libsparse\",\n        \"libutils\",\n        \"liblog\",\n        \"libz\",\n        \"libdiagnose_usb\",\n        \"libbase\",\n        \"libcutils\",\n        \"libgtest\",\n        \"libgtest_main\",\n        \"libbase\",\n        \"libadb_host\",\n        \"liblp\",\n        \"liblog\",\n    ],\n    fuzz_config: {\n        cc: [\n            \"dvander@google.com\",\n            \"elsk@google.com\",\n            \"enh@google.com\",\n            \"zhangkelvin@google.com\",\n        ],\n        componentid: 533764,\n        hotlists: [\n            \"4593311\",\n        ],\n        description: \"The fuzzer targets the APIs of libfastboot library\",\n        vector: \"local_no_privileges_required\",\n        service_privilege: \"host_only\",\n        users: \"single_user\",\n        fuzzed_code_usage: \"shipped\",\n    },\n}\n"
  },
  {
    "path": "fastboot/fuzzer/README.md",
    "content": "# Fuzzer for libfastboot\n\n## Plugin Design Considerations\nThe fuzzer plugin for libfastboot is designed based on the understanding of the\nsource code and tries to achieve the following:\n\n##### Maximize code coverage\nThe configuration parameters are not hardcoded, but instead selected based on\nincoming data. This ensures more code paths are reached by the fuzzer.\n\nlibfastboot supports the following parameters:\n1. Year (parameter name: `year`)\n2. Month (parameter name: `month`)\n3. Day (parameter name: `day`)\n4. Version (parameter name: `version`)\n5. Fs Option (parameter name: `fsOption`)\n\n| Parameter| Valid Values| Configured Value|\n|------------- |-------------| ----- |\n| `year` | `2000` to `2127` | Value obtained from FuzzedDataProvider|\n| `month` | `1` to `12` | Value obtained from FuzzedDataProvider|\n| `day` | `1` to `31` | Value obtained from FuzzedDataProvider|\n| `version` | `0` to `127` | Value obtained from FuzzedDataProvider|\n| `fsOption` | 0. `casefold` 1. `projid` 2. `compress` | Value obtained from FuzzedDataProvider|\n\n##### Maximize utilization of input data\nThe plugin feeds the entire input data to the module.\nThis ensures that the plugin tolerates any kind of input (empty, huge,\nmalformed, etc) and doesnt `exit()` on any input and thereby increasing the\nchance of identifying vulnerabilities.\n\n## Build\n\nThis describes steps to build fastboot_fuzzer binary.\n\n### Android\n\n#### Steps to build\nBuild the fuzzer\n```\n  $ mm -j$(nproc) fastboot_fuzzer_fuzzer\n```\n#### Steps to run\nTo run on host\n```\n  $ $ANDROID_HOST_OUT/fuzz/${TARGET_ARCH}/fastboot_fuzzer/fastboot_fuzzer CORPUS_DIR\n```\n\n## References:\n * http://llvm.org/docs/LibFuzzer.html\n * https://github.com/google/oss-fuzz\n"
  },
  {
    "path": "fastboot/fuzzer/fastboot_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n#include \"fastboot.h\"\n#include \"socket.h\"\n#include \"socket_mock_fuzz.h\"\n#include \"tcp.h\"\n#include \"udp.h\"\n#include \"vendor_boot_img_utils.h\"\n\n#include <fuzzer/FuzzedDataProvider.h>\n\nusing namespace std;\nusing android::base::unique_fd;\n\nconst size_t kYearMin = 2000;\nconst size_t kYearMax = 2127;\nconst size_t kMonthMin = 1;\nconst size_t kMonthMax = 12;\nconst size_t kDayMin = 1;\nconst size_t kDayMax = 31;\nconst size_t kVersionMin = 0;\nconst size_t kVersionMax = 127;\nconst size_t kMaxStringSize = 100;\nconst size_t kMinTimeout = 10;\nconst size_t kMaxTimeout = 3000;\nconst uint16_t kValidUdpPacketSize = 512;\nconst uint16_t kMinUdpPackets = 1;\nconst uint16_t kMaxUdpPackets = 10;\n\nconst string kValidTcpHandshakeString = \"FB01\";\nconst string kInvalidTcpHandshakeString = \"FB00\";\nconst string kValidRamdiskName = \"default\";\nconst string kVendorBootFile = \"/tmp/vendorBootFile\";\nconst string kRamdiskFile = \"/tmp/ramdiskFile\";\nconst char* kFsOptionsArray[] = {\"casefold\", \"projid\", \"compress\"};\n\nclass FastbootFuzzer {\n  public:\n    void Process(const uint8_t* data, size_t size);\n\n  private:\n    void InvokeParseApi();\n    void InvokeSocket();\n    void InvokeTcp();\n    void InvokeUdp();\n    void InvokeVendorBootImgUtils(const uint8_t* data, size_t size);\n    bool MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,\n                              unique_ptr<Socket>* client, const string& hostname);\n    unique_ptr<FuzzedDataProvider> fdp_ = nullptr;\n};\n\nvoid FastbootFuzzer::InvokeParseApi() {\n    boot_img_hdr_v1 hdr = {};\n    FastBootTool fastBoot;\n\n    int32_t year = fdp_->ConsumeIntegralInRange<int32_t>(kYearMin, kYearMax);\n    int32_t month = fdp_->ConsumeIntegralInRange<int32_t>(kMonthMin, kMonthMax);\n    int32_t day = fdp_->ConsumeIntegralInRange<int32_t>(kDayMin, kDayMax);\n    string date = to_string(year) + \"-\" + to_string(month) + \"-\" + to_string(day);\n    fastBoot.ParseOsPatchLevel(&hdr, date.c_str());\n\n    int32_t major = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);\n    int32_t minor = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);\n    int32_t patch = fdp_->ConsumeIntegralInRange<int32_t>(kVersionMin, kVersionMax);\n    string version = to_string(major) + \".\" + to_string(minor) + \".\" + to_string(patch);\n    fastBoot.ParseOsVersion(&hdr, version.c_str());\n\n    fastBoot.ParseFsOption(fdp_->PickValueInArray(kFsOptionsArray));\n}\n\nbool FastbootFuzzer::MakeConnectedSockets(Socket::Protocol protocol, unique_ptr<Socket>* server,\n                                          unique_ptr<Socket>* client,\n                                          const string& hostname = \"localhost\") {\n    *server = Socket::NewServer(protocol, 0);\n    if (*server == nullptr) {\n        return false;\n    }\n    *client = Socket::NewClient(protocol, hostname, (*server)->GetLocalPort(), nullptr);\n    if (*client == nullptr) {\n        return false;\n    }\n    if (protocol == Socket::Protocol::kTcp) {\n        *server = (*server)->Accept();\n        if (*server == nullptr) {\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid FastbootFuzzer::InvokeSocket() {\n    unique_ptr<Socket> server, client;\n\n    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {\n        if (MakeConnectedSockets(protocol, &server, &client)) {\n            string message = fdp_->ConsumeRandomLengthString(kMaxStringSize);\n            client->Send(message.c_str(), message.length());\n            string received(message.length(), '\\0');\n            if (fdp_->ConsumeBool()) {\n                client->Close();\n            }\n            if (fdp_->ConsumeBool()) {\n                server->Close();\n            }\n            server->ReceiveAll(&received[0], received.length(),\n                               /* timeout_ms */\n                               fdp_->ConsumeIntegralInRange<size_t>(kMinTimeout, kMaxTimeout));\n            server->Close();\n            client->Close();\n        }\n    }\n}\n\nvoid FastbootFuzzer::InvokeTcp() {\n    /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */\n    SocketMockFuzz* tcp_mock = new SocketMockFuzz;\n    tcp_mock->ExpectSend(fdp_->ConsumeBool() ? kValidTcpHandshakeString\n                                             : kInvalidTcpHandshakeString);\n    tcp_mock->AddReceive(fdp_->ConsumeBool() ? kValidTcpHandshakeString\n                                             : kInvalidTcpHandshakeString);\n\n    string error;\n    unique_ptr<Transport> transport = tcp::internal::Connect(unique_ptr<Socket>(tcp_mock), &error);\n\n    if (transport.get()) {\n        string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);\n        if (fdp_->ConsumeBool()) {\n            tcp_mock->ExpectSend(write_message);\n        } else {\n            tcp_mock->ExpectSendFailure(write_message);\n        }\n        string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);\n        if (fdp_->ConsumeBool()) {\n            tcp_mock->AddReceive(read_message);\n        } else {\n            tcp_mock->AddReceiveFailure();\n        }\n\n        transport->Write(write_message.data(), write_message.length());\n\n        string buffer(read_message.length(), '\\0');\n        transport->Read(&buffer[0], buffer.length());\n\n        transport->Close();\n    }\n}\n\nstatic string PacketValue(uint16_t value) {\n    return string{static_cast<char>(value >> 8), static_cast<char>(value)};\n}\n\nstatic string ErrorPacket(uint16_t sequence, const string& message = \"\",\n                          char flags = udp::internal::kFlagNone) {\n    return string{udp::internal::kIdError, flags} + PacketValue(sequence) + message;\n}\n\nstatic string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {\n    return string{udp::internal::kIdInitialization, udp::internal::kFlagNone} +\n           PacketValue(sequence) + PacketValue(version) + PacketValue(max_packet_size);\n}\n\nstatic string QueryPacket(uint16_t sequence, uint16_t new_sequence) {\n    return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence) +\n           PacketValue(new_sequence);\n}\n\nstatic string QueryPacket(uint16_t sequence) {\n    return string{udp::internal::kIdDeviceQuery, udp::internal::kFlagNone} + PacketValue(sequence);\n}\n\nstatic string FastbootPacket(uint16_t sequence, const string& data = \"\",\n                             char flags = udp::internal::kFlagNone) {\n    return string{udp::internal::kIdFastboot, flags} + PacketValue(sequence) + data;\n}\n\nvoid FastbootFuzzer::InvokeUdp() {\n    /* Using a raw SocketMockFuzz* here because ownership shall be passed to the Transport object */\n    SocketMockFuzz* udp_mock = new SocketMockFuzz;\n    uint16_t starting_sequence = fdp_->ConsumeIntegral<uint16_t>();\n    int32_t device_max_packet_size = fdp_->ConsumeBool() ? kValidUdpPacketSize\n                                                         : fdp_->ConsumeIntegralInRange<uint16_t>(\n                                                                   0, kValidUdpPacketSize - 1);\n    udp_mock->ExpectSend(QueryPacket(0));\n    udp_mock->AddReceive(QueryPacket(0, starting_sequence));\n    udp_mock->ExpectSend(InitPacket(starting_sequence, udp::internal::kProtocolVersion,\n                                    udp::internal::kHostMaxPacketSize));\n    udp_mock->AddReceive(\n            InitPacket(starting_sequence, udp::internal::kProtocolVersion, device_max_packet_size));\n\n    string error;\n    unique_ptr<Transport> transport = udp::internal::Connect(unique_ptr<Socket>(udp_mock), &error);\n    bool is_transport_initialized = transport != nullptr && error.empty();\n\n    if (is_transport_initialized) {\n        uint16_t num_packets =\n                fdp_->ConsumeIntegralInRange<uint16_t>(kMinUdpPackets, kMaxUdpPackets);\n\n        for (uint16_t i = 0; i < num_packets; ++i) {\n            string write_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);\n            string read_message = fdp_->ConsumeRandomLengthString(kMaxStringSize);\n            if (fdp_->ConsumeBool()) {\n                udp_mock->ExpectSend(FastbootPacket(i, write_message));\n            } else {\n                udp_mock->ExpectSend(ErrorPacket(i, write_message));\n            }\n\n            if (fdp_->ConsumeBool()) {\n                udp_mock->AddReceive(FastbootPacket(i, read_message));\n            } else {\n                udp_mock->AddReceive(ErrorPacket(i, read_message));\n            }\n            transport->Write(write_message.data(), write_message.length());\n            string buffer(read_message.length(), '\\0');\n            transport->Read(&buffer[0], buffer.length());\n        }\n        transport->Close();\n    }\n}\n\nvoid FastbootFuzzer::InvokeVendorBootImgUtils(const uint8_t* data, size_t size) {\n    int32_t vendor_boot_fd = open(kVendorBootFile.c_str(), O_CREAT | O_RDWR, 0644);\n    if (vendor_boot_fd < 0) {\n        return;\n    }\n    int32_t ramdisk_fd = open(kRamdiskFile.c_str(), O_CREAT | O_RDWR, 0644);\n    if (ramdisk_fd < 0) {\n        return;\n    }\n    write(vendor_boot_fd, data, size);\n    write(ramdisk_fd, data, size);\n    string ramdisk_name = fdp_->ConsumeBool() ? kValidRamdiskName\n                                              : fdp_->ConsumeRandomLengthString(kMaxStringSize);\n    string content_vendor_boot_fd = {};\n    string content_ramdisk_fd = {};\n    lseek(vendor_boot_fd, 0, SEEK_SET);\n    lseek(ramdisk_fd, 0, SEEK_SET);\n    android::base::ReadFdToString(vendor_boot_fd, &content_vendor_boot_fd);\n    android::base::ReadFdToString(ramdisk_fd, &content_ramdisk_fd);\n    uint64_t vendor_boot_size =\n            fdp_->ConsumeBool() ? content_vendor_boot_fd.size() : fdp_->ConsumeIntegral<uint64_t>();\n    uint64_t ramdisk_size =\n            fdp_->ConsumeBool() ? content_ramdisk_fd.size() : fdp_->ConsumeIntegral<uint64_t>();\n    (void)replace_vendor_ramdisk(vendor_boot_fd, vendor_boot_size, ramdisk_name, ramdisk_fd,\n                                 ramdisk_size, unique_fd(-1), 0);\n    close(vendor_boot_fd);\n    close(ramdisk_fd);\n}\n\nvoid FastbootFuzzer::Process(const uint8_t* data, size_t size) {\n    fdp_ = make_unique<FuzzedDataProvider>(data, size);\n    InvokeParseApi();\n    InvokeSocket();\n    InvokeTcp();\n    InvokeUdp();\n    InvokeVendorBootImgUtils(data, size);\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FastbootFuzzer fastbootFuzzer;\n    fastbootFuzzer.Process(data, size);\n    return 0;\n}\n"
  },
  {
    "path": "fastboot/fuzzer/socket_mock_fuzz.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n#include \"socket_mock_fuzz.h\"\n\nSocketMockFuzz::SocketMockFuzz() : Socket(INVALID_SOCKET) {}\n\nSocketMockFuzz::~SocketMockFuzz() {}\n\nbool SocketMockFuzz::Send(const void* data, size_t length) {\n    if (events_.empty()) {\n        return false;\n    }\n\n    if (events_.front().type != EventType::kSend) {\n        return false;\n    }\n\n    std::string message(reinterpret_cast<const char*>(data), length);\n    if (events_.front().message != message) {\n        return false;\n    }\n\n    bool return_value = events_.front().status;\n    events_.pop();\n    return return_value;\n}\n\n// Mock out multi-buffer send to be one large send, since that's what it should looks like from\n// the user's perspective.\nbool SocketMockFuzz::Send(std::vector<cutils_socket_buffer_t> buffers) {\n    std::string data;\n    for (const auto& buffer : buffers) {\n        data.append(reinterpret_cast<const char*>(buffer.data), buffer.length);\n    }\n    return Send(data.data(), data.size());\n}\n\nssize_t SocketMockFuzz::Receive(void* data, size_t length, int /*timeout_ms*/) {\n    if (events_.empty()) {\n        return -1;\n    }\n\n    const Event& event = events_.front();\n    if (event.type != EventType::kReceive) {\n        return -1;\n    }\n\n    const std::string& message = event.message;\n    if (message.length() > length) {\n        return -1;\n    }\n\n    receive_timed_out_ = event.status;\n    ssize_t return_value = message.length();\n\n    // Empty message indicates failure.\n    if (message.empty()) {\n        return_value = -1;\n    } else {\n        memcpy(data, message.data(), message.length());\n    }\n\n    events_.pop();\n    return return_value;\n}\n\nint SocketMockFuzz::Close() {\n    return 0;\n}\n\nstd::unique_ptr<Socket> SocketMockFuzz::Accept() {\n    if (events_.empty()) {\n        return nullptr;\n    }\n\n    if (events_.front().type != EventType::kAccept) {\n        return nullptr;\n    }\n\n    std::unique_ptr<Socket> sock = std::move(events_.front().sock);\n    events_.pop();\n    return sock;\n}\n\nvoid SocketMockFuzz::ExpectSend(std::string message) {\n    events_.push(Event(EventType::kSend, std::move(message), true, nullptr));\n}\n\nvoid SocketMockFuzz::ExpectSendFailure(std::string message) {\n    events_.push(Event(EventType::kSend, std::move(message), false, nullptr));\n}\n\nvoid SocketMockFuzz::AddReceive(std::string message) {\n    events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));\n}\n\nvoid SocketMockFuzz::AddReceiveTimeout() {\n    events_.push(Event(EventType::kReceive, \"\", true, nullptr));\n}\n\nvoid SocketMockFuzz::AddReceiveFailure() {\n    events_.push(Event(EventType::kReceive, \"\", false, nullptr));\n}\n\nvoid SocketMockFuzz::AddAccept(std::unique_ptr<Socket> sock) {\n    events_.push(Event(EventType::kAccept, \"\", false, std::move(sock)));\n}\n\nSocketMockFuzz::Event::Event(EventType _type, std::string _message, ssize_t _status,\n                             std::unique_ptr<Socket> _sock)\n    : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}\n"
  },
  {
    "path": "fastboot/fuzzer/socket_mock_fuzz.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n#pragma once\n\n#include <memory>\n#include <queue>\n#include <string>\n\n#include <android-base/macros.h>\n\n#include \"socket.h\"\n\nclass SocketMockFuzz : public Socket {\n  public:\n    SocketMockFuzz();\n    ~SocketMockFuzz() override;\n\n    bool Send(const void* data, size_t length) override;\n    bool Send(std::vector<cutils_socket_buffer_t> buffers) override;\n    ssize_t Receive(void* data, size_t length, int timeout_ms) override;\n    int Close() override;\n    virtual std::unique_ptr<Socket> Accept();\n\n    // Adds an expectation for Send().\n    void ExpectSend(std::string message);\n\n    // Adds an expectation for Send() that returns false.\n    void ExpectSendFailure(std::string message);\n\n    // Adds data to provide for Receive().\n    void AddReceive(std::string message);\n\n    // Adds a Receive() timeout after which ReceiveTimedOut() will return true.\n    void AddReceiveTimeout();\n\n    // Adds a Receive() failure after which ReceiveTimedOut() will return false.\n    void AddReceiveFailure();\n\n    // Adds a Socket to return from Accept().\n    void AddAccept(std::unique_ptr<Socket> sock);\n\n  private:\n    enum class EventType { kSend, kReceive, kAccept };\n\n    struct Event {\n        Event(EventType _type, std::string _message, ssize_t _status,\n              std::unique_ptr<Socket> _sock);\n\n        EventType type;\n        std::string message;\n        bool status;  // Return value for Send() or timeout status for Receive().\n        std::unique_ptr<Socket> sock;\n    };\n\n    std::queue<Event> events_;\n\n    DISALLOW_COPY_AND_ASSIGN(SocketMockFuzz);\n};\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/Android.bp",
    "content": "package {\n    // See: http://go/android-license-faq\n    // A large-scale-change added 'default_applicable_licenses' to import\n    // all of the 'license_kinds' from \"system_core_fastboot_license\"\n    // to get the below license kinds:\n    //   SPDX-license-identifier-BSD\n    default_applicable_licenses: [\"system_core_fastboot_license\"],\n}\n\ncc_test_host {\n  name: \"fuzzy_fastboot\",\n  isolated: false,\n  compile_multilib: \"first\",\n\n  srcs: [\n    \"main.cpp\",\n    \"extensions.cpp\",\n    \"transport_sniffer.cpp\",\n    \"fixtures.cpp\",\n    \"test_utils.cpp\",\n  ],\n\n  static_libs: [\n    \"libfastboot2\",\n    \"libziparchive\",\n    \"libsparse\",\n    \"libutils\",\n    \"liblog\",\n    \"libz\",\n    \"libdiagnose_usb\",\n    \"libbase\",\n    \"libcutils\",\n    \"libgtest\",\n    \"libgtest_main\",\n    \"libbase\",\n    \"libadb_host\",\n    \"libtinyxml2\",\n    \"libsparse\",\n    \"liblp\",\n    \"libcrypto\",\n    \"libext4_utils\",\n  ],\n\n  stl: \"libc++_static\",\n\n  // Static libs (libfastboot2) shared library dependencies are not transitively included\n  // This is needed to avoid link time errors when building for mac\n  target: {\n    darwin: {\n      host_ldlibs: [\n          \"-framework CoreFoundation\",\n          \"-framework IOKit\",\n      ],\n    },\n  },\n\n  // Disable auto-generation of test config as this binary itself is not a test in the test suites,\n  // rather it is used by other tests.\n  auto_gen_config: false,\n  test_suites: [\n    \"general-tests\",\n    \"vts\",\n  ],\n}\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/README.md",
    "content": "# Fuzzy Fastboot\n\nFuzzy Fastboot (FF) is a standalone automated conformance and penetration tester for\nvalidating device-side fastboot protocol implementations.\nThe tool is completely generic, and uses a simple extensible XML\nconfiguration file to auto-generate device-specific tests for any device.\nAny Android device that uses the fastboot protocol should have fuzzy fastboot run on it prior to\nrelease to find implementation bugs, make sure it conforms to the fastboot spec,\nand that it safely handles malicious inputs.\n\n\n## Background\nThe [fastboot protocol](../README.md) provides an easy way to manage low level\naspects of the device directly from bootloader. However, with great power comes\ngreat responsibility. An improper or insecure fastboot implementation can\nopen the possibility for critical security exploits on the bootloader via fastboot\ncommands. Furthermore, an untrustworthy or insecure bootloader means nothing that is\neither directly or indirectly bootstrapped by the bootloader can be trusted (including Android).\nBy checking a bootloader's conformance to the fastboot spec, as well as make sure\nnefarious/malformed input is properly and gracefully handled, easy exploits of a\ndevice's bootloaders can be mitigated.\n\nAdditionally, since the fastboot tool itself must support a myriad of fastboot\nimplementations, it is important to make sure an implementation is conforming to\navoid potential incompatibilities with the fastboot command line tool itself.\nThus, Fuzzy Fastboot also checks for proper conformance to the spec.\n\n## Overview\nFuzzy Fastboot is written in C++ and uses [Google Test](https://github.com/google/googletest)\nfor the underlying test framework. This means that Fuzzy Fastboot supports all of\ngtest's command line flags and options.\n\nAdditionally, by using gtest it makes it extremely easy to add additional C++ based\ntests to Fuzzy Fastboot. However, in most cases the optional device specific\nXML configuration file that is provided to Fuzzy Fastboot supports the necessary\nfeatures and hooks for testing device specific commands/features\nwithout touching the underlying C++.\n\n### Generic Tests\nWithout a provided device XML configuration, Fuzzy Fastboot can only perform\nsome basic tests that are generic to any fastboot device. These generic tests are\ndivided into several test suite categories:\n\n1. **USBFunctionality** - Test USB communication\n2. **Conformance** - Test the device properly handles well-formed fastboot commands\n3. **UnlockPermissions** - Test commands only allowed in the unlocked state work\n4. **LockPermissions** - Test commands only not allowed in the locked state are rejected\n5. **Fuzz** - Test malicious and/or ill-formed commands are properly and gracefully handled\n\n\n### XML Generated Tests\nWith a provided XML device configuration, Fuzzy Fastboot will be able to generate\nmany more additional tests cases.\n\nThe device config XML has five element pairs all inside a root level `<config>`:\n\n#### `<getvar>` Element\nInside the `<getvar></getvar>` element pairs, one should list all the device's getvar\nvariables, with an associated ECMAScript regex you wish the returned variable to match on.\nEach tested variable should appear in a `<var key=\"key\" assert=\"regex\"/>` format.\nFor example:\n```xml\n<getvar>\n  <var key=\"product\" assert=\"superphone2000\"/>\n  <var key=\"secure\" assert=\"no|yes\"/>\n  <var key=\"battery-voltage\" assert=\"[34][[:digit:]]{3}\"/>\n  <!-- etc...  -->\n</getvar>\n```\n\n#### `<partitions>` Element\nInside the `<partitions></partitions>` element pairs, one should list all the device's\npartitions. Each device partition has should be put inside a `<part/>` element.\nThe `<part/>` element supports the following attributes:\n\n\n| Attribute | Value          | Purpose                                                                                     | Default  |\n|-----------|----------------|---------------------------------------------------------------------------------------------|----------|\n| value     | Partition name | The name of the partition                                                                   | Required |\n| slots     | \"yes\" or \"no\"  | Is this partition is slotted                                                                | \"no\"     |\n| test      | \"yes\" or \"no\"  | Is Fuzzy Fastboot is allowed to generate tests that overwrite this partition                | Required |\n| hashable  | \"yes\" or \"no\"  | Is this partition hashable with the hash command specified in `<checksum>`                  | \"yes\"    |\n| parsed    | \"yes\" or \"no\"  | Does the bootloader parse this partition, such as look for a header, look for magic, etc... | \"no\"     |\n\nFor example:\n```xml\n<!-- All the device partitions should be listed here -->\n<partitions>\n  <part value=\"boot\" slots=\"yes\" test=\"yes\" hashable=\"yes\" parsed=\"yes\"/>\n  <part value=\"modem\" slots=\"yes\" test=\"yes\" hashable=\"yes\"/>\n  <part value=\"userdata\" slots=\"no\" test=\"yes\" hashable=\"no\"/>\n  <!-- etc...  -->\n</partitions>\n```\n\n#### `<packed>` Element\nMost devices have pseudo partitions, such as a `bootloader` partition,\nthat in reality is composed of several real partitions.\nWhen one of these pseudo partitions is flashed, the bootloader\nwill internally expand the image into the individual images for each underlying\npartition. These pseudo partitions should be listed inside a `<part></part>`\nelement pair. Each element `<part>` has a mandatory attribute `value`,\nwhich lists the name of this pseudo partition, and a `slots` attribute,\nwhich can be yes or no if this pseudo partition is slotted.\nAdditionally, inside the `<part></part>` element pair, one should list\nall the real partition that make up this pseudo partition inside of\n`<child>PART_NAME</child>` element pairs.\nAn example is should below:\n\n```xml\n<!-- All the device packed partitions should be listed here -->\n<packed>\n\t<part value=\"bootloader\" slots=\"yes\">\n\t\t<!-- We list the real partitions it is composed of -->\n\t\t<child>foo1</child>\n\t\t<child>foo2</child>\n\t\t<child>bar3</child>\n\t\t<!-- We list tests, expect defaults to 'okay' -->\n\t\t<test packed=\"bootloader.img\" unpacked=\"unpacked\"/>\n\t\t<test packed=\"bootloader_garbage.img\" expect=\"fail\"/>\n\t</part>\n</packed>\n```\n\nYou might notice there are additional `<test/>` elements as well contained inside of\na `<part></part>` pair. This is because Fuzzy Fastboot allows (and recommends) one to specify\nvalid and invalid test packed images for flashing this particular pseudo partition.\nAdditionally, one should specify a folder with all the partitions' images\nthat the packed image unpacks to. If your device supports hashing partitions, this\nwill allow Fuzzy Fastboot to validate the images are unpacking correctly, and\nthe correct slots are being flashed.\n\nEach `<test/>` element has the following supported attributes:\n\n| Attribute | Value | Purpose | Default |\n|-----------|---------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------|\n| packed | The name of the packed test image | The image uploaded to the device. It is searched for in dir if --search_path=dir | Required |\n| unpacked | The name of the directory containing the unpacked version of packed | Searched for in dir if --search_path=dir. This folder should have the all the images that packed unpacks to. The name of each of the images should be the name of the real partition it is flashed to. | Required if expect != \"fail\" |\n| expect | \"okay\" or \"fail\" | If uploading a invalid or garbage image the bootloader should reject use \"fail\" otherwise \"okay\" | \"okay\" |\n\n\n#### `<oem>` Element\nVendors can extend the fastboot protocol with oem commands. This allows vendors\nto support device/vendor specific features/commands over the fastboot protocol.\nFuzzy Fastboot allows testing these oem commands as well.\n\nOem commands are specefied in `<command></command>` element pairs. Each command\nelement supports the following attributes:\n\n\n| Attribute   | Value                | Purpose                                                       | Default  |\n|-------------|----------------------|---------------------------------------------------------------|----------|\n| value       | The oem command name | Ex: if value=\"foo\", the oem command will start with \"oem foo\" | Required |\n| permissions | \"none\" or \"unlocked\" | Whether the bootloader must be \"unlocked\" to perform command  | \"none\"   |\n\nAn example is should below:\n```xml\n<oem>\n  <command value=\"self_destruct\" permissions=\"unlocked\">\n    <!-- This will test that \"oem self_destruct now\" returns 'okay' -->\n    <test value=\"now\" expect=\"okay\"/>\n    <!-- This will test that \"oem self_destruct yesterday\" returns 'fail' -->\n    <test value=\"yesterday\" expect=\"fail\" />\n  </command>\n\n  <command value=\"foobar\" permissions=\"unlocked\">\n    <!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->\n    <test value=\"use_staged\" expect=\"okay\" input=\"test_image.img\" />\n    <!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->\n    <test value=\"send_response\" expect=\"fail\" validate=\"python validator.py\"/>\n  </command>\n<oem/>\n```\n\nAgain you will notice that one can, and should, specify tests to run with `<test/>` elements.\nThe test elements support the following attributes:\n\n\n| Attribute | Value                                            | Purpose                                                                                                                                                                                                                                                                                                                                                                                                                                            | Default                    |\n|-----------|--------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------|\n| value     | The oem command argument                         | Ex: if value=\"bar\", and the oem command name was \"foo\", the full command will be \"oem foo bar\"                                                                                                                                                                                                                                                                                                                                                     | Empty String (no argument) |\n| expect    | \"okay\" or \"fail\"                                 | Whether the bootloader should accept or reject this command                                                                                                                                                                                                                                                                                                                                                                                        | \"okay\"                     |\n| input     | A image filename                                 | Some oem commands require staging files before the command is executed                                                                                                                                                                                                                                                                                                                                                                             | Empty String (no argument) |\n| validate  | A program/script to run to validate the response | Some oem commands will stage data that can be downloaded afterwards and should be validated to be correct. Fuzzy Fastboot will launch the validation program with the first arg the oem command executed, the second arg  the path to the downloaded image. Ex: \"python validate.py'. If the program has a non-zero  return code, the validation is marked as failed and anything from the launched programs stderr is logged in the test failure. | Empty String (no argument) |\n| assert    | A Regular expression                             | In the \"okay\" or \"fail\" response, Fuzzy Fastboot will assert the  response matches this regular expression.                                                                                                                                                                                                                                                                                                                                        | Empty String (no argument) |\n| output    | The name of the saved file                       | This is the name of the saved output file passed to the validation script. It is saved in whatever location is specified by the --output_path argument                                                                                                                                                                                                                                                                                             | out.img                    |\n\n\n#### `<checksum/>` Element\nIf the bootloader supports hashing partitions (implementing this is strongly recommended), Fuzzy Fastboot can\nuse it to do a bunch more testing. Make sure this hash is a cryptographically secure hash, as a non-secure one\nmight reveal secrets about the partitions' contents.\n\nThe checksum element has no children and only two required attributes:\n\n- **value** - The command that hashes a partition. Note that the name of the partition will be appended to the end of the command. For example, if `value=\"oem hash\"`, hashing the partition `bar` would be issued with `oem hash bar`.\n- **parser** - How the hash is returned is up to the vendor's implementation. It could be part of the `OKAY` response, or be encoded in `INFO` responses. Thus, the parser attribute is used to specify a program/script that will extract the hash. The first argument to the program will be the be the response from `OKAY`, the second argument will be all the `INFO` responses joined by newlines. The extracted hash should be sent back to Fuzzy Fastboot as a string written to stderr, and a return code of 0 to signal the parsing was successful. In the case of failure, return a non-zero return code, an optionally an associated error message written to stderr.\n\n\n\n## Full Example XML Configuration\nHere is a basic example configuration. This can also be found in the 'example' folder\nas well as the associated python scripts 'checksum_parser.py' (used to extract partition hash),\nand 'validator.py' (used to validate an oem command that returns data).\n```xml\n<?xml version=\"1.0\"?>\n<config>\n<!-- All the device getvar variables should be listed here -->\n<getvar>\n\t<var key=\"product\" assert=\"superphone2000\"/>\n\t<var key=\"secure\" assert=\"no|yes\"/>\n</getvar>\n\n<!-- All the device partitions should be listed here -->\n<partitions>\n\t<part value=\"boot\" slots=\"yes\" test=\"yes\" hashable=\"yes\" parsed=\"yes\"/>\n\t<part value=\"modem\" slots=\"yes\" test=\"yes\" hashable=\"yes\"/>\n\t<part value=\"userdata\" slots=\"no\" test=\"yes\" hashable=\"no\"/>\n\n\t<!-- Bootloader partitions -->\n\t<part value=\"foo1\" slots=\"yes\" test=\"no\" hashable=\"yes\"/>\n\t<part value=\"foo2\" slots=\"yes\" test=\"no\" hashable=\"yes\"/>\n\t<part value=\"bar3\" slots=\"yes\" test=\"no\" hashable=\"yes\"/>\n</partitions>\n\n<!-- All the device packed partitions should be listed here -->\n<packed>\n\t<part value=\"bootloader\" slots=\"yes\">\n\t\t<!-- We list the real partitions it is composed of -->\n\t\t<child>foo1</child>\n\t\t<child>foo2</child>\n\t\t<child>bar3</child>\n\t\t<!-- We list tests, expect defaults to 'okay' -->\n\t\t<test packed=\"bootloader.img\" unpacked=\"unpacked\"/>\n\t\t<test packed=\"bootloader_garbage.img\" expect=\"fail\"/>\n\t</part>\n</packed>\n\n<!-- All the oem commands should be listed here -->\n<oem>\n\t<!-- The 'oem self_destruct' command requires an unlocked bootloader -->\n\t<command value=\"self_destruct\" permissions=\"unlocked\">\n\t\t<!-- This will test that \"oem self_destruct now\" returns 'okay' -->\n\t\t<test value=\"now\" expect=\"okay\"/>\n\t\t<test value=\"yesterday\" expect=\"fail\" />\n\t</command>\n\n\t<!-- Test a fictional 'oem get' command -->\n\t<command value=\"get\" permissions=\"none\">\n\t\t<test value=\"batch_id\" expect=\"okay\" assert=\"[[:digit:]]+\"/>\n\t\t<test value=\"device_color\" expect=\"okay\" assert=\"green|blue\"/>\n\t\t<test value=\"build_num\" expect=\"okay\" assert=\"[\\w\\-.]+\"/>\n\t\t<test value=\"garbage\" expect=\"fail\" assert=\"Invalid var '[\\w ]+'\"/>\n\t</command>\n\n\t<!-- Some oem commands might require staging or downloading data, or both -->\n\t<command value=\"foobar\" permissions=\"unlocked\">\n\t\t<!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->\n\t\t<test value=\"use_staged\" expect=\"okay\" input=\"test_image.img\" />\n\t\t<!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->\n\t\t<test value=\"send_response\" expect=\"fail\" validate=\"python validator.py\"/>\n\t</command>\n</oem>\n\n<!-- If there is a custom oem checksum command to hash partitions, add it here -->\n<checksum value=\"oem sha1sum\"/>\n</config>\n\n```\n\n## Running Fuzzy Fastboot\nFuzzy Fastboot is built with the fastboot tool itself. It will appear in `out/host/linux-x86/testcases/fuzzy_fastboot/x86_64`.\n\n### Command Line Arguments\n- **--config=**: Specify the name of the configuration XML file. If omitted, only the generic tests will be available.\n- **--search_path=**: Specify the path where Fuzzy Fastboot will look for files referenced in the XML. This includes all the test images and the referenced programs/scripts. This is also where the --config is searched for. If this argument is omitted it defaults to the current directory.\n- **--output_path**: Some oem tests can download an image to the host for validation. This is the location where that image is stored. This deafults to '/tmp'.\n- **--serial_port**: Many devices have a UART or serial log, that reports logging information. Fuzzy Fastboot can include this logging information in the backtraces it generates. This can make debugging far easier. If your device has this, it can be specified with the path to the tty device. Ex: \"/dev/ttyUSB0\".\n- **--gtest_***: Any valid gtest argument (they all start with 'gtest_')\n- **-h**: Print gtest's help message\n\n\n## Using Fuzzy Fastboot on my Device\nAll Fuzzy Fastboot tests should pass on your device. No test should be able to\ncrash the bootloader. Invalid input MUST be handled gracefully. Using \"asserts\"\nor panicking on invalid or malformed input is not an acceptable way to handle\nthese tests, as ungraceful forced termination of the bootloader can expose\nvulnerabilities and leave the device in a bad state.\n\nThe following is the recommended workflow for using Fuzzy Fastboot on a new device:\n\n### Step 1: Pass the generic Conformance tests\nBegin with just the generic tests (i.e. no XML file). In particular, make sure all\nthe conformance tests are passing before you move on. All other tests require that\nthe basic generic conformance tests all pass for them to be valid. The conformance\ntests can be run with `./fuzzy_fastboot --gtest_filter=Conformance.*`.\n\n#### Understanding and Fixing Failed Tests\nWhenever a test fails, it will print out to the console the reason for failure\nand the lines and file where the error happened. At the end of each failure\nblock, there will usually be a message that Fuzzy Fastboot reports to gtest\nexplaining what went wrong. An example is shown below:\n\n```\nExpected equality of these values:\n  resp\n    Which is: \"no\"\n  unlock ? \"yes\" : \"no\"\n    Which is: \"yes\"\ngetvar:unlocked response was not 'no' or 'yes': no\nsystem/core/fastboot/fuzzy_fastboot/fixtures.cpp:227: Failure\nExpected: SetLockState(UNLOCKED) doesn't generate new fatal failures in the current thread.\n  Actual: it does.\n[THERE WILL BE A MESSAGE HERE EXPLAINING WHY IT FAILED]\n```\n\nIn most cases this message at the bottom is all that is needed to figure out why it failed.\nIf this is not enough information, below this, gtest will also print out a human readable\nbacktrace of the underlying fastboot commands leading up the failure in this test.\nHere is an example:\n```\n<<<<<<<< TRACE BEGIN >>>>>>>>>\n[WRITE 0ms](15 bytes): \"getvar:unlocked\"\n[READ 20ms](6 bytes): \"OKAYno\"\n<<<<<<<< TRACE END >>>>>>>>>\n```\nOne can easily see the reason for the failure was the test expected the device to\nbe unlocked.\n\nIf it is still unclear why the failure is happening, the last thing to do is look\nat what line number and file is generating the error. Gtest will always print this out.\nYou can then manually look through Fuzzy Fastboot's test source code, and figure out\nwhat went wrong.\n\n\n### Step 2: Pass all the other generic tests\nRun all the other generic tests (still no XML file). A list of all of them can be\nprinted out with: \"./fuzzy_fastboot --gtest_list_tests\". As before, \"--gtest_filter\"\ncan be used to select certain tests to run, once you figure out which ones failed.\n\nOne particular set of tests to watch out for are the ones that involve USB resets.\nUSB resets effectively unplug and replug the device in software. Fuzzy Fastboot,\nexpects USB resets to cancel whatever transaction is currently going on.\nThis is also how Fuzzy Fastboot attempts to recover from errors when the device is\nunresponsive.\n\n### Step 3: Create a device XML configuration\nWithout a device specific configuration file, Fuzzy Fastboot will have poor test\ncoverage of your device. The vast majority of tests are auto-generated via the XML\nconfiguration file. Use the guide above to generate a configuration for your device.\nMake sure to be as thorough as possible, and list everything in the configuration\nthat can be tested. Finally, make sure that the packed pseudo partitions and\noem commands all have provided test cases. Be sure to test both the positive case\n(i.e. with valid input), as well as the opposite. Make sure the failure tests\nhave good coverage by thinking about all the ways invalid and malicious inputs\ncould be formed. These means creating images with malformed headers, illegal chars,\nand other evil inputs.\n\nNow run fuzzy_fastboot with the supplied configuration file. If you do \"--gtest_list_tests\",\nyou should see a ton more tests that were autogenerated by Fuzzy Fastboot.\nAs before, run these tests till everything passes. Again, use \"--gtest_filter\"\nto select specific tests to run once you know what fail,\nas running the whole things with a large configuration can take over 30 minutes.\nSee the gtest documentation, for nifty tricks and command line options.\n\n### Step 4: Figure out what Fuzzy Fastboot can't/isn't testing\nWhile Fuzzy Fastboot with a XML configuration file, should provide good test coverage.\nThink about what device specific things are not being tested, and test them manually.\nIn particular, things that if not handled carefully could create security exploits.\nDon't be lazy here, as you already put in the time to get this far.\n\n### Step 5: Celebrate\nYou're done :). Now you can be more confident that your implementation is sound, and\nhave piece of mind knowing you are protecting the users' security and data by\nrunning these tests. Don't get too complacent. If the bootloader's source code\nis modified in a way that could introduce bugs or security issues. Make sure to\ntest again. You might have to add to your existing configuration file.\n\n## Limitations and Warnings\n- Currently this only works on Linux (even if it builds on Mac)\n- Only fastboot over USB is currently supported\n- Passing these tests does not mean there are not bugs/security issues. For example, a buffer overrun might not always trigger a crash or have any noticeable side effects.\n- **Be extremely careful of the Fuzzy Fastboot tests you are running. Know exactly what the tests do you are about to run before you run them. It is very possible to brick a device with many of these tests.**\n\n## Fuzzy Fastboot Missing Features TODO's\nThe following are missing features that should eventually be added\n- *Sparse Image Tests*: Currently there are no tests that tests sparse images. Both well-formed and malicious images need to be tested.\n- *Unlocking/Locking Critical*: Currently there are no tests that tests that locking/unlocking critical functionality.\n- *Saved Test Log*: Fuzzy Fastboot should be able to create a failure log for every failing test and save it to a file. This file should include the test information, the reason it failed, and the fastboot command trace (with the serial console logs). Currently it just prints it to the console at the end of every test.\n- *Host Side Hashing*: One should be able to provide the hashing algorithm to the Fuzzy Fastboot, so it can be checked to agree with what the device is reporting.\n\n\n## Author\nAaron Wisner - awisner@google.com\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/example/checksum_parser.py",
    "content": "'''\nSome bootloader's support hashing partitions. This is a great feature for testing\ncorrectness. However, the format for the way the hash is returned depends on the\nimplementation. The hash could be send through an INFO response, or be as part\nof the OKAY response itself. This script is called with the first argument\nas the string mesage from the okay response. The second argument is each\ninfo response joined by newlines into one argument.\n'''\n\nimport sys\n\n\ndef main():\n  '''\n  Data is sent back to the parent fuzzy_fastboot process through the stderr pipe.\n  There are two interpretations of this data by FF.\n\n  0 return code:\n    Anything written to STDERR will be interpreted as part of the hash.\n\n  non-zero return code:\n    Anything written to STDERR is part of the error message that will logged by FF\n    to explain why hash extraction failed.\n\n  Feel free to print to to STDOUT with print() as usual to print info to console\n  '''\n  script, response, info = sys.argv\n  # the info responses are concated by newlines\n  infos = [s.strip() for s in info.splitlines()]\n  sys.stderr.write(infos[-1])\n  print(\"Extracted checksum: '%s'\" % infos[-1])\n  # non-zero return code signals error\n  return 0\n\n\nif __name__ == \"__main__\":\n  sys.exit(main())\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/example/config.xml",
    "content": "<?xml version=\"1.0\"?>\n<config>\n<!-- All the device getvar variables should be listed here -->\n<getvar>\n\t<var key=\"product\" assert=\"superphone2000\"/>\n\t<var key=\"secure\" assert=\"no|yes\"/>\n</getvar>\n\n<!-- All the device partitions should be listed here -->\n<partitions>\n\t<part value=\"boot\" slots=\"yes\" test=\"yes\" hashable=\"yes\" parsed=\"yes\"/>\n\t<part value=\"modem\" slots=\"yes\" test=\"yes\" hashable=\"yes\"/>\n\t<part value=\"userdata\" slots=\"no\" test=\"yes\" hashable=\"no\"/>\n\n\t<!-- Bootloader partitions -->\n\t<part value=\"foo1\" slots=\"yes\" test=\"no\" hashable=\"yes\"/>\n\t<part value=\"foo2\" slots=\"yes\" test=\"no\" hashable=\"yes\"/>\n\t<part value=\"bar3\" slots=\"yes\" test=\"no\" hashable=\"yes\"/>\n</partitions>\n\n<!-- All the device packed partitions should be listed here -->\n<packed>\n\t<part value=\"bootloader\" slots=\"yes\">\n\t\t<!-- We list the real partitions it is composed of -->\n\t\t<child>foo1</child>\n\t\t<child>foo2</child>\n\t\t<child>bar3</child>\n\t\t<!-- We list tests, expect defaults to 'okay' -->\n\t\t<test packed=\"bootloader.img\" unpacked=\"unpacked\"/>\n\t\t<test packed=\"bootloader_garbage.img\" expect=\"fail\"/>\n\t</part>\n</packed>\n\n<!-- All the oem commands should be listed here -->\n<oem>\n\t<!-- The 'oem self_destruct' command requires an unlocked bootloader -->\n\t<command value=\"self_destruct\" permissions=\"unlocked\">\n\t\t<!-- This will test that \"oem self_destruct now\" returns 'okay' -->\n\t\t<test value=\"now\" expect=\"okay\"/>\n\t\t<test value=\"yesterday\" expect=\"fail\" />\n\t</command>\n\n\t<!-- Test a fictional 'oem get' command -->\n\t<command value=\"get\" permissions=\"none\">\n\t\t<test value=\"batch_id\" expect=\"okay\" assert=\"[[:digit:]]+\"/>\n\t\t<test value=\"device_color\" expect=\"okay\" assert=\"green|blue\"/>\n\t\t<test value=\"build_num\" expect=\"okay\" assert=\"[\\w\\-.]+\"/>\n\t\t<test value=\"garbage\" expect=\"fail\" assert=\"Invalid var '[\\w ]+'\"/>\n\t</command>\n\n\t<!-- Some oem commands might require staging or downloading data, or both -->\n\t<command value=\"foobar\" permissions=\"unlocked\">\n\t\t<!-- FF will first stage test_image.img before running 'oem foobar use_staged' -->\n\t\t<test value=\"use_staged\" expect=\"okay\" input=\"test_image.img\" />\n\t\t<!-- FF will run 'oem foobar send_response', upload data from device, then run the validator script -->\n\t\t<test value=\"send_response\" expect=\"fail\" validate=\"python validator.py\"/>\n\t</command>\n</oem>\n\n<!-- If there is a custom oem checksum command to hash partitions, add it here -->\n<checksum value=\"oem sha1sum\"/>\n</config>\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/example/validator.py",
    "content": "'''\nThis is an example validator to be used with oem commands that allow you to\nupload data afterwards that you wish to validate locally.\n'''\nimport sys\n\ndef eprint(msg):\n  '''\n  A helper function for logging error messages to fuzzy_fastboot\n  Use this function as you would \"print()\"\n  '''\n  sys.stderr.write(msg + '\\n')\n\n\ndef main():\n  '''\n  Data is sent back to the parent fuzzy_fastboot process through the stderr pipe.\n\n  If this script has a non-zero return code, anything written to STDERR is part of\n  the error message that will logged by FF to explain why this validation failed.\n\n  Feel free to print to to STDOUT with print() as usual to print info to console\n  '''\n  script, command, fname = sys.argv\n  eprint(\"Messages here will go to the parent testers logs\")\n  eprint(\"Hello world\")\n  print(\"This goes to stdout as expected\")\n  with open(fname, \"rb\") as fd:\n    # Do some validation on the buffer\n    pass\n\n  # non-zero return code signals error\n  return -1\n\n\nif __name__ == \"__main__\":\n  sys.exit(main())\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/extensions.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <csignal>\n#include <cstdlib>\n#include <fstream>\n\n#include \"extensions.h\"\n#include \"test_utils.h\"\n#include \"tinyxml2.h\"\n\nnamespace fastboot {\nnamespace extension {\n\nnamespace {  // private to this file\n\n// Since exceptions are disabled, a bad regex will trigger an abort in the constructor\n// We at least need to print something out\nstd::regex MakeRegex(const std::string& regex_str, int line_num,\n                     std::regex_constants::syntax_option_type type = std::regex::ECMAScript) {\n    // The signal handler can only access static vars\n    static std::string err_str;\n    err_str = android::base::StringPrintf(\"'%s' is not a valid regex string (line %d)\\n\",\n                                          regex_str.c_str(), line_num);\n    const auto sighandler = [](int) {\n        int nbytes = write(fileno(stderr), err_str.c_str(), err_str.length());\n        static_cast<void>(nbytes);  // need to supress the unused nbytes/ or unused result\n    };\n    std::signal(SIGABRT, sighandler);\n    // Now attempt to create the regex\n    std::regex ret(regex_str, type);\n    // unregister\n    std::signal(SIGABRT, SIG_DFL);\n\n    return ret;\n}\n\nbool XMLAssert(bool cond, const tinyxml2::XMLElement* elem, const char* msg) {\n    if (!cond) {\n        printf(\"%s (line %d)\\n\", msg, elem->GetLineNum());\n    }\n    return !cond;\n}\n\nconst std::string XMLAttribute(const tinyxml2::XMLElement* elem, const std::string key,\n                               const std::string key_default = \"\") {\n    if (!elem->Attribute(key.c_str())) {\n        return key_default;\n    }\n\n    return elem->Attribute(key.c_str());\n}\n\nbool XMLYesNo(const tinyxml2::XMLElement* elem, const std::string key, bool* res,\n              bool def = false) {\n    if (!elem->Attribute(key.c_str())) {\n        *res = def;\n        return true;\n    }\n    const std::string val = elem->Attribute(key.c_str());\n    if (val != \"yes\" && val != \"no\") {\n        return false;\n    }\n    *res = (val == \"yes\");\n    return true;\n}\n\nbool ExtractPartitions(tinyxml2::XMLConstHandle handle, Configuration* config) {\n    // Extract partitions\n    const tinyxml2::XMLElement* part = handle.FirstChildElement(\"part\").ToElement();\n    while (part) {\n        Configuration::PartitionInfo part_info;\n        const std::string name = XMLAttribute(part, \"value\");\n        const std::string test = XMLAttribute(part, \"test\");\n        if (XMLAssert(!name.empty(), part, \"The name of a partition can not be empty\") ||\n            XMLAssert(XMLYesNo(part, \"slots\", &part_info.slots), part,\n                      \"Slots attribute must be 'yes' or 'no'\") ||\n            XMLAssert(XMLYesNo(part, \"hashable\", &part_info.hashable, true), part,\n                      \"Hashable attribute must be 'yes' or 'no'\") ||\n            XMLAssert(XMLYesNo(part, \"parsed\", &part_info.parsed), part,\n                      \"Parsed attribute must be 'yes' or 'no'\"))\n            return false;\n\n        bool allowed = test == \"yes\" || test == \"no-writes\" || test == \"no\";\n        if (XMLAssert(allowed, part, \"The test attribute must be 'yes' 'no-writes' or 'no'\"))\n            return false;\n        if (XMLAssert(config->partitions.find(name) == config->partitions.end(), part,\n                      \"The same partition name is listed twice\"))\n            return false;\n        part_info.test = (test == \"yes\")\n                                 ? Configuration::PartitionInfo::YES\n                                 : (test == \"no-writes\") ? Configuration::PartitionInfo::NO_WRITES\n                                                         : Configuration::PartitionInfo::NO;\n        config->partitions[name] = part_info;\n        part = part->NextSiblingElement(\"part\");\n    }\n    return true;\n}\n\nbool ExtractPacked(tinyxml2::XMLConstHandle handle, Configuration* config) {\n    // Extract partitions\n    const tinyxml2::XMLElement* part = handle.FirstChildElement(\"part\").ToElement();\n    while (part) {\n        Configuration::PackedInfo packed_info;\n        const std::string name = XMLAttribute(part, \"value\");\n\n        if (XMLAssert(!name.empty(), part, \"The name of a packed partition can not be empty\") ||\n            XMLAssert(XMLYesNo(part, \"slots\", &packed_info.slots), part,\n                      \"Slots attribute must be 'yes' or 'no'\") ||\n            XMLAssert(config->partitions.find(name) == config->partitions.end(), part,\n                      \"A packed partition can not have same name as a real one\") ||\n            XMLAssert(config->partitions.find(name) == config->partitions.end(), part,\n                      \"The same partition name is listed twice\"))\n            return false;\n\n        // Extract children partitions\n        const tinyxml2::XMLElement* child = part->FirstChildElement(\"child\")\n                                                    ? part->FirstChildElement(\"child\")->ToElement()\n                                                    : nullptr;\n        while (child) {\n            const std::string text(child->GetText());\n            // Make sure child exists\n            if (XMLAssert(config->partitions.find(text) != config->partitions.end(), child,\n                          \"The child partition was not listed in <partitions>\"))\n                return false;\n            packed_info.children.insert(text);\n            child = child->NextSiblingElement(\"child\");\n        }\n\n        // Extract tests\n        const tinyxml2::XMLElement* test = part->FirstChildElement(\"test\")\n                                                   ? part->FirstChildElement(\"test\")->ToElement()\n                                                   : nullptr;\n        while (test) {\n            Configuration::PackedInfoTest packed_test;\n            packed_test.packed_img = XMLAttribute(test, \"packed\");\n            packed_test.unpacked_dir = XMLAttribute(test, \"unpacked\");\n            const std::string expect = XMLAttribute(test, \"expect\", \"okay\");\n\n            if (XMLAssert(!packed_test.packed_img.empty(), test,\n                          \"The packed image location must be specified\") ||\n                XMLAssert(CMD_EXPECTS.find(expect) != CMD_EXPECTS.end(), test,\n                          \"Expect attribute must be 'okay' or 'fail'\"))\n                return false;\n\n            packed_test.expect = CMD_EXPECTS.at(expect);\n            // The expect is only unpacked directory is only needed if success\n            if (packed_test.expect == OKAY &&\n                XMLAssert(!packed_test.unpacked_dir.empty(), test,\n                          \"The unpacked image folder location must be specified\"))\n                return false;\n\n            packed_info.tests.push_back(packed_test);\n            test = test->NextSiblingElement(\"test\");\n        }\n\n        config->packed[name] = packed_info;\n        part = part->NextSiblingElement(\"part\");\n    }\n    return true;\n}\n\nbool ExtractGetVars(tinyxml2::XMLConstHandle handle, Configuration* config) {\n    // Extract getvars\n    const tinyxml2::XMLElement* var = handle.FirstChildElement(\"var\").ToElement();\n    while (var) {\n        const std::string key = XMLAttribute(var, \"key\");\n        const std::string reg = XMLAttribute(var, \"assert\");\n        if (XMLAssert(key.size(), var, \"The var key name is empty\")) return false;\n        if (XMLAssert(config->getvars.find(key) == config->getvars.end(), var,\n                      \"The same getvar variable name is listed twice\"))\n            return false;\n        Configuration::GetVar getvar{reg, MakeRegex(reg, var->GetLineNum()), var->GetLineNum()};\n        config->getvars[key] = std::move(getvar);\n        var = var->NextSiblingElement(\"var\");\n    }\n    return true;\n}\n\nbool ExtractOem(tinyxml2::XMLConstHandle handle, Configuration* config) {\n    // Extract getvars\n    // Extract oem commands\n    const tinyxml2::XMLElement* command = handle.FirstChildElement(\"command\").ToElement();\n    while (command) {\n        const std::string cmd = XMLAttribute(command, \"value\");\n        const std::string permissions = XMLAttribute(command, \"permissions\");\n        if (XMLAssert(cmd.size(), command, \"Empty command value\")) return false;\n        if (XMLAssert(permissions == \"none\" || permissions == \"unlocked\", command,\n                      \"Permissions attribute must be 'none' or 'unlocked'\"))\n            return false;\n\n        // Each command has tests\n        std::vector<Configuration::CommandTest> tests;\n        const tinyxml2::XMLElement* test = command->FirstChildElement(\"test\");\n        while (test) {  // iterate through tests\n            Configuration::CommandTest ctest;\n\n            ctest.line_num = test->GetLineNum();\n            const std::string default_name = \"XMLTest-line-\" + std::to_string(test->GetLineNum());\n            ctest.name = XMLAttribute(test, \"name\", default_name);\n            ctest.arg = XMLAttribute(test, \"value\");\n            ctest.input = XMLAttribute(test, \"input\");\n            ctest.output = XMLAttribute(test, \"output\");\n            ctest.validator = XMLAttribute(test, \"validate\");\n            ctest.regex_str = XMLAttribute(test, \"assert\");\n\n            const std::string expect = XMLAttribute(test, \"expect\");\n\n            if (XMLAssert(CMD_EXPECTS.find(expect) != CMD_EXPECTS.end(), test,\n                          \"Expect attribute must be 'okay' or 'fail'\"))\n                return false;\n            ctest.expect = CMD_EXPECTS.at(expect);\n            std::regex regex;\n            if (expect == \"okay\" && ctest.regex_str.size()) {\n                ctest.regex = MakeRegex(ctest.regex_str, test->GetLineNum());\n            }\n            tests.push_back(std::move(ctest));\n            test = test->NextSiblingElement(\"test\");\n        }\n\n        // Build the command struct\n        const Configuration::OemCommand oem_cmd{permissions == \"unlocked\", std::move(tests)};\n        config->oem[cmd] = std::move(oem_cmd);\n\n        command = command->NextSiblingElement(\"command\");\n    }\n    return true;\n}\n\nbool ExtractChecksum(tinyxml2::XMLConstHandle handle, Configuration* config) {\n    const tinyxml2::XMLElement* checksum = handle.ToElement();\n    if (checksum && checksum->Attribute(\"value\")) {\n        config->checksum = XMLAttribute(checksum, \"value\");\n        config->checksum_parser = XMLAttribute(checksum, \"parser\");\n        if (XMLAssert(config->checksum_parser != \"\", checksum,\n                      \"A checksum parser attribute is mandatory\"))\n            return false;\n    }\n    return true;\n}\n\n}  // anonymous namespace\n\nbool ParseXml(const std::string& file, Configuration* config) {\n    tinyxml2::XMLDocument doc;\n    if (doc.LoadFile(file.c_str())) {\n        printf(\"Failed to open/parse XML file '%s'\\nXMLError: %s\\n\", file.c_str(), doc.ErrorStr());\n        return false;\n    }\n\n    tinyxml2::XMLConstHandle handle(&doc);\n    tinyxml2::XMLConstHandle root(handle.FirstChildElement(\"config\"));\n\n    // Extract the getvars\n    if (!ExtractGetVars(root.FirstChildElement(\"getvar\"), config)) {\n        return false;\n    }\n    // Extract the partition info\n    if (!ExtractPartitions(root.FirstChildElement(\"partitions\"), config)) {\n        return false;\n    }\n\n    // Extract packed\n    if (!ExtractPacked(root.FirstChildElement(\"packed\"), config)) {\n        return false;\n    }\n\n    // Extract oem commands\n    if (!ExtractOem(root.FirstChildElement(\"oem\"), config)) {\n        return false;\n    }\n\n    // Extract checksum\n    if (!ExtractChecksum(root.FirstChildElement(\"checksum\"), config)) {\n        return false;\n    }\n\n    return true;\n}\n\n}  // namespace extension\n}  // namespace fastboot\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/extensions.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#pragma once\n\n#include <regex>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\nnamespace fastboot {\nnamespace extension {\n\nenum Expect { OKAY = 0, FAIL, DATA };\n\nstatic const std::unordered_map<std::string, Expect> CMD_EXPECTS = {\n        {\"okay\", OKAY},\n        {\"fail\", FAIL},\n        {\"data\", DATA},\n};\n\nstatic const std::unordered_map<Expect, std::string> EXPECTS_STR = {\n        {OKAY, \"okay\"},\n        {FAIL, \"fail\"},\n        {DATA, \"data\"},\n};\n\nstruct Configuration {\n    struct GetVar {\n        std::string regex_str;\n        std::regex regex;\n        int line_num;\n\n        // So gtest can print me\n        friend ::std::ostream& operator<<(::std::ostream& os, const GetVar& self) {\n            return os << \"<regex='\" << self.regex_str << \"' line_num=\" << self.line_num << \">\";\n        }\n    };\n    struct PartitionInfo {\n        enum TestConfig { NO = 0, NO_WRITES, YES };\n        bool hashable;\n        bool slots;   // Does it have slots\n        bool parsed;  // Does the bootloader do parsing on the img?\n        TestConfig test;\n\n        // So gtest can print me\n        friend ::std::ostream& operator<<(::std::ostream& os, const PartitionInfo& pinfo) {\n            return os << \"<hashable=\" << pinfo.hashable << \" slots=\" << pinfo.slots\n                      << \" parsed=\" << pinfo.parsed << \">\";\n        }\n    };\n\n    struct PackedInfoTest {\n        Expect expect;  // Does it have slots\n        std::string packed_img;\n        std::string unpacked_dir;\n\n        // So gtest can print me\n        friend ::std::ostream& operator<<(::std::ostream& os, const PackedInfoTest& pinfo) {\n            return os << \"<\"\n                      << \"expect=\" << EXPECTS_STR.at(pinfo.expect)\n                      << \" packed_img=\" << pinfo.packed_img\n                      << \" unpacked_dir=\" << pinfo.unpacked_dir << \">\";\n        }\n    };\n\n    struct PackedInfo {\n        bool slots;  // Does it have slots\n        std::unordered_set<std::string> children;\n        std::vector<PackedInfoTest> tests;\n    };\n\n    struct CommandTest {\n        std::string name;\n        int line_num;\n        std::string arg;\n        Expect expect;\n        std::string regex_str;\n        std::regex regex;\n        std::string input;\n        std::string output;\n        std::string validator;\n\n        // So gtest can print me\n        friend ::std::ostream& operator<<(::std::ostream& os, const CommandTest& self) {\n            return os << \"test: \" << self.name << \" (line: \" << self.line_num << \")\";\n        }\n    };\n\n    struct OemCommand {\n        bool restricted;  // Does device need to be unlocked?\n        std::vector<CommandTest> tests;\n    };\n\n    std::unordered_map<std::string, GetVar> getvars;\n    std::unordered_map<std::string, PartitionInfo> partitions;\n    std::unordered_map<std::string, PackedInfo> packed;\n    std::unordered_map<std::string, OemCommand> oem;\n\n    std::string checksum;\n    std::string checksum_parser;\n};\n\nbool ParseXml(const std::string& file, Configuration* config);\n\n}  // namespace extension\n}  // namespace fastboot\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/fixtures.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#include <errno.h>\n#include <fcntl.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <termios.h>\n#include <unistd.h>\n#include <chrono>\n#include <cstdlib>\n#include <fstream>\n#include <map>\n#include <random>\n#include <regex>\n#include <set>\n#include <thread>\n#include <vector>\n\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <gtest/gtest.h>\n\n#include \"fastboot_driver.h\"\n#include \"tcp.h\"\n#include \"usb.h\"\n\n#include \"extensions.h\"\n#include \"fixtures.h\"\n#include \"test_utils.h\"\n#include \"transport_sniffer.h\"\n\nusing namespace std::literals::chrono_literals;\n\nnamespace fastboot {\n\nint FastBootTest::MatchFastboot(usb_ifc_info* info, const std::string& local_serial) {\n    if (info->ifc_class != 0xff || info->ifc_subclass != 0x42 || info->ifc_protocol != 0x03) {\n        return -1;\n    }\n\n    cb_scratch = info->device_path;\n\n    // require matching serial number or device path if requested\n    // at the command line with the -s option.\n    if (!local_serial.empty() && local_serial != info->serial_number &&\n        local_serial != info->device_path)\n        return -1;\n    return 0;\n}\n\nbool FastBootTest::IsFastbootOverTcp() {\n    return android::base::StartsWith(device_serial, \"tcp:\");\n}\n\nbool FastBootTest::UsbStillAvailible() {\n    if (IsFastbootOverTcp()) return true;\n\n    // For some reason someone decided to prefix the path with \"usb:\"\n    std::string prefix(\"usb:\");\n    if (std::equal(prefix.begin(), prefix.end(), device_path.begin())) {\n        std::string fname(device_path.begin() + prefix.size(), device_path.end());\n        std::string real_path =\n                android::base::StringPrintf(\"/sys/bus/usb/devices/%s/serial\", fname.c_str());\n        std::ifstream f(real_path.c_str());\n        return f.good();\n    }\n    exit(-1);  // This should never happen\n    return true;\n}\n\nbool FastBootTest::UserSpaceFastboot() {\n    std::string value;\n    fb->GetVar(\"is-userspace\", &value);\n    return value == \"yes\";\n}\n\nRetCode FastBootTest::DownloadCommand(uint32_t size, std::string* response,\n                                      std::vector<std::string>* info) {\n    return fb->DownloadCommand(size, response, info);\n}\n\nRetCode FastBootTest::SendBuffer(const std::vector<char>& buf) {\n    return fb->SendBuffer(buf);\n}\n\nRetCode FastBootTest::HandleResponse(std::string* response, std::vector<std::string>* info,\n                                     int* dsize) {\n    return fb->HandleResponse(response, info, dsize);\n}\n\nvoid FastBootTest::SetUp() {\n    if (device_path != \"\") {               // make sure the device is still connected\n        ASSERT_TRUE(UsbStillAvailible());  // The device disconnected\n    }\n\n    if (IsFastbootOverTcp()) {\n        ConnectTcpFastbootDevice();\n    } else {\n        const auto matcher = [](usb_ifc_info* info) -> int {\n            return MatchFastboot(info, device_serial);\n        };\n        for (int i = 0; i < MAX_USB_TRIES && !transport; i++) {\n            std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);\n            if (usb)\n                transport = std::unique_ptr<TransportSniffer>(\n                        new TransportSniffer(std::move(usb), serial_port));\n            std::this_thread::sleep_for(std::chrono::milliseconds(10));\n        }\n    }\n\n    ASSERT_TRUE(transport);  // no nullptr\n\n    if (device_path == \"\") {  // We set it the first time, then make sure it never changes\n        device_path = cb_scratch;\n    } else {\n        ASSERT_EQ(device_path, cb_scratch);  // The path can not change\n    }\n    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));\n    // No error checking since non-A/B devices may not support the command\n    fb->GetVar(\"current-slot\", &initial_slot);\n}\n\nvoid FastBootTest::TearDown() {\n    EXPECT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n    // No error checking since non-A/B devices may not support the command\n    fb->SetActive(initial_slot);\n\n    TearDownSerial();\n\n    fb.reset();\n\n    if (transport) {\n        transport.reset();\n    }\n\n    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n}\n\n// TODO, this should eventually be piped to a file instead of stdout\nvoid FastBootTest::TearDownSerial() {\n    if (IsFastbootOverTcp()) return;\n\n    if (!transport) return;\n    // One last read from serial\n    transport->ProcessSerial();\n    if (HasFailure()) {\n        // TODO, print commands leading up\n        printf(\"<<<<<<<< TRACE BEGIN >>>>>>>>>\\n\");\n        printf(\"%s\", transport->CreateTrace().c_str());\n        printf(\"<<<<<<<< TRACE END >>>>>>>>>\\n\");\n        // std::vector<std::pair<const TransferType, const std::vector<char>>>  prev =\n        // transport->Transfers();\n    }\n}\n\nvoid FastBootTest::ConnectTcpFastbootDevice() {\n    for (int i = 0; i < MAX_TCP_TRIES && !transport; i++) {\n        std::string error;\n        std::unique_ptr<Transport> tcp(\n                tcp::Connect(device_serial.substr(4), tcp::kDefaultPort, &error).release());\n        if (tcp)\n            transport = std::unique_ptr<TransportSniffer>(new TransportSniffer(std::move(tcp), 0));\n        if (transport != nullptr) break;\n        std::this_thread::sleep_for(std::chrono::milliseconds(10));\n    }\n}\n\nvoid FastBootTest::ReconnectFastbootDevice() {\n    fb.reset();\n    transport.reset();\n\n    if (IsFastbootOverTcp()) {\n        ConnectTcpFastbootDevice();\n        device_path = cb_scratch;\n        fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));\n        return;\n    }\n\n    while (UsbStillAvailible())\n        ;\n    printf(\"WAITING FOR DEVICE\\n\");\n    // Need to wait for device\n    const auto matcher = [](usb_ifc_info* info) -> int {\n        return MatchFastboot(info, device_serial);\n    };\n    while (!transport) {\n        std::unique_ptr<UsbTransport> usb = usb_open(matcher, USB_TIMEOUT);\n        if (usb) {\n            transport = std::unique_ptr<TransportSniffer>(\n                    new TransportSniffer(std::move(usb), serial_port));\n        }\n        std::this_thread::sleep_for(1s);\n    }\n    device_path = cb_scratch;\n    fb = std::unique_ptr<FastBootDriver>(new FastBootDriver(std::move(transport), {}, true));\n}\n\nvoid FastBootTest::SetLockState(bool unlock, bool assert_change) {\n    if (!fb) {\n        return;\n    }\n\n    // User space fastboot implementations are not allowed to communicate to\n    // secure hardware and hence cannot lock/unlock the device.\n    if (UserSpaceFastboot()) {\n        return;\n    }\n\n    std::string resp;\n    std::vector<std::string> info;\n    // To avoid risk of bricking device, make sure unlock ability is set to 1\n    ASSERT_EQ(fb->RawCommand(\"flashing get_unlock_ability\", &resp, &info), SUCCESS)\n            << \"'flashing get_unlock_ability' failed\";\n\n    // There are two ways this can be reported, through info or the actual response\n    if (!resp.empty()) {  // must be in the info response\n        ASSERT_EQ(resp.back(), '1')\n                << \"Unlock ability must be set to 1 to avoid bricking device, see \"\n                   \"'https://source.android.com/devices/bootloader/unlock-trusty'\";\n    } else {\n        ASSERT_FALSE(info.empty()) << \"'flashing get_unlock_ability' returned empty response\";\n        ASSERT_FALSE(info.back().empty()) << \"Expected non-empty info response\";\n        ASSERT_EQ(info.back().back(), '1')\n                << \"Unlock ability must be set to 1 to avoid bricking device, see \"\n                   \"'https://source.android.com/devices/bootloader/unlock-trusty'\";\n    }\n\n    EXPECT_EQ(fb->GetVar(\"unlocked\", &resp), SUCCESS) << \"getvar:unlocked failed\";\n    ASSERT_TRUE(resp == \"no\" || resp == \"yes\")\n            << \"getvar:unlocked response was not 'no' or 'yes': \" + resp;\n\n    if ((unlock && resp == \"no\") || (!unlock && resp == \"yes\")) {\n        std::string cmd = unlock ? \"unlock\" : \"lock\";\n        ASSERT_EQ(fb->RawCommand(\"flashing \" + cmd, &resp), SUCCESS)\n                << \"Attempting to change locked state, but 'flashing\" + cmd + \"' command failed\";\n        printf(\"PLEASE RESPOND TO PROMPT FOR '%sing' BOOTLOADER ON DEVICE\\n\", cmd.c_str());\n        ReconnectFastbootDevice();\n        if (assert_change) {\n            ASSERT_EQ(fb->GetVar(\"unlocked\", &resp), SUCCESS) << \"getvar:unlocked failed\";\n            ASSERT_EQ(resp, unlock ? \"yes\" : \"no\")\n                    << \"getvar:unlocked response was not 'no' or 'yes': \" + resp;\n        }\n        printf(\"SUCCESS\\n\");\n    }\n}\n\nstd::string FastBootTest::device_path = \"\";\nstd::string FastBootTest::cb_scratch = \"\";\nstd::string FastBootTest::initial_slot = \"\";\nint FastBootTest::serial_port = 0;\nstd::string FastBootTest::device_serial = \"\";\n\ntemplate <bool UNLOCKED>\nvoid ModeTest<UNLOCKED>::SetUp() {\n    ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());\n    ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));\n}\n// Need to instatiate it, so linker can find it later\ntemplate class ModeTest<true>;\ntemplate class ModeTest<false>;\n\nvoid Fuzz::TearDown() {\n    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n\n    TearDownSerial();\n\n    std::string tmp;\n    if (fb->GetVar(\"product\", &tmp) != SUCCESS) {\n        printf(\"DEVICE UNRESPONSE, attempting to recover...\");\n        transport->Reset();\n        printf(\"issued USB reset...\");\n\n        if (fb->GetVar(\"product\", &tmp) != SUCCESS) {\n            printf(\"FAIL\\n\");\n            exit(-1);\n        }\n        printf(\"SUCCESS!\\n\");\n    }\n\n    if (transport) {\n        transport.reset();\n    }\n\n    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n}\n\ntemplate <bool UNLOCKED>\nvoid ExtensionsPartition<UNLOCKED>::SetUp() {\n    ASSERT_NO_FATAL_FAILURE(FastBootTest::SetUp());\n    ASSERT_NO_FATAL_FAILURE(SetLockState(UNLOCKED));\n\n    if (!fb) {\n        return;\n    }\n    const std::string name = GetParam().first;\n\n    std::string var;\n    ASSERT_EQ(fb->GetVar(\"slot-count\", &var), SUCCESS) << \"Getting slot count failed\";\n    int32_t num_slots = strtol(var.c_str(), nullptr, 10);\n    real_parts = GeneratePartitionNames(name, GetParam().second.slots ? num_slots : 0);\n\n    ASSERT_EQ(fb->GetVar(\"partition-size:\" + real_parts.front(), &var), SUCCESS)\n            << \"Getting partition size failed\";\n    part_size = strtoll(var.c_str(), nullptr, 16);\n    ASSERT_GT(part_size, 0) << \"Partition size reported was invalid\";\n\n    ASSERT_EQ(fb->GetVar(\"max-download-size\", &var), SUCCESS) << \"Getting max download size failed\";\n    max_dl = strtoll(var.c_str(), nullptr, 16);\n    ASSERT_GT(max_dl, 0) << \"Max download size reported was invalid\";\n\n    max_flash = std::min(part_size, max_dl);\n}\ntemplate class ExtensionsPartition<true>;\ntemplate class ExtensionsPartition<false>;\n\n}  // end namespace fastboot\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/fixtures.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#pragma once\n#include <gtest/gtest.h>\n\n#include \"fastboot_driver.h\"\n\n#include \"extensions.h\"\n#include \"transport_sniffer.h\"\n\nnamespace fastboot {\n\nconst int USB_TIMEOUT = 30000;\n\nconstexpr char USB_PORT_GONE[] =\n        \"The USB port has disappeared, this is usually due to the bootloader crashing\";\n\nclass FastBootTest : public testing::Test {\n  public:\n    static int serial_port;\n    static std::string device_serial;\n    static constexpr int MAX_USB_TRIES = 10;\n    static constexpr int MAX_TCP_TRIES = 6000;\n\n    static int MatchFastboot(usb_ifc_info* info, const std::string& local_serial = \"\");\n    static bool IsFastbootOverTcp();\n    bool UsbStillAvailible();\n    bool UserSpaceFastboot();\n    void ReconnectFastbootDevice();\n    void ConnectTcpFastbootDevice();\n\n  protected:\n    RetCode DownloadCommand(uint32_t size, std::string* response = nullptr,\n                            std::vector<std::string>* info = nullptr);\n\n    RetCode SendBuffer(const std::vector<char>& buf);\n    RetCode HandleResponse(std::string* response = nullptr,\n                           std::vector<std::string>* info = nullptr, int* dsize = nullptr);\n\n    void SetUp() override;\n    void TearDown() override;\n    void TearDownSerial();\n    void SetLockState(bool unlock, bool assert_change = true);\n\n    std::unique_ptr<TransportSniffer> transport;\n    std::unique_ptr<FastBootDriver> fb;\n\n  private:\n    // This is an annoying hack\n    static std::string cb_scratch;\n    static std::string device_path;\n    static std::string initial_slot;\n};\n\ntemplate <bool UNLOCKED>\nclass ModeTest : public FastBootTest {\n  protected:\n    void SetUp() override;\n};\n\nclass Fuzz : public ModeTest<true> {\n  protected:\n    void TearDown() override;\n};\n\n// These derived classes without overrides serve no purpose other than to allow gtest to name them\n// differently\nclass BasicFunctionality : public ModeTest<true> {};\nclass Conformance : public ModeTest<true> {};\nclass LogicalPartitionCompliance : public ModeTest<true> {};\nclass UnlockPermissions : public ModeTest<true> {};\nclass LockPermissions : public ModeTest<false> {};\n\n// Magic C++ double inheritance\nclass ExtensionsGetVarConformance\n    : public ModeTest<true>,\n      public ::testing::WithParamInterface<\n              std::pair<std::string, extension::Configuration::GetVar>> {};\n\nclass ExtensionsOemConformance\n    : public ModeTest<true>,\n      public ::testing::WithParamInterface<\n              std::tuple<std::string, bool, extension::Configuration::CommandTest>> {};\n\nclass ExtensionsPackedValid\n    : public ModeTest<true>,\n      public ::testing::WithParamInterface<\n              std::pair<std::string, extension::Configuration::PackedInfoTest>> {};\n\nclass ExtensionsPackedInvalid\n    : public ModeTest<true>,\n      public ::testing::WithParamInterface<\n              std::pair<std::string, extension::Configuration::PackedInfoTest>> {};\n\ntemplate <bool UNLOCKED>\nclass ExtensionsPartition\n    : public FastBootTest,\n      public ::testing::WithParamInterface<\n              std::pair<std::string, extension::Configuration::PartitionInfo>> {\n  protected:\n    void SetUp() override;\n    int64_t part_size;\n    int64_t max_flash;\n    int64_t max_dl;\n    std::vector<std::string> real_parts;  // includes the slots\n};\n\nclass AnyPartition : public ExtensionsPartition<true> {};\nclass WriteablePartition : public ExtensionsPartition<true> {};\nclass WriteHashablePartition : public ExtensionsPartition<true> {};\nclass WriteHashNonParsedPartition : public ExtensionsPartition<true> {};\n\nclass FuzzWriteablePartition : public ExtensionsPartition<true> {};\nclass FuzzWriteableParsedPartition : public ExtensionsPartition<true> {};\nclass FuzzAnyPartitionLocked : public ExtensionsPartition<false> {};\n\nclass UserdataPartition : public ExtensionsPartition<true> {};\n\nclass SparseTestPartition : public ExtensionsPartition<true> {};\n\n}  // end namespace fastboot\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/main.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#include <errno.h>\n#include <fcntl.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <algorithm>\n#include <chrono>\n#include <cstdlib>\n#include <fstream>\n#include <map>\n#include <random>\n#include <regex>\n#include <set>\n#include <thread>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <gtest/gtest.h>\n#include <sparse/sparse.h>\n\n#include \"constants.h\"\n#include \"fastboot_driver.h\"\n#include \"usb.h\"\n\n#include \"extensions.h\"\n#include \"fixtures.h\"\n#include \"test_utils.h\"\n#include \"transport_sniffer.h\"\n\nnamespace fastboot {\n\nextension::Configuration config;  // The parsed XML config\n\nstd::string SEARCH_PATH;\nstd::string OUTPUT_PATH;\n\n// gtest's INSTANTIATE_TEST_CASE_P() must be at global scope,\n// so our autogenerated tests must be as well\nstd::vector<std::pair<std::string, extension::Configuration::GetVar>> GETVAR_XML_TESTS;\nstd::vector<std::tuple<std::string, bool, extension::Configuration::CommandTest>> OEM_XML_TESTS;\nstd::vector<std::pair<std::string, extension::Configuration::PartitionInfo>> PARTITION_XML_TESTS;\nstd::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>\n        PARTITION_XML_WRITEABLE;\nstd::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>\n        PARTITION_XML_WRITE_HASHABLE;\nstd::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>\n        PARTITION_XML_WRITE_PARSED;\nstd::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>\n        PARTITION_XML_WRITE_HASH_NONPARSED;\nstd::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>\n        PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE;\nstd::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>\n        PACKED_XML_SUCCESS_TESTS;\nstd::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;\n// This only has 1 or zero elements so it will disappear from gtest when empty\nstd::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>\n        SINGLE_PARTITION_XML_WRITE_HASHABLE;\n\nconst std::string DEFAULT_OUPUT_NAME = \"out.img\";\n// const char scratch_partition[] = \"userdata\";\nconst std::vector<std::string> CMDS{\"boot\",    \"continue\", \"download:\",   \"erase:\", \"flash:\",\n                                    \"getvar:\", \"reboot\",   \"set_active:\", \"upload\"};\n\n// For pretty printing we need all these overloads\n::std::ostream& operator<<(::std::ostream& os, const RetCode& ret) {\n    return os << FastBootDriver::RCString(ret);\n}\n\nbool PartitionHash(FastBootDriver* fb, const std::string& part, std::string* hash, int* retcode,\n                   std::string* err_msg) {\n    if (config.checksum.empty()) {\n        return -1;\n    }\n\n    std::string resp;\n    std::vector<std::string> info;\n    const std::string cmd = config.checksum + ' ' + part;\n    RetCode ret;\n    if ((ret = fb->RawCommand(cmd, &resp, &info)) != SUCCESS) {\n        *err_msg =\n                android::base::StringPrintf(\"Hashing partition with command '%s' failed with: %s\",\n                                            cmd.c_str(), fb->RCString(ret).c_str());\n        return false;\n    }\n    std::stringstream imploded;\n    std::copy(info.begin(), info.end(), std::ostream_iterator<std::string>(imploded, \"\\n\"));\n\n    // If payload, we validate that as well\n    const std::vector<std::string> args = SplitBySpace(config.checksum_parser);\n    std::vector<std::string> prog_args(args.begin() + 1, args.end());\n    prog_args.push_back(resp);                          // Pass in the full command\n    prog_args.push_back(SEARCH_PATH + imploded.str());  // Pass in the save location\n\n    int pipe;\n    pid_t pid = StartProgram(args[0], prog_args, &pipe);\n    if (pid <= 0) {\n        *err_msg = android::base::StringPrintf(\"Launching hash parser '%s' failed with: %s\",\n                                               config.checksum_parser.c_str(), strerror(errno));\n        return false;\n    }\n    *retcode = WaitProgram(pid, pipe, hash);\n    if (*retcode) {\n        // In this case the stderr pipe is a log message\n        *err_msg = android::base::StringPrintf(\"Hash parser '%s' failed with: %s\",\n                                               config.checksum_parser.c_str(), hash->c_str());\n        return false;\n    }\n\n    return true;\n}\n\nbool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {\n    int64_t len = sparse_file_len(sf, true, with_crc);\n    if (len <= 0) {\n        return false;\n    }\n    out->clear();\n    auto cb = [](void* priv, const void* data, size_t len) {\n        auto vec = static_cast<std::vector<char>*>(priv);\n        const char* cbuf = static_cast<const char*>(data);\n        vec->insert(vec->end(), cbuf, cbuf + len);\n        return 0;\n    };\n\n    return !sparse_file_callback(sf, true, with_crc, cb, out);\n}\n\n// Only allow alphanumeric, _, -, and .\nconst auto not_allowed = [](char c) -> int {\n    return !(isalnum(c) || c == '_' || c == '-' || c == '.');\n};\n\n// Test that USB even works\nTEST(USBFunctionality, USBConnect) {\n    const auto matcher = [](usb_ifc_info* info) -> int {\n        return FastBootTest::MatchFastboot(info, fastboot::FastBootTest::device_serial);\n    };\n    std::unique_ptr<Transport> transport;\n    for (int i = 0; i < FastBootTest::MAX_USB_TRIES && !transport; i++) {\n        transport = usb_open(matcher);\n        std::this_thread::sleep_for(std::chrono::milliseconds(10));\n    }\n    ASSERT_NE(transport.get(), nullptr) << \"Could not find the fastboot device after: \"\n                                        << 10 * FastBootTest::MAX_USB_TRIES << \"ms\";\n    if (transport) {\n        transport->Close();\n    }\n}\n\n// Test commands related to super partition\nTEST_F(LogicalPartitionCompliance, SuperPartition) {\n    ASSERT_TRUE(UserSpaceFastboot());\n    std::string partition_type;\n    // getvar partition-type:super must fail for retrofit devices because the\n    // partition does not exist.\n    if (fb->GetVar(\"partition-type:super\", &partition_type) == SUCCESS) {\n        std::string is_logical;\n        EXPECT_EQ(fb->GetVar(\"is-logical:super\", &is_logical), SUCCESS)\n                << \"getvar is-logical:super failed\";\n        EXPECT_EQ(is_logical, \"no\") << \"super must not be a logical partition\";\n        std::string super_name;\n        EXPECT_EQ(fb->GetVar(\"super-partition-name\", &super_name), SUCCESS)\n                << \"'getvar super-partition-name' failed\";\n        EXPECT_EQ(super_name, \"super\") << \"'getvar super-partition-name' must return 'super' for \"\n                                          \"device with a super partition\";\n    }\n}\n\n// Test 'fastboot getvar is-logical'\nTEST_F(LogicalPartitionCompliance, GetVarIsLogical) {\n    ASSERT_TRUE(UserSpaceFastboot());\n    std::string has_slot;\n    EXPECT_EQ(fb->GetVar(\"has-slot:system\", &has_slot), SUCCESS) << \"getvar has-slot:system failed\";\n    std::string is_logical_cmd_system = \"is-logical:system\";\n    std::string is_logical_cmd_vendor = \"is-logical:vendor\";\n    std::string is_logical_cmd_boot = \"is-logical:boot\";\n    if (has_slot == \"yes\") {\n        std::string current_slot;\n        ASSERT_EQ(fb->GetVar(\"current-slot\", &current_slot), SUCCESS)\n                << \"getvar current-slot failed\";\n        std::string slot_suffix = \"_\" + current_slot;\n        is_logical_cmd_system += slot_suffix;\n        is_logical_cmd_vendor += slot_suffix;\n        is_logical_cmd_boot += slot_suffix;\n    }\n    std::string is_logical;\n    EXPECT_EQ(fb->GetVar(is_logical_cmd_system, &is_logical), SUCCESS)\n            << \"system must be a logical partition\";\n    EXPECT_EQ(is_logical, \"yes\");\n    EXPECT_EQ(fb->GetVar(is_logical_cmd_vendor, &is_logical), SUCCESS)\n            << \"vendor must be a logical partition\";\n    EXPECT_EQ(is_logical, \"yes\");\n    EXPECT_EQ(fb->GetVar(is_logical_cmd_boot, &is_logical), SUCCESS)\n            << \"boot must not be logical partition\";\n    EXPECT_EQ(is_logical, \"no\");\n}\n\nTEST_F(LogicalPartitionCompliance, FastbootRebootTest) {\n    ASSERT_TRUE(UserSpaceFastboot());\n    GTEST_LOG_(INFO) << \"Rebooting back to fastbootd mode\";\n    fb->RebootTo(\"fastboot\");\n\n    ReconnectFastbootDevice();\n    ASSERT_TRUE(UserSpaceFastboot());\n}\n\n// Testing creation/resize/delete of logical partitions\nTEST_F(LogicalPartitionCompliance, CreateResizeDeleteLP) {\n    ASSERT_TRUE(UserSpaceFastboot());\n    std::string test_partition_name = \"test_partition\";\n    std::string slot_count;\n    // Add suffix to test_partition_name if device is slotted.\n    EXPECT_EQ(fb->GetVar(\"slot-count\", &slot_count), SUCCESS) << \"getvar slot-count failed\";\n    int32_t num_slots = strtol(slot_count.c_str(), nullptr, 10);\n    if (num_slots > 0) {\n        std::string current_slot;\n        EXPECT_EQ(fb->GetVar(\"current-slot\", &current_slot), SUCCESS)\n                << \"getvar current-slot failed\";\n        std::string slot_suffix = \"_\" + current_slot;\n        test_partition_name += slot_suffix;\n    }\n\n    GTEST_LOG_(INFO) << \"Testing 'fastboot create-logical-partition' command\";\n    EXPECT_EQ(fb->CreatePartition(test_partition_name, \"0\"), SUCCESS)\n            << \"create-logical-partition failed\";\n    GTEST_LOG_(INFO) << \"Testing 'fastboot resize-logical-partition' command\";\n    EXPECT_EQ(fb->ResizePartition(test_partition_name, \"4096\"), SUCCESS)\n            << \"resize-logical-partition failed\";\n    std::vector<char> buf(4096);\n\n    GTEST_LOG_(INFO) << \"Flashing a logical partition..\";\n    EXPECT_EQ(fb->FlashPartition(test_partition_name, buf), SUCCESS)\n            << \"flash logical -partition failed\";\n\n    GTEST_LOG_(INFO) << \"Testing 'fastboot delete-logical-partition' command\";\n    EXPECT_EQ(fb->DeletePartition(test_partition_name), SUCCESS)\n            << \"delete logical-partition failed\";\n}\n\n// Conformance tests\nTEST_F(Conformance, GetVar) {\n    std::string product;\n    EXPECT_EQ(fb->GetVar(\"product\", &product), SUCCESS) << \"getvar:product failed\";\n    EXPECT_NE(product, \"\") << \"getvar:product response was empty string\";\n    EXPECT_EQ(std::count_if(product.begin(), product.end(), not_allowed), 0)\n            << \"getvar:product response contained illegal chars\";\n    EXPECT_LE(product.size(), FB_RESPONSE_SZ - 4) << \"getvar:product response was too large\";\n}\n\nTEST_F(Conformance, GetVarVersionBootloader) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"version-bootloader\", &var), SUCCESS)\n            << \"getvar:version-bootloader failed\";\n    EXPECT_NE(var, \"\") << \"getvar:version-bootloader response was empty string\";\n    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)\n            << \"getvar:version-bootloader response contained illegal chars\";\n    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << \"getvar:version-bootloader response was too large\";\n}\n\nTEST_F(Conformance, GetVarVersionBaseband) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"version-baseband\", &var), SUCCESS) << \"getvar:version-baseband failed\";\n    EXPECT_NE(var, \"\") << \"getvar:version-baseband response was empty string\";\n    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)\n            << \"getvar:version-baseband response contained illegal chars\";\n    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << \"getvar:version-baseband response was too large\";\n}\n\nTEST_F(Conformance, GetVarSerialNo) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"serialno\", &var), SUCCESS) << \"getvar:serialno failed\";\n    EXPECT_NE(var, \"\") << \"getvar:serialno can not be empty string\";\n    EXPECT_EQ(std::count_if(var.begin(), var.end(), isalnum), var.size())\n            << \"getvar:serialno must be alpha-numeric\";\n    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << \"getvar:serialno response is too long\";\n}\n\nTEST_F(Conformance, GetVarSecure) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"secure\", &var), SUCCESS);\n    EXPECT_TRUE(var == \"yes\" || var == \"no\");\n}\n\nTEST_F(Conformance, GetVarOffModeCharge) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"off-mode-charge\", &var), SUCCESS) << \"getvar:off-mode-charge failed\";\n    EXPECT_TRUE(var == \"0\" || var == \"1\") << \"getvar:off-mode-charge response must be '0' or '1'\";\n}\n\nTEST_F(Conformance, GetVarVariant) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"variant\", &var), SUCCESS) << \"getvar:variant failed\";\n    EXPECT_NE(var, \"\") << \"getvar:variant response can not be empty\";\n    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << \"getvar:variant response is too large\";\n}\n\nTEST_F(Conformance, GetVarRevision) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"hw-revision\", &var), SUCCESS) << \"getvar:hw-revision failed\";\n    EXPECT_NE(var, \"\") << \"getvar:battery-voltage response was empty\";\n    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)\n            << \"getvar:hw-revision contained illegal ASCII chars\";\n    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4) << \"getvar:hw-revision response was too large\";\n}\n\nTEST_F(Conformance, GetVarBattVoltage) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"battery-voltage\", &var), SUCCESS) << \"getvar:battery-voltage failed\";\n    EXPECT_NE(var, \"\") << \"getvar:battery-voltage response was empty\";\n    EXPECT_EQ(std::count_if(var.begin(), var.end(), not_allowed), 0)\n            << \"getvar:battery-voltage response contains illegal ASCII chars\";\n    EXPECT_LE(var.size(), FB_RESPONSE_SZ - 4)\n            << \"getvar:battery-voltage response is too large: \" + var;\n}\n\nTEST_F(Conformance, GetVarBattVoltageOk) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"battery-soc-ok\", &var), SUCCESS) << \"getvar:battery-soc-ok failed\";\n    EXPECT_TRUE(var == \"yes\" || var == \"no\") << \"getvar:battery-soc-ok must be 'yes' or 'no'\";\n}\n\nvoid AssertHexUint32(const std::string& name, const std::string& var) {\n    ASSERT_NE(var, \"\") << \"getvar:\" << name << \" responded with empty string\";\n    // This must start with 0x\n    ASSERT_FALSE(isspace(var.front()))\n            << \"getvar:\" << name << \" responded with a string with leading whitespace\";\n    ASSERT_FALSE(var.compare(0, 2, \"0x\"))\n            << \"getvar:\" << name << \" responded with a string that does not start with 0x...\";\n    int64_t size = strtoll(var.c_str(), nullptr, 16);\n    ASSERT_GT(size, 0) << \"'\" + var + \"' is not a valid response from getvar:\" << name;\n    // At most 32-bits\n    ASSERT_LE(size, std::numeric_limits<uint32_t>::max())\n            << \"getvar:\" << name << \" must fit in a uint32_t\";\n    ASSERT_LE(var.size(), FB_RESPONSE_SZ - 4)\n            << \"getvar:\" << name << \" responded with too large of string: \" + var;\n}\n\nTEST_F(Conformance, GetVarDownloadSize) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"max-download-size\", &var), SUCCESS) << \"getvar:max-download-size failed\";\n    AssertHexUint32(\"max-download-size\", var);\n}\n\n// If fetch is supported, getvar:max-fetch-size must return a hex string.\nTEST_F(Conformance, GetVarFetchSize) {\n    std::string var;\n    if (SUCCESS != fb->GetVar(\"max-fetch-size\", &var)) {\n        GTEST_SKIP() << \"getvar:max-fetch-size failed\";\n    }\n    AssertHexUint32(\"max-fetch-size\", var);\n}\n\nTEST_F(Conformance, GetVarAll) {\n    std::vector<std::string> vars;\n    EXPECT_EQ(fb->GetVarAll(&vars), SUCCESS) << \"getvar:all failed\";\n    EXPECT_GT(vars.size(), 0) << \"getvar:all did not respond with any INFO responses\";\n    for (const auto& s : vars) {\n        EXPECT_LE(s.size(), FB_RESPONSE_SZ - 4)\n                << \"getvar:all included an INFO response: 'INFO\" + s << \"' which is too long\";\n    }\n}\n\nTEST_F(Conformance, UnlockAbility) {\n    std::string resp;\n    std::vector<std::string> info;\n    // Userspace fastboot implementations do not have a way to get this\n    // information.\n    if (UserSpaceFastboot()) {\n        GTEST_LOG_(INFO) << \"This test is skipped for userspace fastboot.\";\n        return;\n    }\n    EXPECT_EQ(fb->RawCommand(\"flashing get_unlock_ability\", &resp, &info), SUCCESS)\n            << \"'flashing get_unlock_ability' failed\";\n    // There are two ways this can be reported, through info or the actual response\n    char last;\n    if (!resp.empty()) {  // must be in the response\n        last = resp.back();\n    } else {  // else must be in info\n        ASSERT_FALSE(info.empty()) << \"'flashing get_unlock_ability' returned empty response\";\n        ASSERT_FALSE(info.back().empty()) << \"Expected non-empty info response\";\n        last = info.back().back();\n    }\n    ASSERT_TRUE(last == '1' || last == '0') << \"Unlock ability must report '0' or '1' in response\";\n}\n\nTEST_F(Conformance, PartitionInfo) {\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed\";\n    EXPECT_GT(parts.size(), 0)\n            << \"getvar:all did not report any partition-size: through INFO responses\";\n    std::set<std::string> allowed{\"ext4\", \"f2fs\", \"raw\"};\n    for (const auto& p : parts) {\n        EXPECT_GE(std::get<1>(p), 0);\n        std::string part(std::get<0>(p));\n        std::set<std::string> allowed{\"ext4\", \"f2fs\", \"raw\"};\n        std::string resp;\n        EXPECT_EQ(fb->GetVar(\"partition-type:\" + part, &resp), SUCCESS);\n        EXPECT_NE(allowed.find(resp), allowed.end()) << \"getvar:partition-type:\" + part << \" was '\"\n                                                     << resp << \"' this is not a valid type\";\n        const std::string cmd = \"partition-size:\" + part;\n        EXPECT_EQ(fb->GetVar(cmd, &resp), SUCCESS);\n\n        // This must start with 0x\n        EXPECT_FALSE(isspace(resp.front()))\n                << cmd + \" responded with a string with leading whitespace\";\n        EXPECT_FALSE(resp.compare(0, 2, \"0x\"))\n                << cmd + \"responded with a string that does not start with 0x...\";\n        uint64_t size;\n        ASSERT_TRUE(android::base::ParseUint(resp, &size))\n                << \"'\" + resp + \"' is not a valid response from \" + cmd;\n    }\n}\n\nTEST_F(Conformance, Slots) {\n    std::string var;\n    ASSERT_EQ(fb->GetVar(\"slot-count\", &var), SUCCESS) << \"getvar:slot-count failed\";\n    ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())\n            << \"'\" << var << \"' is not all digits which it should be for getvar:slot-count\";\n    int32_t num_slots = strtol(var.c_str(), nullptr, 10);\n\n    // Can't run out of alphabet letters...\n    ASSERT_LE(num_slots, 26) << \"What?! You can't have more than 26 slots\";\n\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed\";\n\n    std::map<std::string, std::set<char>> part_slots;\n    if (num_slots > 0) {\n        EXPECT_EQ(fb->GetVar(\"current-slot\", &var), SUCCESS) << \"getvar:current-slot failed\";\n\n        for (const auto& p : parts) {\n            std::string part(std::get<0>(p));\n            std::regex reg(\"([[:graph:]]*)_([[:lower:]])\");\n            std::smatch sm;\n\n            if (std::regex_match(part, sm, reg)) {  // This partition has slots\n                std::string part_base(sm[1]);\n                std::string slot(sm[2]);\n                EXPECT_EQ(fb->GetVar(\"has-slot:\" + part_base, &var), SUCCESS)\n                        << \"'getvar:has-slot:\" << part_base << \"' failed\";\n                EXPECT_EQ(var, \"yes\") << \"'getvar:has-slot:\" << part_base << \"' was not 'yes'\";\n                EXPECT_TRUE(islower(slot.front()))\n                        << \"'\" << slot.front() << \"' is an invalid slot-suffix for \" << part_base;\n                std::set<char> tmp{slot.front()};\n                part_slots.emplace(part_base, tmp);\n                part_slots.at(part_base).insert(slot.front());\n            } else {\n                EXPECT_EQ(fb->GetVar(\"has-slot:\" + part, &var), SUCCESS)\n                        << \"'getvar:has-slot:\" << part << \"' failed\";\n                EXPECT_EQ(var, \"no\") << \"'getvar:has-slot:\" << part << \"' should be no\";\n            }\n        }\n        // Ensure each partition has the correct slot suffix\n        for (const auto& iter : part_slots) {\n            const std::set<char>& char_set = iter.second;\n            std::string chars;\n            for (char c : char_set) {\n                chars += c;\n                chars += ',';\n            }\n            EXPECT_EQ(char_set.size(), num_slots)\n                    << \"There should only be slot suffixes from a to \" << 'a' + num_slots - 1\n                    << \" instead encountered: \" << chars;\n            for (const char c : char_set) {\n                EXPECT_GE(c, 'a') << \"Encountered invalid slot suffix of '\" << c << \"'\";\n                EXPECT_LT(c, 'a' + num_slots) << \"Encountered invalid slot suffix of '\" << c << \"'\";\n            }\n        }\n    }\n}\n\nTEST_F(Conformance, SetActive) {\n    std::string var;\n    ASSERT_EQ(fb->GetVar(\"slot-count\", &var), SUCCESS) << \"getvar:slot-count failed\";\n    ASSERT_EQ(std::count_if(var.begin(), var.end(), isdigit), var.size())\n            << \"'\" << var << \"' is not all digits which it should be for getvar:slot-count\";\n    int32_t num_slots = strtol(var.c_str(), nullptr, 10);\n\n    // Can't run out of alphabet letters...\n    ASSERT_LE(num_slots, 26) << \"You can't have more than 26 slots\";\n\n    for (char c = 'a'; c < 'a' + num_slots; c++) {\n        const std::string slot(&c, &c + 1);\n        ASSERT_EQ(fb->SetActive(slot), SUCCESS) << \"Set active for slot '\" << c << \"' failed\";\n        ASSERT_EQ(fb->GetVar(\"current-slot\", &var), SUCCESS) << \"getvar:current-slot failed\";\n        EXPECT_EQ(var, slot) << \"getvar:current-slot repots incorrect slot after setting it\";\n    }\n}\n\nTEST_F(Conformance, LockAndUnlockPrompt) {\n    std::string resp;\n    ASSERT_EQ(fb->GetVar(\"unlocked\", &resp), SUCCESS) << \"getvar:unlocked failed\";\n    ASSERT_TRUE(resp == \"yes\" || resp == \"no\")\n            << \"Device did not respond with 'yes' or 'no' for getvar:unlocked\";\n    bool curr = resp == \"yes\";\n    if (UserSpaceFastboot()) {\n        GTEST_LOG_(INFO) << \"This test is skipped for userspace fastboot.\";\n        return;\n    }\n\n    for (int i = 0; i < 2; i++) {\n        std::string action = !curr ? \"unlock\" : \"lock\";\n        printf(\"Device should prompt to '%s' bootloader, select 'no'\\n\", action.c_str());\n        SetLockState(!curr, false);\n        ASSERT_EQ(fb->GetVar(\"unlocked\", &resp), SUCCESS) << \"getvar:unlocked failed\";\n        ASSERT_EQ(resp, curr ? \"yes\" : \"no\") << \"The locked/unlocked state of the bootloader \"\n                                                \"incorrectly changed after selecting no\";\n        printf(\"Device should prompt to '%s' bootloader, select 'yes'\\n\", action.c_str());\n        SetLockState(!curr, true);\n        ASSERT_EQ(fb->GetVar(\"unlocked\", &resp), SUCCESS) << \"getvar:unlocked failed\";\n        ASSERT_EQ(resp, !curr ? \"yes\" : \"no\") << \"The locked/unlocked state of the bootloader \"\n                                                 \"failed to change after selecting yes\";\n        curr = !curr;\n    }\n}\n\nTEST_F(Conformance, SparseBlockSupport0) {\n    // The sparse block size can be any multiple of 4\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"max-download-size\", &var), SUCCESS) << \"getvar:max-download-size failed\";\n    int64_t size = strtoll(var.c_str(), nullptr, 16);\n\n    // It is reasonable to expect it to handle a single dont care block equal to its DL size\n    for (int64_t bs = 4; bs < size; bs <<= 1) {\n        SparseWrapper sparse(bs, bs);\n        ASSERT_TRUE(*sparse) << \"Sparse file creation failed on: \" << bs;\n        EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n        EXPECT_EQ(fb->Flash(\"userdata\"), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n    }\n}\n\nTEST_F(Conformance, SparseBlockSupport1) {\n    // The sparse block size can be any multiple of 4\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"max-download-size\", &var), SUCCESS) << \"getvar:max-download-size failed\";\n    int64_t size = strtoll(var.c_str(), nullptr, 16);\n\n    // handle a packed block to half its max download size block\n    for (int64_t bs = 4; bs < size / 2; bs <<= 1) {\n        SparseWrapper sparse(bs, bs);\n        ASSERT_TRUE(*sparse) << \"Sparse file creation failed on: \" << bs;\n        std::vector<char> buf = RandomBuf(bs);\n        ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)\n                << \"Adding data failed to sparse file: \" << sparse.Rep();\n        EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n        EXPECT_EQ(fb->Flash(\"userdata\"), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n    }\n}\n\n// A single don't care download\nTEST_F(Conformance, SparseDownload0) {\n    SparseWrapper sparse(4096, 4096);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n    EXPECT_EQ(fb->Flash(\"userdata\"), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n}\n\nTEST_F(Conformance, SparseDownload1) {\n    SparseWrapper sparse(4096, 10 * 4096);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    std::vector<char> buf = RandomBuf(4096);\n    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 9), 0)\n            << \"Adding data failed to sparse file: \" << sparse.Rep();\n    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n    EXPECT_EQ(fb->Flash(\"userdata\"), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n}\n\nTEST_F(Conformance, SparseDownload2) {\n    SparseWrapper sparse(4096, 4097);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    std::vector<char> buf = RandomBuf(4096);\n    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)\n            << \"Adding data failed to sparse file: \" << sparse.Rep();\n    std::vector<char> buf2 = RandomBuf(1);\n    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 1), 0)\n            << \"Adding data failed to sparse file: \" << sparse.Rep();\n    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n    EXPECT_EQ(fb->Flash(\"userdata\"), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n}\n\nTEST_F(Conformance, SparseDownload3) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"max-download-size\", &var), SUCCESS) << \"getvar:max-download-size failed\";\n    int size = strtoll(var.c_str(), nullptr, 16);\n\n    SparseWrapper sparse(4096, size);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    // Don't want this to take forever\n    unsigned num_chunks = std::min(1000, size / (2 * 4096));\n    for (int i = 0; i < num_chunks; i++) {\n        std::vector<char> buf;\n        int r = random_int(0, 2);\n        // Three cases\n        switch (r) {\n            case 0:\n                break;  // Dont Care chunnk\n            case 1:     // Buffer\n                buf = RandomBuf(4096);\n                ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), i), 0)\n                        << \"Adding data failed to sparse file: \" << sparse.Rep();\n                break;\n            case 2:  // fill\n                ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, i), 0)\n                        << \"Adding fill to sparse file failed: \" << sparse.Rep();\n                break;\n        }\n    }\n    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n    EXPECT_EQ(fb->Flash(\"userdata\"), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n}\n\nTEST_F(Conformance, SparseVersionCheck) {\n    SparseWrapper sparse(4096, 4096);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    std::vector<char> buf;\n    ASSERT_TRUE(SparseToBuf(*sparse, &buf)) << \"Sparse buffer creation failed\";\n    // Invalid, right after magic\n    buf[4] = 0xff;\n    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << \"Device rejected download command\";\n    ASSERT_EQ(SendBuffer(buf), SUCCESS) << \"Downloading payload failed\";\n\n    // It can either reject this download or reject it during flash\n    if (HandleResponse() != DEVICE_FAIL) {\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing an invalid sparse version should fail \" << sparse.Rep();\n    }\n}\n\nTEST_F(UnlockPermissions, Download) {\n    std::vector<char> buf{'a', 'o', 's', 'p'};\n    EXPECT_EQ(fb->Download(buf), SUCCESS) << \"Download 4-byte payload failed\";\n}\n\nTEST_F(UnlockPermissions, DownloadFlash) {\n    std::vector<char> buf{'a', 'o', 's', 'p'};\n    EXPECT_EQ(fb->Download(buf), SUCCESS) << \"Download failed in unlocked mode\";\n    ;\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed in unlocked mode\";\n}\n\n// If the implementation supports getvar:max-fetch-size, it must also support fetch:vendor_boot*.\nTEST_F(UnlockPermissions, FetchVendorBoot) {\n    std::string var;\n    uint64_t fetch_size;\n    if (fb->GetVar(\"max-fetch-size\", &var) != SUCCESS) {\n        GTEST_SKIP() << \"This test is skipped because fetch is not supported.\";\n    }\n    ASSERT_FALSE(var.empty());\n    ASSERT_TRUE(android::base::ParseUint(var, &fetch_size)) << var << \" is not an integer\";\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed\";\n    for (const auto& [partition, partition_size] : parts) {\n        if (!android::base::StartsWith(partition, \"vendor_boot\")) continue;\n        TemporaryFile fetched;\n\n        uint64_t offset = 0;\n        while (offset < partition_size) {\n            uint64_t chunk_size = std::min(fetch_size, partition_size - offset);\n            auto ret = fb->FetchToFd(partition, fetched.fd, offset, chunk_size);\n            ASSERT_EQ(fastboot::RetCode::SUCCESS, ret)\n                    << \"Unable to fetch \" << partition << \" (offset=\" << offset\n                    << \", size=\" << chunk_size << \")\";\n            offset += chunk_size;\n        }\n    }\n}\n\nTEST_F(LockPermissions, DownloadFlash) {\n    std::vector<char> buf{'a', 'o', 's', 'p'};\n    EXPECT_EQ(fb->Download(buf), SUCCESS) << \"Download failed in locked mode\";\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed in locked mode\";\n    std::string resp;\n    for (const auto& tup : parts) {\n        EXPECT_EQ(fb->Flash(std::get<0>(tup), &resp), DEVICE_FAIL)\n                << \"Device did not respond with FAIL when trying to flash '\" << std::get<0>(tup)\n                << \"' in locked mode\";\n        EXPECT_GT(resp.size(), 0)\n                << \"Device sent empty error message after FAIL\";  // meaningful error message\n    }\n}\n\nTEST_F(LockPermissions, Erase) {\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed\";\n    std::string resp;\n    for (const auto& tup : parts) {\n        EXPECT_EQ(fb->Erase(std::get<0>(tup), &resp), DEVICE_FAIL)\n                << \"Device did not respond with FAIL when trying to erase '\" << std::get<0>(tup)\n                << \"' in locked mode\";\n        EXPECT_GT(resp.size(), 0) << \"Device sent empty error message after FAIL\";\n    }\n}\n\nTEST_F(LockPermissions, SetActive) {\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed\";\n\n    std::string resp;\n    EXPECT_EQ(fb->GetVar(\"slot-count\", &resp), SUCCESS) << \"getvar:slot-count failed\";\n    int32_t num_slots = strtol(resp.c_str(), nullptr, 10);\n\n    for (const auto& tup : parts) {\n        std::string part(std::get<0>(tup));\n        std::regex reg(\"([[:graph:]]*)_([[:lower:]])\");\n        std::smatch sm;\n\n        if (std::regex_match(part, sm, reg)) {  // This partition has slots\n            std::string part_base(sm[1]);\n            for (char c = 'a'; c < 'a' + num_slots; c++) {\n                // We should not be able to SetActive any of these\n                EXPECT_EQ(fb->SetActive(part_base + '_' + c, &resp), DEVICE_FAIL)\n                        << \"set:active:\" << part_base + '_' + c << \" did not fail in locked mode\";\n            }\n        }\n    }\n}\n\nTEST_F(LockPermissions, Boot) {\n    std::vector<char> buf;\n    buf.resize(1000);\n    EXPECT_EQ(fb->Download(buf), SUCCESS) << \"A 1000 byte download failed\";\n    std::string resp;\n    ASSERT_EQ(fb->Boot(&resp), DEVICE_FAIL)\n            << \"The device did not respond with failure for 'boot' when locked\";\n    EXPECT_GT(resp.size(), 0) << \"No error message was returned by device after FAIL\";\n}\n\nTEST_F(LockPermissions, FetchVendorBoot) {\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    EXPECT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed\";\n    for (const auto& [partition, _] : parts) {\n        TemporaryFile fetched;\n        ASSERT_EQ(fb->FetchToFd(partition, fetched.fd, 0, 0), DEVICE_FAIL)\n                << \"fetch:\" << partition << \":0:0 did not fail in locked mode\";\n    }\n}\n\nTEST_F(Fuzz, DownloadSize) {\n    std::string var;\n    EXPECT_EQ(fb->GetVar(\"max-download-size\", &var), SUCCESS) << \"getvar:max-download-size failed\";\n    int64_t size = strtoll(var.c_str(), nullptr, 0);\n    EXPECT_GT(size, 0) << '\\'' << var << \"' is not a valid response for getvar:max-download-size\";\n\n    EXPECT_EQ(DownloadCommand(size + 1), DEVICE_FAIL)\n            << \"Device reported max-download-size as '\" << size\n            << \"' but did not reject a download of \" << size + 1;\n\n    std::vector<char> buf(size);\n    EXPECT_EQ(fb->Download(buf), SUCCESS) << \"Device reported max-download-size as '\" << size\n                                          << \"' but downloading a payload of this size failed\";\n    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n}\n\nTEST_F(Fuzz, DownloadPartialBuf) {\n    std::vector<char> buf{'a', 'o', 's', 'p'};\n    ASSERT_EQ(DownloadCommand(buf.size() + 1), SUCCESS)\n            << \"Download command for \" << buf.size() + 1 << \" bytes failed\";\n\n    std::string resp;\n    RetCode ret = SendBuffer(buf);\n    EXPECT_EQ(ret, SUCCESS) << \"Device did not accept partial payload download\";\n    // Send the partial buffer, then cancel it with a reset\n    EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n\n    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n    // The device better still work after all that if we unplug and replug\n    EXPECT_EQ(fb->GetVar(\"product\", &resp), SUCCESS) << \"getvar:product failed\";\n}\n\nTEST_F(Fuzz, DownloadOverRun) {\n    std::vector<char> buf(1000, 'F');\n    ASSERT_EQ(DownloadCommand(10), SUCCESS) << \"Device rejected download request for 10 bytes\";\n    // There are two ways to handle this\n    // Accept download, but send error response\n    // Reject the download outright\n    std::string resp;\n    RetCode ret = SendBuffer(buf);\n    if (ret == SUCCESS) {\n        // If it accepts the buffer, it better send back an error response\n        EXPECT_EQ(HandleResponse(&resp), DEVICE_FAIL)\n                << \"After sending too large of a payload for a download command, device accepted \"\n                   \"payload and did not respond with FAIL\";\n    } else {\n        EXPECT_EQ(ret, IO_ERROR) << \"After sending too large of a payload for a download command, \"\n                                    \"device did not return error\";\n    }\n\n    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n    // The device better still work after all that if we unplug and replug\n    EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n    EXPECT_EQ(fb->GetVar(\"product\", &resp), SUCCESS)\n            << \"Device did not respond with SUCCESS to getvar:product.\";\n}\n\nTEST_F(Fuzz, DownloadInvalid1) {\n    EXPECT_EQ(DownloadCommand(0), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command 'download:0'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid2) {\n    std::string cmd(\"download:1\");\n    EXPECT_EQ(fb->RawCommand(\"download:1\"), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid3) {\n    std::string cmd(\"download:-1\");\n    EXPECT_EQ(fb->RawCommand(\"download:-1\"), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid4) {\n    std::string cmd(\"download:-01000000\");\n    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid5) {\n    std::string cmd(\"download:-0100000\");\n    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid6) {\n    std::string cmd(\"download:\");\n    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid7) {\n    std::string cmd(\"download:01000000\\0999\", sizeof(\"download:01000000\\0999\"));\n    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid8) {\n    std::string cmd(\"download:01000000\\0dkjfvijafdaiuybgidabgybr\",\n                    sizeof(\"download:01000000\\0dkjfvijafdaiuybgidabgybr\"));\n    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, DownloadInvalid9) {\n    std::string cmd(\"download:2PPPPPPPPPPPPPPPPPPPPPPPPPPPPPP\");\n    EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)\n            << \"Device did not respond with FAIL for malformed download command '\" << cmd << \"'\";\n}\n\nTEST_F(Fuzz, GetVarAllSpam) {\n    auto start = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<double> elapsed;\n    unsigned i = 1;\n    do {\n        std::vector<std::string> vars;\n        ASSERT_EQ(fb->GetVarAll(&vars), SUCCESS) << \"Device did not respond with success after \"\n                                                 << i << \"getvar:all commands in a row\";\n        ASSERT_GT(vars.size(), 0)\n                << \"Device did not send any INFO responses after getvar:all command\";\n        elapsed = std::chrono::high_resolution_clock::now() - start;\n    } while (i++, elapsed.count() < 5);\n}\n\nTEST_F(Fuzz, BadCommandTooLarge) {\n    std::string s = RandomString(FB_COMMAND_SZ + 1, rand_legal);\n    RetCode ret = fb->RawCommand(s);\n    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)\n            << \"Device did not respond with failure after sending length \" << s.size()\n            << \" string of random ASCII chars\";\n    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n    std::string s1 = RandomString(10000, rand_legal);\n    ret = fb->RawCommand(s1);\n    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)\n            << \"Device did not respond with failure after sending length \" << s1.size()\n            << \" string of random ASCII chars\";\n    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n    std::string s2 = RandomString(10000, rand_illegal);\n    ret = fb->RawCommand(s2);\n    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)\n            << \"Device did not respond with failure after sending length \" << s2.size()\n            << \" string of random non-ASCII chars\";\n    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n    std::string s3 = RandomString(10000, rand_char);\n    ret = fb->RawCommand(s3);\n    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)\n            << \"Device did not respond with failure after sending length \" << s3.size()\n            << \" string of random chars\";\n    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n\n    std::string s4 = RandomString(10 * 1024 * 1024, rand_legal);\n    ret = fb->RawCommand(s);\n    EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)\n            << \"Device did not respond with failure after sending length \" << s4.size()\n            << \" string of random ASCII chars \";\n    if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n\n    ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n    std::string resp;\n    EXPECT_EQ(fb->GetVar(\"product\", &resp), SUCCESS) << \"Device is unresponsive to getvar command\";\n}\n\nTEST_F(Fuzz, CommandTooLarge) {\n    for (const std::string& s : CMDS) {\n        std::string rs = RandomString(10000, rand_char);\n        RetCode ret;\n        ret = fb->RawCommand(s + rs);\n        EXPECT_TRUE(ret == DEVICE_FAIL || ret == IO_ERROR)\n                << \"Device did not respond with failure \" << ret << \"after '\" << s + rs << \"'\";\n        if (ret == IO_ERROR) EXPECT_EQ(transport->Reset(), 0) << \"USB reset failed\";\n        ASSERT_TRUE(UsbStillAvailible()) << USB_PORT_GONE;\n        std::string resp;\n        EXPECT_EQ(fb->GetVar(\"product\", &resp), SUCCESS)\n                << \"Device is unresponsive to getvar command\";\n    }\n}\n\nTEST_F(Fuzz, CommandMissingArgs) {\n    for (const std::string& s : CMDS) {\n        if (s.back() == ':') {\n            EXPECT_EQ(fb->RawCommand(s), DEVICE_FAIL)\n                    << \"Device did not respond with failure after '\" << s << \"'\";\n            std::string sub(s.begin(), s.end() - 1);\n            EXPECT_EQ(fb->RawCommand(sub), DEVICE_FAIL)\n                    << \"Device did not respond with failure after '\" << sub << \"'\";\n        } else {\n            std::string rs = RandomString(10, rand_illegal);\n            EXPECT_EQ(fb->RawCommand(rs + s), DEVICE_FAIL)\n                    << \"Device did not respond with failure after '\" << rs + s << \"'\";\n        }\n        std::string resp;\n        EXPECT_EQ(fb->GetVar(\"product\", &resp), SUCCESS)\n                << \"Device is unresponsive to getvar command\";\n    }\n}\n\nTEST_F(Fuzz, SparseZeroLength) {\n    SparseWrapper sparse(4096, 0);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    RetCode ret = fb->Download(*sparse);\n    // Two ways to handle it\n    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing zero length sparse image did not fail: \" << sparse.Rep();\n    }\n    ret = fb->Download(*sparse, true);\n    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing zero length sparse image did not fail \" << sparse.Rep();\n    }\n}\n\nTEST_F(Fuzz, SparseZeroBlkSize) {\n    // handcrafted malform sparse file with zero as block size\n    const std::vector<char> buf = {\n            '\\x3a', '\\xff', '\\x26', '\\xed', '\\x01', '\\x00', '\\x00', '\\x00', '\\x1c', '\\x00', '\\x0c',\n            '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\x01', '\\x00', '\\x00', '\\x00', '\\x01', '\\x00',\n            '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\xc2', '\\xca', '\\x00', '\\x00', '\\x01',\n            '\\x00', '\\x00', '\\x00', '\\x10', '\\x00', '\\x00', '\\x00', '\\x11', '\\x22', '\\x33', '\\x44'};\n\n    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << \"Device rejected download command\";\n    ASSERT_EQ(SendBuffer(buf), SUCCESS) << \"Downloading payload failed\";\n\n    // It can either reject this download or reject it during flash\n    if (HandleResponse() != DEVICE_FAIL) {\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing a zero block size in sparse file should fail\";\n    }\n}\n\nTEST_F(Fuzz, SparseVeryLargeBlkSize) {\n    // handcrafted sparse file with block size of ~4GB and divisible 4\n    const std::vector<char> buf = {\n            '\\x3a', '\\xff', '\\x26', '\\xed', '\\x01', '\\x00', '\\x00', '\\x00', '\\x1c', '\\x00', '\\x0c',\n            '\\x00', '\\xF0', '\\xFF', '\\xFF', '\\xFF', '\\x01', '\\x00', '\\x00', '\\x00', '\\x01', '\\x00',\n            '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\xc3', '\\xca', '\\x00', '\\x00', '\\x01',\n            '\\x00', '\\x00', '\\x00', '\\x0c', '\\x00', '\\x00', '\\x00', '\\x11', '\\x22', '\\x33', '\\x44'};\n\n    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << \"Device rejected download command\";\n    ASSERT_EQ(SendBuffer(buf), SUCCESS) << \"Downloading payload failed\";\n    ASSERT_EQ(HandleResponse(), SUCCESS) << \"Not receive okay\";\n    ASSERT_EQ(fb->Flash(\"userdata\"), SUCCESS) << \"Flashing sparse failed\";\n}\n\nTEST_F(Fuzz, SparseTrimmed) {\n    // handcrafted malform sparse file which is trimmed\n    const std::vector<char> buf = {\n            '\\x3a', '\\xff', '\\x26', '\\xed', '\\x01', '\\x00', '\\x00', '\\x00', '\\x1c', '\\x00', '\\x0c',\n            '\\x00', '\\x00', '\\x10', '\\x00', '\\x00', '\\x00', '\\x00', '\\x08', '\\x00', '\\x01', '\\x00',\n            '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\xc1', '\\xca', '\\x00', '\\x00', '\\x01',\n            '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\x80', '\\x11', '\\x22', '\\x33', '\\x44'};\n\n    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << \"Device rejected download command\";\n    ASSERT_EQ(SendBuffer(buf), SUCCESS) << \"Downloading payload failed\";\n\n    // It can either reject this download or reject it during flash\n    if (HandleResponse() != DEVICE_FAIL) {\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing a trimmed sparse file should fail\";\n    }\n}\n\nTEST_F(Fuzz, SparseInvalidChurk) {\n    // handcrafted malform sparse file with invalid churk\n    const std::vector<char> buf = {\n            '\\x3a', '\\xff', '\\x26', '\\xed', '\\x01', '\\x00', '\\x00', '\\x00', '\\x1c', '\\x00', '\\x0c',\n            '\\x00', '\\x00', '\\x10', '\\x00', '\\x00', '\\x00', '\\x00', '\\x08', '\\x00', '\\x01', '\\x00',\n            '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\x00', '\\xc1', '\\xca', '\\x00', '\\x00', '\\x01',\n            '\\x00', '\\x00', '\\x00', '\\x10', '\\x00', '\\x00', '\\x00', '\\x11', '\\x22', '\\x33', '\\x44'};\n\n    ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << \"Device rejected download command\";\n    ASSERT_EQ(SendBuffer(buf), SUCCESS) << \"Downloading payload failed\";\n\n    // It can either reject this download or reject it during flash\n    if (HandleResponse() != DEVICE_FAIL) {\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing a sparse file with invalid churk should fail\";\n    }\n}\n\nTEST_F(Fuzz, SparseTooManyChunks) {\n    SparseWrapper sparse(4096, 4096);  // 1 block, but we send two chunks that will use 2 blocks\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    std::vector<char> buf = RandomBuf(4096);\n    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)\n            << \"Adding data failed to sparse file: \" << sparse.Rep();\n    // We take advantage of the fact the sparse library does not check this\n    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)\n            << \"Adding fill to sparse file failed: \" << sparse.Rep();\n\n    RetCode ret = fb->Download(*sparse);\n    // Two ways to handle it\n    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing sparse image with 'total_blks' in header 1 too small did not fail \"\n                << sparse.Rep();\n    }\n    ret = fb->Download(*sparse, true);\n    if (ret != DEVICE_FAIL) {  // if lazily parsed it better fail on a flash\n        EXPECT_EQ(fb->Flash(\"userdata\"), DEVICE_FAIL)\n                << \"Flashing sparse image with 'total_blks' in header 1 too small did not fail \"\n                << sparse.Rep();\n    }\n}\n\nTEST_F(Fuzz, USBResetSpam) {\n    auto start = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<double> elapsed;\n    int i = 0;\n    do {\n        ASSERT_EQ(transport->Reset(), 0) << \"USB Reset failed after \" << i << \" resets in a row\";\n        elapsed = std::chrono::high_resolution_clock::now() - start;\n    } while (i++, elapsed.count() < 5);\n    std::string resp;\n    EXPECT_EQ(fb->GetVar(\"product\", &resp), SUCCESS)\n            << \"getvar failed after \" << i << \" USB reset(s) in a row\";\n}\n\nTEST_F(Fuzz, USBResetCommandSpam) {\n    auto start = std::chrono::high_resolution_clock::now();\n    std::chrono::duration<double> elapsed;\n    do {\n        std::string resp;\n        std::vector<std::string> all;\n        ASSERT_EQ(transport->Reset(), 0) << \"USB Reset failed\";\n        EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << \"getvar:all failed after USB reset\";\n        EXPECT_EQ(fb->GetVar(\"product\", &resp), SUCCESS) << \"getvar:product failed\";\n        elapsed = std::chrono::high_resolution_clock::now() - start;\n    } while (elapsed.count() < 10);\n}\n\nTEST_F(Fuzz, USBResetAfterDownload) {\n    std::vector<char> buf;\n    buf.resize(1000000);\n    EXPECT_EQ(DownloadCommand(buf.size()), SUCCESS) << \"Download command failed\";\n    EXPECT_EQ(transport->Reset(), 0) << \"USB Reset failed\";\n    std::vector<std::string> all;\n    EXPECT_EQ(fb->GetVarAll(&all), SUCCESS) << \"getvar:all failed after USB reset.\";\n}\n\n// Getvar XML tests\nTEST_P(ExtensionsGetVarConformance, VarExists) {\n    std::string resp;\n    EXPECT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);\n}\n\nTEST_P(ExtensionsGetVarConformance, VarMatchesRegex) {\n    std::string resp;\n    ASSERT_EQ(fb->GetVar(GetParam().first, &resp), SUCCESS);\n    std::smatch sm;\n    std::regex_match(resp, sm, GetParam().second.regex);\n    EXPECT_FALSE(sm.empty()) << \"The regex did not match\";\n}\n\nINSTANTIATE_TEST_CASE_P(XMLGetVar, ExtensionsGetVarConformance,\n                        ::testing::ValuesIn(GETVAR_XML_TESTS));\n\nTEST_P(AnyPartition, ReportedGetVarAll) {\n    // As long as the partition is reported in INFO, it would be tested by generic Conformance\n    std::vector<std::tuple<std::string, uint64_t>> parts;\n    ASSERT_EQ(fb->Partitions(&parts), SUCCESS) << \"getvar:all failed\";\n    const std::string name = GetParam().first;\n    if (GetParam().second.slots) {\n        auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {\n            return std::get<0>(tup) == name + \"_a\";\n        };\n        EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())\n                << \"partition '\" + name + \"_a' not reported in getvar:all\";\n    } else {\n        auto matcher = [&](const std::tuple<std::string, uint32_t>& tup) {\n            return std::get<0>(tup) == name;\n        };\n        EXPECT_NE(std::find_if(parts.begin(), parts.end(), matcher), parts.end())\n                << \"partition '\" + name + \"' not reported in getvar:all\";\n    }\n}\n\nTEST_P(AnyPartition, Hashable) {\n    const std::string name = GetParam().first;\n    if (!config.checksum.empty()) {  // We can use hash to validate\n        for (const auto& part_name : real_parts) {\n            // Get hash\n            std::string hash;\n            int retcode;\n            std::string err_msg;\n            if (GetParam().second.hashable) {\n                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))\n                        << err_msg;\n                EXPECT_EQ(retcode, 0) << err_msg;\n            } else {  // Make sure it fails\n                const std::string cmd = config.checksum + ' ' + part_name;\n                EXPECT_EQ(fb->RawCommand(cmd), DEVICE_FAIL)\n                        << part_name + \" is marked as non-hashable, but hashing did not fail\";\n            }\n        }\n    }\n}\n\nTEST_P(WriteablePartition, FlashCheck) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n\n    for (const auto& part_name : real_parts) {\n        std::vector<char> buf = RandomBuf(max_flash, rand_char);\n        EXPECT_EQ(fb->FlashPartition(part_name, buf), part_info.parsed ? DEVICE_FAIL : SUCCESS)\n                << \"A partition with an image parsed by the bootloader should reject random \"\n                   \"garbage \"\n                   \"otherwise it should succeed\";\n    }\n}\n\nTEST_P(WriteablePartition, EraseCheck) {\n    const std::string name = GetParam().first;\n\n    for (const auto& part_name : real_parts) {\n        ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n    }\n}\n\nTEST_P(WriteHashNonParsedPartition, EraseZerosData) {\n    const std::string name = GetParam().first;\n\n    for (const auto& part_name : real_parts) {\n        std::string err_msg;\n        int retcode;\n        const std::vector<char> buf = RandomBuf(max_flash, rand_char);\n        // Partition is too big to write to entire thing\n        // This can eventually be supported by using sparse images if too large\n        if (max_flash < part_size) {\n            std::string hash_before, hash_after;\n            ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_NE(hash_before, hash_after)\n                    << \"The partition hash for \" + part_name +\n                               \" did not change after erasing a known value\";\n        } else {\n            std::string hash_zeros, hash_ones, hash_middle, hash_after;\n            const std::vector<char> buf_zeros(max_flash, 0);\n            const std::vector<char> buf_ones(max_flash, -1);  // All bits are set to 1\n            ASSERT_EQ(fb->FlashPartition(part_name, buf_zeros), SUCCESS);\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_zeros, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            ASSERT_EQ(fb->FlashPartition(part_name, buf_ones), SUCCESS);\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_ones, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            ASSERT_NE(hash_zeros, hash_ones)\n                    << \"Hashes of partion should not be the same when all bytes are 0xFF or 0x00\";\n            ASSERT_EQ(fb->FlashPartition(part_name, buf), SUCCESS);\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_middle, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            ASSERT_NE(hash_zeros, hash_middle)\n                    << \"Hashes of partion are the same when all bytes are 0x00 or test payload\";\n            ASSERT_NE(hash_ones, hash_middle)\n                    << \"Hashes of partion are the same when all bytes are 0xFF or test payload\";\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_TRUE(hash_zeros == hash_after || hash_ones == hash_after)\n                    << \"Erasing \" + part_name + \" should set all the bytes to 0xFF or 0x00\";\n        }\n    }\n}\n\n// Only partitions that we can write and hash (name, fixture), TEST_P is (Fixture, test_name)\nINSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashNonParsed, WriteHashNonParsedPartition,\n                        ::testing::ValuesIn(PARTITION_XML_WRITE_HASH_NONPARSED));\n\nINSTANTIATE_TEST_CASE_P(XMLPartitionsWriteHashable, WriteHashablePartition,\n                        ::testing::ValuesIn(PARTITION_XML_WRITE_HASHABLE));\n\n// only partitions writeable\nINSTANTIATE_TEST_CASE_P(XMLPartitionsWriteable, WriteablePartition,\n                        ::testing::ValuesIn(PARTITION_XML_WRITEABLE));\n\n// Every partition\nINSTANTIATE_TEST_CASE_P(XMLPartitionsAll, AnyPartition, ::testing::ValuesIn(PARTITION_XML_TESTS));\n\n// Partition Fuzz tests\nTEST_P(FuzzWriteablePartition, BoundsCheck) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n\n    for (const auto& part_name : real_parts) {\n        // try and flash +1 too large, first erase and get a hash, make sure it does not change\n        std::vector<char> buf = RandomBuf(max_flash + 1);  // One too large\n        if (part_info.hashable) {\n            std::string hash_before, hash_after, err_msg;\n            int retcode;\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"Flashing an image 1 byte too large to \" + part_name + \" did not fail\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(hash_before, hash_after)\n                    << \"Flashing too large of an image resulted in a changed partition hash for \" +\n                               part_name;\n        } else {\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"Flashing an image 1 byte too large to \" + part_name + \" did not fail\";\n        }\n    }\n}\n\nINSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteable, FuzzWriteablePartition,\n                        ::testing::ValuesIn(PARTITION_XML_WRITEABLE));\n\n// A parsed partition should have magic and such that is checked by the bootloader\n// Attempting to flash a random single byte should definately fail\nTEST_P(FuzzWriteableParsedPartition, FlashGarbageImageSmall) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n\n    for (const auto& part_name : real_parts) {\n        std::vector<char> buf = RandomBuf(1);\n        if (part_info.hashable) {\n            std::string hash_before, hash_after, err_msg;\n            int retcode;\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"A parsed partition should fail on a single byte\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(hash_before, hash_after)\n                    << \"Flashing a single byte to parsed partition  \" + part_name +\n                               \" should fail and not change the partition hash\";\n        } else {\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"Flashing a 1 byte image to a parsed partition should fail\";\n        }\n    }\n}\n\nTEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n\n    for (const auto& part_name : real_parts) {\n        std::vector<char> buf = RandomBuf(max_flash);\n        if (part_info.hashable) {\n            std::string hash_before, hash_after, err_msg;\n            int retcode;\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"A parsed partition should not accept randomly generated images\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(hash_before, hash_after)\n                    << \"The hash of the partition has changed after attempting to flash garbage to \"\n                       \"a parsed partition\";\n        } else {\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"A parsed partition should not accept randomly generated images\";\n        }\n    }\n}\n\nTEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge2) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n\n    for (const auto& part_name : real_parts) {\n        std::vector<char> buf(max_flash, -1);  // All 1's\n        if (part_info.hashable) {\n            std::string hash_before, hash_after, err_msg;\n            int retcode;\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"A parsed partition should not accept a image of all 0xFF\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(hash_before, hash_after)\n                    << \"The hash of the partition has changed after attempting to flash garbage to \"\n                       \"a parsed partition\";\n        } else {\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"A parsed partition should not accept a image of all 0xFF\";\n        }\n    }\n}\n\nTEST_P(FuzzWriteableParsedPartition, FlashGarbageImageLarge3) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n\n    for (const auto& part_name : real_parts) {\n        std::vector<char> buf(max_flash, 0);  // All 0's\n        if (part_info.hashable) {\n            std::string hash_before, hash_after, err_msg;\n            int retcode;\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS) << \"Erasing \" + part_name + \" failed\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_before, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"A parsed partition should not accept a image of all 0x00\";\n            ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_after, &retcode, &err_msg))\n                    << err_msg;\n            ASSERT_EQ(retcode, 0) << err_msg;\n            EXPECT_EQ(hash_before, hash_after)\n                    << \"The hash of the partition has changed after attempting to flash garbage to \"\n                       \"a parsed partition\";\n        } else {\n            EXPECT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                    << \"A parsed partition should not accept a image of all 0x00\";\n        }\n    }\n}\n\nINSTANTIATE_TEST_CASE_P(XMLFuzzPartitionsWriteableParsed, FuzzWriteableParsedPartition,\n                        ::testing::ValuesIn(PARTITION_XML_WRITE_PARSED));\n\n// Make sure all attempts to flash things are rejected\nTEST_P(FuzzAnyPartitionLocked, RejectFlash) {\n    std::vector<char> buf = RandomBuf(5);\n    for (const auto& part_name : real_parts) {\n        ASSERT_EQ(fb->FlashPartition(part_name, buf), DEVICE_FAIL)\n                << \"Flashing a partition should always fail in locked mode\";\n    }\n}\n\nINSTANTIATE_TEST_CASE_P(XMLFuzzAnyPartitionLocked, FuzzAnyPartitionLocked,\n                        ::testing::ValuesIn(PARTITION_XML_TESTS));\n\n// Test flashing unlock erases userdata\nTEST_P(UserdataPartition, UnlockErases) {\n    // Get hash after an erase\n    int retcode;\n    std::string err_msg, hash_before, hash_buf, hash_after;\n    ASSERT_EQ(fb->Erase(\"userdata\"), SUCCESS) << \"Erasing uesrdata failed\";\n    ASSERT_TRUE(PartitionHash(fb.get(), \"userdata\", &hash_before, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n\n    // Write garbage\n    std::vector<char> buf = RandomBuf(max_flash / 2);\n    ASSERT_EQ(fb->FlashPartition(\"userdata\", buf), SUCCESS);\n    ASSERT_TRUE(PartitionHash(fb.get(), \"userdata\", &hash_buf, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n\n    // Validity check of hash\n    EXPECT_NE(hash_before, hash_buf)\n            << \"Writing a random buffer to 'userdata' had the same hash as after erasing it\";\n    SetLockState(true);  // Lock the device\n\n    SetLockState(false);  // Unlock the device (should cause erase)\n    ASSERT_TRUE(PartitionHash(fb.get(), \"userdata\", &hash_after, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n\n    EXPECT_NE(hash_after, hash_buf) << \"Unlocking the device did not cause the hash of userdata to \"\n                                       \"change (i.e. it was not erased as required)\";\n    EXPECT_EQ(hash_after, hash_before) << \"Unlocking the device did not produce the same hash of \"\n                                          \"userdata as after doing an erase to userdata\";\n}\n\n// This is a hack to make this test disapeer if there is not a checsum, userdata is not hashable,\n// or userdata is not marked to be writeable in testing\nINSTANTIATE_TEST_CASE_P(XMLUserdataLocked, UserdataPartition,\n                        ::testing::ValuesIn(PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE));\n\n// Packed images test\nTEST_P(ExtensionsPackedValid, TestDeviceUnpack) {\n    const std::string& packed_name = GetParam().first;\n    const std::string& packed_image = GetParam().second.packed_img;\n    const std::string& unpacked = GetParam().second.unpacked_dir;\n\n    // First we need to check for existence of images\n    const extension::Configuration::PackedInfo& info = config.packed[packed_name];\n\n    const auto flash_part = [&](const std::string fname, const std::string part_name) {\n        FILE* to_flash = fopen((SEARCH_PATH + fname).c_str(), \"rb\");\n        ASSERT_NE(to_flash, nullptr) << \"'\" << fname << \"'\"\n                                     << \" failed to open for flashing\";\n        int fd = fileno(to_flash);\n        size_t fsize = lseek(fd, 0, SEEK_END);\n        ASSERT_GT(fsize, 0) << fname + \" appears to be an empty image\";\n        ASSERT_EQ(fb->FlashPartition(part_name, fd, fsize), SUCCESS);\n        fclose(to_flash);\n    };\n\n    // We first need to set the slot count\n    std::string var;\n    int num_slots = 1;\n    if (info.slots) {\n        ASSERT_EQ(fb->GetVar(\"slot-count\", &var), SUCCESS) << \"Getting slot count failed\";\n        num_slots = strtol(var.c_str(), nullptr, 10);\n    } else {\n        for (const auto& part : info.children) {\n            EXPECT_FALSE(config.partitions[part].slots)\n                    << \"A partition can not have slots if the packed image does not\";\n        }\n    }\n\n    for (int i = 0; i < num_slots; i++) {\n        std::unordered_map<std::string, std::string> initial_hashes;\n        const std::string packed_suffix =\n                info.slots ? android::base::StringPrintf(\"_%c\", 'a' + i) : \"\";\n\n        // Flash the paritions manually and get hash\n        for (const auto& part : info.children) {\n            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];\n            const std::string suffix = part_info.slots ? packed_suffix : \"\";\n            const std::string part_name = part + suffix;\n\n            ASSERT_EQ(fb->Erase(part_name), SUCCESS);\n            const std::string fpath = unpacked + '/' + part + \".img\";\n            ASSERT_NO_FATAL_FAILURE(flash_part(fpath, part_name))\n                    << \"Failed to flash '\" + fpath + \"'\";\n            // If the partition is hashable we store it\n            if (part_info.hashable) {\n                std::string hash, err_msg;\n                int retcode;\n                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))\n                        << err_msg;\n                ASSERT_EQ(retcode, 0) << err_msg;\n                initial_hashes[part] = hash;\n            }\n        }\n\n        // erase once at the end, to avoid false positives if flashing does nothing\n        for (const auto& part : info.children) {\n            const std::string suffix = config.partitions[part].slots ? packed_suffix : \"\";\n            ASSERT_EQ(fb->Erase(part + suffix), SUCCESS);\n        }\n\n        // Now we flash the packed image and compare our hashes\n        ASSERT_NO_FATAL_FAILURE(flash_part(packed_image, packed_name + packed_suffix));\n\n        for (const auto& part : info.children) {\n            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];\n            // If the partition is hashable we check it\n            if (part_info.hashable) {\n                const std::string suffix = part_info.slots ? packed_suffix : \"\";\n                const std::string part_name = part + suffix;\n                std::string hash, err_msg;\n                int retcode;\n                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))\n                        << err_msg;\n                ASSERT_EQ(retcode, 0) << err_msg;\n                std::string msg =\n                        \"The hashes between flashing the packed image and directly flashing '\" +\n                        part_name + \"' does not match\";\n                EXPECT_EQ(hash, initial_hashes[part]) << msg;\n            }\n        }\n    }\n}\n\nINSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedValid,\n                        ::testing::ValuesIn(PACKED_XML_SUCCESS_TESTS));\n\n// Packed images test\nTEST_P(ExtensionsPackedInvalid, TestDeviceUnpack) {\n    const std::string& packed_name = GetParam().first;\n    const std::string& packed_image = GetParam().second.packed_img;\n\n    // First we need to check for existence of images\n    const extension::Configuration::PackedInfo& info = config.packed[packed_name];\n\n    // We first need to set the slot count\n    std::string var;\n    int num_slots = 1;\n    if (info.slots) {\n        ASSERT_EQ(fb->GetVar(\"slot-count\", &var), SUCCESS) << \"Getting slot count failed\";\n        num_slots = strtol(var.c_str(), nullptr, 10);\n    } else {\n        for (const auto& part : info.children) {\n            EXPECT_FALSE(config.partitions[part].slots)\n                    << \"A partition can not have slots if the packed image does not\";\n        }\n    }\n\n    for (int i = 0; i < num_slots; i++) {\n        std::unordered_map<std::string, std::string> initial_hashes;\n        const std::string packed_suffix =\n                info.slots ? android::base::StringPrintf(\"_%c\", 'a' + i) : \"\";\n\n        // manually and get hash\n        for (const auto& part : info.children) {\n            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];\n            const std::string suffix = part_info.slots ? packed_suffix : \"\";\n            const std::string part_name = part + suffix;\n\n            // If the partition is hashable we store it\n            if (part_info.hashable) {\n                std::string hash, err_msg;\n                int retcode;\n                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))\n                        << err_msg;\n                ASSERT_EQ(retcode, 0) << err_msg;\n                initial_hashes[part] = hash;\n            }\n        }\n\n        // Attempt to flash the invalid file\n        FILE* to_flash = fopen((SEARCH_PATH + packed_image).c_str(), \"rb\");\n        ASSERT_NE(to_flash, nullptr) << \"'\" << packed_image << \"'\"\n                                     << \" failed to open for flashing\";\n        int fd = fileno(to_flash);\n        size_t fsize = lseek(fd, 0, SEEK_END);\n        ASSERT_GT(fsize, 0) << packed_image + \" appears to be an empty image\";\n        ASSERT_EQ(fb->FlashPartition(packed_name + packed_suffix, fd, fsize), DEVICE_FAIL)\n                << \"Expected flashing to fail for \" + packed_image;\n        fclose(to_flash);\n\n        for (const auto& part : info.children) {\n            const extension::Configuration::PartitionInfo& part_info = config.partitions[part];\n            // If the partition is hashable we check it\n            if (part_info.hashable) {\n                const std::string suffix = part_info.slots ? packed_suffix : \"\";\n                const std::string part_name = part + suffix;\n                std::string hash, err_msg;\n                int retcode;\n                ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg))\n                        << err_msg;\n                ASSERT_EQ(retcode, 0) << err_msg;\n                std::string msg = \"Flashing an invalid image changed the hash of '\" + part_name;\n                EXPECT_EQ(hash, initial_hashes[part]) << msg;\n            }\n        }\n    }\n}\n\nINSTANTIATE_TEST_CASE_P(XMLTestPacked, ExtensionsPackedInvalid,\n                        ::testing::ValuesIn(PACKED_XML_FAIL_TESTS));\n\n// OEM xml tests\nTEST_P(ExtensionsOemConformance, RunOEMTest) {\n    const std::string& cmd = std::get<0>(GetParam());\n    // bool restricted = std::get<1>(GetParam());\n    const extension::Configuration::CommandTest& test = std::get<2>(GetParam());\n\n    const RetCode expect = (test.expect == extension::FAIL) ? DEVICE_FAIL : SUCCESS;\n\n    // Does the test require staging something?\n    if (!test.input.empty()) {  // Non-empty string\n        FILE* to_stage = fopen((SEARCH_PATH + test.input).c_str(), \"rb\");\n        ASSERT_NE(to_stage, nullptr) << \"'\" << test.input << \"'\"\n                                     << \" failed to open for staging\";\n        int fd = fileno(to_stage);\n        size_t fsize = lseek(fd, 0, SEEK_END);\n        std::string var;\n        EXPECT_EQ(fb->GetVar(\"max-download-size\", &var), SUCCESS);\n        int64_t size = strtoll(var.c_str(), nullptr, 16);\n        EXPECT_LT(fsize, size) << \"'\" << test.input << \"'\"\n                               << \" is too large for staging\";\n        ASSERT_EQ(fb->Download(fd, fsize), SUCCESS) << \"'\" << test.input << \"'\"\n                                                    << \" failed to download for staging\";\n        fclose(to_stage);\n    }\n    // Run the command\n    int dsize = -1;\n    std::string resp;\n    const std::string full_cmd = \"oem \" + cmd + \" \" + test.arg;\n    ASSERT_EQ(fb->RawCommand(full_cmd, &resp, nullptr, &dsize), expect);\n\n    // This is how we test if indeed data response\n    if (test.expect == extension::DATA) {\n        EXPECT_GT(dsize, 0);\n    }\n\n    // Validate response if neccesary\n    if (!test.regex_str.empty()) {\n        std::smatch sm;\n        std::regex_match(resp, sm, test.regex);\n        EXPECT_FALSE(sm.empty()) << \"The oem regex did not match\";\n    }\n\n    // If payload, we validate that as well\n    const std::vector<std::string> args = SplitBySpace(test.validator);\n    if (args.size()) {\n        // Save output\n        const std::string save_loc =\n                OUTPUT_PATH + (test.output.empty() ? DEFAULT_OUPUT_NAME : test.output);\n        std::string resp;\n        ASSERT_EQ(fb->Upload(save_loc, &resp), SUCCESS)\n                << \"Saving output file failed with (\" << fb->Error() << \") \" << resp;\n        // Build the arguments to the validator\n        std::vector<std::string> prog_args(args.begin() + 1, args.end());\n        prog_args.push_back(full_cmd);  // Pass in the full command\n        prog_args.push_back(save_loc);  // Pass in the save location\n        // Run the validation program\n        int pipe;\n        const pid_t pid = StartProgram(args[0], prog_args, &pipe);\n        ASSERT_GT(pid, 0) << \"Failed to launch validation program: \" << args[0];\n        std::string error_msg;\n        int ret = WaitProgram(pid, pipe, &error_msg);\n        EXPECT_EQ(ret, 0) << error_msg;  // Program exited correctly\n    }\n}\n\nINSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));\n\n// Sparse Tests\nTEST_P(SparseTestPartition, SparseSingleBlock) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n    const std::string part_name = name + (part_info.slots ? \"_a\" : \"\");\n    SparseWrapper sparse(4096, 4096);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    std::vector<char> buf = RandomBuf(4096);\n    ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)\n            << \"Adding data failed to sparse file: \" << sparse.Rep();\n\n    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n    std::string hash, hash_new, err_msg;\n    int retcode;\n    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n    // Now flash it the non-sparse way\n    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << \"Flashing image failed: \";\n    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n\n    EXPECT_EQ(hash, hash_new) << \"Flashing a random buffer of 4096 using sparse and non-sparse \"\n                                 \"methods did not result in the same hash\";\n}\n\nTEST_P(SparseTestPartition, SparseFill) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n    const std::string part_name = name + (part_info.slots ? \"_a\" : \"\");\n    int64_t size = (max_dl / 4096) * 4096;\n    SparseWrapper sparse(4096, size);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)\n            << \"Adding data failed to sparse file: \" << sparse.Rep();\n\n    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n    std::string hash, hash_new, err_msg;\n    int retcode;\n    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n    // Now flash it the non-sparse way\n    std::vector<char> buf(size);\n    for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {\n        iter[0] = 0xef;\n        iter[1] = 0xbe;\n        iter[2] = 0xad;\n        iter[3] = 0xde;\n    }\n    EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << \"Flashing image failed: \";\n    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n\n    EXPECT_EQ(hash, hash_new) << \"Flashing a random buffer of 4096 using sparse and non-sparse \"\n                                 \"methods did not result in the same hash\";\n}\n\n// This tests to make sure it does not overwrite previous flashes\nTEST_P(SparseTestPartition, SparseMultiple) {\n    const std::string name = GetParam().first;\n    auto part_info = GetParam().second;\n    const std::string part_name = name + (part_info.slots ? \"_a\" : \"\");\n    int64_t size = (max_dl / 4096) * 4096;\n    SparseWrapper sparse(4096, size / 2);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)\n            << \"Adding data failed to sparse file: \" << sparse.Rep();\n    EXPECT_EQ(fb->Download(*sparse), SUCCESS) << \"Download sparse failed: \" << sparse.Rep();\n    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << \"Flashing sparse failed: \" << sparse.Rep();\n\n    SparseWrapper sparse2(4096, size / 2);\n    ASSERT_TRUE(*sparse) << \"Sparse image creation failed\";\n    std::vector<char> buf = RandomBuf(size / 2);\n    ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)\n            << \"Adding data failed to sparse file: \" << sparse2.Rep();\n    EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << \"Download sparse failed: \" << sparse2.Rep();\n    EXPECT_EQ(fb->Flash(part_name), SUCCESS) << \"Flashing sparse failed: \" << sparse2.Rep();\n\n    std::string hash, hash_new, err_msg;\n    int retcode;\n    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n    // Now flash it the non-sparse way\n    std::vector<char> fbuf(size);\n    for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {\n        iter[0] = 0xef;\n        iter[1] = 0xbe;\n        iter[2] = 0xad;\n        iter[3] = 0xde;\n    }\n    fbuf.assign(buf.begin(), buf.end());\n    EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << \"Flashing image failed: \";\n    ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;\n    ASSERT_EQ(retcode, 0) << err_msg;\n\n    EXPECT_EQ(hash, hash_new) << \"Flashing a random buffer of 4096 using sparse and non-sparse \"\n                                 \"methods did not result in the same hash\";\n}\n\nINSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,\n                        ::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));\n\nvoid GenerateXmlTests(const extension::Configuration& config) {\n    // Build the getvar tests\n    for (const auto& it : config.getvars) {\n        GETVAR_XML_TESTS.push_back(std::make_pair(it.first, it.second));\n    }\n\n    // Build the partition tests, to interface with gtest we need to do it this way\n    for (const auto& it : config.partitions) {\n        const auto tup = std::make_tuple(it.first, it.second);\n        PARTITION_XML_TESTS.push_back(tup);  // All partitions\n\n        if (it.second.test == it.second.YES) {\n            PARTITION_XML_WRITEABLE.push_back(tup);  // All writeable partitions\n\n            if (it.second.hashable) {\n                PARTITION_XML_WRITE_HASHABLE.push_back(tup);  // All write and hashable\n                if (!it.second.parsed) {\n                    PARTITION_XML_WRITE_HASH_NONPARSED.push_back(\n                            tup);  // All write hashed and non-parsed\n                }\n            }\n            if (it.second.parsed) {\n                PARTITION_XML_WRITE_PARSED.push_back(tup);  // All write and parsed\n            }\n        }\n    }\n\n    // Build the packed tests, only useful if we have a hash\n    if (!config.checksum.empty()) {\n        for (const auto& it : config.packed) {\n            for (const auto& test : it.second.tests) {\n                const auto tup = std::make_tuple(it.first, test);\n                if (test.expect == extension::OKAY) {  // only testing the success case\n                    PACKED_XML_SUCCESS_TESTS.push_back(tup);\n                } else {\n                    PACKED_XML_FAIL_TESTS.push_back(tup);\n                }\n            }\n        }\n    }\n\n    // This is a hack to make this test disapeer if there is not a checksum, userdata is not\n    // hashable, or userdata is not marked to be writeable in testing\n    const auto part_info = config.partitions.find(\"userdata\");\n    if (!config.checksum.empty() && part_info != config.partitions.end() &&\n        part_info->second.hashable &&\n        part_info->second.test == extension::Configuration::PartitionInfo::YES) {\n        PARTITION_XML_USERDATA_CHECKSUM_WRITEABLE.push_back(\n                std::make_tuple(part_info->first, part_info->second));\n    }\n\n    if (!PARTITION_XML_WRITE_HASHABLE.empty()) {\n        SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());\n    }\n\n    // Build oem tests\n    for (const auto& it : config.oem) {\n        auto oem_cmd = it.second;\n        for (const auto& t : oem_cmd.tests) {\n            OEM_XML_TESTS.push_back(std::make_tuple(it.first, oem_cmd.restricted, t));\n        }\n    }\n}\n\n}  // namespace fastboot\n\nint main(int argc, char** argv) {\n    std::string err;\n    // Parse the args\n    const std::unordered_map<std::string, std::string> args = fastboot::ParseArgs(argc, argv, &err);\n    if (!err.empty()) {\n        printf(\"%s\\n\", err.c_str());\n        return -1;\n    }\n\n    if (args.find(\"config\") != args.end()) {\n        auto found = args.find(\"search_path\");\n        fastboot::SEARCH_PATH = (found != args.end()) ? found->second + \"/\" : \"\";\n        found = args.find(\"output_path\");\n        fastboot::OUTPUT_PATH = (found != args.end()) ? found->second + \"/\" : \"/tmp/\";\n        if (!fastboot::extension::ParseXml(fastboot::SEARCH_PATH + args.at(\"config\"),\n                                           &fastboot::config)) {\n            printf(\"XML config parsing failed\\n\");\n            return -1;\n        }\n        // To interface with gtest, must set global scope test variables\n        fastboot::GenerateXmlTests(fastboot::config);\n    }\n\n    if (args.find(\"serial\") != args.end()) {\n        fastboot::FastBootTest::device_serial = args.at(\"serial\");\n    }\n\n    setbuf(stdout, NULL);  // no buffering\n\n    if (!fastboot::FastBootTest::IsFastbootOverTcp()) {\n        printf(\"<Waiting for Device>\\n\");\n        const auto matcher = [](usb_ifc_info* info) -> int {\n            return fastboot::FastBootTest::MatchFastboot(info,\n                                                         fastboot::FastBootTest::device_serial);\n        };\n        std::unique_ptr<Transport> transport;\n        while (!transport) {\n            transport = usb_open(matcher);\n            std::this_thread::sleep_for(std::chrono::milliseconds(10));\n        }\n        transport->Close();\n    }\n\n    if (args.find(\"serial_port\") != args.end()) {\n        fastboot::FastBootTest::serial_port = fastboot::ConfigureSerial(args.at(\"serial_port\"));\n    }\n\n    ::testing::InitGoogleTest(&argc, argv);\n    auto ret = RUN_ALL_TESTS();\n    if (fastboot::FastBootTest::serial_port > 0) {\n        close(fastboot::FastBootTest::serial_port);\n    }\n    return ret;\n}\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/test_listeners.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#pragma once\n\n// TODO, print out logs for each failed test with custom listner\n\nclass MinimalistPrinter : public ::testing::EmptyTestEventListener {\n    // Called before a test starts.\n    virtual void OnTestStart(const ::testing::TestInfo& test_info) {\n        printf(\"*** Test %s.%s starting.\\n\", test_info.test_case_name(), test_info.name());\n    }\n\n    // Called after a failed assertion or a SUCCESS().\n    virtual void OnTestPartResult(const ::testing::TestPartResult& test_part_result) {\n        printf(\"%s in %s:%d\\n%s\\n\", test_part_result.failed() ? \"*** Failure\" : \"Success\",\n               test_part_result.file_name(), test_part_result.line_number(),\n               test_part_result.summary());\n    }\n\n    // Called after a test ends.\n    virtual void OnTestEnd(const ::testing::TestInfo& test_info) {\n        printf(\"*** Test %s.%s ending.\\n\", test_info.test_case_name(), test_info.name());\n    }\n};\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/test_utils.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#include \"test_utils.h\"\n#include <fcntl.h>\n#include <termios.h>\n#include <algorithm>\n#include <iterator>\n#include <sstream>\n\nnamespace fastboot {\n\nnamespace {\nconstexpr int rand_seed = 0;\nstd::default_random_engine rnd(rand_seed);\n}  // namespace\n\nchar rand_legal() {\n    return rnd() % 128;\n}\n\nchar rand_illegal() {\n    return rand_legal() + 128;\n}\n\nchar rand_char() {\n    return rnd() % 256;\n}\n\nint random_int(int start, int end) {\n    std::uniform_int_distribution<int> uni(start, end);\n    return uni(rnd);\n}\n\nstd::string RandomString(size_t length, std::function<char(void)> provider) {\n    std::string str(length, 0);\n    std::generate_n(str.begin(), length, provider);\n    return str;\n}\n\nstd::vector<char> RandomBuf(size_t length, std::function<char(void)> provider) {\n    std::vector<char> ret;\n    ret.resize(length);\n    std::generate_n(ret.begin(), length, provider);\n    return ret;\n}\n\nstd::vector<std::string> SplitBySpace(const std::string& s) {\n    std::istringstream iss(s);\n    return std::vector<std::string>{std::istream_iterator<std::string>{iss},\n                                    std::istream_iterator<std::string>{}};\n}\n\nstd::vector<std::string> GeneratePartitionNames(const std::string& base, int num_slots) {\n    if (!num_slots) {\n        return std::vector<std::string>{base};\n    }\n    std::vector<std::string> ret;\n    for (char c = 'a'; c < 'a' + num_slots; c++) {\n        ret.push_back(base + '_' + c);\n    }\n\n    return ret;\n}\n\nstd::unordered_map<std::string, std::string> ParseArgs(int argc, char** argv,\n                                                       std::string* err_msg) {\n    // We ignore any gtest stuff\n    std::unordered_map<std::string, std::string> ret;\n\n    for (int i = 1; i < argc - 1; i++) {\n        std::string arg(argv[i]);\n\n        const std::string gtest_start(\"--gtest\");\n        // We found a non gtest argument\n        if (!arg.find(\"-h\") ||\n            (!arg.find(\"--\") && arg.find(\"--gtest\") && arg.find(\"=\") != arg.npos)) {\n            const std::string start(arg.begin() + 2, arg.begin() + arg.find(\"=\"));\n            const std::string end(arg.begin() + arg.find(\"=\") + 1, arg.end());\n            ret[start] = end;\n        } else if (arg.find(\"--gtest\") != 0) {\n            *err_msg = android::base::StringPrintf(\"Illegal argument '%s'\\n\", arg.c_str());\n            return ret;\n        }\n    }\n\n    return ret;\n}\n\nint ConfigureSerial(const std::string& port) {\n    int fd = open(port.c_str(), O_RDONLY | O_NOCTTY | O_NONBLOCK);\n\n    if (fd <= 0) {\n        return fd;\n    }\n\n    struct termios tty;\n    tcgetattr(fd, &tty);\n\n    cfsetospeed(&tty, (speed_t)B115200);\n    cfsetispeed(&tty, (speed_t)B115200);\n\n    tty.c_cflag &= ~PARENB;\n    tty.c_cflag &= ~CSTOPB;\n    tty.c_cflag &= ~CSIZE;\n    tty.c_cflag |= CS8;\n\n    tty.c_cflag &= ~CRTSCTS;\n    tty.c_cc[VMIN] = 0;\n    tty.c_cc[VTIME] = 2;\n    tty.c_cflag &= ~(ICANON | ECHO | ECHOE | ISIG);\n\n    cfmakeraw(&tty);\n\n    tcflush(fd, TCIFLUSH);\n    if (tcsetattr(fd, TCSANOW, &tty) != 0) {\n        return -1;\n    }\n\n    return fd;\n}\n\nint StartProgram(const std::string program, const std::vector<std::string> args, int* rpipe) {\n    int link[2];\n    if (pipe(link) < 0) {\n        return -1;\n    }\n\n    pid_t pid = fork();\n    if (pid < 0) {  // error\n        return -1;\n    }\n\n    if (pid) {  // parent\n        close(link[1]);\n        *rpipe = link[0];\n        fcntl(*rpipe, F_SETFL, O_NONBLOCK);  // Non-blocking\n    } else {                                 // child\n        std::vector<const char*> argv(args.size() + 2, nullptr);\n        argv[0] = program.c_str();\n\n        for (int i = 0; i < args.size(); i++) {\n            argv[i + 1] = args[i].c_str();\n        }\n\n        // We pipe any stderr writes to the parent test process\n        dup2(link[1], STDERR_FILENO);  // close stdout and have it now be link[1]\n        // Close duplicates\n        close(link[0]);\n        close(link[1]);\n\n        execvp(program.c_str(), const_cast<char* const*>(argv.data()));\n        fprintf(stderr, \"Launching validator process '%s' failed with: %s\\n\", program.c_str(),\n                strerror(errno));\n        exit(-1);\n    }\n\n    return pid;\n}\n\nint WaitProgram(const int pid, const int pipe, std::string* error_msg) {\n    int status;\n    if (waitpid(pid, &status, 0) != pid) {\n        close(pipe);\n        return -1;\n    }\n    // Read from pipe\n    char buf[1024];\n    int n;\n    while ((n = read(pipe, buf, sizeof(buf))) > 0) {\n        buf[n] = 0; /* terminate the string */\n        error_msg->append(buf, n);\n    }\n    close(pipe);\n\n    if (WIFEXITED(status)) {\n        // This WEXITSTATUS macro masks off lower bytes, with no sign extension\n        // casting it as a signed char fixes the sign extension issue\n        int retmask = WEXITSTATUS(status);\n        return reinterpret_cast<int8_t*>(&retmask)[0];\n    }\n\n    return -1;\n}\n\n}  // namespace fastboot\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/test_utils.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#pragma once\n\n#include <sparse/sparse.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <cstdlib>\n#include <fstream>\n#include <random>\n#include <string>\n#include <unordered_map>\n#include \"fastboot_driver.h\"\n\nnamespace fastboot {\n\nchar rand_legal();\nchar rand_illegal();\nchar rand_char();\n// start and end are inclusive\nint random_int(int start, int end);\n\n// I don't want to have to manage memory for this guy\nstruct SparseWrapper {\n    SparseWrapper(unsigned int block_size, int64_t len) {\n        sparse = sparse_file_new(block_size, len);\n    }\n\n    SparseWrapper(struct sparse_file* sf) { sparse = sf; }\n\n    ~SparseWrapper() {\n        if (sparse) {\n            sparse_file_destroy(sparse);\n        }\n    }\n\n    const std::string Rep() {\n        unsigned bs = sparse_file_block_size(sparse);\n        unsigned len = sparse_file_len(sparse, true, false);\n        return android::base::StringPrintf(\"[block_size=%u, len=%u]\", bs, len);\n    }\n\n    struct sparse_file* operator*() {\n        return sparse;\n    }\n\n    struct sparse_file* sparse;\n};\n\nstd::string RandomString(size_t length, std::function<char(void)> provider);\n// RVO will avoid copy\nstd::vector<char> RandomBuf(size_t length, std::function<char(void)> provider = rand_char);\n\nstd::vector<std::string> SplitBySpace(const std::string& s);\n\nstd::unordered_map<std::string, std::string> ParseArgs(int argc, char** argv, std::string* err_msg);\n\nstd::vector<std::string> GeneratePartitionNames(const std::string& base, int num_slots = 0);\n\nint ConfigureSerial(const std::string& port);\n\nint StartProgram(const std::string program, const std::vector<std::string> args, int* pipe);\nint WaitProgram(const pid_t pid, const int pipe, std::string* error_msg);\n\n}  // namespace fastboot\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/transport_sniffer.cpp",
    "content": "#include \"transport_sniffer.h\"\n#include <android-base/stringprintf.h>\n#include <sys/select.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <algorithm>\n#include <iomanip>\n#include <sstream>\n\nnamespace fastboot {\n\nTransportSniffer::TransportSniffer(std::unique_ptr<Transport> transport,\n                                         const int serial_fd)\n    : transport_(std::move(transport)), serial_fd_(serial_fd) {}\n\nTransportSniffer::~TransportSniffer() {\n    Close();\n}\n\nssize_t TransportSniffer::Read(void* data, size_t len) {\n    ProcessSerial();\n\n    ssize_t ret = transport_->Read(data, len);\n    if (ret < 0) {\n        const char* err = strerror(errno);\n        std::vector<char> buf(err, err + strlen(err));\n        Event e(READ_ERROR, std::move(buf));\n        transfers_.push_back(e);\n        return ret;\n    }\n\n    char* cdata = static_cast<char*>(data);\n    std::vector<char> buf(cdata, cdata + ret);\n    Event e(READ, std::move(buf));\n    transfers_.push_back(e);\n\n    ProcessSerial();\n    return ret;\n}\n\nssize_t TransportSniffer::Write(const void* data, size_t len) {\n    ProcessSerial();\n\n    size_t ret = transport_->Write(data, len);\n    if (ret != len) {\n        const char* err = strerror(errno);\n        std::vector<char> buf(err, err + strlen(err));\n        Event e(WRITE_ERROR, std::move(buf));\n        transfers_.push_back(e);\n        return ret;\n    }\n\n    const char* cdata = static_cast<const char*>(data);\n    std::vector<char> buf(cdata, cdata + len);\n    Event e(WRITE, std::move(buf));\n    transfers_.push_back(e);\n\n    ProcessSerial();\n    return ret;\n}\n\nint TransportSniffer::Close() {\n    return transport_->Close();\n}\n\nint TransportSniffer::Reset() {\n    ProcessSerial();\n    int ret = transport_->Reset();\n    std::vector<char> buf;\n    Event e(RESET, std::move(buf));\n    transfers_.push_back(e);\n    ProcessSerial();\n    return ret;\n}\n\nconst std::vector<TransportSniffer::Event> TransportSniffer::Transfers() {\n    return transfers_;\n}\n\n/*\n * When a test fails, we want a human readable log of everything going on up until\n * the failure. This method will look through its log of captured events, and\n * create a clean printable string of everything that happened.\n */\nstd::string TransportSniffer::CreateTrace() {\n    std::string ret;\n\n    const auto no_print = [](char c) -> bool { return !isprint(c); };\n    // This lambda creates a humand readable representation of a byte buffer\n    // It first attempts to figure out whether it should be interpreted as an ASCII buffer,\n    // and be printed as a string, or just a raw byte-buffer\n    const auto msg = [&ret, no_print](const std::vector<char>& buf) {\n        ret += android::base::StringPrintf(\"(%lu bytes): \", buf.size());\n        std::vector<char>::const_iterator iter = buf.end();\n        const unsigned max_chars = 50;\n        if (buf.size() > max_chars) {\n            iter = buf.begin() + max_chars;\n        }\n        ret += '\"';\n        if (std::count_if(buf.begin(), iter, no_print) == 0) {  // print as ascii\n            ret.insert(ret.end(), buf.begin(), iter);\n        } else {  // print as hex\n            std::stringstream ss;\n            for (auto c = buf.begin(); c < iter; c++) {\n                ss << std::hex << std::setw(2) << std::setfill('0')\n                   << static_cast<uint16_t>(static_cast<uint8_t>(*c));\n                ss << ',';\n            }\n            ret += ss.str();\n        }\n        if (buf.size() > max_chars) {\n            ret += android::base::StringPrintf(\"...\\\"(+%lu bytes)\\n\", buf.size() - max_chars);\n        } else {\n            ret += \"\\\"\\n\";\n        }\n    };\n\n    // Now we just scan through the log of everything that happened and create a\n    // printable string for each one\n    for (const auto& event : transfers_) {\n        const std::vector<char>& cbuf = event.buf;\n        const std::string tmp(cbuf.begin(), cbuf.end());\n        auto start = transfers_.front().start;\n        auto durr = event.start - start;\n        auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(durr).count();\n\n        switch (event.type) {\n            case READ:\n                ret += android::base::StringPrintf(\"[READ %lldms]\", millis);\n                msg(cbuf);\n                break;\n\n            case WRITE:\n                ret += android::base::StringPrintf(\"[WRITE %lldms]\", millis);\n                msg(cbuf);\n                break;\n\n            case RESET:\n                ret += android::base::StringPrintf(\"[RESET %lldms]\\n\", millis);\n                break;\n\n            case READ_ERROR:\n                ret += android::base::StringPrintf(\"[READ_ERROR %lldms] %s\\n\", millis, tmp.c_str());\n                break;\n\n            case WRITE_ERROR:\n                ret += android::base::StringPrintf(\"[WRITE_ERROR %lldms] %s\\n\", millis,\n                                                   tmp.c_str());\n                break;\n\n            case SERIAL:\n                ret += android::base::StringPrintf(\"[SERIAL %lldms] %s\", millis, tmp.c_str());\n                if (ret.back() != '\\n') ret += '\\n';\n                break;\n        }\n    }\n    return ret;\n}\n\n// This is a quick call to flush any UART logs the device might have sent\n// to our internal event log. It will wait up to 10ms for data to appear\nvoid TransportSniffer::ProcessSerial() {\n    if (serial_fd_ <= 0) return;\n\n    fd_set set;\n    struct timeval timeout;\n\n    FD_ZERO(&set);\n    FD_SET(serial_fd_, &set);\n    timeout.tv_sec = 0;\n    timeout.tv_usec = 10000;  // 10ms\n\n    int count = 0;\n    int n = 0;\n    std::vector<char> buf;\n    buf.resize(1000);\n    while (select(serial_fd_ + 1, &set, NULL, NULL, &timeout) > 0) {\n        n = read(serial_fd_, buf.data() + count, buf.size() - count);\n        if (n > 0) {\n            count += n;\n        } else {\n            break;\n        }\n    }\n\n    buf.resize(count);\n\n    if (count > 0) {\n        Event e(SERIAL, std::move(buf));\n        transfers_.push_back(e);\n    }\n}\n\n}  // namespace fastboot\n"
  },
  {
    "path": "fastboot/fuzzy_fastboot/transport_sniffer.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n#pragma once\n\n#include <sys/types.h>\n#include <unistd.h>\n#include <chrono>\n#include <cstdlib>\n#include <fstream>\n#include <string>\n#include <vector>\n\n#include \"usb.h\"\n\nnamespace fastboot {\n\n/* A special class for sniffing reads and writes\n *\n * A useful debugging tool is to see the raw fastboot transactions going between\n * the host and device. This class is a special subclass of Transport that snoops and saves\n * all the transactions going on. Additionally, if there is a console serial port\n * from the device, this class can monitor it as well and capture the interleaving of\n * transport transactions and UART log messages.\n */\nclass TransportSniffer : public Transport {\n  public:\n    enum EventType {\n        READ,\n        WRITE,\n        RESET,\n        SERIAL,  // Serial log message from device\n        READ_ERROR,\n        WRITE_ERROR,\n    };\n\n    struct Event {\n        Event(EventType t, const std::vector<char> cbuf) : type(t), buf(cbuf) {\n            start = std::chrono::high_resolution_clock::now();\n        };\n        EventType type;\n        std::chrono::high_resolution_clock::time_point start;\n        const std::vector<char> buf;\n    };\n\n    TransportSniffer(std::unique_ptr<Transport> transport, const int serial_fd = 0);\n    ~TransportSniffer() override;\n\n    virtual ssize_t Read(void* data, size_t len) override;\n    virtual ssize_t Write(const void* data, size_t len) override;\n    virtual int Close() override final;  // note usage in destructor\n    virtual int Reset() override;\n\n    const std::vector<Event> Transfers();\n    std::string CreateTrace();\n    void ProcessSerial();\n\n  private:\n    std::vector<Event> transfers_;\n    std::unique_ptr<Transport> transport_;\n    const int serial_fd_;\n};\n\n}  // End namespace fastboot\n"
  },
  {
    "path": "fastboot/main.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"fastboot.h\"\n\nint main(int argc, char* argv[]) {\n    FastBootTool fb;\n    return fb.Main(argc, argv);\n}\n"
  },
  {
    "path": "fastboot/mock_transport.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <string.h>\n\n#include <algorithm>\n#include <string_view>\n\n#include <gmock/gmock.h>\n#include \"transport.h\"\n\nclass MockTransport : public Transport {\n  public:\n    MOCK_METHOD(ssize_t, Read, (void* data, size_t len), (override));\n    MOCK_METHOD(ssize_t, Write, (const void* data, size_t len), (override));\n    MOCK_METHOD(int, Close, (), (override));\n    MOCK_METHOD(int, Reset, (), (override));\n};\n\nclass RawDataMatcher {\n  public:\n    explicit RawDataMatcher(const char* data) : data_(data) {}\n    explicit RawDataMatcher(std::string_view data) : data_(data) {}\n\n    bool MatchAndExplain(std::tuple<const void*, size_t> args,\n                         ::testing::MatchResultListener*) const {\n        const void* expected_data = std::get<0>(args);\n        size_t expected_len = std::get<1>(args);\n        if (expected_len != data_.size()) {\n            return false;\n        }\n        return memcmp(expected_data, data_.data(), expected_len) == 0;\n    }\n    void DescribeTo(std::ostream* os) const { *os << \"raw data is\"; }\n    void DescribeNegationTo(std::ostream* os) const { *os << \"raw data is not\"; }\n\n  private:\n    std::string_view data_;\n};\n\ntemplate <typename T>\nstatic inline ::testing::PolymorphicMatcher<RawDataMatcher> RawData(T data) {\n    return ::testing::MakePolymorphicMatcher(RawDataMatcher(data));\n}\n\nstatic inline auto CopyData(const char* source) {\n    return [source](void* buffer, size_t size) -> ssize_t {\n        size_t to_copy = std::min(size, strlen(source));\n        memcpy(buffer, source, to_copy);\n        return to_copy;\n    };\n};\n"
  },
  {
    "path": "fastboot/result.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <android-base/result.h>\n\n#include \"util.h\"\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\nusing android::base::ResultError;\n\nclass FastbootError {\n  public:\n    enum Type { NETWORK_SERIAL_WRONG_PREFIX = 1, NETWORK_SERIAL_WRONG_ADDRESS = 2 };\n\n    FastbootError(Type&& type) : type_(std::forward<Type>(type)) {}\n\n    Type value() const { return type_; }\n    operator Type() const { return value(); }\n    std::string print() const { return \"\"; }\n\n  private:\n    Type type_;\n};\n\ntemplate <typename T, typename U>\ninline T Expect(Result<T, U> r) {\n    if (r.ok()) {\n        return r.value();\n    }\n\n    die(r.error().message());\n\n    return r.value();\n}"
  },
  {
    "path": "fastboot/socket.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"socket.h\"\n\n#ifndef _WIN32\n#include <sys/select.h>\n#endif\n\n#include <android-base/errors.h>\n#include <android-base/stringprintf.h>\n\nSocket::Socket(cutils_socket_t sock) : sock_(sock) {}\n\nSocket::~Socket() {\n    Close();\n}\n\nint Socket::Close() {\n    int ret = 0;\n\n    if (sock_ != INVALID_SOCKET) {\n        ret = socket_close(sock_);\n        sock_ = INVALID_SOCKET;\n    }\n\n    return ret;\n}\n\nssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) {\n    size_t total = 0;\n\n    while (total < length) {\n        ssize_t bytes = Receive(reinterpret_cast<char*>(data) + total, length - total, timeout_ms);\n\n        // Returns 0 only when the peer has disconnected because our requested length is not 0. So\n        // we return immediately to avoid dead loop here.\n        if (bytes <= 0) {\n            if (total == 0) {\n                return -1;\n            }\n            break;\n        }\n        total += bytes;\n    }\n\n    return total;\n}\n\nint Socket::GetLocalPort() {\n    return socket_get_local_port(sock_);\n}\n\n// According to Windows setsockopt() documentation, if a Windows socket times out during send() or\n// recv() the state is indeterminate and should not be used. Our UDP protocol relies on being able\n// to re-send after a timeout, so we must use select() rather than SO_RCVTIMEO.\n// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx.\nbool Socket::WaitForRecv(int timeout_ms) {\n    receive_timed_out_ = false;\n\n    // In our usage |timeout_ms| <= 0 means block forever, so just return true immediately and let\n    // the subsequent recv() do the blocking.\n    if (timeout_ms <= 0) {\n        return true;\n    }\n\n    // select() doesn't always check this case and will block for |timeout_ms| if we let it.\n    if (sock_ == INVALID_SOCKET) {\n        return false;\n    }\n\n    fd_set read_set;\n    FD_ZERO(&read_set);\n    FD_SET(sock_, &read_set);\n\n    timeval timeout;\n    timeout.tv_sec = timeout_ms / 1000;\n    timeout.tv_usec = (timeout_ms % 1000) * 1000;\n\n    int result = TEMP_FAILURE_RETRY(select(sock_ + 1, &read_set, nullptr, nullptr, &timeout));\n\n    if (result == 0) {\n        receive_timed_out_ = true;\n    }\n    return result == 1;\n}\n\n// Implements the Socket interface for UDP.\nclass UdpSocket : public Socket {\n  public:\n    enum class Type { kClient, kServer };\n\n    UdpSocket(Type type, cutils_socket_t sock);\n\n    bool Send(const void* data, size_t length) override;\n    bool Send(std::vector<cutils_socket_buffer_t> buffers) override;\n    ssize_t Receive(void* data, size_t length, int timeout_ms) override;\n\n  private:\n    std::unique_ptr<sockaddr_storage> addr_;\n    socklen_t addr_size_ = 0;\n\n    DISALLOW_COPY_AND_ASSIGN(UdpSocket);\n};\n\nUdpSocket::UdpSocket(Type type, cutils_socket_t sock) : Socket(sock) {\n    // Only servers need to remember addresses; clients are connected to a server in NewClient()\n    // so will send to that server without needing to specify the address again.\n    if (type == Type::kServer) {\n        addr_.reset(new sockaddr_storage);\n        addr_size_ = sizeof(*addr_);\n        memset(addr_.get(), 0, addr_size_);\n    }\n}\n\nbool UdpSocket::Send(const void* data, size_t length) {\n    return TEMP_FAILURE_RETRY(sendto(sock_, reinterpret_cast<const char*>(data), length, 0,\n                                     reinterpret_cast<sockaddr*>(addr_.get()), addr_size_)) ==\n           static_cast<ssize_t>(length);\n}\n\nbool UdpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {\n    size_t total_length = 0;\n    for (const auto& buffer : buffers) {\n        total_length += buffer.length;\n    }\n\n    return TEMP_FAILURE_RETRY(socket_send_buffers_function_(\n                   sock_, buffers.data(), buffers.size())) == static_cast<ssize_t>(total_length);\n}\n\nssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {\n    if (!WaitForRecv(timeout_ms)) {\n        return -1;\n    }\n\n    socklen_t* addr_size_ptr = nullptr;\n    if (addr_ != nullptr) {\n        // Reset addr_size as it may have been modified by previous recvfrom() calls.\n        addr_size_ = sizeof(*addr_);\n        addr_size_ptr = &addr_size_;\n    }\n\n    return TEMP_FAILURE_RETRY(recvfrom(sock_, reinterpret_cast<char*>(data), length, 0,\n                                       reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr));\n}\n\n// Implements the Socket interface for TCP.\nclass TcpSocket : public Socket {\n  public:\n    explicit TcpSocket(cutils_socket_t sock) : Socket(sock) {}\n\n    bool Send(const void* data, size_t length) override;\n    bool Send(std::vector<cutils_socket_buffer_t> buffers) override;\n    ssize_t Receive(void* data, size_t length, int timeout_ms) override;\n\n    std::unique_ptr<Socket> Accept() override;\n\n  private:\n    DISALLOW_COPY_AND_ASSIGN(TcpSocket);\n};\n\nbool TcpSocket::Send(const void* data, size_t length) {\n    while (length > 0) {\n        ssize_t sent =\n                TEMP_FAILURE_RETRY(send(sock_, reinterpret_cast<const char*>(data), length, 0));\n\n        if (sent == -1) {\n            return false;\n        }\n        length -= sent;\n    }\n\n    return true;\n}\n\nbool TcpSocket::Send(std::vector<cutils_socket_buffer_t> buffers) {\n    while (!buffers.empty()) {\n        ssize_t sent = TEMP_FAILURE_RETRY(\n                socket_send_buffers_function_(sock_, buffers.data(), buffers.size()));\n\n        if (sent == -1) {\n            return false;\n        }\n\n        // Adjust the buffers to skip past the bytes we've just sent.\n        auto iter = buffers.begin();\n        while (sent > 0) {\n            if (iter->length > static_cast<size_t>(sent)) {\n                // Incomplete buffer write; adjust the buffer to point to the next byte to send.\n                iter->length -= sent;\n                iter->data = reinterpret_cast<const char*>(iter->data) + sent;\n                break;\n            }\n\n            // Complete buffer write; move on to the next buffer.\n            sent -= iter->length;\n            ++iter;\n        }\n\n        // Shortcut the common case: we've written everything remaining.\n        if (iter == buffers.end()) {\n            break;\n        }\n        buffers.erase(buffers.begin(), iter);\n    }\n\n    return true;\n}\n\nssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {\n    if (!WaitForRecv(timeout_ms)) {\n        return -1;\n    }\n\n    return TEMP_FAILURE_RETRY(recv(sock_, reinterpret_cast<char*>(data), length, 0));\n}\n\nstd::unique_ptr<Socket> TcpSocket::Accept() {\n    cutils_socket_t handler = accept(sock_, nullptr, nullptr);\n    if (handler == INVALID_SOCKET) {\n        return nullptr;\n    }\n    return std::unique_ptr<TcpSocket>(new TcpSocket(handler));\n}\n\nstd::unique_ptr<Socket> Socket::NewClient(Protocol protocol, const std::string& host, int port,\n                                          std::string* error) {\n    if (protocol == Protocol::kUdp) {\n        cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_DGRAM);\n        if (sock != INVALID_SOCKET) {\n            return std::unique_ptr<UdpSocket>(new UdpSocket(UdpSocket::Type::kClient, sock));\n        }\n    } else {\n        cutils_socket_t sock = socket_network_client(host.c_str(), port, SOCK_STREAM);\n        if (sock != INVALID_SOCKET) {\n            return std::unique_ptr<TcpSocket>(new TcpSocket(sock));\n        }\n    }\n\n    if (error) {\n        *error = android::base::StringPrintf(\"Failed to connect to %s:%d\", host.c_str(), port);\n    }\n    return nullptr;\n}\n\n// This functionality is currently only used by tests so we don't need any error messages.\nstd::unique_ptr<Socket> Socket::NewServer(Protocol protocol, int port) {\n    if (protocol == Protocol::kUdp) {\n        cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_DGRAM);\n        if (sock != INVALID_SOCKET) {\n            return std::unique_ptr<UdpSocket>(new UdpSocket(UdpSocket::Type::kServer, sock));\n        }\n    } else {\n        cutils_socket_t sock = socket_inaddr_any_server(port, SOCK_STREAM);\n        if (sock != INVALID_SOCKET) {\n            return std::unique_ptr<TcpSocket>(new TcpSocket(sock));\n        }\n    }\n\n    return nullptr;\n}\n\nstd::string Socket::GetErrorMessage() {\n#if defined(_WIN32)\n    DWORD error_code = WSAGetLastError();\n#else\n    int error_code = errno;\n#endif\n    return android::base::SystemErrorCodeToString(error_code);\n}\n"
  },
  {
    "path": "fastboot/socket.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n// This file provides a class interface for cross-platform socket functionality. The main fastboot\n// engine should not be using this interface directly, but instead should use a higher-level\n// interface that enforces the fastboot protocol.\n\n#pragma once\n\n#include <functional>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <android-base/macros.h>\n#include <cutils/sockets.h>\n#include <gtest/gtest_prod.h>\n\n// Socket interface to be implemented for each platform.\nclass Socket {\n  public:\n    enum class Protocol { kTcp, kUdp };\n\n    // Returns the socket error message. This must be called immediately after a socket failure\n    // before any other system calls are made.\n    static std::string GetErrorMessage();\n\n    // Creates a new client connection. Clients are connected to a specific hostname/port and can\n    // only send to that destination.\n    // On failure, |error| is filled (if non-null) and nullptr is returned.\n    static std::unique_ptr<Socket> NewClient(Protocol protocol, const std::string& hostname,\n                                             int port, std::string* error);\n\n    // Creates a new server bound to local |port|. This is only meant for testing, during normal\n    // fastboot operation the device acts as the server.\n    // A UDP server saves sender addresses in Receive(), and uses the most recent address during\n    // calls to Send().\n    static std::unique_ptr<Socket> NewServer(Protocol protocol, int port);\n\n    // Destructor closes the socket if it's open.\n    virtual ~Socket();\n\n    // Sends |length| bytes of |data|. For TCP sockets this will continue trying to send until all\n    // bytes are transmitted. Returns true on success.\n    virtual bool Send(const void* data, size_t length) = 0;\n\n    // Sends |buffers| using multi-buffer write, which can be significantly faster than making\n    // multiple calls. For UDP sockets |buffers| are all combined into a single datagram; for\n    // TCP sockets this will continue sending until all buffers are fully transmitted. Returns true\n    // on success.\n    //\n    // Note: This is non-functional for UDP server Sockets because it's not currently needed and\n    // would require an additional sendto() variation of multi-buffer write.\n    virtual bool Send(std::vector<cutils_socket_buffer_t> buffers) = 0;\n\n    // Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will\n    // block forever. Returns the number of bytes received or -1 on error/timeout; see\n    // ReceiveTimedOut() to distinguish between the two.\n    virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;\n\n    // Calls Receive() until exactly |length| bytes have been received or an error occurs.\n    virtual ssize_t ReceiveAll(void* data, size_t length, int timeout_ms);\n\n    // Returns true if the last Receive() call timed out normally and can be retried; fatal errors\n    // or successful reads will return false.\n    bool ReceiveTimedOut() { return receive_timed_out_; }\n\n    // Closes the socket. Returns 0 on success, -1 on error.\n    virtual int Close();\n\n    // Accepts an incoming TCP connection. No effect for UDP sockets. Returns a new Socket\n    // connected to the client on success, nullptr on failure.\n    virtual std::unique_ptr<Socket> Accept() { return nullptr; }\n\n    // Returns the local port the Socket is bound to or -1 on error.\n    int GetLocalPort();\n\n  protected:\n    // Protected constructor to force factory function use.\n    explicit Socket(cutils_socket_t sock);\n\n    // Blocks up to |timeout_ms| until a read is possible on |sock_|, and sets |receive_timed_out_|\n    // as appropriate to help distinguish between normal timeouts and fatal errors. Returns true if\n    // a subsequent recv() on |sock_| will complete without blocking or if |timeout_ms| <= 0.\n    bool WaitForRecv(int timeout_ms);\n\n    cutils_socket_t sock_ = INVALID_SOCKET;\n    bool receive_timed_out_ = false;\n\n    // Non-class functions we want to override during tests to verify functionality. Implementation\n    // should call this rather than using socket_send_buffers() directly.\n    std::function<ssize_t(cutils_socket_t, cutils_socket_buffer_t*, size_t)>\n            socket_send_buffers_function_ = &socket_send_buffers;\n\n  private:\n    FRIEND_TEST(SocketTest, TestTcpSendBuffers);\n    FRIEND_TEST(SocketTest, TestUdpSendBuffers);\n\n    DISALLOW_COPY_AND_ASSIGN(Socket);\n};\n"
  },
  {
    "path": "fastboot/socket_mock.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"socket_mock.h\"\n\n#include <gtest/gtest.h>\n\nSocketMock::SocketMock() : Socket(INVALID_SOCKET) {}\n\nSocketMock::~SocketMock() {\n    if (!events_.empty()) {\n        ADD_FAILURE() << events_.size() << \" event(s) were not handled\";\n    }\n}\n\nbool SocketMock::Send(const void* data, size_t length) {\n    if (events_.empty()) {\n        ADD_FAILURE() << \"Send() was called when no message was expected\";\n        return false;\n    }\n\n    if (events_.front().type != EventType::kSend) {\n        ADD_FAILURE() << \"Send() was called out-of-order\";\n        return false;\n    }\n\n    std::string message(reinterpret_cast<const char*>(data), length);\n    if (events_.front().message != message) {\n        ADD_FAILURE() << \"Send() expected \" << events_.front().message << \", but got \" << message;\n        return false;\n    }\n\n    bool return_value = events_.front().status;\n    events_.pop();\n    return return_value;\n}\n\n// Mock out multi-buffer send to be one large send, since that's what it should looks like from\n// the user's perspective.\nbool SocketMock::Send(std::vector<cutils_socket_buffer_t> buffers) {\n    std::string data;\n    for (const auto& buffer : buffers) {\n        data.append(reinterpret_cast<const char*>(buffer.data), buffer.length);\n    }\n    return Send(data.data(), data.size());\n}\n\nssize_t SocketMock::Receive(void* data, size_t length, int /*timeout_ms*/) {\n    if (events_.empty()) {\n        ADD_FAILURE() << \"Receive() was called when no message was ready\";\n        return -1;\n    }\n\n    const Event& event = events_.front();\n    if (event.type != EventType::kReceive) {\n        ADD_FAILURE() << \"Receive() was called out-of-order\";\n        return -1;\n    }\n\n    const std::string& message = event.message;\n    if (message.length() > length) {\n        ADD_FAILURE() << \"Receive(): not enough bytes (\" << length << \") for \" << message;\n        return -1;\n    }\n\n    receive_timed_out_ = event.status;\n    ssize_t return_value = message.length();\n\n    // Empty message indicates failure.\n    if (message.empty()) {\n        return_value = -1;\n    } else {\n        memcpy(data, message.data(), message.length());\n    }\n\n    events_.pop();\n    return return_value;\n}\n\nint SocketMock::Close() {\n    return 0;\n}\n\nstd::unique_ptr<Socket> SocketMock::Accept() {\n    if (events_.empty()) {\n        ADD_FAILURE() << \"Accept() was called when no socket was ready\";\n        return nullptr;\n    }\n\n    if (events_.front().type != EventType::kAccept) {\n        ADD_FAILURE() << \"Accept() was called out-of-order\";\n        return nullptr;\n    }\n\n    std::unique_ptr<Socket> sock = std::move(events_.front().sock);\n    events_.pop();\n    return sock;\n}\n\nvoid SocketMock::ExpectSend(std::string message) {\n    events_.push(Event(EventType::kSend, std::move(message), true, nullptr));\n}\n\nvoid SocketMock::ExpectSendFailure(std::string message) {\n    events_.push(Event(EventType::kSend, std::move(message), false, nullptr));\n}\n\nvoid SocketMock::AddReceive(std::string message) {\n    events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));\n}\n\nvoid SocketMock::AddReceiveTimeout() {\n    events_.push(Event(EventType::kReceive, \"\", true, nullptr));\n}\n\nvoid SocketMock::AddReceiveFailure() {\n    events_.push(Event(EventType::kReceive, \"\", false, nullptr));\n}\n\nvoid SocketMock::AddAccept(std::unique_ptr<Socket> sock) {\n    events_.push(Event(EventType::kAccept, \"\", false, std::move(sock)));\n}\n\nSocketMock::Event::Event(EventType _type, std::string _message, ssize_t _status,\n                         std::unique_ptr<Socket> _sock)\n        : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}\n"
  },
  {
    "path": "fastboot/socket_mock.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#pragma once\n\n#include <memory>\n#include <queue>\n#include <string>\n\n#include <android-base/macros.h>\n\n#include \"socket.h\"\n\n// A mock Socket implementation to be used for testing. Tests can set expectations for messages\n// to be sent and provide messages to be received in order to verify protocol behavior.\n//\n// Example: testing sending \"foo\" and receiving \"bar\".\n//   SocketMock mock;\n//   mock.ExpectSend(\"foo\");\n//   mock.AddReceive(\"bar\");\n//   EXPECT_TRUE(DoFooBar(&mock));\n//\n// Example: testing sending \"foo\" and expecting \"bar\", but receiving \"baz\" instead.\n//   SocketMock mock;\n//   mock.ExpectSend(\"foo\");\n//   mock.AddReceive(\"baz\");\n//   EXPECT_FALSE(DoFooBar(&mock));\nclass SocketMock : public Socket {\n  public:\n    SocketMock();\n    ~SocketMock() override;\n\n    bool Send(const void* data, size_t length) override;\n    bool Send(std::vector<cutils_socket_buffer_t> buffers) override;\n    ssize_t Receive(void* data, size_t length, int timeout_ms) override;\n    int Close() override;\n    virtual std::unique_ptr<Socket> Accept();\n\n    // Adds an expectation for Send().\n    void ExpectSend(std::string message);\n\n    // Adds an expectation for Send() that returns false.\n    void ExpectSendFailure(std::string message);\n\n    // Adds data to provide for Receive().\n    void AddReceive(std::string message);\n\n    // Adds a Receive() timeout after which ReceiveTimedOut() will return true.\n    void AddReceiveTimeout();\n\n    // Adds a Receive() failure after which ReceiveTimedOut() will return false.\n    void AddReceiveFailure();\n\n    // Adds a Socket to return from Accept().\n    void AddAccept(std::unique_ptr<Socket> sock);\n\n  private:\n    enum class EventType { kSend, kReceive, kAccept };\n\n    struct Event {\n        Event(EventType _type, std::string _message, ssize_t _status,\n              std::unique_ptr<Socket> _sock);\n\n        EventType type;\n        std::string message;\n        bool status;  // Return value for Send() or timeout status for Receive().\n        std::unique_ptr<Socket> sock;\n    };\n\n    std::queue<Event> events_;\n\n    DISALLOW_COPY_AND_ASSIGN(SocketMock);\n};\n"
  },
  {
    "path": "fastboot/socket_test.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Tests socket functionality using loopback connections. The UDP tests assume that no packets are\n// lost, which should be the case for loopback communication, but is not guaranteed.\n//\n// Also tests our SocketMock class to make sure it works as expected and reports errors properly\n// if the mock expectations aren't met during a test.\n\n#include \"socket.h\"\n#include \"socket_mock.h\"\n\n#include <list>\n\n#include <gtest/gtest-spi.h>\n#include <gtest/gtest.h>\n\nstatic constexpr int kShortTimeoutMs = 10;\nstatic constexpr int kTestTimeoutMs = 3000;\n\n// Creates connected sockets |server| and |client|. Returns true on success.\nbool MakeConnectedSockets(Socket::Protocol protocol, std::unique_ptr<Socket>* server,\n                          std::unique_ptr<Socket>* client,\n                          const std::string& hostname = \"localhost\") {\n    *server = Socket::NewServer(protocol, 0);\n    if (*server == nullptr) {\n        ADD_FAILURE() << \"Failed to create server.\";\n        return false;\n    }\n\n    *client = Socket::NewClient(protocol, hostname, (*server)->GetLocalPort(), nullptr);\n    if (*client == nullptr) {\n        ADD_FAILURE() << \"Failed to create client.\";\n        return false;\n    }\n\n    // TCP passes the client off to a new socket.\n    if (protocol == Socket::Protocol::kTcp) {\n        *server = (*server)->Accept();\n        if (*server == nullptr) {\n            ADD_FAILURE() << \"Failed to accept client connection.\";\n            return false;\n        }\n    }\n\n    return true;\n}\n\n// Sends a string over a Socket. Returns true if the full string (without terminating char)\n// was sent.\nstatic bool SendString(Socket* sock, const std::string& message) {\n    return sock->Send(message.c_str(), message.length());\n}\n\n// Receives a string from a Socket. Returns true if the full string (without terminating char)\n// was received.\nstatic bool ReceiveString(Socket* sock, const std::string& message) {\n    std::string received(message.length(), '\\0');\n    ssize_t bytes = sock->ReceiveAll(&received[0], received.length(), kTestTimeoutMs);\n    return static_cast<size_t>(bytes) == received.length() && received == message;\n}\n\n// Tests sending packets client -> server, then server -> client.\nTEST(SocketTest, TestSendAndReceive) {\n    std::unique_ptr<Socket> server, client;\n\n    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {\n        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));\n\n        EXPECT_TRUE(SendString(client.get(), \"foo\"));\n        EXPECT_TRUE(ReceiveString(server.get(), \"foo\"));\n\n        EXPECT_TRUE(SendString(server.get(), \"bar baz\"));\n        EXPECT_TRUE(ReceiveString(client.get(), \"bar baz\"));\n    }\n}\n\nTEST(SocketTest, TestReceiveTimeout) {\n    std::unique_ptr<Socket> server, client;\n    char buffer[16];\n\n    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {\n        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));\n\n        EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kShortTimeoutMs));\n        EXPECT_TRUE(server->ReceiveTimedOut());\n\n        EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));\n        EXPECT_TRUE(client->ReceiveTimedOut());\n    }\n\n    // UDP will wait for timeout if the other side closes.\n    ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));\n    EXPECT_EQ(0, server->Close());\n    EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));\n    EXPECT_TRUE(client->ReceiveTimedOut());\n}\n\nTEST(SocketTest, TestReceiveFailure) {\n    std::unique_ptr<Socket> server, client;\n    char buffer[16];\n\n    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {\n        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));\n\n        EXPECT_EQ(0, server->Close());\n        EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kTestTimeoutMs));\n        EXPECT_FALSE(server->ReceiveTimedOut());\n\n        EXPECT_EQ(0, client->Close());\n        EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));\n        EXPECT_FALSE(client->ReceiveTimedOut());\n    }\n\n    // TCP knows right away when the other side closes and returns 0 to indicate EOF.\n    ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kTcp, &server, &client));\n    EXPECT_EQ(0, server->Close());\n    EXPECT_EQ(0, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));\n    EXPECT_FALSE(client->ReceiveTimedOut());\n}\n\n// Tests sending and receiving large packets.\nTEST(SocketTest, TestLargePackets) {\n    std::string message(1024, '\\0');\n    std::unique_ptr<Socket> server, client;\n\n    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {\n        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));\n\n        // Run through the test a few times.\n        for (int i = 0; i < 10; ++i) {\n            // Use a different message each iteration to prevent false positives.\n            for (size_t j = 0; j < message.length(); ++j) {\n                message[j] = static_cast<char>(i + j);\n            }\n\n            EXPECT_TRUE(SendString(client.get(), message));\n            EXPECT_TRUE(ReceiveString(server.get(), message));\n        }\n    }\n}\n\n// Tests UDP receive overflow when the UDP packet is larger than the receive buffer.\nTEST(SocketTest, TestUdpReceiveOverflow) {\n    std::unique_ptr<Socket> server, client;\n    ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));\n\n    EXPECT_TRUE(SendString(client.get(), \"1234567890\"));\n\n    // This behaves differently on different systems, either truncating the packet or returning -1.\n    char buffer[5];\n    ssize_t bytes = server->Receive(buffer, 5, kTestTimeoutMs);\n    if (bytes == 5) {\n        EXPECT_EQ(0, memcmp(buffer, \"12345\", 5));\n    } else {\n        EXPECT_EQ(-1, bytes);\n    }\n}\n\n// Tests UDP multi-buffer send.\nTEST(SocketTest, TestUdpSendBuffers) {\n    std::unique_ptr<Socket> sock = Socket::NewServer(Socket::Protocol::kUdp, 0);\n    std::vector<std::string> data{\"foo\", \"bar\", \"12345\"};\n    std::vector<cutils_socket_buffer_t> buffers{{data[0].data(), data[0].length()},\n                                                {data[1].data(), data[1].length()},\n                                                {data[2].data(), data[2].length()}};\n    ssize_t mock_return_value = 0;\n\n    // Mock out socket_send_buffers() to verify we're sending in the correct buffers and\n    // return |mock_return_value|.\n    sock->socket_send_buffers_function_ = [&buffers, &mock_return_value](\n            cutils_socket_t /*cutils_sock*/, cutils_socket_buffer_t* sent_buffers,\n            size_t num_sent_buffers) -> ssize_t {\n        EXPECT_EQ(buffers.size(), num_sent_buffers);\n        for (size_t i = 0; i < num_sent_buffers; ++i) {\n            EXPECT_EQ(buffers[i].data, sent_buffers[i].data);\n            EXPECT_EQ(buffers[i].length, sent_buffers[i].length);\n        }\n        return mock_return_value;\n    };\n\n    mock_return_value = strlen(\"foobar12345\");\n    EXPECT_TRUE(sock->Send(buffers));\n\n    mock_return_value -= 1;\n    EXPECT_FALSE(sock->Send(buffers));\n\n    mock_return_value = 0;\n    EXPECT_FALSE(sock->Send(buffers));\n\n    mock_return_value = -1;\n    EXPECT_FALSE(sock->Send(buffers));\n}\n\n// Tests TCP re-sending until socket_send_buffers() sends all data. This is a little complicated,\n// but the general idea is that we intercept calls to socket_send_buffers() using a lambda mock\n// function that simulates partial writes.\nTEST(SocketTest, TestTcpSendBuffers) {\n    std::unique_ptr<Socket> sock = Socket::NewServer(Socket::Protocol::kTcp, 0);\n    std::vector<std::string> data{\"foo\", \"bar\", \"12345\"};\n    std::vector<cutils_socket_buffer_t> buffers{{data[0].data(), data[0].length()},\n                                                {data[1].data(), data[1].length()},\n                                                {data[2].data(), data[2].length()}};\n\n    // Test breaking up the buffered send at various points.\n    std::list<std::string> test_sends[] = {\n            // Successes.\n            {\"foobar12345\"},\n            {\"f\", \"oob\", \"ar12345\"},\n            {\"fo\", \"obar12\", \"345\"},\n            {\"foo\", \"bar12345\"},\n            {\"foob\", \"ar123\", \"45\"},\n            {\"f\", \"o\", \"o\", \"b\", \"a\", \"r\", \"1\", \"2\", \"3\", \"4\", \"5\"},\n\n            // Failures.\n            {},\n            {\"f\"},\n            {\"foo\", \"bar\"},\n            {\"fo\", \"obar12\"},\n            {\"foobar1234\"}\n    };\n\n    for (auto& test : test_sends) {\n        ssize_t bytes_sent = 0;\n        bool expect_success = true;\n\n        // Create a mock function for custom socket_send_buffers() behavior. This function will\n        // check to make sure the input buffers start at the next unsent byte, then return the\n        // number of bytes indicated by the next entry in |test|.\n        sock->socket_send_buffers_function_ = [&bytes_sent, &data, &expect_success, &test](\n                cutils_socket_t /*cutils_sock*/, cutils_socket_buffer_t* buffers,\n                size_t num_buffers) -> ssize_t {\n            EXPECT_TRUE(num_buffers > 0);\n\n            // Failure case - pretend we errored out before sending all the buffers.\n            if (test.empty()) {\n                expect_success = false;\n                return -1;\n            }\n\n            // Count the bytes we've sent to find where the next buffer should start and how many\n            // bytes should be left in it.\n            size_t byte_count = bytes_sent, data_index = 0;\n            while (data_index < data.size()) {\n                if (byte_count >= data[data_index].length()) {\n                    byte_count -= data[data_index].length();\n                    ++data_index;\n                } else {\n                    break;\n                }\n            }\n            void* expected_next_byte = &data[data_index][byte_count];\n            size_t expected_next_size = data[data_index].length() - byte_count;\n\n            EXPECT_EQ(data.size() - data_index, num_buffers);\n            EXPECT_EQ(expected_next_byte, buffers[0].data);\n            EXPECT_EQ(expected_next_size, buffers[0].length);\n\n            std::string to_send = std::move(test.front());\n            test.pop_front();\n            bytes_sent += to_send.length();\n            return to_send.length();\n        };\n\n        EXPECT_EQ(expect_success, sock->Send(buffers));\n        EXPECT_TRUE(test.empty());\n    }\n}\n\nTEST(SocketMockTest, TestSendSuccess) {\n    SocketMock mock;\n\n    mock.ExpectSend(\"foo\");\n    EXPECT_TRUE(SendString(&mock, \"foo\"));\n\n    mock.ExpectSend(\"abc\");\n    mock.ExpectSend(\"123\");\n    EXPECT_TRUE(SendString(&mock, \"abc\"));\n    EXPECT_TRUE(SendString(&mock, \"123\"));\n}\n\nTEST(SocketMockTest, TestSendFailure) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n\n    mock->ExpectSendFailure(\"foo\");\n    EXPECT_FALSE(SendString(mock.get(), \"foo\"));\n\n    EXPECT_NONFATAL_FAILURE(SendString(mock.get(), \"foo\"), \"no message was expected\");\n\n    mock->ExpectSend(\"foo\");\n    EXPECT_NONFATAL_FAILURE(SendString(mock.get(), \"bar\"), \"expected foo, but got bar\");\n    EXPECT_TRUE(SendString(mock.get(), \"foo\"));\n\n    mock->AddReceive(\"foo\");\n    EXPECT_NONFATAL_FAILURE(SendString(mock.get(), \"foo\"), \"called out-of-order\");\n    EXPECT_TRUE(ReceiveString(mock.get(), \"foo\"));\n\n    mock->ExpectSend(\"foo\");\n    EXPECT_NONFATAL_FAILURE(mock.reset(), \"1 event(s) were not handled\");\n}\n\nTEST(SocketMockTest, TestReceiveSuccess) {\n    SocketMock mock;\n\n    mock.AddReceive(\"foo\");\n    EXPECT_TRUE(ReceiveString(&mock, \"foo\"));\n\n    mock.AddReceive(\"abc\");\n    mock.AddReceive(\"123\");\n    EXPECT_TRUE(ReceiveString(&mock, \"abc\"));\n    EXPECT_TRUE(ReceiveString(&mock, \"123\"));\n\n    // Make sure ReceiveAll() can piece together multiple receives.\n    mock.AddReceive(\"foo\");\n    mock.AddReceive(\"bar\");\n    mock.AddReceive(\"123\");\n    EXPECT_TRUE(ReceiveString(&mock, \"foobar123\"));\n}\n\nTEST(SocketMockTest, TestReceiveFailure) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n\n    mock->AddReceiveFailure();\n    EXPECT_FALSE(ReceiveString(mock.get(), \"foo\"));\n    EXPECT_FALSE(mock->ReceiveTimedOut());\n\n    mock->AddReceiveTimeout();\n    EXPECT_FALSE(ReceiveString(mock.get(), \"foo\"));\n    EXPECT_TRUE(mock->ReceiveTimedOut());\n\n    mock->AddReceive(\"foo\");\n    mock->AddReceiveFailure();\n    EXPECT_FALSE(ReceiveString(mock.get(), \"foobar\"));\n\n    EXPECT_NONFATAL_FAILURE(ReceiveString(mock.get(), \"foo\"), \"no message was ready\");\n\n    mock->ExpectSend(\"foo\");\n    EXPECT_NONFATAL_FAILURE(ReceiveString(mock.get(), \"foo\"), \"called out-of-order\");\n    EXPECT_TRUE(SendString(mock.get(), \"foo\"));\n\n    char c;\n    mock->AddReceive(\"foo\");\n    EXPECT_NONFATAL_FAILURE(mock->Receive(&c, 1, 0), \"not enough bytes (1) for foo\");\n    EXPECT_TRUE(ReceiveString(mock.get(), \"foo\"));\n\n    mock->AddReceive(\"foo\");\n    EXPECT_NONFATAL_FAILURE(mock.reset(), \"1 event(s) were not handled\");\n}\n\nTEST(SocketMockTest, TestAcceptSuccess) {\n    SocketMock mock;\n\n    SocketMock* mock_handler = new SocketMock;\n    mock.AddAccept(std::unique_ptr<SocketMock>(mock_handler));\n    EXPECT_EQ(mock_handler, mock.Accept().get());\n\n    mock.AddAccept(nullptr);\n    EXPECT_EQ(nullptr, mock.Accept().get());\n}\n\nTEST(SocketMockTest, TestAcceptFailure) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n\n    EXPECT_NONFATAL_FAILURE(mock->Accept(), \"no socket was ready\");\n\n    mock->ExpectSend(\"foo\");\n    EXPECT_NONFATAL_FAILURE(mock->Accept(), \"called out-of-order\");\n    EXPECT_TRUE(SendString(mock.get(), \"foo\"));\n\n    mock->AddAccept(nullptr);\n    EXPECT_NONFATAL_FAILURE(mock.reset(), \"1 event(s) were not handled\");\n}\n"
  },
  {
    "path": "fastboot/storage.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n\n#include <fstream>\n#include <iterator>\n\n#include \"storage.h\"\n#include \"util.h\"\n\nConnectedDevicesStorage::ConnectedDevicesStorage() {\n    home_fastboot_path_ = GetHomeDirPath() + kPathSeparator + \".fastboot\";\n    devices_path_ = home_fastboot_path_ + kPathSeparator + \"devices\";\n\n    // We're using a separate file for locking because the Windows LockFileEx does not\n    // permit opening a file stream for the locked file, even within the same process. So,\n    // we have to use fd or handle API to manipulate the storage files, which makes it\n    // nearly impossible to fully rewrite a file content without having to recreate it.\n    // Unfortunately, this is not an option during holding a lock.\n    devices_lock_path_ = home_fastboot_path_ + kPathSeparator + \"devices.lock\";\n}\n\nbool ConnectedDevicesStorage::Exists() const {\n    return FileExists(devices_path_);\n}\n\nvoid ConnectedDevicesStorage::WriteDevices(const FileLock&, const std::set<std::string>& devices) {\n    std::ofstream devices_stream(devices_path_);\n    std::copy(devices.begin(), devices.end(),\n              std::ostream_iterator<std::string>(devices_stream, \"\\n\"));\n}\n\nstd::set<std::string> ConnectedDevicesStorage::ReadDevices(const FileLock&) {\n    std::ifstream devices_stream(devices_path_);\n    std::istream_iterator<std::string> start(devices_stream), end;\n    std::set<std::string> devices(start, end);\n    return devices;\n}\n\nvoid ConnectedDevicesStorage::Clear(const FileLock&) {\n    if (!android::base::RemoveFileIfExists(devices_path_)) {\n        LOG(FATAL) << \"Failed to clear connected device list: \" << devices_path_;\n    }\n}\n\nFileLock ConnectedDevicesStorage::Lock() const {\n    if (!EnsureDirectoryExists(home_fastboot_path_)) {\n        LOG(FATAL) << \"Cannot create directory: \" << home_fastboot_path_;\n    }\n    return FileLock(devices_lock_path_);\n}\n"
  },
  {
    "path": "fastboot/storage.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <set>\n#include <string>\n\n#include \"filesystem.h\"\n\nclass ConnectedDevicesStorage {\n  public:\n    ConnectedDevicesStorage();\n\n    bool Exists() const;\n    void WriteDevices(const FileLock&, const std::set<std::string>& devices);\n    std::set<std::string> ReadDevices(const FileLock&);\n    void Clear(const FileLock&);\n    FileLock Lock() const;\n\n  private:\n    std::string home_fastboot_path_;\n    std::string devices_path_;\n    std::string devices_lock_path_;\n};"
  },
  {
    "path": "fastboot/super_flash_helper.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"super_flash_helper.h\"\n\n#include <android-base/logging.h>\n\n#include \"util.h\"\n\nusing android::base::borrowed_fd;\nusing android::base::unique_fd;\nusing android::fs_mgr::SuperImageExtent;\n\nSuperFlashHelper::SuperFlashHelper(const ImageSource& source) : source_(source) {}\n\nbool SuperFlashHelper::Open(borrowed_fd fd) {\n    if (!builder_.Open(fd)) {\n        LOG(VERBOSE) << \"device does not support optimized super flashing\";\n        return false;\n    }\n\n    base_metadata_ = builder_.Export();\n    return !!base_metadata_;\n}\n\nbool SuperFlashHelper::IncludeInSuper(const std::string& partition) {\n    return should_flash_in_userspace(*base_metadata_.get(), partition);\n}\n\nbool SuperFlashHelper::AddPartition(const std::string& partition, const std::string& image_name,\n                                    bool optional) {\n    if (!IncludeInSuper(partition)) {\n        return true;\n    }\n    auto iter = image_fds_.find(image_name);\n    if (iter == image_fds_.end()) {\n        unique_fd fd = source_.OpenFile(image_name);\n        if (fd < 0) {\n            if (!optional) {\n                LOG(VERBOSE) << \"could not find partition image: \" << image_name;\n                return false;\n            }\n            return true;\n        }\n        if (is_sparse_file(fd)) {\n            LOG(VERBOSE) << \"cannot optimize dynamic partitions with sparse images\";\n            return false;\n        }\n        iter = image_fds_.emplace(image_name, std::move(fd)).first;\n    }\n\n    if (!builder_.AddPartition(partition, image_name, get_file_size(iter->second))) {\n        return false;\n    }\n\n    will_flash_.emplace(partition);\n    return true;\n}\n\nSparsePtr SuperFlashHelper::GetSparseLayout() {\n    // Cache extents since the sparse ptr depends on data pointers.\n    if (extents_.empty()) {\n        extents_ = builder_.GetImageLayout();\n        if (extents_.empty()) {\n            LOG(VERBOSE) << \"device does not support optimized super flashing\";\n            return {nullptr, nullptr};\n        }\n    }\n\n    unsigned int block_size = base_metadata_->geometry.logical_block_size;\n    int64_t flashed_size = extents_.back().offset + extents_.back().size;\n    SparsePtr s(sparse_file_new(block_size, flashed_size), sparse_file_destroy);\n\n    for (const auto& extent : extents_) {\n        if (extent.offset / block_size > UINT_MAX) {\n            // Super image is too big to send via sparse files (>8TB).\n            LOG(VERBOSE) << \"super image is too big to flash\";\n            return {nullptr, nullptr};\n        }\n        unsigned int block = extent.offset / block_size;\n\n        int rv = 0;\n        switch (extent.type) {\n            case SuperImageExtent::Type::DONTCARE:\n                break;\n            case SuperImageExtent::Type::ZERO:\n                rv = sparse_file_add_fill(s.get(), 0, extent.size, block);\n                break;\n            case SuperImageExtent::Type::DATA:\n                rv = sparse_file_add_data(s.get(), extent.blob->data(), extent.size, block);\n                break;\n            case SuperImageExtent::Type::PARTITION: {\n                auto iter = image_fds_.find(extent.image_name);\n                if (iter == image_fds_.end()) {\n                    LOG(FATAL) << \"image added but not found: \" << extent.image_name;\n                    return {nullptr, nullptr};\n                }\n                rv = sparse_file_add_fd(s.get(), iter->second.get(), extent.image_offset,\n                                        extent.size, block);\n                break;\n            }\n            default:\n                LOG(VERBOSE) << \"unrecognized extent type in super image layout\";\n                return {nullptr, nullptr};\n        }\n        if (rv) {\n            LOG(VERBOSE) << \"sparse failure building super image layout\";\n            return {nullptr, nullptr};\n        }\n    }\n    return s;\n}\n"
  },
  {
    "path": "fastboot/super_flash_helper.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include <android-base/unique_fd.h>\n#include <liblp/liblp.h>\n#include <liblp/super_layout_builder.h>\n\n#include \"util.h\"\n\nclass SuperFlashHelper final {\n  public:\n    explicit SuperFlashHelper(const ImageSource& source);\n\n    bool Open(android::base::borrowed_fd fd);\n    bool IncludeInSuper(const std::string& partition);\n    bool AddPartition(const std::string& partition, const std::string& image_name, bool optional);\n\n    // Note: the SparsePtr if non-null should not outlive SuperFlashHelper, since\n    // it depends on open fds and data pointers.\n    SparsePtr GetSparseLayout();\n\n    bool WillFlash(const std::string& partition) const {\n        return will_flash_.find(partition) != will_flash_.end();\n    }\n\n  private:\n    const ImageSource& source_;\n    android::fs_mgr::SuperLayoutBuilder builder_;\n    std::unique_ptr<android::fs_mgr::LpMetadata> base_metadata_;\n    std::vector<android::fs_mgr::SuperImageExtent> extents_;\n\n    // Cache open image fds. This keeps them alive while we flash the sparse\n    // file.\n    std::unordered_map<std::string, android::base::unique_fd> image_fds_;\n    std::unordered_set<std::string> will_flash_;\n};\n"
  },
  {
    "path": "fastboot/super_flash_helper_test.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"super_flash_helper.h\"\n\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n#include <sparse/sparse.h>\n\nusing android::base::unique_fd;\n\nunique_fd OpenTestFile(const std::string& file, int flags) {\n    std::string path = \"testdata/\" + file;\n\n    unique_fd fd(open(path.c_str(), flags));\n    if (fd >= 0) {\n        return fd;\n    }\n\n    path = android::base::GetExecutableDirectory() + \"/\" + path;\n    return unique_fd{open(path.c_str(), flags)};\n}\n\nclass TestImageSource final : public ImageSource {\n  public:\n    bool ReadFile(const std::string&, std::vector<char>*) const override {\n        // Not used here.\n        return false;\n    }\n    unique_fd OpenFile(const std::string& name) const override {\n        return OpenTestFile(name, O_RDONLY | O_CLOEXEC);\n    }\n};\n\nTEST(SuperFlashHelper, ImageEquality) {\n    auto super_empty_fd = OpenTestFile(\"super_empty.img\", O_RDONLY);\n    ASSERT_GE(super_empty_fd, 0);\n\n    TestImageSource source;\n    SuperFlashHelper helper(source);\n    ASSERT_TRUE(helper.Open(super_empty_fd));\n    ASSERT_TRUE(helper.AddPartition(\"system_a\", \"system.img\", false));\n\n    auto sparse_file = helper.GetSparseLayout();\n    ASSERT_NE(sparse_file, nullptr);\n\n    TemporaryFile fb_super;\n    ASSERT_GE(fb_super.fd, 0);\n    ASSERT_EQ(sparse_file_write(sparse_file.get(), fb_super.fd, false, false, false), 0);\n\n    auto real_super_fd = OpenTestFile(\"super.img\", O_RDONLY);\n    ASSERT_GE(real_super_fd, 0);\n\n    std::string expected(get_file_size(real_super_fd), '\\0');\n    ASSERT_FALSE(expected.empty());\n    ASSERT_TRUE(android::base::ReadFully(real_super_fd, expected.data(), expected.size()));\n\n    std::string actual(get_file_size(fb_super.fd), '\\0');\n    ASSERT_FALSE(actual.empty());\n    ASSERT_EQ(lseek(fb_super.fd, 0, SEEK_SET), 0);\n    ASSERT_TRUE(android::base::ReadFully(fb_super.fd, actual.data(), actual.size()));\n\n    // The helper doesn't add any extra zeroes to the image, whereas lpmake does, to\n    // pad to the entire super size.\n    ASSERT_LE(actual.size(), expected.size());\n    for (size_t i = 0; i < actual.size(); i++) {\n        ASSERT_EQ(actual[i], expected[i]) << \"byte mismatch at position \" << i;\n    }\n    for (size_t i = actual.size(); i < expected.size(); i++) {\n        ASSERT_EQ(expected[i], 0) << \"byte mismatch at position \" << i;\n    }\n}\n"
  },
  {
    "path": "fastboot/task.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#include \"task.h\"\n\n#include \"fastboot_driver.h\"\n\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n\n#include \"fastboot.h\"\n#include \"filesystem.h\"\n#include \"super_flash_helper.h\"\n#include \"util.h\"\n\nusing namespace std::string_literals;\nFlashTask::FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,\n                     const bool apply_vbmeta, const FlashingPlan* fp)\n    : pname_(pname), fname_(fname), slot_(slot), apply_vbmeta_(apply_vbmeta), fp_(fp) {}\n\nbool FlashTask::IsDynamicPartition(const ImageSource* source, const FlashTask* task) {\n    std::vector<char> contents;\n    if (!source->ReadFile(\"super_empty.img\", &contents)) {\n        return false;\n    }\n    auto metadata = android::fs_mgr::ReadFromImageBlob(contents.data(), contents.size());\n    return should_flash_in_userspace(*metadata.get(), task->GetPartitionAndSlot());\n}\n\nvoid FlashTask::Run() {\n    auto flash = [&](const std::string& partition) {\n        if (should_flash_in_userspace(fp_->source.get(), partition) && !is_userspace_fastboot() &&\n            !fp_->force_flash) {\n            die(\"The partition you are trying to flash is dynamic, and \"\n                \"should be flashed via fastbootd. Please run:\\n\"\n                \"\\n\"\n                \"    fastboot reboot fastboot\\n\"\n                \"\\n\"\n                \"And try again. If you are intentionally trying to \"\n                \"overwrite a fixed partition, use --force.\");\n        }\n        do_flash(partition.c_str(), fname_.c_str(), apply_vbmeta_, fp_);\n    };\n    do_for_partitions(pname_, slot_, flash, true);\n}\n\nstd::string FlashTask::ToString() const {\n    std::string apply_vbmeta_string = \"\";\n    if (apply_vbmeta_) {\n        apply_vbmeta_string = \" --apply_vbmeta\";\n    }\n    return \"flash\" + apply_vbmeta_string + \" \" + pname_ + \" \" + fname_;\n}\n\nstd::string FlashTask::GetPartitionAndSlot() const {\n    auto slot = slot_;\n    if (slot.empty()) {\n        slot = get_current_slot();\n    }\n    if (slot.empty()) {\n        return pname_;\n    }\n    if (slot == \"all\") {\n        LOG(FATAL) << \"Cannot retrieve a singular name when using all slots\";\n    }\n    return pname_ + \"_\" + slot;\n}\n\nRebootTask::RebootTask(const FlashingPlan* fp) : fp_(fp){};\nRebootTask::RebootTask(const FlashingPlan* fp, const std::string& reboot_target)\n    : reboot_target_(reboot_target), fp_(fp){};\n\nvoid RebootTask::Run() {\n    if (reboot_target_ == \"fastboot\") {\n        if (!is_userspace_fastboot()) {\n            reboot_to_userspace_fastboot();\n            fp_->fb->WaitForDisconnect();\n        }\n    } else if (reboot_target_ == \"recovery\") {\n        fp_->fb->RebootTo(\"recovery\");\n        fp_->fb->WaitForDisconnect();\n    } else if (reboot_target_ == \"bootloader\") {\n        fp_->fb->RebootTo(\"bootloader\");\n        fp_->fb->WaitForDisconnect();\n    } else if (reboot_target_ == \"\") {\n        fp_->fb->Reboot();\n        fp_->fb->WaitForDisconnect();\n    } else {\n        syntax_error(\"unknown reboot target %s\", reboot_target_.c_str());\n    }\n}\n\nstd::string RebootTask::ToString() const {\n    return \"reboot \" + reboot_target_;\n}\n\nOptimizedFlashSuperTask::OptimizedFlashSuperTask(const std::string& super_name,\n                                                 std::unique_ptr<SuperFlashHelper> helper,\n                                                 SparsePtr sparse_layout, uint64_t super_size,\n                                                 const FlashingPlan* fp)\n    : super_name_(super_name),\n      helper_(std::move(helper)),\n      sparse_layout_(std::move(sparse_layout)),\n      super_size_(super_size),\n      fp_(fp) {}\n\nvoid OptimizedFlashSuperTask::Run() {\n    // Use the reported super partition size as the upper limit, rather than\n    // sparse_file_len, which (1) can fail and (2) is kind of expensive, since\n    // it will map in all of the embedded fds.\n    std::vector<SparsePtr> files;\n    if (int limit = get_sparse_limit(super_size_, fp_)) {\n        files = resparse_file(sparse_layout_.get(), limit);\n    } else {\n        files.emplace_back(std::move(sparse_layout_));\n    }\n\n    // Send the data to the device.\n    flash_partition_files(super_name_, files);\n}\n\nstd::string OptimizedFlashSuperTask::ToString() const {\n    return \"optimized-flash-super\";\n}\n\n// This looks for a block within tasks that has the following pattern [reboot fastboot,\n// update-super, $LIST_OF_DYNAMIC_FLASH_TASKS] and returns true if this is found.Theoretically\n// this check is just a pattern match and could break if fastboot-info has a bunch of junk commands\n// but all devices should pretty much follow this pattern\nbool OptimizedFlashSuperTask::CanOptimize(const ImageSource* source,\n                                          const std::vector<std::unique_ptr<Task>>& tasks) {\n    for (size_t i = 0; i < tasks.size(); i++) {\n        auto reboot_task = tasks[i]->AsRebootTask();\n        if (!reboot_task || reboot_task->GetTarget() != \"fastboot\") {\n            continue;\n        }\n        // The check for i >= tasks.size() - 2 is because we are peeking two tasks ahead. We need to\n        // check for an update-super && flash {dynamic_partition}\n        if (i >= tasks.size() - 2 || !tasks[i + 1]->AsUpdateSuperTask()) {\n            continue;\n        }\n        auto flash_task = tasks[i + 2]->AsFlashTask();\n        if (!FlashTask::IsDynamicPartition(source, flash_task)) {\n            continue;\n        }\n        return true;\n    }\n    return false;\n}\n\nstd::unique_ptr<OptimizedFlashSuperTask> OptimizedFlashSuperTask::Initialize(\n        const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks) {\n    if (!fp->should_optimize_flash_super) {\n        LOG(INFO) << \"super optimization is disabled\";\n        return nullptr;\n    }\n    if (!supports_AB(fp->fb)) {\n        LOG(VERBOSE) << \"Cannot optimize flashing super on non-AB device\";\n        return nullptr;\n    }\n    if (fp->slot_override == \"all\") {\n        LOG(VERBOSE) << \"Cannot optimize flashing super for all slots\";\n        return nullptr;\n    }\n    if (!CanOptimize(fp->source.get(), tasks)) {\n        return nullptr;\n    }\n\n    // Does this device use dynamic partitions at all?\n    unique_fd fd = fp->source->OpenFile(\"super_empty.img\");\n\n    if (fd < 0) {\n        LOG(VERBOSE) << \"could not open super_empty.img\";\n        return nullptr;\n    }\n\n    std::string super_name;\n    // Try to find whether there is a super partition.\n    if (fp->fb->GetVar(\"super-partition-name\", &super_name) != fastboot::SUCCESS) {\n        super_name = \"super\";\n    }\n    uint64_t partition_size;\n    std::string partition_size_str;\n    if (fp->fb->GetVar(\"partition-size:\" + super_name, &partition_size_str) != fastboot::SUCCESS) {\n        LOG(VERBOSE) << \"Cannot optimize super flashing: could not determine super partition\";\n        return nullptr;\n    }\n    partition_size_str = fb_fix_numeric_var(partition_size_str);\n    if (!android::base::ParseUint(partition_size_str, &partition_size)) {\n        LOG(VERBOSE) << \"Could not parse \" << super_name << \" size: \" << partition_size_str;\n        return nullptr;\n    }\n\n    std::unique_ptr<SuperFlashHelper> helper = std::make_unique<SuperFlashHelper>(*fp->source);\n    if (!helper->Open(fd)) {\n        return nullptr;\n    }\n\n    for (const auto& task : tasks) {\n        if (auto flash_task = task->AsFlashTask()) {\n            auto partition = flash_task->GetPartitionAndSlot();\n            if (!helper->AddPartition(partition, flash_task->GetImageName(), false)) {\n                return nullptr;\n            }\n        }\n    }\n\n    auto s = helper->GetSparseLayout();\n    if (!s) return nullptr;\n\n    // Remove tasks that are concatenated into this optimized task\n    auto remove_if_callback = [&](const auto& task) -> bool {\n        if (auto flash_task = task->AsFlashTask()) {\n            return helper->WillFlash(flash_task->GetPartitionAndSlot());\n        } else if (task->AsUpdateSuperTask()) {\n            return true;\n        } else if (auto reboot_task = task->AsRebootTask()) {\n            if (reboot_task->GetTarget() == \"fastboot\") {\n                return true;\n            }\n        }\n        return false;\n    };\n\n    tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());\n\n    return std::make_unique<OptimizedFlashSuperTask>(super_name, std::move(helper), std::move(s),\n                                                     partition_size, fp);\n}\n\nUpdateSuperTask::UpdateSuperTask(const FlashingPlan* fp) : fp_(fp) {}\n\nvoid UpdateSuperTask::Run() {\n    unique_fd fd = fp_->source->OpenFile(\"super_empty.img\");\n    if (fd < 0) {\n        return;\n    }\n    if (!is_userspace_fastboot()) {\n        reboot_to_userspace_fastboot();\n    }\n\n    std::string super_name;\n    if (fp_->fb->GetVar(\"super-partition-name\", &super_name) != fastboot::RetCode::SUCCESS) {\n        super_name = \"super\";\n    }\n    fp_->fb->Download(super_name, fd, get_file_size(fd));\n\n    std::string command = \"update-super:\" + super_name;\n    if (fp_->wants_wipe) {\n        command += \":wipe\";\n    }\n    fp_->fb->RawCommand(command, \"Updating super partition\");\n}\nstd::string UpdateSuperTask::ToString() const {\n    return \"update-super\";\n}\n\nResizeTask::ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,\n                       const std::string& slot)\n    : fp_(fp), pname_(pname), size_(size), slot_(slot) {}\n\nvoid ResizeTask::Run() {\n    auto resize_partition = [this](const std::string& partition) -> void {\n        if (is_logical(partition)) {\n            fp_->fb->ResizePartition(partition, size_);\n        }\n    };\n    do_for_partitions(pname_, slot_, resize_partition, false);\n}\n\nstd::string ResizeTask::ToString() const {\n    return \"resize \" + pname_;\n}\n\nDeleteTask::DeleteTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};\n\nvoid DeleteTask::Run() {\n    fp_->fb->DeletePartition(pname_);\n}\n\nstd::string DeleteTask::ToString() const {\n    return \"delete \" + pname_;\n}\n\nWipeTask::WipeTask(const FlashingPlan* fp, const std::string& pname) : fp_(fp), pname_(pname){};\n\nvoid WipeTask::Run() {\n    std::string partition_type;\n    if (fp_->fb->GetVar(\"partition-type:\" + pname_, &partition_type) != fastboot::SUCCESS) {\n        LOG(ERROR) << \"wipe task partition not found: \" << pname_;\n        return;\n    }\n    if (partition_type.empty()) return;\n    if (fp_->fb->Erase(pname_) != fastboot::SUCCESS) {\n        LOG(ERROR) << \"wipe task erase failed with partition: \" << pname_;\n        return;\n    }\n    fb_perform_format(pname_, 1, partition_type, \"\", fp_->fs_options, fp_);\n}\n\nstd::string WipeTask::ToString() const {\n    return \"erase \" + pname_;\n}\n"
  },
  {
    "path": "fastboot/task.h",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#pragma once\n\n#include <string>\n\n#include \"super_flash_helper.h\"\n#include \"util.h\"\n\nstruct FlashingPlan;\nstruct Image;\nusing ImageEntry = std::pair<const Image*, std::string>;\n\nclass FlashTask;\nclass RebootTask;\nclass UpdateSuperTask;\nclass OptimizedFlashSuperTask;\nclass WipeTask;\nclass ResizeTask;\nclass Task {\n  public:\n    Task() = default;\n    virtual void Run() = 0;\n    virtual std::string ToString() const = 0;\n\n    virtual FlashTask* AsFlashTask() { return nullptr; }\n    virtual RebootTask* AsRebootTask() { return nullptr; }\n    virtual UpdateSuperTask* AsUpdateSuperTask() { return nullptr; }\n    virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() { return nullptr; }\n    virtual WipeTask* AsWipeTask() { return nullptr; }\n    virtual ResizeTask* AsResizeTask() { return nullptr; }\n\n    virtual ~Task() = default;\n};\n\nclass FlashTask : public Task {\n  public:\n    FlashTask(const std::string& slot, const std::string& pname, const std::string& fname,\n              const bool apply_vbmeta, const FlashingPlan* fp);\n    virtual FlashTask* AsFlashTask() override { return this; }\n\n    static bool IsDynamicPartition(const ImageSource* source, const FlashTask* task);\n    void Run() override;\n    std::string ToString() const override;\n    std::string GetPartition() const { return pname_; }\n    std::string GetImageName() const { return fname_; }\n    std::string GetSlot() const { return slot_; }\n    std::string GetPartitionAndSlot() const;\n\n  private:\n    const std::string pname_;\n    const std::string fname_;\n    const std::string slot_;\n    const bool apply_vbmeta_;\n    const FlashingPlan* fp_;\n};\n\nclass RebootTask : public Task {\n  public:\n    RebootTask(const FlashingPlan* fp);\n    RebootTask(const FlashingPlan* fp, const std::string& reboot_target);\n    virtual RebootTask* AsRebootTask() override { return this; }\n    void Run() override;\n    std::string ToString() const override;\n    std::string GetTarget() const { return reboot_target_; };\n\n  private:\n    const std::string reboot_target_ = \"\";\n    const FlashingPlan* fp_;\n};\n\nclass OptimizedFlashSuperTask : public Task {\n  public:\n    OptimizedFlashSuperTask(const std::string& super_name, std::unique_ptr<SuperFlashHelper> helper,\n                            SparsePtr sparse_layout, uint64_t super_size, const FlashingPlan* fp);\n    virtual OptimizedFlashSuperTask* AsOptimizedFlashSuperTask() override { return this; }\n\n    static std::unique_ptr<OptimizedFlashSuperTask> Initialize(\n            const FlashingPlan* fp, std::vector<std::unique_ptr<Task>>& tasks);\n    static bool CanOptimize(const ImageSource* source,\n                            const std::vector<std::unique_ptr<Task>>& tasks);\n\n    void Run() override;\n    std::string ToString() const override;\n\n  private:\n    const std::string super_name_;\n    std::unique_ptr<SuperFlashHelper> helper_;\n    SparsePtr sparse_layout_;\n    uint64_t super_size_;\n    const FlashingPlan* fp_;\n};\n\nclass UpdateSuperTask : public Task {\n  public:\n    UpdateSuperTask(const FlashingPlan* fp);\n    virtual UpdateSuperTask* AsUpdateSuperTask() override { return this; }\n\n    void Run() override;\n    std::string ToString() const override;\n\n  private:\n    const FlashingPlan* fp_;\n};\n\nclass ResizeTask : public Task {\n  public:\n    ResizeTask(const FlashingPlan* fp, const std::string& pname, const std::string& size,\n               const std::string& slot);\n    void Run() override;\n    std::string ToString() const override;\n    virtual ResizeTask* AsResizeTask() override { return this; }\n\n  private:\n    const FlashingPlan* fp_;\n    const std::string pname_;\n    const std::string size_;\n    const std::string slot_;\n};\n\nclass DeleteTask : public Task {\n  public:\n    DeleteTask(const FlashingPlan* fp, const std::string& pname);\n    void Run() override;\n    std::string ToString() const override;\n\n  private:\n    const FlashingPlan* fp_;\n    const std::string pname_;\n};\n\nclass WipeTask : public Task {\n  public:\n    WipeTask(const FlashingPlan* fp, const std::string& pname);\n    virtual WipeTask* AsWipeTask() override { return this; }\n    void Run() override;\n    std::string ToString() const override;\n\n  private:\n    const FlashingPlan* fp_;\n    const std::string pname_;\n};\n"
  },
  {
    "path": "fastboot/task_test.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"task.h\"\n#include \"fastboot.h\"\n#include \"fastboot_driver_mock.h\"\n\n#include <gtest/gtest.h>\n#include <iostream>\n#include <memory>\n#include \"android-base/strings.h\"\n#include \"gmock/gmock.h\"\n\nusing android::base::Split;\nusing testing::_;\n\nclass ParseTest : public ::testing ::Test {\n  protected:\n    void SetUp() override {\n        fp = std::make_unique<FlashingPlan>();\n        fp->slot_override = \"b\";\n        fp->secondary_slot = \"a\";\n        fp->wants_wipe = false;\n    }\n    void TearDown() override {}\n\n    std::unique_ptr<FlashingPlan> fp;\n\n  private:\n};\n\nstatic std::vector<std::unique_ptr<Task>> collectTasks(FlashingPlan* fp,\n                                                       const std::vector<std::string>& commands) {\n    std::vector<std::vector<std::string>> vec_commands;\n    for (auto& command : commands) {\n        vec_commands.emplace_back(android::base::Split(command, \" \"));\n    }\n    std::vector<std::unique_ptr<Task>> tasks;\n    for (auto& command : vec_commands) {\n        tasks.emplace_back(ParseFastbootInfoLine(fp, command));\n    }\n    return tasks;\n}\n\nstd::unique_ptr<Task> ParseCommand(FlashingPlan* fp, std::string command) {\n    std::vector<std::string> vec_command = android::base::Split(command, \" \");\n    return ParseFastbootInfoLine(fp, vec_command);\n}\n\n// tests if tasks_a is a superset of tasks_b. Used for checking to ensure all partitions flashed\n// from hardcoded image list is also flashed in new fastboot-info.txt\nstatic bool compareTaskList(std::vector<std::unique_ptr<Task>>& tasks_a,\n                            std::vector<std::unique_ptr<Task>>& tasks_b) {\n    std::set<std::string> list;\n    for (auto& task : tasks_a) {\n        list.insert(task->ToString());\n    }\n    for (auto& task : tasks_b) {\n        if (list.find(task->ToString()) == list.end()) {\n            std::cout << \"ERROR: \" << task->ToString()\n                      << \" not found in task list created by fastboot-info.txt\";\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic std::string tasksToString(std::vector<std::unique_ptr<Task>>& tasks) {\n    std::string output;\n    for (auto& task : tasks) {\n        output.append(task->ToString());\n        output.append(\"\\n\");\n    }\n    return output;\n}\n\nTEST_F(ParseTest, CorrectFlashTaskFormed) {\n    std::vector<std::string> commands = {\"flash dtbo\", \"flash --slot-other system system_other.img\",\n                                         \"flash system\", \"flash --apply-vbmeta vbmeta\"};\n\n    std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);\n\n    std::vector<std::vector<std::string>> expected_values{\n            {\"dtbo\", \"dtbo_b\", \"b\", \"dtbo.img\"},\n            {\"system\", \"system_a\", \"a\", \"system_other.img\"},\n            {\"system\", \"system_b\", \"b\", \"system.img\"},\n            {\"vbmeta\", \"vbmeta_b\", \"b\", \"vbmeta.img\"}\n\n    };\n\n    for (auto& task : tasks) {\n        ASSERT_TRUE(task != nullptr);\n    }\n\n    for (size_t i = 0; i < tasks.size(); i++) {\n        auto task = tasks[i]->AsFlashTask();\n        ASSERT_TRUE(task != nullptr);\n        ASSERT_EQ(task->GetPartition(), expected_values[i][0]);\n        ASSERT_EQ(task->GetPartitionAndSlot(), expected_values[i][1]);\n        ASSERT_EQ(task->GetSlot(), expected_values[i][2]);\n        ASSERT_EQ(task->GetImageName(), expected_values[i][3]);\n    }\n}\n\nTEST_F(ParseTest, VersionCheckCorrect) {\n    std::vector<std::string> correct_versions = {\"version 1\", \"version 22\", \"version 5\",\n                                                 \"version 17\"};\n\n    std::vector<std::string> bad_versions = {\"version\",         \"version .01\",    \"version x1\",\n                                             \"version 1.0.1\",   \"version 1.\",     \"s 1.0\",\n                                             \"version 1.0 2.0\", \"version 100.00\", \"version 1 2\"};\n\n    for (auto& version : correct_versions) {\n        ASSERT_TRUE(CheckFastbootInfoRequirements(android::base::Split(version, \" \"), 26))\n                << version;\n    }\n\n    // returning False for failing version check\n    for (auto& version : correct_versions) {\n        ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, \" \"), 0))\n                << version;\n    }\n    // returning False for bad format\n    for (auto& version : bad_versions) {\n        ASSERT_FALSE(CheckFastbootInfoRequirements(android::base::Split(version, \" \"), 100))\n                << version;\n    }\n}\n\nTEST_F(ParseTest, BadFastbootInput) {\n    ASSERT_EQ(ParseCommand(fp.get(), \"flash\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"flash --slot-other --apply-vbmeta\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"flash --apply-vbmeta\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"if-wipe\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"if-wipe flash\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"wipe dtbo\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"update-super dtbo\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"flash system system.img system\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"reboot bootloader fastboot\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(),\n                           \"flash --slot-other --apply-vbmeta system system_other.img system\"),\n              nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"erase\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"erase dtbo dtbo\"), nullptr);\n    ASSERT_EQ(ParseCommand(fp.get(), \"wipe this\"), nullptr);\n}\n\nTEST_F(ParseTest, CorrectTaskFormed) {\n    std::vector<std::string> commands = {\"flash dtbo\", \"flash --slot-other system system_other.img\",\n                                         \"reboot bootloader\", \"update-super\", \"erase cache\"};\n    std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);\n\n    ASSERT_TRUE(tasks[0]->AsFlashTask());\n    ASSERT_TRUE(tasks[0]->AsFlashTask());\n    ASSERT_TRUE(tasks[1]->AsFlashTask());\n    ASSERT_TRUE(tasks[2]->AsRebootTask());\n    ASSERT_TRUE(tasks[3]->AsUpdateSuperTask());\n    ASSERT_TRUE(tasks[4]->AsWipeTask());\n}\n\nTEST_F(ParseTest, CorrectDriverCalls) {\n    fastboot::MockFastbootDriver fb;\n    fp->fb = &fb;\n\n    EXPECT_CALL(fb, RebootTo(_, _, _)).Times(1);\n    EXPECT_CALL(fb, Reboot(_, _)).Times(1);\n    EXPECT_CALL(fb, WaitForDisconnect()).Times(2);\n\n    std::vector<std::string> commands = {\"reboot bootloader\", \"reboot\"};\n    std::vector<std::unique_ptr<Task>> tasks = collectTasks(fp.get(), commands);\n\n    for (auto& task : tasks) {\n        task->Run();\n    }\n}\n\nTEST_F(ParseTest, CorrectTaskLists) {\n    if (!get_android_product_out()) {\n        GTEST_SKIP();\n    }\n\n    fp->source.reset(new LocalImageSource);\n    fp->sparse_limit = std::numeric_limits<int64_t>::max();\n\n    fastboot::MockFastbootDriver fb;\n    fp->fb = &fb;\n    fp->should_optimize_flash_super = false;\n\n    ON_CALL(fb, GetVar(\"super-partition-name\", _, _))\n            .WillByDefault(testing::Return(fastboot::BAD_ARG));\n\n    FlashAllTool tool(fp.get());\n\n    fp->should_use_fastboot_info = false;\n    auto hardcoded_tasks = tool.CollectTasks();\n    fp->should_use_fastboot_info = true;\n    auto fastboot_info_tasks = tool.CollectTasks();\n\n    auto is_non_flash_task = [](const auto& task) -> bool {\n        return task->AsFlashTask() == nullptr;\n    };\n\n    // remove non flash tasks for testing purposes\n    hardcoded_tasks.erase(\n            std::remove_if(hardcoded_tasks.begin(), hardcoded_tasks.end(), is_non_flash_task),\n            hardcoded_tasks.end());\n    fastboot_info_tasks.erase(std::remove_if(fastboot_info_tasks.begin(), fastboot_info_tasks.end(),\n                                             is_non_flash_task),\n                              fastboot_info_tasks.end());\n\n    if (!compareTaskList(fastboot_info_tasks, hardcoded_tasks)) {\n        std::cout << \"\\n\\n---Hardcoded Task List---\\n\"\n                  << tasksToString(hardcoded_tasks) << \"\\n---Fastboot-Info Task List---\\n\"\n                  << tasksToString(fastboot_info_tasks);\n    }\n\n    ASSERT_TRUE(compareTaskList(fastboot_info_tasks, hardcoded_tasks));\n\n    ASSERT_TRUE(fastboot_info_tasks.size() >= hardcoded_tasks.size())\n            << \"size of fastboot-info task list: \" << fastboot_info_tasks.size()\n            << \" size of hardcoded task list: \" << hardcoded_tasks.size();\n}\nTEST_F(ParseTest, IsDynamicPartitiontest) {\n    if (!get_android_product_out()) {\n        GTEST_SKIP();\n    }\n\n    fp->source.reset(new LocalImageSource);\n\n    fastboot::MockFastbootDriver fb;\n    fp->fb = &fb;\n    fp->should_optimize_flash_super = true;\n    fp->should_use_fastboot_info = true;\n\n    std::vector<std::pair<std::string, bool>> test_cases = {\n            {\"flash boot\", false},\n            {\"flash init_boot\", false},\n            {\"flash --apply-vbmeta vbmeta\", false},\n            {\"flash product\", true},\n            {\"flash system\", true},\n            {\"flash --slot-other system system_other.img\", true},\n    };\n    for (auto& test : test_cases) {\n        std::unique_ptr<Task> task =\n                ParseFastbootInfoLine(fp.get(), android::base::Tokenize(test.first, \" \"));\n        auto flash_task = task->AsFlashTask();\n        ASSERT_FALSE(flash_task == nullptr);\n        ASSERT_EQ(FlashTask::IsDynamicPartition(fp->source.get(), flash_task), test.second);\n    }\n}\n\nTEST_F(ParseTest, CanOptimizeTest) {\n    if (!get_android_product_out()) {\n        GTEST_SKIP();\n    }\n\n    fp->source.reset(new LocalImageSource);\n    fp->sparse_limit = std::numeric_limits<int64_t>::max();\n\n    fastboot::MockFastbootDriver fb;\n    fp->fb = &fb;\n    fp->should_optimize_flash_super = false;\n    fp->should_use_fastboot_info = true;\n\n    std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"reboot fastboot\",\n              \"update-super\", \"flash product\", \"flash system\", \"flash system_ext\", \"flash odm\",\n              \"if-wipe erase userdata\"},\n             true},\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"reboot fastboot\",\n              \"update-super\", \"flash product\", \"flash system\", \"flash system_ext\", \"flash odm\",\n              \"if-wipe erase userdata\"},\n             true},\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"reboot fastboot\",\n              \"flash product\", \"flash system\", \"flash system_ext\", \"flash odm\",\n              \"if-wipe erase userdata\"},\n             false},\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"update-super\", \"flash product\",\n              \"flash system\", \"flash system_ext\", \"flash odm\", \"if-wipe erase userdata\"},\n             false},\n    };\n\n    auto remove_if_callback = [&](const auto& task) -> bool { return !!task->AsResizeTask(); };\n\n    for (auto& test : patternmatchtest) {\n        std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);\n        tasks.erase(std::remove_if(tasks.begin(), tasks.end(), remove_if_callback), tasks.end());\n        ASSERT_EQ(OptimizedFlashSuperTask::CanOptimize(fp->source.get(), tasks), test.second);\n    }\n}\n\n// Note: this test is exclusively testing that optimized flash super pattern matches a given task\n// list and is able to optimized based on a correct sequence of tasks\nTEST_F(ParseTest, OptimizedFlashSuperPatternMatchTest) {\n    if (!get_android_product_out()) {\n        GTEST_SKIP();\n    }\n\n    fp->source.reset(new LocalImageSource);\n    fp->sparse_limit = std::numeric_limits<int64_t>::max();\n\n    fastboot::MockFastbootDriver fb;\n    fp->fb = &fb;\n    fp->should_optimize_flash_super = true;\n    fp->should_use_fastboot_info = true;\n\n    ON_CALL(fb, GetVar(\"super-partition-name\", _, _))\n            .WillByDefault(testing::Return(fastboot::BAD_ARG));\n\n    ON_CALL(fb, GetVar(\"slot-count\", _, _))\n            .WillByDefault(testing::DoAll(testing::SetArgPointee<1>(\"2\"),\n                                          testing::Return(fastboot::SUCCESS)));\n\n    ON_CALL(fb, GetVar(\"partition-size:super\", _, _))\n            .WillByDefault(testing::DoAll(testing::SetArgPointee<1>(\"1000\"),\n                                          testing::Return(fastboot::SUCCESS)));\n\n    std::vector<std::pair<std::vector<std::string>, bool>> patternmatchtest = {\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"reboot fastboot\",\n              \"update-super\", \"flash product\", \"flash system\", \"flash system_ext\", \"flash odm\",\n              \"if-wipe erase userdata\"},\n             true},\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"reboot fastboot\",\n              \"update-super\", \"flash product\", \"flash system\", \"flash system_ext\", \"flash odm\",\n              \"if-wipe erase userdata\"},\n             true},\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"reboot fastboot\",\n              \"flash product\", \"flash system\", \"flash system_ext\", \"flash odm\",\n              \"if-wipe erase userdata\"},\n             false},\n            {{\"flash boot\", \"flash init_boot\", \"flash vendor_boot\", \"update-super\", \"flash product\",\n              \"flash system\", \"flash system_ext\", \"flash odm\", \"if-wipe erase userdata\"},\n             false},\n    };\n\n    for (auto& test : patternmatchtest) {\n        std::vector<std::unique_ptr<Task>> tasks = ParseFastbootInfo(fp.get(), test.first);\n        // Check to make sure we have an optimized flash super task && no more dynamic partition\n        // flashing tasks\n        auto&& IsOptimized = [](const FlashingPlan* fp,\n                                const std::vector<std::unique_ptr<Task>>& tasks) {\n            bool contains_optimized_task = false;\n            for (auto& task : tasks) {\n                if (task->AsOptimizedFlashSuperTask()) {\n                    contains_optimized_task = true;\n                }\n                if (auto flash_task = task->AsFlashTask()) {\n                    if (FlashTask::IsDynamicPartition(fp->source.get(), flash_task)) {\n                        return false;\n                    }\n                }\n            }\n            return contains_optimized_task;\n        };\n        ASSERT_EQ(IsOptimized(fp.get(), tasks), test.second);\n    }\n}\n"
  },
  {
    "path": "fastboot/tcp.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"tcp.h\"\n\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n\nnamespace tcp {\n\nstatic constexpr int kProtocolVersion = 1;\nstatic constexpr size_t kHandshakeLength = 4;\nstatic constexpr int kHandshakeTimeoutMs = 2000;\n\n// Extract the big-endian 8-byte message length into a 64-bit number.\nstatic uint64_t ExtractMessageLength(const void* buffer) {\n    uint64_t ret = 0;\n    for (int i = 0; i < 8; ++i) {\n        ret |= uint64_t{reinterpret_cast<const uint8_t*>(buffer)[i]} << (56 - i * 8);\n    }\n    return ret;\n}\n\n// Encode the 64-bit number into a big-endian 8-byte message length.\nstatic void EncodeMessageLength(uint64_t length, void* buffer) {\n    for (int i = 0; i < 8; ++i) {\n        reinterpret_cast<uint8_t*>(buffer)[i] = length >> (56 - i * 8);\n    }\n}\n\nclass TcpTransport : public Transport {\n  public:\n    // Factory function so we can return nullptr if initialization fails.\n    static std::unique_ptr<TcpTransport> NewTransport(std::unique_ptr<Socket> socket,\n                                                      std::string* error);\n\n    ~TcpTransport() override = default;\n\n    ssize_t Read(void* data, size_t length) override;\n    ssize_t Write(const void* data, size_t length) override;\n    int Close() override;\n    int Reset() override;\n\n  private:\n    explicit TcpTransport(std::unique_ptr<Socket> sock) : socket_(std::move(sock)) {}\n\n    // Connects to the device and performs the initial handshake. Returns false and fills |error|\n    // on failure.\n    bool InitializeProtocol(std::string* error);\n\n    std::unique_ptr<Socket> socket_;\n    uint64_t message_bytes_left_ = 0;\n\n    DISALLOW_COPY_AND_ASSIGN(TcpTransport);\n};\n\nstd::unique_ptr<TcpTransport> TcpTransport::NewTransport(std::unique_ptr<Socket> socket,\n                                                         std::string* error) {\n    std::unique_ptr<TcpTransport> transport(new TcpTransport(std::move(socket)));\n\n    if (!transport->InitializeProtocol(error)) {\n        return nullptr;\n    }\n\n    return transport;\n}\n\n// These error strings are checked in tcp_test.cpp and should be kept in sync.\nbool TcpTransport::InitializeProtocol(std::string* error) {\n    std::string handshake_message(android::base::StringPrintf(\"FB%02d\", kProtocolVersion));\n\n    if (!socket_->Send(handshake_message.c_str(), kHandshakeLength)) {\n        *error = android::base::StringPrintf(\"Failed to send initialization message (%s)\",\n                                             Socket::GetErrorMessage().c_str());\n        return false;\n    }\n\n    char buffer[kHandshakeLength + 1];\n    buffer[kHandshakeLength] = '\\0';\n    if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) {\n        *error = android::base::StringPrintf(\n                \"No initialization message received (%s). Target may not support TCP fastboot\",\n                Socket::GetErrorMessage().c_str());\n        return false;\n    }\n\n    if (memcmp(buffer, \"FB\", 2) != 0) {\n        *error = \"Unrecognized initialization message. Target may not support TCP fastboot\";\n        return false;\n    }\n\n    int version = 0;\n    if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {\n        *error = android::base::StringPrintf(\"Unknown TCP protocol version %s (host version %02d)\",\n                                             buffer + 2, kProtocolVersion);\n        return false;\n    }\n\n    error->clear();\n    return true;\n}\n\nssize_t TcpTransport::Read(void* data, size_t length) {\n    if (socket_ == nullptr) {\n        return -1;\n    }\n\n    // Unless we're mid-message, read the next 8-byte message length.\n    if (message_bytes_left_ == 0) {\n        char buffer[8];\n        if (socket_->ReceiveAll(buffer, 8, 0) != 8) {\n            Close();\n            return -1;\n        }\n        message_bytes_left_ = ExtractMessageLength(buffer);\n    }\n\n    // Now read the message (up to |length| bytes).\n    if (length > message_bytes_left_) {\n        length = message_bytes_left_;\n    }\n    ssize_t bytes_read = socket_->ReceiveAll(data, length, 0);\n    if (bytes_read == -1) {\n        Close();\n    } else {\n        message_bytes_left_ -= bytes_read;\n    }\n    return bytes_read;\n}\n\nssize_t TcpTransport::Write(const void* data, size_t length) {\n    if (socket_ == nullptr) {\n        return -1;\n    }\n\n    // Use multi-buffer writes for better performance.\n    char header[8];\n    EncodeMessageLength(length, header);\n    if (!socket_->Send(std::vector<cutils_socket_buffer_t>{{header, 8}, {data, length}})) {\n        Close();\n        return -1;\n    }\n\n    return length;\n}\n\nint TcpTransport::Close() {\n    if (socket_ == nullptr) {\n        return 0;\n    }\n\n    int result = socket_->Close();\n    socket_.reset();\n    return result;\n}\n\nint TcpTransport::Reset() {\n    return 0;\n}\n\nstd::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {\n    return internal::Connect(Socket::NewClient(Socket::Protocol::kTcp, hostname, port, error),\n                             error);\n}\n\nnamespace internal {\n\nstd::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {\n    if (sock == nullptr) {\n        // If Socket creation failed |error| is already set.\n        return nullptr;\n    }\n\n    return TcpTransport::NewTransport(std::move(sock), error);\n}\n\n}  // namespace internal\n\n}  // namespace tcp\n"
  },
  {
    "path": "fastboot/tcp.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#pragma once\n\n#include <memory>\n#include <string>\n\n#include <android-base/macros.h>\n\n#include \"socket.h\"\n#include \"transport.h\"\n\nnamespace tcp {\n\nconstexpr int kDefaultPort = 5554;\n\n// Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is\n// filled and nullptr is returned.\nstd::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error);\n\n// Internal namespace for test use only.\nnamespace internal {\n\n// Creates a TCP Transport object but using a given Socket instead of connecting to a hostname.\n// Used for unit tests to create a Transport object that uses a SocketMock.\nstd::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error);\n\n}  // namespace internal\n\n}  // namespace tcp\n"
  },
  {
    "path": "fastboot/tcp_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include \"tcp.h\"\n\n#include <gtest/gtest.h>\n\n#include \"socket_mock.h\"\n\nTEST(TcpConnectTest, TestSuccess) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n    mock->ExpectSend(\"FB01\");\n    mock->AddReceive(\"FB01\");\n\n    std::string error;\n    EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));\n    EXPECT_EQ(\"\", error);\n}\n\nTEST(TcpConnectTest, TestNewerVersionSuccess) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n    mock->ExpectSend(\"FB01\");\n    mock->AddReceive(\"FB99\");\n\n    std::string error;\n    EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));\n    EXPECT_EQ(\"\", error);\n}\n\nTEST(TcpConnectTest, TestSendFailure) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n    mock->ExpectSendFailure(\"FB01\");\n\n    std::string error;\n    EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));\n    EXPECT_NE(std::string::npos, error.find(\"Failed to send initialization message\"));\n}\n\nTEST(TcpConnectTest, TestNoResponseFailure) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n    mock->ExpectSend(\"FB01\");\n    mock->AddReceiveFailure();\n\n    std::string error;\n    EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));\n    EXPECT_NE(std::string::npos, error.find(\"No initialization message received\"));\n}\n\nTEST(TcpConnectTest, TestBadResponseFailure) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n    mock->ExpectSend(\"FB01\");\n    mock->AddReceive(\"XX01\");\n\n    std::string error;\n    EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));\n    EXPECT_NE(std::string::npos, error.find(\"Unrecognized initialization message\"));\n}\n\nTEST(TcpConnectTest, TestUnknownVersionFailure) {\n    std::unique_ptr<SocketMock> mock(new SocketMock);\n    mock->ExpectSend(\"FB01\");\n    mock->AddReceive(\"FB00\");\n\n    std::string error;\n    EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));\n    EXPECT_EQ(\"Unknown TCP protocol version 00 (host version 01)\", error);\n}\n\n// Fixture to configure a SocketMock for a successful TCP connection.\nclass TcpTest : public ::testing::Test {\n  protected:\n    void SetUp() override {\n        mock_ = new SocketMock;\n        mock_->ExpectSend(\"FB01\");\n        mock_->AddReceive(\"FB01\");\n\n        std::string error;\n        transport_ = tcp::internal::Connect(std::unique_ptr<Socket>(mock_), &error);\n        ASSERT_NE(nullptr, transport_);\n        ASSERT_EQ(\"\", error);\n    };\n\n    // Writes |message| to |transport_|, returns true on success.\n    bool Write(const std::string& message) {\n        return transport_->Write(message.data(), message.length()) ==\n               static_cast<ssize_t>(message.length());\n    }\n\n    // Reads from |transport_|, returns true if it matches |message|.\n    bool Read(const std::string& message) {\n        std::string buffer(message.length(), '\\0');\n        return transport_->Read(&buffer[0], buffer.length()) ==\n                       static_cast<ssize_t>(message.length()) &&\n               buffer == message;\n    }\n\n    // Use a raw SocketMock* here because we pass ownership to the Transport object, but we still\n    // need access to configure mock expectations.\n    SocketMock* mock_ = nullptr;\n    std::unique_ptr<Transport> transport_;\n};\n\nTEST_F(TcpTest, TestWriteSuccess) {\n    mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0, 0, 3} + \"foo\");\n\n    EXPECT_TRUE(Write(\"foo\"));\n}\n\nTEST_F(TcpTest, TestReadSuccess) {\n    mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 3});\n    mock_->AddReceive(\"foo\");\n\n    EXPECT_TRUE(Read(\"foo\"));\n}\n\n// Tests that fragmented TCP reads are handled properly.\nTEST_F(TcpTest, TestReadFragmentSuccess) {\n    mock_->AddReceive(std::string{0, 0, 0, 0});\n    mock_->AddReceive(std::string{0, 0, 0, 3});\n    mock_->AddReceive(\"f\");\n    mock_->AddReceive(\"o\");\n    mock_->AddReceive(\"o\");\n\n    EXPECT_TRUE(Read(\"foo\"));\n}\n\nTEST_F(TcpTest, TestLargeWriteSuccess) {\n    // 0x100000 = 1MiB.\n    std::string data(0x100000, '\\0');\n    for (size_t i = 0; i < data.length(); ++i) {\n        data[i] = i;\n    }\n    mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0x10, 0, 0} + data);\n\n    EXPECT_TRUE(Write(data));\n}\n\nTEST_F(TcpTest, TestLargeReadSuccess) {\n    // 0x100000 = 1MiB.\n    std::string data(0x100000, '\\0');\n    for (size_t i = 0; i < data.length(); ++i) {\n        data[i] = i;\n    }\n    mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0x10, 0, 0});\n    mock_->AddReceive(data);\n\n    EXPECT_TRUE(Read(data));\n}\n\n// Tests a few sample fastboot protocol commands.\nTEST_F(TcpTest, TestFastbootProtocolSuccess) {\n    mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0, 0, 14} + \"getvar:version\");\n    mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 7});\n    mock_->AddReceive(\"OKAY0.4\");\n\n    mock_->ExpectSend(std::string{0, 0, 0, 0, 0, 0, 0, 10} + \"getvar:all\");\n    mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 16});\n    mock_->AddReceive(\"INFOversion: 0.4\");\n    mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 12});\n    mock_->AddReceive(\"INFOfoo: bar\");\n    mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 4});\n    mock_->AddReceive(\"OKAY\");\n\n    EXPECT_TRUE(Write(\"getvar:version\"));\n    EXPECT_TRUE(Read(\"OKAY0.4\"));\n\n    EXPECT_TRUE(Write(\"getvar:all\"));\n    EXPECT_TRUE(Read(\"INFOversion: 0.4\"));\n    EXPECT_TRUE(Read(\"INFOfoo: bar\"));\n    EXPECT_TRUE(Read(\"OKAY\"));\n}\n\nTEST_F(TcpTest, TestReadLengthFailure) {\n    mock_->AddReceiveFailure();\n\n    char buffer[16];\n    EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));\n}\n\nTEST_F(TcpTest, TestReadDataFailure) {\n    mock_->AddReceive(std::string{0, 0, 0, 0, 0, 0, 0, 3});\n    mock_->AddReceiveFailure();\n\n    char buffer[16];\n    EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));\n}\n\nTEST_F(TcpTest, TestWriteFailure) {\n    mock_->ExpectSendFailure(std::string{0, 0, 0, 0, 0, 0, 0, 3} + \"foo\");\n\n    EXPECT_EQ(-1, transport_->Write(\"foo\", 3));\n}\n\nTEST_F(TcpTest, TestTransportClose) {\n    EXPECT_EQ(0, transport_->Close());\n\n    // After closing, Transport Read()/Write() should return -1 without actually attempting any\n    // network operations.\n    char buffer[16];\n    EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));\n    EXPECT_EQ(-1, transport_->Write(\"foo\", 3));\n}\n"
  },
  {
    "path": "fastboot/test_fastboot.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\"\"\"Tests for the fastboot command line utility.\n\"\"\"\n\nimport re\nimport subprocess\nimport unittest\n\n\nclass DevicesTest(unittest.TestCase):\n    \"\"\"Tests for `fastboot devices`.\"\"\"\n\n\n    def test_devices(self):\n        \"\"\"Ensure that the format of `fastboot devices` does not change.\n\n        `fastboot devices` should alternate between a line containing the\n        device's serial number and fastboot state and an empty line\n        \"\"\"\n        output = subprocess.check_output([\"fastboot\", \"devices\"])\n\n        previous_line_was_empty = True\n        for line in output.decode().splitlines():\n            if previous_line_was_empty:\n                if not re.match(r\"[a-zA-Z\\d]+\\s+(bootloader|fastbootd)\", line):\n                    self.fail(\"%s does not match the expected format <serial no>\\\\s+(bootloader|fastbootd)\" % line)\n                previous_line_was_empty = False\n            else:\n                if line:\n                    self.fail(\"Expected an empty line. Received '%s'\" % line)\n                previous_line_was_empty = True\n\n        if len(output) == 0:\n            self.fail(\"Output is empty. Are any devices connected?\")\n\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "fastboot/testdata/Android.bp",
    "content": "// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_binary_host {\n    name: \"fastboot_gen_rand\",\n    visibility: [\":__subpackages__\"],\n    srcs: [\"fastboot_gen_rand.py\"],\n}\n\ngenrule_defaults {\n    name: \"fastboot_test_data_gen_defaults\",\n    visibility: [\"//system/core/fastboot\"],\n    tools: [\n        \"fastboot_gen_rand\",\n    ],\n}\n\n// Genrules for components of test vendor boot image.\n\n// Fake dtb image.\ngenrule {\n    name: \"fastboot_test_dtb\",\n    defaults: [\"fastboot_test_data_gen_defaults\"],\n    out: [\"test_dtb.img\"],\n    cmd: \"$(location fastboot_gen_rand) --seed dtb --length 1024 > $(out)\",\n}\n\n// Fake dtb image for replacement.\ngenrule {\n    name: \"fastboot_test_dtb_replace\",\n    defaults: [\"fastboot_test_data_gen_defaults\"],\n    out: [\"dtb_replace.img\"],\n    cmd: \"$(location fastboot_gen_rand) --seed dtb --length 2048 > $(out)\",\n}\n\n// Fake bootconfig image.\ngenrule {\n    name: \"fastboot_test_bootconfig\",\n    defaults: [\"fastboot_test_data_gen_defaults\"],\n    out: [\"test_bootconfig.img\"],\n    cmd: \"$(location fastboot_gen_rand) --seed bootconfig --length 1024 > $(out)\",\n}\n\n// Fake vendor ramdisk with type \"none\".\ngenrule {\n    name: \"fastboot_test_vendor_ramdisk_none\",\n    defaults: [\"fastboot_test_data_gen_defaults\"],\n    out: [\"test_vendor_ramdisk_none.img\"],\n    cmd: \"$(location fastboot_gen_rand) --seed vendor_ramdisk_none --length 1024 > $(out)\",\n}\n\n// Fake vendor ramdisk with type \"platform\".\ngenrule {\n    name: \"fastboot_test_vendor_ramdisk_platform\",\n    defaults: [\"fastboot_test_data_gen_defaults\"],\n    out: [\"test_vendor_ramdisk_platform.img\"],\n    cmd: \"$(location fastboot_gen_rand) --seed vendor_ramdisk_platform --length 1024 > $(out)\",\n}\n\n// Fake replacement ramdisk.\ngenrule {\n    name: \"fastboot_test_vendor_ramdisk_replace\",\n    defaults: [\"fastboot_test_data_gen_defaults\"],\n    out: [\"test_vendor_ramdisk_replace.img\"],\n    cmd: \"$(location fastboot_gen_rand) --seed replace --length 3072 > $(out)\",\n}\n\n// Genrules for test vendor boot images.\n\nfastboot_sign_test_image = \"$(location avbtool) add_hash_footer --salt 00 --image $(out) \" +\n    \"--partition_name vendor_boot --partition_size $$(( 1 * 1024 * 1024 ))\"\n\ngenrule_defaults {\n    name: \"fastboot_test_vendor_boot_gen_defaults\",\n    defaults: [\"fastboot_test_data_gen_defaults\"],\n    tools: [\n        \"avbtool\",\n        \"mkbootimg\",\n    ],\n}\n\ngenrule {\n    name: \"fastboot_test_vendor_boot_v3\",\n    defaults: [\"fastboot_test_vendor_boot_gen_defaults\"],\n    out: [\"vendor_boot_v3.img\"],\n    srcs: [\n        \":fastboot_test_dtb\",\n        \":fastboot_test_vendor_ramdisk_none\",\n    ],\n    cmd: \"$(location mkbootimg) --header_version 3 \" +\n        \"--vendor_ramdisk $(location :fastboot_test_vendor_ramdisk_none) \" +\n        \"--dtb $(location :fastboot_test_dtb) \" +\n        \"--vendor_boot $(out) && \" +\n        fastboot_sign_test_image,\n}\n\ngenrule {\n    name: \"fastboot_test_vendor_boot_v4_without_frag\",\n    defaults: [\"fastboot_test_vendor_boot_gen_defaults\"],\n    out: [\"vendor_boot_v4_without_frag.img\"],\n    srcs: [\n        \":fastboot_test_dtb\",\n        \":fastboot_test_vendor_ramdisk_none\",\n        \":fastboot_test_bootconfig\",\n    ],\n    cmd: \"$(location mkbootimg) --header_version 4 \" +\n        \"--vendor_ramdisk $(location :fastboot_test_vendor_ramdisk_none) \" +\n        \"--dtb $(location :fastboot_test_dtb) \" +\n        \"--vendor_bootconfig $(location :fastboot_test_bootconfig) \" +\n        \"--vendor_boot $(out) && \" +\n        fastboot_sign_test_image,\n}\n\ngenrule {\n    name: \"fastboot_test_vendor_boot_v4_with_frag\",\n    defaults: [\"fastboot_test_vendor_boot_gen_defaults\"],\n    out: [\"vendor_boot_v4_with_frag.img\"],\n    srcs: [\n        \":fastboot_test_dtb\",\n        \":fastboot_test_vendor_ramdisk_none\",\n        \":fastboot_test_vendor_ramdisk_platform\",\n        \":fastboot_test_bootconfig\",\n    ],\n    cmd: \"$(location mkbootimg) --header_version 4 \" +\n        \"--dtb $(location :fastboot_test_dtb) \" +\n        \"--vendor_bootconfig $(location :fastboot_test_bootconfig) \" +\n        \"--ramdisk_type none --ramdisk_name none_ramdisk \" +\n        \"--vendor_ramdisk_fragment $(location :fastboot_test_vendor_ramdisk_none) \" +\n        \"--ramdisk_type platform --ramdisk_name platform_ramdisk \" +\n        \"--vendor_ramdisk_fragment $(location :fastboot_test_vendor_ramdisk_platform) \" +\n        \"--vendor_boot $(out) && \" +\n        fastboot_sign_test_image,\n}\n"
  },
  {
    "path": "fastboot/testdata/fastboot_gen_rand.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nWrite given number of random bytes, generated with optional seed.\n\"\"\"\n\nimport random, argparse\n\nif __name__ == '__main__':\n  parser = argparse.ArgumentParser(description=__doc__)\n  parser.add_argument('--seed', help='Seed to random generator')\n  parser.add_argument('--length', type=int, required=True, help='Length of output')\n  args = parser.parse_args()\n\n  if args.seed:\n    random.seed(args.seed)\n\n  print(''.join(chr(random.randrange(0,0xff)) for _ in range(args.length)))\n"
  },
  {
    "path": "fastboot/testdata/make_super_images.sh",
    "content": "#!/bin/bash\n\nset -e\nset -x\n\nlpmake \\\n    --device-size=auto \\\n    --metadata-size=4096 \\\n    --metadata-slots=3 \\\n    --partition=system_a:readonly:0 \\\n    --alignment=16384 \\\n    --output=super_empty.img\n\nlpmake \\\n    --device-size=auto \\\n    --metadata-size=4096 \\\n    --metadata-slots=3 \\\n    --partition=system_a:readonly:0 \\\n    --alignment=16384 \\\n    --output=super.img \\\n    --image=system_a=system.img\n"
  },
  {
    "path": "fastboot/transport.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android-base/macros.h>\n\n// General interface to allow the fastboot protocol to be used over different\n// types of transports.\nclass Transport {\n  public:\n    Transport() = default;\n    virtual ~Transport() = default;\n\n    // Reads |len| bytes into |data|. Returns the number of bytes actually\n    // read or -1 on error.\n    virtual ssize_t Read(void* data, size_t len) = 0;\n\n    // Writes |len| bytes from |data|. Returns the number of bytes actually\n    // written or -1 on error.\n    virtual ssize_t Write(const void* data, size_t len) = 0;\n\n    // Closes the underlying transport. Returns 0 on success.\n    virtual int Close() = 0;\n\n    virtual int Reset() = 0;\n\n    // Blocks until the transport disconnects. Transports that don't support\n    // this will return immediately. Returns 0 on success.\n    virtual int WaitForDisconnect() { return 0; }\n\n  private:\n    DISALLOW_COPY_AND_ASSIGN(Transport);\n};\n"
  },
  {
    "path": "fastboot/udp.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n// This file implements the fastboot UDP protocol; see fastboot_protocol.txt for documentation.\n\n#include \"udp.h\"\n\n#include <errno.h>\n#include <stdio.h>\n\n#include <list>\n#include <memory>\n#include <vector>\n\n#include <android-base/macros.h>\n#include <android-base/stringprintf.h>\n\n#include \"socket.h\"\n\nnamespace udp {\n\nusing namespace internal;\n\nconstexpr size_t kMinPacketSize = 512;\nconstexpr size_t kHeaderSize = 4;\n\nenum Index {\n    kIndexId = 0,\n    kIndexFlags = 1,\n    kIndexSeqH = 2,\n    kIndexSeqL = 3,\n};\n\n// Extracts a big-endian uint16_t from a byte array.\nstatic uint16_t ExtractUint16(const uint8_t* bytes) {\n    return (static_cast<uint16_t>(bytes[0]) << 8) | bytes[1];\n}\n\n// Packet header handling.\nclass Header {\n  public:\n    Header();\n    ~Header() = default;\n\n    uint8_t id() const { return bytes_[kIndexId]; }\n    const uint8_t* bytes() const { return bytes_; }\n\n    void Set(uint8_t id, uint16_t sequence, Flag flag);\n\n    // Checks whether |response| is a match for this header.\n    bool Matches(const uint8_t* response);\n\n  private:\n    uint8_t bytes_[kHeaderSize];\n};\n\nHeader::Header() {\n    Set(kIdError, 0, kFlagNone);\n}\n\nvoid Header::Set(uint8_t id, uint16_t sequence, Flag flag) {\n    bytes_[kIndexId] = id;\n    bytes_[kIndexFlags] = flag;\n    bytes_[kIndexSeqH] = sequence >> 8;\n    bytes_[kIndexSeqL] = sequence;\n}\n\nbool Header::Matches(const uint8_t* response) {\n    // Sequence numbers must be the same to match, but the response ID can either be the same\n    // or an error response which is always accepted.\n    return bytes_[kIndexSeqH] == response[kIndexSeqH] &&\n           bytes_[kIndexSeqL] == response[kIndexSeqL] &&\n           (bytes_[kIndexId] == response[kIndexId] || response[kIndexId] == kIdError);\n}\n\n// Implements the Transport interface to work with the fastboot engine.\nclass UdpTransport : public Transport {\n  public:\n    // Factory function so we can return nullptr if initialization fails.\n    static std::unique_ptr<UdpTransport> NewTransport(std::unique_ptr<Socket> socket,\n                                                      std::string* error);\n    ~UdpTransport() override = default;\n\n    ssize_t Read(void* data, size_t length) override;\n    ssize_t Write(const void* data, size_t length) override;\n    int Close() override;\n    int Reset() override;\n\n  private:\n    explicit UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}\n\n    // Performs the UDP initialization procedure. Returns true on success.\n    bool InitializeProtocol(std::string* error);\n\n    // Sends |length| bytes from |data| and waits for the response packet up to |attempts| times.\n    // Continuation packets are handled automatically and any return data is written to |rx_data|.\n    // Excess bytes that cannot fit in |rx_data| are dropped.\n    // On success, returns the number of response data bytes received, which may be greater than\n    // |rx_length|. On failure, returns -1 and fills |error| on failure.\n    ssize_t SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,\n                     size_t rx_length, int attempts, std::string* error);\n\n    // Helper for SendData(); sends a single packet and handles the response. |header| specifies\n    // the initial outgoing packet information but may be modified by this function.\n    ssize_t SendSinglePacketHelper(Header* header, const uint8_t* tx_data, size_t tx_length,\n                                   uint8_t* rx_data, size_t rx_length, int attempts,\n                                   std::string* error);\n\n    std::unique_ptr<Socket> socket_;\n    int sequence_ = -1;\n    size_t max_data_length_ = kMinPacketSize - kHeaderSize;\n    std::vector<uint8_t> rx_packet_;\n\n    DISALLOW_COPY_AND_ASSIGN(UdpTransport);\n};\n\nstd::unique_ptr<UdpTransport> UdpTransport::NewTransport(std::unique_ptr<Socket> socket,\n                                                         std::string* error) {\n    std::unique_ptr<UdpTransport> transport(new UdpTransport(std::move(socket)));\n\n    if (!transport->InitializeProtocol(error)) {\n        return nullptr;\n    }\n\n    return transport;\n}\n\nbool UdpTransport::InitializeProtocol(std::string* error) {\n    uint8_t rx_data[4];\n\n    sequence_ = 0;\n    rx_packet_.resize(kMinPacketSize);\n\n    // First send the query packet to sync with the target. Only attempt this a small number of\n    // times so we can fail out quickly if the target isn't available.\n    ssize_t rx_bytes = SendData(kIdDeviceQuery, nullptr, 0, rx_data, sizeof(rx_data),\n                                kMaxConnectAttempts, error);\n    if (rx_bytes == -1) {\n        return false;\n    } else if (rx_bytes < 2) {\n        *error = \"invalid query response from target\";\n        return false;\n    }\n    // The first two bytes contain the next expected sequence number.\n    sequence_ = ExtractUint16(rx_data);\n\n    // Now send the initialization packet with our version and maximum packet size.\n    uint8_t init_data[] = {kProtocolVersion >> 8, kProtocolVersion & 0xFF,\n                           kHostMaxPacketSize >> 8, kHostMaxPacketSize & 0xFF};\n    rx_bytes = SendData(kIdInitialization, init_data, sizeof(init_data), rx_data, sizeof(rx_data),\n                        kMaxTransmissionAttempts, error);\n    if (rx_bytes == -1) {\n        return false;\n    } else if (rx_bytes < 4) {\n        *error = \"invalid initialization response from target\";\n        return false;\n    }\n\n    // The first two data bytes contain the version, the second two bytes contain the target max\n    // supported packet size, which must be at least 512 bytes.\n    uint16_t version = ExtractUint16(rx_data);\n    if (version < kProtocolVersion) {\n        *error = android::base::StringPrintf(\"target reported invalid protocol version %d\",\n                                             version);\n        return false;\n    }\n    uint16_t packet_size = ExtractUint16(rx_data + 2);\n    if (packet_size < kMinPacketSize) {\n        *error = android::base::StringPrintf(\"target reported invalid packet size %d\", packet_size);\n        return false;\n    }\n\n    packet_size = std::min(kHostMaxPacketSize, packet_size);\n    max_data_length_ = packet_size - kHeaderSize;\n    rx_packet_.resize(packet_size);\n\n    return true;\n}\n\n// SendData() is just responsible for chunking |data| into packets until it's all been sent.\n// Per-packet timeout/retransmission logic is done in SendSinglePacketHelper().\nssize_t UdpTransport::SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,\n                               size_t rx_length, int attempts, std::string* error) {\n    if (socket_ == nullptr) {\n        *error = \"socket is closed\";\n        return -1;\n    }\n\n    Header header;\n    size_t packet_data_length;\n    ssize_t ret = 0;\n    // We often send header-only packets with no data as part of the protocol, so always send at\n    // least once even if |length| == 0, then repeat until we've sent all of |data|.\n    do {\n        // Set the continuation flag and truncate packet data if needed.\n        if (tx_length > max_data_length_) {\n            packet_data_length = max_data_length_;\n            header.Set(id, sequence_, kFlagContinuation);\n        } else {\n            packet_data_length = tx_length;\n            header.Set(id, sequence_, kFlagNone);\n        }\n\n        ssize_t bytes = SendSinglePacketHelper(&header, tx_data, packet_data_length, rx_data,\n                                               rx_length, attempts, error);\n\n        // Advance our read and write buffers for the next packet. Keep going even if we run out\n        // of receive buffer space so we can detect overflows.\n        if (bytes == -1) {\n            return -1;\n        } else if (static_cast<size_t>(bytes) < rx_length) {\n            rx_data += bytes;\n            rx_length -= bytes;\n        } else {\n            rx_data = nullptr;\n            rx_length = 0;\n        }\n\n        tx_length -= packet_data_length;\n        tx_data += packet_data_length;\n\n        ret += bytes;\n    } while (tx_length > 0);\n\n    return ret;\n}\n\nssize_t UdpTransport::SendSinglePacketHelper(\n        Header* header, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,\n        size_t rx_length, const int attempts, std::string* error) {\n    ssize_t total_data_bytes = 0;\n    error->clear();\n\n    int attempts_left = attempts;\n    while (attempts_left > 0) {\n        if (!socket_->Send({{header->bytes(), kHeaderSize}, {tx_data, tx_length}})) {\n            *error = Socket::GetErrorMessage();\n            return -1;\n        }\n\n        // Keep receiving until we get a matching response or we timeout.\n        ssize_t bytes = 0;\n        do {\n            bytes = socket_->Receive(rx_packet_.data(), rx_packet_.size(), kResponseTimeoutMs);\n            if (bytes == -1) {\n                if (socket_->ReceiveTimedOut()) {\n                    break;\n                }\n                *error = Socket::GetErrorMessage();\n                return -1;\n            } else if (bytes < static_cast<ssize_t>(kHeaderSize)) {\n                *error = \"protocol error: incomplete header\";\n                return -1;\n            }\n        } while (!header->Matches(rx_packet_.data()));\n\n        if (socket_->ReceiveTimedOut()) {\n            --attempts_left;\n            continue;\n        }\n        ++sequence_;\n\n        // Save to |error| or |rx_data| as appropriate.\n        if (rx_packet_[kIndexId] == kIdError) {\n            error->append(rx_packet_.data() + kHeaderSize, rx_packet_.data() + bytes);\n        } else {\n            total_data_bytes += bytes - kHeaderSize;\n            size_t rx_data_bytes = std::min<size_t>(bytes - kHeaderSize, rx_length);\n            if (rx_data_bytes > 0) {\n                memcpy(rx_data, rx_packet_.data() + kHeaderSize, rx_data_bytes);\n                rx_data += rx_data_bytes;\n                rx_length -= rx_data_bytes;\n            }\n        }\n\n        // If the response has a continuation flag we need to prompt for more data by sending\n        // an empty packet.\n        if (rx_packet_[kIndexFlags] & kFlagContinuation) {\n            // We got a valid response so reset our attempt counter.\n            attempts_left = attempts;\n            header->Set(rx_packet_[kIndexId], sequence_, kFlagNone);\n            tx_data = nullptr;\n            tx_length = 0;\n            continue;\n        }\n\n        break;\n    }\n\n    if (attempts_left <= 0) {\n        *error = \"no response from target\";\n        return -1;\n    }\n\n    if (rx_packet_[kIndexId] == kIdError) {\n        *error = \"target reported error: \" + *error;\n        return -1;\n    }\n\n    return total_data_bytes;\n}\n\nssize_t UdpTransport::Read(void* data, size_t length) {\n    // Read from the target by sending an empty packet.\n    std::string error;\n    ssize_t bytes = SendData(kIdFastboot, nullptr, 0, reinterpret_cast<uint8_t*>(data), length,\n                             kMaxTransmissionAttempts, &error);\n\n    if (bytes == -1) {\n        fprintf(stderr, \"UDP error: %s\\n\", error.c_str());\n        return -1;\n    } else if (static_cast<size_t>(bytes) > length) {\n        // Fastboot protocol error: the target sent more data than our fastboot engine was prepared\n        // to receive.\n        fprintf(stderr, \"UDP error: receive overflow, target sent too much fastboot data\\n\");\n        return -1;\n    }\n\n    return bytes;\n}\n\nssize_t UdpTransport::Write(const void* data, size_t length) {\n    std::string error;\n    ssize_t bytes = SendData(kIdFastboot, reinterpret_cast<const uint8_t*>(data), length, nullptr,\n                             0, kMaxTransmissionAttempts, &error);\n\n    if (bytes == -1) {\n        fprintf(stderr, \"UDP error: %s\\n\", error.c_str());\n        return -1;\n    } else if (bytes > 0) {\n        // UDP protocol error: only empty ACK packets are allowed when writing to a device.\n        fprintf(stderr, \"UDP error: target sent fastboot data out-of-turn\\n\");\n        return -1;\n    }\n\n    return length;\n}\n\nint UdpTransport::Close() {\n    if (socket_ == nullptr) {\n        return 0;\n    }\n\n    int result = socket_->Close();\n    socket_.reset();\n    return result;\n}\n\nint UdpTransport::Reset() {\n    return 0;\n}\n\nstd::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {\n    return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),\n                             error);\n}\n\nnamespace internal {\n\nstd::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {\n    if (sock == nullptr) {\n        // If Socket creation failed |error| is already set.\n        return nullptr;\n    }\n\n    return UdpTransport::NewTransport(std::move(sock), error);\n}\n\n}  // namespace internal\n\n}  // namespace udp\n"
  },
  {
    "path": "fastboot/udp.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#pragma once\n\n#include <memory>\n#include <string>\n\n#include \"socket.h\"\n#include \"transport.h\"\n\nnamespace udp {\n\nconstexpr int kDefaultPort = 5554;\n\n// Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is\n// filled and nullptr is returned.\nstd::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error);\n\n// Internal namespace for test use only.\nnamespace internal {\n\nconstexpr uint16_t kProtocolVersion = 1;\n\n// This will be negotiated with the device so may end up being smaller.\nconstexpr uint16_t kHostMaxPacketSize = 8192;\n\n// Retransmission constants. Retransmission timeout must be at least 500ms, and the host must\n// attempt to send packets for at least 1 minute once the device has connected. See\n// fastboot_protocol.txt for more information.\nconstexpr int kResponseTimeoutMs = 500;\nconstexpr int kMaxConnectAttempts = 4;\nconstexpr int kMaxTransmissionAttempts = 60 * 1000 / kResponseTimeoutMs;\n\nenum Id : uint8_t {\n    kIdError = 0x00,\n    kIdDeviceQuery = 0x01,\n    kIdInitialization = 0x02,\n    kIdFastboot = 0x03\n};\n\nenum Flag : uint8_t {\n    kFlagNone = 0x00,\n    kFlagContinuation = 0x01\n};\n\n// Creates a UDP Transport object using a given Socket. Used for unit tests to create a Transport\n// object that uses a SocketMock.\nstd::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error);\n\n}  // namespace internal\n\n}  // namespace udp\n"
  },
  {
    "path": "fastboot/udp_test.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"udp.h\"\n\n#include <gtest/gtest.h>\n\n#include \"socket.h\"\n#include \"socket_mock.h\"\n\nusing namespace udp;\nusing namespace udp::internal;\n\n// Some possible corner case sequence numbers we want to check.\nstatic const uint16_t kTestSequenceNumbers[] = {0x0000, 0x0001, 0x00FF, 0x0100,\n                                                0x7FFF, 0x8000, 0xFFFF};\n\n// Converts |value| to a binary big-endian string.\nstatic std::string PacketValue(uint16_t value) {\n    return std::string{static_cast<char>(value >> 8), static_cast<char>(value)};\n}\n\n// Returns an Error packet.\nstatic std::string ErrorPacket(uint16_t sequence, const std::string& message = \"\",\n                               char flags = kFlagNone) {\n    return std::string{kIdError, flags} + PacketValue(sequence) + message;\n}\n\n// Returns a Query packet with no data.\nstatic std::string QueryPacket(uint16_t sequence) {\n    return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence);\n}\n\n// Returns a Query packet with a 2-byte |new_sequence|.\nstatic std::string QueryPacket(uint16_t sequence, uint16_t new_sequence) {\n    return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence) +\n           PacketValue(new_sequence);\n}\n\n// Returns an Init packet with a 2-byte |version| and |max_packet_size|.\nstatic std::string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {\n    return std::string{kIdInitialization, kFlagNone} + PacketValue(sequence) +\n           PacketValue(version) + PacketValue(max_packet_size);\n}\n\n// Returns a Fastboot packet with |data|.\nstatic std::string FastbootPacket(uint16_t sequence, const std::string& data = \"\",\n                                  char flags = kFlagNone) {\n    return std::string{kIdFastboot, flags} + PacketValue(sequence) + data;\n}\n\n// Fixture class to test protocol initialization. Usage is to set up the expected calls to the\n// SocketMock object then call UdpConnect() and check the result.\nclass UdpConnectTest : public ::testing::Test {\n  public:\n    UdpConnectTest() : mock_socket_(new SocketMock) {}\n\n    // Run the initialization, return whether it was successful or not. This passes ownership of\n    // the current |mock_socket_| but allocates a new one for re-use.\n    bool UdpConnect(std::string* error = nullptr) {\n        std::string local_error;\n        if (error == nullptr) {\n            error = &local_error;\n        }\n        std::unique_ptr<Transport> transport(Connect(std::move(mock_socket_), error));\n        mock_socket_.reset(new SocketMock);\n        return transport != nullptr && error->empty();\n    }\n\n  protected:\n    std::unique_ptr<SocketMock> mock_socket_;\n};\n\n// Tests a successful protocol initialization with various starting sequence numbers.\nTEST_F(UdpConnectTest, InitializationSuccess) {\n    for (uint16_t seq : kTestSequenceNumbers) {\n        mock_socket_->ExpectSend(QueryPacket(0));\n        mock_socket_->AddReceive(QueryPacket(0, seq));\n        mock_socket_->ExpectSend(InitPacket(seq, kProtocolVersion, kHostMaxPacketSize));\n        mock_socket_->AddReceive(InitPacket(seq, kProtocolVersion, 1024));\n\n        EXPECT_TRUE(UdpConnect());\n    }\n}\n\n// Tests continuation packets during initialization.\nTEST_F(UdpConnectTest, InitializationContinuationSuccess) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagContinuation, 0, 0, 0x44});\n    mock_socket_->ExpectSend(std::string{kIdDeviceQuery, kFlagNone, 0, 1});\n    mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagNone, 0, 1, 0x55});\n\n    mock_socket_->ExpectSend(InitPacket(0x4455, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x55, 0});\n    mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x56});\n    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x56, 1});\n    mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x57});\n    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x57, 2});\n    mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x58});\n    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagNone, 0x44, 0x58, 0});\n\n    EXPECT_TRUE(UdpConnect());\n}\n\n\n// Tests a mismatched version number; as long as the minimum of the two versions is supported\n// we should allow the connection.\nTEST_F(UdpConnectTest, InitializationVersionMismatch) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(InitPacket(0, 2, 1024));\n\n    EXPECT_TRUE(UdpConnect());\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(InitPacket(0, 0, 1024));\n\n    EXPECT_FALSE(UdpConnect());\n}\n\nTEST_F(UdpConnectTest, QueryResponseTimeoutFailure) {\n    for (int i = 0; i < kMaxConnectAttempts; ++i) {\n        mock_socket_->ExpectSend(QueryPacket(0));\n        mock_socket_->AddReceiveTimeout();\n    }\n\n    EXPECT_FALSE(UdpConnect());\n}\n\nTEST_F(UdpConnectTest, QueryResponseReceiveFailure) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceiveFailure();\n\n    EXPECT_FALSE(UdpConnect());\n}\n\nTEST_F(UdpConnectTest, InitResponseTimeoutFailure) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n    for (int i = 0; i < kMaxTransmissionAttempts; ++i) {\n        mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n        mock_socket_->AddReceiveTimeout();\n    }\n\n    EXPECT_FALSE(UdpConnect());\n}\n\nTEST_F(UdpConnectTest, InitResponseReceiveFailure) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceiveFailure();\n\n    EXPECT_FALSE(UdpConnect());\n}\n\n// Tests that we can recover up to the maximum number of allowed retries.\nTEST_F(UdpConnectTest, ResponseRecovery) {\n    // The device query packet can recover from up to (kMaxConnectAttempts - 1) timeouts.\n    for (int i = 0; i < kMaxConnectAttempts - 1; ++i) {\n        mock_socket_->ExpectSend(QueryPacket(0));\n        mock_socket_->AddReceiveTimeout();\n    }\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n\n    // Subsequent packets try up to (kMaxTransmissionAttempts - 1) times.\n    for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {\n        mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n        mock_socket_->AddReceiveTimeout();\n    }\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));\n\n    EXPECT_TRUE(UdpConnect());\n}\n\n// Tests that the host can handle receiving additional bytes for forward compatibility.\nTEST_F(UdpConnectTest, ExtraResponseDataSuccess) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0) + \"foo\");\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024) + \"bar\");\n\n    EXPECT_TRUE(UdpConnect());\n}\n\n// Tests mismatched response sequence numbers. A wrong sequence number is interpreted as a previous\n// retransmission and just ignored so we should be able to recover.\nTEST_F(UdpConnectTest, WrongSequenceRecovery) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(1, 0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(InitPacket(1, kProtocolVersion, 1024));\n    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));\n\n    EXPECT_TRUE(UdpConnect());\n}\n\n// Tests mismatched response IDs. This should also be interpreted as a retransmission and ignored.\nTEST_F(UdpConnectTest, WrongIdRecovery) {\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(FastbootPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(FastbootPacket(0));\n    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));\n\n    EXPECT_TRUE(UdpConnect());\n}\n\n// Tests an invalid query response. Query responses must have at least 2 bytes of data.\nTEST_F(UdpConnectTest, InvalidQueryResponseFailure) {\n    std::string error;\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0));\n\n    EXPECT_FALSE(UdpConnect(&error));\n    EXPECT_EQ(\"invalid query response from target\", error);\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0) + std::string{0x00});\n\n    EXPECT_FALSE(UdpConnect(&error));\n    EXPECT_EQ(\"invalid query response from target\", error);\n}\n\n// Tests an invalid initialization response. Max packet size must be at least 512 bytes.\nTEST_F(UdpConnectTest, InvalidInitResponseFailure) {\n    std::string error;\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 511));\n\n    EXPECT_FALSE(UdpConnect(&error));\n    EXPECT_EQ(\"target reported invalid packet size 511\", error);\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(InitPacket(0, 0, 1024));\n\n    EXPECT_FALSE(UdpConnect(&error));\n    EXPECT_EQ(\"target reported invalid protocol version 0\", error);\n}\n\nTEST_F(UdpConnectTest, ErrorResponseFailure) {\n    std::string error;\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(ErrorPacket(0, \"error1\"));\n\n    EXPECT_FALSE(UdpConnect(&error));\n    EXPECT_NE(std::string::npos, error.find(\"error1\"));\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(QueryPacket(0, 0));\n    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));\n    mock_socket_->AddReceive(ErrorPacket(0, \"error2\"));\n\n    EXPECT_FALSE(UdpConnect(&error));\n    EXPECT_NE(std::string::npos, error.find(\"error2\"));\n}\n\n// Tests an error response with continuation flag.\nTEST_F(UdpConnectTest, ErrorContinuationFailure) {\n    std::string error;\n\n    mock_socket_->ExpectSend(QueryPacket(0));\n    mock_socket_->AddReceive(ErrorPacket(0, \"error1\", kFlagContinuation));\n    mock_socket_->ExpectSend(ErrorPacket(1));\n    mock_socket_->AddReceive(ErrorPacket(1, \" \", kFlagContinuation));\n    mock_socket_->ExpectSend(ErrorPacket(2));\n    mock_socket_->AddReceive(ErrorPacket(2, \"error2\"));\n\n    EXPECT_FALSE(UdpConnect(&error));\n    EXPECT_NE(std::string::npos, error.find(\"error1 error2\"));\n}\n\n// Fixture class to test UDP Transport read/write functionality.\nclass UdpTest : public ::testing::Test {\n  public:\n    void SetUp() override {\n        // Create |transport_| starting at sequence 0 with 512 byte max packet size. Tests can call\n        // InitializeTransport() again to change settings.\n        ASSERT_TRUE(InitializeTransport(0, 512));\n    }\n\n    // Sets up |mock_socket_| to correctly initialize the protocol and creates |transport_|. This\n    // can be called multiple times in a test if needed.\n    bool InitializeTransport(uint16_t starting_sequence, int device_max_packet_size = 512) {\n        mock_socket_ = new SocketMock;\n        mock_socket_->ExpectSend(QueryPacket(0));\n        mock_socket_->AddReceive(QueryPacket(0, starting_sequence));\n        mock_socket_->ExpectSend(\n                InitPacket(starting_sequence, kProtocolVersion, kHostMaxPacketSize));\n        mock_socket_->AddReceive(\n                InitPacket(starting_sequence, kProtocolVersion, device_max_packet_size));\n\n        std::string error;\n        transport_ = Connect(std::unique_ptr<Socket>(mock_socket_), &error);\n        return transport_ != nullptr && error.empty();\n    }\n\n    // Writes |message| to |transport_|, returns true on success.\n    bool Write(const std::string& message) {\n        return transport_->Write(message.data(), message.length()) ==\n                static_cast<ssize_t>(message.length());\n    }\n\n    // Reads from |transport_|, returns true if it matches |message|.\n    bool Read(const std::string& message) {\n        std::string buffer(message.length(), '\\0');\n        return transport_->Read(&buffer[0], buffer.length()) ==\n                static_cast<ssize_t>(message.length()) && buffer == message;\n    }\n\n  protected:\n    // |mock_socket_| is a raw pointer here because we transfer ownership to |transport_| but we\n    // need to retain a pointer to set send and receive expectations.\n    SocketMock* mock_socket_ = nullptr;\n    std::unique_ptr<Transport> transport_;\n};\n\n// Tests sequence behavior with various starting sequence numbers.\nTEST_F(UdpTest, SequenceIncrementCheck) {\n    for (uint16_t seq : kTestSequenceNumbers) {\n        ASSERT_TRUE(InitializeTransport(seq));\n\n        for (int i = 0; i < 10; ++i) {\n            mock_socket_->ExpectSend(FastbootPacket(++seq, \"foo\"));\n            mock_socket_->AddReceive(FastbootPacket(seq, \"\"));\n            mock_socket_->ExpectSend(FastbootPacket(++seq, \"\"));\n            mock_socket_->AddReceive(FastbootPacket(seq, \"bar\"));\n\n            EXPECT_TRUE(Write(\"foo\"));\n            EXPECT_TRUE(Read(\"bar\"));\n        }\n    }\n}\n\n// Tests sending and receiving a few small packets.\nTEST_F(UdpTest, ReadAndWriteSmallPackets) {\n    mock_socket_->ExpectSend(FastbootPacket(1, \"foo\"));\n    mock_socket_->AddReceive(FastbootPacket(1, \"\"));\n    mock_socket_->ExpectSend(FastbootPacket(2, \"\"));\n    mock_socket_->AddReceive(FastbootPacket(2, \"bar\"));\n\n    EXPECT_TRUE(Write(\"foo\"));\n    EXPECT_TRUE(Read(\"bar\"));\n\n    mock_socket_->ExpectSend(FastbootPacket(3, \"12345 67890\"));\n    mock_socket_->AddReceive(FastbootPacket(3));\n    mock_socket_->ExpectSend(FastbootPacket(4, \"\\x01\\x02\\x03\\x04\\x05\"));\n    mock_socket_->AddReceive(FastbootPacket(4));\n\n    EXPECT_TRUE(Write(\"12345 67890\"));\n    EXPECT_TRUE(Write(\"\\x01\\x02\\x03\\x04\\x05\"));\n\n    // Reads are done by sending empty packets.\n    mock_socket_->ExpectSend(FastbootPacket(5));\n    mock_socket_->AddReceive(FastbootPacket(5, \"foo bar baz\"));\n    mock_socket_->ExpectSend(FastbootPacket(6));\n    mock_socket_->AddReceive(FastbootPacket(6, \"\\x01\\x02\\x03\\x04\\x05\"));\n\n    EXPECT_TRUE(Read(\"foo bar baz\"));\n    EXPECT_TRUE(Read(\"\\x01\\x02\\x03\\x04\\x05\"));\n}\n\nTEST_F(UdpTest, ResponseTimeoutFailure) {\n    for (int i = 0; i < kMaxTransmissionAttempts; ++i) {\n        mock_socket_->ExpectSend(FastbootPacket(1, \"foo\"));\n        mock_socket_->AddReceiveTimeout();\n    }\n\n    EXPECT_FALSE(Write(\"foo\"));\n}\n\nTEST_F(UdpTest, ResponseReceiveFailure) {\n    mock_socket_->ExpectSend(FastbootPacket(1, \"foo\"));\n    mock_socket_->AddReceiveFailure();\n\n    EXPECT_FALSE(Write(\"foo\"));\n}\n\nTEST_F(UdpTest, ResponseTimeoutRecovery) {\n    for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {\n        mock_socket_->ExpectSend(FastbootPacket(1, \"foo\"));\n        mock_socket_->AddReceiveTimeout();\n    }\n    mock_socket_->ExpectSend(FastbootPacket(1, \"foo\"));\n    mock_socket_->AddReceive(FastbootPacket(1, \"\"));\n\n    EXPECT_TRUE(Write(\"foo\"));\n}\n\n// Tests continuation packets for various max packet sizes.\n// The important part of this test is that regardless of what kind of packet fragmentation happens\n// at the socket layer, a single call to Transport::Read() and Transport::Write() is all the\n// fastboot code needs to do.\nTEST_F(UdpTest, ContinuationPackets) {\n    for (uint16_t max_packet_size : {512, 1024, 1200}) {\n        ASSERT_TRUE(InitializeTransport(0, max_packet_size));\n\n        // Initialize the data we want to send. Use (size - 4) to leave room for the header.\n        size_t max_data_size = max_packet_size - 4;\n        std::string data(max_data_size * 3, '\\0');\n        for (size_t i = 0; i < data.length(); ++i) {\n            data[i] = i;\n        }\n        std::string chunks[] = {data.substr(0, max_data_size),\n                                data.substr(max_data_size, max_data_size),\n                                data.substr(max_data_size * 2, max_data_size)};\n\n        // Write data: split into 3 UDP packets, each of which will be ACKed.\n        mock_socket_->ExpectSend(FastbootPacket(1, chunks[0], kFlagContinuation));\n        mock_socket_->AddReceive(FastbootPacket(1));\n        mock_socket_->ExpectSend(FastbootPacket(2, chunks[1], kFlagContinuation));\n        mock_socket_->AddReceive(FastbootPacket(2));\n        mock_socket_->ExpectSend(FastbootPacket(3, chunks[2]));\n        mock_socket_->AddReceive(FastbootPacket(3));\n        EXPECT_TRUE(Write(data));\n\n        // Same thing for reading the data.\n        mock_socket_->ExpectSend(FastbootPacket(4));\n        mock_socket_->AddReceive(FastbootPacket(4, chunks[0], kFlagContinuation));\n        mock_socket_->ExpectSend(FastbootPacket(5));\n        mock_socket_->AddReceive(FastbootPacket(5, chunks[1], kFlagContinuation));\n        mock_socket_->ExpectSend(FastbootPacket(6));\n        mock_socket_->AddReceive(FastbootPacket(6, chunks[2]));\n        EXPECT_TRUE(Read(data));\n    }\n}\n\n// Tests that the continuation bit is respected even if the packet isn't max size.\nTEST_F(UdpTest, SmallContinuationPackets) {\n    mock_socket_->ExpectSend(FastbootPacket(1));\n    mock_socket_->AddReceive(FastbootPacket(1, \"foo\", kFlagContinuation));\n    mock_socket_->ExpectSend(FastbootPacket(2));\n    mock_socket_->AddReceive(FastbootPacket(2, \"bar\"));\n\n    EXPECT_TRUE(Read(\"foobar\"));\n}\n\n// Tests receiving an error packet mid-continuation.\nTEST_F(UdpTest, ContinuationPacketError) {\n    mock_socket_->ExpectSend(FastbootPacket(1));\n    mock_socket_->AddReceive(FastbootPacket(1, \"foo\", kFlagContinuation));\n    mock_socket_->ExpectSend(FastbootPacket(2));\n    mock_socket_->AddReceive(ErrorPacket(2, \"test error\"));\n\n    EXPECT_FALSE(Read(\"foo\"));\n}\n\n// Tests timeout during a continuation sequence.\nTEST_F(UdpTest, ContinuationTimeoutRecovery) {\n    mock_socket_->ExpectSend(FastbootPacket(1));\n    mock_socket_->AddReceive(FastbootPacket(1, \"foo\", kFlagContinuation));\n    mock_socket_->ExpectSend(FastbootPacket(2));\n    mock_socket_->AddReceiveTimeout();\n    mock_socket_->ExpectSend(FastbootPacket(2));\n    mock_socket_->AddReceive(FastbootPacket(2, \"bar\"));\n\n    EXPECT_TRUE(Read(\"foobar\"));\n}\n\n// Tests read overflow returns -1 to indicate the failure.\nTEST_F(UdpTest, MultipleReadPacket) {\n    mock_socket_->ExpectSend(FastbootPacket(1));\n    mock_socket_->AddReceive(FastbootPacket(1, \"foobarbaz\"));\n\n    char buffer[3];\n    EXPECT_EQ(-1, transport_->Read(buffer, 3));\n}\n\n// Tests that packets arriving out-of-order are ignored.\nTEST_F(UdpTest, IgnoreOutOfOrderPackets) {\n    mock_socket_->ExpectSend(FastbootPacket(1));\n    mock_socket_->AddReceive(FastbootPacket(0, \"sequence too low\"));\n    mock_socket_->AddReceive(FastbootPacket(2, \"sequence too high\"));\n    mock_socket_->AddReceive(QueryPacket(1));\n    mock_socket_->AddReceive(FastbootPacket(1, \"correct\"));\n\n    EXPECT_TRUE(Read(\"correct\"));\n}\n\n// Tests that an error response with the correct sequence number causes immediate failure.\nTEST_F(UdpTest, ErrorResponse) {\n    // Error packets with the wrong sequence number should be ignored like any other packet.\n    mock_socket_->ExpectSend(FastbootPacket(1, \"foo\"));\n    mock_socket_->AddReceive(ErrorPacket(0, \"ignored error\"));\n    mock_socket_->AddReceive(FastbootPacket(1));\n\n    EXPECT_TRUE(Write(\"foo\"));\n\n    // Error packets with the correct sequence should abort immediately without retransmission.\n    mock_socket_->ExpectSend(FastbootPacket(2, \"foo\"));\n    mock_socket_->AddReceive(ErrorPacket(2, \"test error\"));\n\n    EXPECT_FALSE(Write(\"foo\"));\n}\n\n// Tests that attempting to use a closed transport returns -1 without making any socket calls.\nTEST_F(UdpTest, CloseTransport) {\n    char buffer[32];\n    EXPECT_EQ(0, transport_->Close());\n    EXPECT_EQ(-1, transport_->Write(\"foo\", 3));\n    EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));\n}\n"
  },
  {
    "path": "fastboot/usb.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#pragma once\n\n#include <functional>\n#include <memory>\n\n#include \"transport.h\"\n\nstruct usb_ifc_info {\n        /* from device descriptor */\n    unsigned short dev_vendor;\n    unsigned short dev_product;\n\n    unsigned char dev_class;\n    unsigned char dev_subclass;\n    unsigned char dev_protocol;\n\n    unsigned char ifc_class;\n    unsigned char ifc_subclass;\n    unsigned char ifc_protocol;\n\n    unsigned char has_bulk_in;\n    unsigned char has_bulk_out;\n\n    unsigned char writable;\n\n    char serial_number[256];\n    char device_path[256];\n\n    char interface[256];\n};\n\nclass UsbTransport : public Transport {\n    // Resets the underlying transport.  Returns 0 on success.\n    // This effectively simulates unplugging and replugging\n  public:\n    virtual int Reset() = 0;\n};\n\ntypedef std::function<int(usb_ifc_info*)> ifc_match_func;\n\n// 0 is non blocking\nstd::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms = 0);\n"
  },
  {
    "path": "fastboot/usb_linux.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <ctype.h>\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <linux/usbdevice_fs.h>\n#include <linux/version.h>\n#include <linux/usb/ch9.h>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <chrono>\n#include <memory>\n#include <thread>\n\n#include \"usb.h\"\n#include \"util.h\"\n\nusing namespace std::chrono_literals;\n\n#define MAX_RETRIES 2\n\n/* Timeout in seconds for usb_wait_for_disconnect.\n * It doesn't usually take long for a device to disconnect (almost always\n * under 2 seconds) but we'll time out after 3 seconds just in case.\n */\n#define WAIT_FOR_DISCONNECT_TIMEOUT  3\n\n#ifdef TRACE_USB\n#define DBG1(x...) fprintf(stderr, x)\n#define DBG(x...) fprintf(stderr, x)\n#else\n#define DBG(x...)\n#define DBG1(x...)\n#endif\n\n// Kernels before 3.3 have a 16KiB transfer limit. That limit was replaced\n// with a 16MiB global limit in 3.3, but each URB submitted required a\n// contiguous kernel allocation, so you would get ENOMEM if you tried to\n// send something larger than the biggest available contiguous kernel\n// memory region. 256KiB contiguous allocations are generally not reliable\n// on a device kernel that has been running for a while fragmenting its\n// memory, but that shouldn't be a problem for fastboot on the host.\n// In 3.6, the contiguous buffer limit was removed by allocating multiple\n// 16KiB chunks and having the USB driver stitch them back together while\n// transmitting using a scatter-gather list, so 256KiB bulk transfers should\n// be reliable.\n// 256KiB seems to work, but 1MiB bulk transfers lock up my z620 with a 3.13\n// kernel.\n// 128KiB was experimentally found to be enough to saturate the bus at\n// SuperSpeed+, so we first try double that for writes. If the operation fails\n// due to a lack of contiguous regions (or an ancient kernel), try smaller sizes\n// until we find one that works (see LinuxUsbTransport::Write). Reads are less\n// performance critical so for now just use a known good size.\n#define MAX_USBFS_BULK_WRITE_SIZE (256 * 1024)\n#define MAX_USBFS_BULK_READ_SIZE (16 * 1024)\n\n// This size should pretty much always work (it's compatible with pre-3.3\n// kernels and it's what we used to use historically), so if it doesn't work\n// something has gone badly wrong.\n#define MIN_USBFS_BULK_WRITE_SIZE (16 * 1024)\n\nstruct usb_handle\n{\n    char fname[64];\n    int desc;\n    unsigned char ep_in;\n    unsigned char ep_out;\n};\n\nclass LinuxUsbTransport : public UsbTransport {\n  public:\n    explicit LinuxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)\n        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}\n    ~LinuxUsbTransport() override;\n\n    ssize_t Read(void* data, size_t len) override;\n    ssize_t Write(const void* data, size_t len) override;\n    int Close() override;\n    int Reset() override;\n    int WaitForDisconnect() override;\n\n  private:\n    std::unique_ptr<usb_handle> handle_;\n    const uint32_t ms_timeout_;\n    size_t max_usbfs_bulk_write_size_ = MAX_USBFS_BULK_WRITE_SIZE;\n\n    DISALLOW_COPY_AND_ASSIGN(LinuxUsbTransport);\n};\n\n/* True if name isn't a valid name for a USB device in /sys/bus/usb/devices.\n * Device names are made up of numbers, dots, and dashes, e.g., '7-1.5'.\n * We reject interfaces (e.g., '7-1.5:1.0') and host controllers (e.g. 'usb1').\n * The name must also start with a digit, to disallow '.' and '..'\n */\nstatic inline int badname(const char *name)\n{\n    if (!isdigit(*name))\n      return 1;\n    while(*++name) {\n        if(!isdigit(*name) && *name != '.' && *name != '-')\n            return 1;\n    }\n    return 0;\n}\n\nstatic int check(void *_desc, int len, unsigned type, int size)\n{\n    struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)_desc;\n\n    if(len < size) return -1;\n    if(hdr->bLength < size) return -1;\n    if(hdr->bLength > len) return -1;\n    if(hdr->bDescriptorType != type) return -1;\n\n    return 0;\n}\n\nstatic int filter_usb_device(char* sysfs_name,\n                             char *ptr, int len, int writable,\n                             ifc_match_func callback,\n                             int *ept_in_id, int *ept_out_id, int *ifc_id)\n{\n    struct usb_device_descriptor *dev;\n    struct usb_config_descriptor *cfg;\n    struct usb_interface_descriptor *ifc;\n    struct usb_endpoint_descriptor *ept;\n    struct usb_ifc_info info;\n\n    int in, out;\n    unsigned i;\n    unsigned e;\n\n    if (check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE))\n        return -1;\n    dev = (struct usb_device_descriptor *)ptr;\n    len -= dev->bLength;\n    ptr += dev->bLength;\n\n    if (check(ptr, len, USB_DT_CONFIG, USB_DT_CONFIG_SIZE))\n        return -1;\n    cfg = (struct usb_config_descriptor *)ptr;\n    len -= cfg->bLength;\n    ptr += cfg->bLength;\n\n    info.dev_vendor = dev->idVendor;\n    info.dev_product = dev->idProduct;\n    info.dev_class = dev->bDeviceClass;\n    info.dev_subclass = dev->bDeviceSubClass;\n    info.dev_protocol = dev->bDeviceProtocol;\n    info.writable = writable;\n\n    snprintf(info.device_path, sizeof(info.device_path), \"usb:%s\", sysfs_name);\n\n    /* Read device serial number (if there is one).\n     * We read the serial number from sysfs, since it's faster and more\n     * reliable than issuing a control pipe read, and also won't\n     * cause problems for devices which don't like getting descriptor\n     * requests while they're in the middle of flashing.\n     */\n    info.serial_number[0] = '\\0';\n    if (dev->iSerialNumber) {\n        char path[80];\n        int fd;\n\n        snprintf(path, sizeof(path),\n                 \"/sys/bus/usb/devices/%s/serial\", sysfs_name);\n        path[sizeof(path) - 1] = '\\0';\n\n        fd = open(path, O_RDONLY);\n        if (fd >= 0) {\n            int chars_read = read(fd, info.serial_number,\n                                  sizeof(info.serial_number) - 1);\n            close(fd);\n\n            if (chars_read <= 0)\n                info.serial_number[0] = '\\0';\n            else if (info.serial_number[chars_read - 1] == '\\n') {\n                // strip trailing newline\n                info.serial_number[chars_read - 1] = '\\0';\n            }\n        }\n    }\n\n    for(i = 0; i < cfg->bNumInterfaces; i++) {\n\n        while (len > 0) {\n\t        struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)ptr;\n            if (check(hdr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE) == 0)\n                break;\n            len -= hdr->bLength;\n            ptr += hdr->bLength;\n        }\n\n        if (len <= 0)\n            return -1;\n\n        ifc = (struct usb_interface_descriptor *)ptr;\n        len -= ifc->bLength;\n        ptr += ifc->bLength;\n\n        in = -1;\n        out = -1;\n        info.ifc_class = ifc->bInterfaceClass;\n        info.ifc_subclass = ifc->bInterfaceSubClass;\n        info.ifc_protocol = ifc->bInterfaceProtocol;\n\n        for(e = 0; e < ifc->bNumEndpoints; e++) {\n            while (len > 0) {\n\t            struct usb_descriptor_header *hdr = (struct usb_descriptor_header *)ptr;\n                if (check(hdr, len, USB_DT_ENDPOINT, USB_DT_ENDPOINT_SIZE) == 0)\n                    break;\n                len -= hdr->bLength;\n                ptr += hdr->bLength;\n            }\n            if (len < 0) {\n                break;\n            }\n\n            ept = (struct usb_endpoint_descriptor *)ptr;\n            len -= ept->bLength;\n            ptr += ept->bLength;\n\n            if((ept->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)\n                continue;\n\n            if(ept->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {\n                in = ept->bEndpointAddress;\n            } else {\n                out = ept->bEndpointAddress;\n            }\n\n            // For USB 3.0 devices skip the SS Endpoint Companion descriptor\n            if (check((struct usb_descriptor_hdr *)ptr, len,\n                      USB_DT_SS_ENDPOINT_COMP, USB_DT_SS_EP_COMP_SIZE) == 0) {\n                len -= USB_DT_SS_EP_COMP_SIZE;\n                ptr += USB_DT_SS_EP_COMP_SIZE;\n            }\n        }\n\n        info.has_bulk_in = (in != -1);\n        info.has_bulk_out = (out != -1);\n\n        std::string interface;\n        auto path = android::base::StringPrintf(\"/sys/bus/usb/devices/%s/%s:1.%d/interface\",\n                                                sysfs_name, sysfs_name, ifc->bInterfaceNumber);\n        if (android::base::ReadFileToString(path, &interface)) {\n            if (!interface.empty() && interface.back() == '\\n') {\n                interface.pop_back();\n            }\n            snprintf(info.interface, sizeof(info.interface), \"%s\", interface.c_str());\n        }\n\n        if(callback(&info) == 0) {\n            *ept_in_id = in;\n            *ept_out_id = out;\n            *ifc_id = ifc->bInterfaceNumber;\n            return 0;\n        }\n    }\n\n    return -1;\n}\n\nstatic int read_sysfs_string(const char *sysfs_name, const char *sysfs_node,\n                             char* buf, int bufsize)\n{\n    char path[80];\n    int fd, n;\n\n    snprintf(path, sizeof(path),\n             \"/sys/bus/usb/devices/%s/%s\", sysfs_name, sysfs_node);\n    path[sizeof(path) - 1] = '\\0';\n\n    fd = open(path, O_RDONLY);\n    if (fd < 0)\n        return -1;\n\n    n = read(fd, buf, bufsize - 1);\n    close(fd);\n\n    if (n < 0)\n        return -1;\n\n    buf[n] = '\\0';\n\n    return n;\n}\n\nstatic int read_sysfs_number(const char *sysfs_name, const char *sysfs_node)\n{\n    char buf[16];\n    int value;\n\n    if (read_sysfs_string(sysfs_name, sysfs_node, buf, sizeof(buf)) < 0)\n        return -1;\n\n    if (sscanf(buf, \"%d\", &value) != 1)\n        return -1;\n\n    return value;\n}\n\n/* Given the name of a USB device in sysfs, get the name for the same\n * device in devfs. Returns 0 for success, -1 for failure.\n */\nstatic int convert_to_devfs_name(const char* sysfs_name,\n                                 char* devname, int devname_size)\n{\n    int busnum, devnum;\n\n    busnum = read_sysfs_number(sysfs_name, \"busnum\");\n    if (busnum < 0)\n        return -1;\n\n    devnum = read_sysfs_number(sysfs_name, \"devnum\");\n    if (devnum < 0)\n        return -1;\n\n    snprintf(devname, devname_size, \"/dev/bus/usb/%03d/%03d\", busnum, devnum);\n    return 0;\n}\n\nstatic std::unique_ptr<usb_handle> find_usb_device(const char* base, ifc_match_func callback)\n{\n    std::unique_ptr<usb_handle> usb;\n    char devname[64];\n    char desc[1024];\n    int n, in, out, ifc;\n\n    struct dirent *de;\n    int fd;\n    int writable;\n\n    std::unique_ptr<DIR, decltype(&closedir)> busdir(opendir(base), closedir);\n    if (busdir == 0) return 0;\n\n    while ((de = readdir(busdir.get())) && (usb == nullptr)) {\n        if (badname(de->d_name)) continue;\n\n        if (!convert_to_devfs_name(de->d_name, devname, sizeof(devname))) {\n\n//            DBG(\"[ scanning %s ]\\n\", devname);\n            writable = 1;\n            if ((fd = open(devname, O_RDWR)) < 0) {\n                // Check if we have read-only access, so we can give a helpful\n                // diagnostic like \"adb devices\" does.\n                writable = 0;\n                if ((fd = open(devname, O_RDONLY)) < 0) {\n                    continue;\n                }\n            }\n\n            n = read(fd, desc, sizeof(desc));\n\n            if (filter_usb_device(de->d_name, desc, n, writable, callback, &in, &out, &ifc) == 0) {\n                usb.reset(new usb_handle());\n                strcpy(usb->fname, devname);\n                usb->ep_in = in;\n                usb->ep_out = out;\n                usb->desc = fd;\n\n                n = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &ifc);\n                if (n != 0) {\n                    close(fd);\n                    usb.reset();\n                    continue;\n                }\n            } else {\n                close(fd);\n            }\n        }\n    }\n\n    return usb;\n}\n\nLinuxUsbTransport::~LinuxUsbTransport() {\n    Close();\n}\n\nssize_t LinuxUsbTransport::Write(const void* _data, size_t len)\n{\n    unsigned char *data = (unsigned char*) _data;\n    unsigned count = 0;\n    struct usbdevfs_urb urb[2] = {};\n    bool pending[2] = {};\n\n    if (handle_->ep_out == 0 || handle_->desc == -1) {\n        return -1;\n    }\n\n    auto submit_urb = [&](size_t i) {\n        while (true) {\n            int xfer = (len > max_usbfs_bulk_write_size_) ? max_usbfs_bulk_write_size_ : len;\n\n            urb[i].type = USBDEVFS_URB_TYPE_BULK;\n            urb[i].endpoint = handle_->ep_out;\n            urb[i].buffer_length = xfer;\n            urb[i].buffer = data;\n            urb[i].usercontext = (void *)i;\n\n            int n = ioctl(handle_->desc, USBDEVFS_SUBMITURB, &urb[i]);\n            if (n != 0) {\n                if (errno == ENOMEM && max_usbfs_bulk_write_size_ > MIN_USBFS_BULK_WRITE_SIZE) {\n                    max_usbfs_bulk_write_size_ /= 2;\n                    continue;\n                }\n                DBG(\"ioctl(USBDEVFS_SUBMITURB) failed\\n\");\n                return false;\n            }\n\n            pending[i] = true;\n            count += xfer;\n            len -= xfer;\n            data += xfer;\n\n            return true;\n        }\n    };\n\n    auto reap_urb = [&](size_t i) {\n        while (pending[i]) {\n            struct usbdevfs_urb *urbp;\n            int res = ioctl(handle_->desc, USBDEVFS_REAPURB, &urbp);\n            if (res != 0) {\n                DBG(\"ioctl(USBDEVFS_REAPURB) failed\\n\");\n                return false;\n            }\n            size_t done = (size_t)urbp->usercontext;\n            if (done >= 2 || !pending[done]) {\n                DBG(\"unexpected urb\\n\");\n                return false;\n            }\n            if (urbp->status != 0 || urbp->actual_length != urbp->buffer_length) {\n                DBG(\"urb returned error\\n\");\n                return false;\n            }\n            pending[done] = false;\n        }\n        return true;\n    };\n\n    if (!submit_urb(0)) {\n        return -1;\n    }\n    while (len > 0) {\n        if (!submit_urb(1)) {\n            return -1;\n        }\n        if (!reap_urb(0)) {\n            return -1;\n        }\n        if (len <= 0) {\n            if (!reap_urb(1)) {\n                return -1;\n            }\n            return count;\n        }\n        if (!submit_urb(0)) {\n            return -1;\n        }\n        if (!reap_urb(1)) {\n            return -1;\n        }\n    }\n    if (!reap_urb(0)) {\n        return -1;\n    }\n    return count;\n}\n\nssize_t LinuxUsbTransport::Read(void* _data, size_t len)\n{\n    unsigned char *data = (unsigned char*) _data;\n    unsigned count = 0;\n    struct usbdevfs_bulktransfer bulk;\n    int n, retry;\n\n    if (handle_->ep_in == 0 || handle_->desc == -1) {\n        return -1;\n    }\n\n    while (len > 0) {\n        int xfer = (len > MAX_USBFS_BULK_READ_SIZE) ? MAX_USBFS_BULK_READ_SIZE : len;\n\n        bulk.ep = handle_->ep_in;\n        bulk.len = xfer;\n        bulk.data = data;\n        bulk.timeout = ms_timeout_;\n        retry = 0;\n\n        do {\n            DBG(\"[ usb read %d fd = %d], fname=%s\\n\", xfer, handle_->desc, handle_->fname);\n            n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);\n            DBG(\"[ usb read %d ] = %d, fname=%s, Retry %d \\n\", xfer, n, handle_->fname, retry);\n\n            if (n < 0) {\n                DBG1(\"ERROR: n = %d, errno = %d (%s)\\n\",n, errno, strerror(errno));\n                if (++retry > MAX_RETRIES) return -1;\n                std::this_thread::sleep_for(100ms);\n            }\n        } while (n < 0);\n\n        count += n;\n        len -= n;\n        data += n;\n\n        if(n < xfer) {\n            break;\n        }\n    }\n\n    return count;\n}\n\nint LinuxUsbTransport::Close()\n{\n    int fd;\n\n    fd = handle_->desc;\n    handle_->desc = -1;\n    if(fd >= 0) {\n        close(fd);\n        DBG(\"[ usb closed %d ]\\n\", fd);\n    }\n\n    return 0;\n}\n\nint LinuxUsbTransport::Reset() {\n    int ret = 0;\n    // We reset the USB connection\n    if ((ret = ioctl(handle_->desc, USBDEVFS_RESET, 0))) {\n        return ret;\n    }\n\n    return 0;\n}\n\nstd::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {\n    std::unique_ptr<UsbTransport> result;\n    std::unique_ptr<usb_handle> handle = find_usb_device(\"/sys/bus/usb/devices\", callback);\n\n    if (handle) {\n        result = std::make_unique<LinuxUsbTransport>(std::move(handle), timeout_ms);\n    }\n\n    return result;\n}\n\n/* Wait for the system to notice the device is gone, so that a subsequent\n * fastboot command won't try to access the device before it's rebooted.\n * Returns 0 for success, -1 for timeout.\n */\nint LinuxUsbTransport::WaitForDisconnect()\n{\n  double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT;\n  while (now() < deadline) {\n    if (access(handle_->fname, F_OK)) return 0;\n    std::this_thread::sleep_for(50ms);\n  }\n  return -1;\n}\n"
  },
  {
    "path": "fastboot/usb_osx.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <inttypes.h>\n#include <stdio.h>\n#include <CoreFoundation/CoreFoundation.h>\n#include <IOKit/IOKitLib.h>\n#include <IOKit/IOCFPlugIn.h>\n#include <IOKit/usb/IOUSBLib.h>\n#include <IOKit/IOMessage.h>\n#include <mach/mach_port.h>\n\n#include <memory>\n\n#include \"usb.h\"\n\n\n/*\n * Internal helper functions and associated definitions.\n */\n\n#if TRACE_USB\n#define WARN(x...) fprintf(stderr, x)\n#else\n#define WARN(x...)\n#endif\n\n#define ERR(x...) fprintf(stderr, \"ERROR: \" x)\n\n/** An open usb device */\nstruct usb_handle\n{\n    int success;\n    ifc_match_func callback;\n    usb_ifc_info info;\n\n    UInt8 bulkIn;\n    UInt8 bulkOut;\n    IOUSBInterfaceInterface500** interface;\n    unsigned int zero_mask;\n};\n\nclass OsxUsbTransport : public UsbTransport {\n  public:\n    // A timeout of 0 is blocking\n    OsxUsbTransport(std::unique_ptr<usb_handle> handle, uint32_t ms_timeout = 0)\n        : handle_(std::move(handle)), ms_timeout_(ms_timeout) {}\n    ~OsxUsbTransport() override;\n\n    ssize_t Read(void* data, size_t len) override;\n    ssize_t Write(const void* data, size_t len) override;\n    int Close() override;\n    int Reset() override;\n\n  private:\n    std::unique_ptr<usb_handle> handle_;\n    const uint32_t ms_timeout_;\n\n    DISALLOW_COPY_AND_ASSIGN(OsxUsbTransport);\n};\n\n/** Try out all the interfaces and see if there's a match. Returns 0 on\n * success, -1 on failure. */\nstatic int try_interfaces(IOUSBDeviceInterface500** dev, usb_handle* handle) {\n    IOReturn kr;\n    IOUSBFindInterfaceRequest request;\n    io_iterator_t iterator;\n    io_service_t usbInterface;\n    IOCFPlugInInterface **plugInInterface;\n    IOUSBInterfaceInterface500** interface = NULL;\n    HRESULT result;\n    SInt32 score;\n    UInt8 interfaceNumEndpoints;\n\n    request.bInterfaceClass = 0xff;\n    request.bInterfaceSubClass = 0x42;\n    request.bInterfaceProtocol = 0x03;\n    request.bAlternateSetting = kIOUSBFindInterfaceDontCare;\n\n    // Get an iterator for the interfaces on the device\n    kr = (*dev)->CreateInterfaceIterator(dev, &request, &iterator);\n\n    if (kr != 0) {\n        WARN(\"Couldn't create a device interface iterator: (%08x)\\n\", kr);\n        return -1;\n    }\n\n    while ((usbInterface = IOIteratorNext(iterator))) {\n        // Create an intermediate plugin\n        kr = IOCreatePlugInInterfaceForService(\n                usbInterface,\n                kIOUSBInterfaceUserClientTypeID,\n                kIOCFPlugInInterfaceID,\n                &plugInInterface,\n                &score);\n\n        // No longer need the usbInterface object now that we have the plugin\n        (void) IOObjectRelease(usbInterface);\n\n        if ((kr != 0) || (!plugInInterface)) {\n            WARN(\"Unable to create plugin (%08x)\\n\", kr);\n            continue;\n        }\n\n        // Now create the interface interface for the interface\n        result = (*plugInInterface)\n                         ->QueryInterface(plugInInterface,\n                                          CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID500),\n                                          (LPVOID*)&interface);\n\n        // No longer need the intermediate plugin\n        (*plugInInterface)->Release(plugInInterface);\n\n        if (result || !interface) {\n            ERR(\"Couldn't create interface interface: (%08x)\\n\",\n               (unsigned int) result);\n            // continue so we can try the next interface\n            continue;\n        }\n\n        /*\n         * Now open the interface. This will cause the pipes\n         * associated with the endpoints in the interface descriptor\n         * to be instantiated.\n         */\n\n        /*\n         * TODO: Earlier comments here indicated that it was a bad\n         * idea to just open any interface, because opening \"mass\n         * storage endpoints\" is bad. However, the only way to find\n         * out if an interface does bulk in or out is to open it, and\n         * the framework in this application wants to be told about\n         * bulk in / out before deciding whether it actually wants to\n         * use the interface. Maybe something needs to be done about\n         * this situation.\n         */\n\n        kr = (*interface)->USBInterfaceOpen(interface);\n\n        if (kr != 0) {\n            WARN(\"Could not open interface: (%08x)\\n\", kr);\n            (void) (*interface)->Release(interface);\n            // continue so we can try the next interface\n            continue;\n        }\n\n        // Get the number of endpoints associated with this interface.\n        kr = (*interface)->GetNumEndpoints(interface, &interfaceNumEndpoints);\n\n        if (kr != 0) {\n            ERR(\"Unable to get number of endpoints: (%08x)\\n\", kr);\n            goto next_interface;\n        }\n\n        // Get interface class, subclass and protocol\n        if ((*interface)->GetInterfaceClass(interface, &handle->info.ifc_class) != 0 ||\n            (*interface)->GetInterfaceSubClass(interface, &handle->info.ifc_subclass) != 0 ||\n            (*interface)->GetInterfaceProtocol(interface, &handle->info.ifc_protocol) != 0)\n        {\n            ERR(\"Unable to get interface class, subclass and protocol\\n\");\n            goto next_interface;\n        }\n\n        handle->info.has_bulk_in = 0;\n        handle->info.has_bulk_out = 0;\n\n        // Iterate over the endpoints for this interface and see if there\n        // are any that do bulk in/out.\n        for (UInt8 endpoint = 1; endpoint <= interfaceNumEndpoints; ++endpoint) {\n            UInt8   transferType;\n            UInt16  endPointMaxPacketSize = 0;\n            UInt8   interval;\n\n            // Attempt to retrieve the 'true' packet-size from supported interface.\n            kr = (*interface)\n                 ->GetEndpointProperties(interface, 0, endpoint,\n                                       kUSBOut,\n                                       &transferType,\n                                       &endPointMaxPacketSize, &interval);\n            if (kr == kIOReturnSuccess && !endPointMaxPacketSize) {\n                ERR(\"GetEndpointProperties() returned zero len packet-size\");\n            }\n\n            UInt16  pipePropMaxPacketSize;\n            UInt8   number;\n            UInt8   direction;\n\n            // Proceed with extracting the transfer direction, so we can fill in the\n            // appropriate fields (bulkIn or bulkOut).\n            kr = (*interface)->GetPipeProperties(interface, endpoint,\n                    &direction,\n                    &number, &transferType, &pipePropMaxPacketSize, &interval);\n\n            if (kr == 0) {\n                if (transferType != kUSBBulk) {\n                    continue;\n                }\n\n                if (direction == kUSBIn) {\n                    handle->info.has_bulk_in = 1;\n                    handle->bulkIn = endpoint;\n                } else if (direction == kUSBOut) {\n                    handle->info.has_bulk_out = 1;\n                    handle->bulkOut = endpoint;\n                }\n\n                if (handle->info.ifc_protocol == 0x01) {\n                    handle->zero_mask = (endPointMaxPacketSize == 0) ?\n                        pipePropMaxPacketSize - 1 : endPointMaxPacketSize - 1;\n                }\n            } else {\n                ERR(\"could not get pipe properties for endpoint %u (%08x)\\n\", endpoint, kr);\n            }\n\n            if (handle->info.has_bulk_in && handle->info.has_bulk_out) {\n                break;\n            }\n        }\n\n        if (handle->callback(&handle->info) == 0) {\n            handle->interface = interface;\n            handle->success = 1;\n\n            /*\n             * Clear both the endpoints, because it has been observed\n             * that the Mac may otherwise (incorrectly) start out with\n             * them in bad state.\n             */\n\n            if (handle->info.has_bulk_in) {\n                kr = (*interface)->ClearPipeStallBothEnds(interface,\n                        handle->bulkIn);\n                if (kr != 0) {\n                    ERR(\"could not clear input pipe; result %x, ignoring...\\n\", kr);\n                }\n            }\n\n            if (handle->info.has_bulk_out) {\n                kr = (*interface)->ClearPipeStallBothEnds(interface,\n                        handle->bulkOut);\n                if (kr != 0) {\n                    ERR(\"could not clear output pipe; result %x, ignoring....\\n\", kr);\n                }\n            }\n\n            return 0;\n        }\n\nnext_interface:\n        (*interface)->USBInterfaceClose(interface);\n        (*interface)->Release(interface);\n    }\n\n    return 0;\n}\n\n/** Try out the given device and see if there's a match. Returns 0 on\n * success, -1 on failure.\n */\nstatic int try_device(io_service_t device, usb_handle *handle) {\n    kern_return_t kr;\n    IOCFPlugInInterface **plugin = NULL;\n    IOUSBDeviceInterface500** dev = NULL;\n    SInt32 score;\n    HRESULT result;\n    UInt8 serialIndex;\n    UInt32 locationId;\n\n    // Create an intermediate plugin.\n    kr = IOCreatePlugInInterfaceForService(device,\n            kIOUSBDeviceUserClientTypeID,\n            kIOCFPlugInInterfaceID,\n            &plugin, &score);\n\n    if ((kr != 0) || (plugin == NULL)) {\n        goto error;\n    }\n\n    // Now create the device interface.\n    result = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID500),\n                                       (LPVOID*)&dev);\n    if ((result != 0) || (dev == NULL)) {\n        ERR(\"Couldn't create a device interface (%08x)\\n\", (int) result);\n        goto error;\n    }\n\n    /*\n     * We don't need the intermediate interface after the device interface\n     * is created.\n     */\n    IODestroyPlugInInterface(plugin);\n\n    // So, we have a device, finally. Grab its vitals.\n\n    kr = (*dev)->GetDeviceVendor(dev, &handle->info.dev_vendor);\n    if (kr != 0) {\n        ERR(\"GetDeviceVendor\");\n        goto error;\n    }\n\n    kr = (*dev)->GetDeviceProduct(dev, &handle->info.dev_product);\n    if (kr != 0) {\n        ERR(\"GetDeviceProduct\");\n        goto error;\n    }\n\n    kr = (*dev)->GetDeviceClass(dev, &handle->info.dev_class);\n    if (kr != 0) {\n        ERR(\"GetDeviceClass\");\n        goto error;\n    }\n\n    kr = (*dev)->GetDeviceSubClass(dev, &handle->info.dev_subclass);\n    if (kr != 0) {\n        ERR(\"GetDeviceSubClass\");\n        goto error;\n    }\n\n    kr = (*dev)->GetDeviceProtocol(dev, &handle->info.dev_protocol);\n    if (kr != 0) {\n        ERR(\"GetDeviceProtocol\");\n        goto error;\n    }\n\n    kr = (*dev)->GetLocationID(dev, &locationId);\n    if (kr != 0) {\n        ERR(\"GetLocationId\");\n        goto error;\n    }\n    snprintf(handle->info.device_path, sizeof(handle->info.device_path),\n             \"usb:%\" PRIu32 \"X\", (unsigned int)locationId);\n\n    kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);\n\n    if (serialIndex > 0) {\n        IOUSBDevRequest req;\n        UInt16  buffer[256];\n\n        req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);\n        req.bRequest = kUSBRqGetDescriptor;\n        req.wValue = (kUSBStringDesc << 8) | serialIndex;\n        //language ID (en-us) for serial number string\n        req.wIndex = 0x0409;\n        req.pData = buffer;\n        req.wLength = sizeof(buffer);\n        kr = (*dev)->DeviceRequest(dev, &req);\n\n        if (kr == kIOReturnSuccess && req.wLenDone > 0) {\n            int i, count;\n\n            // skip first word, and copy the rest to the serial string, changing shorts to bytes.\n            count = (req.wLenDone - 1) / 2;\n            for (i = 0; i < count; i++)\n              handle->info.serial_number[i] = buffer[i + 1];\n            handle->info.serial_number[i] = 0;\n        }\n    } else {\n        // device has no serial number\n        handle->info.serial_number[0] = 0;\n    }\n    handle->info.interface[0] = 0;\n    handle->info.writable = 1;\n\n    if (try_interfaces(dev, handle)) {\n        goto error;\n    }\n\n    (*dev)->Release(dev);\n    return 0;\n\n    error:\n\n    if (dev != NULL) {\n        (*dev)->Release(dev);\n    }\n\n    return -1;\n}\n\n\n/** Initializes the USB system. Returns 0 on success, -1 on error. */\nstatic int init_usb(ifc_match_func callback, std::unique_ptr<usb_handle>* handle) {\n    int ret = -1;\n    CFMutableDictionaryRef matchingDict;\n    kern_return_t result;\n    io_iterator_t iterator;\n    usb_handle h;\n\n    h.success = 0;\n    h.callback = callback;\n\n    /*\n     * Create our matching dictionary to find appropriate devices.\n     * IOServiceAddMatchingNotification consumes the reference, so we\n     * do not need to release it.\n     */\n    matchingDict = IOServiceMatching(kIOUSBDeviceClassName);\n\n    if (matchingDict == NULL) {\n        ERR(\"Couldn't create USB matching dictionary.\\n\");\n        return -1;\n    }\n\n    result = IOServiceGetMatchingServices(\n            kIOMasterPortDefault, matchingDict, &iterator);\n\n    if (result != 0) {\n        ERR(\"Could not create iterator.\");\n        return -1;\n    }\n\n    for (;;) {\n        if (! IOIteratorIsValid(iterator)) {\n            break;\n        }\n\n        io_service_t device = IOIteratorNext(iterator);\n\n        if (device == 0) {\n            break;\n        }\n\n        if (try_device(device, &h) != 0) {\n            IOObjectRelease(device);\n            continue;\n        }\n\n        if (h.success) {\n            handle->reset(new usb_handle(h));\n            ret = 0;\n            break;\n        }\n\n        IOObjectRelease(device);\n    }\n\n    IOObjectRelease(iterator);\n\n    return ret;\n}\n\n\n\n/*\n * Definitions of this file's public functions.\n */\nstd::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t timeout_ms) {\n    std::unique_ptr<UsbTransport> result;\n    std::unique_ptr<usb_handle> handle;\n\n    if (init_usb(callback, &handle) < 0) {\n        /* Something went wrong initializing USB. */\n        return result;\n    }\n\n    if (handle) {\n        result = std::make_unique<OsxUsbTransport>(std::move(handle), timeout_ms);\n    }\n\n    return result;\n}\n\nOsxUsbTransport::~OsxUsbTransport() {\n    Close();\n}\n\nint OsxUsbTransport::Close() {\n    /* TODO: Something better here? */\n    return 0;\n}\n\n/*\n  TODO: this SHOULD be easy to do with ResetDevice() from IOUSBDeviceInterface.\n  However to perform operations that manipulate the state of the device, you must\n  claim ownership of the device with USBDeviceOpenSeize(). However, this operation\n  always fails with kIOReturnExclusiveAccess.\n  It seems that the kext com.apple.driver.usb.AppleUSBHostCompositeDevice\n  always loads and claims ownership of the device and refuses to give it up.\n*/\nint OsxUsbTransport::Reset() {\n    ERR(\"USB reset is currently unsupported on osx\\n\");\n    return -1;\n}\n\nssize_t OsxUsbTransport::Read(void* data, size_t len) {\n    IOReturn result;\n    UInt32 numBytes = len;\n\n    if (len == 0) {\n        return 0;\n    }\n\n    if (handle_ == nullptr) {\n        return -1;\n    }\n\n    if (handle_->interface == nullptr) {\n        ERR(\"usb_read interface was null\\n\");\n        return -1;\n    }\n\n    if (handle_->bulkIn == 0) {\n        ERR(\"bulkIn endpoint not assigned\\n\");\n        return -1;\n    }\n\n    if (!ms_timeout_) {\n        result = (*handle_->interface)\n                         ->ReadPipe(handle_->interface, handle_->bulkIn, data, &numBytes);\n    } else {\n        result = (*handle_->interface)\n                         ->ReadPipeTO(handle_->interface, handle_->bulkIn, data, &numBytes,\n                                      ms_timeout_, ms_timeout_);\n    }\n\n    if (result == 0) {\n        return (int) numBytes;\n    } else {\n        ERR(\"usb_read failed with status %x\\n\", result);\n    }\n\n    return -1;\n}\n\nssize_t OsxUsbTransport::Write(const void* data, size_t len) {\n    IOReturn result;\n\n    if (len == 0) {\n        return 0;\n    }\n\n    if (handle_ == NULL) {\n        return -1;\n    }\n\n    if (handle_->interface == NULL) {\n        ERR(\"usb_write interface was null\\n\");\n        return -1;\n    }\n\n    if (handle_->bulkOut == 0) {\n        ERR(\"bulkOut endpoint not assigned\\n\");\n        return -1;\n    }\n\n#if 0\n    result = (*handle_->interface)->WritePipe(\n            handle_->interface, handle_->bulkOut, (void *)data, len);\n#else\n    /* Attempt to work around crashes in the USB driver that may be caused\n     * by trying to write too much data at once.  The kernel IOCopyMapper\n     * panics if a single iovmAlloc needs more than half of its mapper pages.\n     */\n    const int maxLenToSend = 1048576; // 1 MiB\n    int lenRemaining = len;\n    result = 0;\n    while (lenRemaining > 0) {\n        int lenToSend = lenRemaining > maxLenToSend\n            ? maxLenToSend : lenRemaining;\n\n        if (!ms_timeout_) {  // blocking\n            result = (*handle_->interface)\n                             ->WritePipe(handle_->interface, handle_->bulkOut, (void*)data,\n                                         lenToSend);\n        } else {\n            result = (*handle_->interface)\n                             ->WritePipeTO(handle_->interface, handle_->bulkOut, (void*)data,\n                                           lenToSend, ms_timeout_, ms_timeout_);\n        }\n\n        if (result != 0) break;\n\n        lenRemaining -= lenToSend;\n        data = (const char*)data + lenToSend;\n    }\n#endif\n\n    #if 0\n    if ((result == 0) && (handle_->zero_mask)) {\n        /* we need 0-markers and our transfer */\n        if(!(len & handle_->zero_mask)) {\n            result = (*handle_->interface)->WritePipe(\n                    handle_->interface, handle_->bulkOut, (void *)data, 0);\n        }\n    }\n    #endif\n\n    if (result != 0) {\n        ERR(\"usb_write failed with status %x\\n\", result);\n        return -1;\n    }\n\n    return len;\n}\n"
  },
  {
    "path": "fastboot/usb_windows.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <windows.h>\n#include <winerror.h>\n#include <errno.h>\n#include <usb100.h>\n#include <adb_api.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <memory>\n#include <string>\n\n#include \"usb.h\"\n\n//#define TRACE_USB 1\n#if TRACE_USB\n#define DBG(x...) fprintf(stderr, x)\n#else\n#define DBG(x...)\n#endif\n\n#define MAX_USBFS_BULK_SIZE (1024 * 1024)\n\n/** Structure usb_handle describes our connection to the usb device via\n  AdbWinApi.dll. This structure is returned from usb_open() routine and\n  is expected in each subsequent call that is accessing the device.\n*/\nstruct usb_handle {\n    /// Handle to USB interface\n    ADBAPIHANDLE  adb_interface;\n\n    /// Handle to USB read pipe (endpoint)\n    ADBAPIHANDLE  adb_read_pipe;\n\n    /// Handle to USB write pipe (endpoint)\n    ADBAPIHANDLE  adb_write_pipe;\n\n    /// Interface name\n    std::string interface_name;\n};\n\nclass WindowsUsbTransport : public UsbTransport {\n  public:\n    WindowsUsbTransport(std::unique_ptr<usb_handle> handle) : handle_(std::move(handle)) {}\n    ~WindowsUsbTransport() override;\n\n    ssize_t Read(void* data, size_t len) override;\n    ssize_t Write(const void* data, size_t len) override;\n    int Close() override;\n    int Reset() override;\n\n  private:\n    std::unique_ptr<usb_handle> handle_;\n\n    DISALLOW_COPY_AND_ASSIGN(WindowsUsbTransport);\n};\n\n/// Class ID assigned to the device by androidusb.sys\nstatic const GUID usb_class_id = ANDROID_USB_CLASS_ID;\n\n/// Checks if interface (device) matches certain criteria\nint recognized_device(usb_handle* handle, ifc_match_func callback);\n\n/// Opens usb interface (device) by interface (device) name.\nstd::unique_ptr<usb_handle> do_usb_open(const wchar_t* interface_name);\n\n/// Cleans up opened usb handle\nvoid usb_cleanup_handle(usb_handle* handle);\n\n/// Cleans up (but don't close) opened usb handle\nvoid usb_kick(usb_handle* handle);\n\n\nstd::unique_ptr<usb_handle> do_usb_open(const wchar_t* interface_name) {\n    // Allocate our handle\n    std::unique_ptr<usb_handle> ret(new usb_handle);\n\n    // Create interface.\n    ret->adb_interface = AdbCreateInterfaceByName(interface_name);\n\n    if (nullptr == ret->adb_interface) {\n        errno = GetLastError();\n        DBG(\"failed to open interface %S\\n\", interface_name);\n        return nullptr;\n    }\n\n    // Open read pipe (endpoint)\n    ret->adb_read_pipe =\n        AdbOpenDefaultBulkReadEndpoint(ret->adb_interface,\n                                   AdbOpenAccessTypeReadWrite,\n                                   AdbOpenSharingModeReadWrite);\n    if (nullptr != ret->adb_read_pipe) {\n        // Open write pipe (endpoint)\n        ret->adb_write_pipe =\n            AdbOpenDefaultBulkWriteEndpoint(ret->adb_interface,\n                                      AdbOpenAccessTypeReadWrite,\n                                      AdbOpenSharingModeReadWrite);\n        if (nullptr != ret->adb_write_pipe) {\n            // Save interface name\n            unsigned long name_len = 0;\n\n            // First get expected name length\n            AdbGetInterfaceName(ret->adb_interface,\n                          nullptr,\n                          &name_len,\n                          true);\n            if (0 != name_len) {\n                // Now save the name\n                ret->interface_name.resize(name_len);\n                if (AdbGetInterfaceName(ret->adb_interface,\n                              &ret->interface_name[0],\n                              &name_len,\n                              true)) {\n                    // We're done at this point\n                    return ret;\n                }\n            }\n        }\n    }\n\n    // Something went wrong.\n    errno = GetLastError();\n    usb_cleanup_handle(ret.get());\n    SetLastError(errno);\n\n    return nullptr;\n}\n\nssize_t WindowsUsbTransport::Write(const void* data, size_t len) {\n    unsigned long time_out = 5000;\n    unsigned long written = 0;\n    unsigned count = 0;\n    int ret;\n\n    DBG(\"usb_write %zu\\n\", len);\n    if (nullptr != handle_) {\n        // Perform write\n        while(len > 0) {\n            int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;\n            ret = AdbWriteEndpointSync(handle_->adb_write_pipe, const_cast<void*>(data), xfer,\n                                       &written, time_out);\n            errno = GetLastError();\n            DBG(\"AdbWriteEndpointSync returned %d, errno: %d\\n\", ret, errno);\n            if (ret == 0) {\n                // assume ERROR_INVALID_HANDLE indicates we are disconnected\n                if (errno == ERROR_INVALID_HANDLE)\n                usb_kick(handle_.get());\n                return -1;\n            }\n\n            count += written;\n            len -= written;\n            data = (const char *)data + written;\n\n            if (len == 0)\n                return count;\n        }\n    } else {\n        DBG(\"usb_write NULL handle\\n\");\n        SetLastError(ERROR_INVALID_HANDLE);\n    }\n\n    DBG(\"usb_write failed: %d\\n\", errno);\n\n    return -1;\n}\n\nssize_t WindowsUsbTransport::Read(void* data, size_t len) {\n    unsigned long time_out = 0;\n    unsigned long read = 0;\n    size_t count = 0;\n    int ret;\n\n    DBG(\"usb_read %zu\\n\", len);\n    if (nullptr != handle_) {\n        while (len > 0) {\n            size_t xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;\n\n            ret = AdbReadEndpointSync(handle_->adb_read_pipe, data, xfer, &read, time_out);\n            errno = GetLastError();\n            DBG(\"usb_read got: %lu, expected: %zu, errno: %d\\n\", read, xfer, errno);\n            if (ret == 0) {\n                // assume ERROR_INVALID_HANDLE indicates we are disconnected\n                if (errno == ERROR_INVALID_HANDLE)\n                    usb_kick(handle_.get());\n                break;\n            }\n            count += read;\n            len -= read;\n            data = (char*)data + read;\n\n            if (xfer != read || len == 0) return count;\n        }\n    } else {\n        DBG(\"usb_read NULL handle\\n\");\n        SetLastError(ERROR_INVALID_HANDLE);\n    }\n\n    DBG(\"usb_read failed: %d\\n\", errno);\n\n    return -1;\n}\n\nvoid usb_cleanup_handle(usb_handle* handle) {\n    if (NULL != handle) {\n        if (NULL != handle->adb_write_pipe)\n            AdbCloseHandle(handle->adb_write_pipe);\n        if (NULL != handle->adb_read_pipe)\n            AdbCloseHandle(handle->adb_read_pipe);\n        if (NULL != handle->adb_interface)\n            AdbCloseHandle(handle->adb_interface);\n\n        handle->interface_name.clear();\n        handle->adb_write_pipe = NULL;\n        handle->adb_read_pipe = NULL;\n        handle->adb_interface = NULL;\n    }\n}\n\nvoid usb_kick(usb_handle* handle) {\n    if (NULL != handle) {\n        usb_cleanup_handle(handle);\n    } else {\n        SetLastError(ERROR_INVALID_HANDLE);\n        errno = ERROR_INVALID_HANDLE;\n    }\n}\n\nWindowsUsbTransport::~WindowsUsbTransport() {\n    Close();\n}\n\nint WindowsUsbTransport::Close() {\n    DBG(\"usb_close\\n\");\n\n    if (nullptr != handle_) {\n        // Cleanup handle\n        usb_cleanup_handle(handle_.get());\n        handle_.reset();\n    }\n\n    return 0;\n}\n\nint WindowsUsbTransport::Reset() {\n    DBG(\"usb_reset currently unsupported\\n\\n\");\n    // TODO, this is a bit complicated since it is using ADB\n    return -1;\n}\n\nint recognized_device(usb_handle* handle, ifc_match_func callback) {\n    struct usb_ifc_info info;\n    USB_DEVICE_DESCRIPTOR device_desc;\n    USB_INTERFACE_DESCRIPTOR interf_desc;\n\n    if (NULL == handle)\n        return 0;\n\n    // Check vendor and product id first\n    if (!AdbGetUsbDeviceDescriptor(handle->adb_interface, &device_desc)) {\n        DBG(\"skipping device %x:%x\\n\", device_desc.idVendor, device_desc.idProduct);\n        return 0;\n    }\n\n    // Then check interface properties\n    if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface, &interf_desc)) {\n        DBG(\"skipping device %x:%x, failed to find interface\\n\", device_desc.idVendor,\n            device_desc.idProduct);\n        return 0;\n    }\n\n    // Must have two endpoints\n    if (2 != interf_desc.bNumEndpoints) {\n        DBG(\"skipping device %x:%x, incorrect number of endpoints\\n\", device_desc.idVendor,\n            device_desc.idProduct);\n        return 0;\n    }\n\n    info.dev_vendor = device_desc.idVendor;\n    info.dev_product = device_desc.idProduct;\n    info.dev_class = device_desc.bDeviceClass;\n    info.dev_subclass = device_desc.bDeviceSubClass;\n    info.dev_protocol = device_desc.bDeviceProtocol;\n    info.ifc_class = interf_desc.bInterfaceClass;\n    info.ifc_subclass = interf_desc.bInterfaceSubClass;\n    info.ifc_protocol = interf_desc.bInterfaceProtocol;\n    info.writable = 1;\n\n    // read serial number (if there is one)\n    unsigned long serial_number_len = sizeof(info.serial_number);\n    if (!AdbGetSerialNumber(handle->adb_interface, info.serial_number,\n                    &serial_number_len, true)) {\n        info.serial_number[0] = 0;\n    }\n    info.interface[0] = 0;\n\n    info.device_path[0] = 0;\n\n    if (callback(&info) == 0) {\n        DBG(\"skipping device %x:%x, not selected by callback\\n\", device_desc.idVendor,\n            device_desc.idProduct);\n        return 1;\n    }\n\n    DBG(\"found device %x:%x (%s)\\n\", device_desc.idVendor, device_desc.idProduct,\n        info.serial_number);\n    return 0;\n}\n\nstatic std::unique_ptr<usb_handle> find_usb_device(ifc_match_func callback) {\n    std::unique_ptr<usb_handle> handle;\n    char entry_buffer[2048];\n    char interf_name[2048];\n    AdbInterfaceInfo* next_interface = (AdbInterfaceInfo*)(&entry_buffer[0]);\n    unsigned long entry_buffer_size = sizeof(entry_buffer);\n    char* copy_name;\n\n    // Enumerate all present and active interfaces.\n    ADBAPIHANDLE enum_handle =\n        AdbEnumInterfaces(usb_class_id, true, true, true);\n\n    if (NULL == enum_handle)\n        return NULL;\n\n    while (AdbNextInterface(enum_handle, next_interface, &entry_buffer_size)) {\n        // TODO(vchtchetkine): FIXME - temp hack converting wchar_t into char.\n        // It would be better to change AdbNextInterface so it will return\n        // interface name as single char string.\n        const wchar_t* wchar_name = next_interface->device_name;\n        for(copy_name = interf_name;\n                L'\\0' != *wchar_name;\n                wchar_name++, copy_name++) {\n            *copy_name = (char)(*wchar_name);\n        }\n        *copy_name = '\\0';\n\n        DBG(\"attempting to open interface %S\\n\", next_interface->device_name);\n        handle = do_usb_open(next_interface->device_name);\n        if (NULL != handle) {\n            // Lets see if this interface (device) belongs to us\n            if (recognized_device(handle.get(), callback)) {\n                // found it!\n                break;\n            } else {\n                usb_cleanup_handle(handle.get());\n                handle.reset();\n            }\n        }\n\n        entry_buffer_size = sizeof(entry_buffer);\n    }\n\n    AdbCloseHandle(enum_handle);\n    return handle;\n}\n\nstd::unique_ptr<UsbTransport> usb_open(ifc_match_func callback, uint32_t) {\n    std::unique_ptr<UsbTransport> result;\n    std::unique_ptr<usb_handle> handle = find_usb_device(callback);\n\n    if (handle) {\n        result = std::make_unique<WindowsUsbTransport>(std::move(handle));\n    }\n\n    return result;\n}\n"
  },
  {
    "path": "fastboot/util.cpp",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n\n#include \"util.h\"\n\nusing android::base::borrowed_fd;\n\nstatic bool g_verbose = false;\n\ndouble now() {\n    struct timespec ts;\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n    return (double)ts.tv_sec + (double)ts.tv_nsec / 1000000000;\n}\n\nvoid die(const char* fmt, ...) {\n    va_list ap;\n    va_start(ap, fmt);\n    fprintf(stderr, \"fastboot: error: \");\n    vfprintf(stderr, fmt, ap);\n    fprintf(stderr, \"\\n\");\n    va_end(ap);\n    exit(EXIT_FAILURE);\n}\n\nvoid die(const std::string& str) {\n    die(\"%s\", str.c_str());\n}\n\nvoid set_verbose() {\n    g_verbose = true;\n}\n\nvoid verbose(const char* fmt, ...) {\n    if (!g_verbose) return;\n\n    if (*fmt != '\\n') {\n        va_list ap;\n        va_start(ap, fmt);\n        fprintf(stderr, \"fastboot: verbose: \");\n        vfprintf(stderr, fmt, ap);\n        va_end(ap);\n    }\n    fprintf(stderr, \"\\n\");\n}\n\nbool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,\n                               const std::string& partition_name) {\n    for (const auto& partition : metadata.partitions) {\n        auto candidate = android::fs_mgr::GetPartitionName(partition);\n        if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {\n            // On retrofit devices, we don't know if, or whether, the A or B\n            // slot has been flashed for dynamic partitions. Instead we add\n            // both names to the list as a conservative guess.\n            if (candidate + \"_a\" == partition_name || candidate + \"_b\" == partition_name) {\n                return true;\n            }\n        } else if (candidate == partition_name) {\n            return true;\n        }\n    }\n    return false;\n}\n\nbool is_sparse_file(borrowed_fd fd) {\n    SparsePtr s(sparse_file_import(fd.get(), false, false), sparse_file_destroy);\n    return !!s;\n}\n\nint64_t get_file_size(borrowed_fd fd) {\n    struct stat sb;\n    if (fstat(fd.get(), &sb) == -1) {\n        die(\"could not get file size\");\n    }\n    return sb.st_size;\n}\n\nstd::string fb_fix_numeric_var(std::string var) {\n    // Some bootloaders (angler, for example), send spurious leading whitespace.\n    var = android::base::Trim(var);\n    // Some bootloaders (hammerhead, for example) use implicit hex.\n    // This code used to use strtol with base 16.\n    if (!android::base::StartsWith(var, \"0x\")) var = \"0x\" + var;\n    return var;\n}\n"
  },
  {
    "path": "fastboot/util.h",
    "content": "#pragma once\n\n#include <inttypes.h>\n#include <stdlib.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <bootimg.h>\n#include <liblp/liblp.h>\n#include <sparse/sparse.h>\n\nusing SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;\n\n/* util stuff */\ndouble now();\nvoid set_verbose();\n\n// These printf-like functions are implemented in terms of vsnprintf, so they\n// use the same attribute for compile-time format string checking.\nvoid die(const char* fmt, ...) __attribute__((__noreturn__))\n__attribute__((__format__(__printf__, 1, 2)));\n\nvoid verbose(const char* fmt, ...) __attribute__((__format__(__printf__, 1, 2)));\n\nvoid die(const std::string& str) __attribute__((__noreturn__));\n\nbool should_flash_in_userspace(const android::fs_mgr::LpMetadata& metadata,\n                               const std::string& partition_name);\nbool is_sparse_file(android::base::borrowed_fd fd);\nint64_t get_file_size(android::base::borrowed_fd fd);\nstd::string fb_fix_numeric_var(std::string var);\n\nclass ImageSource {\n  public:\n    virtual ~ImageSource(){};\n    virtual bool ReadFile(const std::string& name, std::vector<char>* out) const = 0;\n    virtual android::base::unique_fd OpenFile(const std::string& name) const = 0;\n};\n"
  },
  {
    "path": "fastboot/vendor_boot_img_utils.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"vendor_boot_img_utils.h\"\n\n#include <string.h>\n\n#include <android-base/file.h>\n#include <android-base/result.h>\n#include <bootimg.h>\n#include <libavb/libavb.h>\n\nnamespace {\n\nusing android::base::Result;\n\n// Updates a given buffer by creating a new one.\nclass DataUpdater {\n  public:\n    DataUpdater(const std::string& old_data) : old_data_(&old_data) {\n        old_data_ptr_ = old_data_->data();\n        new_data_.resize(old_data_->size(), '\\0');\n        new_data_ptr_ = new_data_.data();\n    }\n    // Copy |num_bytes| from src to dst.\n    [[nodiscard]] Result<void> Copy(uint32_t num_bytes) {\n        if (num_bytes == 0) return {};\n        if (auto res = CheckAdvance(old_data_ptr_, old_end(), num_bytes, __FUNCTION__); !res.ok())\n            return res;\n        if (auto res = CheckAdvance(new_data_ptr_, new_end(), num_bytes, __FUNCTION__); !res.ok())\n            return res;\n        memcpy(new_data_ptr_, old_data_ptr_, num_bytes);\n        old_data_ptr_ += num_bytes;\n        new_data_ptr_ += num_bytes;\n        return {};\n    }\n    // Replace |old_num_bytes| from src with new data.\n    [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const std::string& new_data) {\n        return Replace(old_num_bytes, new_data.data(), new_data.size());\n    }\n    [[nodiscard]] Result<void> Replace(uint32_t old_num_bytes, const void* new_data,\n                                       uint32_t new_data_size) {\n        if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_num_bytes, __FUNCTION__);\n            !res.ok())\n            return res;\n        old_data_ptr_ += old_num_bytes;\n\n        if (new_data_size == 0) return {};\n        if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_data_size, __FUNCTION__);\n            !res.ok())\n            return res;\n        memcpy(new_data_ptr_, new_data, new_data_size);\n        new_data_ptr_ += new_data_size;\n        return {};\n    }\n    // Skip |old_skip| from src and |new_skip| from dst, respectively.\n    [[nodiscard]] Result<void> Skip(uint32_t old_skip, uint32_t new_skip) {\n        if (auto res = CheckAdvance(old_data_ptr_, old_end(), old_skip, __FUNCTION__); !res.ok())\n            return res;\n        old_data_ptr_ += old_skip;\n        if (auto res = CheckAdvance(new_data_ptr_, new_end(), new_skip, __FUNCTION__); !res.ok())\n            return res;\n        new_data_ptr_ += new_skip;\n        return {};\n    }\n\n    [[nodiscard]] Result<void> Seek(uint32_t offset) {\n        if (offset > size()) return Errorf(\"Cannot seek 0x{:x}, size is 0x{:x}\", offset, size());\n        old_data_ptr_ = old_begin() + offset;\n        new_data_ptr_ = new_begin() + offset;\n        return {};\n    }\n\n    std::string Finish() {\n        new_data_ptr_ = nullptr;\n        return std::move(new_data_);\n    }\n\n    [[nodiscard]] Result<void> CheckOffset(uint32_t old_offset, uint32_t new_offset) {\n        if (old_begin() + old_offset != old_cur())\n            return Errorf(\"Old offset mismatch: expected: 0x{:x}, actual: 0x{:x}\", old_offset,\n                          old_cur() - old_begin());\n        if (new_begin() + new_offset != new_cur())\n            return Errorf(\"New offset mismatch: expected: 0x{:x}, actual: 0x{:x}\", new_offset,\n                          new_cur() - new_begin());\n        return {};\n    }\n\n    uint64_t size() const { return old_data_->size(); }\n    const char* old_begin() const { return old_data_->data(); }\n    const char* old_cur() { return old_data_ptr_; }\n    const char* old_end() const { return old_data_->data() + old_data_->size(); }\n    char* new_begin() { return new_data_.data(); }\n    char* new_cur() { return new_data_ptr_; }\n    char* new_end() { return new_data_.data() + new_data_.size(); }\n\n  private:\n    // Check if it is okay to advance |num_bytes| from |current|.\n    [[nodiscard]] Result<void> CheckAdvance(const char* current, const char* end,\n                                            uint32_t num_bytes, const char* op) {\n        auto new_end = current + num_bytes;\n        if (new_end < current /* add overflow */)\n            return Errorf(\"{}: Addition overflow: 0x{} + 0x{:x} < 0x{}\", op, fmt::ptr(current),\n                          num_bytes, fmt::ptr(current));\n        if (new_end > end)\n            return Errorf(\"{}: Boundary overflow: 0x{} + 0x{:x} > 0x{}\", op, fmt::ptr(current),\n                          num_bytes, fmt::ptr(end));\n        return {};\n    }\n    const std::string* old_data_;\n    std::string new_data_;\n    const char* old_data_ptr_;\n    char* new_data_ptr_;\n};\n\n// Get the size of vendor boot header.\n[[nodiscard]] Result<uint32_t> get_vendor_boot_header_size(const vendor_boot_img_hdr_v3* hdr) {\n    if (hdr->header_version == 3) return sizeof(vendor_boot_img_hdr_v3);\n    if (hdr->header_version == 4) return sizeof(vendor_boot_img_hdr_v4);\n    return Errorf(\"Unrecognized vendor boot header version {}\", hdr->header_version);\n}\n\n// Check that content contains a valid vendor boot image header with a version at least |version|.\n[[nodiscard]] Result<void> check_vendor_boot_hdr(const std::string& content, uint32_t version) {\n    // get_vendor_boot_header_size reads header_version, so make sure reading it does not\n    // go out of bounds by ensuring that the content has at least the size of V3 header.\n    if (content.size() < sizeof(vendor_boot_img_hdr_v3)) {\n        return Errorf(\"Size of vendor boot is 0x{:x}, less than size of V3 header: 0x{:x}\",\n                      content.size(), sizeof(vendor_boot_img_hdr_v3));\n    }\n    // Now read hdr->header_version and assert the size.\n    auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(content.data());\n    auto expect_header_size = get_vendor_boot_header_size(hdr);\n    if (!expect_header_size.ok()) return expect_header_size.error();\n    if (content.size() < *expect_header_size) {\n        return Errorf(\"Size of vendor boot is 0x{:x}, less than size of V{} header: 0x{:x}\",\n                      content.size(), version, *expect_header_size);\n    }\n    if (memcmp(hdr->magic, VENDOR_BOOT_MAGIC, VENDOR_BOOT_MAGIC_SIZE) != 0) {\n        return Errorf(\"Vendor boot image magic mismatch\");\n    }\n    if (hdr->page_size == 0) {\n        return Errorf(\"Page size cannot be zero\");\n    }\n    if (hdr->header_version < version) {\n        return Errorf(\"Require vendor boot header V{} but is V{}\", version, hdr->header_version);\n    }\n    return {};\n}\n\n// Wrapper of ReadFdToString. Seek to the beginning and read the whole file to string.\n[[nodiscard]] Result<std::string> load_file(android::base::borrowed_fd fd, uint64_t expected_size,\n                                            const char* what) {\n    if (lseek(fd.get(), 0, SEEK_SET) != 0) {\n        return ErrnoErrorf(\"Can't seek to the beginning of {} image\", what);\n    }\n    std::string content;\n    if (!android::base::ReadFdToString(fd, &content)) {\n        return ErrnoErrorf(\"Cannot read {} to string\", what);\n    }\n    if (content.size() != expected_size) {\n        return Errorf(\"Size of {} does not match, expected 0x{:x}, read 0x{:x}\", what,\n                      expected_size, content.size());\n    }\n    return content;\n}\n\n// Wrapper of WriteStringToFd. Seek to the beginning and write the whole file to string.\n[[nodiscard]] Result<void> store_file(android::base::borrowed_fd fd, const std::string& data,\n                                      const char* what) {\n    if (lseek(fd.get(), 0, SEEK_SET) != 0) {\n        return ErrnoErrorf(\"Cannot seek to beginning of {} before writing\", what);\n    }\n    if (!android::base::WriteStringToFd(data, fd)) {\n        return ErrnoErrorf(\"Cannot write new content to {}\", what);\n    }\n    if (TEMP_FAILURE_RETRY(ftruncate(fd.get(), data.size())) == -1) {\n        return ErrnoErrorf(\"Truncating new vendor boot image to 0x{:x} fails\", data.size());\n    }\n    return {};\n}\n\n// Copy AVB footer if it exists in the old buffer.\n[[nodiscard]] Result<void> copy_avb_footer(DataUpdater* updater) {\n    if (updater->size() < AVB_FOOTER_SIZE) return {};\n    if (auto res = updater->Seek(updater->size() - AVB_FOOTER_SIZE); !res.ok()) return res;\n    if (memcmp(updater->old_cur(), AVB_FOOTER_MAGIC, AVB_FOOTER_MAGIC_LEN) != 0) return {};\n    return updater->Copy(AVB_FOOTER_SIZE);\n}\n\n// round |value| up to a multiple of |page_size|.\n// aware that this can be integer overflow if value is too large\ninline uint32_t round_up(uint32_t value, uint32_t page_size) {\n    return (value + page_size - 1) / page_size * page_size;\n}\n\n// Replace the vendor ramdisk as a whole.\n[[nodiscard]] Result<std::string> replace_default_vendor_ramdisk(const std::string& vendor_boot,\n                                                                 const std::string& new_ramdisk,\n                                                                 const std::string& new_dtb) {\n    if (auto res = check_vendor_boot_hdr(vendor_boot, 3); !res.ok()) return res.error();\n    auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(vendor_boot.data());\n    auto hdr_size = get_vendor_boot_header_size(hdr);\n    if (!hdr_size.ok()) return hdr_size.error();\n    // Refer to bootimg.h for details. Numbers are in bytes.\n    const uint32_t o = round_up(*hdr_size, hdr->page_size);\n    const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);\n    const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);\n\n    DataUpdater updater(vendor_boot);\n\n    // Copy header (O bytes), then update fields in header.\n    if (auto res = updater.Copy(o); !res.ok()) return res.error();\n    auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v3*>(updater.new_begin());\n    new_hdr->vendor_ramdisk_size = new_ramdisk.size();\n    // Because it is unknown how the new ramdisk is fragmented, the whole table is replaced\n    // with a single entry representing the full ramdisk.\n    if (new_hdr->header_version >= 4) {\n        auto new_hdr_v4 = static_cast<vendor_boot_img_hdr_v4*>(new_hdr);\n        new_hdr_v4->vendor_ramdisk_table_entry_size = sizeof(vendor_ramdisk_table_entry_v4);\n        new_hdr_v4->vendor_ramdisk_table_entry_num = 1;\n        new_hdr_v4->vendor_ramdisk_table_size = new_hdr_v4->vendor_ramdisk_table_entry_num *\n                                                new_hdr_v4->vendor_ramdisk_table_entry_size;\n    }\n\n    // Copy the new ramdisk.\n    if (auto res = updater.Replace(hdr->vendor_ramdisk_size, new_ramdisk); !res.ok())\n        return res.error();\n    const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);\n    if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);\n        !res.ok())\n        return res.error();\n    if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();\n\n    // Copy DTB (Q bytes). Replace if a new one was provided.\n    new_hdr->dtb_size = !new_dtb.empty() ? new_dtb.size() : hdr->dtb_size;\n    const uint32_t new_q = round_up(new_hdr->dtb_size, new_hdr->page_size);\n    if (new_dtb.empty()) {\n        if (auto res = updater.Copy(q); !res.ok()) return res.error();\n    } else {\n        if (auto res = updater.Replace(hdr->dtb_size, new_dtb); !res.ok()) return res.error();\n        if (auto res = updater.Skip(q - hdr->dtb_size, new_q - new_hdr->dtb_size); !res.ok())\n            return res.error();\n    }\n    if (auto res = updater.CheckOffset(o + p + q, o + new_p + new_q); !res.ok()) {\n        return res.error();\n    }\n\n    if (new_hdr->header_version >= 4) {\n        auto hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(hdr);\n        const uint32_t r = round_up(hdr_v4->vendor_ramdisk_table_size, hdr_v4->page_size);\n        const uint32_t s = round_up(hdr_v4->bootconfig_size, hdr_v4->page_size);\n\n        auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());\n        auto new_hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(new_hdr);\n        auto new_r = round_up(new_hdr_v4->vendor_ramdisk_table_size, new_hdr->page_size);\n        if (auto res = updater.Skip(r, new_r); !res.ok()) return res.error();\n        if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + new_q + new_r); !res.ok())\n            return res.error();\n\n        // Replace table with single entry representing the full ramdisk.\n        new_entry->ramdisk_size = new_hdr->vendor_ramdisk_size;\n        new_entry->ramdisk_offset = 0;\n        new_entry->ramdisk_type = VENDOR_RAMDISK_TYPE_NONE;\n        memset(new_entry->ramdisk_name, '\\0', VENDOR_RAMDISK_NAME_SIZE);\n        memset(new_entry->board_id, '\\0', VENDOR_RAMDISK_TABLE_ENTRY_BOARD_ID_SIZE);\n\n        // Copy bootconfig (S bytes).\n        if (auto res = updater.Copy(s); !res.ok()) return res.error();\n    }\n\n    if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();\n    return updater.Finish();\n}\n\n// Find a ramdisk fragment with a unique name. Abort if none or multiple fragments are found.\n[[nodiscard]] Result<const vendor_ramdisk_table_entry_v4*> find_unique_ramdisk(\n        const std::string& ramdisk_name, const vendor_ramdisk_table_entry_v4* table,\n        uint32_t size) {\n    const vendor_ramdisk_table_entry_v4* ret = nullptr;\n    uint32_t idx = 0;\n    const vendor_ramdisk_table_entry_v4* entry = table;\n    for (; idx < size; idx++, entry++) {\n        auto entry_name_c_str = reinterpret_cast<const char*>(entry->ramdisk_name);\n        auto entry_name_len = strnlen(entry_name_c_str, VENDOR_RAMDISK_NAME_SIZE);\n        std::string_view entry_name(entry_name_c_str, entry_name_len);\n        if (entry_name == ramdisk_name) {\n            if (ret != nullptr) {\n                return Errorf(\"Multiple vendor ramdisk '{}' found, name should be unique\",\n                              ramdisk_name.c_str());\n            }\n            ret = entry;\n        }\n    }\n    if (ret == nullptr) {\n        return Errorf(\"Vendor ramdisk '{}' not found\", ramdisk_name.c_str());\n    }\n    return ret;\n}\n\n// Find the vendor ramdisk fragment with |ramdisk_name| within the content of |vendor_boot|, and\n// replace it with the content of |new_ramdisk|.\n[[nodiscard]] Result<std::string> replace_vendor_ramdisk_fragment(const std::string& ramdisk_name,\n                                                                  const std::string& vendor_boot,\n                                                                  const std::string& new_ramdisk,\n                                                                  const std::string& new_dtb) {\n    if (auto res = check_vendor_boot_hdr(vendor_boot, 4); !res.ok()) return res.error();\n    auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v4*>(vendor_boot.data());\n    auto hdr_size = get_vendor_boot_header_size(hdr);\n    if (!hdr_size.ok()) return hdr_size.error();\n    // Refer to bootimg.h for details. Numbers are in bytes.\n    const uint32_t o = round_up(*hdr_size, hdr->page_size);\n    const uint32_t p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);\n    const uint32_t q = round_up(hdr->dtb_size, hdr->page_size);\n    const uint32_t r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);\n    const uint32_t s = round_up(hdr->bootconfig_size, hdr->page_size);\n\n    uint64_t total_size = (uint64_t)o + p + q + r + s;\n    if (total_size > vendor_boot.size()) {\n        return Errorf(\"Vendor boot image size is too small, overflow\");\n    }\n\n    if ((uint64_t)hdr->vendor_ramdisk_table_entry_num * sizeof(vendor_ramdisk_table_entry_v4) >\n        (uint64_t)o + p + q + r) {\n        return Errorf(\"Too many vendor ramdisk entries in table, overflow\");\n    }\n\n    // Find entry with name |ramdisk_name|.\n    auto old_table_start =\n            reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(vendor_boot.data() + o + p + q);\n    auto find_res =\n            find_unique_ramdisk(ramdisk_name, old_table_start, hdr->vendor_ramdisk_table_entry_num);\n    if (!find_res.ok()) return find_res.error();\n    const vendor_ramdisk_table_entry_v4* replace_entry = *find_res;\n    uint32_t replace_idx = replace_entry - old_table_start;\n\n    // Now reconstruct.\n    DataUpdater updater(vendor_boot);\n\n    // Copy header (O bytes), then update fields in header.\n    if (auto res = updater.Copy(o); !res.ok()) return res.error();\n    auto new_hdr = reinterpret_cast<vendor_boot_img_hdr_v4*>(updater.new_begin());\n\n    // Copy ramdisk fragments, replace for the matching index.\n    {\n        auto old_ramdisk_entry = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(\n                vendor_boot.data() + o + p + q);\n        uint32_t new_total_ramdisk_size = 0;\n        for (uint32_t new_ramdisk_idx = 0; new_ramdisk_idx < hdr->vendor_ramdisk_table_entry_num;\n             new_ramdisk_idx++, old_ramdisk_entry++) {\n            if (new_ramdisk_idx == replace_idx) {\n                if (auto res = updater.Replace(replace_entry->ramdisk_size, new_ramdisk); !res.ok())\n                    return res.error();\n                new_total_ramdisk_size += new_ramdisk.size();\n            } else {\n                if (auto res = updater.Copy(old_ramdisk_entry->ramdisk_size); !res.ok())\n                    return res.error();\n                new_total_ramdisk_size += old_ramdisk_entry->ramdisk_size;\n            }\n        }\n        new_hdr->vendor_ramdisk_size = new_total_ramdisk_size;\n    }\n\n    // Pad ramdisk to page boundary.\n    const uint32_t new_p = round_up(new_hdr->vendor_ramdisk_size, new_hdr->page_size);\n    if (auto res = updater.Skip(p - hdr->vendor_ramdisk_size, new_p - new_hdr->vendor_ramdisk_size);\n        !res.ok())\n        return res.error();\n    if (auto res = updater.CheckOffset(o + p, o + new_p); !res.ok()) return res.error();\n\n    // Copy DTB (Q bytes). Replace if a new one was provided.\n    new_hdr->dtb_size = !new_dtb.empty() ? new_dtb.size() : hdr->dtb_size;\n    const uint32_t new_q = round_up(new_hdr->dtb_size, new_hdr->page_size);\n    if (new_dtb.empty()) {\n        if (auto res = updater.Copy(q); !res.ok()) return res.error();\n    } else {\n        if (auto res = updater.Replace(hdr->dtb_size, new_dtb); !res.ok()) return res.error();\n        if (auto res = updater.Skip(q - hdr->dtb_size, new_q - new_hdr->dtb_size); !res.ok())\n            return res.error();\n    }\n    if (auto res = updater.CheckOffset(o + p + q, o + new_p + new_q); !res.ok()) {\n        return res.error();\n    }\n\n    // Copy table, but with corresponding entries modified, including:\n    // - ramdisk_size of the entry replaced\n    // - ramdisk_offset of subsequent entries.\n    for (uint32_t new_total_ramdisk_size = 0, new_entry_idx = 0;\n         new_entry_idx < hdr->vendor_ramdisk_table_entry_num; new_entry_idx++) {\n        auto new_entry = reinterpret_cast<vendor_ramdisk_table_entry_v4*>(updater.new_cur());\n        if (auto res = updater.Copy(hdr->vendor_ramdisk_table_entry_size); !res.ok())\n            return res.error();\n        new_entry->ramdisk_offset = new_total_ramdisk_size;\n\n        if (new_entry_idx == replace_idx) {\n            new_entry->ramdisk_size = new_ramdisk.size();\n        }\n        new_total_ramdisk_size += new_entry->ramdisk_size;\n    }\n\n    // Copy padding of R pages; this is okay because table size is not changed.\n    if (auto res = updater.Copy(r - hdr->vendor_ramdisk_table_entry_num *\n                                            hdr->vendor_ramdisk_table_entry_size);\n        !res.ok())\n        return res.error();\n    if (auto res = updater.CheckOffset(o + p + q + r, o + new_p + new_q + r); !res.ok())\n        return res.error();\n\n    // Copy bootconfig (S bytes).\n    if (auto res = updater.Copy(s); !res.ok()) return res.error();\n\n    if (auto res = copy_avb_footer(&updater); !res.ok()) return res.error();\n    return updater.Finish();\n}\n\n}  // namespace\n\n[[nodiscard]] Result<void> replace_vendor_ramdisk(\n        android::base::borrowed_fd vendor_boot_fd, uint64_t vendor_boot_size,\n        const std::string& ramdisk_name, android::base::borrowed_fd new_ramdisk_fd,\n        uint64_t new_ramdisk_size, android::base::borrowed_fd new_dtb_fd, uint64_t new_dtb_size) {\n    Result<std::string> new_dtb = {\"\"};\n    if (new_ramdisk_size > std::numeric_limits<uint32_t>::max()) {\n        return Errorf(\"New vendor ramdisk is too big\");\n    }\n\n    auto vendor_boot = load_file(vendor_boot_fd, vendor_boot_size, \"vendor boot\");\n    if (!vendor_boot.ok()) return vendor_boot.error();\n    auto new_ramdisk = load_file(new_ramdisk_fd, new_ramdisk_size, \"new vendor ramdisk\");\n    if (!new_ramdisk.ok()) return new_ramdisk.error();\n    if (new_dtb_size > 0 && new_dtb_fd >= 0) {\n        new_dtb = load_file(new_dtb_fd, new_dtb_size, \"new dtb\");\n        if (!new_dtb.ok()) return new_dtb.error();\n    }\n\n    Result<std::string> new_vendor_boot;\n    if (ramdisk_name == \"default\") {\n        new_vendor_boot = replace_default_vendor_ramdisk(*vendor_boot, *new_ramdisk, *new_dtb);\n    } else {\n        new_vendor_boot =\n                replace_vendor_ramdisk_fragment(ramdisk_name, *vendor_boot, *new_ramdisk, *new_dtb);\n    }\n    if (!new_vendor_boot.ok()) return new_vendor_boot.error();\n    if (auto res = store_file(vendor_boot_fd, *new_vendor_boot, \"new vendor boot image\"); !res.ok())\n        return res.error();\n\n    return {};\n}\n"
  },
  {
    "path": "fastboot/vendor_boot_img_utils.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <inttypes.h>\n\n#include <string>\n\n#include <android-base/result.h>\n#include <android-base/unique_fd.h>\n\n// Replace the vendor ramdisk named |ramdisk_name| within the vendor boot image,\n// specified by |vendor_boot_fd|, with the ramdisk specified by |new_ramdisk_fd|. Checks\n// that the size of the files are |vendor_boot_size| and |new_ramdisk_size|, respectively.\n// If |ramdisk_name| is \"default\", replace the vendor ramdisk as a whole. Otherwise, replace\n// a vendor ramdisk fragment with the given unique name.\n[[nodiscard]] android::base::Result<void> replace_vendor_ramdisk(\n        android::base::borrowed_fd vendor_boot_fd, uint64_t vendor_boot_size,\n        const std::string& ramdisk_name, android::base::borrowed_fd new_ramdisk_fd,\n        uint64_t new_ramdisk_size, android::base::borrowed_fd new_dtb_fd, uint64_t new_dtb_size);\n"
  },
  {
    "path": "fastboot/vendor_boot_img_utils_test.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <filesystem>\n#include <optional>\n#include <string_view>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/result.h>\n#include <android-base/strings.h>\n#include <bootimg.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <libavb/libavb.h>\n\n#include \"vendor_boot_img_utils.h\"\n\nusing android::base::borrowed_fd;\nusing android::base::ErrnoError;\nusing android::base::GetExecutableDirectory;\nusing android::base::ReadFdToString;\nusing android::base::Result;\nusing testing::AllOf;\nusing testing::Each;\nusing testing::Eq;\nusing testing::HasSubstr;\nusing testing::Not;\nusing testing::Property;\nusing std::string_literals::operator\"\"s;\n\n// Expect that the Result<T> returned by |expr| is successful, and value matches |result_matcher|.\n#define EXPECT_RESULT(expr, result_matcher)                          \\\n    EXPECT_THAT(expr, AllOf(Property(&decltype(expr)::ok, Eq(true)), \\\n                            Property(&decltype(expr)::value, result_matcher)))\n\n// Expect that the Result<T> returned by |expr| fails, and error message matches |error_matcher|.\n#define EXPECT_ERROR(expr, error_matcher)                                                        \\\n    do {                                                                                         \\\n        EXPECT_THAT(                                                                             \\\n                expr,                                                                            \\\n                AllOf(Property(&decltype(expr)::ok, Eq(false)),                                  \\\n                      Property(&decltype(expr)::error,                                           \\\n                               Property(&decltype(expr)::error_type::message, error_matcher)))); \\\n    } while (0)\n\nnamespace {\n\n// Wrapper of fstat.\nResult<uint64_t> FileSize(borrowed_fd fd, std::filesystem::path path) {\n    struct stat sb;\n    if (fstat(fd.get(), &sb) == -1) return ErrnoError() << \"fstat(\" << path << \")\";\n    return sb.st_size;\n}\n\n// Seek to beginning then read the whole file.\nResult<std::string> ReadStartOfFdToString(borrowed_fd fd, std::filesystem::path path) {\n    if (lseek(fd.get(), 0, SEEK_SET) != 0)\n        return ErrnoError() << \"lseek(\" << path << \", 0, SEEK_SET)\";\n    std::string content;\n    if (!android::base::ReadFdToString(fd, &content)) return ErrnoError() << \"read(\" << path << \")\";\n    return content;\n}\n\n// Round |value| up to page boundary.\ninline uint32_t round_up(uint32_t value, uint32_t page_size) {\n    return (value + page_size - 1) / page_size * page_size;\n}\n\n// Match is successful if |arg| is a zero-padded version of |expected|.\nMATCHER_P(IsPadded, expected, (negation ? \"is\" : \"isn't\") + \" zero-padded of expected value\"s) {\n    if (arg.size() < expected.size()) return false;\n    if (0 != memcmp(arg.data(), expected.data(), expected.size())) return false;\n    auto remainder = std::string_view(arg).substr(expected.size());\n    for (char e : remainder)\n        if (e != '\\0') return false;\n    return true;\n}\n\n// Same as Eq, but don't print the content to avoid spam.\nMATCHER_P(MemEq, expected, (negation ? \"is\" : \"isn't\") + \" expected value\"s) {\n    if (arg.size() != expected.size()) return false;\n    return 0 == memcmp(arg.data(), expected.data(), expected.size());\n}\n\n// Expect that |arg| and |expected| has the same AVB footer.\nMATCHER_P(HasSameAvbFooter, expected,\n          (negation ? \"has\" : \"does not have\") + \"expected AVB footer\"s) {\n    if (expected.size() < AVB_FOOTER_SIZE || arg.size() < AVB_FOOTER_SIZE) return false;\n    return std::string_view(expected).substr(expected.size() - AVB_FOOTER_SIZE) ==\n           std::string_view(arg).substr(arg.size() - AVB_FOOTER_SIZE);\n}\n\n// A lazy handle of a file.\nstruct TestFileHandle {\n    virtual ~TestFileHandle() = default;\n    // Lazily call OpenImpl(), cache result in open_result_.\n    android::base::Result<void> Open() {\n        if (!open_result_.has_value()) open_result_ = OpenImpl();\n        return open_result_.value();\n    }\n    // The original size at the time when the file is opened. If the file has been modified,\n    // this field is NOT updated.\n    uint64_t size() {\n        CHECK(open_result_.has_value());\n        return size_;\n    }\n    // The current size of the file. If the file has been modified since opened,\n    // this is updated.\n    Result<uint64_t> fsize() {\n        CHECK(open_result_.has_value());\n        return FileSize(fd_, abs_path_);\n    }\n    borrowed_fd fd() {\n        CHECK(open_result_.has_value());\n        return fd_;\n    }\n    Result<std::string> Read() {\n        CHECK(open_result_.has_value());\n        return ReadStartOfFdToString(fd_, abs_path_);\n    }\n\n  private:\n    std::filesystem::path abs_path_;\n    uint64_t size_;\n    std::optional<android::base::Result<void>> open_result_;\n    borrowed_fd fd_{-1};\n    // Opens |rel_path_| as a readonly fd, pass it to Transform, and store result to\n    // |borrowed_fd_|.\n    android::base::Result<void> OpenImpl() {\n        android::base::unique_fd read_fd(TEMP_FAILURE_RETRY(\n                open(abs_path_.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY)));\n        if (!read_fd.ok()) return ErrnoError() << \"open(\" << abs_path_ << \")\";\n        auto size = FileSize(read_fd, abs_path_);\n        if (!size.ok()) return size.error();\n        size_ = *size;\n\n        auto borrowed_fd = Transform(abs_path_, std::move(read_fd));\n        if (!borrowed_fd.ok()) return borrowed_fd.error();\n        fd_ = borrowed_fd.value();\n\n        return {};\n    }\n\n  protected:\n    // |rel_path| is the relative path under test data directory.\n    TestFileHandle(const std::filesystem::path& rel_path)\n        : abs_path_(std::filesystem::path(GetExecutableDirectory()) / rel_path) {}\n    // Given |read_fd|, the readonly fd on the test file, return an fd that's suitable for client\n    // to use. Implementation is responsible for managing the lifetime of the returned fd.\n    virtual android::base::Result<borrowed_fd> Transform(const std::filesystem::path& abs_path,\n                                                         android::base::unique_fd read_fd) = 0;\n};\n\n// A TestFileHandle where the file is readonly.\nstruct ReadOnlyTestFileHandle : TestFileHandle {\n    ReadOnlyTestFileHandle(const std::filesystem::path& rel_path) : TestFileHandle(rel_path) {}\n\n  private:\n    android::base::unique_fd owned_fd_;\n    android::base::Result<borrowed_fd> Transform(const std::filesystem::path&,\n                                                 android::base::unique_fd read_fd) override {\n        owned_fd_ = std::move(read_fd);\n        return owned_fd_;\n    }\n};\n\n// A TestFileHandle where the test file is copies, hence read-writable.\nstruct ReadWriteTestFileHandle : TestFileHandle {\n    ReadWriteTestFileHandle(const std::filesystem::path& rel_path) : TestFileHandle(rel_path) {}\n\n  private:\n    std::unique_ptr<TemporaryFile> temp_file_;\n\n    android::base::Result<borrowed_fd> Transform(const std::filesystem::path& abs_path,\n                                                 android::base::unique_fd read_fd) override {\n        // Make a copy to avoid writing to test data. Test files are small, so it is okay\n        // to read the whole file.\n        auto content = ReadStartOfFdToString(read_fd, abs_path);\n        if (!content.ok()) return content.error();\n        temp_file_ = std::make_unique<TemporaryFile>();\n        if (temp_file_->fd == -1)\n            return ErrnoError() << \"copy \" << abs_path << \": open temp file failed\";\n        if (!android::base::WriteStringToFd(*content, temp_file_->fd))\n            return ErrnoError() << \"copy \" << abs_path << \": write temp file failed\";\n\n        return temp_file_->fd;\n    }\n};\n\nclass RepackVendorBootImgTestEnv : public ::testing::Environment {\n  public:\n    virtual void SetUp() {\n        OpenTestFile(\"test_dtb.img\", &dtb, &dtb_content);\n        OpenTestFile(\"test_bootconfig.img\", &bootconfig, &bootconfig_content);\n        OpenTestFile(\"test_vendor_ramdisk_none.img\", &none, &none_content);\n        OpenTestFile(\"test_vendor_ramdisk_platform.img\", &platform, &platform_content);\n        OpenTestFile(\"test_vendor_ramdisk_replace.img\", &replace, &replace_content);\n    }\n\n    std::unique_ptr<TestFileHandle> dtb;\n    std::string dtb_content;\n    std::unique_ptr<TestFileHandle> bootconfig;\n    std::string bootconfig_content;\n    std::unique_ptr<TestFileHandle> none;\n    std::string none_content;\n    std::unique_ptr<TestFileHandle> platform;\n    std::string platform_content;\n    std::unique_ptr<TestFileHandle> replace;\n    std::string replace_content;\n\n  private:\n    void OpenTestFile(const char* rel_path, std::unique_ptr<TestFileHandle>* handle,\n                      std::string* content) {\n        *handle = std::make_unique<ReadOnlyTestFileHandle>(rel_path);\n        ASSERT_RESULT_OK((*handle)->Open());\n        auto content_res = (*handle)->Read();\n        ASSERT_RESULT_OK(content_res);\n        *content = *content_res;\n    }\n};\nRepackVendorBootImgTestEnv* env = nullptr;\n\nstruct RepackVendorBootImgTestParam {\n    std::string vendor_boot_file_name;\n    std::string dtb_file_name;\n    uint32_t expected_header_version;\n    friend std::ostream& operator<<(std::ostream& os, const RepackVendorBootImgTestParam& param) {\n        return os << param.vendor_boot_file_name;\n    }\n};\n\nclass RepackVendorBootImgTest : public ::testing::TestWithParam<RepackVendorBootImgTestParam> {\n  public:\n    virtual void SetUp() {\n        vboot = std::make_unique<ReadWriteTestFileHandle>(GetParam().vendor_boot_file_name);\n        ASSERT_RESULT_OK(vboot->Open());\n\n        if (!GetParam().dtb_file_name.empty()) {\n            dtb_replacement = std::make_unique<ReadOnlyTestFileHandle>(GetParam().dtb_file_name);\n            ASSERT_RESULT_OK(dtb_replacement->Open());\n        }\n    }\n    std::unique_ptr<TestFileHandle> vboot;\n    std::unique_ptr<TestFileHandle> dtb_replacement;\n};\n\nTEST_P(RepackVendorBootImgTest, InvalidSize) {\n    EXPECT_ERROR(\n            replace_vendor_ramdisk(vboot->fd(), vboot->size() + 1, \"default\", env->replace->fd(),\n                                   env->replace->size(),\n                                   !GetParam().dtb_file_name.empty() ? dtb_replacement->fd()\n                                                                     : android::base::unique_fd(-1),\n                                   !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0),\n            HasSubstr(\"Size of vendor boot does not match\"));\n    EXPECT_ERROR(\n            replace_vendor_ramdisk(vboot->fd(), vboot->size(), \"default\", env->replace->fd(),\n                                   env->replace->size() + 1,\n                                   !GetParam().dtb_file_name.empty() ? dtb_replacement->fd()\n                                                                     : android::base::unique_fd(-1),\n                                   !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0),\n            HasSubstr(\"Size of new vendor ramdisk does not match\"));\n    if (!GetParam().dtb_file_name.empty()) {\n        EXPECT_ERROR(replace_vendor_ramdisk(vboot->fd(), vboot->size(), \"default\",\n                                            env->replace->fd(), env->replace->size(),\n                                            dtb_replacement->fd(), dtb_replacement->size() + 1),\n                     HasSubstr(\"Size of new dtb does not match\"));\n    }\n    EXPECT_ERROR(\n            replace_vendor_ramdisk(\n                    vboot->fd(), vboot->size(), \"default\", env->replace->fd(), env->replace->size(),\n                    android::base::unique_fd(std::numeric_limits<int32_t>::max()), 1),\n            HasSubstr(\"Can't seek to the beginning of new dtb image\"));\n}\n\nTEST_P(RepackVendorBootImgTest, ReplaceUnknown) {\n    auto res = replace_vendor_ramdisk(\n            vboot->fd(), vboot->size(), \"unknown\", env->replace->fd(), env->replace->size(),\n            !GetParam().dtb_file_name.empty() ? dtb_replacement->fd()\n                                              : android::base::unique_fd(-1),\n            !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0);\n    if (GetParam().expected_header_version == 3) {\n        EXPECT_ERROR(res, Eq(\"Require vendor boot header V4 but is V3\"));\n    } else if (GetParam().expected_header_version == 4) {\n        EXPECT_ERROR(res, Eq(\"Vendor ramdisk 'unknown' not found\"));\n    }\n}\n\nTEST_P(RepackVendorBootImgTest, ReplaceDefault) {\n    auto old_content = vboot->Read();\n    ASSERT_RESULT_OK(old_content);\n\n    ASSERT_RESULT_OK(replace_vendor_ramdisk(\n            vboot->fd(), vboot->size(), \"default\", env->replace->fd(), env->replace->size(),\n            !GetParam().dtb_file_name.empty() ? dtb_replacement->fd()\n                                              : android::base::unique_fd(-1),\n            !GetParam().dtb_file_name.empty() ? dtb_replacement->size() : 0));\n    EXPECT_RESULT(vboot->fsize(), vboot->size()) << \"File size should not change after repack\";\n\n    auto new_content_res = vboot->Read();\n    ASSERT_RESULT_OK(new_content_res);\n    std::string_view new_content(*new_content_res);\n\n    auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v3*>(new_content.data());\n    ASSERT_EQ(0, memcmp(VENDOR_BOOT_MAGIC, hdr->magic, VENDOR_BOOT_MAGIC_SIZE));\n    ASSERT_EQ(GetParam().expected_header_version, hdr->header_version);\n    EXPECT_EQ(hdr->vendor_ramdisk_size, env->replace->size());\n    if (GetParam().dtb_file_name.empty()) {\n        EXPECT_EQ(hdr->dtb_size, env->dtb->size());\n    } else {\n        EXPECT_EQ(hdr->dtb_size, dtb_replacement->size());\n    }\n\n    auto o = round_up(sizeof(vendor_boot_img_hdr_v3), hdr->page_size);\n    auto p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);\n    auto q = round_up(hdr->dtb_size, hdr->page_size);\n\n    EXPECT_THAT(new_content.substr(o, p), IsPadded(env->replace_content));\n    if (GetParam().dtb_file_name.empty()) {\n        EXPECT_THAT(new_content.substr(o + p, q), IsPadded(env->dtb_content));\n    } else {\n        auto dtb_content_res = dtb_replacement->Read();\n        EXPECT_THAT(new_content.substr(o + p, q), IsPadded(*dtb_content_res));\n    }\n\n    if (hdr->header_version < 4) return;\n\n    auto hdr_v4 = static_cast<const vendor_boot_img_hdr_v4*>(hdr);\n    EXPECT_EQ(hdr_v4->vendor_ramdisk_table_entry_num, 1);\n    EXPECT_EQ(hdr_v4->vendor_ramdisk_table_size, 1 * hdr_v4->vendor_ramdisk_table_entry_size);\n    EXPECT_GE(hdr_v4->vendor_ramdisk_table_entry_size, sizeof(vendor_ramdisk_table_entry_v4));\n    auto entry = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(&new_content[o + p + q]);\n    EXPECT_EQ(entry->ramdisk_offset, 0);\n    EXPECT_EQ(entry->ramdisk_size, hdr_v4->vendor_ramdisk_size);\n    EXPECT_EQ(entry->ramdisk_type, VENDOR_RAMDISK_TYPE_NONE);\n\n    EXPECT_EQ(hdr_v4->bootconfig_size, env->bootconfig->size());\n    auto r = round_up(hdr_v4->vendor_ramdisk_table_size, hdr_v4->page_size);\n    auto s = round_up(hdr_v4->bootconfig_size, hdr_v4->page_size);\n    EXPECT_THAT(new_content.substr(o + p + q + r, s), IsPadded(env->bootconfig_content));\n\n    EXPECT_THAT(new_content, HasSameAvbFooter(*old_content));\n}\n\nINSTANTIATE_TEST_SUITE_P(\n        RepackVendorBootImgTest, RepackVendorBootImgTest,\n        ::testing::Values(RepackVendorBootImgTestParam{\"vendor_boot_v3.img\", \"\", 3},\n                          RepackVendorBootImgTestParam{\"vendor_boot_v4_with_frag.img\", \"\", 4},\n                          RepackVendorBootImgTestParam{\"vendor_boot_v4_without_frag.img\", \"\", 4},\n                          RepackVendorBootImgTestParam{\"vendor_boot_v4_with_frag.img\",\n                                                       \"dtb_replace.img\", 4},\n                          RepackVendorBootImgTestParam{\"vendor_boot_v4_without_frag.img\",\n                                                       \"dtb_replace.img\", 4}),\n        [](const auto& info) {\n            std::string test_name =\n                    android::base::StringReplace(info.param.vendor_boot_file_name, \".\", \"_\", false);\n            return test_name + (!info.param.dtb_file_name.empty() ? \"_replace_dtb\" : \"\");\n        });\n\nstd::string_view GetRamdiskName(const vendor_ramdisk_table_entry_v4* entry) {\n    auto ramdisk_name = reinterpret_cast<const char*>(entry->ramdisk_name);\n    return std::string_view(ramdisk_name, strnlen(ramdisk_name, VENDOR_RAMDISK_NAME_SIZE));\n}\n\nclass RepackVendorBootImgTestV4 : public ::testing::TestWithParam<uint32_t /* ramdisk type */> {\n  public:\n    virtual void SetUp() {\n        vboot = std::make_unique<ReadWriteTestFileHandle>(\"vendor_boot_v4_with_frag.img\");\n        ASSERT_RESULT_OK(vboot->Open());\n    }\n    std::unique_ptr<TestFileHandle> vboot;\n};\n\nTEST_P(RepackVendorBootImgTestV4, Replace) {\n    uint32_t replace_ramdisk_type = GetParam();\n    std::string replace_ramdisk_name;\n    std::string expect_new_ramdisk_content;\n    uint32_t expect_none_size = env->none->size();\n    uint32_t expect_platform_size = env->platform->size();\n    switch (replace_ramdisk_type) {\n        case VENDOR_RAMDISK_TYPE_NONE:\n            replace_ramdisk_name = \"none_ramdisk\";\n            expect_new_ramdisk_content = env->replace_content + env->platform_content;\n            expect_none_size = env->replace->size();\n            break;\n        case VENDOR_RAMDISK_TYPE_PLATFORM:\n            replace_ramdisk_name = \"platform_ramdisk\";\n            expect_new_ramdisk_content = env->none_content + env->replace_content;\n            expect_platform_size = env->replace->size();\n            break;\n        default:\n            LOG(FATAL) << \"Ramdisk type \" << replace_ramdisk_type\n                       << \" is not supported by this test.\";\n    }\n\n    auto old_content = vboot->Read();\n    ASSERT_RESULT_OK(old_content);\n\n    ASSERT_RESULT_OK(replace_vendor_ramdisk(vboot->fd(), vboot->size(), replace_ramdisk_name,\n                                            env->replace->fd(), env->replace->size(),\n                                            android::base::unique_fd(-1), 0));\n    EXPECT_RESULT(vboot->fsize(), vboot->size()) << \"File size should not change after repack\";\n\n    auto new_content_res = vboot->Read();\n    ASSERT_RESULT_OK(new_content_res);\n    std::string_view new_content(*new_content_res);\n\n    auto hdr = reinterpret_cast<const vendor_boot_img_hdr_v4*>(new_content.data());\n    ASSERT_EQ(0, memcmp(VENDOR_BOOT_MAGIC, hdr->magic, VENDOR_BOOT_MAGIC_SIZE));\n    ASSERT_EQ(4, hdr->header_version);\n    EXPECT_EQ(hdr->vendor_ramdisk_size, expect_none_size + expect_platform_size);\n    EXPECT_EQ(hdr->dtb_size, env->dtb->size());\n    EXPECT_EQ(hdr->bootconfig_size, env->bootconfig->size());\n\n    auto o = round_up(sizeof(vendor_boot_img_hdr_v3), hdr->page_size);\n    auto p = round_up(hdr->vendor_ramdisk_size, hdr->page_size);\n    auto q = round_up(hdr->dtb_size, hdr->page_size);\n    auto r = round_up(hdr->vendor_ramdisk_table_size, hdr->page_size);\n    auto s = round_up(hdr->bootconfig_size, hdr->page_size);\n\n    EXPECT_THAT(new_content.substr(o, p), IsPadded(expect_new_ramdisk_content));\n    EXPECT_THAT(new_content.substr(o + p, q), IsPadded(env->dtb_content));\n\n    // Check changes in table.\n    EXPECT_EQ(hdr->vendor_ramdisk_table_entry_num, 2);\n    EXPECT_EQ(hdr->vendor_ramdisk_table_size, 2 * hdr->vendor_ramdisk_table_entry_size);\n    EXPECT_GE(hdr->vendor_ramdisk_table_entry_size, sizeof(vendor_ramdisk_table_entry_v4));\n    auto entry_none =\n            reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(&new_content[o + p + q]);\n    EXPECT_EQ(entry_none->ramdisk_offset, 0);\n    EXPECT_EQ(entry_none->ramdisk_size, expect_none_size);\n    EXPECT_EQ(entry_none->ramdisk_type, VENDOR_RAMDISK_TYPE_NONE);\n    EXPECT_EQ(GetRamdiskName(entry_none), \"none_ramdisk\");\n\n    auto entry_platform = reinterpret_cast<const vendor_ramdisk_table_entry_v4*>(\n            &new_content[o + p + q + hdr->vendor_ramdisk_table_entry_size]);\n    EXPECT_EQ(entry_platform->ramdisk_offset, expect_none_size);\n    EXPECT_EQ(entry_platform->ramdisk_size, expect_platform_size);\n    EXPECT_EQ(entry_platform->ramdisk_type, VENDOR_RAMDISK_TYPE_PLATFORM);\n    EXPECT_EQ(GetRamdiskName(entry_platform), \"platform_ramdisk\");\n\n    EXPECT_THAT(new_content.substr(o + p + q + r, s), IsPadded(env->bootconfig_content));\n\n    EXPECT_THAT(new_content, HasSameAvbFooter(*old_content));\n}\nINSTANTIATE_TEST_SUITE_P(RepackVendorBootImgTest, RepackVendorBootImgTestV4,\n                         ::testing::Values(VENDOR_RAMDISK_TYPE_NONE, VENDOR_RAMDISK_TYPE_PLATFORM),\n                         [](const auto& info) {\n                             return info.param == VENDOR_RAMDISK_TYPE_NONE ? \"none\" : \"platform\";\n                         });\n\n}  // namespace\n\nint main(int argc, char* argv[]) {\n    ::testing::InitGoogleTest(&argc, argv);\n    env = static_cast<RepackVendorBootImgTestEnv*>(\n            testing::AddGlobalTestEnvironment(new RepackVendorBootImgTestEnv));\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "fs_mgr/Android.bp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\n        \"Android-Apache-2.0\",\n        \"system_core_fs_mgr_license\",\n    ],\n}\n\n// Added automatically by a large-scale-change that took the approach of\n// 'apply every license found to every target'. While this makes sure we respect\n// every license restriction, it may not be entirely correct.\n//\n// e.g. GPL in an MIT project might only apply to the contrib/ directory.\n//\n// Please consider splitting the single license below into multiple licenses,\n// taking care not to lose any license_kind information, and overriding the\n// default license using the 'licenses: [...]' property on targets as needed.\n//\n// For unused files, consider creating a 'fileGroup' with \"//visibility:private\"\n// to attach the license to, and including a comment whether the files may be\n// used in the current project.\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_fs_mgr_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-MIT\",\n    ],\n    license_text: [\"NOTICE\"],\n}\n\ncc_defaults {\n    name: \"fs_mgr_defaults\",\n    sanitize: {\n        misc_undefined: [\"integer\"],\n    },\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\ncc_defaults {\n    name: \"libfs_mgr_defaults\",\n    defaults: [\"fs_mgr_defaults\"],\n    export_include_dirs: [\"include\"],\n    local_include_dirs: [\"include/\"],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n    ],\n    srcs: [\n        \"blockdev.cpp\",\n        \"file_wait.cpp\",\n        \"fs_mgr.cpp\",\n        \"fs_mgr_format.cpp\",\n        \"fs_mgr_dm_linear.cpp\",\n        \"fs_mgr_roots.cpp\",\n        \"fs_mgr_overlayfs_control.cpp\",\n        \"fs_mgr_overlayfs_mount.cpp\",\n        \"fs_mgr_vendor_overlay.cpp\",\n        \":libfiemap_srcs\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcrypto\",\n        \"libcrypto_utils\",\n        \"libcutils\",\n        \"libext4_utils\",\n        \"libfec\",\n        \"liblog\",\n        \"liblp\",\n        \"libselinux\",\n    ],\n    static_libs: [\n        \"libavb\",\n        \"libfs_avb\",\n        \"libgsi\",\n    ],\n    export_static_lib_headers: [\n        \"libfs_avb\",\n        \"libfstab\",\n        \"libdm\",\n    ],\n    export_shared_lib_headers: [\n        \"liblp\",\n    ],\n    whole_static_libs: [\n        \"liblogwrap\",\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libfscrypt\",\n        \"libfstab\",\n    ],\n    cppflags: [\n        \"-DALLOW_ADBD_DISABLE_VERITY=0\",\n    ],\n    product_variables: {\n        debuggable: {\n            cppflags: [\n                \"-UALLOW_ADBD_DISABLE_VERITY\",\n                \"-DALLOW_ADBD_DISABLE_VERITY=1\",\n            ],\n        },\n    },\n    header_libs: [\n        \"libfiemap_headers\",\n        \"libstorage_literals_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libfiemap_headers\",\n    ],\n    target: {\n        platform: {\n            required: [\n                \"e2freefrag\",\n                \"e2fsdroid\",\n            ],\n        },\n        recovery: {\n            required: [\n                \"e2fsdroid.recovery\",\n            ],\n        },\n    },\n}\n\n// Two variants of libfs_mgr are provided: libfs_mgr and libfs_mgr_binder.\n// Use libfs_mgr in recovery, first-stage-init, or when libfiemap or overlayfs\n// is not used.\n//\n// Use libfs_mgr_binder when not in recovery/first-stage init, or when overlayfs\n// or libfiemap is needed. In this case, libfiemap will proxy over binder to\n// gsid.\ncc_library {\n    // Do not ever allow this library to be vendor_available as a shared library.\n    // It does not have a stable interface.\n    name: \"libfs_mgr\",\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    defaults: [\n        \"libfs_mgr_defaults\",\n    ],\n    srcs: [\n        \":libfiemap_passthrough_srcs\",\n    ],\n}\n\ncc_library {\n    // Do not ever allow this library to be vendor_available as a shared library.\n    // It does not have a stable interface.\n    name: \"libfs_mgr_binder\",\n    defaults: [\n        \"libfs_mgr_defaults\",\n        \"libfiemap_binder_defaults\",\n    ],\n}\n\ncc_library_static {\n    name: \"libfs_mgr_file_wait\",\n    defaults: [\"fs_mgr_defaults\"],\n    export_include_dirs: [\"include\"],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n    ],\n    srcs: [\n        \"file_wait.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    host_supported: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n}\n\ncc_binary {\n    name: \"remount\",\n    defaults: [\"fs_mgr_defaults\"],\n    static_libs: [\n        \"libavb_user\",\n        \"libgsid\",\n        \"libvold_binder\",\n    ],\n    shared_libs: [\n        \"libbootloader_message\",\n        \"libbase\",\n        \"libbinder\",\n        \"libcutils\",\n        \"libcrypto\",\n        \"libext4_utils\",\n        \"libfs_mgr_binder\",\n        \"liblog\",\n        \"liblp\",\n        \"libselinux\",\n        \"libutils\",\n    ],\n    header_libs: [\n        \"libcutils_headers\",\n    ],\n    srcs: [\n        \"fs_mgr_remount.cpp\",\n    ],\n    cppflags: [\n        \"-DALLOW_ADBD_DISABLE_VERITY=0\",\n    ],\n    product_variables: {\n        debuggable: {\n            cppflags: [\n                \"-UALLOW_ADBD_DISABLE_VERITY\",\n                \"-DALLOW_ADBD_DISABLE_VERITY=1\",\n            ],\n            init_rc: [\n                \"clean_scratch_files.rc\",\n            ],\n        },\n    },\n    symlinks: [\n        \"clean_scratch_files\",\n        \"disable-verity\",\n        \"enable-verity\",\n        \"set-verity-state\",\n    ],\n}\n"
  },
  {
    "path": "fs_mgr/NOTICE",
    "content": "Copyright (C) 2016 The Android Open Source Project\n\nPermission is hereby granted, free of charge, to any person\nobtaining a copy of this software and associated documentation\nfiles (the \"Software\"), to deal in the Software without\nrestriction, including without limitation the rights to use, copy,\nmodify, merge, publish, distribute, sublicense, and/or sell copies\nof the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\nBE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\nACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "fs_mgr/OWNERS",
    "content": "# Bug component: 325626\nbowgotsai@google.com\ndvander@google.com\nelsk@google.com\nyochiang@google.com\n"
  },
  {
    "path": "fs_mgr/README.overlayfs.md",
    "content": "Android OverlayFS Integration with adb Remount\n==============================================\n\nIntroduction\n------------\n\nUsers working with userdebug or eng builds expect to be able to remount the\nsystem partition as read-write and then add or modify any number of files\nwithout reflashing the system image, which is efficient for a development cycle.\n\nLimited memory systems use read-only types of file systems or dynamic\nAndroid partitions (DAPs). These file systems land system partition images\nright-sized, and have been deduped at the block level to compress the content.\nThis means that a remount either isn’t possible, or isn't useful because of\nspace limitations or support logistics.\n\nOverlayFS resolves these debug scenarios with the _adb disable-verity_ and\n_adb remount_ commands, which set up backing storage for a writable file\nsystem as an upper reference, and mount the lower reference on top.\n\nPerforming a remount\n--------------------\n\nUse the following sequence to perform the remount.\n\n    $ adb root\n    $ adb disable-verity\n    $ adb reboot\n    $ adb wait-for-device\n    $ adb root\n    $ adb remount\n\nThen enter one of the following sequences:\n\n    $ adb shell stop\n    $ adb sync\n    $ adb shell start\n    $ adb reboot\n\n*or*\n\n    $ adb push <source> <destination>\n    $ adb reboot\n\nNote that you can replace these two lines in the above sequence:\n\n    $ adb disable-verity\n    $ adb reboot\n\nwith this line:\n\n    $ adb remount -R\n\n**Note:** _adb remount -R_ won’t reboot if the device is already in the adb remount state.\n\nNone of this changes if OverlayFS needs to be engaged.\nThe decisions whether to use traditional direct file-system remount,\nor one wrapped by OverlayFS is automatically determined based on\na probe of the file-system types and space remaining.\n\n### Backing Storage\n\nWhen *OverlayFS* logic is feasible, it uses either the\n**/cache/overlay/** directory for non-A/B devices, or the\n**/mnt/scratch/overlay** directory for A/B devices that have\naccess to *LRAP*.\nIt is also possible for an A/B device to use the system_<other> partition\nfor backing storage. eg: if booting off system_a+vendor_a, use system_b.\nThe backing store is used as soon as possible in the boot\nprocess and can occur at first stage init, or when the\n*mount_all* commands are run in init RC scripts.\n\nBy attaching OverlayFS early, SEpolicy or init can be pushed and used after the exec phases of each stage.\n\nCaveats\n-------\n\n- Backing storage requires more space than immutable storage, as backing is\n  done file by file. Be mindful of wasted space. For example, defining\n  **BOARD_IMAGE_PARTITION_RESERVED_SIZE** has a negative impact on the\n  right-sizing of images and requires more free dynamic partition space.\n- The kernel requires **CONFIG_OVERLAY_FS=y**. If the kernel version is higher\n  than 4.4, it requires source to be in line with android-common kernels. \n  The patch series is available on the upstream mailing list and the latest as\n  of Sep 5 2019 is https://www.spinics.net/lists/linux-mtd/msg08331.html\n  This patch adds an override_creds _mount_ option to OverlayFS that\n  permits legacy behavior for systems that do not have overlapping\n  sepolicy rules, principals of least privilege, which is how Android behaves.\n  For 4.19 and higher a rework of the xattr handling to deal with recursion\n  is required. https://patchwork.kernel.org/patch/11117145/ is a start of that\n  adjustment.\n- _adb enable-verity_ frees up OverlayFS and reverts the device to the state\n  prior to content updates. The update engine performs a full OTA.\n- _adb remount_ overrides are incompatible with OTA resources, so the update\n  engine may not run if fs_mgr_overlayfs_is_setup() returns true.\n- If a dynamic partition runs out of space, making a logical partition larger\n  may fail because of the scratch partition. If this happens, clear the scratch\n  storage by running either either _fastboot flashall_ or _adb enable-verity_.\n  Then reinstate the overrides and continue.\n- For implementation simplicity on retrofit dynamic partition devices,\n  take the whole alternate super (eg: if \"*a*\" slot, then the whole of\n  \"*system_b*\").\n  Since landing a filesystem on the alternate super physical device\n  without differentiating if it is setup to support logical or physical,\n  the alternate slot metadata and previous content will be lost.\n- There are other subtle caveats requiring complex logic to solve.\n  Have evaluated them as too complex or not worth the trouble, please\n  File a bug if a use case needs to be covered.\n  - The backing storage is treated fragile, if anything else has\n    issue with the space taken, the backing storage will be cleared\n    out and we reserve the right to not inform, if the layering\n    does not prevent any messaging.\n  - Space remaining threshold is hard coded.  If 1% or more space\n    still remains, OverlayFS will not be used, yet that amount of\n    space remaining is problematic.\n  - Flashing a partition via bootloader fastboot, as opposed to user\n    space fastbootd, is not detected, thus a partition may have\n    override content remaining.  adb enable-verity to wipe.\n  - Space is limited, there is near unlimited space on userdata,\n    we have made an architectural decision to not utilize\n    /data/overlay/ at this time.  Acquiring space to use for\n    backing remains an ongoing battle.\n  - First stage init, or ramdisk, can not be overriden.\n  - Backing storage will be discarded or ignored on errors, leading\n    to confusion.  When debugging using **adb remount** it is\n    currently advised to confirm update is present after a reboot\n    to develop confidence.\n- File bugs or submit fixes for review.\n"
  },
  {
    "path": "fs_mgr/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"CtsFsMgrTestCases\"\n    },\n    {\n      \"name\": \"libdm_test\"\n    },\n    {\n      \"name\": \"liblp_test\"\n    },\n    {\n      \"name\": \"fiemap_image_test\"\n    },\n    {\n      \"name\": \"fiemap_writer_test\"\n    },\n    {\n      \"name\": \"fs_mgr_vendor_overlay_test\",\n      \"keywords\": [\"internal\"]\n    },\n    {\n      \"name\": \"vts_libsnapshot_test\"\n    },\n    {\n      \"name\": \"vab_legacy_tests\"\n    },\n    {\n      \"name\": \"cow_api_test\"\n    }\n  ],\n  \"postsubmit\": [\n    {\n      \"name\": \"snapuserd_test\"\n    }\n  ],\n  \"kernel-presubmit\": [\n    {\n      \"name\": \"libdm_test\"\n    },\n    {\n      \"name\": \"liblp_test\"\n    },\n    {\n      \"name\": \"vab_legacy_tests\"\n    },\n    {\n      \"name\": \"snapuserd_test\"\n    }\n  ]\n}\n"
  },
  {
    "path": "fs_mgr/blockdev.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n\n#include <dirent.h>\n#include <libdm/dm.h>\n#include <sys/stat.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include \"blockdev.h\"\n\nusing android::base::Basename;\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\nusing android::base::StartsWith;\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\nusing android::dm::DeviceMapper;\n\n// Return the parent device of a partition. Converts e.g. \"sda26\" into \"sda\".\nstatic std::string PartitionParent(const std::string& blockdev) {\n    if (blockdev.find('/') != std::string::npos) {\n        LOG(ERROR) << __func__ << \": invalid argument \" << blockdev;\n        return blockdev;\n    }\n    auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir(\"/sys/class/block\"), closedir};\n    if (!dir) {\n        return blockdev;\n    }\n    for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {\n        if (ent->d_name[0] == '.') {\n            continue;\n        }\n        std::string path = StringPrintf(\"/sys/class/block/%s/%s\", ent->d_name, blockdev.c_str());\n        struct stat statbuf;\n        if (stat(path.c_str(), &statbuf) >= 0) {\n            return ent->d_name;\n        }\n    }\n    return blockdev;\n}\n\n// Convert a major:minor pair into a block device name.\nstatic std::string BlockdevName(dev_t dev) {\n    auto dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir(\"/dev/block\"), closedir};\n    if (!dir) {\n        return {};\n    }\n    for (struct dirent* ent = readdir(dir.get()); ent; ent = readdir(dir.get())) {\n        if (ent->d_name[0] == '.') {\n            continue;\n        }\n        const std::string path = std::string(\"/dev/block/\") + ent->d_name;\n        struct stat statbuf;\n        if (stat(path.c_str(), &statbuf) >= 0 && dev == statbuf.st_rdev) {\n            return ent->d_name;\n        }\n    }\n    return {};\n}\n\n// Trim whitespace from the end of a string.\nstatic void rtrim(std::string& s) {\n    s.erase(s.find_last_not_of('\\n') + 1, s.length());\n}\n\n// For file `file_path`, retrieve the block device backing the filesystem on\n// which the file exists and return the queue depth of the block device.\nstatic Result<uint32_t> BlockDeviceQueueDepth(const std::string& file_path) {\n    struct stat statbuf;\n    int res = stat(file_path.c_str(), &statbuf);\n    if (res < 0) {\n        return ErrnoError() << \"stat(\" << file_path << \")\";\n    }\n    std::string blockdev = \"/dev/block/\" + BlockdevName(statbuf.st_dev);\n    LOG(DEBUG) << __func__ << \": \" << file_path << \" -> \" << blockdev;\n    if (blockdev.empty()) {\n        return Errorf(\"Failed to convert {}:{} (path {})\", major(statbuf.st_dev),\n                      minor(statbuf.st_dev), file_path.c_str());\n    }\n    auto& dm = DeviceMapper::Instance();\n    for (;;) {\n        std::optional<std::string> child = dm.GetParentBlockDeviceByPath(blockdev);\n        if (!child) {\n            break;\n        }\n        LOG(DEBUG) << __func__ << \": \" << blockdev << \" -> \" << *child;\n        blockdev = *child;\n    }\n    std::optional<std::string> maybe_blockdev = android::dm::ExtractBlockDeviceName(blockdev);\n    if (!maybe_blockdev) {\n        return Errorf(\"Failed to remove /dev/block/ prefix from {}\", blockdev);\n    }\n    blockdev = PartitionParent(*maybe_blockdev);\n    LOG(DEBUG) << __func__ << \": \"\n               << \"Partition parent: \" << blockdev;\n    const std::string nr_tags_path =\n            StringPrintf(\"/sys/class/block/%s/mq/0/nr_tags\", blockdev.c_str());\n    std::string nr_tags;\n    if (!android::base::ReadFileToString(nr_tags_path, &nr_tags)) {\n        return Errorf(\"Failed to read {}\", nr_tags_path);\n    }\n    rtrim(nr_tags);\n    LOG(DEBUG) << __func__ << \": \" << file_path << \" is backed by /dev/\" << blockdev\n               << \" and that block device supports queue depth \" << nr_tags;\n    return strtol(nr_tags.c_str(), NULL, 0);\n}\n\n// Set 'nr_requests' of `loop_device_path` to the queue depth of the block\n// device backing `file_path`.\nResult<void> ConfigureQueueDepth(const std::string& loop_device_path,\n                                 const std::string& file_path) {\n    if (!StartsWith(loop_device_path, \"/dev/\")) {\n        return Error() << \"Invalid argument \" << loop_device_path;\n    }\n\n    const std::string loop_device_name = Basename(loop_device_path);\n\n    const auto qd = BlockDeviceQueueDepth(file_path);\n    if (!qd.ok()) {\n        return qd.error();\n    }\n    const std::string nr_requests = StringPrintf(\"%u\", *qd);\n    const std::string sysfs_path =\n            StringPrintf(\"/sys/class/block/%s/queue/nr_requests\", loop_device_name.c_str());\n    unique_fd sysfs_fd(open(sysfs_path.c_str(), O_RDWR | O_CLOEXEC));\n    if (sysfs_fd == -1) {\n        return ErrnoError() << \"Failed to open \" << sysfs_path;\n    }\n\n    const int res = write(sysfs_fd.get(), nr_requests.data(), nr_requests.length());\n    if (res < 0) {\n        return ErrnoError() << \"Failed to write to \" << sysfs_path;\n    }\n    return {};\n}\n"
  },
  {
    "path": "fs_mgr/blockdev.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/result.h>\n#include <string>\n\nandroid::base::Result<void> ConfigureQueueDepth(const std::string& loop_device_path,\n                                                const std::string& file_path);\n"
  },
  {
    "path": "fs_mgr/clean_scratch_files.rc",
    "content": "on post-fs-data && property:ro.debuggable=1 && property:ro.boot.dynamic_partitions=true\n    exec_background - root root -- /system/bin/clean_scratch_files\n"
  },
  {
    "path": "fs_mgr/file_wait.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <fs_mgr/file_wait.h>\n\n#include <limits.h>\n#if defined(__linux__)\n#include <poll.h>\n#include <sys/inotify.h>\n#endif\n#if defined(WIN32)\n#include <io.h>\n#else\n#include <unistd.h>\n#endif\n\n#include <functional>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nusing namespace std::literals;\nusing android::base::unique_fd;\n\nbool PollForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {\n    auto start_time = std::chrono::steady_clock::now();\n\n    while (true) {\n        if (!access(path.c_str(), F_OK) || errno != ENOENT) return true;\n\n        std::this_thread::sleep_for(50ms);\n\n        auto now = std::chrono::steady_clock::now();\n        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);\n        if (time_elapsed > relative_timeout) return false;\n    }\n}\n\nbool PollForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {\n    auto start_time = std::chrono::steady_clock::now();\n\n    while (true) {\n        if (access(path.c_str(), F_OK) && errno == ENOENT) return true;\n\n        std::this_thread::sleep_for(50ms);\n\n        auto now = std::chrono::steady_clock::now();\n        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);\n        if (time_elapsed > relative_timeout) return false;\n    }\n}\n\n#if defined(__linux__)\nclass OneShotInotify {\n  public:\n    OneShotInotify(const std::string& path, uint32_t mask,\n                   const std::chrono::milliseconds relative_timeout);\n\n    bool Wait();\n\n  private:\n    bool CheckCompleted();\n    int64_t RemainingMs() const;\n    bool ConsumeEvents();\n\n    enum class Result { Success, Timeout, Error };\n    Result WaitImpl();\n\n    unique_fd inotify_fd_;\n    std::string path_;\n    uint32_t mask_;\n    std::chrono::time_point<std::chrono::steady_clock> start_time_;\n    std::chrono::milliseconds relative_timeout_;\n    bool finished_;\n};\n\nOneShotInotify::OneShotInotify(const std::string& path, uint32_t mask,\n                               const std::chrono::milliseconds relative_timeout)\n    : path_(path),\n      mask_(mask),\n      start_time_(std::chrono::steady_clock::now()),\n      relative_timeout_(relative_timeout),\n      finished_(false) {\n    // If the condition is already met, don't bother creating an inotify.\n    if (CheckCompleted()) return;\n\n    unique_fd inotify_fd(inotify_init1(IN_CLOEXEC | IN_NONBLOCK));\n    if (inotify_fd < 0) {\n        PLOG(ERROR) << \"inotify_init1 failed\";\n        return;\n    }\n\n    std::string watch_path;\n    if (mask == IN_CREATE) {\n        watch_path = android::base::Dirname(path);\n    } else {\n        watch_path = path;\n    }\n    if (inotify_add_watch(inotify_fd, watch_path.c_str(), mask) < 0) {\n        PLOG(ERROR) << \"inotify_add_watch failed\";\n        return;\n    }\n\n    // It's possible the condition was met before the add_watch. Check for\n    // this and abort early if so.\n    if (CheckCompleted()) return;\n\n    inotify_fd_ = std::move(inotify_fd);\n}\n\nbool OneShotInotify::Wait() {\n    Result result = WaitImpl();\n    if (result == Result::Success) return true;\n    if (result == Result::Timeout) return false;\n\n    // Some kind of error with inotify occurred, so fallback to a poll.\n    std::chrono::milliseconds timeout(RemainingMs());\n    if (mask_ == IN_CREATE) {\n        return PollForFile(path_, timeout);\n    } else if (mask_ == IN_DELETE_SELF) {\n        return PollForFileDeleted(path_, timeout);\n    } else {\n        LOG(ERROR) << \"Unknown inotify mask: \" << mask_;\n        return false;\n    }\n}\n\nOneShotInotify::Result OneShotInotify::WaitImpl() {\n    // If the operation completed super early, we'll never have created an\n    // inotify instance.\n    if (finished_) return Result::Success;\n    if (inotify_fd_ < 0) return Result::Error;\n\n    while (true) {\n        auto remaining_ms = RemainingMs();\n        if (remaining_ms <= 0) return Result::Timeout;\n\n        struct pollfd event = {\n                .fd = inotify_fd_,\n                .events = POLLIN,\n                .revents = 0,\n        };\n        int rv = poll(&event, 1, static_cast<int>(remaining_ms));\n        if (rv <= 0) {\n            if (rv == 0 || errno == EINTR) {\n                continue;\n            }\n            PLOG(ERROR) << \"poll for inotify failed\";\n            return Result::Error;\n        }\n        if (event.revents & POLLERR) {\n            LOG(ERROR) << \"error reading inotify for \" << path_;\n            return Result::Error;\n        }\n\n        // Note that we don't bother checking what kind of event it is, since\n        // it's cheap enough to just see if the initial condition is satisified.\n        // If it's not, we consume all the events available and continue.\n        if (CheckCompleted()) return Result::Success;\n        if (!ConsumeEvents()) return Result::Error;\n    }\n}\n\nbool OneShotInotify::CheckCompleted() {\n    if (mask_ == IN_CREATE) {\n        finished_ = !access(path_.c_str(), F_OK) || errno != ENOENT;\n    } else if (mask_ == IN_DELETE_SELF) {\n        finished_ = access(path_.c_str(), F_OK) && errno == ENOENT;\n    } else {\n        LOG(ERROR) << \"Unexpected mask: \" << mask_;\n    }\n    return finished_;\n}\n\nbool OneShotInotify::ConsumeEvents() {\n    // According to the manpage, this is enough to read at least one event.\n    static constexpr size_t kBufferSize = sizeof(struct inotify_event) + NAME_MAX + 1;\n    char buffer[kBufferSize];\n\n    do {\n        ssize_t rv = TEMP_FAILURE_RETRY(read(inotify_fd_, buffer, sizeof(buffer)));\n        if (rv <= 0) {\n            if (rv == 0 || errno == EAGAIN) {\n                return true;\n            }\n            PLOG(ERROR) << \"read inotify failed\";\n            return false;\n        }\n    } while (true);\n}\n\nint64_t OneShotInotify::RemainingMs() const {\n    if (relative_timeout_ == std::chrono::milliseconds::max()) {\n        return std::chrono::milliseconds::max().count();\n    }\n    auto remaining = (std::chrono::steady_clock::now() - start_time_);\n    auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(remaining);\n    return (relative_timeout_ - elapsed).count();\n}\n#endif\n\nbool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout) {\n#if defined(__linux__)\n    OneShotInotify inotify(path, IN_CREATE, relative_timeout);\n    return inotify.Wait();\n#else\n    return PollForFile(path, relative_timeout);\n#endif\n}\n\n// Wait at most |relative_timeout| milliseconds for |path| to stop existing.\nbool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout) {\n#if defined(__linux__)\n    OneShotInotify inotify(path, IN_DELETE_SELF, relative_timeout);\n    return inotify.Wait();\n#else\n    return PollForFileDeleted(path, relative_timeout);\n#endif\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fs_mgr.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <libgen.h>\n#include <selinux/selinux.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <sys/swap.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n#include <sys/wait.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <array>\n#include <chrono>\n#include <map>\n#include <memory>\n#include <string>\n#include <string_view>\n#include <thread>\n#include <utility>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <cutils/android_filesystem_config.h>\n#include <cutils/android_reboot.h>\n#include <cutils/partition_utils.h>\n#include <cutils/properties.h>\n#include <ext4_utils/ext4.h>\n#include <ext4_utils/ext4_sb.h>\n#include <ext4_utils/ext4_utils.h>\n#include <ext4_utils/wipe.h>\n#include <fs_avb/fs_avb.h>\n#include <fs_mgr/file_wait.h>\n#include <fs_mgr_overlayfs.h>\n#include <fscrypt/fscrypt.h>\n#include <fstab/fstab.h>\n#include <libdm/dm.h>\n#include <libdm/loop_control.h>\n#include <liblp/metadata_format.h>\n#include <linux/fs.h>\n#include <linux/loop.h>\n#include <linux/magic.h>\n#include <log/log_properties.h>\n#include <logwrap/logwrap.h>\n\n#include \"blockdev.h\"\n#include \"fs_mgr_priv.h\"\n\n#define E2FSCK_BIN      \"/system/bin/e2fsck\"\n#define F2FS_FSCK_BIN   \"/system/bin/fsck.f2fs\"\n#define MKSWAP_BIN      \"/system/bin/mkswap\"\n#define TUNE2FS_BIN     \"/system/bin/tune2fs\"\n#define RESIZE2FS_BIN   \"/system/bin/resize2fs\"\n\n#define FSCK_LOG_FILE   \"/dev/fscklogs/log\"\n\n#define ZRAM_CONF_DEV   \"/sys/block/zram0/disksize\"\n#define ZRAM_CONF_MCS   \"/sys/block/zram0/max_comp_streams\"\n#define ZRAM_BACK_DEV   \"/sys/block/zram0/backing_dev\"\n\n#define SYSFS_EXT4_VERITY \"/sys/fs/ext4/features/verity\"\n#define SYSFS_EXT4_CASEFOLD \"/sys/fs/ext4/features/casefold\"\n\n#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))\n\nusing android::base::Basename;\nusing android::base::GetBoolProperty;\nusing android::base::GetUintProperty;\nusing android::base::Realpath;\nusing android::base::SetProperty;\nusing android::base::StartsWith;\nusing android::base::StringPrintf;\nusing android::base::Timer;\nusing android::base::unique_fd;\nusing android::dm::DeviceMapper;\nusing android::dm::DmDeviceState;\nusing android::dm::DmTargetLinear;\nusing android::dm::LoopControl;\n\n// Realistically, this file should be part of the android::fs_mgr namespace;\nusing namespace android::fs_mgr;\n\nusing namespace std::literals;\n\n// record fs stat\nenum FsStatFlags {\n    FS_STAT_IS_EXT4 = 0x0001,\n    FS_STAT_NEW_IMAGE_VERSION = 0x0002,\n    FS_STAT_E2FSCK_F_ALWAYS = 0x0004,\n    FS_STAT_UNCLEAN_SHUTDOWN = 0x0008,\n    FS_STAT_QUOTA_ENABLED = 0x0010,\n    FS_STAT_RO_MOUNT_FAILED = 0x0040,\n    FS_STAT_RO_UNMOUNT_FAILED = 0x0080,\n    FS_STAT_FULL_MOUNT_FAILED = 0x0100,\n    FS_STAT_FSCK_FAILED = 0x0200,\n    FS_STAT_FSCK_FS_FIXED = 0x0400,\n    FS_STAT_INVALID_MAGIC = 0x0800,\n    FS_STAT_TOGGLE_QUOTAS_FAILED = 0x10000,\n    FS_STAT_SET_RESERVED_BLOCKS_FAILED = 0x20000,\n    FS_STAT_ENABLE_ENCRYPTION_FAILED = 0x40000,\n    FS_STAT_ENABLE_VERITY_FAILED = 0x80000,\n    FS_STAT_ENABLE_CASEFOLD_FAILED = 0x100000,\n    FS_STAT_ENABLE_METADATA_CSUM_FAILED = 0x200000,\n};\n\nstatic void log_fs_stat(const std::string& blk_device, int fs_stat) {\n    std::string msg =\n            android::base::StringPrintf(\"\\nfs_stat,%s,0x%x\\n\", blk_device.c_str(), fs_stat);\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(\n            open(FSCK_LOG_FILE, O_WRONLY | O_CLOEXEC | O_APPEND | O_CREAT, 0664)));\n    if (fd == -1 || !android::base::WriteStringToFd(msg, fd)) {\n        LWARNING << __FUNCTION__ << \"() cannot log \" << msg;\n    }\n}\n\nstatic bool is_extfs(const std::string& fs_type) {\n    return fs_type == \"ext4\" || fs_type == \"ext3\" || fs_type == \"ext2\";\n}\n\nstatic bool is_f2fs(const std::string& fs_type) {\n    return fs_type == \"f2fs\";\n}\n\nstatic std::string realpath(const std::string& blk_device) {\n    std::string real_path;\n    if (!Realpath(blk_device, &real_path)) {\n        real_path = blk_device;\n    }\n    return real_path;\n}\n\nstatic bool should_force_check(int fs_stat) {\n    return fs_stat &\n           (FS_STAT_E2FSCK_F_ALWAYS | FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED |\n            FS_STAT_RO_MOUNT_FAILED | FS_STAT_RO_UNMOUNT_FAILED | FS_STAT_FULL_MOUNT_FAILED |\n            FS_STAT_FSCK_FAILED | FS_STAT_TOGGLE_QUOTAS_FAILED |\n            FS_STAT_SET_RESERVED_BLOCKS_FAILED | FS_STAT_ENABLE_ENCRYPTION_FAILED);\n}\n\nstatic bool umount_retry(const std::string& mount_point) {\n    int retry_count = 5;\n    bool umounted = false;\n\n    while (retry_count-- > 0) {\n        umounted = umount(mount_point.c_str()) == 0;\n        if (umounted) {\n            LINFO << __FUNCTION__ << \"(): unmount(\" << mount_point << \") succeeded\";\n            break;\n        }\n        PERROR << __FUNCTION__ << \"(): umount(\" << mount_point << \") failed\";\n        if (retry_count) sleep(1);\n    }\n    return umounted;\n}\n\nstatic void check_fs(const std::string& blk_device, const std::string& fs_type,\n                     const std::string& target, int* fs_stat) {\n    int status;\n    int ret;\n    long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;\n    auto tmpmnt_opts = \"errors=remount-ro\"s;\n    const char* e2fsck_argv[] = {E2FSCK_BIN, \"-y\", blk_device.c_str()};\n    const char* e2fsck_forced_argv[] = {E2FSCK_BIN, \"-f\", \"-y\", blk_device.c_str()};\n\n    if (*fs_stat & FS_STAT_INVALID_MAGIC) {  // will fail, so do not try\n        return;\n    }\n\n    Timer t;\n    /* Check for the types of filesystems we know how to check */\n    if (is_extfs(fs_type)) {\n        /*\n         * First try to mount and unmount the filesystem.  We do this because\n         * the kernel is more efficient than e2fsck in running the journal and\n         * processing orphaned inodes, and on at least one device with a\n         * performance issue in the emmc firmware, it can take e2fsck 2.5 minutes\n         * to do what the kernel does in about a second.\n         *\n         * After mounting and unmounting the filesystem, run e2fsck, and if an\n         * error is recorded in the filesystem superblock, e2fsck will do a full\n         * check.  Otherwise, it does nothing.  If the kernel cannot mount the\n         * filesytsem due to an error, e2fsck is still run to do a full check\n         * fix the filesystem.\n         */\n        if (!(*fs_stat & FS_STAT_FULL_MOUNT_FAILED)) {  // already tried if full mount failed\n            errno = 0;\n            ret = mount(blk_device.c_str(), target.c_str(), fs_type.c_str(), tmpmnt_flags,\n                        tmpmnt_opts.c_str());\n            PINFO << __FUNCTION__ << \"(): mount(\" << blk_device << \",\" << target << \",\" << fs_type\n                  << \")=\" << ret;\n            if (ret) {\n                *fs_stat |= FS_STAT_RO_MOUNT_FAILED;\n            } else if (!umount_retry(target)) {\n                // boot may fail but continue and leave it to later stage for now.\n                PERROR << __FUNCTION__ << \"(): umount(\" << target << \") timed out\";\n                *fs_stat |= FS_STAT_RO_UNMOUNT_FAILED;\n            }\n        }\n\n        /*\n         * Some system images do not have e2fsck for licensing reasons\n         * (e.g. recent SDK system images). Detect these and skip the check.\n         */\n        if (access(E2FSCK_BIN, X_OK)) {\n            LINFO << \"Not running \" << E2FSCK_BIN << \" on \" << realpath(blk_device)\n                  << \" (executable not in system image)\";\n        } else {\n            LINFO << \"Running \" << E2FSCK_BIN << \" on \" << realpath(blk_device);\n            if (should_force_check(*fs_stat)) {\n                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_forced_argv), e2fsck_forced_argv,\n                                          &status, false, LOG_KLOG | LOG_FILE, false,\n                                          FSCK_LOG_FILE);\n            } else {\n                ret = logwrap_fork_execvp(ARRAY_SIZE(e2fsck_argv), e2fsck_argv, &status, false,\n                                          LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);\n            }\n\n            if (ret < 0) {\n                /* No need to check for error in fork, we can't really handle it now */\n                LERROR << \"Failed trying to run \" << E2FSCK_BIN;\n                *fs_stat |= FS_STAT_FSCK_FAILED;\n            } else if (status != 0) {\n                LINFO << \"e2fsck returned status 0x\" << std::hex << status;\n                *fs_stat |= FS_STAT_FSCK_FS_FIXED;\n            }\n        }\n    } else if (is_f2fs(fs_type)) {\n        const char* f2fs_fsck_argv[] = {F2FS_FSCK_BIN,     \"-a\", \"-c\", \"10000\", \"--debug-cache\",\n                                        blk_device.c_str()};\n        const char* f2fs_fsck_forced_argv[] = {\n                F2FS_FSCK_BIN, \"-f\", \"-c\", \"10000\", \"--debug-cache\", blk_device.c_str()};\n\n        if (access(F2FS_FSCK_BIN, X_OK)) {\n            LINFO << \"Not running \" << F2FS_FSCK_BIN << \" on \" << realpath(blk_device)\n                  << \" (executable not in system image)\";\n        } else {\n            if (should_force_check(*fs_stat)) {\n                LINFO << \"Running \" << F2FS_FSCK_BIN << \" -f -c 10000 --debug-cache \"\n                      << realpath(blk_device);\n                ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_forced_argv), f2fs_fsck_forced_argv,\n                                          &status, false, LOG_KLOG | LOG_FILE, false,\n                                          FSCK_LOG_FILE);\n            } else {\n                LINFO << \"Running \" << F2FS_FSCK_BIN << \" -a -c 10000 --debug-cache \"\n                      << realpath(blk_device);\n                ret = logwrap_fork_execvp(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv, &status,\n                                          false, LOG_KLOG | LOG_FILE, false, FSCK_LOG_FILE);\n            }\n            if (ret < 0) {\n                /* No need to check for error in fork, we can't really handle it now */\n                LERROR << \"Failed trying to run \" << F2FS_FSCK_BIN;\n                *fs_stat |= FS_STAT_FSCK_FAILED;\n            } else if (status != 0) {\n                LINFO << F2FS_FSCK_BIN << \" returned status 0x\" << std::hex << status;\n                *fs_stat |= FS_STAT_FSCK_FS_FIXED;\n            }\n        }\n    }\n    android::base::SetProperty(\"ro.boottime.init.fsck.\" + Basename(target),\n                               std::to_string(t.duration().count()));\n    return;\n}\n\nstatic ext4_fsblk_t ext4_blocks_count(const struct ext4_super_block* es) {\n    return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) |\n           le32_to_cpu(es->s_blocks_count_lo);\n}\n\nstatic ext4_fsblk_t ext4_r_blocks_count(const struct ext4_super_block* es) {\n    return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) |\n           le32_to_cpu(es->s_r_blocks_count_lo);\n}\n\nstatic bool is_ext4_superblock_valid(const struct ext4_super_block* es) {\n    if (es->s_magic != EXT4_SUPER_MAGIC) return false;\n    if (es->s_rev_level != EXT4_DYNAMIC_REV && es->s_rev_level != EXT4_GOOD_OLD_REV) return false;\n    if (EXT4_INODES_PER_GROUP(es) == 0) return false;\n    return true;\n}\n\n// Read the primary superblock from an ext4 filesystem.  On failure return\n// false.  If it's not an ext4 filesystem, also set FS_STAT_INVALID_MAGIC.\nstatic bool read_ext4_superblock(const std::string& blk_device, struct ext4_super_block* sb,\n                                 int* fs_stat) {\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));\n\n    if (fd < 0) {\n        PERROR << \"Failed to open '\" << blk_device << \"'\";\n        return false;\n    }\n\n    if (TEMP_FAILURE_RETRY(pread(fd, sb, sizeof(*sb), 1024)) != sizeof(*sb)) {\n        PERROR << \"Can't read '\" << blk_device << \"' superblock\";\n        return false;\n    }\n\n    if (!is_ext4_superblock_valid(sb)) {\n        LINFO << \"Invalid ext4 superblock on '\" << blk_device << \"'\";\n        // not a valid fs, tune2fs, fsck, and mount  will all fail.\n        *fs_stat |= FS_STAT_INVALID_MAGIC;\n        return false;\n    }\n    *fs_stat |= FS_STAT_IS_EXT4;\n    LINFO << \"superblock s_max_mnt_count:\" << sb->s_max_mnt_count << \",\" << blk_device;\n    if (sb->s_max_mnt_count == 0xffff) {  // -1 (int16) in ext2, but uint16 in ext4\n        *fs_stat |= FS_STAT_NEW_IMAGE_VERSION;\n    }\n    return true;\n}\n\n// exported silent version of the above that just answer the question is_ext4\nbool fs_mgr_is_ext4(const std::string& blk_device) {\n    android::base::ErrnoRestorer restore;\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) return false;\n    ext4_super_block sb;\n    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), 1024)) != sizeof(sb)) return false;\n    if (!is_ext4_superblock_valid(&sb)) return false;\n    return true;\n}\n\n// Some system images do not have tune2fs for licensing reasons.\n// Detect these and skip running it.\nstatic bool tune2fs_available(void) {\n    return access(TUNE2FS_BIN, X_OK) == 0;\n}\n\nstatic bool run_command(const char* argv[], int argc) {\n    int ret;\n\n    ret = logwrap_fork_execvp(argc, argv, nullptr, false, LOG_KLOG, false, nullptr);\n    return ret == 0;\n}\n\n// Enable/disable quota support on the filesystem if needed.\nstatic void tune_quota(const std::string& blk_device, const FstabEntry& entry,\n                       const struct ext4_super_block* sb, int* fs_stat) {\n    bool has_quota = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;\n    bool want_quota = entry.fs_mgr_flags.quota;\n    // Enable projid support by default\n    bool want_projid = true;\n    if (has_quota == want_quota) {\n        return;\n    }\n\n    if (!tune2fs_available()) {\n        LERROR << \"Unable to \" << (want_quota ? \"enable\" : \"disable\") << \" quotas on \" << blk_device\n               << \" because \" TUNE2FS_BIN \" is missing\";\n        return;\n    }\n\n    const char* argv[] = {TUNE2FS_BIN, nullptr, nullptr, blk_device.c_str()};\n\n    if (want_quota) {\n        LINFO << \"Enabling quotas on \" << blk_device;\n        argv[1] = \"-Oquota\";\n        // Once usr/grp unneeded, make just prjquota to save overhead\n        if (want_projid)\n            argv[2] = \"-Qusrquota,grpquota,prjquota\";\n        else\n            argv[2] = \"-Qusrquota,grpquota\";\n        *fs_stat |= FS_STAT_QUOTA_ENABLED;\n    } else {\n        LINFO << \"Disabling quotas on \" << blk_device;\n        argv[1] = \"-O^quota\";\n        argv[2] = \"-Q^usrquota,^grpquota,^prjquota\";\n    }\n\n    if (!run_command(argv, ARRAY_SIZE(argv))) {\n        LERROR << \"Failed to run \" TUNE2FS_BIN \" to \" << (want_quota ? \"enable\" : \"disable\")\n               << \" quotas on \" << blk_device;\n        *fs_stat |= FS_STAT_TOGGLE_QUOTAS_FAILED;\n    }\n}\n\n// Set the number of reserved filesystem blocks if needed.\nstatic void tune_reserved_size(const std::string& blk_device, const FstabEntry& entry,\n                               const struct ext4_super_block* sb, int* fs_stat) {\n    if (entry.reserved_size == 0) {\n        return;\n    }\n\n    // The size to reserve is given in the fstab, but we won't reserve more\n    // than 2% of the filesystem.\n    const uint64_t max_reserved_blocks = ext4_blocks_count(sb) * 0.02;\n    uint64_t reserved_blocks = entry.reserved_size / EXT4_BLOCK_SIZE(sb);\n\n    if (reserved_blocks > max_reserved_blocks) {\n        LWARNING << \"Reserved blocks \" << reserved_blocks << \" is too large; \"\n                 << \"capping to \" << max_reserved_blocks;\n        reserved_blocks = max_reserved_blocks;\n    }\n\n    if ((ext4_r_blocks_count(sb) == reserved_blocks) && (sb->s_def_resgid == AID_RESERVED_DISK)) {\n        return;\n    }\n\n    if (!tune2fs_available()) {\n        LERROR << \"Unable to set the number of reserved blocks on \" << blk_device\n               << \" because \" TUNE2FS_BIN \" is missing\";\n        return;\n    }\n\n    LINFO << \"Setting reserved block count on \" << blk_device << \" to \" << reserved_blocks;\n\n    auto reserved_blocks_str = std::to_string(reserved_blocks);\n    auto reserved_gid_str = std::to_string(AID_RESERVED_DISK);\n    const char* argv[] = {\n            TUNE2FS_BIN,       \"-r\", reserved_blocks_str.c_str(), \"-g\", reserved_gid_str.c_str(),\n            blk_device.c_str()};\n    if (!run_command(argv, ARRAY_SIZE(argv))) {\n        LERROR << \"Failed to run \" TUNE2FS_BIN \" to set the number of reserved blocks on \"\n               << blk_device;\n        *fs_stat |= FS_STAT_SET_RESERVED_BLOCKS_FAILED;\n    }\n}\n\n// Enable file-based encryption if needed.\nstatic void tune_encrypt(const std::string& blk_device, const FstabEntry& entry,\n                         const struct ext4_super_block* sb, int* fs_stat) {\n    if (!entry.fs_mgr_flags.file_encryption) {\n        return;  // Nothing needs done.\n    }\n    std::vector<std::string> features_needed;\n    if ((sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_ENCRYPT)) == 0) {\n        features_needed.emplace_back(\"encrypt\");\n    }\n    android::fscrypt::EncryptionOptions options;\n    if (!android::fscrypt::ParseOptions(entry.encryption_options, &options)) {\n        LERROR << \"Unable to parse encryption options on \" << blk_device << \": \"\n               << entry.encryption_options;\n        return;\n    }\n    if ((options.flags &\n         (FSCRYPT_POLICY_FLAG_IV_INO_LBLK_64 | FSCRYPT_POLICY_FLAG_IV_INO_LBLK_32)) != 0) {\n        // We can only use this policy on ext4 if the \"stable_inodes\" feature\n        // is set on the filesystem, otherwise shrinking will break encrypted files.\n        if ((sb->s_feature_compat & cpu_to_le32(EXT4_FEATURE_COMPAT_STABLE_INODES)) == 0) {\n            features_needed.emplace_back(\"stable_inodes\");\n        }\n    }\n    if (features_needed.size() == 0) {\n        return;\n    }\n    if (!tune2fs_available()) {\n        LERROR << \"Unable to enable ext4 encryption on \" << blk_device\n               << \" because \" TUNE2FS_BIN \" is missing\";\n        return;\n    }\n\n    auto flags = android::base::Join(features_needed, ',');\n    auto flag_arg = \"-O\"s + flags;\n    const char* argv[] = {TUNE2FS_BIN, flag_arg.c_str(), blk_device.c_str()};\n\n    LINFO << \"Enabling ext4 flags \" << flags << \" on \" << blk_device;\n    if (!run_command(argv, ARRAY_SIZE(argv))) {\n        LERROR << \"Failed to run \" TUNE2FS_BIN \" to enable \"\n               << \"ext4 flags \" << flags << \" on \" << blk_device;\n        *fs_stat |= FS_STAT_ENABLE_ENCRYPTION_FAILED;\n    }\n}\n\n// Enable fs-verity if needed.\nstatic void tune_verity(const std::string& blk_device, const FstabEntry& entry,\n                        const struct ext4_super_block* sb, int* fs_stat) {\n    bool has_verity = (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_VERITY)) != 0;\n    bool want_verity = entry.fs_mgr_flags.fs_verity;\n\n    if (has_verity || !want_verity) {\n        return;\n    }\n\n    std::string verity_support;\n    if (!android::base::ReadFileToString(SYSFS_EXT4_VERITY, &verity_support)) {\n        LERROR << \"Failed to open \" << SYSFS_EXT4_VERITY;\n        return;\n    }\n\n    if (!(android::base::Trim(verity_support) == \"supported\")) {\n        LERROR << \"Current ext4 verity not supported by kernel\";\n        return;\n    }\n\n    if (!tune2fs_available()) {\n        LERROR << \"Unable to enable ext4 verity on \" << blk_device\n               << \" because \" TUNE2FS_BIN \" is missing\";\n        return;\n    }\n\n    LINFO << \"Enabling ext4 verity on \" << blk_device;\n\n    const char* argv[] = {TUNE2FS_BIN, \"-O\", \"verity\", blk_device.c_str()};\n    if (!run_command(argv, ARRAY_SIZE(argv))) {\n        LERROR << \"Failed to run \" TUNE2FS_BIN \" to enable \"\n               << \"ext4 verity on \" << blk_device;\n        *fs_stat |= FS_STAT_ENABLE_VERITY_FAILED;\n    }\n}\n\n// Enable casefold if needed.\nstatic void tune_casefold(const std::string& blk_device, const FstabEntry& entry,\n                          const struct ext4_super_block* sb, int* fs_stat) {\n    bool has_casefold = (sb->s_feature_incompat & cpu_to_le32(EXT4_FEATURE_INCOMPAT_CASEFOLD)) != 0;\n    bool wants_casefold =\n            android::base::GetBoolProperty(\"external_storage.casefold.enabled\", false);\n\n    if (entry.mount_point != \"/data\" || !wants_casefold || has_casefold) return;\n\n    std::string casefold_support;\n    if (!android::base::ReadFileToString(SYSFS_EXT4_CASEFOLD, &casefold_support)) {\n        LERROR << \"Failed to open \" << SYSFS_EXT4_CASEFOLD;\n        return;\n    }\n\n    if (!(android::base::Trim(casefold_support) == \"supported\")) {\n        LERROR << \"Current ext4 casefolding not supported by kernel\";\n        return;\n    }\n\n    if (!tune2fs_available()) {\n        LERROR << \"Unable to enable ext4 casefold on \" << blk_device\n               << \" because \" TUNE2FS_BIN \" is missing\";\n        return;\n    }\n\n    LINFO << \"Enabling ext4 casefold on \" << blk_device;\n\n    const char* argv[] = {TUNE2FS_BIN, \"-O\", \"casefold\", \"-E\", \"encoding=utf8\", blk_device.c_str()};\n    if (!run_command(argv, ARRAY_SIZE(argv))) {\n        LERROR << \"Failed to run \" TUNE2FS_BIN \" to enable \"\n               << \"ext4 casefold on \" << blk_device;\n        *fs_stat |= FS_STAT_ENABLE_CASEFOLD_FAILED;\n    }\n}\n\nstatic bool resize2fs_available(void) {\n    return access(RESIZE2FS_BIN, X_OK) == 0;\n}\n\n// Enable metadata_csum\nstatic void tune_metadata_csum(const std::string& blk_device, const FstabEntry& entry,\n                               const struct ext4_super_block* sb, int* fs_stat) {\n    bool has_meta_csum =\n            (sb->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) != 0;\n    bool want_meta_csum = entry.fs_mgr_flags.ext_meta_csum;\n\n    if (has_meta_csum || !want_meta_csum) return;\n\n    if (!tune2fs_available()) {\n        LERROR << \"Unable to enable metadata_csum on \" << blk_device\n               << \" because \" TUNE2FS_BIN \" is missing\";\n        return;\n    }\n    if (!resize2fs_available()) {\n        LERROR << \"Unable to enable metadata_csum on \" << blk_device\n               << \" because \" RESIZE2FS_BIN \" is missing\";\n        return;\n    }\n\n    LINFO << \"Enabling ext4 metadata_csum on \" << blk_device;\n\n    // Must give `-T now` to prevent last_fsck_time from growing too large,\n    // otherwise, tune2fs won't enable metadata_csum.\n    const char* tune2fs_args[] = {TUNE2FS_BIN, \"-O\",  \"metadata_csum,64bit,extent\",\n                                  \"-T\",        \"now\", blk_device.c_str()};\n    const char* resize2fs_args[] = {RESIZE2FS_BIN, \"-b\", blk_device.c_str()};\n\n    if (!run_command(tune2fs_args, ARRAY_SIZE(tune2fs_args))) {\n        LERROR << \"Failed to run \" TUNE2FS_BIN \" to enable \"\n               << \"ext4 metadata_csum on \" << blk_device;\n        *fs_stat |= FS_STAT_ENABLE_METADATA_CSUM_FAILED;\n    } else if (!run_command(resize2fs_args, ARRAY_SIZE(resize2fs_args))) {\n        LERROR << \"Failed to run \" RESIZE2FS_BIN \" to enable \"\n               << \"ext4 metadata_csum on \" << blk_device;\n        *fs_stat |= FS_STAT_ENABLE_METADATA_CSUM_FAILED;\n    }\n}\n\n// Read the primary superblock from an f2fs filesystem.  On failure return\n// false.  If it's not an f2fs filesystem, also set FS_STAT_INVALID_MAGIC.\n#define F2FS_SUPER_OFFSET 1024\nstatic bool read_f2fs_superblock(const std::string& blk_device, int* fs_stat) {\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));\n    __le32 sb1, sb2;\n\n    if (fd < 0) {\n        PERROR << \"Failed to open '\" << blk_device << \"'\";\n        return false;\n    }\n\n    if (TEMP_FAILURE_RETRY(pread(fd, &sb1, sizeof(sb1), F2FS_SUPER_OFFSET)) != sizeof(sb1)) {\n        PERROR << \"Can't read '\" << blk_device << \"' superblock1\";\n        return false;\n    }\n    // F2FS only supports block_size=page_size case. So, it is safe to call\n    // `getpagesize()` and use that as size of super block.\n    if (TEMP_FAILURE_RETRY(pread(fd, &sb2, sizeof(sb2), getpagesize() + F2FS_SUPER_OFFSET)) !=\n        sizeof(sb2)) {\n        PERROR << \"Can't read '\" << blk_device << \"' superblock2\";\n        return false;\n    }\n\n    if (sb1 != cpu_to_le32(F2FS_SUPER_MAGIC) && sb2 != cpu_to_le32(F2FS_SUPER_MAGIC)) {\n        LINFO << \"Invalid f2fs superblock on '\" << blk_device << \"'\";\n        *fs_stat |= FS_STAT_INVALID_MAGIC;\n        return false;\n    }\n    return true;\n}\n\n// exported silent version of the above that just answer the question is_f2fs\nbool fs_mgr_is_f2fs(const std::string& blk_device) {\n    android::base::ErrnoRestorer restore;\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blk_device.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) return false;\n    __le32 sb;\n    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), F2FS_SUPER_OFFSET)) != sizeof(sb)) {\n        return false;\n    }\n    if (sb == cpu_to_le32(F2FS_SUPER_MAGIC)) return true;\n    if (TEMP_FAILURE_RETRY(pread(fd, &sb, sizeof(sb), getpagesize() + F2FS_SUPER_OFFSET)) !=\n        sizeof(sb)) {\n        return false;\n    }\n    return sb == cpu_to_le32(F2FS_SUPER_MAGIC);\n}\n\nstatic void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {\n    std::string block_device;\n    if (!Realpath(entry_block_device, &block_device)) {\n        PERROR << \"Failed to realpath \" << entry_block_device;\n        return;\n    }\n\n    static constexpr std::string_view kDevBlockPrefix(\"/dev/block/\");\n    if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {\n        LWARNING << block_device << \" is not a block device\";\n        return;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    while (true) {\n        std::string block_name = block_device;\n        if (android::base::StartsWith(block_device, kDevBlockPrefix)) {\n            block_name = block_device.substr(kDevBlockPrefix.length());\n        }\n        std::string sys_partition =\n                android::base::StringPrintf(\"/sys/class/block/%s/partition\", block_name.c_str());\n        struct stat info;\n        if (lstat(sys_partition.c_str(), &info) == 0) {\n            // it has a partition like \"sda12\".\n            block_name += \"/..\";\n        }\n        std::string sys_ra = android::base::StringPrintf(\"/sys/class/block/%s/queue/read_ahead_kb\",\n                                                         block_name.c_str());\n        std::string size = android::base::StringPrintf(\"%llu\", (long long)size_kb);\n        android::base::WriteStringToFile(size, sys_ra.c_str());\n        LINFO << \"Set readahead_kb: \" << size << \" on \" << sys_ra;\n\n        auto parent = dm.GetParentBlockDeviceByPath(block_device);\n        if (!parent) {\n            return;\n        }\n        block_device = *parent;\n    }\n}\n\n//\n// Mechanism to allow fsck to be triggered by setting ro.preventative_fsck\n// Introduced to address b/305658663\n// If the property value is not equal to the flag file contents, trigger\n// fsck and store the property value in the flag file\n// If we want to trigger again, simply change the property value\n//\nstatic bool check_if_preventative_fsck_needed(const FstabEntry& entry) {\n    const char* flag_file = \"/metadata/vold/preventative_fsck\";\n    if (entry.mount_point != \"/data\") return false;\n\n    // Don't error check - both default to empty string, which is OK\n    std::string prop = android::base::GetProperty(\"ro.preventative_fsck\", \"\");\n    std::string flag;\n    android::base::ReadFileToString(flag_file, &flag);\n    if (prop == flag) return false;\n    // fsck is run immediately, so assume it runs or there is some deeper problem\n    if (!android::base::WriteStringToFile(prop, flag_file))\n        PERROR << \"Failed to write file \" << flag_file;\n    LINFO << \"Run preventative fsck on /data\";\n    return true;\n}\n\n//\n// Prepare the filesystem on the given block device to be mounted.\n//\n// If the \"check\" option was given in the fstab record, or it seems that the\n// filesystem was uncleanly shut down, we'll run fsck on the filesystem.\n//\n// If needed, we'll also enable (or disable) filesystem features as specified by\n// the fstab record.\n//\nstatic int prepare_fs_for_mount(const std::string& blk_device, const FstabEntry& entry,\n                                const std::string& alt_mount_point = \"\") {\n    auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;\n    // We need this because sometimes we have legacy symlinks that are\n    // lingering around and need cleaning up.\n    struct stat info;\n    if (lstat(mount_point.c_str(), &info) == 0 && (info.st_mode & S_IFMT) == S_IFLNK) {\n        unlink(mount_point.c_str());\n    }\n    mkdir(mount_point.c_str(), 0755);\n\n    // Don't need to return error, since it's a salt\n    if (entry.readahead_size_kb != -1) {\n        SetReadAheadSize(blk_device, entry.readahead_size_kb);\n    }\n\n    int fs_stat = 0;\n\n    if (is_extfs(entry.fs_type)) {\n        struct ext4_super_block sb;\n\n        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {\n            if ((sb.s_feature_incompat & EXT4_FEATURE_INCOMPAT_RECOVER) != 0 ||\n                (sb.s_state & EXT4_VALID_FS) == 0) {\n                LINFO << \"Filesystem on \" << blk_device << \" was not cleanly shutdown; \"\n                      << \"state flags: 0x\" << std::hex << sb.s_state << \", \"\n                      << \"incompat feature flags: 0x\" << std::hex << sb.s_feature_incompat;\n                fs_stat |= FS_STAT_UNCLEAN_SHUTDOWN;\n            }\n\n            // Note: quotas should be enabled before running fsck.\n            tune_quota(blk_device, entry, &sb, &fs_stat);\n        } else {\n            return fs_stat;\n        }\n    } else if (is_f2fs(entry.fs_type)) {\n        if (!read_f2fs_superblock(blk_device, &fs_stat)) {\n            return fs_stat;\n        }\n    }\n\n    if (check_if_preventative_fsck_needed(entry) || entry.fs_mgr_flags.check ||\n        (fs_stat & (FS_STAT_UNCLEAN_SHUTDOWN | FS_STAT_QUOTA_ENABLED))) {\n        check_fs(blk_device, entry.fs_type, mount_point, &fs_stat);\n    }\n\n    if (is_extfs(entry.fs_type) &&\n        (entry.reserved_size != 0 || entry.fs_mgr_flags.file_encryption ||\n         entry.fs_mgr_flags.fs_verity || entry.fs_mgr_flags.ext_meta_csum)) {\n        struct ext4_super_block sb;\n\n        if (read_ext4_superblock(blk_device, &sb, &fs_stat)) {\n            tune_reserved_size(blk_device, entry, &sb, &fs_stat);\n            tune_encrypt(blk_device, entry, &sb, &fs_stat);\n            tune_verity(blk_device, entry, &sb, &fs_stat);\n            tune_casefold(blk_device, entry, &sb, &fs_stat);\n            tune_metadata_csum(blk_device, entry, &sb, &fs_stat);\n        }\n    }\n\n    return fs_stat;\n}\n\n// Mark the given block device as read-only, using the BLKROSET ioctl.\nbool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly) {\n    unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) {\n        return false;\n    }\n\n    int ON = readonly;\n    return ioctl(fd, BLKROSET, &ON) == 0;\n}\n\n// Orange state means the device is unlocked, see the following link for details.\n// https://source.android.com/security/verifiedboot/verified-boot#device_state\nbool fs_mgr_is_device_unlocked() {\n    std::string verified_boot_state;\n    if (fs_mgr_get_boot_config(\"verifiedbootstate\", &verified_boot_state)) {\n        return verified_boot_state == \"orange\";\n    }\n    return false;\n}\n\n// __mount(): wrapper around the mount() system call which also\n// sets the underlying block device to read-only if the mount is read-only.\n// See \"man 2 mount\" for return values.\nstatic int __mount(const std::string& source, const std::string& target, const FstabEntry& entry,\n                   bool read_only = false) {\n    errno = 0;\n    unsigned long mountflags = entry.flags;\n    if (read_only) {\n        mountflags |= MS_RDONLY;\n    }\n    int ret = 0;\n    int save_errno = 0;\n    int gc_allowance = 0;\n    std::string opts;\n    std::string checkpoint_opts;\n    bool try_f2fs_gc_allowance = is_f2fs(entry.fs_type) && entry.fs_checkpoint_opts.length() > 0;\n    bool try_f2fs_fallback = false;\n    Timer t;\n\n    do {\n        if (save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)) {\n            PINFO << \"Kernel does not support \" << checkpoint_opts << \", trying without.\";\n            try_f2fs_gc_allowance = false;\n            // Attempt without gc allowance before dropping.\n            try_f2fs_fallback = !try_f2fs_fallback;\n        }\n        if (try_f2fs_gc_allowance) {\n            checkpoint_opts = entry.fs_checkpoint_opts + \":\" + std::to_string(gc_allowance) + \"%\";\n        } else if (try_f2fs_fallback) {\n            checkpoint_opts = entry.fs_checkpoint_opts;\n        } else {\n            checkpoint_opts = \"\";\n        }\n        opts = entry.fs_options + checkpoint_opts;\n        if (save_errno == EAGAIN) {\n            PINFO << \"Retrying mount (source=\" << source << \",target=\" << target\n                  << \",type=\" << entry.fs_type << \", gc_allowance=\" << gc_allowance << \"%)=\" << ret\n                  << \"(\" << save_errno << \")\";\n        }\n\n        // Let's get the raw dm target, if it's a symlink, since some existing applications\n        // rely on /proc/mounts to find the userdata's dm target path. Don't break that assumption.\n        std::string real_source;\n        if (!android::base::Realpath(source, &real_source)) {\n            real_source = source;\n        }\n\n        // Clear errno prior to calling `mount`, to avoid clobbering with any errno that\n        // may have been set from prior calls (e.g. realpath).\n        errno = 0;\n        ret = mount(real_source.c_str(), target.c_str(), entry.fs_type.c_str(), mountflags,\n                    opts.c_str());\n        save_errno = errno;\n        if (try_f2fs_gc_allowance) gc_allowance += 10;\n    } while ((ret && save_errno == EAGAIN && gc_allowance <= 100) ||\n             (ret && save_errno == EINVAL && (try_f2fs_gc_allowance || try_f2fs_fallback)));\n    const char* target_missing = \"\";\n    const char* source_missing = \"\";\n    if (save_errno == ENOENT) {\n        if (access(target.c_str(), F_OK)) {\n            target_missing = \"(missing)\";\n        } else if (access(source.c_str(), F_OK)) {\n            source_missing = \"(missing)\";\n        }\n        errno = save_errno;\n    }\n    PINFO << __FUNCTION__ << \"(source=\" << source << source_missing << \",target=\" << target\n          << target_missing << \",type=\" << entry.fs_type << \")=\" << ret;\n    if ((ret == 0) && (mountflags & MS_RDONLY) != 0) {\n        fs_mgr_set_blk_ro(source);\n    }\n    if (ret == 0) {\n        android::base::SetProperty(\"ro.boottime.init.mount.\" + Basename(target),\n                                   std::to_string(t.duration().count()));\n    }\n    errno = save_errno;\n    return ret;\n}\n\nstatic bool fs_match(const std::string& in1, const std::string& in2) {\n    if (in1.empty() || in2.empty()) {\n        return false;\n    }\n\n    auto in1_end = in1.size() - 1;\n    while (in1_end > 0 && in1[in1_end] == '/') {\n        in1_end--;\n    }\n\n    auto in2_end = in2.size() - 1;\n    while (in2_end > 0 && in2[in2_end] == '/') {\n        in2_end--;\n    }\n\n    if (in1_end != in2_end) {\n        return false;\n    }\n\n    for (size_t i = 0; i <= in1_end; ++i) {\n        if (in1[i] != in2[i]) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic bool should_use_metadata_encryption(const FstabEntry& entry) {\n    return !entry.metadata_key_dir.empty() && entry.fs_mgr_flags.file_encryption;\n}\n\n// Tries to mount any of the consecutive fstab entries that match\n// the mountpoint of the one given by fstab[start_idx].\n//\n// end_idx: On return, will be the last entry that was looked at.\n// attempted_idx: On return, will indicate which fstab entry\n//     succeeded. In case of failure, it will be the start_idx.\n// Sets errno to match the 1st mount failure on failure.\nstatic bool mount_with_alternatives(Fstab& fstab, int start_idx, bool interrupted, int* end_idx,\n                                    int* attempted_idx) {\n    unsigned long i;\n    int mount_errno = 0;\n    bool mounted = false;\n\n    // Hunt down an fstab entry for the same mount point that might succeed.\n    for (i = start_idx;\n         // We required that fstab entries for the same mountpoint be consecutive.\n         i < fstab.size() && fstab[start_idx].mount_point == fstab[i].mount_point; i++) {\n        // Don't try to mount/encrypt the same mount point again.\n        // Deal with alternate entries for the same point which are required to be all following\n        // each other.\n        if (mounted) {\n            LINFO << __FUNCTION__ << \"(): skipping fstab dup mountpoint=\" << fstab[i].mount_point\n                  << \" rec[\" << i << \"].fs_type=\" << fstab[i].fs_type << \" already mounted as \"\n                  << fstab[*attempted_idx].fs_type;\n            continue;\n        }\n\n        if (interrupted) {\n            LINFO << __FUNCTION__ << \"(): skipping fstab mountpoint=\" << fstab[i].mount_point\n                  << \" rec[\" << i << \"].fs_type=\" << fstab[i].fs_type\n                  << \" (previously interrupted during encryption step)\";\n            continue;\n        }\n\n        // fstab[start_idx].blk_device is already updated to /dev/dm-<N> by\n        // AVB related functions. Copy it from start_idx to the current index i.\n        if ((i != start_idx) && fstab[i].fs_mgr_flags.logical &&\n            fstab[start_idx].fs_mgr_flags.logical &&\n            (fstab[i].logical_partition_name == fstab[start_idx].logical_partition_name)) {\n            fstab[i].blk_device = fstab[start_idx].blk_device;\n        }\n\n        int fs_stat = prepare_fs_for_mount(fstab[i].blk_device, fstab[i]);\n        if (fs_stat & FS_STAT_INVALID_MAGIC) {\n            LERROR << __FUNCTION__\n                   << \"(): skipping mount due to invalid magic, mountpoint=\" << fstab[i].mount_point\n                   << \" blk_dev=\" << realpath(fstab[i].blk_device) << \" rec[\" << i\n                   << \"].fs_type=\" << fstab[i].fs_type;\n            mount_errno = EINVAL;  // continue bootup for metadata encryption\n            continue;\n        }\n\n        int retry_count = 2;\n        const auto read_only = should_use_metadata_encryption(fstab[i]);\n        if (read_only) {\n            LOG(INFO) << \"Mount point \" << fstab[i].blk_device << \" @ \" << fstab[i].mount_point\n                      << \" uses metadata encryption, which means we need to unmount it later and \"\n                         \"call encryptFstab/encrypt_inplace. To avoid file operations before \"\n                         \"encryption, we will mount it as read-only first\";\n        }\n        while (retry_count-- > 0) {\n            if (!__mount(fstab[i].blk_device, fstab[i].mount_point, fstab[i], read_only)) {\n                *attempted_idx = i;\n                mounted = true;\n                if (i != start_idx) {\n                    LINFO << __FUNCTION__ << \"(): Mounted \" << fstab[i].blk_device << \" on \"\n                          << fstab[i].mount_point << \" with fs_type=\" << fstab[i].fs_type\n                          << \" instead of \" << fstab[start_idx].fs_type;\n                }\n                fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;\n                mount_errno = 0;\n                break;\n            } else {\n                if (retry_count <= 0) break;  // run check_fs only once\n                fs_stat |= FS_STAT_FULL_MOUNT_FAILED;\n                // back up the first errno for crypto decisions.\n                if (mount_errno == 0) {\n                    mount_errno = errno;\n                }\n                // retry after fsck\n                check_fs(fstab[i].blk_device, fstab[i].fs_type, fstab[i].mount_point, &fs_stat);\n            }\n        }\n        log_fs_stat(fstab[i].blk_device, fs_stat);\n    }\n\n    /* Adjust i for the case where it was still withing the recs[] */\n    if (i < fstab.size()) --i;\n\n    *end_idx = i;\n    if (!mounted) {\n        *attempted_idx = start_idx;\n        errno = mount_errno;\n        return false;\n    }\n    return true;\n}\n\nstatic bool TranslateExtLabels(FstabEntry* entry) {\n    if (!StartsWith(entry->blk_device, \"LABEL=\")) {\n        return true;\n    }\n\n    std::string label = entry->blk_device.substr(6);\n    if (label.size() > 16) {\n        LERROR << \"FS label is longer than allowed by filesystem\";\n        return false;\n    }\n\n    auto blockdir = std::unique_ptr<DIR, decltype(&closedir)>{opendir(\"/dev/block\"), closedir};\n    if (!blockdir) {\n        LERROR << \"couldn't open /dev/block\";\n        return false;\n    }\n\n    struct dirent* ent;\n    while ((ent = readdir(blockdir.get()))) {\n        if (ent->d_type != DT_BLK)\n            continue;\n\n        unique_fd fd(TEMP_FAILURE_RETRY(\n                openat(dirfd(blockdir.get()), ent->d_name, O_RDONLY | O_CLOEXEC)));\n        if (fd < 0) {\n            LERROR << \"Cannot open block device /dev/block/\" << ent->d_name;\n            return false;\n        }\n\n        ext4_super_block super_block;\n        if (TEMP_FAILURE_RETRY(lseek(fd, 1024, SEEK_SET)) < 0 ||\n            TEMP_FAILURE_RETRY(read(fd, &super_block, sizeof(super_block))) !=\n                    sizeof(super_block)) {\n            // Probably a loopback device or something else without a readable superblock.\n            continue;\n        }\n\n        if (super_block.s_magic != EXT4_SUPER_MAGIC) {\n            LINFO << \"/dev/block/\" << ent->d_name << \" not ext{234}\";\n            continue;\n        }\n\n        if (label == super_block.s_volume_name) {\n            std::string new_blk_device = \"/dev/block/\"s + ent->d_name;\n\n            LINFO << \"resolved label \" << entry->blk_device << \" to \" << new_blk_device;\n\n            entry->blk_device = new_blk_device;\n            return true;\n        }\n    }\n\n    return false;\n}\n\n// Check to see if a mountable volume has encryption requirements\nstatic int handle_encryptable(const FstabEntry& entry) {\n    if (should_use_metadata_encryption(entry)) {\n        if (umount_retry(entry.mount_point)) {\n            return FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION;\n        }\n        PERROR << \"Could not umount \" << entry.mount_point << \" - fail since can't encrypt\";\n        return FS_MGR_MNTALL_FAIL;\n    } else if (entry.fs_mgr_flags.file_encryption) {\n        LINFO << entry.mount_point << \" is file encrypted\";\n        return FS_MGR_MNTALL_DEV_FILE_ENCRYPTED;\n    } else {\n        return FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;\n    }\n}\n\nstatic void set_type_property(int status) {\n    switch (status) {\n        case FS_MGR_MNTALL_DEV_FILE_ENCRYPTED:\n        case FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED:\n        case FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION:\n            SetProperty(\"ro.crypto.type\", \"file\");\n            break;\n    }\n}\n\nstatic bool call_vdc(const std::vector<std::string>& args, int* ret) {\n    std::vector<char const*> argv;\n    argv.emplace_back(\"/system/bin/vdc\");\n    for (auto& arg : args) {\n        argv.emplace_back(arg.c_str());\n    }\n    LOG(INFO) << \"Calling: \" << android::base::Join(argv, ' ');\n    int err = logwrap_fork_execvp(argv.size(), argv.data(), ret, false, LOG_ALOG, false, nullptr);\n    if (err != 0) {\n        LOG(ERROR) << \"vdc call failed with error code: \" << err;\n        return false;\n    }\n    LOG(DEBUG) << \"vdc finished successfully\";\n    if (ret != nullptr) {\n        *ret = WEXITSTATUS(*ret);\n    }\n    return true;\n}\n\nbool fs_mgr_update_logical_partition(FstabEntry* entry) {\n    // Logical partitions are specified with a named partition rather than a\n    // block device, so if the block device is a path, then it has already\n    // been updated.\n    if (entry->blk_device[0] == '/') {\n        return true;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    std::string device_name;\n    if (!dm.GetDmDevicePathByName(entry->blk_device, &device_name)) {\n        return false;\n    }\n\n    entry->blk_device = device_name;\n    return true;\n}\n\nstatic bool SupportsCheckpoint(FstabEntry* entry) {\n    return entry->fs_mgr_flags.checkpoint_blk || entry->fs_mgr_flags.checkpoint_fs;\n}\n\nclass CheckpointManager {\n  public:\n    CheckpointManager(int needs_checkpoint = -1, bool metadata_encrypted = false,\n                      bool needs_encrypt = false)\n        : needs_checkpoint_(needs_checkpoint),\n          metadata_encrypted_(metadata_encrypted),\n          needs_encrypt_(needs_encrypt) {}\n\n    bool NeedsCheckpoint() {\n        if (needs_checkpoint_ != UNKNOWN) {\n            return needs_checkpoint_ == YES;\n        }\n        if (!call_vdc({\"checkpoint\", \"needsCheckpoint\"}, &needs_checkpoint_)) {\n            LERROR << \"Failed to find if checkpointing is needed. Assuming no.\";\n            needs_checkpoint_ = NO;\n        }\n        return needs_checkpoint_ == YES;\n    }\n\n    bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {\n        if (!SupportsCheckpoint(entry)) {\n            return true;\n        }\n\n        if (entry->fs_mgr_flags.checkpoint_blk && !metadata_encrypted_) {\n            call_vdc({\"checkpoint\", \"restoreCheckpoint\", entry->blk_device}, nullptr);\n        }\n\n        if (!NeedsCheckpoint()) {\n            return true;\n        }\n\n        if (!UpdateCheckpointPartition(entry, block_device)) {\n            LERROR << \"Could not set up checkpoint partition, skipping!\";\n            return false;\n        }\n\n        return true;\n    }\n\n    bool Revert(FstabEntry* entry) {\n        if (!SupportsCheckpoint(entry)) {\n            return true;\n        }\n\n        if (device_map_.find(entry->blk_device) == device_map_.end()) {\n            return true;\n        }\n\n        std::string bow_device = entry->blk_device;\n        entry->blk_device = device_map_[bow_device];\n        device_map_.erase(bow_device);\n\n        DeviceMapper& dm = DeviceMapper::Instance();\n        if (!dm.DeleteDevice(\"bow\")) {\n            PERROR << \"Failed to remove bow device\";\n        }\n\n        return true;\n    }\n\n  private:\n    bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) {\n        if (entry->fs_mgr_flags.checkpoint_fs) {\n            if (is_f2fs(entry->fs_type)) {\n                entry->fs_checkpoint_opts = \",checkpoint=disable\";\n            } else {\n                LERROR << entry->fs_type << \" does not implement checkpoints.\";\n            }\n        } else if (entry->fs_mgr_flags.checkpoint_blk && !needs_encrypt_) {\n            auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;\n            if (fs_mgr_find_bow_device(actual_block_device).empty()) {\n                unique_fd fd(\n                        TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));\n                if (fd < 0) {\n                    PERROR << \"Cannot open device \" << entry->blk_device;\n                    return false;\n                }\n\n                uint64_t size = get_block_device_size(fd) / 512;\n                if (!size) {\n                    PERROR << \"Cannot get device size\";\n                    return false;\n                }\n\n                // dm-bow will not load if size is not a multiple of 4096\n                // rounding down does not hurt, since ext4 will only use full blocks\n                size &= ~7;\n\n                android::dm::DmTable table;\n                auto bowTarget =\n                        std::make_unique<android::dm::DmTargetBow>(0, size, entry->blk_device);\n\n                // dm-bow uses the first block as a log record, and relocates the real first block\n                // elsewhere. For metadata encrypted devices, dm-bow sits below dm-default-key, and\n                // for post Android Q devices dm-default-key uses a block size of 4096 always.\n                // So if dm-bow's block size, which by default is the block size of the underlying\n                // hardware, is less than dm-default-key's, blocks will get broken up and I/O will\n                // fail as it won't be data_unit_size aligned.\n                // However, since it is possible there is an already shipping non\n                // metadata-encrypted device with smaller blocks, we must not change this for\n                // devices shipped with Q or earlier unless they explicitly selected dm-default-key\n                // v2\n                unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(\n                        \"ro.crypto.dm_default_key.options_format.version\",\n                        (android::fscrypt::GetFirstApiLevel() <= __ANDROID_API_Q__ ? 1 : 2));\n                if (options_format_version > 1) {\n                    bowTarget->SetBlockSize(4096);\n                }\n\n                if (!table.AddTarget(std::move(bowTarget))) {\n                    LERROR << \"Failed to add bow target\";\n                    return false;\n                }\n\n                DeviceMapper& dm = DeviceMapper::Instance();\n                if (!dm.CreateDevice(\"bow\", table)) {\n                    PERROR << \"Failed to create bow device\";\n                    return false;\n                }\n\n                std::string name;\n                if (!dm.GetDmDevicePathByName(\"bow\", &name)) {\n                    PERROR << \"Failed to get bow device name\";\n                    return false;\n                }\n\n                device_map_[name] = entry->blk_device;\n                entry->blk_device = name;\n            }\n        }\n        return true;\n    }\n\n    enum { UNKNOWN = -1, NO = 0, YES = 1 };\n    int needs_checkpoint_;\n    bool metadata_encrypted_;\n    bool needs_encrypt_;\n    std::map<std::string, std::string> device_map_;\n};\n\nstd::string fs_mgr_find_bow_device(const std::string& block_device) {\n    // handle symlink such as \"/dev/block/mapper/userdata\"\n    std::string real_path;\n    if (!android::base::Realpath(block_device, &real_path)) {\n        real_path = block_device;\n    }\n\n    struct stat st;\n    if (stat(real_path.c_str(), &st) < 0) {\n        PLOG(ERROR) << \"stat failed: \" << real_path;\n        return std::string();\n    }\n    if (!S_ISBLK(st.st_mode)) {\n        PLOG(ERROR) << real_path << \" is not block device\";\n        return std::string();\n    }\n    std::string sys_dir = android::base::StringPrintf(\"/sys/dev/block/%u:%u\", major(st.st_rdev),\n                                                      minor(st.st_rdev));\n    for (;;) {\n        std::string name;\n        if (!android::base::ReadFileToString(sys_dir + \"/dm/name\", &name)) {\n            PLOG(ERROR) << real_path << \" is not dm device\";\n            return std::string();\n        }\n\n        if (name == \"bow\\n\") return sys_dir;\n\n        std::string slaves = sys_dir + \"/slaves\";\n        std::unique_ptr<DIR, decltype(&closedir)> directory(opendir(slaves.c_str()), closedir);\n        if (!directory) {\n            PLOG(ERROR) << \"Can't open slave directory \" << slaves;\n            return std::string();\n        }\n\n        int count = 0;\n        for (dirent* entry = readdir(directory.get()); entry; entry = readdir(directory.get())) {\n            if (entry->d_type != DT_LNK) continue;\n\n            if (count == 1) {\n                LOG(ERROR) << \"Too many slaves in \" << slaves;\n                return std::string();\n            }\n\n            ++count;\n            sys_dir = std::string(\"/sys/block/\") + entry->d_name;\n        }\n\n        if (count != 1) {\n            LOG(ERROR) << \"No slave in \" << slaves;\n            return std::string();\n        }\n    }\n}\n\nstatic constexpr const char* kUserdataWrapperName = \"userdata-wrapper\";\n\nstatic void WrapUserdata(FstabEntry* entry, dev_t dev, const std::string& block_device) {\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (dm.GetState(kUserdataWrapperName) != DmDeviceState::INVALID) {\n        // This will report failure for us. If we do fail to get the path,\n        // we leave the device unwrapped.\n        dm.GetDmDevicePathByName(kUserdataWrapperName, &entry->blk_device);\n        return;\n    }\n\n    unique_fd fd(open(block_device.c_str(), O_RDONLY | O_CLOEXEC));\n    if (fd < 0) {\n        PLOG(ERROR) << \"open failed: \" << entry->blk_device;\n        return;\n    }\n\n    auto dev_str = android::base::StringPrintf(\"%u:%u\", major(dev), minor(dev));\n    uint64_t sectors = get_block_device_size(fd) / 512;\n\n    android::dm::DmTable table;\n    table.Emplace<DmTargetLinear>(0, sectors, dev_str, 0);\n\n    std::string dm_path;\n    if (!dm.CreateDevice(kUserdataWrapperName, table, &dm_path, 20s)) {\n        LOG(ERROR) << \"Failed to create userdata wrapper device\";\n        return;\n    }\n    entry->blk_device = dm_path;\n}\n\n// When using Virtual A/B, partitions can be backed by /data and mapped with\n// device-mapper in first-stage init. This can happen when merging an OTA or\n// when using adb remount to house \"scratch\". In this case, /data cannot be\n// mounted directly off the userdata block device, and e2fsck will refuse to\n// scan it, because the kernel reports the block device as in-use.\n//\n// As a workaround, when mounting /data, we create a trivial dm-linear wrapper\n// if the underlying block device already has dependencies. Note that we make\n// an exception for metadata-encrypted devices, since dm-default-key is already\n// a wrapper.\nstatic void WrapUserdataIfNeeded(FstabEntry* entry, const std::string& actual_block_device = {}) {\n    const auto& block_device =\n            actual_block_device.empty() ? entry->blk_device : actual_block_device;\n    if (entry->mount_point != \"/data\" || !entry->metadata_key_dir.empty() ||\n        android::base::StartsWith(block_device, \"/dev/block/dm-\")) {\n        return;\n    }\n\n    struct stat st;\n    if (stat(block_device.c_str(), &st) < 0) {\n        PLOG(ERROR) << \"stat failed: \" << block_device;\n        return;\n    }\n\n    std::string path = android::base::StringPrintf(\"/sys/dev/block/%u:%u/holders\",\n                                                   major(st.st_rdev), minor(st.st_rdev));\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);\n    if (!dir) {\n        PLOG(ERROR) << \"opendir failed: \" << path;\n        return;\n    }\n\n    struct dirent* d;\n    bool has_holders = false;\n    while ((d = readdir(dir.get())) != nullptr) {\n        if (strcmp(d->d_name, \".\") != 0 && strcmp(d->d_name, \"..\") != 0) {\n            has_holders = true;\n            break;\n        }\n    }\n\n    if (has_holders) {\n        WrapUserdata(entry, st.st_rdev, block_device);\n    }\n}\n\nstatic bool IsMountPointMounted(const std::string& mount_point) {\n    // Check if this is already mounted.\n    Fstab fstab;\n    if (!ReadFstabFromFile(\"/proc/mounts\", &fstab)) {\n        return false;\n    }\n    return GetEntryForMountPoint(&fstab, mount_point) != nullptr;\n}\n\nstd::string fs_mgr_metadata_encryption_in_progress_file_name(const FstabEntry& entry) {\n    return entry.metadata_key_dir + \"/in_progress\";\n}\n\nbool WasMetadataEncryptionInterrupted(const FstabEntry& entry) {\n    if (!should_use_metadata_encryption(entry)) return false;\n    return access(fs_mgr_metadata_encryption_in_progress_file_name(entry).c_str(), R_OK) == 0;\n}\n\nstatic FstabEntry* LocateFormattableEntry(FstabEntry* const begin, FstabEntry* const end) {\n    if (begin == end) {\n        return nullptr;\n    }\n    const bool dev_option_enabled =\n            android::base::GetBoolProperty(\"ro.product.build.16k_page.enabled\", false);\n    FstabEntry* f2fs_entry = nullptr;\n    for (auto iter = begin; iter != end && iter->blk_device == begin->blk_device; iter++) {\n        if (iter->fs_mgr_flags.formattable) {\n            if (getpagesize() != 4096 && is_f2fs(iter->fs_type) && dev_option_enabled) {\n                f2fs_entry = iter;\n                continue;\n            }\n            if (f2fs_entry) {\n                LOG(INFO) << \"Skipping F2FS format for block device \" << iter->blk_device << \" @ \"\n                          << iter->mount_point\n                          << \" in non-4K mode for dev option enabled devices, \"\n                             \"as these devices need to toggle between 4K/16K mode, and F2FS does \"\n                             \"not support page_size != block_size configuration.\";\n            }\n            return iter;\n        }\n    }\n    if (f2fs_entry) {\n        LOG(INFO) << \"Using F2FS for \" << f2fs_entry->blk_device << \" @ \" << f2fs_entry->mount_point\n                  << \" even though we are in non-4K mode. Device might require a data wipe after \"\n                     \"going back to 4K mode, as F2FS does not support page_size != block_size\";\n    }\n    return f2fs_entry;\n}\n\n// When multiple fstab records share the same mount_point, it will try to mount each\n// one in turn, and ignore any duplicates after a first successful mount.\n// Returns -1 on error, and  FS_MGR_MNTALL_* otherwise.\nint fs_mgr_mount_all(Fstab* fstab, int mount_mode) {\n    int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;\n    int error_count = 0;\n    CheckpointManager checkpoint_manager;\n    AvbUniquePtr avb_handle(nullptr);\n    bool wiped = false;\n    bool userdata_mounted = false;\n\n    if (fstab->empty()) {\n        return FS_MGR_MNTALL_FAIL;\n    }\n\n    bool scratch_can_be_mounted = true;\n\n    // Keep i int to prevent unsigned integer overflow from (i = top_idx - 1),\n    // where top_idx is 0. It will give SIGABRT\n    for (int i = 0; i < static_cast<int>(fstab->size()); i++) {\n        auto& current_entry = (*fstab)[i];\n\n        // If a filesystem should have been mounted in the first stage, we\n        // ignore it here. With one exception, if the filesystem is\n        // formattable, then it can only be formatted in the second stage,\n        // so we allow it to mount here.\n        if (current_entry.fs_mgr_flags.first_stage_mount &&\n            (!current_entry.fs_mgr_flags.formattable ||\n             IsMountPointMounted(current_entry.mount_point))) {\n            continue;\n        }\n\n        // Don't mount entries that are managed by vold or not for the mount mode.\n        if (current_entry.fs_mgr_flags.vold_managed || current_entry.fs_mgr_flags.recovery_only ||\n            ((mount_mode == MOUNT_MODE_LATE) && !current_entry.fs_mgr_flags.late_mount) ||\n            ((mount_mode == MOUNT_MODE_EARLY) && current_entry.fs_mgr_flags.late_mount)) {\n            continue;\n        }\n\n        // Skip swap and raw partition entries such as boot, recovery, etc.\n        if (current_entry.fs_type == \"swap\" || current_entry.fs_type == \"emmc\" ||\n            current_entry.fs_type == \"mtd\") {\n            continue;\n        }\n\n        // Skip mounting the root partition, as it will already have been mounted.\n        if (current_entry.mount_point == \"/\" || current_entry.mount_point == \"/system\") {\n            if ((current_entry.flags & MS_RDONLY) != 0) {\n                fs_mgr_set_blk_ro(current_entry.blk_device);\n            }\n            continue;\n        }\n\n        // Terrible hack to make it possible to remount /data.\n        // TODO: refactor fs_mgr_mount_all and get rid of this.\n        if (mount_mode == MOUNT_MODE_ONLY_USERDATA && current_entry.mount_point != \"/data\") {\n            continue;\n        }\n\n        // Translate LABEL= file system labels into block devices.\n        if (is_extfs(current_entry.fs_type)) {\n            if (!TranslateExtLabels(&current_entry)) {\n                LERROR << \"Could not translate label to block device\";\n                continue;\n            }\n        }\n\n        if (current_entry.fs_mgr_flags.logical) {\n            if (!fs_mgr_update_logical_partition(&current_entry)) {\n                LERROR << \"Could not set up logical partition, skipping!\";\n                continue;\n            }\n        }\n\n        WrapUserdataIfNeeded(&current_entry);\n\n        if (!checkpoint_manager.Update(&current_entry)) {\n            continue;\n        }\n\n        if (current_entry.fs_mgr_flags.wait && !WaitForFile(current_entry.blk_device, 20s)) {\n            LERROR << \"Skipping '\" << current_entry.blk_device << \"' during mount_all\";\n            continue;\n        }\n\n        if (current_entry.fs_mgr_flags.avb) {\n            if (!avb_handle) {\n                avb_handle = AvbHandle::Open();\n                if (!avb_handle) {\n                    LERROR << \"Failed to open AvbHandle\";\n                    set_type_property(encryptable);\n                    return FS_MGR_MNTALL_FAIL;\n                }\n            }\n            if (avb_handle->SetUpAvbHashtree(&current_entry, true /* wait_for_verity_dev */) ==\n                AvbHashtreeResult::kFail) {\n                LERROR << \"Failed to set up AVB on partition: \" << current_entry.mount_point\n                       << \", skipping!\";\n                // Skips mounting the device.\n                continue;\n            }\n        } else if (!current_entry.avb_keys.empty()) {\n            if (AvbHandle::SetUpStandaloneAvbHashtree(&current_entry) == AvbHashtreeResult::kFail) {\n                LERROR << \"Failed to set up AVB on standalone partition: \"\n                       << current_entry.mount_point << \", skipping!\";\n                // Skips mounting the device.\n                continue;\n            }\n        }\n\n        int last_idx_inspected = -1;\n        const int top_idx = i;\n        int attempted_idx = -1;\n\n        bool encryption_interrupted = WasMetadataEncryptionInterrupted(current_entry);\n        bool mret = mount_with_alternatives(*fstab, i, encryption_interrupted, &last_idx_inspected,\n                                            &attempted_idx);\n        auto& attempted_entry = (*fstab)[attempted_idx];\n        i = last_idx_inspected;\n        int mount_errno = errno;\n\n        // Handle success and deal with encryptability.\n        if (mret) {\n            int status = handle_encryptable(attempted_entry);\n\n            if (status == FS_MGR_MNTALL_FAIL) {\n                // Fatal error - no point continuing.\n                return status;\n            }\n\n            if (status != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {\n                if (encryptable != FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {\n                    // Log and continue\n                    LERROR << \"Only one encryptable/encrypted partition supported\";\n                }\n                encryptable = status;\n                if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {\n                    fs_mgr_set_blk_ro(attempted_entry.blk_device, false);\n                    if (!call_vdc({\"cryptfs\", \"encryptFstab\", attempted_entry.blk_device,\n                                   attempted_entry.mount_point, wiped ? \"true\" : \"false\",\n                                   attempted_entry.fs_type,\n                                   attempted_entry.fs_mgr_flags.is_zoned ? \"true\" : \"false\",\n                                   std::to_string(attempted_entry.length),\n                                   android::base::Join(attempted_entry.user_devices, ' '),\n                                   android::base::Join(attempted_entry.device_aliased, ' ')},\n                                  nullptr)) {\n                        LERROR << \"Encryption failed\";\n                        set_type_property(encryptable);\n                        return FS_MGR_MNTALL_FAIL;\n                    }\n                }\n            }\n\n            if (current_entry.mount_point == \"/data\") {\n                userdata_mounted = true;\n            }\n\n            MountOverlayfs(attempted_entry, &scratch_can_be_mounted);\n\n            // Success!  Go get the next one.\n            continue;\n        }\n        auto formattable_entry =\n                LocateFormattableEntry(fstab->data() + top_idx, fstab->data() + fstab->size());\n        // Mounting failed, understand why and retry.\n        wiped = partition_wiped(current_entry.blk_device.c_str());\n        if (mount_errno != EBUSY && mount_errno != EACCES &&\n            current_entry.fs_mgr_flags.formattable && (wiped || encryption_interrupted)) {\n            // current_entry and attempted_entry point at the same partition, but sometimes\n            // at two different lines in the fstab.  Use current_entry for formatting\n            // as that is the preferred one.\n            if (wiped)\n                LERROR << __FUNCTION__ << \"(): \" << realpath(current_entry.blk_device)\n                       << \" is wiped and \" << current_entry.mount_point << \" \"\n                       << current_entry.fs_type << \" is formattable. Format it.\";\n            if (encryption_interrupted)\n                LERROR << __FUNCTION__ << \"(): \" << realpath(current_entry.blk_device)\n                       << \" was interrupted during encryption and \" << current_entry.mount_point\n                       << \" \" << current_entry.fs_type << \" is formattable. Format it.\";\n\n            checkpoint_manager.Revert(&current_entry);\n\n            // EncryptInplace will be used when vdc gives an error or needs to format partitions\n            // other than /data\n            if (should_use_metadata_encryption(current_entry) &&\n                current_entry.mount_point == \"/data\") {\n\n                // vdc->Format requires \"ro.crypto.type\" to set an encryption flag\n                encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;\n                set_type_property(encryptable);\n\n                if (!call_vdc({\"cryptfs\", \"encryptFstab\", formattable_entry->blk_device,\n                               formattable_entry->mount_point, \"true\" /* shouldFormat */,\n                               formattable_entry->fs_type,\n                               formattable_entry->fs_mgr_flags.is_zoned ? \"true\" : \"false\",\n                               std::to_string(formattable_entry->length),\n                               android::base::Join(formattable_entry->user_devices, ' '),\n                               android::base::Join(formattable_entry->device_aliased, ' ')},\n                              nullptr)) {\n                    LERROR << \"Encryption failed\";\n                } else {\n                    userdata_mounted = true;\n                    continue;\n                }\n            }\n\n            if (fs_mgr_do_format(*formattable_entry) == 0) {\n                // Let's replay the mount actions.\n                i = top_idx - 1;\n                continue;\n            } else {\n                LERROR << __FUNCTION__ << \"(): Format failed. \"\n                       << \"Suggest recovery...\";\n                encryptable = FS_MGR_MNTALL_DEV_NEEDS_RECOVERY;\n                continue;\n            }\n        }\n\n        // mount(2) returned an error, handle the encryptable/formattable case.\n        if (mount_errno != EBUSY && mount_errno != EACCES && !encryption_interrupted &&\n            should_use_metadata_encryption(attempted_entry)) {\n            if (!call_vdc({\"cryptfs\", \"mountFstab\", attempted_entry.blk_device,\n                           attempted_entry.mount_point,\n                           current_entry.fs_mgr_flags.is_zoned ? \"true\" : \"false\",\n                           android::base::Join(current_entry.user_devices, ' ')},\n                          nullptr)) {\n                ++error_count;\n            } else if (current_entry.mount_point == \"/data\") {\n                userdata_mounted = true;\n            }\n            encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;\n            continue;\n        } else {\n            // fs_options might be null so we cannot use PERROR << directly.\n            // Use StringPrintf to output \"(null)\" instead.\n            if (attempted_entry.fs_mgr_flags.no_fail) {\n                PERROR << android::base::StringPrintf(\n                        \"Ignoring failure to mount an un-encryptable, interrupted, or wiped \"\n                        \"partition on %s at %s options: %s\",\n                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),\n                        attempted_entry.fs_options.c_str());\n            } else {\n                PERROR << android::base::StringPrintf(\n                        \"Failed to mount an un-encryptable, interrupted, or wiped partition \"\n                        \"on %s at %s options: %s\",\n                        attempted_entry.blk_device.c_str(), attempted_entry.mount_point.c_str(),\n                        attempted_entry.fs_options.c_str());\n                ++error_count;\n            }\n            continue;\n        }\n    }\n    if (userdata_mounted) {\n        Fstab mounted_fstab;\n        if (!ReadFstabFromFile(\"/proc/mounts\", &mounted_fstab)) {\n            LOG(ERROR) << \"Could't load fstab from /proc/mounts , unable to set ro.fstype.data . \"\n                          \"init.rc actions depending on this prop would not run, boot might fail.\";\n        } else {\n            for (const auto& entry : mounted_fstab) {\n                if (entry.mount_point == \"/data\") {\n                    android::base::SetProperty(\"ro.fstype.data\", entry.fs_type);\n                }\n            }\n        }\n    }\n\n    set_type_property(encryptable);\n\n    if (error_count) {\n        return FS_MGR_MNTALL_FAIL;\n    } else {\n        return encryptable;\n    }\n}\n\nint fs_mgr_umount_all(android::fs_mgr::Fstab* fstab) {\n    AvbUniquePtr avb_handle(nullptr);\n    int ret = FsMgrUmountStatus::SUCCESS;\n    for (auto& current_entry : *fstab) {\n        if (!IsMountPointMounted(current_entry.mount_point)) {\n            continue;\n        }\n\n        if (umount(current_entry.mount_point.c_str()) == -1) {\n            PERROR << \"Failed to umount \" << current_entry.mount_point;\n            ret |= FsMgrUmountStatus::ERROR_UMOUNT;\n            continue;\n        }\n\n        if (current_entry.fs_mgr_flags.logical) {\n            if (!fs_mgr_update_logical_partition(&current_entry)) {\n                LERROR << \"Could not get logical partition blk_device, skipping!\";\n                ret |= FsMgrUmountStatus::ERROR_DEVICE_MAPPER;\n                continue;\n            }\n        }\n\n        if (current_entry.fs_mgr_flags.avb || !current_entry.avb_keys.empty()) {\n            if (!AvbHandle::TearDownAvbHashtree(&current_entry, true /* wait */)) {\n                LERROR << \"Failed to tear down AVB on mount point: \" << current_entry.mount_point;\n                ret |= FsMgrUmountStatus::ERROR_VERITY;\n                continue;\n            }\n        }\n    }\n    return ret;\n}\n\n// wrapper to __mount() and expects a fully prepared fstab_rec,\n// unlike fs_mgr_do_mount which does more things with avb / verity etc.\nint fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {\n    // First check the filesystem if requested.\n    if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {\n        LERROR << \"Skipping mounting '\" << entry.blk_device << \"'\";\n    }\n\n    auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;\n\n    // Run fsck if needed\n    int ret = prepare_fs_for_mount(entry.blk_device, entry, mount_point);\n    // Wiped case doesn't require to try __mount below.\n    if (ret & FS_STAT_INVALID_MAGIC) {\n        return FS_MGR_DOMNT_FAILED;\n    }\n\n    ret = __mount(entry.blk_device, mount_point, entry);\n    if (ret) {\n        ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;\n    }\n\n    return ret;\n}\n\n// If multiple fstab entries are to be mounted on \"n_name\", it will try to mount each one\n// in turn, and stop on 1st success, or no more match.\nint fs_mgr_do_mount(Fstab* fstab, const std::string& n_name, const std::string& n_blk_device,\n                    int needs_checkpoint, bool needs_encrypt) {\n    int mount_errors = 0;\n    int first_mount_errno = 0;\n    std::string mount_point;\n    CheckpointManager checkpoint_manager(needs_checkpoint, true, needs_encrypt);\n    AvbUniquePtr avb_handle(nullptr);\n\n    if (!fstab) {\n        return FS_MGR_DOMNT_FAILED;\n    }\n\n    for (auto& fstab_entry : *fstab) {\n        if (!fs_match(fstab_entry.mount_point, n_name)) {\n            continue;\n        }\n\n        // We found our match.\n        // If this swap or a raw partition, report an error.\n        if (fstab_entry.fs_type == \"swap\" || fstab_entry.fs_type == \"emmc\" ||\n            fstab_entry.fs_type == \"mtd\") {\n            LERROR << \"Cannot mount filesystem of type \" << fstab_entry.fs_type << \" on \"\n                   << n_blk_device;\n            return FS_MGR_DOMNT_FAILED;\n        }\n\n        if (fstab_entry.fs_mgr_flags.logical) {\n            if (!fs_mgr_update_logical_partition(&fstab_entry)) {\n                LERROR << \"Could not set up logical partition, skipping!\";\n                continue;\n            }\n        }\n\n        WrapUserdataIfNeeded(&fstab_entry, n_blk_device);\n\n        if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) {\n            LERROR << \"Could not set up checkpoint partition, skipping!\";\n            continue;\n        }\n\n        // First check the filesystem if requested.\n        if (fstab_entry.fs_mgr_flags.wait && !WaitForFile(n_blk_device, 20s)) {\n            LERROR << \"Skipping mounting '\" << n_blk_device << \"'\";\n            continue;\n        }\n\n        // Now mount it where requested */\n        mount_point = fstab_entry.mount_point;\n\n        int fs_stat = prepare_fs_for_mount(n_blk_device, fstab_entry, mount_point);\n\n        if (fstab_entry.fs_mgr_flags.avb) {\n            if (!avb_handle) {\n                avb_handle = AvbHandle::Open();\n                if (!avb_handle) {\n                    LERROR << \"Failed to open AvbHandle\";\n                    return FS_MGR_DOMNT_FAILED;\n                }\n            }\n            if (avb_handle->SetUpAvbHashtree(&fstab_entry, true /* wait_for_verity_dev */) ==\n                AvbHashtreeResult::kFail) {\n                LERROR << \"Failed to set up AVB on partition: \" << fstab_entry.mount_point\n                       << \", skipping!\";\n                // Skips mounting the device.\n                continue;\n            }\n        } else if (!fstab_entry.avb_keys.empty()) {\n            if (AvbHandle::SetUpStandaloneAvbHashtree(&fstab_entry) == AvbHashtreeResult::kFail) {\n                LERROR << \"Failed to set up AVB on standalone partition: \"\n                       << fstab_entry.mount_point << \", skipping!\";\n                // Skips mounting the device.\n                continue;\n            }\n        }\n\n        int retry_count = 2;\n        while (retry_count-- > 0) {\n            if (!__mount(n_blk_device, mount_point, fstab_entry)) {\n                fs_stat &= ~FS_STAT_FULL_MOUNT_FAILED;\n                log_fs_stat(fstab_entry.blk_device, fs_stat);\n                return FS_MGR_DOMNT_SUCCESS;\n            } else {\n                if (retry_count <= 0) break;  // run check_fs only once\n                if (!first_mount_errno) first_mount_errno = errno;\n                mount_errors++;\n                PERROR << \"Cannot mount filesystem on \" << n_blk_device << \" at \" << mount_point\n                       << \" with fstype \" << fstab_entry.fs_type;\n                fs_stat |= FS_STAT_FULL_MOUNT_FAILED;\n                // try again after fsck\n                check_fs(n_blk_device, fstab_entry.fs_type, mount_point, &fs_stat);\n            }\n        }\n        log_fs_stat(fstab_entry.blk_device, fs_stat);\n    }\n\n    // Reach here means the mount attempt fails.\n    if (mount_errors) {\n        PERROR << \"Cannot mount filesystem on \" << n_blk_device << \" at \" << mount_point;\n        if (first_mount_errno == EBUSY) return FS_MGR_DOMNT_BUSY;\n    } else {\n        // We didn't find a match, say so and return an error.\n        LERROR << \"Cannot find mount point \" << n_name << \" in fstab\";\n    }\n    return FS_MGR_DOMNT_FAILED;\n}\n\nstatic bool ConfigureIoScheduler(const std::string& device_path) {\n    if (!StartsWith(device_path, \"/dev/\")) {\n        LERROR << __func__ << \": invalid argument \" << device_path;\n        return false;\n    }\n\n    const std::string iosched_path =\n            StringPrintf(\"/sys/block/%s/queue/scheduler\", Basename(device_path).c_str());\n    unique_fd iosched_fd(open(iosched_path.c_str(), O_RDWR | O_CLOEXEC));\n    if (iosched_fd.get() == -1) {\n        PERROR << __func__ << \": failed to open \" << iosched_path;\n        return false;\n    }\n\n    // Kernels before v4.1 only support 'noop'. Kernels [v4.1, v5.0) support\n    // 'noop' and 'none'. Kernels v5.0 and later only support 'none'.\n    static constexpr const std::array<std::string_view, 2> kNoScheduler = {\"none\", \"noop\"};\n\n    for (const std::string_view& scheduler : kNoScheduler) {\n        int ret = write(iosched_fd.get(), scheduler.data(), scheduler.size());\n        if (ret > 0) {\n            return true;\n        }\n    }\n\n    PERROR << __func__ << \": failed to write to \" << iosched_path;\n    return false;\n}\n\nstatic bool InstallZramDevice(const std::string& device) {\n    if (!android::base::WriteStringToFile(device, ZRAM_BACK_DEV)) {\n        PERROR << \"Cannot write \" << device << \" in: \" << ZRAM_BACK_DEV;\n        return false;\n    }\n    LINFO << \"Success to set \" << device << \" to \" << ZRAM_BACK_DEV;\n    return true;\n}\n\n/*\n * Zram backing device can be created as long as /data has at least `size`\n * free space, though we may want to leave some extra space for the remaining\n * boot process and other system activities.\n */\nstatic bool ZramBackingDeviceSizeAvailable(off64_t size) {\n    constexpr const char* data_path = \"/data\";\n    uint64_t min_free_mb =\n            android::base::GetUintProperty<uint64_t>(\"ro.zram_backing_device_min_free_mb\", 0);\n\n    // No min_free property. Skip the available size check.\n    if (min_free_mb == 0) return true;\n\n    struct statvfs vst;\n    if (statvfs(data_path, &vst) < 0) {\n        PERROR << \"Cannot check available space: \" << data_path;\n        return false;\n    }\n\n    uint64_t size_free = static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize;\n    uint64_t size_required = size + (min_free_mb * 1024 * 1024);\n    if (size_required > size_free) {\n        PERROR << \"Free space is not enough for zram backing device: \" << size_required << \" > \"\n               << size_free;\n        return false;\n    }\n    return true;\n}\n\nstatic bool PrepareZramBackingDevice(off64_t size) {\n\n    constexpr const char* file_path = \"/data/per_boot/zram_swap\";\n    if (size == 0) return true;\n\n    // Check available space\n    if (!ZramBackingDeviceSizeAvailable(size)) {\n        PERROR << \"No space for target path: \" << file_path;\n        return false;\n    }\n    // Prepare target path\n    unique_fd target_fd(TEMP_FAILURE_RETRY(open(file_path, O_RDWR | O_CREAT | O_CLOEXEC, 0600)));\n    if (target_fd.get() == -1) {\n        PERROR << \"Cannot open target path: \" << file_path;\n        return false;\n    }\n    if (fallocate(target_fd.get(), 0, 0, size) < 0) {\n        PERROR << \"Cannot truncate target path: \" << file_path;\n        unlink(file_path);\n        return false;\n    }\n\n    // Allocate loop device and attach it to file_path.\n    LoopControl loop_control;\n    std::string loop_device;\n    if (!loop_control.Attach(target_fd.get(), 5s, &loop_device)) {\n        return false;\n    }\n\n    ConfigureIoScheduler(loop_device);\n\n    if (auto ret = ConfigureQueueDepth(loop_device, \"/\"); !ret.ok()) {\n        LOG(DEBUG) << \"Failed to config queue depth: \" << ret.error().message();\n    }\n\n    // set block size & direct IO\n    unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loop_device.c_str(), O_RDWR | O_CLOEXEC)));\n    if (loop_fd.get() == -1) {\n        PERROR << \"Cannot open \" << loop_device;\n        return false;\n    }\n    if (!LoopControl::SetAutoClearStatus(loop_fd.get())) {\n        PERROR << \"Failed set LO_FLAGS_AUTOCLEAR for \" << loop_device;\n    }\n    if (!LoopControl::EnableDirectIo(loop_fd.get())) {\n        return false;\n    }\n\n    return InstallZramDevice(loop_device);\n}\n\n// Check whether it is in recovery mode or not.\n//\n// This is a copy from util.h in libinit.\n//\n// You need to check ALL relevant executables calling this function has access to\n// \"/system/bin/recovery\" (including SELinux permissions and UNIX permissions).\nstatic bool IsRecovery() {\n    return access(\"/system/bin/recovery\", F_OK) == 0;\n}\n\n// Decides whether swapon_all should skip setting up zram.\n//\n// swapon_all is deprecated to setup zram after mmd is launched. swapon_all command should skip\n// setting up zram if mmd is enabled by AConfig flag and mmd is configured to set up zram.\nstatic bool ShouldSkipZramSetup() {\n    if (IsRecovery()) {\n        // swapon_all continue to support zram setup in recovery mode after mmd launch.\n        return false;\n    }\n\n    // Since AConfig does not support to load the status from init, we use the system property\n    // \"mmd.enabled_aconfig\" copied from AConfig by `mmd --set-property` command to check whether\n    // mmd is enabled or not.\n    //\n    // aconfig_prop can have either of:\n    //\n    // * \"true\": mmd is enabled by AConfig\n    // * \"false\": mmd is disabled by AConfig\n    // * \"\": swapon_all is executed before `mmd --set-property`\n    //\n    // During mmd being launched, we request OEMs, who decided to use mmd to set up zram, to execute\n    // swapon_all after \"mmd.enabled_aconfig\" system property is initialized. Init can wait the\n    // \"mmd.enabled_aconfig\" initialization by `property:mmd.enabled_aconfig=*` trigger.\n    //\n    // After mmd is launched, we deprecate swapon_all command for setting up zram but recommend to\n    // use `mmd --setup-zram`. It means that the system should call swapon_all with fstab with no\n    // zram entry or the system should never call swapon_all.\n    //\n    // As a transition, OEMs can use the deprecated swapon_all to set up zram for several versions\n    // after mmd is launched. swapon_all command will show warning logs during the transition\n    // period.\n    const std::string aconfig_prop = android::base::GetProperty(\"mmd.enabled_aconfig\", \"\");\n    const bool is_zram_managed_by_mmd = android::base::GetBoolProperty(\"mmd.zram.enabled\", false);\n    if (aconfig_prop == \"true\" && is_zram_managed_by_mmd) {\n        // Skip zram setup since zram is managed by mmd.\n        //\n        // We expect swapon_all is not called when mmd is enabled by AConfig flag.\n        // TODO: b/394484720 - Make this log as warning after mmd is launched.\n        LINFO << \"Skip setting up zram because mmd sets up zram instead.\";\n        return true;\n    }\n\n    if (aconfig_prop == \"false\") {\n        // It is expected to swapon_all command to set up zram before mmd is launched.\n        LOG(DEBUG) << \"mmd is not launched yet. swapon_all setup zram.\";\n    } else if (is_zram_managed_by_mmd) {\n        // This branch is for aconfig_prop == \"\"\n\n        // On the system which uses mmd to setup zram, swapon_all must be executed after\n        // mmd.enabled_aconfig is initialized.\n        LERROR << \"swapon_all must be called after mmd.enabled_aconfig system \"\n                  \"property is initialized\";\n        // Since we don't know whether mmd is enabled on the system or not, we fall back to enable\n        // zram from swapon_all conservatively. Both swapon_all and `mmd --setup-zram` command\n        // trying to set up zram does not break the system but just either ends up failing.\n    } else {\n        // We show the warning log for swapon_all deprecation on both aconfig_prop is \"true\" and \"\"\n        // cases.\n        // If mmd is enabled, swapon_all is already deprecated.\n        // If aconfig_prop is \"\", we don't know whether mmd is launched or not. But we show the\n        // deprecation warning log conservatively.\n        LWARNING << \"mmd is recommended to set up zram over swapon_all command with \"\n                    \"fstab entry.\";\n    }\n\n    return false;\n}\n\nbool fs_mgr_swapon_all(const Fstab& fstab) {\n    bool ret = true;\n    for (const auto& entry : fstab) {\n        // Skip non-swap entries.\n        if (entry.fs_type != \"swap\") {\n            continue;\n        }\n\n        if (entry.zram_size > 0) {\n            if (ShouldSkipZramSetup()) {\n                continue;\n            }\n\n            if (!PrepareZramBackingDevice(entry.zram_backingdev_size)) {\n                LERROR << \"Failure of zram backing device file for '\" << entry.blk_device << \"'\";\n            }\n            // A zram_size was specified, so we need to configure the\n            // device.  There is no point in having multiple zram devices\n            // on a system (all the memory comes from the same pool) so\n            // we can assume the device number is 0.\n            if (entry.max_comp_streams >= 0) {\n                auto zram_mcs_fp = std::unique_ptr<FILE, decltype(&fclose)>{\n                        fopen(ZRAM_CONF_MCS, \"re\"), fclose};\n                if (zram_mcs_fp == nullptr) {\n                    LERROR << \"Unable to open zram conf comp device \" << ZRAM_CONF_MCS;\n                    ret = false;\n                    continue;\n                }\n                fprintf(zram_mcs_fp.get(), \"%d\\n\", entry.max_comp_streams);\n            }\n\n            auto zram_fp =\n                    std::unique_ptr<FILE, decltype(&fclose)>{fopen(ZRAM_CONF_DEV, \"re+\"), fclose};\n            if (zram_fp == nullptr) {\n                LERROR << \"Unable to open zram conf device \" << ZRAM_CONF_DEV;\n                ret = false;\n                continue;\n            }\n            fprintf(zram_fp.get(), \"%\" PRId64 \"\\n\", entry.zram_size);\n        }\n\n        if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {\n            LERROR << \"Skipping mkswap for '\" << entry.blk_device << \"'\";\n            ret = false;\n            continue;\n        }\n\n        // Initialize the swap area.\n        const char* mkswap_argv[2] = {\n                MKSWAP_BIN,\n                entry.blk_device.c_str(),\n        };\n        int err = logwrap_fork_execvp(ARRAY_SIZE(mkswap_argv), mkswap_argv, nullptr, false,\n                                      LOG_KLOG, false, nullptr);\n        if (err) {\n            LERROR << \"mkswap failed for \" << entry.blk_device;\n            ret = false;\n            continue;\n        }\n\n        /* If -1, then no priority was specified in fstab, so don't set\n         * SWAP_FLAG_PREFER or encode the priority */\n        int flags = 0;\n        if (entry.swap_prio >= 0) {\n            flags = (entry.swap_prio << SWAP_FLAG_PRIO_SHIFT) & SWAP_FLAG_PRIO_MASK;\n            flags |= SWAP_FLAG_PREFER;\n        } else {\n            flags = 0;\n        }\n        err = swapon(entry.blk_device.c_str(), flags);\n        if (err) {\n            LERROR << \"swapon failed for \" << entry.blk_device;\n            ret = false;\n        }\n    }\n\n    return ret;\n}\n\nbool fs_mgr_is_verity_enabled(const FstabEntry& entry) {\n    if (!entry.fs_mgr_flags.avb) {\n        return false;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n\n    std::string mount_point = GetVerityDeviceName(entry);\n    if (dm.GetState(mount_point) == DmDeviceState::INVALID) {\n        return false;\n    }\n\n    std::vector<DeviceMapper::TargetInfo> table;\n    if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {\n        return false;\n    }\n\n    auto status = table[0].data.c_str();\n    if (*status == 'C' || *status == 'V') {\n        return true;\n    }\n\n    return false;\n}\n\nstd::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry) {\n    if (!entry.fs_mgr_flags.avb) {\n        return {};\n    }\n    DeviceMapper& dm = DeviceMapper::Instance();\n    std::string device = GetVerityDeviceName(entry);\n\n    std::vector<DeviceMapper::TargetInfo> table;\n    if (dm.GetState(device) == DmDeviceState::INVALID || !dm.GetTableInfo(device, &table)) {\n        return {};\n    }\n    for (const auto& target : table) {\n        if (strcmp(target.spec.target_type, \"verity\") != 0) {\n            continue;\n        }\n\n        // The format is stable for dm-verity version 0 & 1. And the data is expected to have\n        // the fixed format:\n        // <version> <dev> <hash_dev> <data_block_size> <hash_block_size> <num_data_blocks>\n        // <hash_start_block> <algorithm> <digest> <salt>\n        // Details in https://www.kernel.org/doc/html/latest/admin-guide/device-mapper/verity.html\n\n        std::vector<std::string> tokens = android::base::Split(target.data, \" \\t\\r\\n\");\n        if (tokens[0] != \"0\" && tokens[0] != \"1\") {\n            LOG(WARNING) << \"Unrecognized device mapper version in \" << target.data;\n        }\n\n        // Hashtree algorithm & root digest are the 8th & 9th token in the output.\n        return HashtreeInfo{\n                .algorithm = android::base::Trim(tokens[7]),\n                .root_digest = android::base::Trim(tokens[8]),\n                .check_at_most_once = target.data.find(\"check_at_most_once\") != std::string::npos};\n    }\n\n    return {};\n}\n\nbool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry) {\n    auto hashtree_info = fs_mgr_get_hashtree_info(entry);\n    if (!hashtree_info) return false;\n    return hashtree_info->check_at_most_once;\n}\n\nstd::string fs_mgr_get_super_partition_name(int slot) {\n    // Devices upgrading to dynamic partitions are allowed to specify a super\n    // partition name. This includes cuttlefish, which is a non-A/B device.\n    std::string super_partition;\n    if (fs_mgr_get_boot_config(\"force_super_partition\", &super_partition)) {\n        return super_partition;\n    }\n    if (fs_mgr_get_boot_config(\"super_partition\", &super_partition)) {\n        if (fs_mgr_get_slot_suffix().empty()) {\n            return super_partition;\n        }\n        std::string suffix;\n        if (slot == 0) {\n            suffix = \"_a\";\n        } else if (slot == 1) {\n            suffix = \"_b\";\n        } else if (slot == -1) {\n            suffix = fs_mgr_get_slot_suffix();\n        }\n        return super_partition + suffix;\n    }\n    return LP_METADATA_DEFAULT_PARTITION_NAME;\n}\n\nbool fs_mgr_create_canonical_mount_point(const std::string& mount_point) {\n    auto saved_errno = errno;\n    auto ok = true;\n    auto created_mount_point = !mkdir(mount_point.c_str(), 0755);\n    std::string real_mount_point;\n    if (!Realpath(mount_point, &real_mount_point)) {\n        ok = false;\n        PERROR << \"failed to realpath(\" << mount_point << \")\";\n    } else if (mount_point != real_mount_point) {\n        ok = false;\n        LERROR << \"mount point is not canonical: realpath(\" << mount_point << \") -> \"\n               << real_mount_point;\n    }\n    if (!ok && created_mount_point) {\n        rmdir(mount_point.c_str());\n    }\n    errno = saved_errno;\n    return ok;\n}\n\nbool fs_mgr_mount_overlayfs_fstab_entry(const FstabEntry& entry) {\n    const auto overlayfs_check_result = android::fs_mgr::CheckOverlayfs();\n    if (!overlayfs_check_result.supported) {\n        LERROR << __FUNCTION__ << \"(): kernel does not support overlayfs\";\n        return false;\n    }\n\n#if ALLOW_ADBD_DISABLE_VERITY == 0\n    // Allowlist the mount point if user build.\n    static const std::vector<std::string> kAllowedPaths = {\n            \"/odm\",         \"/odm_dlkm\",   \"/oem\",    \"/product\",\n            \"/system_dlkm\", \"/system_ext\", \"/vendor\", \"/vendor_dlkm\",\n    };\n    static const std::vector<std::string> kAllowedPrefixes = {\n            \"/mnt/product/\",\n            \"/mnt/vendor/\",\n    };\n    if (std::none_of(kAllowedPaths.begin(), kAllowedPaths.end(),\n                     [&entry](const auto& path) -> bool {\n                         return entry.mount_point == path ||\n                                StartsWith(entry.mount_point, path + \"/\");\n                     }) &&\n        std::none_of(kAllowedPrefixes.begin(), kAllowedPrefixes.end(),\n                     [&entry](const auto& prefix) -> bool {\n                         return entry.mount_point != prefix &&\n                                StartsWith(entry.mount_point, prefix);\n                     })) {\n        LERROR << __FUNCTION__\n               << \"(): mount point is forbidden on user build: \" << entry.mount_point;\n        return false;\n    }\n#endif  // ALLOW_ADBD_DISABLE_VERITY == 0\n\n    if (!fs_mgr_create_canonical_mount_point(entry.mount_point)) {\n        return false;\n    }\n\n    auto lowerdir = entry.lowerdir;\n    if (entry.fs_mgr_flags.overlayfs_remove_missing_lowerdir) {\n        bool removed_any = false;\n        std::vector<std::string> lowerdirs;\n        for (const auto& dir : android::base::Split(entry.lowerdir, \":\")) {\n            if (access(dir.c_str(), F_OK)) {\n                PWARNING << __FUNCTION__ << \"(): remove missing lowerdir '\" << dir << \"'\";\n                removed_any = true;\n            } else {\n                lowerdirs.push_back(dir);\n            }\n        }\n        if (removed_any) {\n            lowerdir = android::base::Join(lowerdirs, \":\");\n        }\n    }\n\n    const auto options = \"lowerdir=\" + lowerdir + overlayfs_check_result.mount_flags;\n\n    // Use \"overlay-\" + entry.blk_device as the mount() source, so that adb-remout-test don't\n    // confuse this with adb remount overlay, whose device name is \"overlay\".\n    // Overlayfs is a pseudo filesystem, so the source device is a symbolic value and isn't used to\n    // back the filesystem. However the device name would be shown in /proc/mounts.\n    auto source = \"overlay-\" + entry.blk_device;\n    auto report = \"__mount(source=\" + source + \",target=\" + entry.mount_point + \",type=overlay,\" +\n                  options + \")=\";\n    auto ret = mount(source.c_str(), entry.mount_point.c_str(), \"overlay\", MS_RDONLY | MS_NOATIME,\n                     options.c_str());\n    if (ret) {\n        PERROR << report << ret;\n        return false;\n    }\n    LINFO << report << ret;\n    return true;\n}\n\nbool fs_mgr_load_verity_state(int* mode) {\n    // unless otherwise specified, use EIO mode.\n    *mode = VERITY_MODE_EIO;\n\n    // The bootloader communicates verity mode via the kernel commandline\n    std::string verity_mode;\n    if (!fs_mgr_get_boot_config(\"veritymode\", &verity_mode)) {\n        return false;\n    }\n\n    if (verity_mode == \"enforcing\") {\n        *mode = VERITY_MODE_DEFAULT;\n    } else if (verity_mode == \"logging\") {\n        *mode = VERITY_MODE_LOGGING;\n    }\n\n    return true;\n}\n\nbool fs_mgr_filesystem_available(const std::string& filesystem) {\n    std::string filesystems;\n    if (!android::base::ReadFileToString(\"/proc/filesystems\", &filesystems)) return false;\n    return filesystems.find(\"\\t\" + filesystem + \"\\n\") != std::string::npos;\n}\n\nstd::string fs_mgr_get_context(const std::string& mount_point) {\n    char* ctx = nullptr;\n    if (getfilecon(mount_point.c_str(), &ctx) == -1) {\n        PERROR << \"getfilecon \" << mount_point;\n        return \"\";\n    }\n\n    std::string context(ctx);\n    free(ctx);\n    return context;\n}\n\nint fs_mgr_f2fs_ideal_block_size() {\n#if defined(__i386__) || defined(__x86_64__)\n    return 4096;\n#else\n    return getpagesize();\n#endif\n}\n\nnamespace android {\nnamespace fs_mgr {\n\nOverlayfsCheckResult CheckOverlayfs() {\n    if (!fs_mgr_filesystem_available(\"overlay\")) {\n        return {.supported = false};\n    }\n\n    struct utsname uts;\n    if (uname(&uts) == -1) {\n        return {.supported = false};\n    }\n    int major, minor;\n    if (sscanf(uts.release, \"%d.%d\", &major, &minor) != 2) {\n        return {.supported = false};\n    }\n\n    if (!use_override_creds) {\n        if (major > 5 || (major == 5 && minor >= 15)) {\n            return {.supported = true, \",userxattr\"};\n        }\n        return {.supported = true};\n    }\n\n    // Overlayfs available in the kernel, and patched for override_creds?\n    if (access(\"/sys/module/overlay/parameters/override_creds\", F_OK) == 0) {\n        auto mount_flags = \",override_creds=off\"s;\n        if (major > 5 || (major == 5 && minor >= 15)) {\n            mount_flags += \",userxattr\"s;\n        }\n        return {.supported = true, .mount_flags = mount_flags};\n    }\n    if (major < 4 || (major == 4 && minor <= 3)) {\n        return {.supported = true};\n    }\n    return {.supported = false};\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr_dm_linear.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use, copy,\n * modify, merge, publish, distribute, sublicense, and/or sell copies\n * of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include \"fs_mgr_dm_linear.h\"\n\n#include <inttypes.h>\n#include <linux/dm-ioctl.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <sstream>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <fs_mgr/file_wait.h>\n#include <liblp/reader.h>\n\n#include \"fs_mgr_priv.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nusing DeviceMapper = android::dm::DeviceMapper;\nusing DmTable = android::dm::DmTable;\nusing DmTarget = android::dm::DmTarget;\nusing DmTargetZero = android::dm::DmTargetZero;\nusing DmTargetLinear = android::dm::DmTargetLinear;\n\nstatic bool GetPhysicalPartitionDevicePath(const CreateLogicalPartitionParams& params,\n                                           const LpMetadataBlockDevice& block_device,\n                                           const std::string& super_device, std::string* result) {\n    // If the super device is the source of this block device's metadata,\n    // make sure we use the correct super device (and not just \"super\",\n    // which might not exist.)\n    std::string name = GetBlockDevicePartitionName(block_device);\n    if (android::base::StartsWith(name, \"dm-\")) {\n        // Device-mapper nodes are not normally allowed in LpMetadata, since\n        // they are not consistent across reboots. However for the purposes of\n        // testing it's useful to handle them. For example when running DSUs,\n        // userdata is a device-mapper device, and some stacking will result\n        // when using libfiemap.\n        *result = \"/dev/block/\" + name;\n        return true;\n    }\n\n    auto opener = params.partition_opener;\n    std::string dev_string = opener->GetDeviceString(name);\n    if (GetMetadataSuperBlockDevice(*params.metadata) == &block_device) {\n        dev_string = opener->GetDeviceString(super_device);\n    }\n\n    // Note: device-mapper will not accept symlinks, so we must use realpath\n    // here. If the device string is a major:minor sequence, we don't need to\n    // to call Realpath (it would not work anyway).\n    if (android::base::StartsWith(dev_string, \"/\")) {\n        if (!android::base::Realpath(dev_string, result)) {\n            PERROR << \"realpath: \" << dev_string;\n            return false;\n        }\n    } else {\n        *result = dev_string;\n    }\n    return true;\n}\n\nbool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) {\n    const auto& super_device = params.block_device;\n\n    uint64_t sector = 0;\n    for (size_t i = 0; i < params.partition->num_extents; i++) {\n        const auto& extent = params.metadata->extents[params.partition->first_extent_index + i];\n        std::unique_ptr<DmTarget> target;\n        switch (extent.target_type) {\n            case LP_TARGET_TYPE_ZERO:\n                target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);\n                break;\n            case LP_TARGET_TYPE_LINEAR: {\n                const auto& block_device = params.metadata->block_devices[extent.target_source];\n                std::string dev_string;\n                if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,\n                                                    &dev_string)) {\n                    LOG(ERROR) << \"Unable to complete device-mapper table, unknown block device\";\n                    return false;\n                }\n                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string,\n                                                          extent.target_data);\n                break;\n            }\n            default:\n                LOG(ERROR) << \"Unknown target type in metadata: \" << extent.target_type;\n                return false;\n        }\n        if (!table->AddTarget(std::move(target))) {\n            return false;\n        }\n        sector += extent.num_sectors;\n    }\n    if (params.partition->attributes & LP_PARTITION_ATTR_READONLY) {\n        table->set_readonly(true);\n    }\n    if (params.force_writable) {\n        table->set_readonly(false);\n    }\n    return true;\n}\n\nbool CreateDmTable(CreateLogicalPartitionParams params, DmTable* table) {\n    CreateLogicalPartitionParams::OwnedData owned_data;\n    if (!params.InitDefaults(&owned_data)) return false;\n    return CreateDmTableInternal(params, table);\n}\n\nbool CreateLogicalPartitions(const std::string& block_device) {\n    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());\n    auto metadata = ReadMetadata(block_device.c_str(), slot);\n    if (!metadata) {\n        LOG(ERROR) << \"Could not read partition table.\";\n        return true;\n    }\n    return CreateLogicalPartitions(*metadata.get(), block_device);\n}\n\nstd::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device) {\n    uint32_t slot = SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());\n    return ReadMetadata(block_device.c_str(), slot);\n}\n\nbool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {\n    CreateLogicalPartitionParams params = {\n            .block_device = super_device,\n            .metadata = &metadata,\n    };\n    for (const auto& partition : metadata.partitions) {\n        if (!partition.num_extents) {\n            LINFO << \"Skipping zero-length logical partition: \" << GetPartitionName(partition);\n            continue;\n        }\n        if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {\n            LINFO << \"Skipping disabled partition: \" << GetPartitionName(partition);\n            continue;\n        }\n\n        params.partition = &partition;\n\n        std::string ignore_path;\n        if (!CreateLogicalPartition(params, &ignore_path)) {\n            LERROR << \"Could not create logical partition: \" << GetPartitionName(partition);\n            return false;\n        }\n    }\n    return true;\n}\n\nbool CreateLogicalPartitionParams::InitDefaults(CreateLogicalPartitionParams::OwnedData* owned) {\n    if (block_device.empty()) {\n        LOG(ERROR) << \"block_device is required for CreateLogicalPartition\";\n        return false;\n    }\n\n    if (!partition_opener) {\n        owned->partition_opener = std::make_unique<PartitionOpener>();\n        partition_opener = owned->partition_opener.get();\n    }\n\n    // Read metadata if needed.\n    if (!metadata) {\n        if (!metadata_slot) {\n            LOG(ERROR) << \"Either metadata or a metadata slot must be specified.\";\n            return false;\n        }\n        auto slot = *metadata_slot;\n        if (owned->metadata = ReadMetadata(*partition_opener, block_device, slot);\n            !owned->metadata) {\n            LOG(ERROR) << \"Could not read partition table for: \" << block_device;\n            return false;\n        }\n        metadata = owned->metadata.get();\n    }\n\n    // Find the partition by name if needed.\n    if (!partition) {\n        for (const auto& metadata_partition : metadata->partitions) {\n            if (android::fs_mgr::GetPartitionName(metadata_partition) == partition_name) {\n                partition = &metadata_partition;\n                break;\n            }\n        }\n    }\n    if (!partition) {\n        LERROR << \"Could not find any partition with name: \" << partition_name;\n        return false;\n    }\n    if (partition_name.empty()) {\n        partition_name = android::fs_mgr::GetPartitionName(*partition);\n    } else if (partition_name != android::fs_mgr::GetPartitionName(*partition)) {\n        LERROR << \"Inconsistent partition_name \" << partition_name << \" with partition \"\n               << android::fs_mgr::GetPartitionName(*partition);\n        return false;\n    }\n\n    if (device_name.empty()) {\n        device_name = partition_name;\n    }\n\n    return true;\n}\n\nbool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {\n    CreateLogicalPartitionParams::OwnedData owned_data;\n    if (!params.InitDefaults(&owned_data)) return false;\n\n    DmTable table;\n    if (!CreateDmTableInternal(params, &table)) {\n        return false;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {\n        return false;\n    }\n    LINFO << \"Created logical partition \" << params.device_name << \" on device \" << *path;\n    return true;\n}\n\nstd::string CreateLogicalPartitionParams::GetDeviceName() const {\n    if (!device_name.empty()) return device_name;\n    return GetPartitionName();\n}\n\nstd::string CreateLogicalPartitionParams::GetPartitionName() const {\n    if (!partition_name.empty()) return partition_name;\n    if (partition) return android::fs_mgr::GetPartitionName(*partition);\n    return \"<unknown partition>\";\n}\n\nbool UnmapDevice(const std::string& name) {\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.DeleteDevice(name)) {\n        return false;\n    }\n    return true;\n}\n\nbool DestroyLogicalPartition(const std::string& name) {\n    if (!UnmapDevice(name)) {\n        return false;\n    }\n    LINFO << \"Unmapped logical partition \" << name;\n    return true;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr_format.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <unistd.h>\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n#include <sys/wait.h>\n#include <errno.h>\n#include <cutils/partition_utils.h>\n#include <sys/mount.h>\n\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n#include <ext4_utils/ext4.h>\n#include <ext4_utils/ext4_utils.h>\n#include <logwrap/logwrap.h>\n#include <selinux/android.h>\n#include <selinux/label.h>\n#include <selinux/selinux.h>\n#include <filesystem>\n#include <string>\n\n#include \"fs_mgr_priv.h\"\n\nusing android::base::unique_fd;\n\n// Realistically, this file should be part of the android::fs_mgr namespace;\nusing namespace android::fs_mgr;\n\nstatic int get_dev_sz(const std::string& fs_blkdev, uint64_t* dev_sz) {\n    unique_fd fd(TEMP_FAILURE_RETRY(open(fs_blkdev.c_str(), O_RDONLY | O_CLOEXEC)));\n\n    if (fd < 0) {\n        PERROR << \"Cannot open block device\";\n        return -1;\n    }\n\n    if ((ioctl(fd, BLKGETSIZE64, dev_sz)) == -1) {\n        PERROR << \"Cannot get block device size\";\n        return -1;\n    }\n\n    return 0;\n}\n\nstatic int format_ext4(const std::string& fs_blkdev, const std::string& fs_mnt_point,\n                       bool needs_projid, bool needs_metadata_csum) {\n    uint64_t dev_sz;\n    int rc = 0;\n\n    rc = get_dev_sz(fs_blkdev, &dev_sz);\n    if (rc) {\n        return rc;\n    }\n\n    /* Format the partition using the calculated length */\n\n    // EXT4 supports 4K block size on 16K page sizes. A 4K block\n    // size formatted EXT4 partition will mount fine on both 4K and 16K page\n    // size kernels.\n    // However, EXT4 does not support 16K block size on 4K systems.\n    // If we want the same userspace code to work on both 4k/16k kernels,\n    // using a hardcoded 4096 block size is a simple solution. Using\n    // getpagesize() here would work as well, but 4096 is simpler.\n    std::string size_str = std::to_string(dev_sz / 4096);\n\n    std::vector<const char*> mke2fs_args = {\"/system/bin/mke2fs\", \"-t\", \"ext4\", \"-b\", \"4096\"};\n\n    // Project ID's require wider inodes. The Quotas themselves are enabled by tune2fs during boot.\n    if (needs_projid) {\n        mke2fs_args.push_back(\"-I\");\n        mke2fs_args.push_back(\"512\");\n    }\n    // casefolding is enabled via tune2fs during boot.\n\n    if (needs_metadata_csum) {\n        mke2fs_args.push_back(\"-O\");\n        mke2fs_args.push_back(\"metadata_csum\");\n        // tune2fs recommends to enable 64bit and extent:\n        //  Extents are not enabled.  The file extent tree can be checksummed,\n        //  whereas block maps cannot. Not enabling extents reduces the coverage\n        //  of metadata checksumming.  Re-run with -O extent to rectify.\n        //  64-bit filesystem support is not enabled.  The larger fields afforded\n        //  by this feature enable full-strength checksumming.  Run resize2fs -b to rectify.\n        mke2fs_args.push_back(\"-O\");\n        mke2fs_args.push_back(\"64bit\");\n        mke2fs_args.push_back(\"-O\");\n        mke2fs_args.push_back(\"extent\");\n    }\n\n    mke2fs_args.push_back(fs_blkdev.c_str());\n    mke2fs_args.push_back(size_str.c_str());\n\n    rc = logwrap_fork_execvp(mke2fs_args.size(), mke2fs_args.data(), nullptr, false, LOG_KLOG,\n                             false, nullptr);\n    if (rc) {\n        LERROR << \"mke2fs returned \" << rc;\n        return rc;\n    }\n\n    const char* const e2fsdroid_args[] = {\n            \"/system/bin/e2fsdroid\", \"-e\", \"-a\", fs_mnt_point.c_str(), fs_blkdev.c_str(), nullptr};\n\n    rc = logwrap_fork_execvp(arraysize(e2fsdroid_args), e2fsdroid_args, nullptr, false, LOG_KLOG,\n                             false, nullptr);\n    if (rc) {\n        LERROR << \"e2fsdroid returned \" << rc;\n    }\n\n    return rc;\n}\n\nstatic int format_f2fs(const std::string& fs_blkdev, uint64_t dev_sz, bool needs_projid,\n                       bool needs_casefold, bool fs_compress, bool is_zoned,\n                       const std::vector<std::string>& user_devices,\n                       const std::vector<int>& device_aliased) {\n    if (!dev_sz) {\n        int rc = get_dev_sz(fs_blkdev, &dev_sz);\n        if (rc) {\n            return rc;\n        }\n    }\n\n    /* Format the partition using the calculated length */\n\n    const auto size_str = std::to_string(dev_sz / getpagesize());\n    std::string block_size = std::to_string(getpagesize());\n\n    std::vector<const char*> args = {\"/system/bin/make_f2fs\", \"-g\", \"android\"};\n    if (needs_projid) {\n        args.push_back(\"-O\");\n        args.push_back(\"project_quota,extra_attr\");\n    }\n    if (needs_casefold) {\n        args.push_back(\"-O\");\n        args.push_back(\"casefold\");\n        args.push_back(\"-C\");\n        args.push_back(\"utf8\");\n    }\n    if (fs_compress) {\n        args.push_back(\"-O\");\n        args.push_back(\"compression\");\n        args.push_back(\"-O\");\n        args.push_back(\"extra_attr\");\n    }\n    args.push_back(\"-w\");\n    args.push_back(block_size.c_str());\n    args.push_back(\"-b\");\n    args.push_back(block_size.c_str());\n\n    if (is_zoned) {\n        args.push_back(\"-m\");\n    }\n    for (size_t i = 0; i < user_devices.size(); i++) {\n        std::string device_name = user_devices[i];\n\n        args.push_back(\"-c\");\n        if (device_aliased[i]) {\n            std::filesystem::path path = device_name;\n            device_name += \"@\" + path.filename().string();\n        }\n        args.push_back(device_name.c_str());\n    }\n\n    if (user_devices.empty()) {\n        args.push_back(fs_blkdev.c_str());\n        args.push_back(size_str.c_str());\n    } else {\n        args.push_back(fs_blkdev.c_str());\n    }\n    return logwrap_fork_execvp(args.size(), args.data(), nullptr, false, LOG_KLOG, false, nullptr);\n}\n\nint fs_mgr_do_format(const FstabEntry& entry) {\n    LERROR << __FUNCTION__ << \": Format \" << entry.blk_device << \" as '\" << entry.fs_type << \"'\";\n\n    bool needs_casefold = false;\n    bool needs_projid = true;\n\n    if (entry.mount_point == \"/data\") {\n        needs_casefold = android::base::GetBoolProperty(\"external_storage.casefold.enabled\", false);\n    }\n\n    if (entry.fs_type == \"f2fs\") {\n        return format_f2fs(entry.blk_device, entry.length, needs_projid, needs_casefold,\n                           entry.fs_mgr_flags.fs_compress, entry.fs_mgr_flags.is_zoned,\n                           entry.user_devices, entry.device_aliased);\n    } else if (entry.fs_type == \"ext4\") {\n        return format_ext4(entry.blk_device, entry.mount_point, needs_projid,\n                           entry.fs_mgr_flags.ext_meta_csum);\n    } else {\n        LERROR << \"File system type '\" << entry.fs_type << \"' is not supported\";\n        return -EINVAL;\n    }\n}\n"
  },
  {
    "path": "fs_mgr/fs_mgr_overlayfs_control.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n#include <fs_mgr_dm_linear.h>\n#include <fs_mgr_overlayfs.h>\n#include <fstab/fstab.h>\n#include <libdm/dm.h>\n#include <libfiemap/image_manager.h>\n#include <libgsi/libgsi.h>\n#include <liblp/builder.h>\n#include <liblp/liblp.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"fs_mgr_overlayfs_control.h\"\n#include \"fs_mgr_overlayfs_mount.h\"\n#include \"fs_mgr_priv.h\"\n#include \"libfiemap/utility.h\"\n\nusing namespace std::literals;\nusing namespace android::dm;\nusing namespace android::fs_mgr;\nusing namespace android::storage_literals;\nusing android::fiemap::FilesystemHasReliablePinning;\nusing android::fiemap::IImageManager;\n\nnamespace {\n\nconstexpr char kDataScratchSizeMbProp[] = \"fs_mgr.overlayfs.data_scratch_size_mb\";\n\nconstexpr char kPhysicalDevice[] = \"/dev/block/by-name/\";\nconstexpr char kScratchImageMetadata[] = \"/metadata/gsi/remount/lp_metadata\";\n\nconstexpr char kMkF2fs[] = \"/system/bin/make_f2fs\";\nconstexpr char kMkExt4[] = \"/system/bin/mke2fs\";\n\n// Return true if everything is mounted, but before adb is started.  Right\n// after 'trigger load_persist_props_action' is done.\nstatic bool fs_mgr_boot_completed() {\n    return android::base::GetBoolProperty(\"ro.persistent_properties.ready\", false);\n}\n\n// Note: this is meant only for recovery/first-stage init.\nstatic bool ScratchIsOnData() {\n    // The scratch partition of DSU is managed by gsid.\n    if (fs_mgr_is_dsu_running()) {\n        return false;\n    }\n    return access(kScratchImageMetadata, F_OK) == 0;\n}\n\nstatic bool fs_mgr_rm_all(const std::string& path, bool* change = nullptr, int level = 0) {\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path.c_str()), closedir);\n    if (!dir) {\n        if (errno == ENOENT) {\n            return true;\n        }\n        PERROR << \"opendir \" << path << \" depth=\" << level;\n        if ((errno == EPERM) && (level != 0)) {\n            return true;\n        }\n        return false;\n    }\n    dirent* entry;\n    auto ret = true;\n    while ((entry = readdir(dir.get()))) {\n        if ((\".\"s == entry->d_name) || (\"..\"s == entry->d_name)) continue;\n        auto file = path + \"/\" + entry->d_name;\n        if (entry->d_type == DT_UNKNOWN) {\n            struct stat st;\n            if (!lstat(file.c_str(), &st) && (st.st_mode & S_IFDIR)) entry->d_type = DT_DIR;\n        }\n        if (entry->d_type == DT_DIR) {\n            ret &= fs_mgr_rm_all(file, change, level + 1);\n            if (!rmdir(file.c_str())) {\n                if (change) *change = true;\n            } else {\n                if (errno != ENOENT) ret = false;\n                PERROR << \"rmdir \" << file << \" depth=\" << level;\n            }\n            continue;\n        }\n        if (!unlink(file.c_str())) {\n            if (change) *change = true;\n        } else {\n            if (errno != ENOENT) ret = false;\n            PERROR << \"rm \" << file << \" depth=\" << level;\n        }\n    }\n    return ret;\n}\n\nstd::string fs_mgr_overlayfs_setup_dir(const std::string& dir) {\n    auto top = dir + \"/\" + kOverlayTopDir;\n\n    AutoSetFsCreateCon createcon(kOverlayfsFileContext);\n    if (!createcon.Ok()) {\n        return {};\n    }\n    if (mkdir(top.c_str(), 0755) != 0 && errno != EEXIST) {\n        PERROR << \"mkdir \" << top;\n        return {};\n    }\n    if (!createcon.Restore()) {\n        return {};\n    }\n    return top;\n}\n\nbool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,\n                                bool* want_reboot) {\n    if (fs_mgr_overlayfs_already_mounted(mount_point)) {\n        return true;\n    }\n    const auto base = GetEncodedBaseDirForMountPoint(mount_point);\n    auto fsrec_mount_point = overlay + \"/\" + base + \"/\";\n\n    AutoSetFsCreateCon createcon(kOverlayfsFileContext);\n    if (!createcon.Ok()) {\n        return false;\n    }\n    if (mkdir(fsrec_mount_point.c_str(), 0755) != 0 && errno != EEXIST) {\n        PERROR << \"mkdir \" << fsrec_mount_point;\n        return false;\n    }\n    if (mkdir((fsrec_mount_point + kWorkName).c_str(), 0755) != 0 && errno != EEXIST) {\n        PERROR << \"mkdir \" << fsrec_mount_point << kWorkName;\n        return false;\n    }\n    if (!createcon.Restore()) {\n        return false;\n    }\n\n    createcon = {};\n\n    auto new_context = fs_mgr_get_context(mount_point);\n    if (new_context.empty() || !createcon.Set(new_context)) {\n        return false;\n    }\n\n    auto upper = fsrec_mount_point + kUpperName;\n    if (mkdir(upper.c_str(), 0755) != 0 && errno != EEXIST) {\n        PERROR << \"mkdir \" << upper;\n        return false;\n    }\n    if (!createcon.Restore()) {\n        return false;\n    }\n\n    if (want_reboot) *want_reboot = true;\n\n    return true;\n}\n\nstatic uint32_t fs_mgr_overlayfs_slot_number() {\n    return SlotNumberForSlotSuffix(fs_mgr_get_slot_suffix());\n}\n\nstatic bool fs_mgr_overlayfs_has_logical(const Fstab& fstab) {\n    for (const auto& entry : fstab) {\n        if (entry.fs_mgr_flags.logical) {\n            return true;\n        }\n    }\n    return false;\n}\n\nOverlayfsTeardownResult TeardownDataScratch(IImageManager* images,\n                                            const std::string& partition_name, bool was_mounted) {\n    if (!images) {\n        return OverlayfsTeardownResult::Error;\n    }\n    if (!images->DisableImage(partition_name)) {\n        return OverlayfsTeardownResult::Error;\n    }\n    if (was_mounted) {\n        // If overlayfs was mounted, don't bother trying to unmap since\n        // it'll fail and create error spam.\n        return OverlayfsTeardownResult::Busy;\n    }\n    if (!images->UnmapImageIfExists(partition_name)) {\n        return OverlayfsTeardownResult::Busy;\n    }\n    if (!images->DeleteBackingImage(partition_name)) {\n        return OverlayfsTeardownResult::Busy;\n    }\n    return OverlayfsTeardownResult::Ok;\n}\n\nbool GetOverlaysActiveFlag() {\n    auto slot_number = fs_mgr_overlayfs_slot_number();\n    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n\n    auto metadata = ReadMetadata(super_device, slot_number);\n    if (!metadata) {\n        return false;\n    }\n    return !!(metadata->header.flags & LP_HEADER_FLAG_OVERLAYS_ACTIVE);\n}\n\nbool SetOverlaysActiveFlag(bool flag) {\n    // Mark overlays as active in the partition table, to detect re-flash.\n    auto slot_number = fs_mgr_overlayfs_slot_number();\n    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n    auto builder = MetadataBuilder::New(super_device, slot_number);\n    if (!builder) {\n        LERROR << \"open \" << super_device << \" metadata\";\n        return false;\n    }\n    builder->SetOverlaysActiveFlag(flag);\n    auto metadata = builder->Export();\n    if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {\n        LERROR << \"update super metadata\";\n        return false;\n    }\n    return true;\n}\n\nOverlayfsTeardownResult fs_mgr_overlayfs_teardown_scratch(const std::string& overlay,\n                                                          bool* change) {\n    // umount and delete kScratchMountPoint storage if we have logical partitions\n    if (overlay != kScratchMountPoint) {\n        return OverlayfsTeardownResult::Ok;\n    }\n\n    // Validation check.\n    if (fs_mgr_is_dsu_running()) {\n        LERROR << \"Destroying DSU scratch is not allowed.\";\n        return OverlayfsTeardownResult::Error;\n    }\n\n    // Note: we don't care if SetOverlaysActiveFlag fails, since\n    // the overlays are removed no matter what.\n    SetOverlaysActiveFlag(false);\n\n    bool was_mounted = fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);\n    if (was_mounted) {\n        fs_mgr_overlayfs_umount_scratch();\n    }\n\n    const auto partition_name = android::base::Basename(kScratchMountPoint);\n\n    auto images = IImageManager::Open(\"remount\", 10s);\n    if (images && images->BackingImageExists(partition_name)) {\n        // No need to check super partition, if we knew we had a scratch device\n        // in /data.\n        return TeardownDataScratch(images.get(), partition_name, was_mounted);\n    }\n\n    auto slot_number = fs_mgr_overlayfs_slot_number();\n    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n    if (access(super_device.c_str(), R_OK | W_OK)) {\n        return OverlayfsTeardownResult::Ok;\n    }\n\n    auto builder = MetadataBuilder::New(super_device, slot_number);\n    if (!builder) {\n        return OverlayfsTeardownResult::Ok;\n    }\n    if (builder->FindPartition(partition_name) == nullptr) {\n        return OverlayfsTeardownResult::Ok;\n    }\n    builder->RemovePartition(partition_name);\n    auto metadata = builder->Export();\n    if (metadata && UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {\n        if (change) *change = true;\n        if (!DestroyLogicalPartition(partition_name)) {\n            return OverlayfsTeardownResult::Error;\n        }\n    } else {\n        LERROR << \"delete partition \" << overlay;\n        return OverlayfsTeardownResult::Error;\n    }\n\n    if (was_mounted) {\n        return OverlayfsTeardownResult::Busy;\n    }\n    return OverlayfsTeardownResult::Ok;\n}\n\nbool fs_mgr_overlayfs_teardown_one(const std::string& overlay, const std::string& mount_point,\n                                   bool* change, bool* should_destroy_scratch = nullptr) {\n    const auto top = overlay + \"/\" + kOverlayTopDir;\n\n    if (access(top.c_str(), F_OK)) {\n        if (should_destroy_scratch) *should_destroy_scratch = true;\n        return true;\n    }\n\n    auto cleanup_all = mount_point.empty();\n    const auto base = GetEncodedBaseDirForMountPoint(mount_point);\n    const auto oldpath = top + (cleanup_all ? \"\" : (\"/\" + base));\n    const auto newpath = cleanup_all ? overlay + \"/.\" + kOverlayTopDir + \".teardown\"\n                                     : top + \"/.\" + base + \".teardown\";\n    auto ret = fs_mgr_rm_all(newpath);\n    if (!rename(oldpath.c_str(), newpath.c_str())) {\n        if (change) *change = true;\n    } else if (errno != ENOENT) {\n        ret = false;\n        PERROR << \"mv \" << oldpath << \" \" << newpath;\n    }\n    ret &= fs_mgr_rm_all(newpath, change);\n    if (!rmdir(newpath.c_str())) {\n        if (change) *change = true;\n    } else if (errno != ENOENT) {\n        ret = false;\n        PERROR << \"rmdir \" << newpath;\n    }\n    if (!cleanup_all) {\n        if (!rmdir(top.c_str())) {\n            if (change) *change = true;\n            cleanup_all = true;\n        } else if (errno == ENOTEMPTY) {\n            cleanup_all = true;\n            // cleanup all if the content is all hidden (leading .)\n            std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(top.c_str()), closedir);\n            if (!dir) {\n                PERROR << \"opendir \" << top;\n            } else {\n                dirent* entry;\n                while ((entry = readdir(dir.get()))) {\n                    if (entry->d_name[0] != '.') {\n                        cleanup_all = false;\n                        break;\n                    }\n                }\n            }\n        } else if (errno == ENOENT) {\n            cleanup_all = true;\n        } else {\n            ret = false;\n            PERROR << \"rmdir \" << top;\n        }\n    }\n    if (should_destroy_scratch) *should_destroy_scratch = cleanup_all;\n    return ret;\n}\n\n// Note: The scratch partition of DSU is managed by gsid, and should be initialized during\n// first-stage-mount. Just check if the DM device for DSU scratch partition is created or not.\nstatic std::string GetDsuScratchDevice() {\n    auto& dm = DeviceMapper::Instance();\n    std::string device;\n    if (dm.GetState(android::gsi::kDsuScratch) != DmDeviceState::INVALID &&\n        dm.GetDmDevicePathByName(android::gsi::kDsuScratch, &device)) {\n        return device;\n    }\n    return \"\";\n}\n\nbool MakeScratchFilesystem(const std::string& scratch_device) {\n    // Force mkfs by design for overlay support of adb remount, simplify and\n    // thus do not rely on fsck to correct problems that could creep in.\n    auto fs_type = \"\"s;\n    auto command = \"\"s;\n    if (!access(kMkF2fs, X_OK) && fs_mgr_filesystem_available(\"f2fs\")) {\n        fs_type = \"f2fs\";\n        command = kMkF2fs + \" -b \"s;\n        command += std::to_string(fs_mgr_f2fs_ideal_block_size());\n        command += \" -f -d1 -l\" + android::base::Basename(kScratchMountPoint);\n    } else if (!access(kMkExt4, X_OK) && fs_mgr_filesystem_available(\"ext4\")) {\n        fs_type = \"ext4\";\n        command = kMkExt4 + \" -F -b 4096 -t ext4 -m 0 -O has_journal -M \"s + kScratchMountPoint;\n    } else {\n        LERROR << \"No supported mkfs command or filesystem driver available, supported filesystems \"\n                  \"are: f2fs, ext4\";\n        return false;\n    }\n    command += \" \" + scratch_device + \" >/dev/null 2>/dev/null </dev/null\";\n    fs_mgr_set_blk_ro(scratch_device, false);\n    auto ret = system(command.c_str());\n    if (ret) {\n        LERROR << \"make \" << fs_type << \" filesystem on \" << scratch_device << \" return=\" << ret;\n        return false;\n    }\n    return true;\n}\n\nstatic void TruncatePartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) {\n    auto& dm = DeviceMapper::Instance();\n\n    // Remove <other> partitions\n    for (const auto& group : builder->ListGroups()) {\n        for (const auto& part : builder->ListPartitionsInGroup(group)) {\n            const auto& name = part->name();\n            if (!android::base::EndsWith(name, suffix)) {\n                continue;\n            }\n            if (dm.GetState(name) != DmDeviceState::INVALID && !DestroyLogicalPartition(name)) {\n                continue;\n            }\n            builder->ResizePartition(builder->FindPartition(name), 0);\n        }\n    }\n}\n\n// Create or update a scratch partition within super.\nstatic bool CreateDynamicScratch(std::string* scratch_device, bool* partition_exists) {\n    const auto partition_name = android::base::Basename(kScratchMountPoint);\n\n    auto& dm = DeviceMapper::Instance();\n    *partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;\n\n    auto partition_create = !*partition_exists;\n    auto slot_number = fs_mgr_overlayfs_slot_number();\n    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n    auto builder = MetadataBuilder::New(super_device, slot_number);\n    if (!builder) {\n        LERROR << \"open \" << super_device << \" metadata\";\n        return false;\n    }\n    auto partition = builder->FindPartition(partition_name);\n    *partition_exists = partition != nullptr;\n    auto changed = false;\n    if (!*partition_exists) {\n        partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);\n        if (!partition) {\n            LERROR << \"create \" << partition_name;\n            return false;\n        }\n        changed = true;\n    }\n    // Take half of free space, minimum 512MB or maximum free - margin.\n    static constexpr auto kMinimumSize = uint64_t(512 * 1024 * 1024);\n    if (partition->size() < kMinimumSize) {\n        auto partition_size =\n                builder->AllocatableSpace() - builder->UsedSpace() + partition->size();\n        if ((partition_size > kMinimumSize) || !partition->size()) {\n            partition_size = std::max(std::min(kMinimumSize, partition_size), partition_size / 2);\n            if (partition_size > partition->size()) {\n                if (!builder->ResizePartition(partition, partition_size)) {\n                    // Try to free up space by deallocating partitions in the other slot.\n                    TruncatePartitionsWithSuffix(builder.get(), fs_mgr_get_other_slot_suffix());\n\n                    partition_size =\n                            builder->AllocatableSpace() - builder->UsedSpace() + partition->size();\n                    partition_size =\n                            std::max(std::min(kMinimumSize, partition_size), partition_size / 2);\n                    if (!builder->ResizePartition(partition, partition_size)) {\n                        LERROR << \"resize \" << partition_name;\n                        return false;\n                    }\n                }\n                if (!partition_create) DestroyLogicalPartition(partition_name);\n                changed = true;\n                *partition_exists = false;\n            }\n        }\n    }\n\n    // land the update back on to the partition\n    if (changed) {\n        auto metadata = builder->Export();\n        if (!metadata || !UpdatePartitionTable(super_device, *metadata.get(), slot_number)) {\n            LERROR << \"add partition \" << partition_name;\n            return false;\n        }\n    }\n\n    if (changed || partition_create) {\n        CreateLogicalPartitionParams params = {\n                .block_device = super_device,\n                .metadata_slot = slot_number,\n                .partition_name = partition_name,\n                .force_writable = true,\n                .timeout_ms = 10s,\n        };\n        if (!CreateLogicalPartition(params, scratch_device)) {\n            return false;\n        }\n    } else if (scratch_device->empty()) {\n        *scratch_device = GetBootScratchDevice();\n    }\n    return true;\n}\n\nstatic inline uint64_t GetIdealDataScratchSize() {\n    BlockDeviceInfo super_info;\n    PartitionOpener opener;\n    if (!opener.GetInfo(fs_mgr_get_super_partition_name(), &super_info)) {\n        LERROR << \"could not get block device info for super\";\n        return 0;\n    }\n\n    struct statvfs s;\n    if (statvfs(\"/data\", &s) < 0) {\n        PERROR << \"could not statfs /data\";\n        return 0;\n    }\n\n    auto ideal_size = std::min(super_info.size, uint64_t(uint64_t(s.f_frsize) * s.f_bfree * 0.85));\n\n    // Align up to the filesystem block size.\n    if (auto remainder = ideal_size % s.f_bsize; remainder > 0) {\n        ideal_size += s.f_bsize - remainder;\n    }\n    return ideal_size;\n}\n\nstatic bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists) {\n    *partition_exists = false;\n\n    auto images = IImageManager::Open(\"remount\", 10s);\n    if (!images) {\n        return false;\n    }\n\n    auto partition_name = android::base::Basename(kScratchMountPoint);\n    if (images->GetMappedImageDevice(partition_name, scratch_device)) {\n        *partition_exists = true;\n        return true;\n    }\n\n    // Note: calling RemoveDisabledImages here ensures that we do not race with\n    // clean_scratch_files and accidentally try to map an image that will be\n    // deleted.\n    if (!images->RemoveDisabledImages()) {\n        return false;\n    }\n    if (!images->BackingImageExists(partition_name)) {\n        auto size = android::base::GetUintProperty<uint64_t>(kDataScratchSizeMbProp, 0) * 1_MiB;\n        if (!size) {\n            size = GetIdealDataScratchSize();\n        }\n        if (!size) {\n            size = 2_GiB;\n        }\n\n        auto flags = IImageManager::CREATE_IMAGE_DEFAULT;\n\n        if (!images->CreateBackingImage(partition_name, size, flags)) {\n            LERROR << \"could not create scratch image of \" << size << \" bytes\";\n            return false;\n        }\n    }\n    if (!images->MapImageDevice(partition_name, 10s, scratch_device)) {\n        LERROR << \"could not map scratch image\";\n        // If we cannot use this image, then remove it.\n        TeardownDataScratch(images.get(), partition_name, false /* was_mounted */);\n        return false;\n    }\n    return true;\n}\n\nstatic bool CanUseSuperPartition(const Fstab& fstab) {\n    auto slot_number = fs_mgr_overlayfs_slot_number();\n    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n    if (access(super_device.c_str(), R_OK | W_OK) || !fs_mgr_overlayfs_has_logical(fstab)) {\n        return false;\n    }\n    auto metadata = ReadMetadata(super_device, slot_number);\n    if (!metadata) {\n        return false;\n    }\n    return true;\n}\n\nbool fs_mgr_overlayfs_create_scratch(const Fstab& fstab, std::string* scratch_device,\n                                     bool* partition_exists) {\n    // Use the DSU scratch device managed by gsid if within a DSU system.\n    if (fs_mgr_is_dsu_running()) {\n        *scratch_device = GetDsuScratchDevice();\n        *partition_exists = !scratch_device->empty();\n        return *partition_exists;\n    }\n\n    // Try ImageManager on /data first.\n    bool can_use_data = false;\n    if (FilesystemHasReliablePinning(\"/data\", &can_use_data) && can_use_data) {\n        if (CreateScratchOnData(scratch_device, partition_exists)) {\n            return true;\n        }\n        LOG(WARNING) << \"Failed to allocate scratch on /data, fallback to use free space on super\";\n    }\n    // If that fails, see if we can land on super.\n    if (CanUseSuperPartition(fstab)) {\n        return CreateDynamicScratch(scratch_device, partition_exists);\n    }\n    return false;\n}\n\n// Create and mount kScratchMountPoint storage if we have logical partitions\nbool fs_mgr_overlayfs_setup_scratch(const Fstab& fstab) {\n    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {\n        return true;\n    }\n\n    std::string scratch_device;\n    bool partition_exists;\n    if (!fs_mgr_overlayfs_create_scratch(fstab, &scratch_device, &partition_exists)) {\n        LOG(ERROR) << \"Failed to create scratch partition\";\n        return false;\n    }\n\n    if (!SetOverlaysActiveFlag(true)) {\n        LOG(ERROR) << \"Failed to update dynamic partition data\";\n        fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);\n        return false;\n    }\n\n    // If the partition exists, assume first that it can be mounted.\n    if (partition_exists) {\n        if (MountScratch(scratch_device)) {\n            const auto top = kScratchMountPoint + \"/\"s + kOverlayTopDir;\n            if (access(top.c_str(), F_OK) == 0 || fs_mgr_filesystem_has_space(kScratchMountPoint)) {\n                return true;\n            }\n            // declare it useless, no overrides and no free space\n            if (!fs_mgr_overlayfs_umount_scratch()) {\n                LOG(ERROR) << \"Unable to unmount scratch partition\";\n                return false;\n            }\n        }\n    }\n\n    if (!MakeScratchFilesystem(scratch_device)) {\n        LOG(ERROR) << \"Failed to format scratch partition\";\n        return false;\n    }\n\n    return MountScratch(scratch_device);\n}\n\nconstexpr bool OverlayfsTeardownAllowed() {\n    // Never allow on non-debuggable build.\n    return kAllowOverlayfs;\n}\n\n}  // namespace\n\nbool fs_mgr_overlayfs_setup(const Fstab& fstab, const char* mount_point, bool* want_reboot,\n                            bool just_disabled_verity) {\n    if (!OverlayfsSetupAllowed(/*verbose=*/true)) {\n        return false;\n    }\n\n    if (!fs_mgr_boot_completed()) {\n        LOG(ERROR) << \"Cannot setup overlayfs before persistent properties are ready\";\n        return false;\n    }\n\n    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);\n    for (auto it = candidates.begin(); it != candidates.end();) {\n        if (mount_point &&\n            (fs_mgr_mount_point(it->mount_point) != fs_mgr_mount_point(mount_point))) {\n            it = candidates.erase(it);\n            continue;\n        }\n\n        auto verity_enabled = !just_disabled_verity && fs_mgr_is_verity_enabled(*it);\n        if (verity_enabled) {\n            it = candidates.erase(it);\n            continue;\n        }\n        ++it;\n    }\n\n    if (candidates.empty()) {\n        if (mount_point) {\n            LOG(ERROR) << \"No overlayfs candidate was found for \" << mount_point;\n            return false;\n        }\n        return true;\n    }\n\n    std::string dir;\n    for (const auto& overlay_mount_point : OverlayMountPoints()) {\n        if (overlay_mount_point == kScratchMountPoint) {\n            if (!fs_mgr_overlayfs_setup_scratch(fstab)) {\n                continue;\n            }\n        } else {\n            if (!fs_mgr_overlayfs_already_mounted(overlay_mount_point, false /* overlay */)) {\n                continue;\n            }\n        }\n        dir = overlay_mount_point;\n        break;\n    }\n    if (dir.empty()) {\n        LOG(ERROR) << \"Could not allocate backing storage for overlays\";\n        return false;\n    }\n\n    const auto overlay = fs_mgr_overlayfs_setup_dir(dir);\n    if (overlay.empty()) {\n        return false;\n    }\n\n    bool ok = true;\n    for (const auto& entry : candidates) {\n        auto fstab_mount_point = fs_mgr_mount_point(entry.mount_point);\n        ok &= fs_mgr_overlayfs_setup_one(overlay, fstab_mount_point, want_reboot);\n    }\n    return ok;\n}\n\nstruct MapInfo {\n    // If set, partition is owned by ImageManager.\n    std::unique_ptr<IImageManager> images;\n    // If set, and images is null, this is a DAP partition.\n    std::string name;\n    // If set, and images and name are empty, this is a non-dynamic partition.\n    std::string device;\n\n    MapInfo() = default;\n    MapInfo(MapInfo&&) = default;\n    ~MapInfo() {\n        if (images) {\n            images->UnmapImageDevice(name);\n        } else if (!name.empty()) {\n            DestroyLogicalPartition(name);\n        }\n    }\n};\n\n// Note: This function never returns the DSU scratch device in recovery or fastbootd,\n// because the DSU scratch is created in the first-stage-mount, which is not run in recovery.\nstatic std::optional<MapInfo> EnsureScratchMapped() {\n    MapInfo info;\n    info.device = GetBootScratchDevice();\n    if (!info.device.empty()) {\n        return {std::move(info)};\n    }\n    if (!InRecovery()) {\n        return {};\n    }\n\n    auto partition_name = android::base::Basename(kScratchMountPoint);\n\n    // Check for scratch on /data first, before looking for a modified super\n    // partition. We should only reach this code in recovery, because scratch\n    // would otherwise always be mapped.\n    auto images = IImageManager::Open(\"remount\", 10s);\n    if (images && images->BackingImageExists(partition_name)) {\n        if (images->IsImageDisabled(partition_name)) {\n            return {};\n        }\n        if (!images->MapImageDevice(partition_name, 10s, &info.device)) {\n            return {};\n        }\n        info.name = partition_name;\n        info.images = std::move(images);\n        return {std::move(info)};\n    }\n\n    // Avoid uart spam by first checking for a scratch partition.\n    const auto super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n    auto metadata = ReadCurrentMetadata(super_device);\n    if (!metadata) {\n        return {};\n    }\n\n    auto partition = FindPartition(*metadata.get(), partition_name);\n    if (!partition) {\n        return {};\n    }\n\n    CreateLogicalPartitionParams params = {\n            .block_device = super_device,\n            .metadata = metadata.get(),\n            .partition = partition,\n            .force_writable = true,\n            .timeout_ms = 10s,\n    };\n    if (!CreateLogicalPartition(params, &info.device)) {\n        return {};\n    }\n    info.name = partition_name;\n    return {std::move(info)};\n}\n\n// This should only be reachable in recovery, where DSU scratch is not\n// automatically mapped.\nstatic bool MapDsuScratchDevice(std::string* device) {\n    std::string dsu_slot;\n    if (!android::gsi::IsGsiInstalled() || !android::gsi::GetActiveDsu(&dsu_slot) ||\n        dsu_slot.empty()) {\n        // Nothing to do if no DSU installation present.\n        return false;\n    }\n\n    auto images = IImageManager::Open(\"dsu/\" + dsu_slot, 10s);\n    if (!images || !images->BackingImageExists(android::gsi::kDsuScratch)) {\n        // Nothing to do if DSU scratch device doesn't exist.\n        return false;\n    }\n\n    images->UnmapImageDevice(android::gsi::kDsuScratch);\n    if (!images->MapImageDevice(android::gsi::kDsuScratch, 10s, device)) {\n        return false;\n    }\n    return true;\n}\n\nstatic OverlayfsTeardownResult TeardownMountsAndScratch(const char* mount_point,\n                                                        bool* want_reboot) {\n    bool should_destroy_scratch = false;\n    auto rv = OverlayfsTeardownResult::Ok;\n    for (const auto& overlay_mount_point : OverlayMountPoints()) {\n        auto ok = fs_mgr_overlayfs_teardown_one(\n                overlay_mount_point, mount_point ? fs_mgr_mount_point(mount_point) : \"\",\n                want_reboot,\n                overlay_mount_point == kScratchMountPoint ? &should_destroy_scratch : nullptr);\n        if (!ok) {\n            rv = OverlayfsTeardownResult::Error;\n        }\n    }\n\n    // Do not attempt to destroy DSU scratch if within a DSU system,\n    // because DSU scratch partition is managed by gsid.\n    if (should_destroy_scratch && !fs_mgr_is_dsu_running()) {\n        auto rv = fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, want_reboot);\n        if (rv != OverlayfsTeardownResult::Ok) {\n            return rv;\n        }\n    }\n    // And now that we did what we could, lets inform\n    // caller that there may still be more to do.\n    if (!fs_mgr_boot_completed()) {\n        LOG(ERROR) << \"Cannot teardown overlayfs before persistent properties are ready\";\n        return OverlayfsTeardownResult::Error;\n    }\n    return rv;\n}\n\n// Returns false if teardown not permitted. If something is altered, set *want_reboot.\nOverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point, bool* want_reboot) {\n    if (!OverlayfsTeardownAllowed()) {\n        // Nothing to teardown.\n        return OverlayfsTeardownResult::Ok;\n    }\n    // If scratch exists, but is not mounted, lets gain access to clean\n    // specific override entries.\n    auto mount_scratch = false;\n    if ((mount_point != nullptr) && !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {\n        std::string scratch_device = GetBootScratchDevice();\n        if (!scratch_device.empty()) {\n            mount_scratch = MountScratch(scratch_device);\n        }\n    }\n\n    auto rv = TeardownMountsAndScratch(mount_point, want_reboot);\n\n    if (mount_scratch) {\n        if (!fs_mgr_overlayfs_umount_scratch()) {\n            return OverlayfsTeardownResult::Busy;\n        }\n    }\n    return rv;\n}\n\nnamespace android {\nnamespace fs_mgr {\n\nvoid MapScratchPartitionIfNeeded(Fstab* fstab,\n                                 const std::function<bool(const std::set<std::string>&)>& init) {\n    if (!OverlayfsSetupAllowed()) {\n        return;\n    }\n    if (GetEntryForMountPoint(fstab, kScratchMountPoint) != nullptr) {\n        return;\n    }\n\n    if (!GetOverlaysActiveFlag()) {\n        return;\n    }\n    if (ScratchIsOnData()) {\n        if (auto images = IImageManager::Open(\"remount\", 0ms)) {\n            images->MapAllImages(init);\n        }\n    }\n\n    // Physical or logical partitions will have already been mapped here,\n    // so just ensure /dev/block symlinks exist.\n    auto device = GetBootScratchDevice();\n    if (!device.empty()) {\n        init({android::base::Basename(device)});\n    }\n}\n\nvoid CleanupOldScratchFiles() {\n    if (!OverlayfsTeardownAllowed()) {\n        return;\n    }\n    if (!ScratchIsOnData()) {\n        return;\n    }\n    if (auto images = IImageManager::Open(\"remount\", 0ms)) {\n        images->RemoveDisabledImages();\n        if (!GetOverlaysActiveFlag()) {\n            fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);\n        }\n    }\n}\n\n// This returns the scratch device that was detected during early boot (first-\n// stage init). If the device was created later, for example during setup for\n// the adb remount command, it can return an empty string since it does not\n// query ImageManager. (Note that ImageManager in first-stage init will always\n// use device-mapper, since /data is not available to use loop devices.)\nstd::string GetBootScratchDevice() {\n    // Note: fs_mgr_is_dsu_running() always returns false in recovery or fastbootd.\n    if (fs_mgr_is_dsu_running()) {\n        return GetDsuScratchDevice();\n    }\n\n    auto& dm = DeviceMapper::Instance();\n\n    // If there is a scratch partition allocated in /data or on super, we\n    // automatically prioritize that over super_other or system_other.\n    // Some devices, for example, have a write-protected eMMC and the\n    // super partition cannot be used even if it exists.\n    std::string device;\n    auto partition_name = android::base::Basename(kScratchMountPoint);\n    if (dm.GetState(partition_name) != DmDeviceState::INVALID &&\n        dm.GetDmDevicePathByName(partition_name, &device)) {\n        return device;\n    }\n\n    return \"\";\n}\n\nvoid TeardownAllOverlayForMountPoint(const std::string& mount_point) {\n    if (!OverlayfsTeardownAllowed()) {\n        return;\n    }\n    if (!InRecovery()) {\n        LERROR << __FUNCTION__ << \"(): must be called within recovery.\";\n        return;\n    }\n\n    // Empty string means teardown everything.\n    const std::string teardown_dir = mount_point.empty() ? \"\" : fs_mgr_mount_point(mount_point);\n    constexpr bool* ignore_change = nullptr;\n\n    // Teardown legacy overlay mount points that's not backed by a scratch device.\n    for (const auto& overlay_mount_point : OverlayMountPoints()) {\n        if (overlay_mount_point == kScratchMountPoint) {\n            continue;\n        }\n        fs_mgr_overlayfs_teardown_one(overlay_mount_point, teardown_dir, ignore_change);\n    }\n\n    if (mount_point.empty()) {\n        // Throw away the entire partition.\n        auto partition_name = android::base::Basename(kScratchMountPoint);\n        auto images = IImageManager::Open(\"remount\", 10s);\n        if (images && images->BackingImageExists(partition_name)) {\n            if (images->DisableImage(partition_name)) {\n                LOG(INFO) << \"Disabled scratch partition for: \" << kScratchMountPoint;\n            } else {\n                LOG(ERROR) << \"Unable to disable scratch partition for \" << kScratchMountPoint;\n            }\n        }\n    }\n\n    // Note if we just disabled scratch, this mount will fail.\n    if (auto info = EnsureScratchMapped(); info.has_value()) {\n        // Map scratch device, mount kScratchMountPoint and teardown kScratchMountPoint.\n        fs_mgr_overlayfs_umount_scratch();\n        if (MountScratch(info->device)) {\n            bool should_destroy_scratch = false;\n            fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change,\n                                          &should_destroy_scratch);\n            fs_mgr_overlayfs_umount_scratch();\n            if (should_destroy_scratch) {\n                fs_mgr_overlayfs_teardown_scratch(kScratchMountPoint, nullptr);\n            }\n        }\n    }\n\n    // Teardown DSU overlay if present.\n    std::string scratch_device;\n    if (MapDsuScratchDevice(&scratch_device)) {\n        fs_mgr_overlayfs_umount_scratch();\n        if (MountScratch(scratch_device)) {\n            fs_mgr_overlayfs_teardown_one(kScratchMountPoint, teardown_dir, ignore_change);\n            fs_mgr_overlayfs_umount_scratch();\n        }\n        DestroyLogicalPartition(android::gsi::kDsuScratch);\n    }\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr_overlayfs_control.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <fstab/fstab.h>\n\n// If \"mount_point\" is non-null, set up exactly one overlay.\n//\n// If \"mount_point\" is null, setup any overlays.\n//\n// If |want_reboot| is non-null, and a reboot is needed to apply overlays, then\n// it will be true on return. The caller is responsible for initializing it.\nbool fs_mgr_overlayfs_setup(const android::fs_mgr::Fstab& fstab, const char* mount_point = nullptr,\n                            bool* want_reboot = nullptr, bool just_disabled_verity = true);\n\nenum class OverlayfsTeardownResult {\n    Ok,\n    Busy,  // Indicates that overlays are still in use.\n    Error\n};\nOverlayfsTeardownResult fs_mgr_overlayfs_teardown(const char* mount_point = nullptr,\n                                                  bool* want_reboot = nullptr);\n\nnamespace android {\nnamespace fs_mgr {\n\nvoid CleanupOldScratchFiles();\n\nstd::string GetBootScratchDevice();\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr_overlayfs_mount.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <selinux/selinux.h>\n#include <stdlib.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <sys/types.h>\n#include <sys/vfs.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/macros.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <ext4_utils/ext4_utils.h>\n#include <fs_mgr.h>\n#include <fs_mgr/file_wait.h>\n#include <fs_mgr_overlayfs.h>\n#include <fstab/fstab.h>\n#include <libgsi/libgsi.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"fs_mgr_overlayfs_control.h\"\n#include \"fs_mgr_overlayfs_mount.h\"\n#include \"fs_mgr_priv.h\"\n\nusing namespace std::literals;\nusing namespace android::fs_mgr;\nusing namespace android::storage_literals;\n\nconstexpr char kPreferCacheBackingStorageProp[] = \"fs_mgr.overlayfs.prefer_cache_backing_storage\";\n\nconstexpr char kCacheMountPoint[] = \"/cache\";\nconstexpr char kPhysicalDevice[] = \"/dev/block/by-name/\";\n\n// Mount tree to temporarily hold references to submounts.\nconstexpr char kMoveMountTempDir[] = \"/dev/remount\";\n\nconstexpr char kLowerdirOption[] = \"lowerdir=\";\nconstexpr char kUpperdirOption[] = \"upperdir=\";\nconstexpr char kWorkdirOption[] = \"workdir=\";\n\nbool fs_mgr_is_dsu_running() {\n    // Since android::gsi::CanBootIntoGsi() or android::gsi::MarkSystemAsGsi() is\n    // never called in recovery, the return value of android::gsi::IsGsiRunning()\n    // is not well-defined. In this case, just return false as being in recovery\n    // implies not running a DSU system.\n    if (InRecovery()) return false;\n    return android::gsi::IsGsiRunning();\n}\n\nstd::vector<std::string> OverlayMountPoints() {\n    // Never fallback to legacy cache mount point if within a DSU system,\n    // because running a DSU system implies the device supports dynamic\n    // partitions, which means legacy cache mustn't be used.\n    if (fs_mgr_is_dsu_running()) {\n        return {kScratchMountPoint};\n    }\n\n    // For non-A/B devices prefer cache backing storage if\n    // kPreferCacheBackingStorageProp property set.\n    if (fs_mgr_get_slot_suffix().empty() &&\n        android::base::GetBoolProperty(kPreferCacheBackingStorageProp, false) &&\n        android::base::GetIntProperty(\"ro.vendor.api_level\", -1) < __ANDROID_API_T__) {\n        return {kCacheMountPoint, kScratchMountPoint};\n    }\n\n    return {kScratchMountPoint, kCacheMountPoint};\n}\n\nstd::string GetEncodedBaseDirForMountPoint(const std::string& mount_point) {\n    std::string normalized_path;\n    if (mount_point.empty() || !android::base::Realpath(mount_point, &normalized_path)) {\n        return \"\";\n    }\n    std::string_view sv(normalized_path);\n    if (sv != \"/\") {\n        android::base::ConsumePrefix(&sv, \"/\");\n        android::base::ConsumeSuffix(&sv, \"/\");\n    }\n    return android::base::StringReplace(sv, \"/\", \"@\", true);\n}\n\nstatic bool fs_mgr_is_dir(const std::string& path) {\n    struct stat st;\n    return !stat(path.c_str(), &st) && S_ISDIR(st.st_mode);\n}\n\n// At less than 1% or 8MB of free space return value of false,\n// means we will try to wrap with overlayfs.\nbool fs_mgr_filesystem_has_space(const std::string& mount_point) {\n    // If we have access issues to find out space remaining, return true\n    // to prevent us trying to override with overlayfs.\n    struct statvfs vst;\n    if (statvfs(mount_point.c_str(), &vst)) {\n        PLOG(ERROR) << \"statvfs \" << mount_point;\n        return true;\n    }\n\n    static constexpr int kPercentThreshold = 1;                       // 1%\n    static constexpr unsigned long kSizeThreshold = 8 * 1024 * 1024;  // 8MB\n\n    return (vst.f_bfree >= (vst.f_blocks * kPercentThreshold / 100)) &&\n           (static_cast<uint64_t>(vst.f_bfree) * vst.f_frsize) >= kSizeThreshold;\n}\n\nstatic bool fs_mgr_update_blk_device(FstabEntry* entry) {\n    if (entry->fs_mgr_flags.logical) {\n        fs_mgr_update_logical_partition(entry);\n    }\n    if (access(entry->blk_device.c_str(), F_OK) == 0) {\n        return true;\n    }\n    if (entry->blk_device != \"/dev/root\") {\n        return false;\n    }\n\n    // special case for system-as-root (taimen and others)\n    auto blk_device = kPhysicalDevice + \"system\"s;\n    if (access(blk_device.c_str(), F_OK)) {\n        blk_device += fs_mgr_get_slot_suffix();\n        if (access(blk_device.c_str(), F_OK)) {\n            return false;\n        }\n    }\n    entry->blk_device = blk_device;\n    return true;\n}\n\nstatic bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {\n    struct statfs fs;\n    if ((statfs((mount_point + \"/lost+found\").c_str(), &fs) == -1) ||\n        (fs.f_type != EXT4_SUPER_MAGIC)) {\n        return false;\n    }\n\n    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));\n    if (fd < 0) return false;\n\n    struct ext4_super_block sb;\n    if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||\n        (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {\n        return false;\n    }\n\n    struct fs_info info;\n    if (ext4_parse_sb(&sb, &info) < 0) return false;\n\n    return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;\n}\n\n#define F2FS_SUPER_OFFSET 1024\n#define F2FS_FEATURE_OFFSET 2180\n#define F2FS_FEATURE_RO 0x4000\nstatic bool fs_mgr_is_read_only_f2fs(const std::string& dev) {\n    if (!fs_mgr_is_f2fs(dev)) return false;\n\n    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));\n    if (fd < 0) return false;\n\n    __le32 feat;\n    if ((TEMP_FAILURE_RETRY(lseek64(fd, F2FS_SUPER_OFFSET + F2FS_FEATURE_OFFSET, SEEK_SET)) < 0) ||\n        (TEMP_FAILURE_RETRY(read(fd, &feat, sizeof(feat))) < 0)) {\n        return false;\n    }\n\n    return (feat & cpu_to_le32(F2FS_FEATURE_RO)) != 0;\n}\n\nstatic bool fs_mgr_overlayfs_enabled(FstabEntry* entry) {\n    // readonly filesystem, can not be mount -o remount,rw\n    // for squashfs, erofs, or if there are shared blocks that prevent remount,rw\n    if (entry->fs_type == \"erofs\" || entry->fs_type == \"squashfs\") {\n        return true;\n    }\n\n    // blk_device needs to be setup so we can check superblock.\n    // If we fail here, because during init first stage and have doubts.\n    if (!fs_mgr_update_blk_device(entry)) {\n        return true;\n    }\n\n    // f2fs read-only mode doesn't support remount,rw\n    if (fs_mgr_is_read_only_f2fs(entry->blk_device)) {\n        return true;\n    }\n\n    // check if ext4 de-dupe\n    auto has_shared_blocks = fs_mgr_has_shared_blocks(entry->mount_point, entry->blk_device);\n    if (!has_shared_blocks && (entry->mount_point == \"/system\")) {\n        has_shared_blocks = fs_mgr_has_shared_blocks(\"/\", entry->blk_device);\n    }\n    return has_shared_blocks;\n}\n\nconst std::string fs_mgr_mount_point(const std::string& mount_point) {\n    if (\"/\"s != mount_point) return mount_point;\n    return \"/system\";\n}\n\n// default options for mount_point, returns empty string for none available.\nstatic std::string fs_mgr_get_overlayfs_options(const FstabEntry& entry) {\n    const auto mount_point = fs_mgr_mount_point(entry.mount_point);\n    if (!fs_mgr_is_dir(mount_point)) {\n        return \"\";\n    }\n    const auto base = GetEncodedBaseDirForMountPoint(mount_point);\n    if (base.empty()) {\n        return \"\";\n    }\n    for (const auto& overlay_mount_point : OverlayMountPoints()) {\n        const auto dir = overlay_mount_point + \"/\" + kOverlayTopDir + \"/\" + base + \"/\";\n        const auto upper = dir + kUpperName;\n        const auto work = dir + kWorkName;\n        if (!fs_mgr_is_dir(upper) || !fs_mgr_is_dir(work) || access(work.c_str(), R_OK | W_OK)) {\n            continue;\n        }\n        auto ret = kLowerdirOption + mount_point + \",\" + kUpperdirOption + upper + \",\" +\n                   kWorkdirOption + work + android::fs_mgr::CheckOverlayfs().mount_flags;\n        for (const auto& flag : android::base::Split(entry.fs_options, \",\")) {\n            if (android::base::StartsWith(flag, \"context=\")) {\n                ret += \",\" + flag;\n            }\n        }\n        return ret;\n    }\n    return \"\";\n}\n\nbool AutoSetFsCreateCon::Set(const std::string& context) {\n    if (setfscreatecon(context.c_str())) {\n        PLOG(ERROR) << \"setfscreatecon \" << context;\n        return false;\n    }\n    ok_ = true;\n    return true;\n}\n\nbool AutoSetFsCreateCon::Restore() {\n    if (restored_ || !ok_) {\n        return true;\n    }\n    if (setfscreatecon(nullptr)) {\n        PLOG(ERROR) << \"setfscreatecon null\";\n        return false;\n    }\n    restored_ = true;\n    return true;\n}\n\n// Returns true if immediate unmount succeeded and the scratch mount point was\n// removed.\nbool fs_mgr_overlayfs_umount_scratch() {\n    if (umount(kScratchMountPoint) != 0) {\n        return false;\n    }\n    if (rmdir(kScratchMountPoint) != 0 && errno != ENOENT) {\n        PLOG(ERROR) << \"rmdir \" << kScratchMountPoint;\n    }\n    return true;\n}\n\nstatic bool fs_mgr_overlayfs_set_shared_mount(const std::string& mount_point, bool shared_flag) {\n    auto ret = mount(nullptr, mount_point.c_str(), nullptr, shared_flag ? MS_SHARED : MS_PRIVATE,\n                     nullptr);\n    if (ret) {\n        PERROR << \"__mount(target=\" << mount_point\n               << \",flag=\" << (shared_flag ? \"MS_SHARED\" : \"MS_PRIVATE\") << \")=\" << ret;\n        return false;\n    }\n    return true;\n}\n\nstatic bool fs_mgr_overlayfs_move_mount(const std::string& source, const std::string& target) {\n    auto ret = mount(source.c_str(), target.c_str(), nullptr, MS_MOVE, nullptr);\n    if (ret) {\n        PERROR << \"__mount(source=\" << source << \",target=\" << target << \",flag=MS_MOVE)=\" << ret;\n        return false;\n    }\n    return true;\n}\n\nstatic bool fs_mgr_overlayfs_mount(const std::string& mount_point, const std::string& options) {\n    auto report = \"__mount(source=overlay,target=\"s + mount_point + \",type=overlay\";\n    for (const auto& opt : android::base::Split(options, \",\")) {\n        if (android::base::StartsWith(opt, kUpperdirOption)) {\n            report = report + \",\" + opt;\n            break;\n        }\n    }\n    report = report + \")=\";\n    auto ret = mount(\"overlay\", mount_point.c_str(), \"overlay\", MS_RDONLY | MS_NOATIME,\n                     options.c_str());\n    if (ret) {\n        PERROR << report << ret;\n    } else {\n        LINFO << report << ret;\n    }\n    return !ret;\n}\n\nstruct mount_info {\n    std::string mount_point;\n    bool shared_flag;\n};\n\nstatic std::vector<mount_info> ReadMountinfoFromFile(const std::string& path) {\n    std::vector<mount_info> info;\n\n    auto file = std::unique_ptr<FILE, decltype(&fclose)>{fopen(path.c_str(), \"re\"), fclose};\n    if (!file) {\n        PERROR << __FUNCTION__ << \"(): cannot open file: '\" << path << \"'\";\n        return info;\n    }\n\n    ssize_t len;\n    size_t alloc_len = 0;\n    char* line = nullptr;\n    while ((len = getline(&line, &alloc_len, file.get())) != -1) {\n        /* if the last character is a newline, shorten the string by 1 byte */\n        if (line[len - 1] == '\\n') {\n            line[len - 1] = '\\0';\n        }\n\n        static constexpr char delim[] = \" \\t\";\n        char* save_ptr;\n        if (!strtok_r(line, delim, &save_ptr)) {\n            LERROR << \"Error parsing mount ID\";\n            break;\n        }\n        if (!strtok_r(nullptr, delim, &save_ptr)) {\n            LERROR << \"Error parsing parent ID\";\n            break;\n        }\n        if (!strtok_r(nullptr, delim, &save_ptr)) {\n            LERROR << \"Error parsing mount source\";\n            break;\n        }\n        if (!strtok_r(nullptr, delim, &save_ptr)) {\n            LERROR << \"Error parsing root\";\n            break;\n        }\n\n        char* p;\n        if (!(p = strtok_r(nullptr, delim, &save_ptr))) {\n            LERROR << \"Error parsing mount_point\";\n            break;\n        }\n        mount_info entry = {p, false};\n\n        if (!strtok_r(nullptr, delim, &save_ptr)) {\n            LERROR << \"Error parsing mount_flags\";\n            break;\n        }\n\n        while ((p = strtok_r(nullptr, delim, &save_ptr))) {\n            if ((p[0] == '-') && (p[1] == '\\0')) break;\n            if (android::base::StartsWith(p, \"shared:\")) entry.shared_flag = true;\n        }\n        if (!p) {\n            LERROR << \"Error parsing fields\";\n            break;\n        }\n        info.emplace_back(std::move(entry));\n    }\n\n    free(line);\n    if (info.empty()) {\n        LERROR << __FUNCTION__ << \"(): failed to load mountinfo from : '\" << path << \"'\";\n    }\n    return info;\n}\n\nstatic bool fs_mgr_overlayfs_mount_one(const FstabEntry& fstab_entry) {\n    const auto mount_point = fs_mgr_mount_point(fstab_entry.mount_point);\n    const auto options = fs_mgr_get_overlayfs_options(fstab_entry);\n    if (options.empty()) return false;\n\n    struct MoveEntry {\n        std::string mount_point;\n        std::string dir;\n        bool shared_flag;\n    };\n    std::vector<MoveEntry> moved_mounts;\n\n    bool retval = true;\n    bool move_dir_shared = true;\n    bool parent_shared = true;\n    bool parent_have_parent = false;\n    bool parent_made_private = false;\n    bool root_shared = true;\n    bool root_made_private = false;\n\n    // There could be multiple mount entries with the same mountpoint.\n    // Group these entries together with stable_sort, and keep only the last entry of a group.\n    // Only move mount the last entry in an over mount group, because the other entries are\n    // overshadowed and only the filesystem mounted with the last entry participates in file\n    // pathname resolution.\n    auto mountinfo = ReadMountinfoFromFile(\"/proc/self/mountinfo\");\n    std::stable_sort(mountinfo.begin(), mountinfo.end(), [](const auto& lhs, const auto& rhs) {\n        return lhs.mount_point < rhs.mount_point;\n    });\n    std::reverse(mountinfo.begin(), mountinfo.end());\n    auto erase_from = std::unique(\n            mountinfo.begin(), mountinfo.end(),\n            [](const auto& lhs, const auto& rhs) { return lhs.mount_point == rhs.mount_point; });\n    mountinfo.erase(erase_from, mountinfo.end());\n    std::reverse(mountinfo.begin(), mountinfo.end());\n    // mountinfo is reversed twice, so still is in lexical sorted order.\n\n    for (const auto& entry : mountinfo) {\n        if (entry.mount_point == kMoveMountTempDir) {\n            move_dir_shared = entry.shared_flag;\n        }\n        if (entry.mount_point == mount_point ||\n            (mount_point == \"/system\" && entry.mount_point == \"/\")) {\n            parent_shared = entry.shared_flag;\n        }\n        if (entry.mount_point == \"/\") {\n            root_shared = entry.shared_flag;\n        }\n        // Ignore \"/\" as we don't overlay \"/\" directly.\n        if (entry.mount_point != \"/\") {\n            parent_have_parent |= android::base::StartsWith(mount_point, entry.mount_point + \"/\");\n        }\n    }\n\n    // Precondition is that kMoveMountTempDir is MS_PRIVATE, otherwise don't try to move any\n    // submount in to or out of it.\n    if (move_dir_shared) {\n        mountinfo.clear();\n    }\n\n    // Need to make the original mountpoint MS_PRIVATE, so that the overlayfs can be MS_MOVE.\n    // This could happen if its parent mount is remounted later.\n    if (parent_have_parent) {\n        parent_made_private |= fs_mgr_overlayfs_set_shared_mount(mount_point, false);\n        if (!parent_made_private && errno == EINVAL && mount_point == \"/system\") {\n            // If failed to set \"/system\" mount type, it might be due to \"/system\" not being a valid\n            // mountpoint after switch root. Retry with \"/\" in this case.\n            parent_made_private |= fs_mgr_overlayfs_set_shared_mount(\"/\", false);\n            root_made_private |= parent_made_private;\n        }\n    }\n\n    for (const auto& entry : mountinfo) {\n        // Find all immediate submounts.\n        if (!android::base::StartsWith(entry.mount_point, mount_point + \"/\")) {\n            continue;\n        }\n        // Exclude duplicated or more specific entries.\n        if (std::find_if(moved_mounts.begin(), moved_mounts.end(), [&entry](const auto& it) {\n                return it.mount_point == entry.mount_point ||\n                       android::base::StartsWith(entry.mount_point, it.mount_point + \"/\");\n            }) != moved_mounts.end()) {\n            continue;\n        }\n        // mountinfo is in lexical order, so no need to worry about |entry| being a parent mount of\n        // entries of |moved_mounts|.\n\n        MoveEntry new_entry{entry.mount_point, kMoveMountTempDir + \"/TemporaryDir-XXXXXX\"s,\n                            entry.shared_flag};\n        {\n            AutoSetFsCreateCon createcon;\n            auto new_context = fs_mgr_get_context(entry.mount_point);\n            if (new_context.empty() || !createcon.Set(new_context)) {\n                continue;\n            }\n            const auto target = mkdtemp(new_entry.dir.data());\n            if (!target) {\n                retval = false;\n                PERROR << \"temporary directory for MS_MOVE\";\n                continue;\n            }\n            if (!createcon.Restore()) {\n                retval = false;\n                rmdir(new_entry.dir.c_str());\n                continue;\n            }\n        }\n        if (!parent_made_private) {\n            parent_made_private |= fs_mgr_overlayfs_set_shared_mount(mount_point, false);\n            if (!parent_made_private && errno == EINVAL && mount_point == \"/system\") {\n                // If failed to set \"/system\" mount type, it might be due to \"/system\" not being a\n                // valid mountpoint after switch root. Retry with \"/\" in this case.\n                parent_made_private |= fs_mgr_overlayfs_set_shared_mount(\"/\", false);\n                root_made_private |= parent_made_private;\n            }\n        }\n\n        if (new_entry.shared_flag) {\n            new_entry.shared_flag = fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, false);\n        }\n        if (!fs_mgr_overlayfs_move_mount(new_entry.mount_point, new_entry.dir)) {\n            retval = false;\n            if (new_entry.shared_flag) {\n                fs_mgr_overlayfs_set_shared_mount(new_entry.mount_point, true);\n            }\n            rmdir(new_entry.dir.c_str());\n            continue;\n        }\n        moved_mounts.push_back(std::move(new_entry));\n    }\n\n    retval &= fs_mgr_overlayfs_mount(mount_point, options);\n\n    // Move submounts back.\n    for (const auto& entry : moved_mounts) {\n        if (!fs_mgr_overlayfs_move_mount(entry.dir, entry.mount_point)) {\n            retval = false;\n        } else if (entry.shared_flag &&\n                   !fs_mgr_overlayfs_set_shared_mount(entry.mount_point, true)) {\n            retval = false;\n        }\n        rmdir(entry.dir.c_str());\n    }\n    // If the original (overridden) mount was MS_SHARED, then set the overlayfs mount to MS_SHARED.\n    if (parent_shared && parent_made_private) {\n        fs_mgr_overlayfs_set_shared_mount(mount_point, true);\n    }\n    if (root_shared && root_made_private) {\n        fs_mgr_overlayfs_set_shared_mount(\"/\", true);\n    }\n\n    return retval;\n}\n\n// Mount kScratchMountPoint\nbool MountScratch(const std::string& device_path, bool readonly) {\n    if (readonly) {\n        if (access(device_path.c_str(), F_OK)) {\n            LOG(ERROR) << \"Path does not exist: \" << device_path;\n            return false;\n        }\n    } else if (access(device_path.c_str(), R_OK | W_OK)) {\n        LOG(ERROR) << \"Path does not exist or is not readwrite: \" << device_path;\n        return false;\n    }\n\n    std::vector<const char*> filesystem_candidates;\n    if (fs_mgr_is_f2fs(device_path)) {\n        filesystem_candidates = {\"f2fs\", \"ext4\"};\n    } else if (fs_mgr_is_ext4(device_path)) {\n        filesystem_candidates = {\"ext4\", \"f2fs\"};\n    } else {\n        LOG(ERROR) << \"Scratch partition is not f2fs or ext4\";\n        return false;\n    }\n\n    AutoSetFsCreateCon createcon(kOverlayfsFileContext);\n    if (!createcon.Ok()) {\n        return false;\n    }\n    if (mkdir(kScratchMountPoint, 0755) && (errno != EEXIST)) {\n        PERROR << \"create \" << kScratchMountPoint;\n        return false;\n    }\n\n    FstabEntry entry;\n    entry.blk_device = device_path;\n    entry.mount_point = kScratchMountPoint;\n    entry.flags = MS_NOATIME | MS_RDONLY;\n    if (!readonly) {\n        entry.flags &= ~MS_RDONLY;\n        entry.flags |= MS_SYNCHRONOUS;\n        entry.fs_options = \"nodiscard\";\n        fs_mgr_set_blk_ro(device_path, false);\n    }\n    // check_fs requires apex runtime library\n    if (fs_mgr_overlayfs_already_mounted(\"/data\", false)) {\n        entry.fs_mgr_flags.check = true;\n    }\n    bool mounted = false;\n    for (auto fs_type : filesystem_candidates) {\n        entry.fs_type = fs_type;\n        if (fs_mgr_do_mount_one(entry) == 0) {\n            mounted = true;\n            break;\n        }\n    }\n    if (!createcon.Restore()) {\n        return false;\n    }\n    if (!mounted) {\n        rmdir(kScratchMountPoint);\n        return false;\n    }\n    return true;\n}\n\n// NOTE: OverlayfsSetupAllowed() must be \"stricter\" than OverlayfsTeardownAllowed().\n// Setup is allowed only if teardown is also allowed.\nbool OverlayfsSetupAllowed(bool verbose) {\n    if (!kAllowOverlayfs) {\n        if (verbose) {\n            LOG(ERROR) << \"Overlayfs remounts can only be used in debuggable builds\";\n        }\n        return false;\n    }\n    // Check mandatory kernel patches.\n    if (!android::fs_mgr::CheckOverlayfs().supported) {\n        if (verbose) {\n            LOG(ERROR) << \"Kernel does not support overlayfs\";\n        }\n        return false;\n    }\n    // in recovery or fastbootd, not allowed!\n    if (InRecovery()) {\n        if (verbose) {\n            LOG(ERROR) << \"Unsupported overlayfs setup from recovery\";\n        }\n        return false;\n    }\n    return true;\n}\n\nbool fs_mgr_wants_overlayfs(FstabEntry* entry) {\n    // Don't check entries that are managed by vold.\n    if (entry->fs_mgr_flags.vold_managed || entry->fs_mgr_flags.recovery_only) return false;\n\n    // *_other doesn't want overlayfs.\n    if (entry->fs_mgr_flags.slot_select_other) return false;\n\n    // Only concerned with readonly partitions.\n    if (!(entry->flags & MS_RDONLY)) return false;\n\n    // If unbindable, do not allow overlayfs as this could expose us to\n    // security issues.  On Android, this could also be used to turn off\n    // the ability to overlay an otherwise acceptable filesystem since\n    // /system and /vendor are never bound(sic) to.\n    if (entry->flags & MS_UNBINDABLE) return false;\n\n    if (!fs_mgr_overlayfs_enabled(entry)) return false;\n\n    return true;\n}\n\nFstab fs_mgr_overlayfs_candidate_list(const Fstab& fstab) {\n    android::fs_mgr::Fstab mounts;\n    if (!android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &mounts)) {\n        PLOG(ERROR) << \"Failed to read /proc/mounts\";\n        return {};\n    }\n\n    Fstab candidates;\n    for (const auto& entry : fstab) {\n        // Filter out partitions whose type doesn't match what's mounted.\n        // This avoids spammy behavior on devices which can mount different\n        // filesystems for each partition.\n        auto proc_mount_point = (entry.mount_point == \"/system\") ? \"/\" : entry.mount_point;\n        auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);\n        if (!mounted || mounted->fs_type != entry.fs_type) {\n            continue;\n        }\n\n        FstabEntry new_entry = entry;\n        if (!fs_mgr_overlayfs_already_mounted(entry.mount_point) &&\n            !fs_mgr_wants_overlayfs(&new_entry)) {\n            continue;\n        }\n        const auto new_mount_point = fs_mgr_mount_point(new_entry.mount_point);\n        if (std::find_if(candidates.begin(), candidates.end(), [&](const auto& it) {\n                return fs_mgr_mount_point(it.mount_point) == new_mount_point;\n            }) != candidates.end()) {\n            continue;\n        }\n        candidates.push_back(std::move(new_entry));\n    }\n    return candidates;\n}\n\nstatic void TryMountScratch() {\n    // Note we get the boot scratch device here, which means if scratch was\n    // just created through ImageManager, this could fail. In practice this\n    // should not happen because \"remount\" detects this scenario (by checking\n    // if verity is still disabled, i.e. no reboot occurred), and skips calling\n    // fs_mgr_overlayfs_mount_all().\n    auto scratch_device = GetBootScratchDevice();\n    if (access(scratch_device.c_str(), R_OK | W_OK)) {\n        return;\n    }\n    if (!WaitForFile(scratch_device, 10s)) {\n        return;\n    }\n    if (!MountScratch(scratch_device, true /* readonly */)) {\n        return;\n    }\n    const auto top = kScratchMountPoint + \"/\"s + kOverlayTopDir;\n    const bool has_overlayfs_dir = access(top.c_str(), F_OK) == 0;\n    fs_mgr_overlayfs_umount_scratch();\n    if (has_overlayfs_dir) {\n        MountScratch(scratch_device);\n    }\n}\n\nbool fs_mgr_overlayfs_mount_all(Fstab* fstab) {\n    if (!OverlayfsSetupAllowed()) {\n        return false;\n    }\n\n    // Ensure kMoveMountTempDir is standalone mount tree with 'private' propagation by bind mounting\n    // to itself and set to MS_PRIVATE.\n    // Otherwise mounts moved in to it would have their propagation type changed unintentionally.\n    // Section 5d, https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt\n    if (!fs_mgr_overlayfs_already_mounted(kMoveMountTempDir, false)) {\n        if (mkdir(kMoveMountTempDir, 0755) && errno != EEXIST) {\n            PERROR << \"mkdir \" << kMoveMountTempDir;\n        }\n        if (mount(kMoveMountTempDir, kMoveMountTempDir, nullptr, MS_BIND, nullptr)) {\n            PERROR << \"bind mount \" << kMoveMountTempDir;\n        }\n    }\n    fs_mgr_overlayfs_set_shared_mount(kMoveMountTempDir, false);\n    android::base::ScopeGuard umountDir([]() {\n        umount(kMoveMountTempDir);\n        rmdir(kMoveMountTempDir);\n    });\n\n    auto ret = true;\n    auto scratch_can_be_mounted = !fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false);\n    for (const auto& entry : fs_mgr_overlayfs_candidate_list(*fstab)) {\n        if (fs_mgr_is_verity_enabled(entry)) continue;\n        auto mount_point = fs_mgr_mount_point(entry.mount_point);\n        if (fs_mgr_overlayfs_already_mounted(mount_point)) {\n            continue;\n        }\n        if (scratch_can_be_mounted) {\n            scratch_can_be_mounted = false;\n            TryMountScratch();\n        }\n        ret &= fs_mgr_overlayfs_mount_one(entry);\n    }\n    return ret;\n}\n\nbool fs_mgr_overlayfs_is_setup() {\n    if (!OverlayfsSetupAllowed()) {\n        return false;\n    }\n    if (fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) return true;\n    Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        return false;\n    }\n    for (const auto& entry : fs_mgr_overlayfs_candidate_list(fstab)) {\n        if (fs_mgr_is_verity_enabled(entry)) continue;\n        if (fs_mgr_overlayfs_already_mounted(fs_mgr_mount_point(entry.mount_point))) return true;\n    }\n    return false;\n}\n\nbool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only) {\n    Fstab fstab;\n    if (!ReadFstabFromProcMounts(&fstab)) {\n        return false;\n    }\n    const auto lowerdir = kLowerdirOption + mount_point;\n    for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {\n        if (!overlay_only) {\n            return true;\n        }\n        if (entry->fs_type != \"overlay\" && entry->fs_type != \"overlayfs\") {\n            continue;\n        }\n        const auto options = android::base::Split(entry->fs_options, \",\");\n        for (const auto& opt : options) {\n            if (opt == lowerdir) {\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\nnamespace android {\nnamespace fs_mgr {\n\nvoid MountOverlayfs(const FstabEntry& fstab_entry, bool* scratch_can_be_mounted) {\n    if (!OverlayfsSetupAllowed()) {\n        return;\n    }\n    const auto candidates = fs_mgr_overlayfs_candidate_list({fstab_entry});\n    if (candidates.empty()) {\n        return;\n    }\n    const auto& entry = candidates.front();\n    if (fs_mgr_is_verity_enabled(entry)) {\n        return;\n    }\n    const auto mount_point = fs_mgr_mount_point(entry.mount_point);\n    if (fs_mgr_overlayfs_already_mounted(mount_point)) {\n        return;\n    }\n    if (*scratch_can_be_mounted) {\n        *scratch_can_be_mounted = false;\n        if (!fs_mgr_overlayfs_already_mounted(kScratchMountPoint, false)) {\n            TryMountScratch();\n        }\n    }\n    const auto options = fs_mgr_get_overlayfs_options(entry);\n    if (options.empty()) {\n        return;\n    }\n    fs_mgr_overlayfs_mount(mount_point, options);\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr_overlayfs_mount.h",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <fstab/fstab.h>\n\nconstexpr char kOverlayfsFileContext[] = \"u:object_r:overlayfs_file:s0\";\n\nconstexpr char kScratchMountPoint[] = \"/mnt/scratch\";\nconstexpr char kOverlayTopDir[] = \"overlay\";\nconstexpr char kUpperName[] = \"upper\";\nconstexpr char kWorkName[] = \"work\";\n\n#if ALLOW_ADBD_DISABLE_VERITY\nconstexpr bool kAllowOverlayfs = true;\n#else\nconstexpr bool kAllowOverlayfs = false;\n#endif\n\nclass AutoSetFsCreateCon final {\n  public:\n    AutoSetFsCreateCon() {}\n    AutoSetFsCreateCon(const std::string& context) { Set(context); }\n    ~AutoSetFsCreateCon() { Restore(); }\n\n    bool Ok() const { return ok_; }\n    bool Set(const std::string& context);\n    bool Restore();\n\n  private:\n    bool ok_ = false;\n    bool restored_ = false;\n};\n\nbool fs_mgr_is_dsu_running();\nbool fs_mgr_filesystem_has_space(const std::string& mount_point);\nconst std::string fs_mgr_mount_point(const std::string& mount_point);\nbool OverlayfsSetupAllowed(bool verbose = false);\nbool MountScratch(const std::string& device_path, bool readonly = false);\nbool fs_mgr_overlayfs_umount_scratch();\nstd::vector<std::string> OverlayMountPoints();\nbool fs_mgr_overlayfs_already_mounted(const std::string& mount_point, bool overlay_only = true);\nbool fs_mgr_wants_overlayfs(android::fs_mgr::FstabEntry* entry);\nandroid::fs_mgr::Fstab fs_mgr_overlayfs_candidate_list(const android::fs_mgr::Fstab& fstab);\nstd::string GetEncodedBaseDirForMountPoint(const std::string& mount_point);\n"
  },
  {
    "path": "fs_mgr/fs_mgr_priv.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <string>\n\n#include <android-base/logging.h>\n#include <fs_mgr.h>\n#include <fstab/fstab.h>\n\n#include \"libfstab/fstab_priv.h\"\n\n#define FS_MGR_TAG \"[libfs_mgr] \"\n\n// Logs a message to kernel\n#define LINFO    LOG(INFO) << FS_MGR_TAG\n#define LWARNING LOG(WARNING) << FS_MGR_TAG\n#define LERROR   LOG(ERROR) << FS_MGR_TAG\n#define LFATAL LOG(FATAL) << FS_MGR_TAG\n\n// Logs a message with strerror(errno) at the end\n#define PINFO    PLOG(INFO) << FS_MGR_TAG\n#define PWARNING PLOG(WARNING) << FS_MGR_TAG\n#define PERROR   PLOG(ERROR) << FS_MGR_TAG\n#define PFATAL PLOG(FATAL) << FS_MGR_TAG\n\n#define CRYPTO_TMPFS_OPTIONS \"size=512m,mode=0771,uid=1000,gid=1000\"\n\n/* fstab has the following format:\n *\n * Any line starting with a # is a comment and ignored\n *\n * Any blank line is ignored\n *\n * All other lines must be in this format:\n *   <source>  <mount_point> <fs_type> <mount_flags> <fs_options> <fs_mgr_options>\n *\n *   <mount_flags> is a comma separated list of flags that can be passed to the\n *                 mount command.  The list includes noatime, nosuid, nodev, nodiratime,\n *                 ro, rw, remount, defaults.\n *\n *   <fs_options> is a comma separated list of options accepted by the filesystem being\n *                mounted.  It is passed directly to mount without being parsed\n *\n *   <fs_mgr_options> is a comma separated list of flags that control the operation of\n *                     the fs_mgr program.  The list includes \"wait\", which will wait till\n *                     the <source> file exists, and \"check\", which requests that the fs_mgr\n *                     run an fscheck program on the <source> before mounting the filesystem.\n *                     If check is specifed on a read-only filesystem, it is ignored.\n *                     Also, \"encryptable\" means that filesystem can be encrypted.\n *                     The \"encryptable\" flag _MUST_ be followed by a = and a string which\n *                     is the location of the encryption keys.  It can either be a path\n *                     to a file or partition which contains the keys, or the word \"footer\"\n *                     which means the keys are in the last 16 Kbytes of the partition\n *                     containing the filesystem.\n *\n * When the fs_mgr is requested to mount all filesystems, it will first mount all the\n * filesystems that do _NOT_ specify check (including filesystems that are read-only and\n * specify check, because check is ignored in that case) and then it will check and mount\n * filesystem marked with check.\n *\n */\n\n#define DM_BUF_SIZE 4096\n\nusing namespace std::chrono_literals;\n\nbool fs_mgr_is_device_unlocked();\n\nbool fs_mgr_is_f2fs(const std::string& blk_device);\n\nbool fs_mgr_filesystem_available(const std::string& filesystem);\nstd::string fs_mgr_get_context(const std::string& mount_point);\n\nnamespace android {\nnamespace fs_mgr {\n\nbool UnmapDevice(const std::string& name);\n\nstruct OverlayfsCheckResult {\n    bool supported;\n    std::string mount_flags;\n};\n\nOverlayfsCheckResult CheckOverlayfs();\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr_remount.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <getopt.h>\n#include <stdio.h>\n#include <sys/mount.h>\n#include <sys/types.h>\n#include <sys/vfs.h>\n#include <unistd.h>\n\n#include <iostream>\n#include <string>\n#include <thread>\n#include <utility>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android/os/IVold.h>\n#include <binder/IServiceManager.h>\n#include <binder/ProcessState.h>\n#include <bootloader_message/bootloader_message.h>\n#include <cutils/android_reboot.h>\n#include <fs_mgr_overlayfs.h>\n#include <fs_mgr_priv.h>\n#include <fstab/fstab.h>\n#include <libavb_user/libavb_user.h>\n#include <libgsi/libgsid.h>\n#include <private/android_filesystem_config.h>\n\n#include \"fs_mgr_overlayfs_control.h\"\n#include \"fs_mgr_overlayfs_mount.h\"\n\nusing namespace std::literals;\nusing android::fs_mgr::Fstab;\nusing android::fs_mgr::FstabEntry;\n\nnamespace {\n\nvoid usage() {\n    const std::string progname = getprogname();\n    if (progname == \"disable-verity\" || progname == \"enable-verity\" ||\n        progname == \"set-verity-state\") {\n        std::cout << \"Usage: disable-verity\\n\"\n                  << \"       enable-verity\\n\"\n                  << \"       set-verity-state [0|1]\\n\"\n                  << R\"(\nOptions:\n    -h --help       this help\n    -R --reboot     automatic reboot if needed for new settings to take effect\n    -v --verbose    be noisy)\"\n                  << std::endl;\n    } else {\n        std::cout << \"Usage: \" << progname << \" [-h] [-R] [-T fstab_file] [partition]...\\n\"\n                  << R\"(\nOptions:\n    -h --help       this help\n    -R --reboot     disable verity & reboot to facilitate remount\n    -v --verbose    be noisy\n    -T --fstab      custom fstab file location\n    partition       specific partition(s) (empty does all)\n\nRemount specified partition(s) read-write, by name or mount point.\n-R notwithstanding, verity must be disabled on partition(s).\n-R within a DSU guest system reboots into the DSU instead of the host system,\nthis command would enable DSU (one-shot) if not already enabled.)\"\n                  << std::endl;\n    }\n}\n\nconst std::string system_mount_point(const android::fs_mgr::FstabEntry& entry) {\n    if (entry.mount_point == \"/\") return \"/system\";\n    return entry.mount_point;\n}\n\nclass MyLogger {\n  public:\n    explicit MyLogger(bool verbose) : verbose_(verbose) {}\n\n    void operator()(android::base::LogId id, android::base::LogSeverity severity, const char* tag,\n                    const char* file, unsigned int line, const char* message) {\n        // By default, print ERROR logs and logs of this program (does not start with '[')\n        // Print [libfs_mgr] INFO logs only if -v is given.\n        if (verbose_ || severity >= android::base::ERROR || message[0] != '[') {\n            fprintf(stderr, \"%s\\n\", message);\n        }\n        logd_(id, severity, tag, file, line, message);\n    }\n\n  private:\n    android::base::LogdLogger logd_;\n    bool verbose_;\n};\n\n[[noreturn]] void reboot(const std::string& name) {\n    LOG(INFO) << \"Rebooting device for new settings to take effect\";\n    ::sync();\n    android::base::SetProperty(ANDROID_RB_PROPERTY, \"reboot,\" + name);\n    ::sleep(60);\n    LOG(ERROR) << \"Failed to reboot\";\n    ::exit(1);\n}\n\nstatic android::sp<android::os::IVold> GetVold() {\n    auto sm = android::defaultServiceManager();\n    while (true) {\n        if (auto binder = sm->checkService(android::String16(\"vold\"))) {\n            if (auto vold = android::interface_cast<android::os::IVold>(binder)) {\n                return vold;\n            }\n        }\n        std::this_thread::sleep_for(2s);\n    }\n}\n\nstatic bool ReadFstab(const char* fstab_file, android::fs_mgr::Fstab* fstab) {\n    if (fstab_file) {\n        return android::fs_mgr::ReadFstabFromFile(fstab_file, fstab);\n    }\n    if (!android::fs_mgr::ReadDefaultFstab(fstab)) {\n        return false;\n    }\n\n    // Manufacture a / entry from /proc/mounts if missing.\n    if (!GetEntryForMountPoint(fstab, \"/system\") && !GetEntryForMountPoint(fstab, \"/\")) {\n        android::fs_mgr::Fstab mounts;\n        if (android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &mounts)) {\n            if (auto entry = GetEntryForMountPoint(&mounts, \"/\")) {\n                if (entry->fs_type != \"rootfs\") fstab->emplace_back(*entry);\n            }\n        }\n    }\n    return true;\n}\n\nbool VerifyCheckpointing() {\n    if (!android::base::GetBoolProperty(\"ro.virtual_ab.enabled\", false) &&\n        !android::base::GetBoolProperty(\"ro.virtual_ab.retrofit\", false)) {\n        return true;\n    }\n\n    // Virtual A/B devices can use /data as backing storage; make sure we're\n    // not checkpointing.\n    auto vold = GetVold();\n    bool checkpointing = false;\n    bool show_help = true;\n\n    while (true) {\n        if (!vold->isCheckpointing(&checkpointing).isOk()) {\n            LOG(ERROR) << \"Could not determine checkpointing status.\";\n            return false;\n        }\n        if (!checkpointing) {\n            break;\n        }\n        if (show_help) {\n            show_help = false;\n            std::cerr << \"WARNING: Userdata checkpoint is in progress. \"\n                         \"To forcibly end checkpointing, \"\n                         \"call 'vdc checkpoint commitChanges'. \"\n                         \"This can lead to data corruption if rolled back.\"\n                      << std::endl;\n            LOG(INFO) << \"Waiting for checkpoint to complete before remounting...\";\n        }\n        std::this_thread::sleep_for(4s);\n    }\n    return true;\n}\n\nstatic bool IsRemountable(Fstab& candidates, const FstabEntry& entry) {\n    if (entry.fs_mgr_flags.vold_managed || entry.fs_mgr_flags.recovery_only ||\n        entry.fs_mgr_flags.slot_select_other) {\n        return false;\n    }\n    if (!(entry.flags & MS_RDONLY)) {\n        return false;\n    }\n    if (entry.fs_type == \"vfat\") {\n        return false;\n    }\n    if (auto candidate_entry = GetEntryForMountPoint(&candidates, entry.mount_point)) {\n        return candidate_entry->fs_type == entry.fs_type;\n    }\n    return true;\n}\n\nstatic Fstab::const_iterator FindPartition(const Fstab& fstab, const std::string& partition) {\n    Fstab mounts;\n    if (!android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &mounts)) {\n        LOG(ERROR) << \"Failed to read /proc/mounts\";\n        return fstab.end();\n    }\n\n    for (auto iter = fstab.begin(); iter != fstab.end(); iter++) {\n        const auto mount_point = system_mount_point(*iter);\n        if (partition == mount_point || partition == android::base::Basename(mount_point)) {\n            // In case fstab has multiple entries, pick the one that matches the\n            // actual mounted filesystem type.\n            auto proc_mount_point = (iter->mount_point == \"/system\") ? \"/\" : iter->mount_point;\n            auto mounted = GetEntryForMountPoint(&mounts, proc_mount_point);\n            if (mounted && mounted->fs_type == iter->fs_type) {\n                return iter;\n            }\n        }\n    }\n    return fstab.end();\n}\n\nstatic Fstab GetAllRemountablePartitions(Fstab& fstab) {\n    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);\n\n    Fstab partitions;\n    for (const auto& entry : fstab) {\n        if (IsRemountable(candidates, entry)) {\n            partitions.emplace_back(entry);\n        }\n    }\n    return partitions;\n}\n\nbool GetRemountList(const Fstab& fstab, const std::vector<std::string>& argv, Fstab* partitions) {\n    auto candidates = fs_mgr_overlayfs_candidate_list(fstab);\n\n    for (const auto& arg : argv) {\n        std::string partition = arg;\n        if (partition == \"/\") {\n            partition = \"/system\";\n        }\n\n        auto it = FindPartition(fstab, partition);\n        if (it == fstab.end()) {\n            LOG(ERROR) << \"Unknown partition \" << arg;\n            return false;\n        }\n\n        const FstabEntry* entry = &*it;\n\n        // If it's already remounted, include it so it gets gracefully skipped\n        // later on.\n        if (!fs_mgr_overlayfs_already_mounted(entry->mount_point) &&\n            !IsRemountable(candidates, *entry)) {\n            LOG(ERROR) << \"Invalid partition \" << arg;\n            return false;\n        }\n        if (GetEntryForMountPoint(partitions, entry->mount_point) != nullptr) {\n            continue;\n        }\n        partitions->emplace_back(*entry);\n    }\n\n    return true;\n}\n\nstruct RemountCheckResult {\n    bool reboot_later = false;\n    bool setup_overlayfs = false;\n    bool disabled_verity = false;\n    bool verity_error = false;\n    bool remounted_anything = false;\n};\n\nbool CheckOverlayfs(Fstab* partitions, RemountCheckResult* result) {\n    bool ok = true;\n    for (auto it = partitions->begin(); it != partitions->end();) {\n        auto& entry = *it;\n        const auto& mount_point = entry.mount_point;\n\n        if (fs_mgr_wants_overlayfs(&entry)) {\n            bool want_reboot = false;\n            bool force = result->disabled_verity;\n            if (!fs_mgr_overlayfs_setup(*partitions, mount_point.c_str(), &want_reboot, force)) {\n                LOG(ERROR) << \"Overlayfs setup for \" << mount_point << \" failed, skipping\";\n                ok = false;\n                it = partitions->erase(it);\n                continue;\n            }\n            if (want_reboot) {\n                LOG(INFO) << \"Using overlayfs for \" << mount_point;\n                result->reboot_later = true;\n                result->setup_overlayfs = true;\n            }\n        }\n        it++;\n    }\n    return ok;\n}\n\nbool EnableDsuIfNeeded() {\n    auto gsid = android::gsi::GetGsiService();\n    if (!gsid) {\n        return true;\n    }\n\n    auto dsu_running = false;\n    if (auto status = gsid->isGsiRunning(&dsu_running); !status.isOk()) {\n        LOG(ERROR) << \"Failed to get DSU running state: \" << status;\n        return false;\n    }\n    auto dsu_enabled = false;\n    if (auto status = gsid->isGsiEnabled(&dsu_enabled); !status.isOk()) {\n        LOG(ERROR) << \"Failed to get DSU enabled state: \" << status;\n        return false;\n    }\n    if (dsu_running && !dsu_enabled) {\n        std::string dsu_slot;\n        if (auto status = gsid->getActiveDsuSlot(&dsu_slot); !status.isOk()) {\n            LOG(ERROR) << \"Failed to get active DSU slot: \" << status;\n            return false;\n        }\n        LOG(INFO) << \"DSU is running but disabled, enable DSU so that we stay within the \"\n                     \"DSU guest system after reboot\";\n        int error = 0;\n        if (auto status = gsid->enableGsi(/* oneShot = */ true, dsu_slot, &error); !status.isOk()) {\n            LOG(ERROR) << \"Failed to enable DSU: \" << status;\n            return false;\n        }\n        if (error != android::gsi::IGsiService::INSTALL_OK) {\n            LOG(ERROR) << \"Failed to enable DSU, error code: \" << error;\n            return false;\n        }\n        LOG(INFO) << \"Successfully enabled DSU (one-shot mode)\";\n    }\n    return true;\n}\n\nbool RemountPartition(Fstab& fstab, Fstab& mounts, FstabEntry& entry) {\n    // unlock the r/o key for the mount point device\n    if (entry.fs_mgr_flags.logical) {\n        fs_mgr_update_logical_partition(&entry);\n    }\n    auto blk_device = entry.blk_device;\n    auto mount_point = entry.mount_point;\n\n    auto found = false;\n    for (auto it = mounts.rbegin(); it != mounts.rend(); ++it) {\n        auto& rentry = *it;\n        if (mount_point == rentry.mount_point) {\n            blk_device = rentry.blk_device;\n            found = true;\n            break;\n        }\n        // Find overlayfs mount point?\n        if ((mount_point == \"/\" && rentry.mount_point == \"/system\") ||\n            (mount_point == \"/system\" && rentry.mount_point == \"/\")) {\n            blk_device = rentry.blk_device;\n            mount_point = \"/system\";\n            found = true;\n            break;\n        }\n    }\n    if (!found) {\n        PLOG(INFO) << \"skip unmounted partition dev:\" << blk_device << \" mnt:\" << mount_point;\n        return true;\n    }\n    if (blk_device == \"/dev/root\") {\n        auto from_fstab = GetEntryForMountPoint(&fstab, mount_point);\n        if (from_fstab) blk_device = from_fstab->blk_device;\n    }\n    fs_mgr_set_blk_ro(blk_device, false);\n\n    // Find system-as-root mount point?\n    if ((mount_point == \"/system\") && !GetEntryForMountPoint(&mounts, mount_point) &&\n        GetEntryForMountPoint(&mounts, \"/\")) {\n        mount_point = \"/\";\n    }\n\n    // Now remount!\n    for (const auto& mnt_point : {mount_point, entry.mount_point}) {\n        if (::mount(blk_device.c_str(), mnt_point.c_str(), entry.fs_type.c_str(),\n                    MS_REMOUNT | MS_NOATIME, nullptr) == 0) {\n            LOG(INFO) << \"Remounted \" << mnt_point << \" as RW\";\n            return true;\n        }\n        if (errno != EINVAL || mount_point == entry.mount_point) {\n            break;\n        }\n    }\n\n    PLOG(ERROR) << \"failed to remount partition dev:\" << blk_device << \" mnt:\" << mount_point;\n    return false;\n}\n\nstruct SetVerityStateResult {\n    bool success = false;\n    bool want_reboot = false;\n};\n\nSetVerityStateResult SetVerityState(bool enable_verity) {\n    const auto ab_suffix = android::base::GetProperty(\"ro.boot.slot_suffix\", \"\");\n    std::unique_ptr<AvbOps, decltype(&avb_ops_user_free)> ops(avb_ops_user_new(),\n                                                              &avb_ops_user_free);\n    if (!ops) {\n        LOG(ERROR) << \"Error getting AVB ops\";\n        return {};\n    }\n    if (!avb_user_verity_set(ops.get(), ab_suffix.c_str(), enable_verity)) {\n        LOG(ERROR) << \"Error setting verity state\";\n        return {};\n    }\n    bool verification_enabled = false;\n    if (!avb_user_verification_get(ops.get(), ab_suffix.c_str(), &verification_enabled)) {\n        LOG(ERROR) << \"Error getting verification state\";\n        return {};\n    }\n    if (!verification_enabled) {\n        LOG(WARNING) << \"AVB verification is disabled, \"\n                     << (enable_verity ? \"enabling\" : \"disabling\")\n                     << \" verity state may have no effect\";\n        return {.success = true, .want_reboot = false};\n    }\n    const auto verity_mode = android::base::GetProperty(\"ro.boot.veritymode\", \"\");\n    const bool was_enabled = (verity_mode != \"disabled\");\n    if ((was_enabled && enable_verity) || (!was_enabled && !enable_verity)) {\n        LOG(INFO) << \"Verity is already \" << (enable_verity ? \"enabled\" : \"disabled\");\n        return {.success = true, .want_reboot = false};\n    }\n    LOG(INFO) << \"Successfully \" << (enable_verity ? \"enabled\" : \"disabled\") << \" verity\";\n    return {.success = true, .want_reboot = true};\n}\n\nbool SetupOrTeardownOverlayfs(bool enable) {\n    bool want_reboot = false;\n    if (enable) {\n        Fstab fstab;\n        if (!ReadDefaultFstab(&fstab)) {\n            LOG(ERROR) << \"Could not read fstab.\";\n            return want_reboot;\n        }\n        if (!fs_mgr_overlayfs_setup(fstab, nullptr, &want_reboot)) {\n            LOG(ERROR) << \"Overlayfs setup failed.\";\n            return want_reboot;\n        }\n        if (want_reboot) {\n            printf(\"enabling overlayfs\\n\");\n        }\n    } else {\n        auto rv = fs_mgr_overlayfs_teardown(nullptr, &want_reboot);\n        if (rv == OverlayfsTeardownResult::Error) {\n            LOG(ERROR) << \"Overlayfs teardown failed.\";\n            return want_reboot;\n        }\n        if (rv == OverlayfsTeardownResult::Busy) {\n            LOG(ERROR) << \"Overlayfs is still active until reboot.\";\n            return true;\n        }\n        if (want_reboot) {\n            printf(\"disabling overlayfs\\n\");\n        }\n    }\n    return want_reboot;\n}\n\nbool do_remount(Fstab& fstab, const std::vector<std::string>& partition_args,\n                RemountCheckResult* check_result) {\n    Fstab partitions;\n    if (partition_args.empty()) {\n        partitions = GetAllRemountablePartitions(fstab);\n    } else {\n        if (!GetRemountList(fstab, partition_args, &partitions)) {\n            return false;\n        }\n    }\n\n    // Disable verity.\n    auto verity_result = SetVerityState(false /* enable_verity */);\n\n    // Treat error as fatal and suggest reboot only if verity is enabled.\n    // TODO(b/260041315): We check the device mapper for any \"<partition>-verity\" device present\n    // instead of checking ro.boot.veritymode because emulator has incorrect property value.\n    bool must_disable_verity = false;\n    for (const auto& partition : partitions) {\n        if (fs_mgr_is_verity_enabled(partition)) {\n            must_disable_verity = true;\n            break;\n        }\n    }\n    if (must_disable_verity) {\n        if (!verity_result.success) {\n            return false;\n        }\n        if (verity_result.want_reboot) {\n            check_result->reboot_later = true;\n            check_result->disabled_verity = true;\n        }\n    }\n\n    // Optionally setup overlayfs backing.\n    bool ok = CheckOverlayfs(&partitions, check_result);\n\n    if (partitions.empty() || check_result->disabled_verity) {\n        if (partitions.empty()) {\n            LOG(WARNING) << \"No remountable partitions were found.\";\n        }\n        return ok;\n    }\n\n    // Mount overlayfs.\n    if (!fs_mgr_overlayfs_mount_all(&partitions)) {\n        LOG(WARNING) << \"Cannot mount overlayfs for some partitions\";\n        // Continue regardless to handle raw remount case.\n    }\n\n    // Get actual mounts _after_ overlayfs has been added.\n    android::fs_mgr::Fstab mounts;\n    if (!android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &mounts) || mounts.empty()) {\n        PLOG(ERROR) << \"Failed to read /proc/mounts\";\n        return false;\n    }\n\n    // Remount selected partitions.\n    for (auto& entry : partitions) {\n        if (RemountPartition(fstab, mounts, entry)) {\n            check_result->remounted_anything = true;\n        } else {\n            ok = false;\n        }\n    }\n    return ok;\n}\n\n}  // namespace\n\nint main(int argc, char* argv[]) {\n    // Do not use MyLogger() when running as clean_scratch_files, as stdout/stderr of daemon process\n    // are discarded.\n    if (argc > 0 && android::base::Basename(argv[0]) == \"clean_scratch_files\"s) {\n        android::fs_mgr::CleanupOldScratchFiles();\n        return EXIT_SUCCESS;\n    }\n\n    android::base::InitLogging(argv, MyLogger(false /* verbose */));\n\n    const char* fstab_file = nullptr;\n    bool auto_reboot = false;\n    bool verbose = false;\n    std::vector<std::string> partition_args;\n\n    struct option longopts[] = {\n            {\"fstab\", required_argument, nullptr, 'T'},\n            {\"help\", no_argument, nullptr, 'h'},\n            {\"reboot\", no_argument, nullptr, 'R'},\n            {\"verbose\", no_argument, nullptr, 'v'},\n            {0, 0, nullptr, 0},\n    };\n    for (int opt; (opt = ::getopt_long(argc, argv, \"hRT:v\", longopts, nullptr)) != -1;) {\n        switch (opt) {\n            case 'h':\n                usage();\n                return EXIT_SUCCESS;\n            case 'R':\n                auto_reboot = true;\n                break;\n            case 'T':\n                if (fstab_file) {\n                    LOG(ERROR) << \"Cannot supply two fstabs: -T \" << fstab_file << \" -T \" << optarg;\n                    usage();\n                    return EXIT_FAILURE;\n                }\n                fstab_file = optarg;\n                break;\n            case 'v':\n                verbose = true;\n                break;\n            default:\n                LOG(ERROR) << \"Bad argument -\" << char(opt);\n                usage();\n                return EXIT_FAILURE;\n        }\n    }\n\n    if (verbose) {\n        android::base::SetLogger(MyLogger(verbose));\n    }\n\n    bool remount = false;\n    bool enable_verity = false;\n    const std::string progname = getprogname();\n    if (progname == \"enable-verity\") {\n        enable_verity = true;\n    } else if (progname == \"disable-verity\") {\n        enable_verity = false;\n    } else if (progname == \"set-verity-state\") {\n        if (optind < argc && (argv[optind] == \"1\"s || argv[optind] == \"0\"s)) {\n            enable_verity = (argv[optind] == \"1\"s);\n        } else {\n            usage();\n            return EXIT_FAILURE;\n        }\n    } else {\n        remount = true;\n        for (; optind < argc; ++optind) {\n            partition_args.emplace_back(argv[optind]);\n        }\n    }\n\n    // Make sure we are root.\n    if (const auto uid = ::getuid(); uid != AID_ROOT) {\n        // If requesting auto reboot, also try to auto gain root.\n        if (auto_reboot && uid == AID_SHELL && access(\"/system/xbin/su\", F_OK) == 0) {\n            std::vector<char*> args{const_cast<char*>(\"/system/xbin/su\"),\n                                    const_cast<char*>(\"root\")};\n            for (int i = 0; i < argc; ++i) {\n                args.push_back(argv[i]);\n            }\n            args.push_back(nullptr);\n            LOG(INFO) << \"Attempting to gain root with \\\"su root\\\"\";\n            execv(args[0], args.data());\n            PLOG(ERROR) << \"Failed to execute \\\"su root\\\"\";\n        }\n        LOG(ERROR) << \"Not running as root. Try \\\"adb root\\\" first.\";\n        return EXIT_FAILURE;\n    }\n\n    // If somehow this executable is delivered on a \"user\" build, it can\n    // not function, so providing a clear message to the caller rather than\n    // letting if fall through and provide a lot of confusing failure messages.\n    if (!ALLOW_ADBD_DISABLE_VERITY || !android::base::GetBoolProperty(\"ro.debuggable\", false)) {\n        LOG(ERROR) << \"Device must be userdebug build\";\n        return EXIT_FAILURE;\n    }\n\n    if (android::base::GetProperty(\"ro.boot.verifiedbootstate\", \"\") != \"orange\") {\n        LOG(ERROR) << \"Device must be bootloader unlocked\";\n        return EXIT_FAILURE;\n    }\n\n    // Start a threadpool to service waitForService() callbacks as\n    // fs_mgr_overlayfs_* might call waitForService() to get the image service.\n    android::ProcessState::self()->startThreadPool();\n\n    if (!remount) {\n        auto ret = SetVerityState(enable_verity);\n\n        // Disable any overlayfs unconditionally if we want verity enabled.\n        // Enable overlayfs only if verity is successfully disabled or is already disabled.\n        if (enable_verity || ret.success) {\n            ret.want_reboot |= SetupOrTeardownOverlayfs(!enable_verity);\n        }\n\n        if (ret.want_reboot) {\n            if (auto_reboot) {\n                reboot(progname);\n            }\n            std::cout << \"Reboot the device for new settings to take effect\" << std::endl;\n        }\n        return ret.success ? EXIT_SUCCESS : EXIT_FAILURE;\n    }\n\n    // Make sure checkpointing is disabled if necessary.\n    if (!VerifyCheckpointing()) {\n        return EXIT_FAILURE;\n    }\n\n    // Read the selected fstab.\n    Fstab fstab;\n    if (!ReadFstab(fstab_file, &fstab) || fstab.empty()) {\n        PLOG(ERROR) << \"Failed to read fstab\";\n        return EXIT_FAILURE;\n    }\n\n    RemountCheckResult check_result;\n    bool remount_success = do_remount(fstab, partition_args, &check_result);\n\n    if (check_result.disabled_verity && check_result.setup_overlayfs) {\n        LOG(INFO) << \"Verity disabled; overlayfs enabled.\";\n    } else if (check_result.disabled_verity) {\n        LOG(INFO) << \"Verity disabled.\";\n    } else if (check_result.setup_overlayfs) {\n        LOG(INFO) << \"Overlayfs enabled.\";\n    }\n    if (remount_success && check_result.remounted_anything) {\n        LOG(INFO) << \"Remount succeeded\";\n    } else if (!remount_success) {\n        LOG(ERROR) << \"Remount failed\";\n    }\n    if (check_result.reboot_later) {\n        if (auto_reboot) {\n            // If (1) remount requires a reboot to take effect, (2) system is currently\n            // running a DSU guest and (3) DSU is disabled, then enable DSU so that the\n            // next reboot would not take us back to the host system but stay within\n            // the guest system.\n            if (!EnableDsuIfNeeded()) {\n                LOG(ERROR) << \"Unable to automatically enable DSU\";\n                return EXIT_FAILURE;\n            }\n            reboot(\"remount\");\n        } else {\n            LOG(INFO) << \"Now reboot your device for settings to take effect\";\n        }\n        return EXIT_SUCCESS;\n    }\n    return remount_success ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n"
  },
  {
    "path": "fs_mgr/fs_mgr_roots.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"android-base/file.h\"\n#include \"fs_mgr/roots.h\"\n\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include \"fs_mgr.h\"\n#include \"fs_mgr_dm_linear.h\"\n#include \"fs_mgr_priv.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nstatic constexpr const char* kSystemRoot = \"/system\";\n\nstatic bool gDidMapLogicalPartitions = false;\n\nFstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path) {\n    if (path.empty()) return nullptr;\n    std::string str(path);\n    while (true) {\n        auto entry = GetEntryForMountPoint(fstab, str);\n        if (entry != nullptr) return entry;\n        str = android::base::Dirname(str);\n        if (!str.compare(\".\") || !str.compare(\"/\")) break;\n    }\n    return nullptr;\n}\n\nstd::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path) {\n    std::vector<FstabEntry*> entries;\n    if (path.empty()) return entries;\n\n    std::string str(path);\n    while (true) {\n        entries = GetEntriesForMountPoint(fstab, str);\n        if (!entries.empty()) return entries;\n        str = android::base::Dirname(str);\n        if (!str.compare(\".\") || !str.compare(\"/\")) break;\n    }\n    return entries;\n}\n\nenum class MountState {\n    ERROR = -1,\n    NOT_MOUNTED = 0,\n    MOUNTED = 1,\n};\n\nstatic MountState GetMountState(const std::string& mount_point) {\n    Fstab mounted_fstab;\n    if (!ReadFstabFromFile(\"/proc/mounts\", &mounted_fstab)) {\n        LERROR << \"Failed to scan mounted volumes\";\n        return MountState::ERROR;\n    }\n\n    auto mv = GetEntryForMountPoint(&mounted_fstab, mount_point);\n    if (mv != nullptr) {\n        return MountState::MOUNTED;\n    }\n    return MountState::NOT_MOUNTED;\n}\n\nbool TryPathMount(FstabEntry* rec, const std::string& mount_pt) {\n    if (rec->fs_type == \"ramdisk\") {\n        // The ramdisk is always mounted.\n        return true;\n    }\n\n    // If we can't acquire the block device for a logical partition, it likely\n    // was never created. In that case we try to create it.\n    if (rec->fs_mgr_flags.logical && !fs_mgr_update_logical_partition(rec)) {\n        if (gDidMapLogicalPartitions) {\n            LERROR << \"Failed to find block device for partition\";\n            return false;\n        }\n        std::string super_name = fs_mgr_get_super_partition_name();\n        if (!android::fs_mgr::CreateLogicalPartitions(\"/dev/block/by-name/\" + super_name)) {\n            LERROR << \"Failed to create logical partitions\";\n            return false;\n        }\n        gDidMapLogicalPartitions = true;\n        if (!fs_mgr_update_logical_partition(rec)) {\n            LERROR << \"Failed to find block device for partition\";\n            return false;\n        }\n    }\n\n    const std::string mount_point = mount_pt.empty() ? rec->mount_point : mount_pt;\n\n    auto mounted = GetMountState(mount_point);\n    if (mounted == MountState::ERROR) {\n        return false;\n    }\n    if (mounted == MountState::MOUNTED) {\n        return true;\n    }\n\n    static const std::vector<std::string> supported_fs{\"ext4\", \"squashfs\", \"vfat\", \"exfat\", \"f2fs\",\n                                                       \"erofs\", \"none\"};\n    if (std::find(supported_fs.begin(), supported_fs.end(), rec->fs_type) == supported_fs.end()) {\n        LERROR << \"unknown fs_type \\\"\" << rec->fs_type << \"\\\" for \" << mount_point;\n        return false;\n    }\n\n    int result = fs_mgr_do_mount_one(*rec, mount_point);\n    if (result == -1 && rec->fs_mgr_flags.formattable) {\n        PERROR << \"Failed to mount \" << mount_point << \"; formatting\";\n        if (fs_mgr_do_format(*rec) != 0) {\n            PERROR << \"Failed to format \" << mount_point;\n            return false;\n        }\n        result = fs_mgr_do_mount_one(*rec, mount_point);\n    }\n\n    if (result == -1) {\n        PERROR << \"Failed to mount \" << mount_point;\n        return false;\n    }\n    return true;\n}\n\nbool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point) {\n    auto entries = GetEntriesForPath(fstab, path);\n    if (entries.empty()) {\n        LERROR << \"unknown volume for path [\" << path << \"]\";\n        return false;\n    }\n\n    for (auto entry : entries) {\n        if (TryPathMount(entry, mount_point)) return true;\n    }\n\n    LERROR << \"Failed to mount for path [\" << path << \"]\";\n    return false;\n}\n\nbool EnsurePathUnmounted(Fstab* fstab, const std::string& path) {\n    auto rec = GetEntryForPath(fstab, path);\n    if (rec == nullptr) {\n        LERROR << \"unknown volume for path [\" << path << \"]\";\n        return false;\n    }\n    if (rec->fs_type == \"ramdisk\") {\n        // The ramdisk is always mounted; you can't unmount it.\n        return false;\n    }\n\n    Fstab mounted_fstab;\n    if (!ReadFstabFromFile(\"/proc/mounts\", &mounted_fstab)) {\n        LERROR << \"Failed to scan mounted volumes\";\n        return false;\n    }\n\n    auto mounted = GetMountState(rec->mount_point);\n    if (mounted == MountState::ERROR) {\n        return false;\n    }\n    if (mounted == MountState::NOT_MOUNTED) {\n        return true;\n    }\n\n    int result = umount(rec->mount_point.c_str());\n    if (result == -1) {\n        PWARNING << \"Failed to umount \" << rec->mount_point;\n        return false;\n    }\n    return true;\n}\n\nstd::string GetSystemRoot() {\n    Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        LERROR << \"Failed to read default fstab\";\n        return \"\";\n    }\n\n    auto entry = GetEntryForMountPoint(&fstab, kSystemRoot);\n    if (entry == nullptr) {\n        return \"/\";\n    }\n\n    return kSystemRoot;\n}\n\nbool LogicalPartitionsMapped() {\n    return gDidMapLogicalPartitions;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/fs_mgr_vendor_overlay.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <dirent.h>\n#include <selinux/selinux.h>\n#include <sys/mount.h>\n#include <unistd.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <fs_mgr_vendor_overlay.h>\n#include <fstab/fstab.h>\n\n#include \"fs_mgr_priv.h\"\n\nusing namespace std::literals;\n\nnamespace {\n\n// The order of the list means the priority to show the files in the directory.\n// The last one has the highest priority.\nconst std::vector<std::string> kVendorOverlaySourceDirs = {\n        \"/system/vendor_overlay/\",\n        \"/product/vendor_overlay/\",\n};\nconst auto kVndkVersionPropertyName = \"ro.vndk.version\"s;\nconst auto kVendorTopDir = \"/vendor/\"s;\nconst auto kLowerdirOption = \"lowerdir=\"s;\n\nstd::vector<std::pair<std::string, std::string>> fs_mgr_get_vendor_overlay_dirs(\n        const std::string& vndk_version) {\n    std::vector<std::pair<std::string, std::string>> vendor_overlay_dirs;\n    for (const auto& vendor_overlay_source : kVendorOverlaySourceDirs) {\n        const auto overlay_top = vendor_overlay_source + vndk_version;\n        std::unique_ptr<DIR, decltype(&closedir)> vendor_overlay_top(opendir(overlay_top.c_str()),\n                                                                     closedir);\n        if (!vendor_overlay_top) continue;\n\n        // Vendor overlay root for current vendor version found!\n        LINFO << \"vendor overlay root: \" << overlay_top;\n\n        struct dirent* dp;\n        while ((dp = readdir(vendor_overlay_top.get())) != nullptr) {\n            if (dp->d_type != DT_DIR || dp->d_name[0] == '.') {\n                continue;\n            }\n            vendor_overlay_dirs.emplace_back(overlay_top, dp->d_name);\n        }\n    }\n    return vendor_overlay_dirs;\n}\n\nbool fs_mgr_vendor_overlay_mount(const std::pair<std::string, std::string>& mount_point) {\n    const auto [overlay_top, mount_dir] = mount_point;\n    const auto vendor_mount_point = kVendorTopDir + mount_dir;\n    LINFO << \"vendor overlay mount on \" << vendor_mount_point;\n\n    const auto target_context = fs_mgr_get_context(vendor_mount_point);\n    if (target_context.empty()) {\n        PERROR << \" failed: cannot find the target vendor mount point\";\n        return false;\n    }\n    const auto source_directory = overlay_top + \"/\" + mount_dir;\n    const auto source_context = fs_mgr_get_context(source_directory);\n    if (target_context != source_context) {\n        LERROR << \" failed: source and target contexts do not match (source:\" << source_context\n               << \", target:\" << target_context << \")\";\n        return false;\n    }\n\n    const auto options = kLowerdirOption + source_directory + \":\" + vendor_mount_point +\n                         android::fs_mgr::CheckOverlayfs().mount_flags;\n    auto report = \"__mount(source=overlay,target=\"s + vendor_mount_point + \",type=overlay,\" +\n                  options + \")=\";\n    auto ret = mount(\"overlay\", vendor_mount_point.c_str(), \"overlay\", MS_RDONLY | MS_NOATIME,\n                     options.c_str());\n    if (ret) {\n        PERROR << report << ret;\n        return false;\n    } else {\n        LINFO << report << ret;\n        return true;\n    }\n}\n\n}  // namespace\n\n// Since the vendor overlay requires to know the version of the vendor partition,\n// it is not possible to mount vendor overlay at the first stage that cannot\n// initialize properties.\n// To read the properties, vendor overlay must be mounted at the second stage, right\n// after \"property_load_boot_defaults()\" is called.\nbool fs_mgr_vendor_overlay_mount_all() {\n    // To read the property, it must be called at the second init stage after the default\n    // properties are loaded.\n    static const auto vndk_version = android::base::GetProperty(kVndkVersionPropertyName, \"\");\n    if (vndk_version.empty()) {\n        // Vendor overlay is disabled from VNDK deprecated devices.\n        LINFO << \"vendor overlay: vndk version not defined\";\n        return false;\n    }\n\n    const auto vendor_overlay_dirs = fs_mgr_get_vendor_overlay_dirs(vndk_version);\n    if (vendor_overlay_dirs.empty()) return true;\n    if (!android::fs_mgr::CheckOverlayfs().supported) {\n        LINFO << \"vendor overlay: kernel does not support overlayfs\";\n        return false;\n    }\n\n    // Mount each directory in /(system|product)/vendor_overlay/<ver> on /vendor\n    auto ret = true;\n    for (const auto& vendor_overlay_dir : vendor_overlay_dirs) {\n        if (!fs_mgr_vendor_overlay_mount(vendor_overlay_dir)) {\n            ret = false;\n        }\n    }\n    return ret;\n}\n"
  },
  {
    "path": "fs_mgr/include/fs_mgr/file_wait.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <chrono>\n#include <string>\n\nnamespace android {\nnamespace fs_mgr {\n\n// Wait at most |relative_timeout| milliseconds for |path| to exist. dirname(path)\n// must already exist. For example, to wait on /dev/block/dm-6, /dev/block must\n// be a valid directory.\n//\n// If relative_timeout is std::chrono::milliseconds::max(), then the wait will\n// block indefinitely.\nbool WaitForFile(const std::string& path, const std::chrono::milliseconds relative_timeout);\n\n// Wait at most |relative_timeout| milliseconds for |path| to stop existing.\n// Note that this only returns true if the inode itself no longer exists, i.e.,\n// all outstanding file descriptors have been closed.\nbool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds relative_timeout);\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/include/fs_mgr/roots.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <fs_mgr.h>\n\nnamespace android {\nnamespace fs_mgr {\n\n// Finds the volume specified by the given path. fs_mgr_get_entry_for_mount_point() does exact match\n// only, so it attempts the prefixes recursively (e.g. \"/cache/recovery/last_log\",\n// \"/cache/recovery\", \"/cache\", \"/\" for a given path of \"/cache/recovery/last_log\") and returns the\n// first match or nullptr.\nFstabEntry* GetEntryForPath(Fstab* fstab, const std::string& path);\n\nstd::vector<FstabEntry*> GetEntriesForPath(Fstab* fstab, const std::string& path);\n\n// Make sure that the volume 'path' is on is mounted.\n// * If 'mount_point' is nullptr, use mount point in fstab. Caller can call\n//   fs_mgr_ensure_path_unmounted() with the same 'path' argument to unmount.\n// * If 'mount_point' is not nullptr, the mount point is overridden. Caller can\n//   call umount(mount_point) to unmount.\n// Returns true on success (volume is mounted).\nbool EnsurePathMounted(Fstab* fstab, const std::string& path, const std::string& mount_point = \"\");\n\n// Make sure that the volume 'path' is on is unmounted.  Returns true on\n// success (volume is unmounted).\nbool EnsurePathUnmounted(Fstab* fstab, const std::string& path);\n\n// Return \"/system\" if it is in default fstab, otherwise \"/\".\nstd::string GetSystemRoot();\n\n// Return true iff logical partitions are mapped when partitions are mounted via ensure_path_mounted\n// functions.\nbool LogicalPartitionsMapped();\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/include/fs_mgr.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdbool.h>\n#include <linux/dm-ioctl.h>\n\n#include <functional>\n#include <optional>\n#include <string>\n\n#include <fstab/fstab.h>\n\n// Magic number at start of verity metadata\n#define VERITY_METADATA_MAGIC_NUMBER 0xb001b001\n\n// Replacement magic number at start of verity metadata to cleanly\n// turn verity off in userdebug builds.\n#define VERITY_METADATA_MAGIC_DISABLE 0x46464f56 // \"VOFF\"\n\n// Verity modes\nenum verity_mode {\n    VERITY_MODE_EIO = 0,\n    VERITY_MODE_LOGGING = 1,\n    VERITY_MODE_RESTART = 2,\n    VERITY_MODE_LAST = VERITY_MODE_RESTART,\n    VERITY_MODE_DEFAULT = VERITY_MODE_RESTART\n};\n\n// Mount modes\nenum mount_mode {\n    MOUNT_MODE_DEFAULT = 0,\n    MOUNT_MODE_EARLY = 1,\n    MOUNT_MODE_LATE = 2,\n    // TODO(b/135984674): remove this after refactoring fs_mgr_mount_all.\n    MOUNT_MODE_ONLY_USERDATA = 3\n};\n\n#define FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED 7\n#define FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION 6\n#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5\n#define FS_MGR_MNTALL_DEV_NEEDS_RECOVERY 4\n#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0\n#define FS_MGR_MNTALL_FAIL (-1)\n// fs_mgr_mount_all() updates fstab entries that reference device-mapper.\nint fs_mgr_mount_all(android::fs_mgr::Fstab* fstab, int mount_mode);\n\nstruct HashtreeInfo {\n    // The hash algorithm used to build the merkle tree.\n    std::string algorithm;\n    // The root digest of the merkle tree.\n    std::string root_digest;\n    // If check_at_most_once is enabled.\n    bool check_at_most_once;\n};\n\n#define FS_MGR_DOMNT_FAILED (-1)\n#define FS_MGR_DOMNT_BUSY (-2)\n#define FS_MGR_DOMNT_SUCCESS 0\nint fs_mgr_do_mount(android::fs_mgr::Fstab* fstab, const std::string& n_name,\n                    const std::string& n_blk_device, int needs_checkpoint, bool needs_encrypt);\nint fs_mgr_do_mount_one(const android::fs_mgr::FstabEntry& entry,\n                        const std::string& mount_point = \"\");\nbool fs_mgr_load_verity_state(int* mode);\n// Returns true if verity is enabled on this particular FstabEntry.\nbool fs_mgr_is_verity_enabled(const android::fs_mgr::FstabEntry& entry);\n// Returns the verity hashtree information of this particular FstabEntry. Returns std::nullopt\n// if the input isn't a dm-verity entry, or if there is an error.\nstd::optional<HashtreeInfo> fs_mgr_get_hashtree_info(const android::fs_mgr::FstabEntry& entry);\n\nbool fs_mgr_swapon_all(const android::fs_mgr::Fstab& fstab);\nbool fs_mgr_update_logical_partition(android::fs_mgr::FstabEntry* entry);\n\n// Returns true if the given fstab entry has verity enabled, *and* the verity\n// device is in \"check_at_most_once\" mode.\nbool fs_mgr_verity_is_check_at_most_once(const android::fs_mgr::FstabEntry& entry);\n\nint fs_mgr_do_format(const android::fs_mgr::FstabEntry& entry);\n\n#define FS_MGR_SETUP_VERITY_SKIPPED  (-3)\n#define FS_MGR_SETUP_VERITY_DISABLED (-2)\n#define FS_MGR_SETUP_VERITY_FAIL (-1)\n#define FS_MGR_SETUP_VERITY_SUCCESS 0\nint fs_mgr_setup_verity(android::fs_mgr::FstabEntry* fstab, bool wait_for_verity_dev);\n\n// Return the name of the super partition if it exists. If a slot number is\n// specified, the super partition for the corresponding metadata slot will be\n// returned. Otherwise, it will use the current slot.\nstd::string fs_mgr_get_super_partition_name(int slot = -1);\n\n// Set readonly for the block device\nbool fs_mgr_set_blk_ro(const std::string& blockdev, bool readonly = true);\n\n// Check if the block device has ext4 filesystem\nbool fs_mgr_is_ext4(const std::string& blk_device);\n\nenum FsMgrUmountStatus : int {\n    SUCCESS = 0,\n    ERROR_UNKNOWN = 1 << 0,\n    ERROR_UMOUNT = 1 << 1,\n    ERROR_VERITY = 1 << 2,\n    ERROR_DEVICE_MAPPER = 1 << 3,\n};\n// fs_mgr_umount_all() is the reverse of fs_mgr_mount_all. In particular,\n// it destroys verity devices from device mapper after the device is unmounted.\nint fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);\n\n// Finds the dm_bow device on which this block device is stacked, or returns\n// empty string\nstd::string fs_mgr_find_bow_device(const std::string& block_device);\n\n// Creates mount point if not already existed, and checks that mount point is a\n// canonical path that doesn't contain any symbolic link or /../.\nbool fs_mgr_create_canonical_mount_point(const std::string& mount_point);\n\n// Like fs_mgr_do_mount_one() but for overlayfs fstab entries.\n// Unlike fs_mgr_overlayfs, mount overlayfs without upperdir and workdir, so the\n// filesystem cannot be remount read-write.\nbool fs_mgr_mount_overlayfs_fstab_entry(const android::fs_mgr::FstabEntry& entry);\n\n// File name used to track if encryption was interrupted, leading to a known bad fs state\nstd::string fs_mgr_metadata_encryption_in_progress_file_name(\n        const android::fs_mgr::FstabEntry& entry);\n\n// Returns the ideal block size for make_f2fs. Returns -1 on failure.\nint fs_mgr_f2fs_ideal_block_size();\n"
  },
  {
    "path": "fs_mgr/include/fs_mgr_dm_linear.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use, copy,\n * modify, merge, publish, distribute, sublicense, and/or sell copies\n * of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#ifndef __CORE_FS_MGR_DM_LINEAR_H\n#define __CORE_FS_MGR_DM_LINEAR_H\n\n#include <stdint.h>\n\n#include <chrono>\n#include <memory>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include <libdm/dm.h>\n#include <liblp/liblp.h>\n\nnamespace android {\nnamespace fs_mgr {\n\n// Read metadata from the current slot.\nstd::unique_ptr<LpMetadata> ReadCurrentMetadata(const std::string& block_device);\n\n// Create block devices for all logical partitions in the given metadata. The\n// metadata must have been read from the current slot.\nbool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& block_device);\n\n// Create block devices for all logical partitions. This is a convenience\n// method for ReadMetadata and CreateLogicalPartitions.\nbool CreateLogicalPartitions(const std::string& block_device);\n\nstruct CreateLogicalPartitionParams {\n    // Block device of the super partition.\n    std::string block_device;\n\n    // If |metadata| is null, the slot will be read using |metadata_slot|.\n    const LpMetadata* metadata = nullptr;\n    std::optional<uint32_t> metadata_slot;\n\n    // If |partition| is not set, it will be found via |partition_name|.\n    const LpMetadataPartition* partition = nullptr;\n    std::string partition_name;\n\n    // Force the device to be read-write even if it was specified as readonly\n    // in the metadata.\n    bool force_writable = false;\n\n    // If |timeout_ms| is non-zero, then CreateLogicalPartition will block for\n    // the given amount of time until the path returned in |path| is available.\n    std::chrono::milliseconds timeout_ms = {};\n\n    // If this is non-empty, it will override the device mapper name (by\n    // default the partition name will be used).\n    std::string device_name;\n\n    // If non-null, this will use the specified IPartitionOpener rather than\n    // the default one.\n    const IPartitionOpener* partition_opener = nullptr;\n\n    // Helpers for determining the effective partition and device name.\n    std::string GetPartitionName() const;\n    std::string GetDeviceName() const;\n\n    // Specify ownership of fields. The ownership of these fields are managed\n    // by the caller of InitDefaults().\n    // These are not declared in CreateLogicalPartitionParams so that the\n    // copy constructor is not deleted.\n    struct OwnedData {\n        std::unique_ptr<LpMetadata> metadata;\n        std::unique_ptr<IPartitionOpener> partition_opener;\n    };\n\n    // Fill in default values for |params| that CreateLogicalPartition assumes. Caller does\n    // not need to call this before calling CreateLogicalPartition; CreateLogicalPartition sets\n    // values when they are missing.\n    // Caller is responsible for destroying owned_data when |this| is not used.\n    bool InitDefaults(OwnedData* owned);\n};\n\nbool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path);\n\n// Destroy the block device for a logical partition, by name. If |timeout_ms|\n// is non-zero, then this will block until the device path has been unlinked.\nbool DestroyLogicalPartition(const std::string& name);\n\n// Helper for populating a DmTable for a logical partition.\nbool CreateDmTable(CreateLogicalPartitionParams params, android::dm::DmTable* table);\n\n}  // namespace fs_mgr\n}  // namespace android\n\n#endif  // __CORE_FS_MGR_DM_LINEAR_H\n"
  },
  {
    "path": "fs_mgr/include/fs_mgr_overlayfs.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <set>\n#include <string>\n\n#include <fstab/fstab.h>\n\n// Keep the list short and only add interfaces that must be exported public.\n\nbool fs_mgr_overlayfs_mount_all(android::fs_mgr::Fstab* fstab);\nbool fs_mgr_overlayfs_is_setup();\n\nnamespace android {\nnamespace fs_mgr {\n\n// Mount the overlayfs override for |fstab_entry|.\nvoid MountOverlayfs(const FstabEntry& fstab_entry, bool* scratch_can_be_mounted);\n\nvoid MapScratchPartitionIfNeeded(Fstab* fstab,\n                                 const std::function<bool(const std::set<std::string>&)>& init);\n\n// Teardown overlays of all sources (cache dir, scratch device, DSU) for |mount_point|.\n// Teardown all overlays if |mount_point| is empty.\n//\n// Note: This should be called if and only if in recovery or fastbootd to teardown\n// overlays if any partition is flashed or updated.\nvoid TeardownAllOverlayForMountPoint(const std::string& mount_point = {});\n\n// Are we using overlayfs's non-upstreamed override_creds feature?\n// b/388912628 removes the need for override_creds\n// Once this bug is fixed and has had enough soak time, remove this variable and hard code to false\n// where it used\nconstexpr bool use_override_creds = false;\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/include/fs_mgr_vendor_overlay.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fstab/fstab.h>\n\nbool fs_mgr_vendor_overlay_mount_all();\n"
  },
  {
    "path": "fs_mgr/libdm/Android.bp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libdm\",\n    defaults: [\"fs_mgr_defaults\"],\n    recovery_available: true,\n    host_supported: true,\n\n    export_include_dirs: [\"include\"],\n\n    srcs: [\n        \"dm_table.cpp\",\n        \"dm_target.cpp\",\n        \"dm.cpp\",\n        \"loop_control.cpp\",\n        \"utility.cpp\",\n    ],\n\n    static_libs: [\n        \"libext2_uuid\",\n    ],\n    header_libs: [\n        \"libbase_headers\",\n        \"liblog_headers\",\n    ],\n    target: {\n        darwin: {\n            enabled: false,\n        },\n    },\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n}\n\nfilegroup {\n    name: \"libdm_test_srcs\",\n    srcs: [\n        \"dm_test.cpp\",\n        \"loop_control_test.cpp\",\n        \"test_util.cpp\",\n    ],\n}\n\ncc_defaults {\n    name: \"libdm_test_defaults\",\n    defaults: [\"fs_mgr_defaults\"],\n    static_libs: [\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libfs_mgr\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    srcs: [\":libdm_test_srcs\"],\n    auto_gen_config: true,\n    require_root: true,\n}\n\ncc_test {\n    name: \"libdm_test\",\n    defaults: [\"libdm_test_defaults\"],\n    test_suites: [\"device-tests\"],\n}\n\ncc_test {\n    name: \"vts_libdm_test\",\n    defaults: [\"libdm_test_defaults\"],\n    test_suites: [\"vts\"],\n    test_options: {\n        min_shipping_api_level: 29,\n    },\n}\n"
  },
  {
    "path": "fs_mgr/libdm/dm.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libdm/dm.h\"\n\n#include <linux/dm-ioctl.h>\n#include <sys/ioctl.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n\n#include <chrono>\n#include <functional>\n#include <string_view>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <uuid/uuid.h>\n\n#include \"utility.h\"\n\n#ifndef DM_DEFERRED_REMOVE\n#define DM_DEFERRED_REMOVE (1 << 17)\n#endif\n#ifndef DM_IMA_MEASUREMENT_FLAG\n#define DM_IMA_MEASUREMENT_FLAG (1 << 19)\n#endif\n\nnamespace android {\nnamespace dm {\n\nusing namespace std::literals;\n\nDeviceMapper::DeviceMapper() : fd_(-1) {\n    fd_ = TEMP_FAILURE_RETRY(open(\"/dev/device-mapper\", O_RDWR | O_CLOEXEC));\n    if (fd_ < 0) {\n        PLOG(ERROR) << \"Failed to open device-mapper\";\n    }\n}\n\nDeviceMapper& DeviceMapper::Instance() {\n    static DeviceMapper instance;\n    return instance;\n}\n\n// Creates a new device mapper device\nbool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {\n    if (name.empty()) {\n        LOG(ERROR) << \"Unnamed device mapper device creation is not supported\";\n        return false;\n    }\n    if (name.size() >= DM_NAME_LEN) {\n        LOG(ERROR) << \"[\" << name << \"] is too long to be device mapper name\";\n        return false;\n    }\n\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (!uuid.empty()) {\n        snprintf(io.uuid, sizeof(io.uuid), \"%s\", uuid.c_str());\n    }\n\n    if (ioctl(fd_, DM_DEV_CREATE, &io)) {\n        PLOG(ERROR) << \"DM_DEV_CREATE failed for [\" << name << \"]\";\n        return false;\n    }\n\n    // Check to make sure the newly created device doesn't already have targets\n    // added or opened by someone\n    CHECK(io.target_count == 0) << \"Unexpected targets for newly created [\" << name << \"] device\";\n    CHECK(io.open_count == 0) << \"Unexpected opens for newly created [\" << name << \"] device\";\n\n    // Creates a new device mapper device with the name passed in\n    return true;\n}\n\nbool DeviceMapper::DeleteDeviceIfExists(const std::string& name,\n                                        const std::chrono::milliseconds& timeout_ms) {\n    if (GetState(name) == DmDeviceState::INVALID) {\n        return true;\n    }\n    return DeleteDevice(name, timeout_ms);\n}\n\nbool DeviceMapper::DeleteDeviceIfExists(const std::string& name) {\n    return DeleteDeviceIfExists(name, 0ms);\n}\n\nbool DeviceMapper::DeleteDevice(const std::string& name,\n                                const std::chrono::milliseconds& timeout_ms) {\n    std::string unique_path;\n    if (!GetDeviceUniquePath(name, &unique_path)) {\n        LOG(ERROR) << \"Failed to get unique path for device \" << name;\n    }\n    // Expect to have uevent generated if the unique path actually exists. This may not exist\n    // if the device was created but has never been activated before it gets deleted.\n    bool need_uevent = !unique_path.empty() && access(unique_path.c_str(), F_OK) == 0;\n\n    struct dm_ioctl io;\n    InitIo(&io, name);\n\n    if (ioctl(fd_, DM_DEV_REMOVE, &io)) {\n        PLOG(ERROR) << \"DM_DEV_REMOVE failed for [\" << name << \"]\";\n        return false;\n    }\n\n    // Check to make sure appropriate uevent is generated so ueventd will\n    // do the right thing and remove the corresponding device node and symlinks.\n    if (need_uevent && (io.flags & DM_UEVENT_GENERATED_FLAG) == 0) {\n        LOG(ERROR) << \"Didn't generate uevent for [\" << name << \"] removal\";\n        return false;\n    }\n\n    if (timeout_ms <= std::chrono::milliseconds::zero()) {\n        return true;\n    }\n    if (unique_path.empty()) {\n        return false;\n    }\n    if (!WaitForFileDeleted(unique_path, timeout_ms)) {\n        LOG(ERROR) << \"Failed waiting for \" << unique_path << \" to be deleted\";\n        return false;\n    }\n    return true;\n}\n\nbool DeviceMapper::DeleteDevice(const std::string& name) {\n    return DeleteDevice(name, 0ms);\n}\n\nbool DeviceMapper::DeleteDeviceDeferred(const std::string& name) {\n    struct dm_ioctl io;\n    InitIo(&io, name);\n\n    io.flags |= DM_DEFERRED_REMOVE;\n    if (ioctl(fd_, DM_DEV_REMOVE, &io)) {\n        PLOG(ERROR) << \"DM_DEV_REMOVE with DM_DEFERRED_REMOVE failed for [\" << name << \"]\";\n        return false;\n    }\n    return true;\n}\n\nbool DeviceMapper::DeleteDeviceIfExistsDeferred(const std::string& name) {\n    if (GetState(name) == DmDeviceState::INVALID) {\n        return true;\n    }\n    return DeleteDeviceDeferred(name);\n}\n\nstatic std::string GenerateUuid() {\n    uuid_t uuid_bytes;\n    uuid_generate(uuid_bytes);\n\n    char uuid_chars[37] = {};\n    uuid_unparse_lower(uuid_bytes, uuid_chars);\n\n    return std::string{uuid_chars};\n}\n\nstatic bool IsRecovery() {\n    return access(\"/system/bin/recovery\", F_OK) == 0;\n}\n\nbool DeviceMapper::CreateEmptyDevice(const std::string& name) {\n    std::string uuid = GenerateUuid();\n    return CreateDevice(name, uuid);\n}\n\nbool DeviceMapper::WaitForDevice(const std::string& name,\n                                 const std::chrono::milliseconds& timeout_ms, std::string* path) {\n    // We use the unique path for testing whether the device is ready. After\n    // that, it's safe to use the dm-N path which is compatible with callers\n    // that expect it to be formatted as such.\n    std::string unique_path;\n    if (!GetDeviceUniquePath(name, &unique_path) || !GetDmDevicePathByName(name, path)) {\n        DeleteDevice(name);\n        return false;\n    }\n\n    if (timeout_ms <= std::chrono::milliseconds::zero()) {\n        return true;\n    }\n\n    if (IsRecovery()) {\n        bool non_ab_device = android::base::GetProperty(\"ro.build.ab_update\", \"\").empty();\n        int sdk = android::base::GetIntProperty(\"ro.build.version.sdk\", 0);\n        if (non_ab_device && sdk && sdk <= 29) {\n            LOG(INFO) << \"Detected ueventd incompatibility, reverting to legacy libdm behavior.\";\n            unique_path = *path;\n        }\n    }\n\n    if (!WaitForFile(unique_path, timeout_ms)) {\n        LOG(ERROR) << \"Failed waiting for device path: \" << unique_path;\n        DeleteDevice(name);\n        return false;\n    }\n    return true;\n}\n\nbool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,\n                                const std::chrono::milliseconds& timeout_ms) {\n    if (!CreateEmptyDevice(name)) {\n        return false;\n    }\n\n    if (!LoadTableAndActivate(name, table)) {\n        DeleteDevice(name);\n        return false;\n    }\n\n    if (!WaitForDevice(name, timeout_ms, path)) {\n        DeleteDevice(name);\n        return false;\n    }\n\n    return true;\n}\n\nbool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {\n        PLOG(ERROR) << \"Failed to get device path: \" << name;\n        return false;\n    }\n\n    if (io.uuid[0] == '\\0') {\n        LOG(ERROR) << \"Device does not have a unique path: \" << name;\n        return false;\n    }\n    *path = \"/dev/block/mapper/by-uuid/\"s + io.uuid;\n    return true;\n}\n\nbool DeviceMapper::GetDeviceNameAndUuid(dev_t dev, std::string* name, std::string* uuid) {\n    struct dm_ioctl io;\n    InitIo(&io, {});\n    io.dev = dev;\n\n    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {\n        PLOG(ERROR) << \"Failed to find device dev: \" << major(dev) << \":\" << minor(dev);\n        return false;\n    }\n\n    if (name) {\n        *name = io.name;\n    }\n    if (uuid) {\n        *uuid = io.uuid;\n    }\n    return true;\n}\n\nstd::optional<DeviceMapper::Info> DeviceMapper::GetDetailedInfo(const std::string& name) const {\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {\n        return std::nullopt;\n    }\n    return Info(io.flags);\n}\n\nDmDeviceState DeviceMapper::GetState(const std::string& name) const {\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {\n        return DmDeviceState::INVALID;\n    }\n    if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {\n        return DmDeviceState::ACTIVE;\n    }\n    return DmDeviceState::SUSPENDED;\n}\n\nbool DeviceMapper::ChangeState(const std::string& name, DmDeviceState state) {\n    if (state != DmDeviceState::SUSPENDED && state != DmDeviceState::ACTIVE) {\n        return false;\n    }\n\n    struct dm_ioctl io;\n    InitIo(&io, name);\n\n    if (state == DmDeviceState::SUSPENDED) io.flags = DM_SUSPEND_FLAG;\n\n    if (ioctl(fd_, DM_DEV_SUSPEND, &io) < 0) {\n        PLOG(ERROR) << \"DM_DEV_SUSPEND \"\n                    << (state == DmDeviceState::SUSPENDED ? \"suspend\" : \"resume\") << \" failed\";\n        return false;\n    }\n    return true;\n}\n\nbool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {\n    std::string ignore_path;\n    if (!CreateDevice(name, table, &ignore_path, 0ms)) {\n        return false;\n    }\n    return true;\n}\n\nbool DeviceMapper::LoadTable(const std::string& name, const DmTable& table) {\n    std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);\n    ioctl_buffer += table.Serialize();\n\n    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);\n    InitIo(io, name);\n    io->data_size = ioctl_buffer.size();\n    io->data_start = sizeof(struct dm_ioctl);\n    io->target_count = static_cast<uint32_t>(table.num_targets());\n    if (table.readonly()) {\n        io->flags |= DM_READONLY_FLAG;\n    }\n    if (ioctl(fd_, DM_TABLE_LOAD, io)) {\n        PLOG(ERROR) << \"DM_TABLE_LOAD failed\";\n        return false;\n    }\n    return true;\n}\n\nbool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {\n    if (!LoadTable(name, table)) {\n        return false;\n    }\n\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (ioctl(fd_, DM_DEV_SUSPEND, &io)) {\n        PLOG(ERROR) << \"DM_TABLE_SUSPEND resume failed\";\n        return false;\n    }\n    return true;\n}\n\n// Reads all the available device mapper targets and their corresponding\n// versions from the kernel and returns in a vector\nbool DeviceMapper::GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets) {\n    targets->clear();\n\n    // calculate the space needed to read a maximum of kMaxPossibleDmTargets\n    uint32_t payload_size = sizeof(struct dm_target_versions);\n    payload_size += DM_MAX_TYPE_NAME;\n    // device mapper wants every target spec to be aligned at 8-byte boundary\n    payload_size = DM_ALIGN(payload_size);\n    payload_size *= kMaxPossibleDmTargets;\n\n    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;\n    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);\n    if (buffer == nullptr) {\n        LOG(ERROR) << \"failed to allocate memory\";\n        return false;\n    }\n\n    // Sets appropriate data size and data_start to make sure we tell kernel\n    // about the total size of the buffer we are passing and where to start\n    // writing the list of targets.\n    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());\n    InitIo(io);\n    io->data_size = data_size;\n    io->data_start = sizeof(*io);\n\n    if (ioctl(fd_, DM_LIST_VERSIONS, io)) {\n        PLOG(ERROR) << \"DM_LIST_VERSIONS failed\";\n        return false;\n    }\n\n    // If the provided buffer wasn't enough to list all targets, note that\n    // any data beyond sizeof(*io) must not be read in this case\n    if (io->flags & DM_BUFFER_FULL_FLAG) {\n        LOG(INFO) << data_size << \" is not enough memory to list all dm targets\";\n        return false;\n    }\n\n    // if there are no targets registered, return success with empty vector\n    if (io->data_size == sizeof(*io)) {\n        return true;\n    }\n\n    // Parse each target and list the name and version\n    // TODO(b/110035986): Templatize this\n    uint32_t next = sizeof(*io);\n    data_size = io->data_size - next;\n    struct dm_target_versions* vers =\n            reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) + next);\n    while (next && data_size) {\n        targets->emplace_back(vers);\n        if (vers->next == 0) {\n            break;\n        }\n        next += vers->next;\n        data_size -= vers->next;\n        vers = reinterpret_cast<struct dm_target_versions*>(static_cast<char*>(buffer.get()) +\n                                                            next);\n    }\n\n    return true;\n}\n\nbool DeviceMapper::GetTargetByName(const std::string& name, DmTargetTypeInfo* info) {\n    std::vector<DmTargetTypeInfo> targets;\n    if (!GetAvailableTargets(&targets)) {\n        return false;\n    }\n    for (const auto& target : targets) {\n        if (target.name() == name) {\n            if (info) *info = target;\n            return true;\n        }\n    }\n    return false;\n}\n\nbool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {\n    devices->clear();\n\n    // calculate the space needed to read a maximum of 256 targets, each with\n    // name with maximum length of 16 bytes\n    uint32_t payload_size = sizeof(struct dm_name_list);\n    // 128-bytes for the name\n    payload_size += DM_NAME_LEN;\n    // dm wants every device spec to be aligned at 8-byte boundary\n    payload_size = DM_ALIGN(payload_size);\n    payload_size *= kMaxPossibleDmDevices;\n    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;\n    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);\n    if (buffer == nullptr) {\n        LOG(ERROR) << \"failed to allocate memory\";\n        return false;\n    }\n\n    // Sets appropriate data size and data_start to make sure we tell kernel\n    // about the total size of the buffer we are passing and where to start\n    // writing the list of targets.\n    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());\n    InitIo(io);\n    io->data_size = data_size;\n    io->data_start = sizeof(*io);\n\n    if (ioctl(fd_, DM_LIST_DEVICES, io)) {\n        PLOG(ERROR) << \"DM_LIST_DEVICES failed\";\n        return false;\n    }\n\n    // If the provided buffer wasn't enough to list all devices any data\n    // beyond sizeof(*io) must not be read.\n    if (io->flags & DM_BUFFER_FULL_FLAG) {\n        LOG(INFO) << data_size << \" is not enough memory to list all dm devices\";\n        return false;\n    }\n\n    // if there are no devices created yet, return success with empty vector\n    if (io->data_size == sizeof(*io)) {\n        return true;\n    }\n\n    // Parse each device and add a new DmBlockDevice to the vector\n    // created from the kernel data.\n    uint32_t next = sizeof(*io);\n    data_size = io->data_size - next;\n    struct dm_name_list* dm_dev =\n            reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);\n\n    while (next && data_size) {\n        devices->emplace_back((dm_dev));\n        if (dm_dev->next == 0) {\n            break;\n        }\n        next += dm_dev->next;\n        data_size -= dm_dev->next;\n        dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);\n    }\n\n    return true;\n}\n\n// Accepts a device mapper device name (like system_a, vendor_b etc) and\n// returns the path to it's device node (or symlink to the device node)\nbool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {\n        PLOG(WARNING) << \"DM_DEV_STATUS failed for \" << name;\n        return false;\n    }\n\n    uint32_t dev_num = minor(io.dev);\n    *path = \"/dev/block/dm-\" + std::to_string(dev_num);\n    return true;\n}\n\n// Accepts a device mapper device name (like system_a, vendor_b etc) and\n// returns its UUID.\nbool DeviceMapper::GetDmDeviceUuidByName(const std::string& name, std::string* uuid) {\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {\n        PLOG(WARNING) << \"DM_DEV_STATUS failed for \" << name;\n        return false;\n    }\n\n    *uuid = std::string(io.uuid);\n    return true;\n}\n\nbool DeviceMapper::GetDeviceNumber(const std::string& name, dev_t* dev) {\n    struct dm_ioctl io;\n    InitIo(&io, name);\n    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {\n        PLOG(WARNING) << \"DM_DEV_STATUS failed for \" << name;\n        return false;\n    }\n    *dev = io.dev;\n    return true;\n}\n\nbool DeviceMapper::GetDeviceString(const std::string& name, std::string* dev) {\n    dev_t num;\n    if (!GetDeviceNumber(name, &num)) {\n        return false;\n    }\n    *dev = std::to_string(major(num)) + \":\" + std::to_string(minor(num));\n    return true;\n}\n\nbool DeviceMapper::GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {\n    return GetTable(name, 0, table);\n}\n\nbool DeviceMapper::GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) {\n    return GetTable(name, DM_IMA_MEASUREMENT_FLAG, table);\n}\n\nbool DeviceMapper::GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {\n    return GetTable(name, DM_STATUS_TABLE_FLAG, table);\n}\n\nvoid RedactTableInfo(const struct dm_target_spec& spec, std::string* data) {\n    if (DeviceMapper::GetTargetType(spec) == \"crypt\") {\n        auto parts = android::base::Split(*data, \" \");\n        if (parts.size() < 2) {\n            return;\n        }\n        parts[1] = \"redacted\";\n        *data = android::base::Join(parts, \" \");\n    }\n}\n\n// private methods of DeviceMapper\nbool DeviceMapper::GetTable(const std::string& name, uint32_t flags,\n                            std::vector<TargetInfo>* table) {\n    std::vector<char> buffer;\n    struct dm_ioctl* io = nullptr;\n\n    for (buffer.resize(4096);; buffer.resize(buffer.size() * 2)) {\n        io = reinterpret_cast<struct dm_ioctl*>(&buffer[0]);\n\n        InitIo(io, name);\n        io->data_size = buffer.size();\n        io->data_start = sizeof(*io);\n        io->flags = flags;\n        if (ioctl(fd_, DM_TABLE_STATUS, io) < 0) {\n            PLOG(ERROR) << \"DM_TABLE_STATUS failed for \" << name;\n            return false;\n        }\n        if (!(io->flags & DM_BUFFER_FULL_FLAG)) break;\n    }\n\n    uint32_t cursor = io->data_start;\n    uint32_t data_end = std::min(io->data_size, uint32_t(buffer.size()));\n    for (uint32_t i = 0; i < io->target_count; i++) {\n        if (cursor + sizeof(struct dm_target_spec) > data_end) {\n            break;\n        }\n        // After each dm_target_spec is a status string. spec->next is an\n        // offset from |io->data_start|, and we clamp it to the size of our\n        // buffer.\n        struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&buffer[cursor]);\n        uint32_t data_offset = cursor + sizeof(dm_target_spec);\n        uint32_t next_cursor = std::min(io->data_start + spec->next, data_end);\n\n        std::string data;\n        if (next_cursor > data_offset) {\n            // Note: we use c_str() to eliminate any extra trailing 0s.\n            data = std::string(&buffer[data_offset], next_cursor - data_offset).c_str();\n        }\n        if (flags & DM_STATUS_TABLE_FLAG) {\n            RedactTableInfo(*spec, &data);\n        }\n        table->emplace_back(*spec, data);\n        cursor = next_cursor;\n    }\n    return true;\n}\n\nvoid DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {\n    CHECK(io != nullptr) << \"nullptr passed to dm_ioctl initialization\";\n    memset(io, 0, sizeof(*io));\n\n    io->version[0] = DM_VERSION0;\n    io->version[1] = DM_VERSION1;\n    io->version[2] = DM_VERSION2;\n    io->data_size = sizeof(*io);\n    io->data_start = 0;\n    if (!name.empty()) {\n        snprintf(io->name, sizeof(io->name), \"%s\", name.c_str());\n    }\n}\n\nstd::string DeviceMapper::GetTargetType(const struct dm_target_spec& spec) {\n    if (const void* p = memchr(spec.target_type, '\\0', sizeof(spec.target_type))) {\n        ptrdiff_t length = reinterpret_cast<const char*>(p) - spec.target_type;\n        return std::string{spec.target_type, static_cast<size_t>(length)};\n    }\n    return std::string{spec.target_type, sizeof(spec.target_type)};\n}\n\nstd::optional<std::string> ExtractBlockDeviceName(const std::string& path) {\n    static constexpr std::string_view kDevBlockPrefix(\"/dev/block/\");\n    std::string real_path;\n    if (!android::base::Realpath(path, &real_path)) {\n        real_path = path;\n    }\n    if (android::base::StartsWith(real_path, kDevBlockPrefix)) {\n        return real_path.substr(kDevBlockPrefix.length());\n    }\n    return {};\n}\n\nbool DeviceMapper::IsDmBlockDevice(const std::string& path) {\n    std::optional<std::string> name = ExtractBlockDeviceName(path);\n    return name && android::base::StartsWith(*name, \"dm-\");\n}\n\nstd::optional<std::string> DeviceMapper::GetDmDeviceNameByPath(const std::string& path) {\n    std::optional<std::string> name = ExtractBlockDeviceName(path);\n    if (!name) {\n        LOG(WARNING) << path << \" is not a block device\";\n        return std::nullopt;\n    }\n    if (!android::base::StartsWith(*name, \"dm-\")) {\n        LOG(WARNING) << path << \" is not a dm device\";\n        return std::nullopt;\n    }\n    std::string dm_name_file = \"/sys/block/\" + *name + \"/dm/name\";\n    std::string dm_name;\n    if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {\n        PLOG(ERROR) << \"Failed to read file \" << dm_name_file;\n        return std::nullopt;\n    }\n    dm_name = android::base::Trim(dm_name);\n    return dm_name;\n}\n\nstd::optional<std::string> DeviceMapper::GetParentBlockDeviceByPath(const std::string& path) {\n    std::optional<std::string> name = ExtractBlockDeviceName(path);\n    if (!name) {\n        LOG(WARNING) << path << \" is not a block device\";\n        return std::nullopt;\n    }\n    if (!android::base::StartsWith(*name, \"dm-\")) {\n        // Reached bottom of the device mapper stack.\n        return std::nullopt;\n    }\n    auto slaves_dir = \"/sys/block/\" + *name + \"/slaves\";\n    auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(slaves_dir.c_str()), closedir);\n    if (dir == nullptr) {\n        PLOG(ERROR) << \"Failed to open: \" << slaves_dir;\n        return std::nullopt;\n    }\n    std::string sub_device_name = \"\";\n    for (auto entry = readdir(dir.get()); entry; entry = readdir(dir.get())) {\n        if (entry->d_type != DT_LNK) continue;\n        if (!sub_device_name.empty()) {\n            LOG(ERROR) << \"Too many slaves in \" << slaves_dir;\n            return std::nullopt;\n        }\n        sub_device_name = entry->d_name;\n    }\n    if (sub_device_name.empty()) {\n        LOG(ERROR) << \"No slaves in \" << slaves_dir;\n        return std::nullopt;\n    }\n    return \"/dev/block/\" + sub_device_name;\n}\n\nbool DeviceMapper::TargetInfo::IsOverflowSnapshot() const {\n    return spec.target_type == \"snapshot\"s && data == \"Overflow\"s;\n}\n\n// Find directories in format of \"/sys/block/dm-X\".\nstatic int DmNameFilter(const dirent* de) {\n    if (android::base::StartsWith(de->d_name, \"dm-\")) {\n        return 1;\n    }\n    return 0;\n}\n\nstd::map<std::string, std::string> DeviceMapper::FindDmPartitions() {\n    static constexpr auto DM_PATH_PREFIX = \"/sys/block/\";\n    dirent** namelist;\n    int n = scandir(DM_PATH_PREFIX, &namelist, DmNameFilter, alphasort);\n    if (n == -1) {\n        PLOG(ERROR) << \"Failed to scan dir \" << DM_PATH_PREFIX;\n        return {};\n    }\n    if (n == 0) {\n        LOG(ERROR) << \"No dm block device found.\";\n        free(namelist);\n        return {};\n    }\n\n    static constexpr auto DM_PATH_SUFFIX = \"/dm/name\";\n    static constexpr auto DEV_PATH = \"/dev/block/\";\n    std::map<std::string, std::string> dm_block_devices;\n    while (n--) {\n        std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX;\n        std::string content;\n        if (!android::base::ReadFileToString(path, &content)) {\n            PLOG(WARNING) << \"Failed to read \" << path;\n        } else {\n            std::string dm_block_name = android::base::Trim(content);\n            // AVB is using 'vroot' for the root block device but we're expecting 'system'.\n            if (dm_block_name == \"vroot\") {\n                dm_block_name = \"system\";\n            } else if (android::base::EndsWith(dm_block_name, \"-verity\")) {\n                auto npos = dm_block_name.rfind(\"-verity\");\n                dm_block_name = dm_block_name.substr(0, npos);\n            } else if (!android::base::GetProperty(\"ro.boot.avb_version\", \"\").empty()) {\n                // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices,\n                // if DAP is enabled, then a -verity suffix must be used to\n                // differentiate between dm-linear and dm-verity devices. If we get\n                // here, we're AVB 2 and looking at a non-verity partition.\n                free(namelist[n]);\n                continue;\n            }\n\n            dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name));\n        }\n        free(namelist[n]);\n    }\n    free(namelist);\n\n    return dm_block_devices;\n}\n\nbool DeviceMapper::CreatePlaceholderDevice(const std::string& name) {\n    if (!CreateEmptyDevice(name)) {\n        return false;\n    }\n\n    struct utsname uts;\n    unsigned int major, minor;\n    if (uname(&uts) != 0 || sscanf(uts.release, \"%u.%u\", &major, &minor) != 2) {\n        LOG(ERROR) << \"Could not parse the kernel version from uname\";\n        return true;\n    }\n\n    // On Linux 5.15+, there is no uevent until DM_TABLE_LOAD.\n    if (major > 5 || (major == 5 && minor >= 15)) {\n        DmTable table;\n        table.Emplace<DmTargetError>(0, 1);\n        if (!LoadTable(name, table)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool DeviceMapper::SendMessage(const std::string& name, uint64_t sector,\n                               const std::string& message) {\n    std::string ioctl_buffer(sizeof(struct dm_ioctl) + sizeof(struct dm_target_msg), 0);\n    ioctl_buffer += message;\n    ioctl_buffer.push_back('\\0');\n\n    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);\n    InitIo(io, name);\n    io->data_size = ioctl_buffer.size();\n    io->data_start = sizeof(struct dm_ioctl);\n    struct dm_target_msg* msg =\n            reinterpret_cast<struct dm_target_msg*>(&ioctl_buffer[sizeof(struct dm_ioctl)]);\n    msg->sector = sector;\n    if (ioctl(fd_, DM_TARGET_MSG, io)) {\n        PLOG(ERROR) << \"DM_TARGET_MSG failed\";\n        return false;\n    }\n    return true;\n}\n\n}  // namespace dm\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libdm/dm_table.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libdm/dm_table.h\"\n\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n\nnamespace android {\nnamespace dm {\n\nbool DmTable::AddTarget(std::unique_ptr<DmTarget>&& target) {\n    if (!target->Valid()) {\n        return false;\n    }\n    num_sectors_ += target->size();\n    targets_.push_back(std::move(target));\n    return true;\n}\n\nbool DmTable::RemoveTarget(std::unique_ptr<DmTarget>&& /* target */) {\n    return true;\n}\n\nbool DmTable::valid() const {\n    if (targets_.empty()) {\n        LOG(ERROR) << \"Device-mapper table must have at least one target.\";\n        return false;\n    }\n    if (targets_[0]->start() != 0) {\n        LOG(ERROR) << \"Device-mapper table must start at logical sector 0.\";\n        return false;\n    }\n    return true;\n}\n\nuint64_t DmTable::num_sectors() const {\n    return valid() ? num_sectors_ : 0;\n}\n\n// Returns a string representation of the table that is ready to be passed\n// down to the kernel for loading.\n//\n// Implementation must verify there are no gaps in the table, table starts\n// with sector == 0, and iterate over each target to get its table\n// serialized.\nstd::string DmTable::Serialize() const {\n    if (!valid()) {\n        return \"\";\n    }\n\n    std::string table;\n    for (const auto& target : targets_) {\n        table += target->Serialize();\n    }\n    return table;\n}\n\n}  // namespace dm\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libdm/dm_target.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libdm/dm_target.h\"\n\n#include <inttypes.h>\n#include <stdio.h>\n#include <sys/types.h>\n\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n\n#include <libdm/dm.h>\n\nnamespace android {\nnamespace dm {\n\nstd::string DmTarget::Serialize() const {\n    // Create a string containing a dm_target_spec, parameter data, and an\n    // explicit null terminator.\n    std::string data(sizeof(dm_target_spec), '\\0');\n    data += GetParameterString();\n    data.push_back('\\0');\n\n    // The kernel expects each target to be 8-byte aligned.\n    size_t padding = DM_ALIGN(data.size()) - data.size();\n    for (size_t i = 0; i < padding; i++) {\n        data.push_back('\\0');\n    }\n\n    // Finally fill in the dm_target_spec.\n    struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);\n    spec->sector_start = start();\n    spec->length = size();\n    snprintf(spec->target_type, sizeof(spec->target_type), \"%s\", name().c_str());\n    spec->next = (uint32_t)data.size();\n    return data;\n}\n\nstd::string DmTargetZero::GetParameterString() const {\n    // The zero target type has no additional parameters.\n    return \"\";\n}\n\nstd::string DmTargetLinear::GetParameterString() const {\n    return block_device_ + \" \" + std::to_string(physical_sector_);\n}\n\nstd::string DmTargetStripe::GetParameterString() const {\n    return \"2 \" + std::to_string(chunksize) + \" \" + block_device0_ + \" 0 \" + block_device1_ + \" 0\";\n}\n\nDmTargetVerity::DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,\n                               const std::string& block_device, const std::string& hash_device,\n                               uint32_t data_block_size, uint32_t hash_block_size,\n                               uint32_t num_data_blocks, uint32_t hash_start_block,\n                               const std::string& hash_algorithm, const std::string& root_digest,\n                               const std::string& salt)\n    : DmTarget(start, length), valid_(true) {\n    base_args_ = {\n            std::to_string(version),\n            block_device,\n            hash_device,\n            std::to_string(data_block_size),\n            std::to_string(hash_block_size),\n            std::to_string(num_data_blocks),\n            std::to_string(hash_start_block),\n            hash_algorithm,\n            root_digest,\n            salt,\n    };\n}\n\nvoid DmTargetVerity::UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks,\n                            uint32_t start) {\n    optional_args_.emplace_back(\"use_fec_from_device\");\n    optional_args_.emplace_back(device);\n    optional_args_.emplace_back(\"fec_roots\");\n    optional_args_.emplace_back(std::to_string(num_roots));\n    optional_args_.emplace_back(\"fec_blocks\");\n    optional_args_.emplace_back(std::to_string(num_blocks));\n    optional_args_.emplace_back(\"fec_start\");\n    optional_args_.emplace_back(std::to_string(start));\n}\n\nvoid DmTargetVerity::SetVerityMode(const std::string& mode) {\n    if (mode != \"panic_on_corruption\" &&\n        mode != \"restart_on_corruption\" &&\n        mode != \"ignore_corruption\") {\n        LOG(ERROR) << \"Unknown verity mode: \" << mode;\n        valid_ = false;\n        return;\n    }\n    optional_args_.emplace_back(mode);\n}\n\nvoid DmTargetVerity::IgnoreZeroBlocks() {\n    optional_args_.emplace_back(\"ignore_zero_blocks\");\n}\n\nvoid DmTargetVerity::CheckAtMostOnce() {\n    optional_args_.emplace_back(\"check_at_most_once\");\n}\n\nstd::string DmTargetVerity::GetParameterString() const {\n    std::string base = android::base::Join(base_args_, \" \");\n    if (optional_args_.empty()) {\n        return base;\n    }\n    std::string optional = android::base::Join(optional_args_, \" \");\n    return base + \" \" + std::to_string(optional_args_.size()) + \" \" + optional;\n}\n\nstd::string DmTargetAndroidVerity::GetParameterString() const {\n    return keyid_ + \" \" + block_device_;\n}\n\nstd::string DmTargetBow::GetParameterString() const {\n    if (!block_size_) return target_string_;\n    return target_string_ + \" 1 block_size:\" + std::to_string(block_size_);\n}\n\nstd::string DmTargetSnapshot::name() const {\n    if (mode_ == SnapshotStorageMode::Merge) {\n        return \"snapshot-merge\";\n    }\n    return \"snapshot\";\n}\n\nstd::string DmTargetSnapshot::GetParameterString() const {\n    std::string mode;\n    switch (mode_) {\n        case SnapshotStorageMode::Persistent:\n        case SnapshotStorageMode::Merge:\n            // Note: \"O\" lets us query for overflow in the status message. This\n            // is only supported on kernels 4.4+. On earlier kernels, an overflow\n            // will be reported as \"Invalid\" in the status string.\n            mode = \"P\";\n            if (ReportsOverflow(name())) {\n                mode += \"O\";\n            }\n            break;\n        case SnapshotStorageMode::Transient:\n            mode = \"N\";\n            break;\n        default:\n            LOG(ERROR) << \"DmTargetSnapshot unknown mode\";\n            break;\n    }\n    return base_device_ + \" \" + cow_device_ + \" \" + mode + \" \" + std::to_string(chunk_size_);\n}\n\n// Computes the percentage of complition for snapshot status.\n// @sectors_initial is the number of sectors_allocated stored right before\n// starting the merge.\ndouble DmTargetSnapshot::MergePercent(const DmTargetSnapshot::Status& status,\n                                      uint64_t sectors_initial) {\n    uint64_t s = status.sectors_allocated;\n    uint64_t t = status.total_sectors;\n    uint64_t m = status.metadata_sectors;\n    uint64_t i = sectors_initial == 0 ? t : sectors_initial;\n\n    if (t <= s || i <= s) {\n        return 0.0;\n    }\n    if (s == 0 || t == 0 || s <= m) {\n        return 100.0;\n    }\n    return 100.0 / (i - m) * (i - s);\n}\n\nbool DmTargetSnapshot::ReportsOverflow(const std::string& target_type) {\n    DeviceMapper& dm = DeviceMapper::Instance();\n    DmTargetTypeInfo info;\n    if (!dm.GetTargetByName(target_type, &info)) {\n        return false;\n    }\n    if (target_type == \"snapshot\") {\n        return info.IsAtLeast(1, 15, 0);\n    }\n    if (target_type == \"snapshot-merge\") {\n        return info.IsAtLeast(1, 4, 0);\n    }\n    return false;\n}\n\nbool DmTargetSnapshot::ParseStatusText(const std::string& text, Status* status) {\n    // Try to parse the line as it should be\n    int args = sscanf(text.c_str(), \"%\" PRIu64 \"/%\" PRIu64 \" %\" PRIu64, &status->sectors_allocated,\n                      &status->total_sectors, &status->metadata_sectors);\n    if (args == 3) {\n        return true;\n    }\n    auto sections = android::base::Split(text, \" \");\n    if (sections.size() == 0) {\n        LOG(ERROR) << \"could not parse empty status\";\n        return false;\n    }\n    // Error codes are: \"Invalid\", \"Overflow\" and \"Merge failed\"\n    if (sections.size() == 1) {\n        if (text == \"Invalid\" || text == \"Overflow\") {\n            status->error = text;\n            return true;\n        }\n    } else if (sections.size() == 2 && text == \"Merge failed\") {\n        status->error = text;\n        return true;\n    }\n    LOG(ERROR) << \"could not parse snapshot status: wrong format\";\n    return false;\n}\n\nbool DmTargetSnapshot::GetDevicesFromParams(const std::string& params, std::string* base_device,\n                                            std::string* cow_device) {\n    auto pieces = android::base::Split(params, \" \");\n    if (pieces.size() < 2) {\n        LOG(ERROR) << \"Parameter string is invalid: \" << params;\n        return false;\n    }\n    *base_device = pieces[0];\n    *cow_device = pieces[1];\n    return true;\n}\n\nstd::string DmTargetCrypt::GetParameterString() const {\n    std::vector<std::string> argv = {\n            cipher_,\n            key_,\n            std::to_string(iv_sector_offset_),\n            device_,\n            std::to_string(device_sector_),\n    };\n\n    std::vector<std::string> extra_argv;\n    if (allow_discards_) extra_argv.emplace_back(\"allow_discards\");\n    if (allow_encrypt_override_) extra_argv.emplace_back(\"allow_encrypt_override\");\n    if (iv_large_sectors_) extra_argv.emplace_back(\"iv_large_sectors\");\n    if (sector_size_) extra_argv.emplace_back(\"sector_size:\" + std::to_string(sector_size_));\n\n    if (!extra_argv.empty()) argv.emplace_back(std::to_string(extra_argv.size()));\n\n    argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());\n    return android::base::Join(argv, \" \");\n}\n\nbool DmTargetDefaultKey::Valid() const {\n    if (!use_legacy_options_format_ && !set_dun_) return false;\n    return true;\n}\n\nstd::string DmTargetDefaultKey::GetParameterString() const {\n    std::vector<std::string> argv;\n    argv.emplace_back(cipher_);\n    argv.emplace_back(key_);\n    if (!use_legacy_options_format_) {\n        argv.emplace_back(\"0\");  // iv_offset\n    }\n    argv.emplace_back(blockdev_);\n    argv.push_back(std::to_string(start_sector_));\n    std::vector<std::string> extra_argv;\n    if (use_legacy_options_format_) {\n        if (set_dun_) {  // v2 always sets the DUN.\n            extra_argv.emplace_back(\"set_dun\");\n        }\n    } else {\n        extra_argv.emplace_back(\"allow_discards\");\n        extra_argv.emplace_back(\"sector_size:4096\");\n        extra_argv.emplace_back(\"iv_large_sectors\");\n        if (is_hw_wrapped_) extra_argv.emplace_back(\"wrappedkey_v0\");\n    }\n    if (!extra_argv.empty()) {\n        argv.emplace_back(std::to_string(extra_argv.size()));\n        argv.insert(argv.end(), extra_argv.begin(), extra_argv.end());\n    }\n    return android::base::Join(argv, \" \");\n}\n\nstd::string DmTargetUser::GetParameterString() const {\n    std::vector<std::string> argv;\n    argv.push_back(std::to_string(start()));\n    argv.push_back(std::to_string(size()));\n    argv.push_back(control_device());\n    return android::base::Join(argv, \" \");\n}\n\nDmTargetThinPool::DmTargetThinPool(uint64_t start, uint64_t length, const std::string& metadata_dev,\n                                   const std::string& data_dev, uint64_t data_block_size,\n                                   uint64_t low_water_mark)\n    : DmTarget(start, length),\n      metadata_dev_(metadata_dev),\n      data_dev_(data_dev),\n      data_block_size_(data_block_size),\n      low_water_mark_(low_water_mark) {}\n\nstd::string DmTargetThinPool::GetParameterString() const {\n    std::vector<std::string> args{\n            metadata_dev_,\n            data_dev_,\n            std::to_string(data_block_size_),\n            std::to_string(low_water_mark_),\n    };\n    return android::base::Join(args, \" \");\n}\n\nbool DmTargetThinPool::Valid() const {\n    // data_block_size: must be between 128 (64KB) and 2097152 (1GB) and a multiple of 128 (64KB)\n    if (data_block_size_ < 128 || data_block_size_ > 2097152) return false;\n    if (data_block_size_ % 128) return false;\n    return true;\n}\n\nDmTargetThin::DmTargetThin(uint64_t start, uint64_t length, const std::string& pool_dev,\n                           uint64_t dev_id)\n    : DmTarget(start, length), pool_dev_(pool_dev), dev_id_(dev_id) {}\n\nstd::string DmTargetThin::GetParameterString() const {\n    std::vector<std::string> args{\n            pool_dev_,\n            std::to_string(dev_id_),\n    };\n    return android::base::Join(args, \" \");\n}\n\n}  // namespace dm\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libdm/dm_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fcntl.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <ctime>\n#include <iostream>\n#include <map>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n#include <libdm/dm.h>\n#include <libdm/loop_control.h>\n#include <storage_literals/storage_literals.h>\n#include \"test_util.h\"\n#include \"utility.h\"\n\nusing namespace std;\nusing namespace std::chrono_literals;\nusing namespace android::dm;\nusing namespace android::storage_literals;\nusing android::base::make_scope_guard;\nusing android::base::unique_fd;\n\nclass DmTest : public ::testing::Test {\n  protected:\n    void SetUp() override {\n        const testing::TestInfo* const test_info =\n                testing::UnitTest::GetInstance()->current_test_info();\n        test_name_ = test_info->name();\n        test_full_name_ = test_info->test_suite_name() + \"/\"s + test_name_;\n\n        LOG(INFO) << \"Starting test: \" << test_full_name_;\n    }\n    void TearDown() override {\n        LOG(INFO) << \"Tearing down test: \" << test_full_name_;\n\n        auto& dm = DeviceMapper::Instance();\n        ASSERT_TRUE(dm.DeleteDeviceIfExists(test_name_));\n\n        LOG(INFO) << \"Teardown complete for test: \" << test_full_name_;\n    }\n\n    std::string test_name_;\n    std::string test_full_name_;\n};\n\nTEST_F(DmTest, HasMinimumTargets) {\n    DmTargetTypeInfo info;\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    ASSERT_TRUE(dm.GetTargetByName(\"linear\", &info));\n}\n\nTEST_F(DmTest, DmLinear) {\n    unique_fd tmp1(CreateTempFile(\"file_1\", 4096));\n    ASSERT_GE(tmp1, 0);\n    unique_fd tmp2(CreateTempFile(\"file_2\", 4096));\n    ASSERT_GE(tmp2, 0);\n\n    // Create two different files. These will back two separate loop devices.\n    const char message1[] = \"Hello! This is sector 1.\";\n    const char message2[] = \"Goodbye. This is sector 2.\";\n    ASSERT_TRUE(android::base::WriteFully(tmp1, message1, sizeof(message1)));\n    ASSERT_TRUE(android::base::WriteFully(tmp2, message2, sizeof(message2)));\n\n    LoopDevice loop_a(tmp1, 10s);\n    ASSERT_TRUE(loop_a.valid());\n    LoopDevice loop_b(tmp2, 10s);\n    ASSERT_TRUE(loop_b.valid());\n\n    // Define a 2-sector device, with each sector mapping to the first sector\n    // of one of our loop devices.\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(1, 1, loop_b.device(), 0));\n    ASSERT_TRUE(table.valid());\n    ASSERT_EQ(2u, table.num_sectors());\n\n    TempDevice dev(\"libdm-test-dm-linear\", table);\n    ASSERT_TRUE(dev.valid());\n    ASSERT_FALSE(dev.path().empty());\n\n    auto& dm = DeviceMapper::Instance();\n\n    dev_t dev_number;\n    ASSERT_TRUE(dm.GetDeviceNumber(dev.name(), &dev_number));\n    ASSERT_NE(dev_number, 0);\n\n    std::string dev_string;\n    ASSERT_TRUE(dm.GetDeviceString(dev.name(), &dev_string));\n    ASSERT_FALSE(dev_string.empty());\n\n    // Note: a scope is needed to ensure that there are no open descriptors\n    // when we go to close the device.\n    {\n        unique_fd dev_fd(open(dev.path().c_str(), O_RDWR));\n        ASSERT_GE(dev_fd, 0);\n\n        // Test that each sector of our device is correctly mapped to each loop\n        // device.\n        char sector[512];\n        ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));\n        ASSERT_EQ(strncmp(sector, message1, sizeof(message1)), 0);\n        ASSERT_TRUE(android::base::ReadFully(dev_fd, sector, sizeof(sector)));\n        ASSERT_EQ(strncmp(sector, message2, sizeof(message2)), 0);\n    }\n\n    // Test GetTableStatus.\n    vector<DeviceMapper::TargetInfo> targets;\n    ASSERT_TRUE(dm.GetTableStatus(dev.name(), &targets));\n    ASSERT_EQ(targets.size(), 2);\n    EXPECT_EQ(strcmp(targets[0].spec.target_type, \"linear\"), 0);\n    EXPECT_TRUE(targets[0].data.empty());\n    EXPECT_EQ(targets[0].spec.sector_start, 0);\n    EXPECT_EQ(targets[0].spec.length, 1);\n    EXPECT_EQ(strcmp(targets[1].spec.target_type, \"linear\"), 0);\n    EXPECT_TRUE(targets[1].data.empty());\n    EXPECT_EQ(targets[1].spec.sector_start, 1);\n    EXPECT_EQ(targets[1].spec.length, 1);\n\n    // Test GetTargetType().\n    EXPECT_EQ(DeviceMapper::GetTargetType(targets[0].spec), std::string{\"linear\"});\n    EXPECT_EQ(DeviceMapper::GetTargetType(targets[1].spec), std::string{\"linear\"});\n\n    // Normally the TestDevice destructor would delete this, but at least one\n    // test should ensure that device deletion works.\n    ASSERT_TRUE(dev.Destroy());\n}\n\nTEST_F(DmTest, DmSuspendResume) {\n    unique_fd tmp1(CreateTempFile(\"file_suspend_resume\", 512));\n    ASSERT_GE(tmp1, 0);\n\n    LoopDevice loop_a(tmp1, 10s);\n    ASSERT_TRUE(loop_a.valid());\n\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop_a.device(), 0));\n    ASSERT_TRUE(table.valid());\n    ASSERT_EQ(1u, table.num_sectors());\n\n    TempDevice dev(\"libdm-test-dm-suspend-resume\", table);\n    ASSERT_TRUE(dev.valid());\n    ASSERT_FALSE(dev.path().empty());\n\n    auto& dm = DeviceMapper::Instance();\n\n    // Test Set and Get status of device.\n    vector<DeviceMapper::TargetInfo> targets;\n    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);\n\n    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::SUSPENDED));\n    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::SUSPENDED);\n\n    ASSERT_TRUE(dm.ChangeState(dev.name(), DmDeviceState::ACTIVE));\n    ASSERT_EQ(dm.GetState(dev.name()), DmDeviceState::ACTIVE);\n}\n\nTEST_F(DmTest, StripeArgs) {\n    DmTargetStripe target(0, 4096, 1024, \"/dev/loop0\", \"/dev/loop1\");\n    ASSERT_EQ(target.name(), \"striped\");\n    ASSERT_TRUE(target.Valid());\n    ASSERT_EQ(target.GetParameterString(), \"2 1024 /dev/loop0 0 /dev/loop1 0\");\n}\n\nTEST_F(DmTest, DmVerityArgsAvb2) {\n    std::string device = \"/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a\";\n    std::string algorithm = \"sha1\";\n    std::string digest = \"4be7e823b8c40f7bd5c8ccd5123f0722c5baca21\";\n    std::string salt = \"cc99f81ecb9484220a003b0719ee59dcf9be7e5d\";\n\n    DmTargetVerity target(0, 10000, 1, device, device, 4096, 4096, 125961, 125961, algorithm,\n                          digest, salt);\n    target.UseFec(device, 2, 126955, 126955);\n    target.SetVerityMode(\"restart_on_corruption\");\n    target.IgnoreZeroBlocks();\n\n    // Verity table from a walleye build.\n    std::string expected =\n            \"1 /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a \"\n            \"/dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a 4096 4096 125961 125961 sha1 \"\n            \"4be7e823b8c40f7bd5c8ccd5123f0722c5baca21 cc99f81ecb9484220a003b0719ee59dcf9be7e5d 10 \"\n            \"use_fec_from_device /dev/block/platform/soc/1da4000.ufshc/by-name/vendor_a fec_roots \"\n            \"2 fec_blocks 126955 fec_start 126955 restart_on_corruption ignore_zero_blocks\";\n    EXPECT_EQ(target.GetParameterString(), expected);\n}\n\nTEST_F(DmTest, DmSnapshotArgs) {\n    DmTargetSnapshot target1(0, 512, \"base\", \"cow\", SnapshotStorageMode::Persistent, 8);\n    if (DmTargetSnapshot::ReportsOverflow(\"snapshot\")) {\n        EXPECT_EQ(target1.GetParameterString(), \"base cow PO 8\");\n    } else {\n        EXPECT_EQ(target1.GetParameterString(), \"base cow P 8\");\n    }\n    EXPECT_EQ(target1.name(), \"snapshot\");\n\n    DmTargetSnapshot target2(0, 512, \"base\", \"cow\", SnapshotStorageMode::Transient, 8);\n    EXPECT_EQ(target2.GetParameterString(), \"base cow N 8\");\n    EXPECT_EQ(target2.name(), \"snapshot\");\n\n    DmTargetSnapshot target3(0, 512, \"base\", \"cow\", SnapshotStorageMode::Merge, 8);\n    if (DmTargetSnapshot::ReportsOverflow(\"snapshot-merge\")) {\n        EXPECT_EQ(target3.GetParameterString(), \"base cow PO 8\");\n    } else {\n        EXPECT_EQ(target3.GetParameterString(), \"base cow P 8\");\n    }\n    EXPECT_EQ(target3.name(), \"snapshot-merge\");\n}\n\nTEST_F(DmTest, DmSnapshotOriginArgs) {\n    DmTargetSnapshotOrigin target(0, 512, \"base\");\n    EXPECT_EQ(target.GetParameterString(), \"base\");\n    EXPECT_EQ(target.name(), \"snapshot-origin\");\n}\n\nclass SnapshotTestHarness final {\n  public:\n    bool Setup();\n    bool Merge();\n\n    std::string origin_dev() const { return origin_dev_->path(); }\n    std::string snapshot_dev() const { return snapshot_dev_->path(); }\n\n    int base_fd() const { return base_fd_; }\n\n    static const uint64_t kBaseDeviceSize = 1024 * 1024;\n    static const uint64_t kCowDeviceSize = 1024 * 64;\n    static const uint64_t kSectorSize = 512;\n\n  private:\n    void SetupImpl();\n    void MergeImpl();\n\n    unique_fd base_fd_;\n    unique_fd cow_fd_;\n    unique_ptr<LoopDevice> base_loop_;\n    unique_ptr<LoopDevice> cow_loop_;\n    unique_ptr<TempDevice> origin_dev_;\n    unique_ptr<TempDevice> snapshot_dev_;\n    bool setup_ok_ = false;\n    bool merge_ok_ = false;\n};\n\nbool SnapshotTestHarness::Setup() {\n    SetupImpl();\n    return setup_ok_;\n}\n\nvoid SnapshotTestHarness::SetupImpl() {\n    base_fd_ = CreateTempFile(\"base_device\", kBaseDeviceSize);\n    ASSERT_GE(base_fd_, 0);\n    cow_fd_ = CreateTempFile(\"cow_device\", kCowDeviceSize);\n    ASSERT_GE(cow_fd_, 0);\n\n    base_loop_ = std::make_unique<LoopDevice>(base_fd_, 10s);\n    ASSERT_TRUE(base_loop_->valid());\n    cow_loop_ = std::make_unique<LoopDevice>(cow_fd_, 10s);\n    ASSERT_TRUE(cow_loop_->valid());\n\n    DmTable origin_table;\n    ASSERT_TRUE(origin_table.AddTarget(make_unique<DmTargetSnapshotOrigin>(\n            0, kBaseDeviceSize / kSectorSize, base_loop_->device())));\n    ASSERT_TRUE(origin_table.valid());\n    ASSERT_EQ(kBaseDeviceSize / kSectorSize, origin_table.num_sectors());\n\n    origin_dev_ = std::make_unique<TempDevice>(\"libdm-test-dm-snapshot-origin\", origin_table);\n    ASSERT_TRUE(origin_dev_->valid());\n    ASSERT_FALSE(origin_dev_->path().empty());\n\n    // chunk size = 4K blocks.\n    DmTable snap_table;\n    ASSERT_TRUE(snap_table.AddTarget(make_unique<DmTargetSnapshot>(\n            0, kBaseDeviceSize / kSectorSize, base_loop_->device(), cow_loop_->device(),\n            SnapshotStorageMode::Persistent, 8)));\n    ASSERT_TRUE(snap_table.valid());\n    ASSERT_EQ(kBaseDeviceSize / kSectorSize, snap_table.num_sectors());\n\n    snapshot_dev_ = std::make_unique<TempDevice>(\"libdm-test-dm-snapshot\", snap_table);\n    ASSERT_TRUE(snapshot_dev_->valid());\n    ASSERT_FALSE(snapshot_dev_->path().empty());\n\n    setup_ok_ = true;\n}\n\nbool SnapshotTestHarness::Merge() {\n    MergeImpl();\n    return merge_ok_;\n}\n\nvoid SnapshotTestHarness::MergeImpl() {\n    DmTable merge_table;\n    ASSERT_TRUE(merge_table.AddTarget(\n            make_unique<DmTargetSnapshot>(0, kBaseDeviceSize / kSectorSize, base_loop_->device(),\n                                          cow_loop_->device(), SnapshotStorageMode::Merge, 8)));\n    ASSERT_TRUE(merge_table.valid());\n    ASSERT_EQ(kBaseDeviceSize / kSectorSize, merge_table.num_sectors());\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    ASSERT_TRUE(dm.LoadTableAndActivate(\"libdm-test-dm-snapshot\", merge_table));\n\n    while (true) {\n        vector<DeviceMapper::TargetInfo> status;\n        ASSERT_TRUE(dm.GetTableStatus(\"libdm-test-dm-snapshot\", &status));\n        ASSERT_EQ(status.size(), 1);\n        ASSERT_EQ(strncmp(status[0].spec.target_type, \"snapshot-merge\", strlen(\"snapshot-merge\")),\n                  0);\n\n        DmTargetSnapshot::Status merge_status;\n        ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(status[0].data, &merge_status));\n        ASSERT_TRUE(merge_status.error.empty());\n        if (merge_status.sectors_allocated == merge_status.metadata_sectors) {\n            break;\n        }\n\n        std::this_thread::sleep_for(250ms);\n    }\n\n    merge_ok_ = true;\n}\n\nbool CheckSnapshotAvailability() {\n    DmTargetTypeInfo info;\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.GetTargetByName(\"snapshot\", &info)) {\n        cout << \"snapshot module not enabled; skipping test\" << std::endl;\n        return false;\n    }\n    if (!dm.GetTargetByName(\"snapshot-merge\", &info)) {\n        cout << \"snapshot-merge module not enabled; skipping test\" << std::endl;\n        return false;\n    }\n    if (!dm.GetTargetByName(\"snapshot-origin\", &info)) {\n        cout << \"snapshot-origin module not enabled; skipping test\" << std::endl;\n        return false;\n    }\n    return true;\n}\n\nTEST_F(DmTest, DmSnapshot) {\n    if (!CheckSnapshotAvailability()) {\n        return;\n    }\n\n    SnapshotTestHarness harness;\n    ASSERT_TRUE(harness.Setup());\n\n    // Open the dm devices.\n    unique_fd origin_fd(open(harness.origin_dev().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_GE(origin_fd, 0);\n    unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC | O_SYNC));\n    ASSERT_GE(snapshot_fd, 0);\n\n    // Write to the first block of the snapshot device.\n    std::string data(\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\");\n    ASSERT_TRUE(android::base::WriteFully(snapshot_fd, data.data(), data.size()));\n    ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);\n\n    // We should get the same data back from the snapshot device.\n    std::string read(data.size(), '\\0');\n    ASSERT_TRUE(android::base::ReadFully(snapshot_fd, read.data(), read.size()));\n    ASSERT_EQ(read, data);\n\n    // We should see the original data from the origin device.\n    std::string zeroes(data.size(), '\\0');\n    ASSERT_TRUE(android::base::ReadFully(origin_fd, read.data(), read.size()));\n    ASSERT_EQ(lseek(snapshot_fd, 0, SEEK_SET), 0);\n    ASSERT_EQ(read, zeroes);\n\n    // We should also see the original data from the base device.\n    ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));\n    ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);\n    ASSERT_EQ(read, zeroes);\n\n    // Now, perform the merge and wait.\n    ASSERT_TRUE(harness.Merge());\n\n    // Reading from the base device should give us the modified data.\n    ASSERT_TRUE(android::base::ReadFully(harness.base_fd(), read.data(), read.size()));\n    ASSERT_EQ(lseek(harness.base_fd(), 0, SEEK_SET), 0);\n    ASSERT_EQ(read, data);\n}\n\nTEST_F(DmTest, DmSnapshotOverflow) {\n    if (!CheckSnapshotAvailability()) {\n        return;\n    }\n\n    SnapshotTestHarness harness;\n    ASSERT_TRUE(harness.Setup());\n\n    // Open the dm devices.\n    unique_fd snapshot_fd(open(harness.snapshot_dev().c_str(), O_RDWR | O_CLOEXEC));\n    ASSERT_GE(snapshot_fd, 0);\n\n    // Fill the copy-on-write device until it overflows.\n    uint64_t bytes_remaining = SnapshotTestHarness::kCowDeviceSize;\n    uint8_t byte = 1;\n    while (bytes_remaining) {\n        std::string data(4096, char(byte));\n        if (!android::base::WriteFully(snapshot_fd, data.data(), data.size())) {\n            ASSERT_EQ(errno, EIO);\n            break;\n        }\n        bytes_remaining -= data.size();\n    }\n\n    // If writes succeed (because they are buffered), then we should expect an\n    // fsync to fail with EIO.\n    if (!bytes_remaining) {\n        ASSERT_EQ(fsync(snapshot_fd), -1);\n        ASSERT_EQ(errno, EIO);\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n\n    vector<DeviceMapper::TargetInfo> target_status;\n    ASSERT_TRUE(dm.GetTableStatus(\"libdm-test-dm-snapshot\", &target_status));\n    ASSERT_EQ(target_status.size(), 1);\n    ASSERT_EQ(strncmp(target_status[0].spec.target_type, \"snapshot\", strlen(\"snapshot\")), 0);\n\n    DmTargetSnapshot::Status status;\n    ASSERT_TRUE(DmTargetSnapshot::ParseStatusText(target_status[0].data, &status));\n    if (DmTargetSnapshot::ReportsOverflow(\"snapshot\")) {\n        ASSERT_EQ(status.error, \"Overflow\");\n    } else {\n        ASSERT_EQ(status.error, \"Invalid\");\n    }\n}\n\nTEST_F(DmTest, ParseStatusText) {\n    DmTargetSnapshot::Status status;\n\n    // Bad inputs\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"X\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123/456\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123 456\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123 456\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123 456 789\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123 456/789\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123/456/789\", &status));\n    EXPECT_FALSE(DmTargetSnapshot::ParseStatusText(\"123 / 456 789\", &status));\n\n    // Good input\n    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText(\"123/456 789\", &status));\n    EXPECT_EQ(status.sectors_allocated, 123);\n    EXPECT_EQ(status.total_sectors, 456);\n    EXPECT_EQ(status.metadata_sectors, 789);\n\n    // Known error codes\n    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText(\"Invalid\", &status));\n    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText(\"Merge failed\", &status));\n    EXPECT_TRUE(DmTargetSnapshot::ParseStatusText(\"Overflow\", &status));\n}\n\nTEST_F(DmTest, DmSnapshotMergePercent) {\n    DmTargetSnapshot::Status status;\n\n    // Correct input\n    status.sectors_allocated = 1000;\n    status.total_sectors = 1000;\n    status.metadata_sectors = 0;\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 1.0);\n\n    status.sectors_allocated = 500;\n    status.total_sectors = 1000;\n    status.metadata_sectors = 0;\n    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 49.0);\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 51.0);\n\n    status.sectors_allocated = 0;\n    status.total_sectors = 1000;\n    status.metadata_sectors = 0;\n    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);\n\n    status.sectors_allocated = 500;\n    status.total_sectors = 1000;\n    status.metadata_sectors = 500;\n    EXPECT_GE(DmTargetSnapshot::MergePercent(status), 99.0);\n\n    status.sectors_allocated = 500;\n    status.total_sectors = 1000;\n    status.metadata_sectors = 0;\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 500), 1.0);\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 1000), 51.0);\n    EXPECT_GE(DmTargetSnapshot::MergePercent(status, 1000), 49.0);\n\n    // Robustness\n    status.sectors_allocated = 2000;\n    status.total_sectors = 1000;\n    status.metadata_sectors = 0;\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);\n\n    status.sectors_allocated = 2000;\n    status.total_sectors = 1000;\n    status.metadata_sectors = 2000;\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);\n\n    status.sectors_allocated = 2000;\n    status.total_sectors = 0;\n    status.metadata_sectors = 2000;\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status), 0.0);\n\n    status.sectors_allocated = 1000;\n    status.total_sectors = 0;\n    status.metadata_sectors = 1000;\n    EXPECT_LE(DmTargetSnapshot::MergePercent(status, 0), 0.0);\n}\n\nTEST_F(DmTest, CryptArgs) {\n    DmTargetCrypt target1(0, 512, \"sha1\", \"abcdefgh\", 50, \"/dev/loop0\", 100);\n    ASSERT_EQ(target1.name(), \"crypt\");\n    ASSERT_TRUE(target1.Valid());\n    ASSERT_EQ(target1.GetParameterString(), \"sha1 abcdefgh 50 /dev/loop0 100\");\n\n    DmTargetCrypt target2(0, 512, \"sha1\", \"abcdefgh\", 50, \"/dev/loop0\", 100);\n    target2.SetSectorSize(64);\n    target2.AllowDiscards();\n    target2.SetIvLargeSectors();\n    target2.AllowEncryptOverride();\n    ASSERT_EQ(target2.GetParameterString(),\n              \"sha1 abcdefgh 50 /dev/loop0 100 4 allow_discards allow_encrypt_override \"\n              \"iv_large_sectors sector_size:64\");\n}\n\nTEST_F(DmTest, DefaultKeyArgs) {\n    DmTargetDefaultKey target(0, 4096, \"aes-xts-plain64\", \"abcdef0123456789\", \"/dev/loop0\", 0);\n    target.SetSetDun();\n    ASSERT_EQ(target.name(), \"default-key\");\n    ASSERT_TRUE(target.Valid());\n    // TODO: Add case for wrapped key enabled\n    ASSERT_EQ(target.GetParameterString(),\n              \"aes-xts-plain64 abcdef0123456789 0 /dev/loop0 0 3 allow_discards sector_size:4096 \"\n              \"iv_large_sectors\");\n}\n\nTEST_F(DmTest, DefaultKeyLegacyArgs) {\n    DmTargetDefaultKey target(0, 4096, \"AES-256-XTS\", \"abcdef0123456789\", \"/dev/loop0\", 0);\n    target.SetUseLegacyOptionsFormat();\n    ASSERT_EQ(target.name(), \"default-key\");\n    ASSERT_TRUE(target.Valid());\n    ASSERT_EQ(target.GetParameterString(), \"AES-256-XTS abcdef0123456789 /dev/loop0 0\");\n}\n\nTEST_F(DmTest, DeleteDeviceWithTimeout) {\n    unique_fd tmp(CreateTempFile(\"file_1\", 4096));\n    ASSERT_GE(tmp, 0);\n    LoopDevice loop(tmp, 10s);\n    ASSERT_TRUE(loop.valid());\n\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));\n    ASSERT_TRUE(table.valid());\n    TempDevice dev(\"libdm-test-dm-linear\", table);\n    ASSERT_TRUE(dev.valid());\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n\n    std::string path;\n    ASSERT_TRUE(dm.GetDmDevicePathByName(\"libdm-test-dm-linear\", &path));\n    ASSERT_EQ(0, access(path.c_str(), F_OK));\n\n    std::string unique_path;\n    ASSERT_TRUE(dm.GetDeviceUniquePath(\"libdm-test-dm-linear\", &unique_path));\n    ASSERT_EQ(0, access(unique_path.c_str(), F_OK));\n\n    ASSERT_TRUE(dm.DeleteDevice(\"libdm-test-dm-linear\", 5s));\n    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState(\"libdm-test-dm-linear\"));\n    // Check that unique path of this device has been deleteted.\n    // Previously this test case used to check that dev node (i.e. /dev/block/dm-XX) has been\n    // deleted. However, this introduces a race condition, ueventd will remove the unique symlink\n    // (i.e. /dev/block/mapper/by-uuid/...) **before** removing the device node, while DeleteDevice\n    // API synchronizes on the unique symlink being deleted.\n    ASSERT_NE(0, access(unique_path.c_str(), F_OK));\n    ASSERT_EQ(ENOENT, errno);\n}\n\nTEST_F(DmTest, IsDmBlockDevice) {\n    unique_fd tmp(CreateTempFile(\"file_1\", 4096));\n    ASSERT_GE(tmp, 0);\n    LoopDevice loop(tmp, 10s);\n    ASSERT_TRUE(loop.valid());\n    ASSERT_TRUE(android::base::StartsWith(loop.device(), \"/dev/block\"));\n\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));\n    ASSERT_TRUE(table.valid());\n\n    TempDevice dev(\"libdm-test-dm-linear\", table);\n    ASSERT_TRUE(dev.valid());\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    ASSERT_TRUE(dm.IsDmBlockDevice(dev.path()));\n    ASSERT_FALSE(dm.IsDmBlockDevice(loop.device()));\n}\n\nTEST_F(DmTest, GetDmDeviceNameByPath) {\n    unique_fd tmp(CreateTempFile(\"file_1\", 4096));\n    ASSERT_GE(tmp, 0);\n    LoopDevice loop(tmp, 10s);\n    ASSERT_TRUE(loop.valid());\n    ASSERT_TRUE(android::base::StartsWith(loop.device(), \"/dev/block\"));\n\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));\n    ASSERT_TRUE(table.valid());\n\n    TempDevice dev(\"libdm-test-dm-linear\", table);\n    ASSERT_TRUE(dev.valid());\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    // Not a dm device, GetDmDeviceNameByPath will return std::nullopt.\n    ASSERT_FALSE(dm.GetDmDeviceNameByPath(loop.device()));\n    auto name = dm.GetDmDeviceNameByPath(dev.path());\n    ASSERT_EQ(\"libdm-test-dm-linear\", *name);\n}\n\nTEST_F(DmTest, GetParentBlockDeviceByPath) {\n    unique_fd tmp(CreateTempFile(\"file_1\", 4096));\n    ASSERT_GE(tmp, 0);\n    LoopDevice loop(tmp, 10s);\n    ASSERT_TRUE(loop.valid());\n    ASSERT_TRUE(android::base::StartsWith(loop.device(), \"/dev/block\"));\n\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));\n    ASSERT_TRUE(table.valid());\n\n    TempDevice dev(\"libdm-test-dm-linear\", table);\n    ASSERT_TRUE(dev.valid());\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    ASSERT_FALSE(dm.GetParentBlockDeviceByPath(loop.device()));\n    auto sub_block_device = dm.GetParentBlockDeviceByPath(dev.path());\n    ASSERT_EQ(loop.device(), *sub_block_device);\n}\n\nTEST_F(DmTest, DeleteDeviceDeferredNoReferences) {\n    unique_fd tmp(CreateTempFile(\"file_1\", 4096));\n    ASSERT_GE(tmp, 0);\n    LoopDevice loop(tmp, 10s);\n    ASSERT_TRUE(loop.valid());\n\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));\n    ASSERT_TRUE(table.valid());\n    TempDevice dev(\"libdm-test-dm-linear\", table);\n    ASSERT_TRUE(dev.valid());\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n\n    std::string path;\n    ASSERT_TRUE(dm.GetDmDevicePathByName(\"libdm-test-dm-linear\", &path));\n    ASSERT_EQ(0, access(path.c_str(), F_OK));\n\n    ASSERT_TRUE(dm.DeleteDeviceDeferred(\"libdm-test-dm-linear\"));\n\n    ASSERT_TRUE(WaitForFileDeleted(path, 5s));\n    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState(\"libdm-test-dm-linear\"));\n    ASSERT_NE(0, access(path.c_str(), F_OK));\n    ASSERT_EQ(ENOENT, errno);\n}\n\nTEST_F(DmTest, DeleteDeviceDeferredWaitsForLastReference) {\n    unique_fd tmp(CreateTempFile(\"file_1\", 4096));\n    ASSERT_GE(tmp, 0);\n    LoopDevice loop(tmp, 10s);\n    ASSERT_TRUE(loop.valid());\n\n    DmTable table;\n    ASSERT_TRUE(table.Emplace<DmTargetLinear>(0, 1, loop.device(), 0));\n    ASSERT_TRUE(table.valid());\n    TempDevice dev(\"libdm-test-dm-linear\", table);\n    ASSERT_TRUE(dev.valid());\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n\n    std::string path;\n    ASSERT_TRUE(dm.GetDmDevicePathByName(\"libdm-test-dm-linear\", &path));\n    ASSERT_EQ(0, access(path.c_str(), F_OK));\n\n    {\n        // Open a reference to block device.\n        unique_fd fd(TEMP_FAILURE_RETRY(open(dev.path().c_str(), O_RDONLY | O_CLOEXEC)));\n        ASSERT_GE(fd.get(), 0);\n\n        ASSERT_TRUE(dm.DeleteDeviceDeferred(\"libdm-test-dm-linear\"));\n\n        ASSERT_EQ(0, access(path.c_str(), F_OK));\n    }\n\n    // After release device will be removed.\n    ASSERT_TRUE(WaitForFileDeleted(path, 5s));\n    ASSERT_EQ(DmDeviceState::INVALID, dm.GetState(\"libdm-test-dm-linear\"));\n    ASSERT_NE(0, access(path.c_str(), F_OK));\n    ASSERT_EQ(ENOENT, errno);\n}\n\nTEST_F(DmTest, CreateEmptyDevice) {\n    DeviceMapper& dm = DeviceMapper::Instance();\n    ASSERT_TRUE(dm.CreateEmptyDevice(\"empty-device\"));\n    auto guard =\n            android::base::make_scope_guard([&]() { dm.DeleteDeviceIfExists(\"empty-device\", 5s); });\n\n    // Empty device should be in suspended state.\n    ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState(\"empty-device\"));\n}\n\nTEST_F(DmTest, UeventAfterLoadTable) {\n    struct utsname u;\n    ASSERT_EQ(uname(&u), 0);\n\n    unsigned int major, minor;\n    ASSERT_EQ(sscanf(u.release, \"%u.%u\", &major, &minor), 2);\n\n    if (major < 5 || (major == 5 && minor < 15)) {\n        GTEST_SKIP() << \"Skipping test on kernel < 5.15\";\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    ASSERT_TRUE(dm.CreateEmptyDevice(test_name_));\n\n    DmTable table;\n    table.Emplace<DmTargetError>(0, 1);\n    ASSERT_TRUE(dm.LoadTable(test_name_, table));\n\n    std::string ignore_path;\n    ASSERT_TRUE(dm.WaitForDevice(test_name_, 5s, &ignore_path));\n\n    auto info = dm.GetDetailedInfo(test_name_);\n    ASSERT_TRUE(info.has_value());\n    ASSERT_TRUE(info->IsSuspended());\n\n    ASSERT_TRUE(dm.DeleteDevice(test_name_));\n}\n\nTEST_F(DmTest, GetNameAndUuid) {\n    auto& dm = DeviceMapper::Instance();\n    ASSERT_TRUE(dm.CreatePlaceholderDevice(test_name_));\n\n    dev_t dev;\n    ASSERT_TRUE(dm.GetDeviceNumber(test_name_, &dev));\n\n    std::string name, uuid;\n    ASSERT_TRUE(dm.GetDeviceNameAndUuid(dev, &name, &uuid));\n    ASSERT_EQ(name, test_name_);\n    ASSERT_FALSE(uuid.empty());\n}\n\nTEST_F(DmTest, ThinProvisioning) {\n    if (!DeviceMapper::Instance().GetTargetByName(\"thin-pool\", nullptr)) GTEST_SKIP();\n\n    constexpr uint64_t MetaSize = 2_MiB;\n    constexpr uint64_t DataSize = 64_MiB;\n    constexpr uint64_t ThinSize = 1_TiB;\n\n    // Prepare two loop devices for meta and data devices.\n    TemporaryFile meta;\n    ASSERT_GE(meta.fd, 0);\n    ASSERT_EQ(0, ftruncate64(meta.fd, MetaSize));\n    TemporaryFile data;\n    ASSERT_GE(data.fd, 0);\n    ASSERT_EQ(0, ftruncate64(data.fd, DataSize));\n\n    LoopDevice loop_meta(meta.fd, 10s);\n    ASSERT_TRUE(loop_meta.valid());\n    LoopDevice loop_data(data.fd, 10s);\n    ASSERT_TRUE(loop_data.valid());\n\n    // Create a thin-pool\n    DmTable poolTable;\n    poolTable.Emplace<DmTargetThinPool>(0, DataSize / kSectorSize, loop_meta.device(),\n                                        loop_data.device(), 128, 0);\n    TempDevice pool(\"pool\", poolTable);\n    ASSERT_TRUE(pool.valid());\n\n    // Create a thin volume\n    uint64_t thin_volume_id = 0;\n    ASSERT_TRUE(DeviceMapper::Instance().SendMessage(\n            \"pool\", 0, \"create_thin \" + std::to_string(thin_volume_id)));\n\n    // Use a thin volume to create a 1T device\n    DmTable thinTable;\n    thinTable.Emplace<DmTargetThin>(0, ThinSize / kSectorSize, pool.path(), thin_volume_id);\n    TempDevice thin(\"thin\", thinTable);\n    ASSERT_TRUE(thin.valid());\n}\n\nTEST_F(DmTest, RedactDmCrypt) {\n    static constexpr uint64_t kImageSize = 65536;\n    unique_fd temp_file(CreateTempFile(\"file_1\", kImageSize));\n    ASSERT_GE(temp_file, 0);\n\n    LoopDevice loop(temp_file, 10s);\n    ASSERT_TRUE(loop.valid());\n\n    static constexpr const char* kAlgorithm = \"aes-cbc-essiv:sha256\";\n    static constexpr const char* kKey = \"0e64ef514e6a1315b1f6390cb57c9e6a\";\n\n    auto target = std::make_unique<DmTargetCrypt>(0, kImageSize / 512, kAlgorithm, kKey, 0,\n                                                  loop.device(), 0);\n    target->AllowDiscards();\n\n    DmTable table;\n    table.AddTarget(std::move(target));\n\n    auto& dm = DeviceMapper::Instance();\n    std::string crypt_path;\n    ASSERT_TRUE(dm.CreateDevice(test_name_, table, &crypt_path, 10s));\n\n    std::vector<DeviceMapper::TargetInfo> targets;\n    ASSERT_TRUE(dm.GetTableInfo(test_name_, &targets));\n    ASSERT_EQ(targets.size(), 1);\n    EXPECT_EQ(targets[0].data.find(kKey), std::string::npos);\n}\n"
  },
  {
    "path": "fs_mgr/libdm/include/libdm/dm.h",
    "content": "/*\n *  Copyright 2018 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef _LIBDM_DM_H_\n#define _LIBDM_DM_H_\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <linux/dm-ioctl.h>\n#include <linux/kdev_t.h>\n#include <linux/types.h>\n#include <stdint.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <map>\n#include <memory>\n#include <optional>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"dm_table.h\"\n\n// The minimum expected device mapper major.minor version\n#define DM_VERSION0 (4)\n#define DM_VERSION1 (0)\n#define DM_VERSION2 (0)\n\n#define DM_ALIGN_MASK (7)\n#define DM_ALIGN(x) (((x) + DM_ALIGN_MASK) & ~DM_ALIGN_MASK)\n\nnamespace android {\nnamespace dm {\n\nenum class DmDeviceState { INVALID, SUSPENDED, ACTIVE };\n\nstatic constexpr uint64_t kSectorSize = 512;\n\n// Returns `path` without /dev/block prefix if `path` starts with that prefix.\n// Or, if `path` is a symlink, do the same with its real path.\nstd::optional<std::string> ExtractBlockDeviceName(const std::string& path);\n\n// This interface is for testing purposes. See DeviceMapper proper for what these methods do.\nclass IDeviceMapper {\n  public:\n    virtual ~IDeviceMapper() {}\n\n    struct TargetInfo {\n        struct dm_target_spec spec;\n        std::string data;\n        TargetInfo() {}\n        TargetInfo(const struct dm_target_spec& spec, const std::string& data)\n            : spec(spec), data(data) {}\n\n        bool IsOverflowSnapshot() const;\n    };\n\n    virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,\n                              const std::chrono::milliseconds& timeout_ms) = 0;\n    virtual DmDeviceState GetState(const std::string& name) const = 0;\n    virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) = 0;\n    virtual bool LoadTable(const std::string& name, const DmTable& table) = 0;\n    virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) = 0;\n    virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) = 0;\n    virtual bool GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) = 0;\n    virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) = 0;\n    virtual bool GetDeviceString(const std::string& name, std::string* dev) = 0;\n    virtual bool DeleteDeviceIfExists(const std::string& name) = 0;\n};\n\nclass DeviceMapper final : public IDeviceMapper {\n  public:\n    class DmBlockDevice final {\n      public:\n        // only allow creating this with dm_name_list\n        DmBlockDevice() = delete;\n\n        explicit DmBlockDevice(struct dm_name_list* d) : name_(d->name), dev_(d->dev){};\n\n        // Returs device mapper name associated with the block device\n        const std::string& name() const { return name_; }\n\n        // Return major number for the block device\n        uint32_t Major() const { return major(dev_); }\n\n        // Return minor number for the block device\n        uint32_t Minor() const { return minor(dev_); }\n        ~DmBlockDevice() = default;\n\n      private:\n        std::string name_;\n        uint64_t dev_;\n    };\n\n    class Info {\n        uint32_t flags_;\n\n      public:\n        explicit Info(uint32_t flags) : flags_(flags) {}\n\n        bool IsActiveTablePresent() const { return flags_ & DM_ACTIVE_PRESENT_FLAG; }\n        bool IsBufferFull() const { return flags_ & DM_BUFFER_FULL_FLAG; }\n        bool IsInactiveTablePresent() const { return flags_ & DM_INACTIVE_PRESENT_FLAG; }\n        bool IsReadOnly() const { return flags_ & DM_READONLY_FLAG; }\n        bool IsSuspended() const { return !IsActiveTablePresent() || (flags_ & DM_SUSPEND_FLAG); }\n    };\n\n    // Removes a device mapper device with the given name.\n    // Returns 'true' on success, false otherwise.\n    bool DeleteDevice(const std::string& name);\n    bool DeleteDeviceIfExists(const std::string& name) override;\n    // Removes a device mapper device with the given name and waits for |timeout_ms| milliseconds\n    // for the corresponding block device to be deleted.\n    bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms);\n    bool DeleteDeviceIfExists(const std::string& name, const std::chrono::milliseconds& timeout_ms);\n\n    // Enqueues a deletion of device mapper device with the given name once last reference is\n    // closed.\n    // Returns 'true' on success, false otherwise.\n    bool DeleteDeviceDeferred(const std::string& name);\n    bool DeleteDeviceIfExistsDeferred(const std::string& name);\n\n    // Fetches and returns the complete state of the underlying device mapper\n    // device with given name.\n    std::optional<Info> GetDetailedInfo(const std::string& name) const;\n\n    // Returns the current state of the underlying device mapper device\n    // with given name.\n    // One of INVALID, SUSPENDED or ACTIVE.\n    DmDeviceState GetState(const std::string& name) const override;\n\n    // Puts the given device to the specified status, which must be either:\n    // - SUSPENDED: suspend the device, or\n    // - ACTIVE: resumes the device.\n    bool ChangeState(const std::string& name, DmDeviceState state);\n\n    // Creates empty device.\n    // This supports a use case when a caller doesn't need a device straight away, but instead\n    // asks kernel to create it beforehand, thus avoiding blocking itself from waiting for ueventd\n    // to create user space paths.\n    // Callers are expected to then activate their device by calling LoadTableAndActivate function.\n    // To avoid race conditions, callers must still synchronize with ueventd by calling\n    // WaitForDevice function.\n    bool CreateEmptyDevice(const std::string& name);\n\n    // Waits for device paths to be created in the user space.\n    bool WaitForDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,\n                       std::string* path);\n\n    // Creates a device, loads the given table, and activates it. If the device\n    // is not able to be activated, it is destroyed, and false is returned.\n    // After creation, |path| contains the result of calling\n    // GetDmDevicePathByName, and the path is guaranteed to exist. If after\n    // |timeout_ms| the path is not available, the device will be deleted and\n    // this function will return false.\n    //\n    // This variant must be used when depending on the device path. The\n    // following manual sequence should not be used:\n    //\n    //   1. CreateDevice(name, table)\n    //   2. GetDmDevicePathByName(name, &path)\n    //   3. fs_mgr::WaitForFile(path, <timeout>)\n    //\n    // This sequence has a race condition where, if another process deletes a\n    // device, CreateDevice may acquire the same path. When this happens, the\n    // WaitForFile() may early-return since ueventd has not yet processed all\n    // of the outstanding udev events. The caller may unexpectedly get an\n    // ENOENT on a system call using the affected path.\n    //\n    // If |timeout_ms| is 0ms, then this function will return true whether or\n    // not |path| is available. It is the caller's responsibility to ensure\n    // there are no races.\n    bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,\n                      const std::chrono::milliseconds& timeout_ms) override;\n\n    // Create a device and activate the given table, without waiting to acquire\n    // a valid path. If the caller will use GetDmDevicePathByName(), it should\n    // use the timeout variant above.\n    bool CreateDevice(const std::string& name, const DmTable& table);\n\n    // Loads the device mapper table from parameter into the underlying device\n    // mapper device with given name and activate / resumes the device in the\n    // process. A device with the given name must already exist.\n    //\n    // Returns 'true' on success, false otherwise.\n    bool LoadTableAndActivate(const std::string& name, const DmTable& table) override;\n\n    // Same as LoadTableAndActivate, but there is no resume step. This puts the\n    // new table in the inactive slot.\n    //\n    // Returns 'true' on success, false otherwise.\n    bool LoadTable(const std::string& name, const DmTable& table) override;\n\n    // Returns true if a list of available device mapper targets registered in the kernel was\n    // successfully read and stored in 'targets'. Returns 'false' otherwise.\n    bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets);\n\n    // Finds a target by name and returns its information if found. |info| may\n    // be null to check for the existence of a target.\n    bool GetTargetByName(const std::string& name, DmTargetTypeInfo* info);\n\n    // Return 'true' if it can successfully read the list of device mapper block devices\n    // currently created. 'devices' will be empty if the kernel interactions\n    // were successful and there are no block devices at the moment. Returns\n    // 'false' in case of any failure along the way.\n    bool GetAvailableDevices(std::vector<DmBlockDevice>* devices);\n\n    // Returns the path to the device mapper device node in '/dev' corresponding to\n    // 'name'. If the device does not exist, false is returned, and the path\n    // parameter is not set.\n    //\n    // This returns a path in the format \"/dev/block/dm-N\" that can be easily\n    // re-used with sysfs.\n    //\n    // WaitForFile() should not be used in conjunction with this call, since it\n    // could race with ueventd.\n    bool GetDmDevicePathByName(const std::string& name, std::string* path);\n\n    // Returns the device mapper UUID for a given name.  If the device does not\n    // exist, false is returned, and the path parameter is not set.\n    //\n    // WaitForFile() should not be used in conjunction with this call, since it\n    // could race with ueventd.\n    bool GetDmDeviceUuidByName(const std::string& name, std::string* path);\n\n    // Returns a device's unique path as generated by ueventd. This will return\n    // true as long as the device has been created, even if ueventd has not\n    // processed it yet.\n    //\n    // The formatting of this path is /dev/block/mapper/by-uuid/<uuid>.\n    bool GetDeviceUniquePath(const std::string& name, std::string* path);\n\n    // Returns the dev_t for the named device-mapper node.\n    bool GetDeviceNumber(const std::string& name, dev_t* dev);\n\n    // Returns a major:minor string for the named device-mapper node, that can\n    // be used as inputs to DmTargets that take a block device.\n    bool GetDeviceString(const std::string& name, std::string* dev) override;\n\n    // The only way to create a DeviceMapper object.\n    static DeviceMapper& Instance();\n\n    ~DeviceMapper() {\n        if (fd_ != -1) {\n            ::close(fd_);\n        }\n    }\n\n    // Query the status of a table, given a device name. The output vector will\n    // contain one TargetInfo for each target in the table. If the device does\n    // not exist, or there were too many targets, the call will fail and return\n    // false.\n    bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override;\n\n    // Query the status of a table, given a device name. The output vector will\n    // contain IMA TargetInfo for each target in the table. If the device does\n    // not exist, or there were too many targets, the call will fail and return\n    // false.\n    bool GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) override;\n\n    // Identical to GetTableStatus, except also retrives the active table for the device\n    // mapper device from the kernel.\n    bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) override;\n\n    static std::string GetTargetType(const struct dm_target_spec& spec);\n\n    // Returns true if given path is a path to a dm block device.\n    bool IsDmBlockDevice(const std::string& path);\n\n    // Returns name of a dm-device with the given path, or std::nulloptr if given path is not a\n    // dm-device.\n    std::optional<std::string> GetDmDeviceNameByPath(const std::string& path);\n\n    // Returns a parent block device of a dm device with the given path, or std::nullopt if:\n    //  * Given path doesn't correspond to a dm device.\n    //  * A dm device is based on top of more than one block devices.\n    //  * A failure occurred.\n    std::optional<std::string> GetParentBlockDeviceByPath(const std::string& path);\n\n    // Iterate the content over \"/sys/block/dm-x/dm/name\" and find\n    // all the dm-wrapped block devices.\n    //\n    // Returns mapping <partition-name, /dev/block/dm-x>\n    std::map<std::string, std::string> FindDmPartitions();\n\n    // Create a placeholder device. This is useful for ensuring that a uevent is in the pipeline,\n    // to reduce the amount of time a future WaitForDevice will block. On kernels < 5.15, this\n    // simply calls CreateEmptyDevice. On 5.15 and higher, it also loads (but does not activate)\n    // a placeholder table containing dm-error.\n    bool CreatePlaceholderDevice(const std::string& name);\n\n    bool GetDeviceNameAndUuid(dev_t dev, std::string* name, std::string* uuid);\n\n    // Send |message| to target, pointed by |name| and |sector|. Use 0 if |sector| is not needed.\n    bool SendMessage(const std::string& name, uint64_t sector, const std::string& message);\n\n  private:\n    // Maximum possible device mapper targets registered in the kernel.\n    // This is only used to read the list of targets from kernel so we allocate\n    // a finite amount of memory. This limit is in no way enforced by the kernel.\n    static constexpr uint32_t kMaxPossibleDmTargets = 256;\n\n    // Maximum possible device mapper created block devices. Note that this is restricted by\n    // the minor numbers (that used to be 8 bits) that can be range from 0 to 2^20-1 in newer\n    // kernels. In Android systems however, we never expect these to grow beyond the artificial\n    // limit we are imposing here of 256.\n    static constexpr uint32_t kMaxPossibleDmDevices = 256;\n\n    bool CreateDevice(const std::string& name, const std::string& uuid = {});\n    bool GetTable(const std::string& name, uint32_t flags, std::vector<TargetInfo>* table);\n    void InitIo(struct dm_ioctl* io, const std::string& name = std::string()) const;\n\n    DeviceMapper();\n\n    int fd_;\n    // Non-copyable & Non-movable\n    DeviceMapper(const DeviceMapper&) = delete;\n    DeviceMapper& operator=(const DeviceMapper&) = delete;\n    DeviceMapper& operator=(DeviceMapper&&) = delete;\n    DeviceMapper(DeviceMapper&&) = delete;\n};\n\n}  // namespace dm\n}  // namespace android\n\n#endif /* _LIBDM_DM_H_ */\n"
  },
  {
    "path": "fs_mgr/libdm/include/libdm/dm_table.h",
    "content": "/*\n *  Copyright 2018 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef _LIBDM_DMTABLE_H_\n#define _LIBDM_DMTABLE_H_\n\n#include <stdint.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"dm_target.h\"\n\nnamespace android {\nnamespace dm {\n\nclass DmTable {\n  public:\n    DmTable() : num_sectors_(0), readonly_(false) {}\n    DmTable(DmTable&& other) = default;\n\n    // Adds a target to the device mapper table for a range specified in the target object.\n    // The function will return 'true' if the target was successfully added and doesn't overlap with\n    // any of the existing targets in the table. Gaps are allowed. The final check, including\n    // overlaps and gaps are done before loading the table. Returns 'false' on failure.\n    bool AddTarget(std::unique_ptr<DmTarget>&& target);\n\n    // Removes a target from the table for the range specified in the target object. Returns 'false'\n    // if the target name doesn't match with the one in the table. Returns 'true' if target is\n    // successfully removed.\n    bool RemoveTarget(std::unique_ptr<DmTarget>&& target);\n\n    // Adds a target, constructing it in-place for convenience. For example,\n    //\n    //   table.Emplace<DmTargetZero>(0, num_sectors);\n    template <typename T, typename... Args>\n    bool Emplace(Args&&... args) {\n        return AddTarget(std::make_unique<T>(std::forward<Args>(args)...));\n    }\n\n    // Checks the table to make sure it is valid. i.e. Checks for range overlaps, range gaps\n    // and returns 'true' if the table is ready to be loaded into kernel. Returns 'false' if the\n    // table is malformed.\n    bool valid() const;\n\n    // Returns the total number of targets.\n    size_t num_targets() const { return targets_.size(); }\n\n    // Returns the total size represented by the table in terms of number of 512-byte sectors.\n    // NOTE: This function will overlook if there are any gaps in the targets added in the table.\n    uint64_t num_sectors() const;\n\n    // Returns the string represntation of the table that is ready to be passed into the kernel\n    // as part of the DM_TABLE_LOAD ioctl.\n    std::string Serialize() const;\n\n    void set_readonly(bool readonly) { readonly_ = readonly; }\n    bool readonly() const { return readonly_; }\n\n    DmTable& operator=(DmTable&& other) = default;\n\n    ~DmTable() = default;\n\n  private:\n    // list of targets defined in this table sorted by\n    // their start and end sectors.\n    // Note: Overlapping targets MUST never be added in this list.\n    std::vector<std::unique_ptr<DmTarget>> targets_;\n\n    // Total size in terms of # of sectors, as calculated by looking at the last and the first\n    // target in 'target_'.\n    uint64_t num_sectors_;\n\n    // True if the device should be read-only; false otherwise.\n    bool readonly_;\n};\n\n}  // namespace dm\n}  // namespace android\n\n#endif /* _LIBDM_DMTABLE_H_ */\n"
  },
  {
    "path": "fs_mgr/libdm/include/libdm/dm_target.h",
    "content": "/*\n *  Copyright 2018 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef _LIBDM_DMTARGET_H_\n#define _LIBDM_DMTARGET_H_\n\n#include <linux/dm-ioctl.h>\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\nnamespace android {\nnamespace dm {\n\nclass DmTargetTypeInfo {\n  public:\n    DmTargetTypeInfo() : major_(0), minor_(0), patch_(0) {}\n    DmTargetTypeInfo(const struct dm_target_versions* info)\n        : name_(info->name),\n          major_(info->version[0]),\n          minor_(info->version[1]),\n          patch_(info->version[2]) {}\n\n    const std::string& name() const { return name_; }\n    std::string version() const {\n        return std::to_string(major_) + \".\" + std::to_string(minor_) + \".\" + std::to_string(patch_);\n    }\n\n    uint32_t major_version() const { return major_; }\n    uint32_t minor_version() const { return minor_; }\n    uint32_t patch_level() const { return patch_; }\n\n    bool IsAtLeast(uint32_t major, uint32_t minor, uint32_t patch) const {\n        if (major_ > major) return true;\n        if (major_ < major) return false;\n        if (minor_ > minor) return true;\n        if (minor_ < minor) return false;\n        return patch_ >= patch;\n    }\n\n  private:\n    std::string name_;\n    uint32_t major_;\n    uint32_t minor_;\n    uint32_t patch_;\n};\n\nclass DmTarget {\n  public:\n    DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}\n\n    virtual ~DmTarget() = default;\n\n    // Returns name of the target.\n    virtual std::string name() const = 0;\n\n    // Return the first logical sector represented by this target.\n    uint64_t start() const { return start_; }\n\n    // Returns size in number of sectors when this target is part of\n    // a DmTable, return 0 otherwise.\n    uint64_t size() const { return length_; }\n\n    // Function that converts this object to a string of arguments that can\n    // be passed to the kernel for adding this target in a table. Each target (e.g. verity, linear)\n    // must implement this, for it to be used on a device.\n    std::string Serialize() const;\n\n    virtual bool Valid() const { return true; }\n\n  protected:\n    // Get the parameter string that is passed to the end of the dm_target_spec\n    // for this target type.\n    virtual std::string GetParameterString() const = 0;\n\n  private:\n    // logical sector number start and total length (in terms of 512-byte sectors) represented\n    // by this target within a DmTable.\n    uint64_t start_, length_;\n};\n\nclass DmTargetZero final : public DmTarget {\n  public:\n    DmTargetZero(uint64_t start, uint64_t length) : DmTarget(start, length) {}\n\n    std::string name() const override { return \"zero\"; }\n    std::string GetParameterString() const override;\n};\n\nclass DmTargetLinear final : public DmTarget {\n  public:\n    DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,\n                   uint64_t physical_sector)\n        : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}\n\n    std::string name() const override { return \"linear\"; }\n    std::string GetParameterString() const override;\n    const std::string& block_device() const { return block_device_; }\n\n  private:\n    std::string block_device_;\n    uint64_t physical_sector_;\n};\n\nclass DmTargetStripe final : public DmTarget {\n  public:\n    DmTargetStripe(uint64_t start, uint64_t length, uint64_t chunksize,\n                   const std::string& block_device0, const std::string& block_device1)\n        : DmTarget(start, length),\n          chunksize(chunksize),\n          block_device0_(block_device0),\n          block_device1_(block_device1) {}\n\n    std::string name() const override { return \"striped\"; }\n    std::string GetParameterString() const override;\n\n  private:\n    uint64_t chunksize;\n    std::string block_device0_;\n    std::string block_device1_;\n};\n\nclass DmTargetVerity final : public DmTarget {\n  public:\n    DmTargetVerity(uint64_t start, uint64_t length, uint32_t version,\n                   const std::string& block_device, const std::string& hash_device,\n                   uint32_t data_block_size, uint32_t hash_block_size, uint32_t num_data_blocks,\n                   uint32_t hash_start_block, const std::string& hash_algorithm,\n                   const std::string& root_digest, const std::string& salt);\n\n    void UseFec(const std::string& device, uint32_t num_roots, uint32_t num_blocks, uint32_t start);\n    void SetVerityMode(const std::string& mode);\n    void IgnoreZeroBlocks();\n    void CheckAtMostOnce();\n\n    std::string name() const override { return \"verity\"; }\n    std::string GetParameterString() const override;\n    bool Valid() const override { return valid_; }\n\n  private:\n    std::vector<std::string> base_args_;\n    std::vector<std::string> optional_args_;\n    bool valid_;\n};\n\nclass DmTargetAndroidVerity final : public DmTarget {\n  public:\n    DmTargetAndroidVerity(uint64_t start, uint64_t length, const std::string& block_device,\n                          const std::string& keyid)\n        : DmTarget(start, length), keyid_(keyid), block_device_(block_device) {}\n\n    std::string name() const override { return \"android-verity\"; }\n    std::string GetParameterString() const override;\n\n  private:\n    std::string keyid_;\n    std::string block_device_;\n};\n\n// This is the same as DmTargetVerity, but the table may be specified as a raw\n// string. This code exists only for fs_mgr_verity and should be avoided. Use\n// DmTargetVerity for new code instead.\nclass DmTargetVerityString final : public DmTarget {\n  public:\n    DmTargetVerityString(uint64_t start, uint64_t length, const std::string& target_string)\n        : DmTarget(start, length), target_string_(target_string) {}\n\n    std::string name() const override { return \"verity\"; }\n    std::string GetParameterString() const override { return target_string_; }\n    bool Valid() const override { return true; }\n\n  private:\n    std::string target_string_;\n};\n\n// dm-bow is the backup on write target that can provide checkpoint capability\n// for file systems that do not support checkpoints natively\nclass DmTargetBow final : public DmTarget {\n  public:\n    DmTargetBow(uint64_t start, uint64_t length, const std::string& target_string)\n        : DmTarget(start, length), target_string_(target_string) {}\n\n    void SetBlockSize(uint32_t block_size) { block_size_ = block_size; }\n\n    std::string name() const override { return \"bow\"; }\n    std::string GetParameterString() const override;\n\n  private:\n    std::string target_string_;\n    uint32_t block_size_ = 0;\n};\n\nenum class SnapshotStorageMode {\n    // The snapshot will be persisted to the COW device.\n    Persistent,\n    // The snapshot will be lost on reboot.\n    Transient,\n    // The snapshot will be merged from the COW device into the base device,\n    // in the background.\n    Merge\n};\n\n// Writes to a snapshot device will be written to the given COW device. Reads\n// will read from the COW device or base device. The chunk size is specified\n// in sectors.\nclass DmTargetSnapshot final : public DmTarget {\n  public:\n    DmTargetSnapshot(uint64_t start, uint64_t length, const std::string& base_device,\n                     const std::string& cow_device, SnapshotStorageMode mode, uint64_t chunk_size)\n        : DmTarget(start, length),\n          base_device_(base_device),\n          cow_device_(cow_device),\n          mode_(mode),\n          chunk_size_(chunk_size) {}\n\n    std::string name() const override;\n    std::string GetParameterString() const override;\n    bool Valid() const override { return true; }\n\n    struct Status {\n        uint64_t sectors_allocated;\n        uint64_t total_sectors;\n        uint64_t metadata_sectors;\n        std::string error;\n    };\n\n    static double MergePercent(const Status& status, uint64_t sectors_initial = 0);\n    static bool ParseStatusText(const std::string& text, Status* status);\n    static bool ReportsOverflow(const std::string& target_type);\n    static bool GetDevicesFromParams(const std::string& params, std::string* base_device,\n                                     std::string* cow_device);\n\n  private:\n    std::string base_device_;\n    std::string cow_device_;\n    SnapshotStorageMode mode_;\n    uint64_t chunk_size_;\n};\n\n// snapshot-origin will read/write directly to the backing device, updating any\n// snapshot devices with a matching origin.\nclass DmTargetSnapshotOrigin final : public DmTarget {\n  public:\n    DmTargetSnapshotOrigin(uint64_t start, uint64_t length, const std::string& device)\n        : DmTarget(start, length), device_(device) {}\n\n    std::string name() const override { return \"snapshot-origin\"; }\n    std::string GetParameterString() const override { return device_; }\n    bool Valid() const override { return true; }\n\n  private:\n    std::string device_;\n};\n\nclass DmTargetCrypt final : public DmTarget {\n  public:\n    DmTargetCrypt(uint64_t start, uint64_t length, const std::string& cipher,\n                  const std::string& key, uint64_t iv_sector_offset, const std::string& device,\n                  uint64_t device_sector)\n        : DmTarget(start, length),\n          cipher_(cipher),\n          key_(key),\n          iv_sector_offset_(iv_sector_offset),\n          device_(device),\n          device_sector_(device_sector) {}\n\n    void AllowDiscards() { allow_discards_ = true; }\n    void AllowEncryptOverride() { allow_encrypt_override_ = true; }\n    void SetIvLargeSectors() { iv_large_sectors_ = true; }\n    void SetSectorSize(uint32_t sector_size) { sector_size_ = sector_size; }\n\n    std::string name() const override { return \"crypt\"; }\n    bool Valid() const override { return true; }\n    std::string GetParameterString() const override;\n\n  private:\n    std::string cipher_;\n    std::string key_;\n    uint64_t iv_sector_offset_;\n    std::string device_;\n    uint64_t device_sector_;\n    bool allow_discards_ = false;\n    bool allow_encrypt_override_ = false;\n    bool iv_large_sectors_ = false;\n    uint32_t sector_size_ = 0;\n};\n\nclass DmTargetDefaultKey final : public DmTarget {\n  public:\n    DmTargetDefaultKey(uint64_t start, uint64_t length, const std::string& cipher,\n                       const std::string& key, const std::string& blockdev, uint64_t start_sector)\n        : DmTarget(start, length),\n          cipher_(cipher),\n          key_(key),\n          blockdev_(blockdev),\n          start_sector_(start_sector) {}\n\n    std::string name() const override { return kName; }\n    bool Valid() const override;\n    std::string GetParameterString() const override;\n    void SetUseLegacyOptionsFormat() { use_legacy_options_format_ = true; }\n    void SetSetDun() { set_dun_ = true; }\n    void SetWrappedKeyV0() { is_hw_wrapped_ = true; }\n\n  private:\n    inline static const std::string kName = \"default-key\";\n\n    std::string cipher_;\n    std::string key_;\n    std::string blockdev_;\n    uint64_t start_sector_;\n    bool use_legacy_options_format_ = false;\n    bool set_dun_ = false;\n    bool is_hw_wrapped_ = false;\n};\n\nclass DmTargetUser final : public DmTarget {\n  public:\n    DmTargetUser(uint64_t start, uint64_t length, std::string control_device)\n        : DmTarget(start, length), control_device_(control_device) {}\n\n    std::string name() const override { return \"user\"; }\n    std::string control_device() const { return control_device_; }\n    std::string GetParameterString() const override;\n\n  private:\n    std::string control_device_;\n};\n\nclass DmTargetError final : public DmTarget {\n  public:\n    DmTargetError(uint64_t start, uint64_t length) : DmTarget(start, length) {}\n\n    std::string name() const override { return \"error\"; }\n    std::string GetParameterString() const override { return \"\"; }\n};\n\nclass DmTargetThinPool final : public DmTarget {\n  public:\n    DmTargetThinPool(uint64_t start, uint64_t length, const std::string& metadata_dev,\n                     const std::string& data_dev, uint64_t data_block_size,\n                     uint64_t low_water_mark);\n\n    std::string name() const override { return \"thin-pool\"; }\n    std::string GetParameterString() const override;\n    bool Valid() const override;\n\n  private:\n    std::string metadata_dev_;\n    std::string data_dev_;\n    uint64_t data_block_size_;\n    uint64_t low_water_mark_;\n};\n\nclass DmTargetThin final : public DmTarget {\n  public:\n    DmTargetThin(uint64_t start, uint64_t length, const std::string& pool_dev, uint64_t dev_id);\n\n    std::string name() const override { return \"thin\"; }\n    std::string GetParameterString() const override;\n\n  private:\n    std::string pool_dev_;\n    uint64_t dev_id_;\n};\n\n}  // namespace dm\n}  // namespace android\n\n#endif /* _LIBDM_DMTARGET_H_ */\n"
  },
  {
    "path": "fs_mgr/libdm/include/libdm/loop_control.h",
    "content": "/*\n *  Copyright 2018 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef _LIBDM_LOOP_CONTROL_H_\n#define _LIBDM_LOOP_CONTROL_H_\n\n#include <chrono>\n#include <string>\n\n#include <android-base/unique_fd.h>\n\nnamespace android {\nnamespace dm {\n\nclass LoopControl final {\n  public:\n    LoopControl();\n\n    // Attaches the file specified by 'file_fd' to the loop device specified\n    // by 'loopdev'. It is possible that in between allocating and attaching\n    // a loop device, another process attaches to the chosen loop device. If\n    // this happens, Attach() will retry for up to |timeout_ms|. The timeout\n    // should not be zero.\n    //\n    // The caller does not have to call WaitForFile(); it is implicitly called.\n    // The given |timeout_ms| covers both potential sources of timeout.\n    bool Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,\n                std::string* loopdev) const;\n\n    // Detach the loop device given by 'loopdev' from the attached backing file.\n    bool Detach(const std::string& loopdev) const;\n\n    // Enable Direct I/O on a loop device. This requires kernel 4.9+.\n    static bool EnableDirectIo(int fd);\n\n    // Set LO_FLAGS_AUTOCLEAR on a loop device.\n    static bool SetAutoClearStatus(int fd);\n\n    LoopControl(const LoopControl&) = delete;\n    LoopControl& operator=(const LoopControl&) = delete;\n    LoopControl& operator=(LoopControl&&) = default;\n    LoopControl(LoopControl&&) = default;\n\n  private:\n    bool FindFreeLoopDevice(std::string* loopdev) const;\n\n    static constexpr const char* kLoopControlDevice = \"/dev/loop-control\";\n\n    android::base::unique_fd control_fd_;\n};\n\n// Create a temporary loop device around a file descriptor or path.\nclass LoopDevice {\n  public:\n    // Create a loop device for the given file descriptor. It is closed when\n    // LoopDevice is destroyed only if auto_close is true.\n    LoopDevice(android::base::borrowed_fd fd, const std::chrono::milliseconds& timeout_ms,\n               bool auto_close = false);\n    // Create a loop device for the given file path. It will be opened for\n    // reading and writing and closed when the loop device is detached.\n    LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms);\n    ~LoopDevice();\n\n    bool valid() const { return valid_; }\n    const std::string& device() const { return device_; }\n\n    LoopDevice(const LoopDevice&) = delete;\n    LoopDevice& operator=(const LoopDevice&) = delete;\n    LoopDevice& operator=(LoopDevice&&) = default;\n    LoopDevice(LoopDevice&&) = default;\n\n  private:\n    void Init(const std::chrono::milliseconds& timeout_ms);\n\n    android::base::borrowed_fd fd_;\n    android::base::unique_fd owned_fd_;\n    std::string device_;\n    LoopControl control_;\n    bool valid_ = false;\n};\n\n}  // namespace dm\n}  // namespace android\n\n#endif /* _LIBDM_LOOP_CONTROL_H_ */\n"
  },
  {
    "path": "fs_mgr/libdm/loop_control.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libdm/loop_control.h\"\n\n#include <fcntl.h>\n#include <linux/loop.h>\n#include <stdint.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace dm {\n\nLoopControl::LoopControl() : control_fd_(-1) {\n    control_fd_.reset(TEMP_FAILURE_RETRY(open(kLoopControlDevice, O_RDWR | O_CLOEXEC)));\n    if (control_fd_ < 0) {\n        PLOG(ERROR) << \"Failed to open loop-control\";\n    }\n}\n\nbool LoopControl::Attach(int file_fd, const std::chrono::milliseconds& timeout_ms,\n                         std::string* loopdev) const {\n    auto start_time = std::chrono::steady_clock::now();\n    auto condition = [&]() -> WaitResult {\n        if (!FindFreeLoopDevice(loopdev)) {\n            LOG(ERROR) << \"Failed to attach, no free loop devices\";\n            return WaitResult::Fail;\n        }\n\n        auto now = std::chrono::steady_clock::now();\n        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);\n        if (!WaitForFile(*loopdev, timeout_ms - time_elapsed)) {\n            LOG(ERROR) << \"Timed out waiting for path: \" << *loopdev;\n            return WaitResult::Fail;\n        }\n\n        android::base::unique_fd loop_fd(\n                TEMP_FAILURE_RETRY(open(loopdev->c_str(), O_RDWR | O_CLOEXEC)));\n        if (loop_fd < 0) {\n            PLOG(ERROR) << \"Failed to open: \" << *loopdev;\n            return WaitResult::Fail;\n        }\n\n        if (int rc = ioctl(loop_fd, LOOP_SET_FD, file_fd); rc == 0) {\n            return WaitResult::Done;\n        }\n        if (errno != EBUSY) {\n            PLOG(ERROR) << \"Failed LOOP_SET_FD\";\n            return WaitResult::Fail;\n        }\n        return WaitResult::Wait;\n    };\n    if (!WaitForCondition(condition, timeout_ms)) {\n        LOG(ERROR) << \"Timed out trying to acquire a loop device\";\n        return false;\n    }\n    return true;\n}\n\nbool LoopControl::Detach(const std::string& loopdev) const {\n    if (loopdev.empty()) {\n        LOG(ERROR) << \"Must provide a loop device\";\n        return false;\n    }\n\n    android::base::unique_fd loop_fd(TEMP_FAILURE_RETRY(open(loopdev.c_str(), O_RDWR | O_CLOEXEC)));\n    if (loop_fd < 0) {\n        PLOG(ERROR) << \"Failed to open: \" << loopdev;\n        return false;\n    }\n\n    int rc = ioctl(loop_fd, LOOP_CLR_FD, 0);\n    if (rc) {\n        PLOG(ERROR) << \"Failed LOOP_CLR_FD for '\" << loopdev << \"'\";\n        return false;\n    }\n    return true;\n}\n\nbool LoopControl::FindFreeLoopDevice(std::string* loopdev) const {\n    int rc = ioctl(control_fd_, LOOP_CTL_GET_FREE);\n    if (rc < 0) {\n        PLOG(ERROR) << \"Failed to get free loop device\";\n        return false;\n    }\n\n    // Ueventd on android creates all loop devices as /dev/block/loopX\n    // The total number of available devices is determined by 'loop.max_part'\n    // kernel command line argument.\n    *loopdev = ::android::base::StringPrintf(\"/dev/block/loop%d\", rc);\n    return true;\n}\n\nbool LoopControl::EnableDirectIo(int fd) {\n#if !defined(LOOP_SET_BLOCK_SIZE)\n    static constexpr int LOOP_SET_BLOCK_SIZE = 0x4C09;\n#endif\n#if !defined(LOOP_SET_DIRECT_IO)\n    static constexpr int LOOP_SET_DIRECT_IO = 0x4C08;\n#endif\n\n    // Note: the block size has to be >= the logical block size of the underlying\n    // block device, *not* the filesystem block size.\n    if (ioctl(fd, LOOP_SET_BLOCK_SIZE, 4096)) {\n        PLOG(ERROR) << \"Could not set loop device block size\";\n        return false;\n    }\n    if (ioctl(fd, LOOP_SET_DIRECT_IO, 1)) {\n        PLOG(ERROR) << \"Could not set loop direct IO\";\n        return false;\n    }\n    return true;\n}\n\nbool LoopControl::SetAutoClearStatus(int fd) {\n    struct loop_info64 info = {};\n\n    info.lo_flags |= LO_FLAGS_AUTOCLEAR;\n    if (ioctl(fd, LOOP_SET_STATUS64, &info)) {\n        return false;\n    }\n    return true;\n}\n\nLoopDevice::LoopDevice(android::base::borrowed_fd fd, const std::chrono::milliseconds& timeout_ms,\n                       bool auto_close)\n    : fd_(fd), owned_fd_(-1) {\n    if (auto_close) {\n        owned_fd_.reset(fd.get());\n    }\n    Init(timeout_ms);\n}\n\nLoopDevice::LoopDevice(const std::string& path, const std::chrono::milliseconds& timeout_ms)\n    : fd_(-1), owned_fd_(-1) {\n    owned_fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));\n    if (owned_fd_ == -1) {\n        PLOG(ERROR) << \"open failed for \" << path;\n        return;\n    }\n    fd_ = owned_fd_;\n    Init(timeout_ms);\n}\n\nLoopDevice::~LoopDevice() {\n    if (valid()) {\n        control_.Detach(device_);\n    }\n}\n\nvoid LoopDevice::Init(const std::chrono::milliseconds& timeout_ms) {\n    valid_ = control_.Attach(fd_.get(), timeout_ms, &device_);\n}\n\n}  // namespace dm\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libdm/loop_control_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libdm/loop_control.h\"\n\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n#include \"test_util.h\"\n\nusing namespace std;\nusing namespace android::dm;\nusing unique_fd = android::base::unique_fd;\n\nstatic unique_fd TempFile() {\n    // A loop device needs to be at least one sector to actually work, so fill\n    // up the file with a message.\n    unique_fd fd(CreateTempFile(\"temp\", 0));\n    if (fd < 0) {\n        return {};\n    }\n    char buffer[] = \"Hello\";\n    for (size_t i = 0; i < 1000; i++) {\n        if (!android::base::WriteFully(fd, buffer, sizeof(buffer))) {\n            perror(\"write\");\n            return {};\n        }\n    }\n    return fd;\n}\n\nTEST(libdm, LoopControl) {\n    unique_fd fd = TempFile();\n    ASSERT_GE(fd, 0);\n\n    LoopDevice loop(fd, 10s);\n    ASSERT_TRUE(loop.valid());\n\n    char buffer[6];\n    unique_fd loop_fd(open(loop.device().c_str(), O_RDWR));\n    ASSERT_GE(loop_fd, 0);\n    ASSERT_TRUE(android::base::ReadFully(loop_fd, buffer, sizeof(buffer)));\n    ASSERT_EQ(memcmp(buffer, \"Hello\", 6), 0);\n}\n"
  },
  {
    "path": "fs_mgr/libdm/test_util.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fcntl.h>\n#include <linux/memfd.h>\n#include <stdio.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"test_util.h\"\n\nnamespace android {\nnamespace dm {\n\nusing unique_fd = android::base::unique_fd;\n\n// Create a temporary in-memory file. If size is non-zero, the file will be\n// created with a fixed size.\nunique_fd CreateTempFile(const std::string& name, size_t size) {\n    unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING));\n    if (fd < 0) {\n        return {};\n    }\n    if (size) {\n        if (ftruncate(fd, size) < 0) {\n            perror(\"ftruncate\");\n            return {};\n        }\n        if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {\n            perror(\"fcntl\");\n            return {};\n        }\n    }\n    return fd;\n}\n\n}  // namespace dm\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libdm/test_util.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBDM_TEST_UTILS_H_\n#define _LIBDM_TEST_UTILS_H_\n\n#include <android-base/unique_fd.h>\n#include <stddef.h>\n\n#include <chrono>\n#include <string>\n\n#include <libdm/dm.h>\n#include <libdm/dm_table.h>\n\nnamespace android {\nnamespace dm {\n\n// Create a temporary in-memory file. If size is non-zero, the file will be\n// created with a fixed size.\nandroid::base::unique_fd CreateTempFile(const std::string& name, size_t size);\n\n// Helper to ensure that device mapper devices are released.\nclass TempDevice {\n  public:\n    TempDevice(const std::string& name, const DmTable& table)\n        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {\n        valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));\n    }\n    TempDevice(TempDevice&& other) noexcept\n        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {\n        other.valid_ = false;\n    }\n    ~TempDevice() {\n        if (valid_) {\n            dm_.DeleteDevice(name_);\n        }\n    }\n    bool Destroy() {\n        if (!valid_) {\n            return false;\n        }\n        valid_ = false;\n        return dm_.DeleteDevice(name_);\n    }\n    std::string path() const { return path_; }\n    const std::string& name() const { return name_; }\n    bool valid() const { return valid_; }\n\n    TempDevice(const TempDevice&) = delete;\n    TempDevice& operator=(const TempDevice&) = delete;\n\n    TempDevice& operator=(TempDevice&& other) noexcept {\n        name_ = other.name_;\n        valid_ = other.valid_;\n        other.valid_ = false;\n        return *this;\n    }\n\n  private:\n    DeviceMapper& dm_;\n    std::string name_;\n    std::string path_;\n    bool valid_;\n};\n\n}  // namespace dm\n}  // namespace android\n\n#endif  // _LIBDM_TEST_UTILS_H_\n"
  },
  {
    "path": "fs_mgr/libdm/utility.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"utility.h\"\n\n#include <errno.h>\n#include <unistd.h>\n\n#include <thread>\n\n#include <android-base/logging.h>\n\nusing namespace std::literals;\n\nnamespace android {\nnamespace dm {\n\nbool WaitForCondition(const std::function<WaitResult()>& condition,\n                      const std::chrono::milliseconds& timeout_ms) {\n    auto start_time = std::chrono::steady_clock::now();\n    while (true) {\n        auto result = condition();\n        if (result == WaitResult::Done) return true;\n        if (result == WaitResult::Fail) return false;\n\n        std::this_thread::sleep_for(20ms);\n\n        auto now = std::chrono::steady_clock::now();\n        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);\n        if (time_elapsed > timeout_ms) return false;\n    }\n}\n\nbool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms) {\n    auto condition = [&]() -> WaitResult {\n        // If the file exists but returns EPERM or something, we consider the\n        // condition met.\n        if (access(path.c_str(), F_OK) != 0) {\n            if (errno == ENOENT) {\n                return WaitResult::Wait;\n            }\n            PLOG(ERROR) << \"access failed: \" << path;\n            return WaitResult::Fail;\n        }\n        return WaitResult::Done;\n    };\n    return WaitForCondition(condition, timeout_ms);\n}\n\nbool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms) {\n    auto condition = [&]() -> WaitResult {\n        if (access(path.c_str(), F_OK) == 0) {\n            return WaitResult::Wait;\n        }\n        if (errno != ENOENT) {\n            PLOG(ERROR) << \"access failed: \" << path;\n            return WaitResult::Fail;\n        }\n        return WaitResult::Done;\n    };\n    return WaitForCondition(condition, timeout_ms);\n}\n\n}  // namespace dm\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libdm/utility.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <chrono>\n#include <functional>\n\nnamespace android {\nnamespace dm {\n\nenum class WaitResult { Wait, Done, Fail };\n\nbool WaitForFile(const std::string& path, const std::chrono::milliseconds& timeout_ms);\nbool WaitForFileDeleted(const std::string& path, const std::chrono::milliseconds& timeout_ms);\nbool WaitForCondition(const std::function<WaitResult()>& condition,\n                      const std::chrono::milliseconds& timeout_ms);\n\n}  // namespace dm\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/Android.bp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_headers {\n    name: \"libfiemap_headers\",\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    export_include_dirs: [\"include\"],\n    host_supported: true,\n}\n\nfilegroup {\n    name: \"libfiemap_srcs\",\n    srcs: [\n        \"fiemap_writer.cpp\",\n        \"fiemap_status.cpp\",\n        \"image_manager.cpp\",\n        \"metadata.cpp\",\n        \"split_fiemap_writer.cpp\",\n        \"utility.cpp\",\n    ],\n}\n\nfilegroup {\n    name: \"libfiemap_binder_srcs\",\n    srcs: [\n        \"binder.cpp\",\n    ],\n}\n\ncc_defaults {\n    name: \"libfiemap_binder_defaults\",\n    srcs: [\":libfiemap_binder_srcs\"],\n    whole_static_libs: [\n        \"gsi_aidl_interface-cpp\",\n        \"libgsi\",\n        \"libgsid\",\n    ],\n    shared_libs: [\n        \"libbinder\",\n        \"libutils\",\n    ],\n}\n\n// Open up a passthrough IImageManager interface. Use libfiemap_binder whenever\n// possible. This should only be used when binder is not available.\nfilegroup {\n    name: \"libfiemap_passthrough_srcs\",\n    srcs: [\n        \"passthrough.cpp\",\n    ],\n}\n\ncc_test {\n    name: \"fiemap_writer_test\",\n    static_libs: [\n        \"libbase\",\n        \"libdm\",\n        \"libfs_mgr\",\n        \"liblog\",\n        \"libgsi\",\n    ],\n\n    data: [\n        \"testdata/unaligned_file\",\n        \"testdata/file_4k\",\n        \"testdata/file_32k\",\n    ],\n\n    srcs: [\n        \"fiemap_writer_test.cpp\",\n    ],\n\n    test_suites: [\"vts\", \"device-tests\"],\n    auto_gen_config: true,\n    test_options: {\n        min_shipping_api_level: 29,\n    },\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    require_root: true,\n}\n\ncc_test {\n    name: \"fiemap_image_test\",\n    static_libs: [\n        \"libcrypto_utils\",\n        \"libdm\",\n        \"libext4_utils\",\n        \"libfs_mgr\",\n        \"liblp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcrypto\",\n        \"libcutils\",\n        \"liblog\",\n    ],\n    srcs: [\n        \"image_test.cpp\",\n    ],\n    test_suites: [\"device-tests\"],\n    auto_gen_config: true,\n    require_root: true,\n}\n"
  },
  {
    "path": "fs_mgr/libfiemap/README.md",
    "content": "libfiemap\n=============\n\n`libfiemap` is a library for creating block-devices that are backed by\nstorage in read-write partitions. It exists primary for gsid. Generally, the\nlibrary works by using `libfiemap_writer` to allocate large files within\nfilesystem, and then tracks their extents.\n\nThere are three main uses for `libfiemap`:\n - Creating images that will act as block devices. For example, gsid needs to\n   create a `system_gsi` image to store Dynamic System Updates.\n - Mapping the image as a block device while /data is mounted. This is fairly\n   tricky and is described in more detail below.\n - Mapping the image as a block device during first-stage init. This is simple\n   because it uses the same logic from dynamic partitions.\n\nImage creation is done through `SplitFiemap`. Depending on the file system,\na large image may have to be split into multiple files. On Ext4 the limit is\n16GiB and on FAT32 it's 4GiB. Images are saved into `/data/gsi/<name>/`\nwhere `<name>` is chosen by the process requesting the image.\n\nAt the same time, a file called `/metadata/gsi/<name>/lp_metadata` is created.\nThis is a super partition header that allows first-stage init to create dynamic\npartitions from the image files. It also tracks the canonical size of the image,\nsince the file size may be larger due to alignment.\n\nMapping\n-------\n\nIt is easy to make block devices out of blocks on `/data` when it is not\nmounted, so first-stage init has no issues mapping dynamic partitions from\nimages. After `/data` is mounted however, there are two problems:\n - `/data` is encrypted.\n - `/dev/block/by-name/data` may be marked as in-use.\n\nWe break the problem down into three scenarios.\n\n### Metadata Encrypted Devices\n\nWhen metadata encryption is used, `/data` is not mounted from\n`/dev/block/by-name/data`. Instead, it is mounted from an intermediate\n`dm-default-key` device. This means the underlying device is not marked in use,\nand we can create new dm-linear devices on top of it.\n\nOn these devices, a block device for an image will consist of a single\ndevice-mapper device with a `dm-linear` table entry for each extent in the\nbacking file.\n\n### Unencrypted and FBE-only Devices\n\nWhen a device is unencrypted, or is encrypted with FBE but not metadata\nencryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.\nSince `/data/gsi` has encryption disabled, this means the raw blocks will be\nunencrypted as well.\n\n### Split Images\n\nIf an image was too large to store a single file on the underlying filesystem,\non an FBE/unencrypted device we will have multiple loop devices. In this case,\nwe create a device-mapper device as well. For each loop device it will have one\n`dm-linear` table entry spanning the length of the device.\n\nState Tracking\n--------------\n\nIt's important that we know whether or not an image is currently in-use by a\nblock device. It could be catastrophic to write to a dm-linear device if the\nunderlying blocks are no longer owned by the original file. Thus, when mapping\nan image, we create a property called `gsid.mapped_image.<name>` and set it to\nthe path of the block device.\n\nAdditionally, we create a `/metadata/gsi/<subdir>/<name>.status` file. Each\nline in this file denotes a dependency on either a device-mapper node or a loop\ndevice. When deleting a block device, this file is used to release all\nresources.\n"
  },
  {
    "path": "fs_mgr/libfiemap/binder.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#if !defined(__ANDROID_RECOVERY__)\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android/gsi/BnProgressCallback.h>\n#include <android/gsi/IGsiService.h>\n#include <libfiemap/image_manager.h>\n#include <libgsi/libgsi.h>\n#include <libgsi/libgsid.h>\n\nnamespace android {\nnamespace fiemap {\n\nusing namespace android::gsi;\nusing namespace std::chrono_literals;\n\nclass ProgressCallback final : public BnProgressCallback {\n  public:\n    ProgressCallback(std::function<bool(uint64_t, uint64_t)>&& callback)\n        : callback_(std::move(callback)) {\n        CHECK(callback_);\n    }\n    android::binder::Status onProgress(int64_t current, int64_t total) {\n        if (callback_(static_cast<uint64_t>(current), static_cast<uint64_t>(total))) {\n            return android::binder::Status::ok();\n        }\n        return android::binder::Status::fromServiceSpecificError(UNKNOWN_ERROR,\n                                                                 \"Progress callback failed\");\n    }\n\n  private:\n    std::function<bool(uint64_t, uint64_t)> callback_;\n};\n\nclass ImageManagerBinder final : public IImageManager {\n  public:\n    ImageManagerBinder(android::sp<IGsiService>&& service, android::sp<IImageService>&& manager);\n    FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,\n                                    std::function<bool(uint64_t, uint64_t)>&& on_progress) override;\n    bool DeleteBackingImage(const std::string& name) override;\n    bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,\n                        std::string* path) override;\n    bool UnmapImageDevice(const std::string& name) override;\n    bool BackingImageExists(const std::string& name) override;\n    bool IsImageMapped(const std::string& name) override;\n    bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,\n                                  std::string* dev) override;\n    FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) override;\n    bool RemoveAllImages() override;\n    bool DisableAllImages() override;\n    bool DisableImage(const std::string& name) override;\n    bool RemoveDisabledImages() override;\n    bool GetMappedImageDevice(const std::string& name, std::string* device) override;\n    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;\n    bool IsImageDisabled(const std::string& name) override;\n\n    std::vector<std::string> GetAllBackingImages() override;\n\n  private:\n    android::sp<IGsiService> service_;\n    android::sp<IImageService> manager_;\n};\n\nstatic FiemapStatus ToFiemapStatus(const char* func, const binder::Status& status) {\n    if (!status.isOk()) {\n        LOG(ERROR) << func << \" binder returned: \" << status.toString8().c_str();\n        if (status.serviceSpecificErrorCode() != 0) {\n            return FiemapStatus::FromErrorCode(status.serviceSpecificErrorCode());\n        } else {\n            return FiemapStatus::Error();\n        }\n    }\n    return FiemapStatus::Ok();\n}\n\nImageManagerBinder::ImageManagerBinder(android::sp<IGsiService>&& service,\n                                       android::sp<IImageService>&& manager)\n    : service_(std::move(service)), manager_(std::move(manager)) {}\n\nFiemapStatus ImageManagerBinder::CreateBackingImage(\n        const std::string& name, uint64_t size, int flags,\n        std::function<bool(uint64_t, uint64_t)>&& on_progress) {\n    sp<IProgressCallback> callback = nullptr;\n    if (on_progress) {\n        callback = new ProgressCallback(std::move(on_progress));\n    }\n    auto status = manager_->createBackingImage(name, size, flags, callback);\n    return ToFiemapStatus(__PRETTY_FUNCTION__, status);\n}\n\nbool ImageManagerBinder::DeleteBackingImage(const std::string& name) {\n    auto status = manager_->deleteBackingImage(name);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return true;\n}\n\nbool ImageManagerBinder::MapImageDevice(const std::string& name,\n                                        const std::chrono::milliseconds& timeout_ms,\n                                        std::string* path) {\n    int32_t timeout_ms_count =\n            static_cast<int32_t>(std::clamp<typename std::chrono::milliseconds::rep>(\n                    timeout_ms.count(), INT32_MIN, INT32_MAX));\n    MappedImage map;\n    auto status = manager_->mapImageDevice(name, timeout_ms_count, &map);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    *path = map.path;\n    return true;\n}\n\nbool ImageManagerBinder::UnmapImageDevice(const std::string& name) {\n    auto status = manager_->unmapImageDevice(name);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return true;\n}\n\nbool ImageManagerBinder::BackingImageExists(const std::string& name) {\n    bool retval;\n    auto status = manager_->backingImageExists(name, &retval);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return retval;\n}\n\nbool ImageManagerBinder::IsImageMapped(const std::string& name) {\n    bool retval;\n    auto status = manager_->isImageMapped(name, &retval);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return retval;\n}\n\nbool ImageManagerBinder::MapImageWithDeviceMapper(const IPartitionOpener& opener,\n                                                  const std::string& name, std::string* dev) {\n    (void)opener;\n    (void)name;\n    (void)dev;\n    LOG(ERROR) << \"MapImageWithDeviceMapper is not available over binder.\";\n    return false;\n}\n\nstd::vector<std::string> ImageManagerBinder::GetAllBackingImages() {\n    std::vector<std::string> retval;\n    auto status = manager_->getAllBackingImages(&retval);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n    }\n    return retval;\n}\n\nFiemapStatus ImageManagerBinder::ZeroFillNewImage(const std::string& name, uint64_t bytes) {\n    auto status = manager_->zeroFillNewImage(name, bytes);\n    return ToFiemapStatus(__PRETTY_FUNCTION__, status);\n}\n\nbool ImageManagerBinder::RemoveAllImages() {\n    auto status = manager_->removeAllImages();\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return true;\n}\nbool ImageManagerBinder::DisableAllImages() {\n    return true;\n}\n\nbool ImageManagerBinder::DisableImage(const std::string& name) {\n    auto status = manager_->disableImage(name);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return true;\n}\n\nbool ImageManagerBinder::RemoveDisabledImages() {\n    auto status = manager_->removeDisabledImages();\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return true;\n}\n\nbool ImageManagerBinder::GetMappedImageDevice(const std::string& name, std::string* device) {\n    auto status = manager_->getMappedImageDevice(name, device);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return !device->empty();\n}\n\nbool ImageManagerBinder::IsImageDisabled(const std::string& name) {\n    bool retval;\n    auto status = manager_->isImageDisabled(name, &retval);\n    if (!status.isOk()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__\n                   << \" binder returned: \" << status.exceptionMessage().c_str();\n        return false;\n    }\n    return retval;\n}\n\nbool ImageManagerBinder::MapAllImages(const std::function<bool(std::set<std::string>)>&) {\n    LOG(ERROR) << __PRETTY_FUNCTION__ << \" not available over binder\";\n    return false;\n}\n\nstd::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir,\n                                                   const std::chrono::milliseconds& /*timeout_ms*/,\n                                                   const DeviceInfo&) {\n    android::sp<IGsiService> service = android::gsi::GetGsiService();\n    android::sp<IImageService> manager;\n\n    auto status = service->openImageService(dir, &manager);\n    if (!status.isOk() || !manager) {\n        LOG(ERROR) << \"Could not acquire IImageManager: \" << status.exceptionMessage().c_str();\n        return nullptr;\n    }\n    return std::make_unique<ImageManagerBinder>(std::move(service), std::move(manager));\n}\n\n}  // namespace fiemap\n}  // namespace android\n\n#endif  // __ANDROID_RECOVERY__\n"
  },
  {
    "path": "fs_mgr/libfiemap/fiemap_status.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <libfiemap/fiemap_status.h>\n\nnamespace android::fiemap {\n\n// FiemapStatus -> string\nstd::string FiemapStatus::string() const {\n    if (error_code() == ErrorCode::ERROR) {\n        return \"Error\";\n    }\n    return strerror(-static_cast<int>(error_code()));\n}\n\n// -errno -> known ErrorCode\n// unknown ErrorCode -> known ErrorCode\nFiemapStatus::ErrorCode FiemapStatus::CastErrorCode(int error_code) {\n    switch (error_code) {\n        case static_cast<int32_t>(ErrorCode::SUCCESS):\n        case static_cast<int32_t>(ErrorCode::NO_SPACE):\n            return static_cast<ErrorCode>(error_code);\n        case static_cast<int32_t>(ErrorCode::ERROR):\n        default:\n            return ErrorCode::ERROR;\n    }\n}\n\n}  // namespace android::fiemap\n"
  },
  {
    "path": "fs_mgr/libfiemap/fiemap_writer.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <libfiemap/fiemap_writer.h>\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <stdio.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <sys/vfs.h>\n#include <unistd.h>\n\n#include <limits>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <libdm/dm.h>\n#include \"utility.h\"\n\nnamespace android {\nnamespace fiemap {\n\nusing namespace android::dm;\n\n// We cap the maximum number of extents as a robustness measure.\nstatic constexpr uint32_t kMaxExtents = 50000;\n\n// TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.\nstatic constexpr const uint32_t kUnsupportedExtentFlags =\n        FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |\n        FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |\n        FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED;\n\n// Large file support must be enabled.\nstatic_assert(sizeof(off_t) == sizeof(uint64_t));\n\nstatic inline void cleanup(const std::string& file_path, bool created) {\n    if (created) {\n        unlink(file_path.c_str());\n        sync();\n    }\n}\n\nstatic bool ValidateDmTarget(const DeviceMapper::TargetInfo& target) {\n    const auto& entry = target.spec;\n    if (entry.sector_start != 0) {\n        LOG(INFO) << \"Stopping at target with non-zero starting sector\";\n        return false;\n    }\n\n    auto target_type = DeviceMapper::GetTargetType(entry);\n    if (target_type == \"bow\" || target_type == \"default-key\" || target_type == \"crypt\") {\n        return true;\n    }\n    if (target_type == \"linear\") {\n        auto pieces = android::base::Split(target.data, \" \");\n        if (pieces[1] != \"0\") {\n            LOG(INFO) << \"Stopping at complex linear target with non-zero starting sector: \"\n                      << pieces[1];\n            return false;\n        }\n        return true;\n    }\n\n    LOG(INFO) << \"Stopping at complex target type \" << target_type;\n    return false;\n}\n\nstatic bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {\n    *bdev_raw = bdev;\n\n    if (!::android::base::StartsWith(bdev, \"dm-\")) {\n        // We are at the bottom of the device mapper stack.\n        return true;\n    }\n\n    // Get the device name.\n    auto dm_name_file = \"/sys/block/\" + bdev + \"/dm/name\";\n    std::string dm_name;\n    if (!android::base::ReadFileToString(dm_name_file, &dm_name)) {\n        PLOG(ERROR) << \"Could not read file: \" << dm_name_file;\n        return false;\n    }\n    dm_name = android::base::Trim(dm_name);\n\n    auto& dm = DeviceMapper::Instance();\n    std::vector<DeviceMapper::TargetInfo> table;\n    if (!dm.GetTableInfo(dm_name, &table)) {\n        LOG(ERROR) << \"Could not read device-mapper table for \" << dm_name << \" at \" << bdev;\n        return false;\n    }\n\n    // The purpose of libfiemap is to provide an extent-based view into\n    // a file. This is difficult if devices are not layered in a 1:1 manner;\n    // we would have to translate and break up extents based on the actual\n    // block mapping. Since this is too complex, we simply stop processing\n    // the device-mapper stack if we encounter a complex case.\n    //\n    // It is up to the caller to decide whether stopping at a virtual block\n    // device is allowable. In most cases it is not, because we want either\n    // \"userdata\" or an external volume. It is useful for tests however.\n    // Callers can check by comparing the device number to that of userdata,\n    // or by checking whether is a device-mapper node.\n    if (table.size() > 1) {\n        LOG(INFO) << \"Stopping at complex table for \" << dm_name << \" at \" << bdev;\n        return true;\n    }\n    if (!ValidateDmTarget(table[0])) {\n        return true;\n    }\n\n    auto dm_leaf_dir = \"/sys/block/\" + bdev + \"/slaves\";\n    auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);\n    if (d == nullptr) {\n        PLOG(ERROR) << \"Failed to open: \" << dm_leaf_dir;\n        return false;\n    }\n\n    struct dirent* de;\n    uint32_t num_leaves = 0;\n    std::string bdev_next = \"\";\n    while ((de = readdir(d.get())) != nullptr) {\n        if (!strcmp(de->d_name, \".\") || !strcmp(de->d_name, \"..\")) {\n            continue;\n        }\n\n        // We set the first name we find here\n        if (bdev_next.empty()) {\n            bdev_next = de->d_name;\n        }\n        num_leaves++;\n    }\n\n    // if we have more than one leaves, we return immediately. We can't continue to create the\n    // file since we don't know how to write it out using fiemap, so it will be readable via the\n    // underlying block devices later. The reader will also have to construct the same device mapper\n    // target in order read the file out.\n    if (num_leaves > 1) {\n        LOG(ERROR) << \"Found \" << num_leaves << \" leaf block devices under device mapper device \"\n                   << bdev;\n        return false;\n    }\n\n    // recursively call with the block device we found in order to pop the device mapper stack.\n    return DeviceMapperStackPop(bdev_next, bdev_raw);\n}\n\nbool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,\n                                         bool* uses_dm) {\n    struct stat sb;\n    if (stat(file_path.c_str(), &sb)) {\n        PLOG(ERROR) << \"Failed to get stat for: \" << file_path;\n        return false;\n    }\n\n    std::string bdev;\n    if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {\n        LOG(ERROR) << \"Failed to get block device name for \" << major(sb.st_dev) << \":\"\n                   << minor(sb.st_dev);\n        return false;\n    }\n\n    std::string bdev_raw;\n    if (!DeviceMapperStackPop(bdev, &bdev_raw)) {\n        LOG(ERROR) << \"Failed to get the bottom of the device mapper stack for device: \" << bdev;\n        return false;\n    }\n\n    if (uses_dm) {\n        *uses_dm = (bdev_raw != bdev);\n    }\n\n    LOG(DEBUG) << \"Popped device (\" << bdev_raw << \") from device mapper stack starting with (\"\n               << bdev << \")\";\n\n    *bdev_path = ::android::base::StringPrintf(\"/dev/block/%s\", bdev_raw.c_str());\n\n    // Make sure we are talking to a block device before calling it a success.\n    if (stat(bdev_path->c_str(), &sb)) {\n        PLOG(ERROR) << \"Failed to get stat for block device: \" << *bdev_path;\n        return false;\n    }\n\n    if ((sb.st_mode & S_IFMT) != S_IFBLK) {\n        PLOG(ERROR) << \"File: \" << *bdev_path << \" is not a block device\";\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {\n    uint64_t size_in_bytes = 0;\n    if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {\n        PLOG(ERROR) << \"Failed to get total size for: \" << bdev_path;\n        return false;\n    }\n\n    *bdev_size = size_in_bytes;\n\n    return true;\n}\n\nstatic uint64_t GetFileSize(const std::string& file_path) {\n    struct stat sb;\n    if (stat(file_path.c_str(), &sb)) {\n        PLOG(ERROR) << \"Failed to get size for file: \" << file_path;\n        return 0;\n    }\n\n    return sb.st_size;\n}\n\nstatic bool PerformFileChecks(const std::string& file_path, uint64_t* blocksz, uint32_t* fs_type) {\n    struct statfs64 sfs;\n    if (statfs64(file_path.c_str(), &sfs)) {\n        PLOG(ERROR) << \"Failed to read file system status at: \" << file_path;\n        return false;\n    }\n\n    if (!sfs.f_bsize) {\n        LOG(ERROR) << \"Unsupported block size: \" << sfs.f_bsize;\n        return false;\n    }\n\n    // Check if the filesystem is of supported types.\n    // Only ext4, f2fs, and vfat are tested and supported.\n    switch (sfs.f_type) {\n        case EXT4_SUPER_MAGIC:\n        case F2FS_SUPER_MAGIC:\n        case MSDOS_SUPER_MAGIC:\n            break;\n        default:\n            LOG(ERROR) << \"Unsupported file system type: 0x\" << std::hex << sfs.f_type;\n            return false;\n    }\n\n    *blocksz = sfs.f_bsize;\n    *fs_type = sfs.f_type;\n    return true;\n}\n\nstatic FiemapStatus FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,\n                                      const std::string& file_path,\n                                      const std::function<bool(uint64_t, uint64_t)>& on_progress) {\n    // Even though this is much faster than writing zeroes, it is still slow\n    // enough that we need to fire the progress callback periodically. To\n    // easily achieve this, we seek in chunks. We use 1000 chunks since\n    // normally we only fire the callback on 1/1000th increments.\n    uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);\n\n    // Seek just to the end of each chunk and write a single byte, causing\n    // the filesystem to allocate blocks.\n    off_t cursor = 0;\n    off_t end = static_cast<off_t>(file_size);\n    while (cursor < end) {\n        cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);\n        auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));\n        if (rv < 0) {\n            PLOG(ERROR) << \"Failed to lseek \" << file_path;\n            return FiemapStatus::FromErrno(errno);\n        }\n        if (rv != cursor - 1) {\n            LOG(ERROR) << \"Seek returned wrong offset \" << rv << \" for file \" << file_path;\n            return FiemapStatus::Error();\n        }\n        char buffer[] = {0};\n        if (!android::base::WriteFully(file_fd, buffer, 1)) {\n            PLOG(ERROR) << \"Write failed: \" << file_path;\n            return FiemapStatus::FromErrno(errno);\n        }\n        if (on_progress && !on_progress(cursor, file_size)) {\n            return FiemapStatus::Error();\n        }\n    }\n    return FiemapStatus::Ok();\n}\n\n// F2FS-specific ioctl\n// It requires the below kernel commit merged in v4.16-rc1.\n//   1ad71a27124c (\"f2fs: add an ioctl to disable GC for specific file\")\n// In android-4.4,\n//   56ee1e817908 (\"f2fs: updates on v4.16-rc1\")\n// In android-4.9,\n//   2f17e34672a8 (\"f2fs: updates on v4.16-rc1\")\n// In android-4.14,\n//   ce767d9a55bc (\"f2fs: updates on v4.16-rc1\")\n#ifndef F2FS_IOC_SET_PIN_FILE\n#ifndef F2FS_IOCTL_MAGIC\n#define F2FS_IOCTL_MAGIC 0xf5\n#endif\n#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)\n#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)\n#endif\n\nstatic bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {\n    if (fs_type != F2FS_SUPER_MAGIC) {\n        // No pinning necessary for ext4 or vfat. The blocks, once allocated,\n        // are expected to be fixed.\n        return true;\n    }\n\n    // f2fs: export FS_NOCOW_FL flag to user\n    uint32_t flags;\n    int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);\n    if (error < 0) {\n        if ((errno == ENOTTY) || (errno == ENOTSUP)) {\n            PLOG(ERROR) << \"Failed to get flags, not supported by kernel: \" << file_path;\n        } else {\n            PLOG(ERROR) << \"Failed to get flags: \" << file_path;\n        }\n        return false;\n    }\n    if (!(flags & FS_NOCOW_FL)) {\n        return false;\n    }\n\n    // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.\n    uint32_t moved_blocks_nr;\n    error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);\n    if (error < 0) {\n        if ((errno == ENOTTY) || (errno == ENOTSUP)) {\n            PLOG(ERROR) << \"Failed to get file pin status, not supported by kernel: \" << file_path;\n        } else {\n            PLOG(ERROR) << \"Failed to get file pin status: \" << file_path;\n        }\n        return false;\n    }\n\n    if (moved_blocks_nr) {\n        LOG(WARNING) << moved_blocks_nr << \" blocks moved in file \" << file_path;\n    }\n    return moved_blocks_nr == 0;\n}\n\nstatic bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {\n    if (IsFilePinned(file_fd, file_path, fs_type)) {\n        return true;\n    }\n    if (fs_type != F2FS_SUPER_MAGIC) {\n        // No pinning necessary for ext4/msdos. The blocks, once allocated, are\n        // expected to be fixed.\n        return true;\n    }\n\n    uint32_t pin_status = 1;\n    int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);\n    if (error < 0) {\n        if ((errno == ENOTTY) || (errno == ENOTSUP)) {\n            PLOG(ERROR) << \"Failed to pin file, not supported by kernel: \" << file_path;\n        } else {\n            PLOG(ERROR) << \"Failed to pin file: \" << file_path;\n        }\n        return false;\n    }\n\n    return true;\n}\n\n// write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data\n// blocks are actually written to by the file system and thus getting rid of the holes in the\n// file.\nstatic FiemapStatus WriteZeroes(int file_fd, const std::string& file_path, size_t blocksz,\n                                uint64_t file_size,\n                                const std::function<bool(uint64_t, uint64_t)>& on_progress) {\n    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);\n    if (buffer == nullptr) {\n        LOG(ERROR) << \"failed to allocate memory for writing file\";\n        return FiemapStatus::Error();\n    }\n\n    off64_t offset = lseek64(file_fd, 0, SEEK_SET);\n    if (offset < 0) {\n        PLOG(ERROR) << \"Failed to seek at the beginning of : \" << file_path;\n        return FiemapStatus::FromErrno(errno);\n    }\n\n    int permille = -1;\n    while (offset < file_size) {\n        if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {\n            PLOG(ERROR) << \"Failed to write\" << blocksz << \" bytes at offset\" << offset\n                        << \" in file \" << file_path;\n            return FiemapStatus::FromErrno(errno);\n        }\n\n        offset += blocksz;\n\n        // Don't invoke the callback every iteration - wait until a significant\n        // chunk (here, 1/1000th) of the data has been processed.\n        int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;\n        if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {\n            if (on_progress && !on_progress(offset, file_size)) {\n                return FiemapStatus::Error();\n            }\n            permille = new_permille;\n        }\n    }\n\n    if (lseek64(file_fd, 0, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"Failed to reset offset at the beginning of : \" << file_path;\n        return FiemapStatus::FromErrno(errno);\n    }\n    return FiemapStatus::Ok();\n}\n\n// Reserve space for the file on the file system and write it out to make sure the extents\n// don't come back unwritten. Return from this function with the kernel file offset set to 0.\n// If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks\n// aren't moved around.\nstatic FiemapStatus AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,\n                                 uint64_t file_size, unsigned int fs_type,\n                                 std::function<bool(uint64_t, uint64_t)> on_progress) {\n    bool need_explicit_writes = true;\n    switch (fs_type) {\n        case EXT4_SUPER_MAGIC:\n            break;\n        case F2FS_SUPER_MAGIC: {\n            bool supported;\n            if (!F2fsPinBeforeAllocate(file_fd, &supported)) {\n                return FiemapStatus::Error();\n            }\n            if (supported) {\n                if (!PinFile(file_fd, file_path, fs_type)) {\n                    return FiemapStatus::Error();\n                }\n                need_explicit_writes = false;\n            }\n            break;\n        }\n        case MSDOS_SUPER_MAGIC:\n            // fallocate() is not supported, and not needed, since VFAT does not support holes.\n            // Instead we can perform a much faster allocation.\n            return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);\n        default:\n            LOG(ERROR) << \"Missing fallocate() support for file system \" << fs_type;\n            return FiemapStatus::Error();\n    }\n\n    // F2FS can return EAGAIN and partially fallocate. Keep trying to fallocate,\n    // and if we don't make forward progress, return ENOSPC.\n    std::optional<off_t> prev_size;\n    while (true) {\n        if (fallocate(file_fd, 0, 0, file_size) == 0) {\n            break;\n        }\n        if (errno != EAGAIN) {\n            PLOG(ERROR) << \"Failed to allocate space for file: \" << file_path\n                        << \" size: \" << file_size;\n            return FiemapStatus::FromErrno(errno);\n        }\n\n        struct stat s;\n        if (fstat(file_fd, &s) < 0) {\n            PLOG(ERROR) << \"Failed to fstat after fallocate failure: \" << file_path;\n            return FiemapStatus::FromErrno(errno);\n        }\n        if (!prev_size) {\n            prev_size = {s.st_size};\n            continue;\n        }\n        if (*prev_size >= s.st_size) {\n            LOG(ERROR) << \"Fallocate retry failed, got \" << s.st_size << \", asked for \"\n                       << file_size;\n            return FiemapStatus(FiemapStatus::ErrorCode::NO_SPACE);\n        }\n        LOG(INFO) << \"Retrying fallocate, got \" << s.st_size << \", asked for \" << file_size;\n    }\n\n    if (need_explicit_writes) {\n        auto status = WriteZeroes(file_fd, file_path, blocksz, file_size, on_progress);\n        if (!status.is_ok()) {\n            return status;\n        }\n    }\n\n    // flush all writes here ..\n    if (fsync(file_fd)) {\n        PLOG(ERROR) << \"Failed to synchronize written file:\" << file_path;\n        return FiemapStatus::FromErrno(errno);\n    }\n\n    // Send one last progress notification.\n    if (on_progress && !on_progress(file_size, file_size)) {\n        return FiemapStatus::Error();\n    }\n    return FiemapStatus::Ok();\n}\n\nbool FiemapWriter::HasPinnedExtents(const std::string& file_path) {\n    android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));\n    if (fd < 0) {\n        PLOG(ERROR) << \"open: \" << file_path;\n        return false;\n    }\n\n    struct statfs64 sfs;\n    if (fstatfs64(fd, &sfs)) {\n        PLOG(ERROR) << \"fstatfs64: \" << file_path;\n        return false;\n    }\n    return IsFilePinned(fd, file_path, sfs.f_type);\n}\n\nstatic bool IsValidExtent(const fiemap_extent* extent, std::string_view file_path) {\n    if (extent->fe_flags & kUnsupportedExtentFlags) {\n        LOG(ERROR) << \"Extent at location \" << extent->fe_logical << \" of file \" << file_path\n                   << \" has unsupported flags\";\n        return false;\n    }\n    return true;\n}\n\nstatic bool IsLastExtent(const fiemap_extent* extent) {\n    return !!(extent->fe_flags & FIEMAP_EXTENT_LAST);\n}\n\nstatic bool FiemapToExtents(struct fiemap* fiemap, std::vector<struct fiemap_extent>* extents,\n                            std::string_view file_path) {\n    uint32_t num_extents = fiemap->fm_mapped_extents;\n    if (num_extents == 0) {\n        LOG(ERROR) << \"File \" << file_path << \" has zero extent\";\n        return false;\n    }\n    const struct fiemap_extent* last_extent = &fiemap->fm_extents[num_extents - 1];\n    if (!IsLastExtent(last_extent)) {\n        LOG(ERROR) << \"FIEMAP did not return a final extent for file: \" << file_path\n                   << \" num_extents=\" << num_extents << \" max_extents=\" << kMaxExtents;\n        return false;\n    }\n\n    // Iterate through each extent, read and make sure its valid before adding it to the vector\n    // merging contiguous extents.\n    fiemap_extent* prev = &fiemap->fm_extents[0];\n    if (!IsValidExtent(prev, file_path)) return false;\n\n    for (uint32_t i = 1; i < num_extents; i++) {\n        fiemap_extent* next = &fiemap->fm_extents[i];\n\n        // Make sure extents are returned in order\n        if (next != last_extent && IsLastExtent(next)) {\n            LOG(ERROR) << \"Extents are being received out-of-order\";\n            return false;\n        }\n\n        // Check if extent's flags are valid\n        if (!IsValidExtent(next, file_path)) return false;\n\n        // Check if the current extent is contiguous with the previous one.\n        // An extent can be combined with its predecessor only if:\n        //  1. There is no physical space between the previous and the current\n        //  extent, and\n        //  2. The physical distance between the previous and current extent\n        //  corresponds to their logical distance (contiguous mapping).\n        if (prev->fe_physical + prev->fe_length == next->fe_physical &&\n            next->fe_physical - prev->fe_physical == next->fe_logical - prev->fe_logical) {\n            prev->fe_length += next->fe_length;\n        } else {\n            extents->emplace_back(*prev);\n            prev = next;\n        }\n    }\n    extents->emplace_back(*prev);\n\n    return true;\n}\n\nstatic bool ReadFiemap(int file_fd, const std::string& file_path,\n                       std::vector<struct fiemap_extent>* extents) {\n    uint64_t fiemap_size = sizeof(struct fiemap) + kMaxExtents * sizeof(struct fiemap_extent);\n    auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);\n    if (buffer == nullptr) {\n        LOG(ERROR) << \"Failed to allocate memory for fiemap\";\n        return false;\n    }\n\n    struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());\n    fiemap->fm_start = 0;\n    fiemap->fm_length = UINT64_MAX;\n    // make sure file is synced to disk before we read the fiemap\n    fiemap->fm_flags = FIEMAP_FLAG_SYNC;\n    fiemap->fm_extent_count = kMaxExtents;\n\n    if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {\n        PLOG(ERROR) << \"Failed to get FIEMAP from the kernel for file: \" << file_path;\n        return false;\n    }\n    return FiemapToExtents(fiemap, extents, file_path);\n}\n\nstatic bool ReadFibmap(int file_fd, const std::string& file_path,\n                       std::vector<struct fiemap_extent>* extents) {\n    struct stat s;\n    if (fstat(file_fd, &s)) {\n        PLOG(ERROR) << \"Failed to stat \" << file_path;\n        return false;\n    }\n\n    unsigned int blksize;\n    if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {\n        PLOG(ERROR) << \"Failed to get FIGETBSZ for \" << file_path;\n        return false;\n    }\n    if (!blksize) {\n        LOG(ERROR) << \"Invalid filesystem block size: \" << blksize;\n        return false;\n    }\n\n    uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;\n    if (num_blocks > std::numeric_limits<uint32_t>::max()) {\n        LOG(ERROR) << \"Too many blocks for FIBMAP (\" << num_blocks << \")\";\n        return false;\n    }\n\n    for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {\n        uint32_t block = block_number;\n        if (ioctl(file_fd, FIBMAP, &block)) {\n            PLOG(ERROR) << \"Failed to get FIBMAP for file \" << file_path;\n            return false;\n        }\n        if (!block) {\n            LOG(ERROR) << \"Logical block \" << block_number << \" is a hole, which is not supported\";\n            return false;\n        }\n\n        if (!extents->empty() && block == last_block + 1) {\n            extents->back().fe_length += blksize;\n        } else {\n            extents->push_back(fiemap_extent{.fe_logical = block_number,\n                                             .fe_physical = static_cast<uint64_t>(block) * blksize,\n                                             .fe_length = static_cast<uint64_t>(blksize),\n                                             .fe_flags = 0});\n            if (extents->size() > kMaxExtents) {\n                LOG(ERROR) << \"File has more than \" << kMaxExtents << \"extents: \" << file_path;\n                return false;\n            }\n        }\n        last_block = block;\n    }\n    return true;\n}\n\nFiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,\n                                   std::function<bool(uint64_t, uint64_t)> progress) {\n    FiemapUniquePtr ret;\n    if (!Open(file_path, file_size, &ret, create, progress).is_ok()) {\n        return nullptr;\n    }\n    return ret;\n}\n\nFiemapStatus FiemapWriter::Open(const std::string& file_path, uint64_t file_size,\n                                FiemapUniquePtr* out, bool create,\n                                std::function<bool(uint64_t, uint64_t)> progress) {\n    out->reset();\n\n    // if 'create' is false, open an existing file and do not truncate.\n    int open_flags = O_RDWR | O_CLOEXEC;\n    if (create) {\n        if (access(file_path.c_str(), F_OK) == 0) {\n            LOG(WARNING) << \"File \" << file_path << \" already exists, truncating\";\n        }\n        open_flags |= O_CREAT | O_TRUNC;\n    }\n    ::android::base::unique_fd file_fd(\n            TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));\n    if (file_fd < 0) {\n        PLOG(ERROR) << \"Failed to create file at: \" << file_path;\n        return FiemapStatus::FromErrno(errno);\n    }\n\n    std::string abs_path;\n    if (!::android::base::Realpath(file_path, &abs_path)) {\n        int saved_errno = errno;\n        PLOG(ERROR) << \"Invalid file path: \" << file_path;\n        cleanup(file_path, create);\n        return FiemapStatus::FromErrno(saved_errno);\n    }\n\n    std::string bdev_path;\n    if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {\n        LOG(ERROR) << \"Failed to get block dev path for file: \" << file_path;\n        cleanup(abs_path, create);\n        return FiemapStatus::Error();\n    }\n\n    ::android::base::unique_fd bdev_fd(\n            TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (bdev_fd < 0) {\n        int saved_errno = errno;\n        PLOG(ERROR) << \"Failed to open block device: \" << bdev_path;\n        cleanup(file_path, create);\n        return FiemapStatus::FromErrno(saved_errno);\n    }\n\n    uint64_t bdevsz;\n    if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {\n        int saved_errno = errno;\n        LOG(ERROR) << \"Failed to get block device size for : \" << bdev_path;\n        cleanup(file_path, create);\n        return FiemapStatus::FromErrno(saved_errno);\n    }\n\n    if (!create) {\n        file_size = GetFileSize(abs_path);\n        if (file_size == 0) {\n            LOG(ERROR) << \"Invalid file size of zero bytes for file: \" << abs_path;\n            return FiemapStatus::FromErrno(errno);\n        }\n    }\n\n    uint64_t blocksz;\n    uint32_t fs_type;\n    if (!PerformFileChecks(abs_path, &blocksz, &fs_type)) {\n        LOG(ERROR) << \"Failed to validate file or file system for file:\" << abs_path;\n        cleanup(abs_path, create);\n        return FiemapStatus::Error();\n    }\n\n    // Align up to the nearest block size.\n    if (file_size % blocksz) {\n        file_size += blocksz - (file_size % blocksz);\n    }\n\n    if (create) {\n        auto status =\n                AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress));\n        if (!status.is_ok()) {\n            LOG(ERROR) << \"Failed to allocate file: \" << abs_path << \" of size: \" << file_size\n                       << \" bytes\";\n            cleanup(abs_path, create);\n            return status;\n        }\n    }\n\n    // f2fs may move the file blocks around.\n    if (!PinFile(file_fd, abs_path, fs_type)) {\n        cleanup(abs_path, create);\n        LOG(ERROR) << \"Failed to pin the file in storage\";\n        return FiemapStatus::Error();\n    }\n\n    // now allocate the FiemapWriter and start setting it up\n    FiemapUniquePtr fmap(new FiemapWriter());\n    switch (fs_type) {\n        case EXT4_SUPER_MAGIC:\n        case F2FS_SUPER_MAGIC:\n            if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {\n                LOG(ERROR) << \"Failed to read fiemap of file: \" << abs_path;\n                cleanup(abs_path, create);\n                return FiemapStatus::Error();\n            }\n            break;\n        case MSDOS_SUPER_MAGIC:\n            if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {\n                LOG(ERROR) << \"Failed to read fibmap of file: \" << abs_path;\n                cleanup(abs_path, create);\n                return FiemapStatus::Error();\n            }\n            break;\n    }\n\n    fmap->file_path_ = abs_path;\n    fmap->bdev_path_ = bdev_path;\n    fmap->file_size_ = file_size;\n    fmap->bdev_size_ = bdevsz;\n    fmap->fs_type_ = fs_type;\n    fmap->block_size_ = blocksz;\n\n    LOG(VERBOSE) << \"Successfully created FiemapWriter for file \" << abs_path << \" on block device \"\n                 << bdev_path;\n    *out = std::move(fmap);\n    return FiemapStatus::Ok();\n}\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/fiemap_writer_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fcntl.h>\n#include <inttypes.h>\n#include <linux/limits.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <sys/types.h>\n#include <sys/vfs.h>\n#include <unistd.h>\n\n#include <cstring>\n#include <string>\n#include <utility>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n#include <fstab/fstab.h>\n#include <gtest/gtest.h>\n#include <libdm/loop_control.h>\n#include <libfiemap/fiemap_writer.h>\n#include <libfiemap/split_fiemap_writer.h>\n#include <libgsi/libgsi.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace fiemap {\n\nusing namespace std;\nusing namespace std::string_literals;\nusing namespace android::fiemap;\nusing namespace android::storage_literals;\nusing unique_fd = android::base::unique_fd;\nusing LoopDevice = android::dm::LoopDevice;\n\nstd::string gTestDir;\nuint64_t testfile_size = 536870912;  // default of 512MiB\nsize_t gBlockSize = 0;\n\nclass FiemapWriterTest : public ::testing::Test {\n  protected:\n    void SetUp() override {\n        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();\n        testfile = gTestDir + \"/\"s + tinfo->name();\n    }\n\n    void TearDown() override {\n        truncate(testfile.c_str(), 0);\n        unlink(testfile.c_str());\n        sync();\n    }\n\n    // name of the file we use for testing\n    std::string testfile;\n};\n\nclass SplitFiemapTest : public ::testing::Test {\n  protected:\n    void SetUp() override {\n        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();\n        testfile = gTestDir + \"/\"s + tinfo->name();\n    }\n\n    void TearDown() override {\n        std::string message;\n        if (!SplitFiemap::RemoveSplitFiles(testfile, &message)) {\n            cerr << \"Could not remove all split files: \" << message;\n        }\n    }\n\n    // name of the file we use for testing\n    std::string testfile;\n};\n\nTEST_F(FiemapWriterTest, CreateImpossiblyLargeFile) {\n    // Try creating a file of size ~100TB but aligned to\n    // 512 byte to make sure block alignment tests don't\n    // fail.\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1099511627997184);\n    EXPECT_EQ(fptr, nullptr);\n    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);\n    EXPECT_EQ(errno, ENOENT);\n}\n\nTEST_F(FiemapWriterTest, CreateUnalignedFile) {\n    // Try creating a file of size 4097 bytes which is guaranteed\n    // to be unaligned to all known block sizes.\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize + 1);\n    ASSERT_NE(fptr, nullptr);\n    ASSERT_EQ(fptr->size(), gBlockSize * 2);\n}\n\nTEST_F(FiemapWriterTest, CheckFilePath) {\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);\n    ASSERT_NE(fptr, nullptr);\n    EXPECT_EQ(fptr->size(), gBlockSize);\n    EXPECT_EQ(fptr->file_path(), testfile);\n    EXPECT_EQ(access(testfile.c_str(), F_OK), 0);\n}\n\nTEST_F(FiemapWriterTest, CheckFileSize) {\n    // Create a large-ish file and test that the expected size matches.\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 1024 * 1024 * 16);\n    ASSERT_NE(fptr, nullptr);\n\n    struct stat s;\n    ASSERT_EQ(stat(testfile.c_str(), &s), 0);\n    EXPECT_EQ(static_cast<uint64_t>(s.st_size), fptr->size());\n}\n\nTEST_F(FiemapWriterTest, CheckProgress) {\n    std::vector<uint64_t> expected;\n    size_t invocations = 0;\n    auto callback = [&](uint64_t done, uint64_t total) -> bool {\n        if (invocations >= expected.size()) {\n            return false;\n        }\n        EXPECT_EQ(done, expected[invocations]);\n        EXPECT_EQ(total, gBlockSize);\n        invocations++;\n        return true;\n    };\n\n    expected.push_back(gBlockSize);\n\n    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));\n    EXPECT_NE(ptr, nullptr);\n    EXPECT_EQ(invocations, expected.size());\n}\n\nTEST_F(FiemapWriterTest, CheckPinning) {\n    auto ptr = FiemapWriter::Open(testfile, 4096);\n    ASSERT_NE(ptr, nullptr);\n    EXPECT_TRUE(FiemapWriter::HasPinnedExtents(testfile));\n}\n\nTEST_F(FiemapWriterTest, CheckBlockDevicePath) {\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);\n    EXPECT_EQ(fptr->size(), gBlockSize);\n    EXPECT_EQ(fptr->bdev_path().find(\"/dev/block/\"), size_t(0));\n\n    if (!android::gsi::IsGsiRunning()) {\n        EXPECT_EQ(fptr->bdev_path().find(\"/dev/block/dm-\"), string::npos);\n    }\n}\n\nTEST_F(FiemapWriterTest, CheckFileCreated) {\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 32768);\n    ASSERT_NE(fptr, nullptr);\n    unique_fd fd(open(testfile.c_str(), O_RDONLY));\n    EXPECT_GT(fd, -1);\n}\n\nTEST_F(FiemapWriterTest, CheckFileSizeActual) {\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);\n    ASSERT_NE(fptr, nullptr);\n\n    struct stat sb;\n    ASSERT_EQ(stat(testfile.c_str(), &sb), 0);\n    EXPECT_GE(sb.st_size, testfile_size);\n}\n\nTEST_F(FiemapWriterTest, CheckFileExtents) {\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, testfile_size);\n    ASSERT_NE(fptr, nullptr);\n    EXPECT_GT(fptr->extents().size(), 0);\n}\n\nTEST_F(FiemapWriterTest, ExistingFile) {\n    // Create the file.\n    { ASSERT_NE(FiemapWriter::Open(testfile, gBlockSize), nullptr); }\n    // Test that we can still open it.\n    {\n        auto ptr = FiemapWriter::Open(testfile, 0, false);\n        ASSERT_NE(ptr, nullptr);\n        EXPECT_GT(ptr->extents().size(), 0);\n    }\n}\n\nTEST_F(FiemapWriterTest, FileDeletedOnError) {\n    auto callback = [](uint64_t, uint64_t) -> bool { return false; };\n    auto ptr = FiemapWriter::Open(testfile, gBlockSize, true, std::move(callback));\n    EXPECT_EQ(ptr, nullptr);\n    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);\n    EXPECT_EQ(errno, ENOENT);\n}\n\nTEST_F(FiemapWriterTest, MaxBlockSize) {\n    uint64_t max_piece_size = 0;\n    ASSERT_TRUE(DetermineMaximumFileSize(testfile, &max_piece_size));\n    ASSERT_GT(max_piece_size, 0);\n}\n\nTEST_F(FiemapWriterTest, FibmapBlockAddressing) {\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, gBlockSize);\n    ASSERT_NE(fptr, nullptr);\n\n    switch (fptr->fs_type()) {\n        case F2FS_SUPER_MAGIC:\n        case EXT4_SUPER_MAGIC:\n            // Skip the test for FIEMAP supported filesystems. This is really\n            // because f2fs/ext4 have caches that seem to defeat reading back\n            // directly from the block device, and writing directly is too\n            // dangerous.\n            std::cout << \"Skipping test, filesystem does not use FIBMAP\\n\";\n            return;\n    }\n\n    bool uses_dm;\n    std::string bdev_path;\n    ASSERT_TRUE(FiemapWriter::GetBlockDeviceForFile(testfile, &bdev_path, &uses_dm));\n\n    if (uses_dm) {\n        // We could use a device-mapper wrapper here to bypass encryption, but\n        // really this test is for FIBMAP correctness on VFAT (where encryption\n        // is never used), so we don't bother.\n        std::cout << \"Skipping test, block device is metadata encrypted\\n\";\n        return;\n    }\n\n    std::string data(fptr->size(), '\\0');\n    for (size_t i = 0; i < data.size(); i++) {\n        data[i] = 'A' + static_cast<char>(data.size() % 26);\n    }\n\n    {\n        unique_fd fd(open(testfile.c_str(), O_WRONLY | O_CLOEXEC));\n        ASSERT_GE(fd, 0);\n        ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));\n        ASSERT_EQ(fsync(fd), 0);\n    }\n\n    ASSERT_FALSE(fptr->extents().empty());\n    const auto& first_extent = fptr->extents()[0];\n\n    unique_fd bdev(open(fptr->bdev_path().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_GE(bdev, 0);\n\n    off_t where = first_extent.fe_physical;\n    ASSERT_EQ(lseek(bdev, where, SEEK_SET), where);\n\n    // Note: this will fail on encrypted folders.\n    std::string actual(data.size(), '\\0');\n    ASSERT_GE(first_extent.fe_length, data.size());\n    ASSERT_TRUE(android::base::ReadFully(bdev, actual.data(), actual.size()));\n    EXPECT_EQ(memcmp(actual.data(), data.data(), data.size()), 0);\n}\n\nTEST_F(FiemapWriterTest, CheckEmptyFile) {\n    // Can't get any fiemap_extent out of a zero-sized file.\n    FiemapUniquePtr fptr = FiemapWriter::Open(testfile, 0);\n    EXPECT_EQ(fptr, nullptr);\n    EXPECT_EQ(access(testfile.c_str(), F_OK), -1);\n}\n\nTEST_F(SplitFiemapTest, Create) {\n    auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);\n    ASSERT_NE(ptr, nullptr);\n\n    auto extents = ptr->extents();\n\n    // Destroy the fiemap, closing file handles. This should not delete them.\n    ptr = nullptr;\n\n    std::vector<std::string> files;\n    ASSERT_TRUE(SplitFiemap::GetSplitFileList(testfile, &files));\n    for (const auto& path : files) {\n        EXPECT_EQ(access(path.c_str(), F_OK), 0);\n    }\n\n    ASSERT_GE(extents.size(), files.size());\n}\n\nTEST_F(SplitFiemapTest, Open) {\n    {\n        auto ptr = SplitFiemap::Create(testfile, 1024 * 768, 1024 * 32);\n        ASSERT_NE(ptr, nullptr);\n    }\n\n    auto ptr = SplitFiemap::Open(testfile);\n    ASSERT_NE(ptr, nullptr);\n\n    auto extents = ptr->extents();\n    ASSERT_GE(extents.size(), 24);\n}\n\nTEST_F(SplitFiemapTest, DeleteOnFail) {\n    auto ptr = SplitFiemap::Create(testfile, 1024 * 1024 * 100, 1);\n    ASSERT_EQ(ptr, nullptr);\n\n    std::string first_file = testfile + \".0001\";\n    ASSERT_NE(access(first_file.c_str(), F_OK), 0);\n    ASSERT_EQ(errno, ENOENT);\n    ASSERT_NE(access(testfile.c_str(), F_OK), 0);\n    ASSERT_EQ(errno, ENOENT);\n}\n\nTEST_F(SplitFiemapTest, CorruptSplit) {\n    unique_fd fd(open(testfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0700));\n    ASSERT_GE(fd, 0);\n\n    // Make a giant random string.\n    std::vector<char> data;\n    for (size_t i = 0x1; i < 0x7f; i++) {\n        for (size_t j = 0; j < 100; j++) {\n            data.emplace_back(i);\n        }\n    }\n    ASSERT_GT(data.size(), PATH_MAX);\n\n    data.emplace_back('\\n');\n\n    ASSERT_TRUE(android::base::WriteFully(fd, data.data(), data.size()));\n    fd = {};\n\n    ASSERT_TRUE(SplitFiemap::RemoveSplitFiles(testfile));\n}\n\nstatic string ReadSplitFiles(const std::string& base_path, size_t num_files) {\n    std::string result;\n    for (int i = 0; i < num_files; i++) {\n        std::string path = base_path + android::base::StringPrintf(\".%04d\", i);\n        std::string data;\n        if (!android::base::ReadFileToString(path, &data)) {\n            return {};\n        }\n        result += data;\n    }\n    return result;\n}\n\nTEST_F(SplitFiemapTest, WriteWholeFile) {\n    static constexpr size_t kChunkSize = 32768;\n    static constexpr size_t kSize = kChunkSize * 3;\n    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);\n    ASSERT_NE(ptr, nullptr);\n\n    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));\n    for (size_t i = 0; i < kSize / sizeof(int); i++) {\n        buffer[i] = i;\n    }\n    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));\n\n    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);\n    auto actual = ReadSplitFiles(testfile, 3);\n    ASSERT_EQ(expected.size(), actual.size());\n    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);\n}\n\nTEST_F(SplitFiemapTest, WriteFileInChunks1) {\n    static constexpr size_t kChunkSize = 32768;\n    static constexpr size_t kSize = kChunkSize * 3;\n    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);\n    ASSERT_NE(ptr, nullptr);\n\n    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));\n    for (size_t i = 0; i < kSize / sizeof(int); i++) {\n        buffer[i] = i;\n    }\n\n    // Write in chunks of 1000 (so some writes straddle the boundary of two\n    // files).\n    size_t bytes_written = 0;\n    while (bytes_written < kSize) {\n        size_t to_write = std::min(kSize - bytes_written, (size_t)1000);\n        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;\n        ASSERT_TRUE(ptr->Write(data, to_write));\n        bytes_written += to_write;\n    }\n\n    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);\n    auto actual = ReadSplitFiles(testfile, 3);\n    ASSERT_EQ(expected.size(), actual.size());\n    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);\n}\n\nTEST_F(SplitFiemapTest, WriteFileInChunks2) {\n    static constexpr size_t kChunkSize = 32768;\n    static constexpr size_t kSize = kChunkSize * 3;\n    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);\n    ASSERT_NE(ptr, nullptr);\n\n    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));\n    for (size_t i = 0; i < kSize / sizeof(int); i++) {\n        buffer[i] = i;\n    }\n\n    // Write in chunks of 32KiB so every write is exactly at the end of the\n    // current file.\n    size_t bytes_written = 0;\n    while (bytes_written < kSize) {\n        size_t to_write = std::min(kSize - bytes_written, kChunkSize);\n        char* data = reinterpret_cast<char*>(buffer.get()) + bytes_written;\n        ASSERT_TRUE(ptr->Write(data, to_write));\n        bytes_written += to_write;\n    }\n\n    std::string expected(reinterpret_cast<char*>(buffer.get()), kSize);\n    auto actual = ReadSplitFiles(testfile, 3);\n    ASSERT_EQ(expected.size(), actual.size());\n    EXPECT_EQ(memcmp(expected.data(), actual.data(), actual.size()), 0);\n}\n\nTEST_F(SplitFiemapTest, WritePastEnd) {\n    static constexpr size_t kChunkSize = 32768;\n    static constexpr size_t kSize = kChunkSize * 3;\n    auto ptr = SplitFiemap::Create(testfile, kSize, kChunkSize);\n    ASSERT_NE(ptr, nullptr);\n\n    auto buffer = std::make_unique<int[]>(kSize / sizeof(int));\n    for (size_t i = 0; i < kSize / sizeof(int); i++) {\n        buffer[i] = i;\n    }\n    ASSERT_TRUE(ptr->Write(buffer.get(), kSize));\n    ASSERT_FALSE(ptr->Write(buffer.get(), kSize));\n}\n\n// Get max file size and free space.\nstd::pair<uint64_t, uint64_t> GetBigFileLimit(const std::string& mount_point) {\n    struct statvfs fs;\n    if (statvfs(mount_point.c_str(), &fs) < 0) {\n        PLOG(ERROR) << \"statfs failed\";\n        return {0, 0};\n    }\n\n    auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);\n    auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;\n\n    LOG(INFO) << \"Big file limit: \" << fs_limit << \", free space: \" << fs_free;\n\n    return {fs_limit, fs_free};\n}\n\nclass FsTest : public ::testing::Test {\n  protected:\n    // 2GB Filesystem and 4k block size by default\n    static constexpr uint64_t block_size = 4096;\n    static constexpr uint64_t fs_size = 64 * 1024 * 1024;\n\n    void SetUp() {\n        android::fs_mgr::Fstab fstab;\n        ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &fstab));\n\n        ASSERT_EQ(access(tmpdir_.path, F_OK), 0);\n        fs_path_ = tmpdir_.path + \"/fs_image\"s;\n        mntpoint_ = tmpdir_.path + \"/mnt_point\"s;\n\n        auto entry = android::fs_mgr::GetEntryForMountPoint(&fstab, \"/data\");\n        ASSERT_NE(entry, nullptr);\n        if (entry->fs_type == \"ext4\") {\n            SetUpExt4();\n        } else if (entry->fs_type == \"f2fs\") {\n            SetUpF2fs();\n        } else {\n            FAIL() << \"Unrecognized fs_type: \" << entry->fs_type;\n        }\n    }\n\n    void SetUpExt4() {\n        uint64_t count = fs_size / block_size;\n        std::string dd_cmd =\n                ::android::base::StringPrintf(\"/system/bin/dd if=/dev/zero of=%s bs=%\" PRIu64\n                                              \" count=%\" PRIu64 \" > /dev/null 2>&1\",\n                                              fs_path_.c_str(), block_size, count);\n        std::string mkfs_cmd =\n                ::android::base::StringPrintf(\"/system/bin/mkfs.ext4 -q %s\", fs_path_.c_str());\n        // create mount point\n        ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);\n        // create file for the file system\n        int ret = system(dd_cmd.c_str());\n        ASSERT_EQ(ret, 0);\n        // Get and attach a loop device to the filesystem we created\n        LoopDevice loop_dev(fs_path_, 10s);\n        ASSERT_TRUE(loop_dev.valid());\n        // create file system\n        ret = system(mkfs_cmd.c_str());\n        ASSERT_EQ(ret, 0);\n\n        // mount the file system\n        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), \"ext4\", 0, nullptr), 0);\n    }\n\n    void SetUpF2fs() {\n        uint64_t count = fs_size / block_size;\n        std::string dd_cmd =\n                ::android::base::StringPrintf(\"/system/bin/dd if=/dev/zero of=%s bs=%\" PRIu64\n                                              \" count=%\" PRIu64 \" > /dev/null 2>&1\",\n                                              fs_path_.c_str(), block_size, count);\n        std::string mkfs_cmd =\n                ::android::base::StringPrintf(\"/system/bin/make_f2fs -q %s\", fs_path_.c_str());\n        // create mount point\n        ASSERT_EQ(mkdir(mntpoint_.c_str(), S_IRWXU), 0);\n        // create file for the file system\n        int ret = system(dd_cmd.c_str());\n        ASSERT_EQ(ret, 0);\n        // Get and attach a loop device to the filesystem we created\n        LoopDevice loop_dev(fs_path_, 10s);\n        ASSERT_TRUE(loop_dev.valid());\n        // create file system\n        ret = system(mkfs_cmd.c_str());\n        ASSERT_EQ(ret, 0);\n\n        // mount the file system\n        ASSERT_EQ(mount(loop_dev.device().c_str(), mntpoint_.c_str(), \"f2fs\", 0, nullptr), 0)\n                << strerror(errno);\n    }\n\n    void TearDown() override {\n        umount(mntpoint_.c_str());\n        rmdir(mntpoint_.c_str());\n        unlink(fs_path_.c_str());\n    }\n\n    TemporaryDir tmpdir_;\n    std::string mntpoint_;\n    std::string fs_path_;\n};\n\nTEST_F(FsTest, LowSpaceError) {\n    auto limits = GetBigFileLimit(mntpoint_);\n    ASSERT_GE(limits.first, 0);\n\n    FiemapUniquePtr ptr;\n\n    auto test_file = mntpoint_ + \"/big_file\";\n    auto status = FiemapWriter::Open(test_file, limits.first, &ptr);\n    ASSERT_FALSE(status.is_ok());\n    ASSERT_EQ(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);\n\n    // Also test for EFBIG.\n    status = FiemapWriter::Open(test_file, 16_TiB, &ptr);\n    ASSERT_FALSE(status.is_ok());\n    ASSERT_NE(status.error_code(), FiemapStatus::ErrorCode::NO_SPACE);\n}\n\nbool DetermineBlockSize() {\n    struct statfs s;\n    if (statfs(gTestDir.c_str(), &s)) {\n        std::cerr << \"Could not call statfs: \" << strerror(errno) << \"\\n\";\n        return false;\n    }\n    if (!s.f_bsize) {\n        std::cerr << \"Invalid block size: \" << s.f_bsize << \"\\n\";\n        return false;\n    }\n\n    gBlockSize = s.f_bsize;\n    return true;\n}\n\n}  // namespace fiemap\n}  // namespace android\n\nusing namespace android::fiemap;\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    if (argc > 1 && argv[1] == \"-h\"s) {\n        cerr << \"Usage: [test_dir] [file_size]\\n\";\n        cerr << \"\\n\";\n        cerr << \"Note: test_dir must be a writable, unencrypted directory.\\n\";\n        exit(EXIT_FAILURE);\n    }\n    ::android::base::InitLogging(argv, ::android::base::StderrLogger);\n\n    std::string root_dir = \"/data/local/unencrypted\";\n    if (access(root_dir.c_str(), F_OK)) {\n        root_dir = \"/data\";\n    }\n\n    std::string tempdir = root_dir + \"/XXXXXX\"s;\n    if (!mkdtemp(tempdir.data())) {\n        cerr << \"unable to create tempdir on \" << root_dir << \"\\n\";\n        exit(EXIT_FAILURE);\n    }\n    if (!android::base::Realpath(tempdir, &gTestDir)) {\n        cerr << \"unable to find realpath for \" << tempdir;\n        exit(EXIT_FAILURE);\n    }\n\n    if (argc > 2) {\n        testfile_size = strtoull(argv[2], NULL, 0);\n        if (testfile_size == ULLONG_MAX) {\n            testfile_size = 512 * 1024 * 1024;\n        }\n    }\n\n    if (!DetermineBlockSize()) {\n        exit(EXIT_FAILURE);\n    }\n\n    auto result = RUN_ALL_TESTS();\n\n    std::string cmd = \"rm -rf \" + gTestDir;\n    system(cmd.c_str());\n\n    return result;\n}\n"
  },
  {
    "path": "fs_mgr/libfiemap/image_manager.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <libfiemap/image_manager.h>\n\n#include <optional>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <ext4_utils/ext4_utils.h>\n#include <fs_mgr/file_wait.h>\n#include <fs_mgr_dm_linear.h>\n#include <libdm/loop_control.h>\n#include <libfiemap/split_fiemap_writer.h>\n#include <libgsi/libgsi.h>\n\n#include \"metadata.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace fiemap {\n\nusing namespace std::literals;\nusing android::base::ReadFileToString;\nusing android::base::unique_fd;\nusing android::dm::DeviceMapper;\nusing android::dm::DmDeviceState;\nusing android::dm::DmTable;\nusing android::dm::DmTargetLinear;\nusing android::dm::LoopControl;\nusing android::fs_mgr::CreateLogicalPartition;\nusing android::fs_mgr::CreateLogicalPartitionParams;\nusing android::fs_mgr::CreateLogicalPartitions;\nusing android::fs_mgr::DestroyLogicalPartition;\nusing android::fs_mgr::GetBlockDevicePartitionName;\nusing android::fs_mgr::GetBlockDevicePartitionNames;\nusing android::fs_mgr::GetPartitionName;\n\nstatic constexpr char kTestImageMetadataDir[] = \"/metadata/gsi/test\";\nstatic constexpr char kOtaTestImageMetadataDir[] = \"/metadata/gsi/ota/test\";\n\nstd::unique_ptr<ImageManager> ImageManager::Open(const std::string& dir_prefix,\n                                                 const DeviceInfo& device_info) {\n    auto metadata_dir = \"/metadata/gsi/\" + dir_prefix;\n    auto data_dir = \"/data/gsi/\" + dir_prefix;\n    auto install_dir_file = gsi::DsuInstallDirFile(gsi::GetDsuSlot(dir_prefix));\n    std::string path;\n    if (ReadFileToString(install_dir_file, &path)) {\n        data_dir = path;\n    }\n    return Open(metadata_dir, data_dir, device_info);\n}\n\nstd::unique_ptr<ImageManager> ImageManager::Open(const std::string& metadata_dir,\n                                                 const std::string& data_dir,\n                                                 const DeviceInfo& device_info) {\n    return std::unique_ptr<ImageManager>(new ImageManager(metadata_dir, data_dir, device_info));\n}\n\nImageManager::ImageManager(const std::string& metadata_dir, const std::string& data_dir,\n                           const DeviceInfo& device_info)\n    : metadata_dir_(metadata_dir), data_dir_(data_dir), device_info_(device_info) {\n    partition_opener_ = std::make_unique<android::fs_mgr::PartitionOpener>();\n\n    // Allow overriding whether ImageManager thinks it's in recovery, for testing.\n#ifdef __ANDROID_RAMDISK__\n    device_info_.is_recovery = {true};\n#else\n    if (!device_info_.is_recovery.has_value()) {\n        device_info_.is_recovery = {false};\n    }\n#endif\n}\n\nstd::string ImageManager::GetImageHeaderPath(const std::string& name) {\n    return JoinPaths(data_dir_, name) + \".img\";\n}\n\n// The status file has one entry per line, with each entry formatted as one of:\n//   dm:<name>\n//   loop:<path>\n//\n// This simplifies the process of tearing down a mapping, since we can simply\n// unmap each entry in the order it appears.\nstd::string ImageManager::GetStatusFilePath(const std::string& image_name) {\n    return JoinPaths(metadata_dir_, image_name) + \".status\";\n}\n\nstatic std::string GetStatusPropertyName(const std::string& image_name) {\n    // Note: we don't prefix |image_name|, because CreateLogicalPartition won't\n    // prefix the name either. There are no plans to change this at the moment,\n    // consumers of the image API must take care to use globally-unique image\n    // names.\n    return \"gsid.mapped_image.\" + image_name;\n}\n\nvoid ImageManager::set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener) {\n    partition_opener_ = std::move(opener);\n}\n\nbool ImageManager::IsImageMapped(const std::string& image_name) {\n    auto prop_name = GetStatusPropertyName(image_name);\n    if (android::base::GetProperty(prop_name, \"\").empty()) {\n        // If mapped in first-stage init, the dm-device will exist but not the\n        // property.\n        auto& dm = DeviceMapper::Instance();\n        return dm.GetState(image_name) != DmDeviceState::INVALID;\n    }\n    return true;\n}\n\nstd::vector<std::string> ImageManager::GetAllBackingImages() {\n    std::vector<std::string> images;\n    if (!MetadataExists(metadata_dir_)) {\n        return images;\n    }\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (metadata) {\n        for (auto&& partition : metadata->partitions) {\n            images.push_back(partition.name);\n        }\n    }\n    return images;\n}\n\nbool ImageManager::BackingImageExists(const std::string& name) {\n    if (!MetadataExists(metadata_dir_)) {\n        return false;\n    }\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n    return !!FindPartition(*metadata.get(), name);\n}\n\nbool ImageManager::MetadataDirIsTest() const {\n    return IsSubdir(metadata_dir_, kTestImageMetadataDir) ||\n           IsSubdir(metadata_dir_, kOtaTestImageMetadataDir);\n}\n\nbool ImageManager::IsUnreliablePinningAllowed() const {\n    return IsSubdir(data_dir_, \"/data/gsi/dsu/\") || MetadataDirIsTest();\n}\n\nFiemapStatus ImageManager::CreateBackingImage(\n        const std::string& name, uint64_t size, int flags,\n        std::function<bool(uint64_t, uint64_t)>&& on_progress) {\n    auto data_path = GetImageHeaderPath(name);\n    std::unique_ptr<SplitFiemap> fw;\n    auto status = SplitFiemap::Create(data_path, size, 0, &fw, on_progress);\n    if (!status.is_ok()) {\n        return status;\n    }\n\n    bool reliable_pinning;\n    if (!FilesystemHasReliablePinning(data_path, &reliable_pinning)) {\n        return FiemapStatus::Error();\n    }\n    if (!reliable_pinning && !IsUnreliablePinningAllowed()) {\n        // For historical reasons, we allow unreliable pinning for certain use\n        // cases (DSUs, testing) because the ultimate use case is either\n        // developer-oriented or ephemeral (the intent is to boot immediately\n        // into DSUs). For everything else - such as snapshots/OTAs or adb\n        // remount, we have a higher bar, and require the filesystem to support\n        // proper pinning.\n        LOG(ERROR) << \"File system does not have reliable block pinning\";\n        SplitFiemap::RemoveSplitFiles(data_path);\n        return FiemapStatus::Error();\n    }\n\n    // Except for testing, we do not allow persisting metadata that references\n    // device-mapper devices. It just doesn't make sense, because the device\n    // numbering may change on reboot. We allow it for testing since the images\n    // are not meant to survive reboot. Outside of tests, this can only happen\n    // if device-mapper is stacked in some complex way not supported by\n    // FiemapWriter.\n    auto device_path = GetDevicePathForFile(fw.get());\n    if (android::base::StartsWith(device_path, \"/dev/block/dm-\") && !MetadataDirIsTest()) {\n        LOG(ERROR) << \"Cannot persist images against device-mapper device: \" << device_path;\n\n        fw = {};\n        SplitFiemap::RemoveSplitFiles(data_path);\n        return FiemapStatus::Error();\n    }\n\n    bool readonly = !!(flags & CREATE_IMAGE_READONLY);\n    if (!UpdateMetadata(metadata_dir_, name, fw.get(), size, readonly)) {\n        return FiemapStatus::Error();\n    }\n\n    if (flags & CREATE_IMAGE_ZERO_FILL) {\n        auto res = ZeroFillNewImage(name, 0);\n        if (!res.is_ok()) {\n            DeleteBackingImage(name);\n            return res;\n        }\n    }\n    return FiemapStatus::Ok();\n}\n\nFiemapStatus ImageManager::ZeroFillNewImage(const std::string& name, uint64_t bytes) {\n    auto data_path = GetImageHeaderPath(name);\n\n    // See the comment in MapImageDevice() about how this works.\n    std::string block_device;\n    bool can_use_devicemapper;\n    if (!FiemapWriter::GetBlockDeviceForFile(data_path, &block_device, &can_use_devicemapper)) {\n        LOG(ERROR) << \"Could not determine block device for \" << data_path;\n        return FiemapStatus::Error();\n    }\n\n    if (!can_use_devicemapper) {\n        // We've backed with loop devices, and since we store files in an\n        // unencrypted folder, the initial zeroes we wrote will suffice.\n        return FiemapStatus::Ok();\n    }\n\n    // data is dm-crypt, or FBE + dm-default-key. This means the zeroes written\n    // by libfiemap were encrypted, so we need to map the image in and correct\n    // this.\n    auto device = MappedDevice::Open(this, 10s, name);\n    if (!device) {\n        return FiemapStatus::Error();\n    }\n\n    static constexpr size_t kChunkSize = 4096;\n    std::string zeroes(kChunkSize, '\\0');\n\n    uint64_t remaining;\n    if (bytes) {\n        remaining = bytes;\n    } else {\n        remaining = get_block_device_size(device->fd());\n        if (!remaining) {\n            PLOG(ERROR) << \"Could not get block device size for \" << device->path();\n            return FiemapStatus::FromErrno(errno);\n        }\n    }\n    while (remaining) {\n        uint64_t to_write = std::min(static_cast<uint64_t>(zeroes.size()), remaining);\n        if (!android::base::WriteFully(device->fd(), zeroes.data(),\n                                       static_cast<size_t>(to_write))) {\n            PLOG(ERROR) << \"write failed: \" << device->path();\n            return FiemapStatus::FromErrno(errno);\n        }\n        remaining -= to_write;\n    }\n    return FiemapStatus::Ok();\n}\n\nbool ImageManager::DeleteBackingImage(const std::string& name) {\n    // For dm-linear devices sitting on top of /data, we cannot risk deleting\n    // the file. The underlying blocks could be reallocated by the filesystem.\n    if (IsImageMapped(name)) {\n        LOG(ERROR) << \"Cannot delete backing image \" << name << \" because mapped to a block device\";\n        return false;\n    }\n\n    if (device_info_.is_recovery.value()) {\n        LOG(ERROR) << \"Cannot remove images backed by /data in recovery\";\n        return false;\n    }\n\n    std::string message;\n    auto header_file = GetImageHeaderPath(name);\n    if (!SplitFiemap::RemoveSplitFiles(header_file, &message)) {\n        // This is fatal, because we don't want to leave these files dangling.\n        LOG(ERROR) << \"Error removing image \" << name << \": \" << message;\n        return false;\n    }\n\n    auto status_file = GetStatusFilePath(name);\n    if (!android::base::RemoveFileIfExists(status_file)) {\n        LOG(ERROR) << \"Error removing \" << status_file << \": \" << message;\n    }\n    return RemoveImageMetadata(metadata_dir_, name);\n}\n\n// Create a block device for an image file, using its extents in its\n// lp_metadata.\nbool ImageManager::MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,\n                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {\n    // :TODO: refresh extents in metadata file until f2fs is fixed.\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n\n    auto super = android::fs_mgr::GetMetadataSuperBlockDevice(*metadata.get());\n    auto block_device = android::fs_mgr::GetBlockDevicePartitionName(*super);\n\n    CreateLogicalPartitionParams params = {\n            .block_device = block_device,\n            .metadata = metadata.get(),\n            .partition_name = name,\n            .force_writable = true,\n            .timeout_ms = timeout_ms,\n            .partition_opener = &opener,\n    };\n    if (!CreateLogicalPartition(params, path)) {\n        LOG(ERROR) << \"Error creating device-mapper node for image \" << name;\n        return false;\n    }\n\n    auto status_string = \"dm:\" + name;\n    auto status_file = GetStatusFilePath(name);\n    if (!android::base::WriteStringToFile(status_string, status_file)) {\n        PLOG(ERROR) << \"Could not write status file: \" << status_file;\n        DestroyLogicalPartition(name);\n        return false;\n    }\n    return true;\n}\n\n// Helper to create a loop device for a file.\nstatic bool CreateLoopDevice(LoopControl& control, const std::string& file,\n                             const std::chrono::milliseconds& timeout_ms, std::string* path) {\n    static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;\n    android::base::unique_fd file_fd(open(file.c_str(), kOpenFlags));\n    if (file_fd < 0) {\n        PLOG(ERROR) << \"Could not open file: \" << file;\n        return false;\n    }\n    if (!control.Attach(file_fd, timeout_ms, path)) {\n        LOG(ERROR) << \"Could not create loop device for: \" << file;\n        return false;\n    }\n    LOG(INFO) << \"Created loop device \" << *path << \" for file \" << file;\n    return true;\n}\n\nclass AutoDetachLoopDevices final {\n  public:\n    AutoDetachLoopDevices(LoopControl& control, const std::vector<std::string>& devices)\n        : control_(control), devices_(devices), commit_(false) {}\n\n    ~AutoDetachLoopDevices() {\n        if (commit_) return;\n        for (const auto& device : devices_) {\n            control_.Detach(device);\n        }\n    }\n\n    void Commit() { commit_ = true; }\n\n  private:\n    LoopControl& control_;\n    const std::vector<std::string>& devices_;\n    bool commit_;\n};\n\n// If an image is stored across multiple files, this takes a list of loop\n// devices and joins them together using device-mapper.\nbool ImageManager::MapWithLoopDeviceList(const std::vector<std::string>& device_list,\n                                         const std::string& name,\n                                         const std::chrono::milliseconds& timeout_ms,\n                                         std::string* path) {\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n    auto partition = FindPartition(*metadata.get(), name);\n    if (!partition) {\n        LOG(ERROR) << \"Could not find image in metadata: \" << name;\n        return false;\n    }\n\n    // Since extent lengths are in sector units, the size should be a multiple\n    // of the sector size.\n    uint64_t partition_size = GetPartitionSize(*metadata.get(), *partition);\n    if (partition_size % LP_SECTOR_SIZE != 0) {\n        LOG(ERROR) << \"Partition size not sector aligned: \" << name << \", \" << partition_size\n                   << \" bytes\";\n        return false;\n    }\n\n    DmTable table;\n\n    uint64_t start_sector = 0;\n    uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;\n    for (const auto& block_device : device_list) {\n        // The final block device must be == partition_size, otherwise we\n        // can't find the AVB footer on verified partitions.\n        static constexpr int kOpenFlags = O_RDWR | O_NOFOLLOW | O_CLOEXEC;\n        unique_fd fd(open(block_device.c_str(), kOpenFlags));\n        if (fd < 0) {\n            PLOG(ERROR) << \"Open failed: \" << block_device;\n            return false;\n        }\n\n        uint64_t file_size = get_block_device_size(fd);\n        uint64_t file_sectors = file_size / LP_SECTOR_SIZE;\n        uint64_t segment_size = std::min(file_sectors, sectors_needed);\n\n        table.Emplace<DmTargetLinear>(start_sector, segment_size, block_device, 0);\n\n        start_sector += segment_size;\n        sectors_needed -= segment_size;\n        if (sectors_needed == 0) {\n            break;\n        }\n    }\n\n    auto& dm = DeviceMapper::Instance();\n    if (!dm.CreateDevice(name, table, path, timeout_ms)) {\n        LOG(ERROR) << \"Could not create device-mapper device over loop set\";\n        return false;\n    }\n\n    // Build the status file.\n    std::vector<std::string> lines;\n    lines.emplace_back(\"dm:\" + name);\n    for (const auto& block_device : device_list) {\n        lines.emplace_back(\"loop:\" + block_device);\n    }\n    auto status_message = android::base::Join(lines, \"\\n\");\n    auto status_file = GetStatusFilePath(name);\n    if (!android::base::WriteStringToFile(status_message, status_file)) {\n        PLOG(ERROR) << \"Write failed: \" << status_file;\n        dm.DeleteDevice(name);\n        return false;\n    }\n    return true;\n}\n\nstatic bool OptimizeLoopDevices(const std::vector<std::string>& device_list) {\n    for (const auto& device : device_list) {\n        unique_fd fd(open(device.c_str(), O_RDWR | O_CLOEXEC | O_NOFOLLOW));\n        if (fd < 0) {\n            PLOG(ERROR) << \"Open failed: \" << device;\n            return false;\n        }\n        if (!LoopControl::EnableDirectIo(fd)) {\n            return false;\n        }\n    }\n    return true;\n}\n\n// Helper to use one or more loop devices around image files.\nbool ImageManager::MapWithLoopDevice(const std::string& name,\n                                     const std::chrono::milliseconds& timeout_ms,\n                                     std::string* path) {\n    auto image_header = GetImageHeaderPath(name);\n\n    std::vector<std::string> file_list;\n    if (!SplitFiemap::GetSplitFileList(image_header, &file_list)) {\n        LOG(ERROR) << \"Could not get image file list\";\n        return false;\n    }\n\n    // Map each image file as a loopback device.\n    LoopControl control;\n    std::vector<std::string> loop_devices;\n    AutoDetachLoopDevices auto_detach(control, loop_devices);\n\n    auto start_time = std::chrono::steady_clock::now();\n    for (const auto& file : file_list) {\n        auto now = std::chrono::steady_clock::now();\n        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);\n\n        std::string loop_device;\n        if (!CreateLoopDevice(control, file, timeout_ms - elapsed, &loop_device)) {\n            break;\n        }\n        loop_devices.emplace_back(loop_device);\n    }\n    if (loop_devices.size() != file_list.size()) {\n        // The number of devices will mismatch if CreateLoopDevice() failed.\n        return false;\n    }\n\n    // If OptimizeLoopDevices fails, we'd use double the memory.\n    if (!OptimizeLoopDevices(loop_devices)) {\n        return false;\n    }\n\n    // If there's only one loop device (by far the most common case, splits\n    // will normally only happen on sdcards with FAT32), then just return that\n    // as the block device. Otherwise, we need to use dm-linear to stitch\n    // together all the loop devices we just created.\n    if (loop_devices.size() > 1) {\n        if (!MapWithLoopDeviceList(loop_devices, name, timeout_ms, path)) {\n            return false;\n        }\n    } else {\n        auto status_message = \"loop:\" + loop_devices.back();\n        auto status_file = GetStatusFilePath(name);\n        if (!android::base::WriteStringToFile(status_message, status_file)) {\n            PLOG(ERROR) << \"Write failed: \" << status_file;\n            return false;\n        }\n    }\n    auto_detach.Commit();\n\n    *path = loop_devices.back();\n    return true;\n}\n\nbool ImageManager::MapImageDevice(const std::string& name,\n                                  const std::chrono::milliseconds& timeout_ms, std::string* path) {\n    if (IsImageMapped(name)) {\n        LOG(ERROR) << \"Backing image \" << name << \" is already mapped\";\n        return false;\n    }\n\n    auto image_header = GetImageHeaderPath(name);\n\n#ifndef __ANDROID_RAMDISK__\n    // If there is a device-mapper node wrapping the block device, then we're\n    // able to create another node around it; the dm layer does not carry the\n    // exclusion lock down the stack when a mount occurs.\n    //\n    // If there is no intermediate device-mapper node, then partitions cannot be\n    // opened writable due to sepolicy and exclusivity of having a mounted\n    // filesystem. This should only happen on devices with no encryption, or\n    // devices with FBE and no metadata encryption. For these cases we COULD\n    // perform normal writes to /data/gsi (which is unencrypted), but given that\n    // metadata encryption has been mandated since Android R, we don't actually\n    // support or test this.\n    //\n    // So, we validate here that /data is backed by device-mapper. This code\n    // isn't needed in recovery since there is no /data.\n    //\n    // If this logic sticks for a release, we can remove MapWithLoopDevice, as\n    // well as WrapUserdataIfNeeded in fs_mgr.\n    std::string block_device;\n    bool can_use_devicemapper;\n    if (!FiemapWriter::GetBlockDeviceForFile(image_header, &block_device, &can_use_devicemapper)) {\n        LOG(ERROR) << \"Could not determine block device for \" << image_header;\n        return false;\n    }\n\n    if (!can_use_devicemapper) {\n        LOG(ERROR) << \"Cannot map image: /data must be mounted on top of device-mapper.\";\n        return false;\n    }\n#endif\n\n    if (!MapWithDmLinear(*partition_opener_.get(), name, timeout_ms, path)) {\n        return false;\n    }\n\n    // Set a property so we remember this is mapped.\n    auto prop_name = GetStatusPropertyName(name);\n    if (!android::base::SetProperty(prop_name, *path)) {\n        UnmapImageDevice(name, true);\n        return false;\n    }\n    return true;\n}\n\nbool ImageManager::MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,\n                                            std::string* dev) {\n    std::string ignore_path;\n    if (!MapWithDmLinear(opener, name, {}, &ignore_path)) {\n        return false;\n    }\n\n    auto& dm = DeviceMapper::Instance();\n    if (!dm.GetDeviceString(name, dev)) {\n        return false;\n    }\n    return true;\n}\n\nbool ImageManager::UnmapImageDevice(const std::string& name) {\n    return UnmapImageDevice(name, false);\n}\n\nbool ImageManager::UnmapImageDevice(const std::string& name, bool force) {\n    if (!force && !IsImageMapped(name)) {\n        LOG(ERROR) << \"Backing image \" << name << \" is not mapped\";\n        return false;\n    }\n    auto& dm = DeviceMapper::Instance();\n    std::optional<LoopControl> loop;\n\n    std::string status;\n    auto status_file = GetStatusFilePath(name);\n    if (!android::base::ReadFileToString(status_file, &status)) {\n        PLOG(ERROR) << \"Read failed: \" << status_file;\n        return false;\n    }\n\n    auto lines = android::base::Split(status, \"\\n\");\n    for (const auto& line : lines) {\n        auto pieces = android::base::Split(line, \":\");\n        if (pieces.size() != 2) {\n            LOG(ERROR) << \"Unknown status line\";\n            continue;\n        }\n        if (pieces[0] == \"dm\") {\n            // Failure to remove a dm node is fatal, since we can't safely\n            // remove the file or loop devices.\n            const auto& name = pieces[1];\n            if (!dm.DeleteDeviceIfExists(name)) {\n                return false;\n            }\n        } else if (pieces[0] == \"loop\") {\n            // Lazily connect to loop-control to avoid spurious errors in recovery.\n            if (!loop.has_value()) {\n                loop.emplace();\n            }\n\n            // Failure to remove a loop device is not fatal, since we can still\n            // remove the backing file if we want.\n            loop->Detach(pieces[1]);\n        } else {\n            LOG(ERROR) << \"Unknown status: \" << pieces[0];\n        }\n    }\n\n    std::string message;\n    if (!android::base::RemoveFileIfExists(status_file, &message)) {\n        LOG(ERROR) << \"Could not remove \" << status_file << \": \" << message;\n    }\n\n    auto status_prop = GetStatusPropertyName(name);\n    android::base::SetProperty(status_prop, \"\");\n    return true;\n}\n\nbool ImageManager::RemoveAllImages() {\n    if (!MetadataExists(metadata_dir_)) {\n        return true;\n    }\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return RemoveAllMetadata(metadata_dir_);\n    }\n\n    bool ok = true;\n    for (const auto& partition : metadata->partitions) {\n        auto partition_name = GetPartitionName(partition);\n        ok &= DeleteBackingImage(partition_name);\n    }\n    return ok && RemoveAllMetadata(metadata_dir_);\n}\n\nbool ImageManager::DisableAllImages() {\n    if (!MetadataExists(metadata_dir_)) {\n        return true;\n    }\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n\n    bool ok = true;\n    for (const auto& partition : metadata->partitions) {\n        auto partition_name = GetPartitionName(partition);\n        ok &= DisableImage(partition_name);\n    }\n    return ok;\n}\n\nbool ImageManager::Validate() {\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n\n    bool ok = true;\n    for (const auto& partition : metadata->partitions) {\n        auto name = GetPartitionName(partition);\n        auto image_path = GetImageHeaderPath(name);\n        auto fiemap = SplitFiemap::Open(image_path);\n        if (fiemap == nullptr) {\n            LOG(ERROR) << \"SplitFiemap::Open(\\\"\" << image_path << \"\\\") failed\";\n            ok = false;\n            continue;\n        }\n        if (!fiemap->HasPinnedExtents()) {\n            LOG(ERROR) << \"Image doesn't have pinned extents: \" << image_path;\n            ok = false;\n        }\n    }\n    return ok;\n}\n\nbool ImageManager::DisableImage(const std::string& name) {\n    return AddAttributes(metadata_dir_, name, LP_PARTITION_ATTR_DISABLED);\n}\n\nbool ImageManager::RemoveDisabledImages() {\n    if (!MetadataExists(metadata_dir_)) {\n        return true;\n    }\n\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n\n    bool ok = true;\n    for (const auto& partition : metadata->partitions) {\n        if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {\n            const auto name = GetPartitionName(partition);\n            if (!DeleteBackingImage(name)) {\n                ok = false;\n            } else {\n                LOG(INFO) << \"Removed disabled partition image: \" << name;\n            }\n        }\n    }\n    return ok;\n}\n\nbool ImageManager::GetMappedImageDevice(const std::string& name, std::string* device) {\n    auto prop_name = GetStatusPropertyName(name);\n    *device = android::base::GetProperty(prop_name, \"\");\n    if (!device->empty()) {\n        return true;\n    }\n\n    auto& dm = DeviceMapper::Instance();\n    if (dm.GetState(name) == DmDeviceState::INVALID) {\n        return false;\n    }\n    return dm.GetDmDevicePathByName(name, device);\n}\n\nbool ImageManager::MapAllImages(const std::function<bool(std::set<std::string>)>& init) {\n    if (!MetadataExists(metadata_dir_)) {\n        return true;\n    }\n\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n\n    std::set<std::string> devices;\n    for (const auto& name : GetBlockDevicePartitionNames(*metadata.get())) {\n        devices.emplace(name);\n    }\n    if (!init(std::move(devices))) {\n        return false;\n    }\n\n    auto data_device = GetMetadataSuperBlockDevice(*metadata.get());\n    auto data_partition_name = GetBlockDevicePartitionName(*data_device);\n    return CreateLogicalPartitions(*metadata.get(), data_partition_name);\n}\n\nstd::ostream& operator<<(std::ostream& os, android::fs_mgr::Extent* extent) {\n    if (auto e = extent->AsLinearExtent()) {\n        return os << \"<begin:\" << e->physical_sector() << \", end:\" << e->end_sector()\n                  << \", device:\" << e->device_index() << \">\";\n    }\n    return os << \"<unknown>\";\n}\n\nstatic bool CompareExtent(android::fs_mgr::Extent* a, android::fs_mgr::Extent* b) {\n    if (auto linear_a = a->AsLinearExtent()) {\n        auto linear_b = b->AsLinearExtent();\n        if (!linear_b) {\n            return false;\n        }\n        return linear_a->physical_sector() == linear_b->physical_sector() &&\n               linear_a->num_sectors() == linear_b->num_sectors() &&\n               linear_a->device_index() == linear_b->device_index();\n    }\n    return false;\n}\n\nstatic bool CompareExtents(android::fs_mgr::Partition* oldp, android::fs_mgr::Partition* newp) {\n    const auto& old_extents = oldp->extents();\n    const auto& new_extents = newp->extents();\n\n    auto old_iter = old_extents.begin();\n    auto new_iter = new_extents.begin();\n    while (true) {\n        if (old_iter == old_extents.end()) {\n            if (new_iter == new_extents.end()) {\n                break;\n            }\n            LOG(ERROR) << \"Unexpected extent added: \" << (*new_iter);\n            return false;\n        }\n        if (new_iter == new_extents.end()) {\n            LOG(ERROR) << \"Unexpected extent removed: \" << (*old_iter);\n            return false;\n        }\n\n        if (!CompareExtent(old_iter->get(), new_iter->get())) {\n            LOG(ERROR) << \"Extents do not match: \" << old_iter->get() << \", \" << new_iter->get();\n            return false;\n        }\n\n        old_iter++;\n        new_iter++;\n    }\n    return true;\n}\n\nbool ImageManager::ValidateImageMaps() {\n    if (!MetadataExists(metadata_dir_)) {\n        LOG(INFO) << \"ImageManager skipping verification; no images for \" << metadata_dir_;\n        return true;\n    }\n\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        LOG(ERROR) << \"ImageManager skipping verification; failed to open \" << metadata_dir_;\n        return true;\n    }\n\n    for (const auto& partition : metadata->partitions) {\n        auto name = GetPartitionName(partition);\n        auto image_path = GetImageHeaderPath(name);\n        auto fiemap = SplitFiemap::Open(image_path);\n        if (fiemap == nullptr) {\n            LOG(ERROR) << \"SplitFiemap::Open(\\\"\" << image_path << \"\\\") failed\";\n            return false;\n        }\n        if (!fiemap->HasPinnedExtents()) {\n            LOG(ERROR) << \"Image doesn't have pinned extents: \" << image_path;\n            return false;\n        }\n\n        android::fs_mgr::PartitionOpener opener;\n        auto builder = android::fs_mgr::MetadataBuilder::New(*metadata.get(), &opener);\n        if (!builder) {\n            LOG(ERROR) << \"Could not create metadata builder: \" << image_path;\n            return false;\n        }\n\n        auto new_p = builder->AddPartition(\"_temp_for_verify\", 0);\n        if (!new_p) {\n            LOG(ERROR) << \"Could not add temporary partition: \" << image_path;\n            return false;\n        }\n\n        auto partition_size = android::fs_mgr::GetPartitionSize(*metadata.get(), partition);\n        if (!FillPartitionExtents(builder.get(), new_p, fiemap.get(), partition_size)) {\n            LOG(ERROR) << \"Could not fill partition extents: \" << image_path;\n            return false;\n        }\n\n        auto old_p = builder->FindPartition(name);\n        if (!old_p) {\n            LOG(ERROR) << \"Could not find metadata for \" << image_path;\n            return false;\n        }\n\n        if (!CompareExtents(old_p, new_p)) {\n            LOG(ERROR) << \"Metadata for \" << image_path << \" does not match fiemap\";\n            return false;\n        }\n    }\n\n    return true;\n}\n\nbool ImageManager::IsImageDisabled(const std::string& name) {\n    if (!MetadataExists(metadata_dir_)) {\n        return true;\n    }\n\n    auto metadata = OpenMetadata(metadata_dir_);\n    if (!metadata) {\n        return false;\n    }\n\n    auto partition = FindPartition(*metadata.get(), name);\n    if (!partition) {\n        return false;\n    }\n\n    return !!(partition->attributes & LP_PARTITION_ATTR_DISABLED);\n}\n\nstd::unique_ptr<MappedDevice> MappedDevice::Open(IImageManager* manager,\n                                                 const std::chrono::milliseconds& timeout_ms,\n                                                 const std::string& name) {\n    std::string path;\n    if (!manager->MapImageDevice(name, timeout_ms, &path)) {\n        return nullptr;\n    }\n\n    auto device = std::unique_ptr<MappedDevice>(new MappedDevice(manager, name, path));\n    if (device->fd() < 0) {\n        return nullptr;\n    }\n    return device;\n}\n\nMappedDevice::MappedDevice(IImageManager* manager, const std::string& name, const std::string& path)\n    : manager_(manager), name_(name), path_(path) {\n    // The device is already mapped; try and open it.\n    fd_.reset(open(path.c_str(), O_RDWR | O_CLOEXEC));\n}\n\nMappedDevice::~MappedDevice() {\n    fd_ = {};\n    manager_->UnmapImageDevice(name_);\n}\n\nbool IImageManager::UnmapImageIfExists(const std::string& name) {\n    // No lock is needed even though this seems to be vulnerable to TOCTOU. If process A\n    // calls MapImageDevice() while process B calls UnmapImageIfExists(), and MapImageDevice()\n    // happens after process B checks IsImageMapped(), it would be as if MapImageDevice() is called\n    // after process B finishes calling UnmapImageIfExists(), resulting the image to be mapped,\n    // which is a reasonable sequence.\n    if (!IsImageMapped(name)) {\n        return true;\n    }\n    return UnmapImageDevice(name);\n}\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/image_test.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <inttypes.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/vfs.h>\n\n#include <chrono>\n#include <iostream>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <ext4_utils/ext4_utils.h>\n#include <fs_mgr/file_wait.h>\n#include <gtest/gtest.h>\n#include <libdm/dm.h>\n#include <libdm/loop_control.h>\n#include <libfiemap/image_manager.h>\n\n#include \"utility.h\"\n\nusing namespace android::dm;\nusing namespace std::literals;\nusing android::base::unique_fd;\nusing android::fiemap::ImageManager;\nusing android::fiemap::IsSubdir;\nusing android::fs_mgr::BlockDeviceInfo;\nusing android::fs_mgr::PartitionOpener;\nusing android::fs_mgr::WaitForFile;\n\nstatic std::string gDataPath;\nstatic std::string gTestDir;\nstatic constexpr char kMetadataPath[] = \"/metadata/gsi/test\";\n\nstatic constexpr uint64_t kTestImageSize = 1024 * 1024;\n\nclass TestPartitionOpener final : public PartitionOpener {\n  public:\n    android::base::unique_fd Open(const std::string& partition_name, int flags) const override {\n        return PartitionOpener::Open(GetPathForBlockDeviceName(partition_name), flags);\n    }\n    bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override {\n        return PartitionOpener::GetInfo(GetPathForBlockDeviceName(partition_name), info);\n    }\n    std::string GetDeviceString(const std::string& partition_name) const override {\n        return PartitionOpener::GetDeviceString(GetPathForBlockDeviceName(partition_name));\n    }\n\n  private:\n    static std::string GetPathForBlockDeviceName(const std::string& name) {\n        if (android::base::StartsWith(name, \"loop\") || android::base::StartsWith(name, \"dm-\")) {\n            return \"/dev/block/\"s + name;\n        }\n        return name;\n    }\n};\n\n// This fixture is for tests against the device's native configuration.\nclass NativeTest : public ::testing::Test {\n  protected:\n    void SetUp() override {\n        manager_ = ImageManager::Open(kMetadataPath, gDataPath);\n        ASSERT_NE(manager_, nullptr);\n\n        manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());\n\n        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();\n        base_name_ = tinfo->name();\n    }\n\n    void TearDown() override {\n        manager_->UnmapImageDevice(base_name_);\n        manager_->DeleteBackingImage(base_name_);\n    }\n\n    std::string PropertyName() { return \"gsid.mapped_image.\" + base_name_; }\n\n    std::unique_ptr<ImageManager> manager_;\n    std::string base_name_;\n};\n\nTEST_F(NativeTest, CreateAndMap) {\n    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));\n\n    std::string path;\n    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path));\n    ASSERT_TRUE(manager_->IsImageMapped(base_name_));\n    ASSERT_EQ(android::base::GetProperty(PropertyName(), \"\"), path);\n\n    {\n        unique_fd fd(open(path.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC));\n        ASSERT_GE(fd, 0);\n        ASSERT_EQ(get_block_device_size(fd), kTestImageSize);\n    }\n\n    ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));\n    ASSERT_FALSE(manager_->IsImageMapped(base_name_));\n    ASSERT_EQ(android::base::GetProperty(PropertyName(), \"\"), \"\");\n}\n\nTEST_F(NativeTest, DisableImage) {\n    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));\n    ASSERT_TRUE(manager_->BackingImageExists(base_name_));\n    ASSERT_TRUE(manager_->DisableImage(base_name_));\n    ASSERT_TRUE(manager_->IsImageDisabled(base_name_));\n    ASSERT_TRUE(manager_->RemoveDisabledImages());\n    ASSERT_TRUE(!manager_->BackingImageExists(base_name_));\n}\n\nTEST_F(NativeTest, GetMappedImageDevice) {\n    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));\n\n    std::string path1, path2;\n    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path1));\n    ASSERT_TRUE(manager_->GetMappedImageDevice(base_name_, &path2));\n    EXPECT_EQ(path1, path2);\n\n    ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));\n}\n\nnamespace {\n\nstruct IsSubdirTestParam {\n    std::string child;\n    std::string parent;\n    bool result;\n};\n\nclass IsSubdirTest : public ::testing::TestWithParam<IsSubdirTestParam> {};\n\nTEST_P(IsSubdirTest, Test) {\n    const auto& param = GetParam();\n    EXPECT_EQ(param.result, IsSubdir(param.child, param.parent))\n            << \"IsSubdir(child=\\\"\" << param.child << \"\\\", parent=\\\"\" << param.parent\n            << \"\\\") != \" << (param.result ? \"true\" : \"false\");\n}\n\nstd::vector<IsSubdirTestParam> IsSubdirTestValues() {\n    // clang-format off\n    std::vector<IsSubdirTestParam> base_cases{\n            {\"/foo/bar\",     \"/foo\",     true},\n            {\"/foo/bar/baz\", \"/foo\",     true},\n            {\"/foo\",         \"/foo\",     true},\n            {\"/foo\",         \"/\",        true},\n            {\"/\",            \"/\",        true},\n            {\"/foo\",         \"/foo/bar\", false},\n            {\"/foo\",         \"/bar\",     false},\n            {\"/foo-bar\",     \"/foo\",     false},\n            {\"/\",            \"/foo\",     false},\n    };\n    // clang-format on\n    std::vector<IsSubdirTestParam> ret;\n    for (const auto& e : base_cases) {\n        ret.push_back(e);\n        ret.push_back({e.child + \"/\", e.parent, e.result});\n        ret.push_back({e.child, e.parent + \"/\", e.result});\n        ret.push_back({e.child + \"/\", e.parent + \"/\", e.result});\n    }\n    return ret;\n}\n\nINSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));\n\n// This allows test cases for filesystems with larger than 4KiB alignment.\n// It creates a loop device, formats it with a FAT filesystem, and then\n// creates an ImageManager so backing images can be created on that filesystem.\nclass VfatTest : public ::testing::Test {\n  protected:\n    // 64MB Filesystem and 32k block size by default\n    static constexpr uint64_t kBlockSize = 32768;\n    static constexpr uint64_t kFilesystemSize = 64 * 1024 * 1024;\n\n    void SetUp() override {\n        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();\n        base_name_ = tinfo->name();\n\n        fs_path_ = gTestDir + \"/vfat.img\";\n        uint64_t count = kFilesystemSize / kBlockSize;\n        std::string dd_cmd =\n                ::android::base::StringPrintf(\"/system/bin/dd if=/dev/zero of=%s bs=%\" PRIu64\n                                              \" count=%\" PRIu64 \" > /dev/null 2>&1\",\n                                              fs_path_.c_str(), kBlockSize, count);\n        // create mount point\n        mntpoint_ = std::string(getenv(\"TMPDIR\")) + \"/fiemap_mnt\";\n        if (mkdir(mntpoint_.c_str(), S_IRWXU) < 0) {\n            ASSERT_EQ(errno, EEXIST) << strerror(errno);\n        }\n\n        // create file for the file system\n        int ret = system(dd_cmd.c_str());\n        ASSERT_EQ(ret, 0);\n\n        // Get and attach a loop device to the filesystem we created\n        loop_device_.emplace(fs_path_, 10s);\n        ASSERT_TRUE(loop_device_->valid());\n\n        // create file system\n        uint64_t sectors = kFilesystemSize / 512;\n        std::string mkfs_cmd =\n                ::android::base::StringPrintf(\"/system/bin/newfs_msdos -A -O Android -s %\" PRIu64\n                                              \" -b %\" PRIu64 \" %s > /dev/null 2>&1\",\n                                              sectors, kBlockSize, loop_device_->device().c_str());\n        ret = system(mkfs_cmd.c_str());\n        ASSERT_EQ(ret, 0);\n\n        // Create a wrapping DM device to prevent gsid taking the loopback path.\n        auto& dm = DeviceMapper::Instance();\n        DmTable table;\n        table.Emplace<DmTargetLinear>(0, kFilesystemSize / 512, loop_device_->device(), 0);\n\n        dm_name_ = android::base::Basename(loop_device_->device()) + \"-wrapper\";\n        ASSERT_TRUE(dm.CreateDevice(dm_name_, table, &dm_path_, 10s));\n\n        // mount the file system\n        ASSERT_EQ(mount(dm_path_.c_str(), mntpoint_.c_str(), \"vfat\", 0, nullptr), 0)\n                << strerror(errno);\n    }\n\n    void TearDown() override {\n        // Clear up anything backed on the temporary FS.\n        if (manager_) {\n            manager_->UnmapImageIfExists(base_name_);\n            manager_->DeleteBackingImage(base_name_);\n        }\n\n        // Unmount temporary FS.\n        if (umount(mntpoint_.c_str()) < 0) {\n            ASSERT_EQ(errno, EINVAL) << strerror(errno);\n        }\n\n        // Destroy the dm wrapper.\n        auto& dm = DeviceMapper::Instance();\n        ASSERT_TRUE(dm.DeleteDeviceIfExists(dm_name_));\n\n        // Destroy the loop device.\n        loop_device_ = {};\n\n        // Destroy the temporary FS.\n        if (rmdir(mntpoint_.c_str()) < 0) {\n            ASSERT_EQ(errno, ENOENT) << strerror(errno);\n        }\n        if (unlink(fs_path_.c_str()) < 0) {\n            ASSERT_EQ(errno, ENOENT) << strerror(errno);\n        }\n    }\n\n    std::string base_name_;\n    std::string mntpoint_;\n    std::string fs_path_;\n    std::optional<LoopDevice> loop_device_;\n    std::string dm_name_;\n    std::string dm_path_;\n    std::unique_ptr<ImageManager> manager_;\n};\n\n// The actual size of the block device should be the requested size. For\n// example, a 16KB image should be mapped as a 16KB device, even if the\n// underlying filesystem requires 32KB to be fallocated.\nTEST_F(VfatTest, DeviceIsRequestedSize) {\n    manager_ = ImageManager::Open(kMetadataPath, mntpoint_);\n    ASSERT_NE(manager_, nullptr);\n\n    manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());\n\n    // Create something not aligned to the backing fs block size.\n    constexpr uint64_t kTestSize = (kBlockSize * 64) - (kBlockSize / 2);\n    ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestSize, false, nullptr));\n\n    std::string path;\n    ASSERT_TRUE(manager_->MapImageDevice(base_name_, 10s, &path));\n\n    unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_GE(fd, 0);\n    ASSERT_EQ(get_block_device_size(fd.get()), kTestSize);\n}\n\n}  // namespace\n\nbool Mkdir(const std::string& path) {\n    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {\n        std::cerr << \"Could not mkdir \" << path << \": \" << strerror(errno) << std::endl;\n        return false;\n    }\n    return true;\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n\n    if (argc >= 2) {\n        gDataPath = argv[1];\n    } else {\n        gDataPath = \"/data/local/tmp\";\n    }\n\n    if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(kMetadataPath + \"/mnt\"s)) {\n        return 1;\n    }\n\n    std::string tempdir = gDataPath + \"/XXXXXX\";\n    if (!mkdtemp(tempdir.data())) {\n        std::cerr << \"unable to create tempdir on \" << tempdir << \"\\n\";\n        exit(EXIT_FAILURE);\n    }\n    if (!android::base::Realpath(tempdir, &gTestDir)) {\n        std::cerr << \"unable to find realpath for \" << tempdir;\n        exit(EXIT_FAILURE);\n    }\n\n    auto rv = RUN_ALL_TESTS();\n\n    std::string cmd = \"rm -rf \" + gTestDir;\n    system(cmd.c_str());\n\n    return rv;\n}\n"
  },
  {
    "path": "fs_mgr/libfiemap/include/libfiemap/fiemap_status.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <errno.h>\n#include <stdint.h>\n\n#include <string>\n\nnamespace android::fiemap {\n\n// Represent error status of libfiemap classes.\nclass FiemapStatus {\n  public:\n    enum class ErrorCode : int32_t {\n        SUCCESS = 0,\n        // Generic non-recoverable failure.\n        ERROR = INT32_MIN,\n        // Not enough space\n        NO_SPACE = -ENOSPC,\n    };\n\n    // Create from a given errno (specified in errno,h)\n    static FiemapStatus FromErrno(int error_num) { return FiemapStatus(CastErrorCode(-error_num)); }\n\n    // Create from an integer error code that is expected to be an ErrorCode\n    // value. If it isn't, Error() is returned.\n    static FiemapStatus FromErrorCode(int32_t error_code) {\n        return FiemapStatus(CastErrorCode(error_code));\n    }\n\n    // Generic error.\n    static FiemapStatus Error() { return FiemapStatus(ErrorCode::ERROR); }\n\n    // Success.\n    static FiemapStatus Ok() { return FiemapStatus(ErrorCode::SUCCESS); }\n\n    ErrorCode error_code() const { return error_code_; }\n    bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }\n    operator bool() const { return is_ok(); }\n\n    // For logging and debugging only.\n    std::string string() const;\n\n    explicit FiemapStatus(ErrorCode code) : error_code_(code) {}\n\n  private:\n    ErrorCode error_code_;\n\n    static ErrorCode CastErrorCode(int error);\n};\n\n}  // namespace android::fiemap\n"
  },
  {
    "path": "fs_mgr/libfiemap/include/libfiemap/fiemap_writer.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <linux/fiemap.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <functional>\n#include <string>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n\n#include <libfiemap/fiemap_status.h>\n\nnamespace android {\nnamespace fiemap {\n\nclass FiemapWriter;\nusing FiemapUniquePtr = std::unique_ptr<FiemapWriter>;\n\nclass FiemapWriter final {\n  public:\n    // Factory method for FiemapWriter.\n    // The method returns FiemapUniquePtr that contains all the data necessary to be able to write\n    // to the given file directly using raw block i/o. The optional progress callback will be\n    // invoked, if create is true, while the file is being initialized. It receives the bytes\n    // written and the number of total bytes. If the callback returns false, the operation will\n    // fail.\n    //\n    // Note: when create is true, the file size will be aligned up to the nearest file system\n    // block.\n    static FiemapUniquePtr Open(const std::string& file_path, uint64_t file_size,\n                                bool create = true,\n                                std::function<bool(uint64_t, uint64_t)> progress = {});\n    static FiemapStatus Open(const std::string& file_path, uint64_t file_size, FiemapUniquePtr* out,\n                             bool create = true,\n                             std::function<bool(uint64_t, uint64_t)> progress = {});\n\n    // Check that a file still has the same extents since it was last opened with FiemapWriter,\n    // assuming the file was not resized outside of FiemapWriter. Returns false either on error\n    // or if the file was not pinned.\n    //\n    // This will always return true on Ext4. On F2FS, it will return true if either of the\n    // following cases are true:\n    //   - The file was never pinned.\n    //   - The file is pinned and has not been moved by the GC.\n    // Thus, this method should only be called for pinned files (such as those returned by\n    // FiemapWriter::Open).\n    static bool HasPinnedExtents(const std::string& file_path);\n\n    // Returns the underlying block device of a file. This will look past device-mapper layers\n    // as long as each layer would not change block mappings (i.e., dm-crypt, dm-bow, and dm-\n    // default-key tables are okay; dm-linear is not). If a mapping such as dm-linear is found,\n    // it will be returned in place of any physical block device.\n    //\n    // It is the caller's responsibility to check whether the returned block device is acceptable.\n    // Gsid, for example, will only accept /dev/block/by-name/userdata as the bottom device.\n    // Callers can check the device name (dm- or loop prefix), inspect sysfs, or compare the major\n    // number against a boot device.\n    //\n    // If device-mapper nodes were encountered, then |uses_dm| will be set to true.\n    static bool GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,\n                                      bool* uses_dm = nullptr);\n\n    ~FiemapWriter() = default;\n\n    const std::string& file_path() const { return file_path_; };\n    uint64_t size() const { return file_size_; };\n    const std::string& bdev_path() const { return bdev_path_; };\n    uint64_t block_size() const { return block_size_; };\n    const std::vector<struct fiemap_extent>& extents() { return extents_; };\n    uint32_t fs_type() const { return fs_type_; }\n\n    // Non-copyable & Non-movable\n    FiemapWriter(const FiemapWriter&) = delete;\n    FiemapWriter& operator=(const FiemapWriter&) = delete;\n    FiemapWriter& operator=(FiemapWriter&&) = delete;\n    FiemapWriter(FiemapWriter&&) = delete;\n\n  private:\n    // Name of the file managed by this class.\n    std::string file_path_;\n    // Block device on which we have created the file.\n    std::string bdev_path_;\n\n    // Size in bytes of the file this class is writing\n    uint64_t file_size_;\n\n    // total size in bytes of the block device\n    uint64_t bdev_size_;\n\n    // Filesystem type where the file is being created.\n    // See: <uapi/linux/magic.h> for filesystem magic numbers\n    uint32_t fs_type_;\n\n    // block size as reported by the kernel of the underlying block device;\n    uint64_t block_size_;\n\n    // This file's fiemap\n    std::vector<struct fiemap_extent> extents_;\n\n    FiemapWriter() = default;\n};\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/include/libfiemap/image_manager.h",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <stdint.h>\n\n#include <chrono>\n#include <functional>\n#include <memory>\n#include <optional>\n#include <set>\n#include <string>\n\n#include <android-base/unique_fd.h>\n#include <libfiemap/fiemap_status.h>\n#include <liblp/partition_opener.h>\n\nnamespace android {\nnamespace fiemap {\n\nclass IImageManager {\n  public:\n    using IPartitionOpener = android::fs_mgr::IPartitionOpener;\n\n    virtual ~IImageManager() {}\n\n    // Helper for dependency injection.\n    struct DeviceInfo {\n        std::optional<bool> is_recovery;\n    };\n\n    // When linking to libfiemap_binder, the Open() call will use binder.\n    // Otherwise, the Open() call will use the ImageManager implementation\n    // below. In binder mode, device_info is ignored.\n    static std::unique_ptr<IImageManager> Open(const std::string& dir_prefix,\n                                               const std::chrono::milliseconds& timeout_ms,\n                                               const DeviceInfo& device_info = {});\n\n    // Flags for CreateBackingImage().\n    static constexpr int CREATE_IMAGE_DEFAULT = 0x0;\n    static constexpr int CREATE_IMAGE_READONLY = 0x1;\n    static constexpr int CREATE_IMAGE_ZERO_FILL = 0x2;\n\n    // Create an image that can be mapped as a block-device. If |force_zero_fill|\n    // is true, the image will be zero-filled. Otherwise, the initial content\n    // of the image is undefined. If zero-fill is requested, and the operation\n    // cannot be completed, the image will be deleted and this function will\n    // return false.\n    virtual FiemapStatus CreateBackingImage(\n            const std::string& name, uint64_t size, int flags,\n            std::function<bool(uint64_t, uint64_t)>&& on_progress = nullptr) = 0;\n\n    // Delete an image created with CreateBackingImage. Its entry will be\n    // removed from the associated lp_metadata file.\n    virtual bool DeleteBackingImage(const std::string& name) = 0;\n\n    // Create a block device for an image previously created with\n    // CreateBackingImage. This will wait for at most |timeout_ms| milliseconds\n    // for |path| to be available, and will return false if not available in\n    // the requested time. If |timeout_ms| is zero, this is NOT guaranteed to\n    // return true. A timeout of 10s is recommended.\n    //\n    // Note that snapshots created with a readonly flag are always mapped\n    // writable. The flag is persisted in the lp_metadata file however, so if\n    // fs_mgr::CreateLogicalPartition(s) is used, the flag will be respected.\n    virtual bool MapImageDevice(const std::string& name,\n                                const std::chrono::milliseconds& timeout_ms, std::string* path) = 0;\n\n    // Unmap a block device previously mapped with mapBackingImage.\n    virtual bool UnmapImageDevice(const std::string& name) = 0;\n\n    // Returns true whether the named backing image exists. This does not check\n    // consistency with the /data partition, so that it can return true in\n    // recovery.\n    virtual bool BackingImageExists(const std::string& name) = 0;\n\n    // Returns true if the specified image is mapped to a device.\n    virtual bool IsImageMapped(const std::string& name) = 0;\n\n    // Map an image using device-mapper. This is not available over binder, and\n    // is intended only for first-stage init. The returned device is a major:minor\n    // device string.\n    virtual bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,\n                                          std::string* dev) = 0;\n\n    // If an image was mapped, return the path to its device. Otherwise, return\n    // false. Errors are not reported in this case, calling IsImageMapped is\n    // not necessary.\n    virtual bool GetMappedImageDevice(const std::string& name, std::string* device) = 0;\n\n    // Map all images owned by this manager. This is only intended to be used\n    // during first-stage init, and as such, it does not provide a timeout\n    // (meaning libdm races can't be resolved, as ueventd is not available),\n    // and is not available over binder.\n    //\n    // The callback provided is given the list of dependent block devices.\n    virtual bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) = 0;\n\n    // Mark an image as disabled. This is useful for marking an image as\n    // will-be-deleted in recovery, since recovery cannot mount /data.\n    virtual bool DisableImage(const std::string& name) = 0;\n\n    // Remove all images that been marked as disabled.\n    virtual bool RemoveDisabledImages() = 0;\n\n    // Get all backing image names.\n    virtual std::vector<std::string> GetAllBackingImages() = 0;\n\n    // Writes |bytes| zeros to |name| file. If |bytes| is 0, then the\n    // whole file if filled with zeros.\n    virtual FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes) = 0;\n\n    // Find and remove all images and metadata for this manager.\n    virtual bool RemoveAllImages() = 0;\n\n    // Finds and marks all images for deletion upon next reboot. This is used during recovery since\n    // we cannot mount /data\n    virtual bool DisableAllImages() = 0;\n\n    virtual bool UnmapImageIfExists(const std::string& name);\n\n    // Returns whether DisableImage() was called.\n    virtual bool IsImageDisabled(const std::string& name) = 0;\n};\n\nclass ImageManager final : public IImageManager {\n  public:\n    // Return an ImageManager for the given metadata and data directories. Both\n    // directories must already exist.\n    static std::unique_ptr<ImageManager> Open(const std::string& metadata_dir,\n                                              const std::string& data_dir,\n                                              const DeviceInfo& device_info = {});\n\n    // Helper function that derives the metadata and data dirs given a single\n    // prefix.\n    static std::unique_ptr<ImageManager> Open(const std::string& dir_prefix,\n                                              const DeviceInfo& device_info = {});\n\n    // Methods that must be implemented from IImageManager.\n    FiemapStatus CreateBackingImage(const std::string& name, uint64_t size, int flags,\n                                    std::function<bool(uint64_t, uint64_t)>&& on_progress) override;\n    bool DeleteBackingImage(const std::string& name) override;\n    bool MapImageDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,\n                        std::string* path) override;\n    bool UnmapImageDevice(const std::string& name) override;\n    bool BackingImageExists(const std::string& name) override;\n    bool IsImageMapped(const std::string& name) override;\n    bool MapImageWithDeviceMapper(const IPartitionOpener& opener, const std::string& name,\n                                  std::string* dev) override;\n    bool RemoveAllImages() override;\n    bool DisableAllImages() override;\n    bool DisableImage(const std::string& name) override;\n    bool RemoveDisabledImages() override;\n    bool GetMappedImageDevice(const std::string& name, std::string* device) override;\n    bool MapAllImages(const std::function<bool(std::set<std::string>)>& init) override;\n    bool IsImageDisabled(const std::string& name) override;\n\n    std::vector<std::string> GetAllBackingImages();\n\n    // Validates that all images still have pinned extents. This will be removed\n    // once b/134588268 is fixed.\n    bool Validate();\n\n    void set_partition_opener(std::unique_ptr<IPartitionOpener>&& opener);\n\n    // Writes |bytes| zeros at the beginning of the passed image\n    FiemapStatus ZeroFillNewImage(const std::string& name, uint64_t bytes);\n\n    // Validate that all images still have the same block map.\n    bool ValidateImageMaps();\n\n  private:\n    ImageManager(const std::string& metadata_dir, const std::string& data_dir,\n                 const DeviceInfo& device_info);\n    std::string GetImageHeaderPath(const std::string& name);\n    std::string GetStatusFilePath(const std::string& image_name);\n    bool MapWithLoopDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms,\n                           std::string* path);\n    bool MapWithLoopDeviceList(const std::vector<std::string>& device_list, const std::string& name,\n                               const std::chrono::milliseconds& timeout_ms, std::string* path);\n    bool MapWithDmLinear(const IPartitionOpener& opener, const std::string& name,\n                         const std::chrono::milliseconds& timeout_ms, std::string* path);\n    bool UnmapImageDevice(const std::string& name, bool force);\n    bool IsUnreliablePinningAllowed() const;\n    bool MetadataDirIsTest() const;\n\n    ImageManager(const ImageManager&) = delete;\n    ImageManager& operator=(const ImageManager&) = delete;\n    ImageManager& operator=(ImageManager&&) = delete;\n    ImageManager(ImageManager&&) = delete;\n\n    std::string metadata_dir_;\n    std::string data_dir_;\n    std::unique_ptr<IPartitionOpener> partition_opener_;\n    DeviceInfo device_info_;\n};\n\n// RAII helper class for mapping and opening devices with an ImageManager.\nclass MappedDevice final {\n  public:\n    static std::unique_ptr<MappedDevice> Open(IImageManager* manager,\n                                              const std::chrono::milliseconds& timeout_ms,\n                                              const std::string& name);\n\n    ~MappedDevice();\n\n    int fd() const { return fd_.get(); }\n    const std::string& path() const { return path_; }\n\n  protected:\n    MappedDevice(IImageManager* manager, const std::string& name, const std::string& path);\n\n    IImageManager* manager_;\n    std::string name_;\n    std::string path_;\n    android::base::unique_fd fd_;\n};\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/include/libfiemap/split_fiemap_writer.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#include <functional>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n\n#include <libfiemap/fiemap_status.h>\n#include <libfiemap/fiemap_writer.h>\n\nnamespace android {\nnamespace fiemap {\n\n// Wrapper around FiemapWriter that is able to split images across files if\n// necessary.\nclass SplitFiemap final {\n  public:\n    using ProgressCallback = std::function<bool(uint64_t, uint64_t)>;\n\n    // Create a new split fiemap file. If |max_piece_size| is 0, the number of\n    // pieces will be determined automatically by detecting the filesystem.\n    // Otherwise, the file will be split evenly (with the remainder in the\n    // final file).\n    static std::unique_ptr<SplitFiemap> Create(const std::string& file_path, uint64_t file_size,\n                                               uint64_t max_piece_size,\n                                               ProgressCallback progress = {});\n    static FiemapStatus Create(const std::string& file_path, uint64_t file_size,\n                               uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,\n                               ProgressCallback progress = {});\n\n    // Open an existing split fiemap file.\n    static std::unique_ptr<SplitFiemap> Open(const std::string& file_path);\n\n    ~SplitFiemap();\n\n    // Return a list of all files created for a split file.\n    static bool GetSplitFileList(const std::string& file_path, std::vector<std::string>* list);\n\n    // Destroy all components of a split file. If the root file does not exist,\n    // this returns true and does not report an error.\n    static bool RemoveSplitFiles(const std::string& file_path, std::string* message = nullptr);\n\n    // Return whether all components of a split file still have pinned extents.\n    bool HasPinnedExtents() const;\n\n    // Helper method for writing data that spans files. Note there is no seek\n    // method (yet); this starts at 0 and increments the position by |bytes|.\n    bool Write(const void* data, uint64_t bytes);\n\n    // Flush all writes to all split files.\n    bool Flush();\n\n    const std::vector<struct fiemap_extent>& extents();\n    uint32_t block_size() const;\n    uint64_t size() const { return total_size_; }\n    const std::string& bdev_path() const;\n\n    // Non-copyable & Non-movable\n    SplitFiemap(const SplitFiemap&) = delete;\n    SplitFiemap& operator=(const SplitFiemap&) = delete;\n    SplitFiemap& operator=(SplitFiemap&&) = delete;\n    SplitFiemap(SplitFiemap&&) = delete;\n\n  private:\n    SplitFiemap() = default;\n    void AddFile(FiemapUniquePtr&& file);\n\n    bool creating_ = false;\n    std::string list_file_;\n    std::vector<FiemapUniquePtr> files_;\n    std::vector<struct fiemap_extent> extents_;\n    uint64_t total_size_ = 0;\n\n    // Most recently open file and position for Write().\n    size_t cursor_index_ = 0;\n    uint64_t cursor_file_pos_ = 0;\n    android::base::unique_fd cursor_fd_;\n};\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/metadata.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"metadata.h\"\n\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <liblp/builder.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace fiemap {\n\nusing namespace android::fs_mgr;\nusing android::base::unique_fd;\n\nstatic constexpr uint32_t kMaxMetadataSize = 256 * 1024;\n\nstd::string GetMetadataFile(const std::string& metadata_dir) {\n    return JoinPaths(metadata_dir, \"lp_metadata\");\n}\n\nbool MetadataExists(const std::string& metadata_dir) {\n    auto metadata_file = GetMetadataFile(metadata_dir);\n    if (access(metadata_file.c_str(), F_OK)) {\n        if (errno != ENOENT) {\n            PLOG(ERROR) << \"Access \" << metadata_file << \" failed:\";\n        }\n        return false;\n    }\n    return true;\n}\n\nstd::unique_ptr<LpMetadata> OpenMetadata(const std::string& metadata_dir) {\n    auto metadata_file = GetMetadataFile(metadata_dir);\n    auto metadata = ReadFromImageFile(metadata_file);\n    if (!metadata) {\n        LOG(ERROR) << \"Could not read metadata file \" << metadata_file;\n        return nullptr;\n    }\n    return metadata;\n}\n\n// :TODO: overwrite on create if open fails\nstd::unique_ptr<MetadataBuilder> OpenOrCreateMetadata(const std::string& metadata_dir,\n                                                      SplitFiemap* file) {\n    auto metadata_file = GetMetadataFile(metadata_dir);\n\n    PartitionOpener opener;\n    std::unique_ptr<MetadataBuilder> builder;\n    if (access(metadata_file.c_str(), R_OK)) {\n        if (errno != ENOENT) {\n            PLOG(ERROR) << \"Access \" << metadata_file << \" failed:\";\n            return nullptr;\n        }\n\n        auto data_device = GetDevicePathForFile(file);\n\n        BlockDeviceInfo device_info;\n        if (!opener.GetInfo(data_device, &device_info)) {\n            LOG(ERROR) << \"Could not read partition: \" << data_device;\n            return nullptr;\n        }\n\n        std::vector<BlockDeviceInfo> block_devices = {device_info};\n        auto super_name = android::base::Basename(data_device);\n        builder = MetadataBuilder::New(block_devices, super_name, kMaxMetadataSize, 1);\n    } else {\n        auto metadata = OpenMetadata(metadata_dir);\n        if (!metadata) {\n            return nullptr;\n        }\n        builder = MetadataBuilder::New(*metadata.get(), &opener);\n    }\n\n    if (!builder) {\n        LOG(ERROR) << \"Could not create metadata builder\";\n        return nullptr;\n    }\n    return builder;\n}\n\nbool SaveMetadata(MetadataBuilder* builder, const std::string& metadata_dir) {\n    auto exported = builder->Export();\n    if (!exported) {\n        LOG(ERROR) << \"Unable to export new metadata\";\n        return false;\n    }\n\n    // If there are no more partitions in the metadata, just delete the file.\n    auto metadata_file = GetMetadataFile(metadata_dir);\n    if (exported->partitions.empty() && android::base::RemoveFileIfExists(metadata_file)) {\n        return true;\n    }\n\n    if (!WriteToImageFile(metadata_file, *exported.get())) {\n        LOG(ERROR) << \"Unable to save new metadata\";\n        return false;\n    }\n\n    return true;\n}\n\nbool RemoveAllMetadata(const std::string& dir) {\n    auto metadata_file = GetMetadataFile(dir);\n    std::string err;\n    if (!android::base::RemoveFileIfExists(metadata_file, &err)) {\n        LOG(ERROR) << \"Could not remove metadata file: \" << err;\n        return false;\n    }\n    return true;\n}\n\nbool FillPartitionExtents(MetadataBuilder* builder, Partition* partition, SplitFiemap* file,\n                          uint64_t partition_size) {\n    auto block_device = android::base::Basename(GetDevicePathForFile(file));\n\n    uint64_t sectors_needed = partition_size / LP_SECTOR_SIZE;\n    for (const auto& extent : file->extents()) {\n        if (extent.fe_length % LP_SECTOR_SIZE != 0) {\n            LOG(ERROR) << \"Extent is not sector-aligned: \" << extent.fe_length;\n            return false;\n        }\n        if (extent.fe_physical % LP_SECTOR_SIZE != 0) {\n            LOG(ERROR) << \"Extent physical sector is not sector-aligned: \" << extent.fe_physical;\n            return false;\n        }\n\n        uint64_t num_sectors =\n                std::min(static_cast<uint64_t>(extent.fe_length / LP_SECTOR_SIZE), sectors_needed);\n        if (!num_sectors || !sectors_needed) {\n            // This should never happen, but we include it just in case. It would\n            // indicate that the last filesystem block had multiple extents.\n            LOG(WARNING) << \"FiemapWriter allocated extra blocks\";\n            break;\n        }\n\n        uint64_t physical_sector = extent.fe_physical / LP_SECTOR_SIZE;\n        if (!builder->AddLinearExtent(partition, block_device, num_sectors, physical_sector)) {\n            LOG(ERROR) << \"Could not add extent to lp metadata\";\n            return false;\n        }\n\n        sectors_needed -= num_sectors;\n    }\n    return true;\n}\n\nbool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name) {\n    if (!MetadataExists(metadata_dir)) {\n        return true;\n    }\n    auto metadata = OpenMetadata(metadata_dir);\n    if (!metadata) {\n        return false;\n    }\n\n    PartitionOpener opener;\n    auto builder = MetadataBuilder::New(*metadata.get(), &opener);\n    if (!builder) {\n        return false;\n    }\n    builder->RemovePartition(partition_name);\n    return SaveMetadata(builder.get(), metadata_dir);\n}\n\nbool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,\n                    SplitFiemap* file, uint64_t partition_size, bool readonly) {\n    auto builder = OpenOrCreateMetadata(metadata_dir, file);\n    if (!builder) {\n        return false;\n    }\n    auto partition = builder->FindPartition(partition_name);\n    if (!partition) {\n        int attrs = 0;\n        if (readonly) attrs |= LP_PARTITION_ATTR_READONLY;\n\n        if ((partition = builder->AddPartition(partition_name, attrs)) == nullptr) {\n            LOG(ERROR) << \"Could not add partition \" << partition_name << \" to metadata\";\n            return false;\n        }\n    }\n    partition->RemoveExtents();\n\n    if (!FillPartitionExtents(builder.get(), partition, file, partition_size)) {\n        return false;\n    }\n    return SaveMetadata(builder.get(), metadata_dir);\n}\n\nbool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,\n                   uint32_t attributes) {\n    auto metadata = OpenMetadata(metadata_dir);\n    if (!metadata) {\n        return false;\n    }\n    auto builder = MetadataBuilder::New(*metadata.get());\n    if (!builder) {\n        return false;\n    }\n    auto partition = builder->FindPartition(partition_name);\n    if (!partition) {\n        return false;\n    }\n    partition->set_attributes(partition->attributes() | attributes);\n    return SaveMetadata(builder.get(), metadata_dir);\n}\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/metadata.h",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <stdint.h>\n\n#include <memory>\n#include <string>\n\n#include <libfiemap/split_fiemap_writer.h>\n#include <liblp/builder.h>\n#include <liblp/liblp.h>\n\nnamespace android {\nnamespace fiemap {\n\nbool MetadataExists(const std::string& metadata_dir);\nstd::unique_ptr<android::fs_mgr::LpMetadata> OpenMetadata(const std::string& metadata_dir);\nbool UpdateMetadata(const std::string& metadata_dir, const std::string& partition_name,\n                    SplitFiemap* file, uint64_t partition_size, bool readonly);\nbool AddAttributes(const std::string& metadata_dir, const std::string& partition_name,\n                   uint32_t attributes);\nbool RemoveImageMetadata(const std::string& metadata_dir, const std::string& partition_name);\nbool RemoveAllMetadata(const std::string& dir);\n\nbool FillPartitionExtents(android::fs_mgr::MetadataBuilder* builder,\n                          android::fs_mgr::Partition* partition, android::fiemap::SplitFiemap* file,\n                          uint64_t partition_size);\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/passthrough.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <libfiemap/image_manager.h>\n\nnamespace android {\nnamespace fiemap {\n\nstd::unique_ptr<IImageManager> IImageManager::Open(const std::string& dir_prefix,\n                                                   const std::chrono::milliseconds& timeout_ms,\n                                                   const DeviceInfo& device_info) {\n    (void)timeout_ms;\n    return ImageManager::Open(dir_prefix, device_info);\n}\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/split_fiemap_writer.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <libfiemap/split_fiemap_writer.h>\n\n#include <fcntl.h>\n#include <stdint.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace fiemap {\n\nusing android::base::unique_fd;\n\n// We use a four-digit suffix at the end of filenames.\nstatic const size_t kMaxFilePieces = 500;\n\nstd::unique_ptr<SplitFiemap> SplitFiemap::Create(const std::string& file_path, uint64_t file_size,\n                                                 uint64_t max_piece_size,\n                                                 ProgressCallback progress) {\n    std::unique_ptr<SplitFiemap> ret;\n    if (!Create(file_path, file_size, max_piece_size, &ret, progress).is_ok()) {\n        return nullptr;\n    }\n    return ret;\n}\n\nFiemapStatus SplitFiemap::Create(const std::string& file_path, uint64_t file_size,\n                                 uint64_t max_piece_size, std::unique_ptr<SplitFiemap>* out_val,\n                                 ProgressCallback progress) {\n    out_val->reset();\n\n    if (!file_size) {\n        LOG(ERROR) << \"Cannot create a fiemap for a 0-length file: \" << file_path;\n        return FiemapStatus::Error();\n    }\n\n    if (!max_piece_size) {\n        auto status = DetermineMaximumFileSize(file_path, &max_piece_size);\n        if (!status.is_ok()) {\n            LOG(ERROR) << \"Could not determine maximum file size for \" << file_path;\n            return status;\n        }\n    }\n\n    // Remove any existing file.\n    RemoveSplitFiles(file_path);\n\n    // Call |progress| only when the total percentage would significantly change.\n    int permille = -1;\n    uint64_t total_bytes_written = 0;\n    auto on_progress = [&](uint64_t written, uint64_t) -> bool {\n        uint64_t actual_written = total_bytes_written + written;\n        int new_permille = (actual_written * 1000) / file_size;\n        if (new_permille != permille && actual_written < file_size) {\n            if (progress && !progress(actual_written, file_size)) {\n                return false;\n            }\n            permille = new_permille;\n        }\n        return true;\n    };\n    std::unique_ptr<SplitFiemap> out(new SplitFiemap());\n    out->creating_ = true;\n    out->list_file_ = file_path;\n\n    // Create the split files.\n    uint64_t remaining_bytes = file_size;\n    while (remaining_bytes) {\n        if (out->files_.size() >= kMaxFilePieces) {\n            LOG(ERROR) << \"Requested size \" << file_size << \" created too many split files\";\n            out.reset();\n            return FiemapStatus::Error();\n        }\n        std::string chunk_path =\n                android::base::StringPrintf(\"%s.%04d\", file_path.c_str(), (int)out->files_.size());\n        uint64_t chunk_size = std::min(max_piece_size, remaining_bytes);\n        FiemapUniquePtr writer;\n        auto status = FiemapWriter::Open(chunk_path, chunk_size, &writer, true, on_progress);\n        if (!status.is_ok()) {\n            out.reset();\n            return status;\n        }\n\n        // To make sure the alignment doesn't create too much inconsistency, we\n        // account the *actual* size, not the requested size.\n        total_bytes_written += writer->size();\n\n        // writer->size() is block size aligned and could be bigger than remaining_bytes\n        // If remaining_bytes is bigger, set remaining_bytes to 0 to avoid underflow error.\n        remaining_bytes = remaining_bytes > writer->size() ? (remaining_bytes - writer->size()) : 0;\n\n        out->AddFile(std::move(writer));\n    }\n\n    // Create the split file list.\n    unique_fd fd(open(out->list_file_.c_str(), O_CREAT | O_WRONLY | O_CLOEXEC, 0660));\n    if (fd < 0) {\n        PLOG(ERROR) << \"Failed to open \" << file_path;\n        out.reset();\n        return FiemapStatus::FromErrno(errno);\n    }\n\n    for (const auto& writer : out->files_) {\n        std::string line = android::base::Basename(writer->file_path()) + \"\\n\";\n        if (!android::base::WriteFully(fd, line.data(), line.size())) {\n            PLOG(ERROR) << \"Write failed \" << file_path;\n            out.reset();\n            return FiemapStatus::FromErrno(errno);\n        }\n    }\n    fsync(fd.get());\n\n    // Unset this bit, so we don't unlink on destruction.\n    out->creating_ = false;\n    *out_val = std::move(out);\n    return FiemapStatus::Ok();\n}\n\nstd::unique_ptr<SplitFiemap> SplitFiemap::Open(const std::string& file_path) {\n    std::vector<std::string> files;\n    if (!GetSplitFileList(file_path, &files)) {\n        return nullptr;\n    }\n\n    std::unique_ptr<SplitFiemap> out(new SplitFiemap());\n    out->list_file_ = file_path;\n\n    for (const auto& file : files) {\n        auto writer = FiemapWriter::Open(file, 0, false);\n        if (!writer) {\n            // Error was logged in Open().\n            return nullptr;\n        }\n        out->AddFile(std::move(writer));\n    }\n    return out;\n}\n\nbool SplitFiemap::GetSplitFileList(const std::string& file_path, std::vector<std::string>* list) {\n    // This is not the most efficient thing, but it is simple and recovering\n    // the fiemap/fibmap is much more expensive.\n    std::string contents;\n    if (!android::base::ReadFileToString(file_path, &contents, true)) {\n        PLOG(ERROR) << \"Error reading file: \" << file_path;\n        return false;\n    }\n\n    std::vector<std::string> names = android::base::Split(contents, \"\\n\");\n    std::string dir = android::base::Dirname(file_path);\n    for (const auto& name : names) {\n        if (!name.empty()) {\n            list->emplace_back(dir + \"/\" + name);\n        }\n    }\n    return true;\n}\n\nbool SplitFiemap::RemoveSplitFiles(const std::string& file_path, std::string* message) {\n    // Early exit if this does not exist, and do not report an error.\n    if (access(file_path.c_str(), F_OK) && errno == ENOENT) {\n        return true;\n    }\n\n    bool ok = true;\n    std::vector<std::string> files;\n    if (GetSplitFileList(file_path, &files)) {\n        for (const auto& file : files) {\n            if (access(file.c_str(), F_OK) != 0 && (errno == ENOENT || errno == ENAMETOOLONG)) {\n                continue;\n            }\n            truncate(file.c_str(), 0);\n            ok &= android::base::RemoveFileIfExists(file, message);\n        }\n    }\n    truncate(file_path.c_str(), 0);\n    ok &= android::base::RemoveFileIfExists(file_path, message);\n    sync();\n    return ok;\n}\n\nbool SplitFiemap::HasPinnedExtents() const {\n    for (const auto& file : files_) {\n        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {\n            return false;\n        }\n    }\n    return true;\n}\n\nconst std::vector<struct fiemap_extent>& SplitFiemap::extents() {\n    if (extents_.empty()) {\n        for (const auto& file : files_) {\n            const auto& extents = file->extents();\n            extents_.insert(extents_.end(), extents.begin(), extents.end());\n        }\n    }\n    return extents_;\n}\n\nbool SplitFiemap::Write(const void* data, uint64_t bytes) {\n    // Open the current file.\n    FiemapWriter* file = files_[cursor_index_].get();\n\n    const uint8_t* data_ptr = reinterpret_cast<const uint8_t*>(data);\n    uint64_t bytes_remaining = bytes;\n    while (bytes_remaining) {\n        // How many bytes can we write into the current file?\n        uint64_t file_bytes_left = file->size() - cursor_file_pos_;\n        if (!file_bytes_left) {\n            if (cursor_index_ == files_.size() - 1) {\n                LOG(ERROR) << \"write past end of file requested\";\n                return false;\n            }\n\n            // No space left in the current file, but we have more files to\n            // use, so prep the next one.\n            cursor_fd_ = {};\n            cursor_file_pos_ = 0;\n            file = files_[++cursor_index_].get();\n            file_bytes_left = file->size();\n        }\n\n        // Open the current file if it's not open.\n        if (cursor_fd_ < 0) {\n            cursor_fd_.reset(open(file->file_path().c_str(), O_CLOEXEC | O_WRONLY));\n            if (cursor_fd_ < 0) {\n                PLOG(ERROR) << \"open failed: \" << file->file_path();\n                return false;\n            }\n            CHECK(cursor_file_pos_ == 0);\n        }\n\n        if (!FiemapWriter::HasPinnedExtents(file->file_path())) {\n            LOG(ERROR) << \"file is no longer pinned: \" << file->file_path();\n            return false;\n        }\n\n        uint64_t bytes_to_write = std::min(file_bytes_left, bytes_remaining);\n        if (!android::base::WriteFully(cursor_fd_, data_ptr, bytes_to_write)) {\n            PLOG(ERROR) << \"write failed: \" << file->file_path();\n            return false;\n        }\n        data_ptr += bytes_to_write;\n        bytes_remaining -= bytes_to_write;\n        cursor_file_pos_ += bytes_to_write;\n    }\n\n    // If we've reached the end of the current file, close it.\n    if (cursor_file_pos_ == file->size()) {\n        cursor_fd_ = {};\n    }\n    return true;\n}\n\nbool SplitFiemap::Flush() {\n    for (const auto& file : files_) {\n        unique_fd fd(open(file->file_path().c_str(), O_RDONLY | O_CLOEXEC));\n        if (fd < 0) {\n            PLOG(ERROR) << \"open failed: \" << file->file_path();\n            return false;\n        }\n        if (fsync(fd)) {\n            PLOG(ERROR) << \"fsync failed: \" << file->file_path();\n            return false;\n        }\n    }\n    return true;\n}\n\nSplitFiemap::~SplitFiemap() {\n    if (!creating_) {\n        return;\n    }\n\n    // We failed to finish creating, so unlink everything.\n    unlink(list_file_.c_str());\n    for (auto&& file : files_) {\n        std::string path = file->file_path();\n        file = nullptr;\n\n        unlink(path.c_str());\n    }\n}\n\nvoid SplitFiemap::AddFile(FiemapUniquePtr&& file) {\n    total_size_ += file->size();\n    files_.emplace_back(std::move(file));\n}\n\nuint32_t SplitFiemap::block_size() const {\n    return files_[0]->block_size();\n}\n\nconst std::string& SplitFiemap::bdev_path() const {\n    return files_[0]->bdev_path();\n}\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/utility.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"utility.h\"\n\n#include <stdint.h>\n#include <sys/stat.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <sys/vfs.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <libfiemap/fiemap_writer.h>\n\nnamespace android {\nnamespace fiemap {\n\nusing namespace std::string_literals;\nusing android::base::unique_fd;\n\nstatic constexpr char kUserdataDevice[] = \"/dev/block/by-name/userdata\";\n\nFiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result) {\n    // Create the smallest file possible (one block).\n    FiemapUniquePtr writer;\n    auto status = FiemapWriter::Open(file_path, 1, &writer);\n    if (!status.is_ok()) {\n        return status;\n    }\n\n    *result = 0;\n    switch (writer->fs_type()) {\n        case EXT4_SUPER_MAGIC:\n            // The minimum is 16GiB, so just report that. If we wanted we could parse the\n            // superblock and figure out if 64-bit support is enabled.\n            *result = 17179869184ULL;\n            break;\n        case F2FS_SUPER_MAGIC:\n            // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt\n            // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.\n            *result = 4329690886144ULL;\n            break;\n        case MSDOS_SUPER_MAGIC:\n            // 4GB-1, which we want aligned to the block size.\n            *result = 4294967295;\n            *result -= (*result % writer->block_size());\n            break;\n        default:\n            LOG(ERROR) << \"Unknown file system type: \" << writer->fs_type();\n            break;\n    }\n\n    // Close and delete the temporary file.\n    writer = nullptr;\n    unlink(file_path.c_str());\n\n    return FiemapStatus::Ok();\n}\n\n// Given a SplitFiemap, this returns a device path that will work during first-\n// stage init (i.e., its path can be found by InitRequiredDevices).\nstd::string GetDevicePathForFile(SplitFiemap* file) {\n    auto bdev_path = file->bdev_path();\n\n    struct stat userdata, given;\n    if (!stat(bdev_path.c_str(), &given) && !stat(kUserdataDevice, &userdata)) {\n        if (S_ISBLK(given.st_mode) && S_ISBLK(userdata.st_mode) &&\n            given.st_rdev == userdata.st_rdev) {\n            return kUserdataDevice;\n        }\n    }\n    return bdev_path;\n}\n\nstd::string JoinPaths(const std::string& dir, const std::string& file) {\n    if (android::base::EndsWith(dir, \"/\")) {\n        return dir + file;\n    }\n    return dir + \"/\" + file;\n}\n\nbool F2fsPinBeforeAllocate(int file_fd, bool* supported) {\n    struct stat st;\n    if (fstat(file_fd, &st) < 0) {\n        PLOG(ERROR) << \"stat failed\";\n        return false;\n    }\n    std::string bdev;\n    if (!BlockDeviceToName(major(st.st_dev), minor(st.st_dev), &bdev)) {\n        LOG(ERROR) << \"Failed to get block device name for \" << major(st.st_dev) << \":\"\n                   << minor(st.st_dev);\n        return false;\n    }\n\n    std::string contents;\n    std::string feature_file = \"/sys/fs/f2fs/\" + bdev + \"/features\";\n    if (!android::base::ReadFileToString(feature_file, &contents)) {\n        PLOG(ERROR) << \"read failed: \" << feature_file;\n        return false;\n    }\n    contents = android::base::Trim(contents);\n\n    auto features = android::base::Split(contents, \", \");\n    auto iter = std::find(features.begin(), features.end(), \"pin_file\"s);\n    *supported = (iter != features.end());\n    return true;\n}\n\nbool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {\n    // The symlinks in /sys/dev/block point to the block device node under /sys/device/..\n    // The directory name in the target corresponds to the name of the block device. We use\n    // that to extract the block device name.\n    // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as\n    // follows.\n    //    1:0 -> ../../devices/virtual/block/ram0\n    std::string sysfs_path = ::android::base::StringPrintf(\"/sys/dev/block/%u:%u\", major, minor);\n    std::string sysfs_bdev;\n\n    if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {\n        PLOG(ERROR) << \"Failed to read link at: \" << sysfs_path;\n        return false;\n    }\n\n    *bdev_name = ::android::base::Basename(sysfs_bdev);\n    // Check that the symlink doesn't point to itself.\n    if (sysfs_bdev == *bdev_name) {\n        LOG(ERROR) << \"Malformed symlink for block device: \" << sysfs_bdev;\n        return false;\n    }\n\n    return true;\n}\n\nbool FilesystemHasReliablePinning(const std::string& file, bool* supported) {\n    struct statfs64 sfs;\n    if (statfs64(file.c_str(), &sfs)) {\n        PLOG(ERROR) << \"statfs failed: \" << file;\n        return false;\n    }\n    if (sfs.f_type != F2FS_SUPER_MAGIC) {\n        *supported = true;\n        return true;\n    }\n\n    unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));\n    if (fd < 0) {\n        PLOG(ERROR) << \"open failed: \" << file;\n        return false;\n    }\n    return F2fsPinBeforeAllocate(fd, supported);\n}\n\nbool IsSubdir(const std::string& child, const std::string& parent) {\n    // Precondition: both are absolute paths.\n    CHECK(android::base::StartsWith(child, \"/\")) << \"Not an absolute path: \" << child;\n    CHECK(android::base::StartsWith(parent, \"/\")) << \"Not an absolute path: \" << parent;\n\n    // Remove extraneous \"/\" at the end.\n    std::string_view child_sv = child;\n    while (child_sv != \"/\" && android::base::ConsumeSuffix(&child_sv, \"/\"))\n        ;\n\n    std::string_view parent_sv = parent;\n    while (parent_sv != \"/\" && android::base::ConsumeSuffix(&parent_sv, \"/\"))\n        ;\n\n    // IsSubdir(anything, \"/\") => true\n    if (parent_sv == \"/\") return true;\n\n    // IsSubdir(\"/foo\", \"/foo\") => true\n    if (parent_sv == child_sv) return true;\n\n    // IsSubdir(\"/foo/bar\", \"/foo\") => true\n    // IsSubdir(\"/foo-bar\", \"/foo\") => false\n    return android::base::StartsWith(child_sv, std::string(parent_sv) + \"/\");\n}\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfiemap/utility.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#include <string>\n\n#include <libfiemap/split_fiemap_writer.h>\n\nnamespace android {\nnamespace fiemap {\n\n// Given a file that will be created, determine the maximum size its containing\n// filesystem allows. Note this is a theoretical maximum size; free space is\n// ignored entirely.\nFiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result);\n\n// Given a SplitFiemap, this returns a device path that will work during first-\n// stage init (i.e., its path can be found by InitRequiredDevices).\nstd::string GetDevicePathForFile(android::fiemap::SplitFiemap* file);\n\n// Combine two path components into a single path.\nstd::string JoinPaths(const std::string& dir, const std::string& file);\n\n// Given a file within an F2FS filesystem, return whether or not the filesystem\n// supports the \"pin_file\" feature, which requires pinning before fallocation.\nbool F2fsPinBeforeAllocate(int file_fd, bool* supported);\n\n// Given a major/minor device number, return its canonical name such that\n// /dev/block/<name> resolves to the device.\nbool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name);\n\n// This is the same as F2fsPinBeforeAllocate, however, it will return true\n// (and supported = true) for non-f2fs filesystems. It is intended to be used\n// in conjunction with ImageManager to reject image requests for reliable use\n// cases (such as snapshots or adb remount).\nbool FilesystemHasReliablePinning(const std::string& file, bool* supported);\n\n// Crude implementation to check if |child| is a subdir of |parent|.\n// Assume both are absolute paths.\nbool IsSubdir(const std::string& child, const std::string& parent);\n\n}  // namespace fiemap\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/Android.bp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    // See: http://go/android-license-faq\n    // A large-scale-change added 'default_applicable_licenses' to import\n    // all of the 'license_kinds' from \"system_core_fs_mgr_license\"\n    // to get the below license kinds:\n    //   SPDX-license-identifier-Apache-2.0\n    //   SPDX-license-identifier-MIT\n    default_applicable_licenses: [\"system_core_fs_mgr_license\"],\n}\n\ncc_library_static {\n    name: \"libfs_avb\",\n    defaults: [\"fs_mgr_defaults\"],\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    host_supported: true,\n    export_include_dirs: [\"include\"],\n    srcs: [\n        \"avb_ops.cpp\",\n        \"avb_util.cpp\",\n        \"fs_avb.cpp\",\n        \"fs_avb_util.cpp\",\n        \"types.cpp\",\n        \"util.cpp\",\n    ],\n    static_libs: [\n        \"libavb\",\n        \"libdm\",\n        \"libgsi\",\n        \"libfstab\",\n    ],\n    export_static_lib_headers: [\n        \"libfstab\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcrypto\",\n    ],\n    target: {\n        darwin: {\n            enabled: false,\n        },\n    },\n}\n\ncc_defaults {\n    name: \"libfs_avb_host_test_defaults\",\n    required: [\n        \"avbtool\",\n    ],\n    data: [\n        \"tests/data/*\",\n    ],\n    static_libs: [\n        \"libavb\",\n        \"libavb_host_sysdeps\",\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libfs_avb\",\n        \"libfstab\",\n        \"libgtest_host\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libchrome\",\n        \"libcrypto\",\n    ],\n    target: {\n        darwin: {\n            enabled: false,\n        },\n    },\n    cflags: [\n        \"-DHOST_TEST\",\n    ],\n}\n\ncc_library_host_static {\n    name: \"libfs_avb_test_util\",\n    defaults: [\"libfs_avb_host_test_defaults\"],\n    srcs: [\n        \"tests/fs_avb_test_util.cpp\",\n    ],\n}\n\ncc_test_host {\n    name: \"libfs_avb_test\",\n    defaults: [\"libfs_avb_host_test_defaults\"],\n    test_suites: [\"general-tests\"],\n    test_options: {\n        unit_test: true,\n    },\n    static_libs: [\n        \"libfs_avb_test_util\",\n    ],\n    compile_multilib: \"first\",\n    data: [\n        \":avbtool\",\n        \":fec\",\n    ],\n    srcs: [\n        \"tests/basic_test.cpp\",\n        \"tests/fs_avb_test.cpp\",\n        \"tests/fs_avb_util_test.cpp\",\n    ],\n}\n\ncc_test_host {\n    name: \"libfs_avb_internal_test\",\n    defaults: [\"libfs_avb_host_test_defaults\"],\n    test_suites: [\"general-tests\"],\n    test_options: {\n        unit_test: true,\n    },\n    static_libs: [\n        \"libfs_avb_test_util\",\n    ],\n    compile_multilib: \"first\",\n    data: [\n        \":avbtool\",\n        \":fec\",\n    ],\n    srcs: [\n        \"avb_util.cpp\",\n        \"util.cpp\",\n        \"tests/avb_util_test.cpp\",\n        \"tests/util_test.cpp\",\n    ],\n}\n\ncc_test {\n    name: \"libfs_avb_device_test\",\n    test_suites: [\"device-tests\"],\n    require_root: true,\n    static_libs: [\n        \"libavb\",\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libfs_avb\",\n        \"libfstab\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcrypto\",\n    ],\n    srcs: [\n        \"tests/fs_avb_device_test.cpp\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n}\n"
  },
  {
    "path": "fs_mgr/libfs_avb/avb_ops.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use, copy,\n * modify, merge, publish, distribute, sublicense, and/or sell copies\n * of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#include \"avb_ops.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n\n#include <string>\n\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <libavb/libavb.h>\n#include <libdm/dm.h>\n#include <utils/Compat.h>\n\n#include \"util.h\"\n\nusing namespace std::literals;\n\nnamespace android {\nnamespace fs_mgr {\n\nstatic AvbIOResult read_from_partition(AvbOps* ops, const char* partition, int64_t offset,\n                                       size_t num_bytes, void* buffer, size_t* out_num_read) {\n    return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->ReadFromPartition(\n            partition, offset, num_bytes, buffer, out_num_read);\n}\n\nstatic AvbIOResult no_op_read_rollback_index(AvbOps* ops ATTRIBUTE_UNUSED,\n                                            size_t rollback_index_location ATTRIBUTE_UNUSED,\n                                            uint64_t* out_rollback_index) {\n    // rollback_index has been checked in bootloader phase.\n    // In user-space, returns the smallest value 0 to pass the check.\n    *out_rollback_index = 0;\n    return AVB_IO_RESULT_OK;\n}\n\nstatic AvbIOResult no_op_validate_vbmeta_public_key(\n        AvbOps* ops ATTRIBUTE_UNUSED, const uint8_t* public_key_data ATTRIBUTE_UNUSED,\n        size_t public_key_length ATTRIBUTE_UNUSED,\n        const uint8_t* public_key_metadata ATTRIBUTE_UNUSED,\n        size_t public_key_metadata_length ATTRIBUTE_UNUSED, bool* out_is_trusted) {\n    // vbmeta public key has been checked in bootloader phase.\n    // In user-space, returns true to pass the check.\n    //\n    // Addtionally, user-space should check\n    // androidboot.vbmeta.{hash_alg, size, digest} against the digest\n    // of all vbmeta images after invoking avb_slot_verify().\n    *out_is_trusted = true;\n    return AVB_IO_RESULT_OK;\n}\n\nstatic AvbIOResult no_op_read_is_device_unlocked(AvbOps* ops ATTRIBUTE_UNUSED,\n                                                bool* out_is_unlocked) {\n    // The function is for bootloader to update the value into\n    // androidboot.vbmeta.device_state in kernel cmdline.\n    // In user-space, returns true as we don't need to update it anymore.\n    *out_is_unlocked = true;\n    return AVB_IO_RESULT_OK;\n}\n\nstatic AvbIOResult no_op_get_unique_guid_for_partition(AvbOps* ops ATTRIBUTE_UNUSED,\n                                                      const char* partition ATTRIBUTE_UNUSED,\n                                                      char* guid_buf, size_t guid_buf_size) {\n    // The function is for bootloader to set the correct UUID\n    // for a given partition in kernel cmdline.\n    // In user-space, returns a faking one as we don't need to update\n    // it anymore.\n    snprintf(guid_buf, guid_buf_size, \"1234-fake-guid-for:%s\", partition);\n    return AVB_IO_RESULT_OK;\n}\n\nstatic AvbIOResult get_size_of_partition(AvbOps* ops ATTRIBUTE_UNUSED,\n                                         const char* partition ATTRIBUTE_UNUSED,\n                                         uint64_t* out_size_num_byte) {\n    return FsManagerAvbOps::GetInstanceFromAvbOps(ops)->GetSizeOfPartition(partition,\n                                                                           out_size_num_byte);\n}\n\n// Converts a partition name (with ab_suffix) to the corresponding mount point.\n// e.g., \"system_a\" => \"/system\",\n// e.g., \"vendor_a\" => \"/vendor\",\nstatic std::string DeriveMountPoint(const std::string& partition_name,\n                                    const std::string& ab_suffix) {\n    std::string mount_point(partition_name);\n    auto found = partition_name.rfind(ab_suffix);\n    if (found != std::string::npos) {\n        mount_point.erase(found);  // converts system_a => system\n    }\n\n    return \"/\" + mount_point;\n}\n\nFsManagerAvbOps::FsManagerAvbOps(const std::string& slot_suffix) {\n    // We only need to provide the implementation of read_from_partition()\n    // operation since that's all what is being used by the avb_slot_verify().\n    // Other I/O operations are only required in bootloader but not in\n    // user-space so we set them as no-op operations. Also zero the entire\n    // struct so operations added in the future will be set to NULL.\n    memset(&avb_ops_, 0, sizeof(AvbOps));\n    avb_ops_.read_from_partition = read_from_partition;\n    avb_ops_.read_rollback_index = no_op_read_rollback_index;\n    avb_ops_.validate_vbmeta_public_key = no_op_validate_vbmeta_public_key;\n    avb_ops_.read_is_device_unlocked = no_op_read_is_device_unlocked;\n    avb_ops_.get_unique_guid_for_partition = no_op_get_unique_guid_for_partition;\n    avb_ops_.get_size_of_partition = get_size_of_partition;\n\n    // Sets user_data for GetInstanceFromAvbOps() to convert it back to FsManagerAvbOps.\n    avb_ops_.user_data = this;\n\n    slot_suffix_ = slot_suffix;\n    if (slot_suffix_.empty()) {\n        slot_suffix_ = fs_mgr_get_slot_suffix();\n    }\n}\n\n// Given a partition name (with ab_suffix), e.g., system_a, returns the corresponding\n// dm-linear path for it. e.g., /dev/block/dm-0. If not found, returns an empty string.\n// This assumes that the prefix of the partition name and the mount point are the same.\n// e.g., partition vendor_a is mounted under /vendor, product_a is mounted under /product, etc.\n// This might not be true for some special fstab files, e.g., fstab.postinstall.\n// But it's good enough for the default fstab. Also note that the logical path is a\n// fallback solution when the physical path (/dev/block/by-name/<partition>) cannot be found.\nstd::string FsManagerAvbOps::GetLogicalPath(const std::string& partition_name) {\n    if (fstab_.empty() && !ReadDefaultFstab(&fstab_)) {\n        return \"\";\n    }\n\n    const auto mount_point = DeriveMountPoint(partition_name, slot_suffix_);\n    if (mount_point.empty()) return \"\";\n\n    auto fstab_entry = GetEntryForMountPoint(&fstab_, mount_point);\n    if (!fstab_entry) return \"\";\n\n    std::string device_path;\n    if (fstab_entry->fs_mgr_flags.logical) {\n        dm::DeviceMapper& dm = dm::DeviceMapper::Instance();\n        if (!dm.GetDmDevicePathByName(fstab_entry->blk_device, &device_path)) {\n            LERROR << \"Failed to resolve logical device path for: \" << fstab_entry->blk_device;\n            return \"\";\n        }\n        return device_path;\n    }\n\n    return \"\";\n}\nstd::string FsManagerAvbOps::GetPartitionPath(const char* partition) {\n    std::string path = \"/dev/block/by-name/\"s + partition;\n    if (!WaitForFile(path, 1s)) {\n        LERROR << \"Device path not found: \" << path;\n        // Falls back to logical path if the physical path is not found.\n        // This mostly only works for emulator (no bootloader). Because in normal\n        // device, bootloader is unable to read logical partitions. So if libavb in\n        // the bootloader failed to read a physical partition, it will failed to boot\n        // the HLOS and we won't reach the code here.\n        path = GetLogicalPath(partition);\n        if (path.empty() || !WaitForFile(path, 1s)) return \"\";\n    }\n    return path;\n}\n\nAvbIOResult FsManagerAvbOps::GetSizeOfPartition(const char* partition,\n                                                uint64_t* out_size_num_byte) {\n    const auto path = GetPartitionPath(partition);\n    if (path.empty()) {\n        return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;\n    }\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) {\n        PERROR << \"Failed to open \" << path;\n        return AVB_IO_RESULT_ERROR_IO;\n    }\n    int err = ioctl(fd, BLKGETSIZE64, out_size_num_byte);\n    if (err) {\n        *out_size_num_byte = 0;\n        return AVB_IO_RESULT_ERROR_IO;\n    }\n    return AVB_IO_RESULT_OK;\n}\n\nAvbIOResult FsManagerAvbOps::ReadFromPartition(const char* partition, int64_t offset,\n                                               size_t num_bytes, void* buffer,\n                                               size_t* out_num_read) {\n    std::string path = GetPartitionPath(partition);\n    if (path.empty()) {\n        return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;\n    }\n\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) {\n        PERROR << \"Failed to open \" << path;\n        return AVB_IO_RESULT_ERROR_IO;\n    }\n\n    // If offset is negative, interprets its absolute value as the\n    //  number of bytes from the end of the partition.\n    if (offset < 0) {\n        off64_t total_size = lseek64(fd, 0, SEEK_END);\n        if (total_size == -1) {\n            PERROR << \"Failed to lseek64 to end of the partition\";\n            return AVB_IO_RESULT_ERROR_IO;\n        }\n        offset = total_size + offset;\n        // Repositions the offset to the beginning.\n        if (lseek64(fd, 0, SEEK_SET) == -1) {\n            PERROR << \"Failed to lseek64 to the beginning of the partition\";\n            return AVB_IO_RESULT_ERROR_IO;\n        }\n    }\n\n    // On Linux, we never get partial reads from block devices (except\n    // for EOF).\n    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, buffer, num_bytes, offset));\n    if (num_read < 0 || (size_t)num_read != num_bytes) {\n        PERROR << \"Failed to read \" << num_bytes << \" bytes from \" << path << \" offset \" << offset;\n        return AVB_IO_RESULT_ERROR_IO;\n    }\n\n    if (out_num_read != nullptr) {\n        *out_num_read = num_read;\n    }\n\n    return AVB_IO_RESULT_OK;\n}\n\nAvbSlotVerifyResult FsManagerAvbOps::AvbSlotVerify(const std::string& ab_suffix,\n                                                   AvbSlotVerifyFlags flags,\n                                                   std::vector<VBMetaData>* out_vbmeta_images) {\n    // Invokes avb_slot_verify() to load and verify all vbmeta images.\n    // Sets requested_partitions to nullptr as it's to copy the contents\n    // of HASH partitions into handle>avb_slot_data_, which is not required as\n    // fs_mgr only deals with HASHTREE partitions.\n    const char* requested_partitions[] = {nullptr};\n\n    // Local resource to store vbmeta images from avb_slot_verify();\n    AvbSlotVerifyData* avb_slot_data;\n\n    // The |hashtree_error_mode| field doesn't matter as it only\n    // influences the generated kernel cmdline parameters.\n    auto verify_result =\n            avb_slot_verify(&avb_ops_, requested_partitions, ab_suffix.c_str(), flags,\n                            AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE, &avb_slot_data);\n\n    if (!avb_slot_data) return verify_result;\n    // Copies avb_slot_data->vbmeta_images[].\n    for (size_t i = 0; i < avb_slot_data->num_vbmeta_images; i++) {\n        out_vbmeta_images->emplace_back(VBMetaData(avb_slot_data->vbmeta_images[i].vbmeta_data,\n                                                   avb_slot_data->vbmeta_images[i].vbmeta_size,\n                                                   avb_slot_data->vbmeta_images[i].partition_name));\n    }\n\n    // Free the local resource.\n    avb_slot_verify_data_free(avb_slot_data);\n\n    return verify_result;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/avb_ops.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Permission is hereby granted, free of charge, to any person\n * obtaining a copy of this software and associated documentation\n * files (the \"Software\"), to deal in the Software without\n * restriction, including without limitation the rights to use, copy,\n * modify, merge, publish, distribute, sublicense, and/or sell copies\n * of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS\n * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN\n * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n * SOFTWARE.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include <fs_avb/types.h>\n#include <fstab/fstab.h>\n#include <libavb/libavb.h>\n\nnamespace android {\nnamespace fs_mgr {\n\n// This class provides C++ bindings to interact with libavb, a small\n// self-contained piece of code that's intended to be used in bootloaders.\n// It mainly contains two functions:\n//   - ReadFromPartition(): to read AVB metadata from a given partition.\n//     It provides the implementation of AvbOps.read_from_partition() when\n//     reading metadata through libavb.\n//   - AvbSlotVerify(): the C++ binding of libavb->avb_slot_verify() to\n//     read and verify the metadata and store it into the out_data parameter.\n//     The caller MUST check the integrity of metadata against the\n//     androidboot.vbmeta.{hash_alg, size, digest} values from /proc/cmdline.\n//     e.g., see class AvbVerifier for more details.\n//\nclass FsManagerAvbOps {\n  public:\n    explicit FsManagerAvbOps(const std::string& slot_suffix = {});\n\n    static FsManagerAvbOps* GetInstanceFromAvbOps(AvbOps* ops) {\n        return reinterpret_cast<FsManagerAvbOps*>(ops->user_data);\n    }\n\n    AvbIOResult ReadFromPartition(const char* partition, int64_t offset, size_t num_bytes,\n                                  void* buffer, size_t* out_num_read);\n    AvbIOResult GetSizeOfPartition(const char* partition, uint64_t* out_size_num_byte);\n\n    AvbSlotVerifyResult AvbSlotVerify(const std::string& ab_suffix, AvbSlotVerifyFlags flags,\n                                      std::vector<VBMetaData>* out_vbmeta_images);\n\n  private:\n    std::string GetLogicalPath(const std::string& partition_name);\n    std::string GetPartitionPath(const char* partition_name);\n    AvbOps avb_ops_;\n    Fstab fstab_;\n    std::string slot_suffix_;\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/avb_util.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"avb_util.h\"\n\n#include <unistd.h>\n\n#include <array>\n#include <sstream>\n\n#include <android-base/logging.h>\n#include <android-base/file.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"util.h\"\n\nusing android::base::Basename;\nusing android::base::ReadFileToString;\nusing android::base::StartsWith;\nusing android::base::unique_fd;\n\nnamespace android {\nnamespace fs_mgr {\n\n// Constructs dm-verity arguments for sending DM_TABLE_LOAD ioctl to kernel.\n// See the following link for more details:\n// https://gitlab.com/cryptsetup/cryptsetup/wikis/DMVerity\nbool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,\n                          const std::string& blk_device, android::dm::DmTable* table) {\n    // Loads androidboot.veritymode from kernel cmdline.\n    std::string verity_mode;\n    if (!fs_mgr_get_boot_config(\"veritymode\", &verity_mode)) {\n        verity_mode = \"enforcing\";  // Defaults to enforcing when it's absent.\n    }\n\n    // Converts veritymode to the format used in kernel.\n    std::string dm_verity_mode;\n    if (verity_mode == \"panicking\") {\n        dm_verity_mode = \"panic_on_corruption\";\n    } else if (verity_mode == \"enforcing\") {\n        dm_verity_mode = \"restart_on_corruption\";\n    } else if (verity_mode == \"logging\") {\n        dm_verity_mode = \"ignore_corruption\";\n    } else if (verity_mode != \"eio\") {  // Default dm_verity_mode is eio.\n        LERROR << \"Unknown androidboot.veritymode: \" << verity_mode;\n        return false;\n    }\n\n    std::ostringstream hash_algorithm;\n    hash_algorithm << hashtree_desc.hash_algorithm;\n\n    android::dm::DmTargetVerity target(\n            0, hashtree_desc.image_size / 512, hashtree_desc.dm_verity_version, blk_device,\n            blk_device, hashtree_desc.data_block_size, hashtree_desc.hash_block_size,\n            hashtree_desc.image_size / hashtree_desc.data_block_size,\n            hashtree_desc.tree_offset / hashtree_desc.hash_block_size, hash_algorithm.str(),\n            hashtree_desc.root_digest, hashtree_desc.salt);\n    if (hashtree_desc.fec_size > 0) {\n        target.UseFec(blk_device, hashtree_desc.fec_num_roots,\n                      hashtree_desc.fec_offset / hashtree_desc.data_block_size,\n                      hashtree_desc.fec_offset / hashtree_desc.data_block_size);\n    }\n    if (!dm_verity_mode.empty()) {\n        target.SetVerityMode(dm_verity_mode);\n    }\n    // Always use ignore_zero_blocks.\n    target.IgnoreZeroBlocks();\n\n    if (hashtree_desc.flags & AVB_HASHTREE_DESCRIPTOR_FLAGS_CHECK_AT_MOST_ONCE) {\n        target.CheckAtMostOnce();\n    }\n\n    LINFO << \"Built verity table: '\" << target.GetParameterString() << \"'\";\n\n    return table->AddTarget(std::make_unique<android::dm::DmTargetVerity>(target));\n}\n\nbool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,\n                           bool wait_for_verity_dev) {\n    android::dm::DmTable table;\n    if (!ConstructVerityTable(hashtree_desc, fstab_entry->blk_device, &table) || !table.valid()) {\n        LERROR << \"Failed to construct verity table.\";\n        return false;\n    }\n    table.set_readonly(true);\n\n    std::chrono::milliseconds timeout = {};\n    if (wait_for_verity_dev) timeout = 1s;\n\n    std::string dev_path;\n    const std::string device_name(GetVerityDeviceName(*fstab_entry));\n    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();\n    if (!dm.CreateDevice(device_name, table, &dev_path, timeout)) {\n        LERROR << \"Couldn't create verity device!\";\n        return false;\n    }\n\n    // Marks the underlying block device as read-only.\n    SetBlockDeviceReadOnly(fstab_entry->blk_device);\n\n    // Updates fstab_rec->blk_device to verity device name.\n    fstab_entry->blk_device = dev_path;\n    return true;\n}\n\nstd::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(\n        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {\n    bool found = false;\n    const uint8_t* desc_partition_name;\n    auto hashtree_desc = std::make_unique<FsAvbHashtreeDescriptor>();\n\n    for (const auto& vbmeta : vbmeta_images) {\n        size_t num_descriptors;\n        std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(\n                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);\n\n        if (!descriptors || num_descriptors < 1) {\n            continue;\n        }\n\n        for (size_t n = 0; n < num_descriptors && !found; n++) {\n            AvbDescriptor desc;\n            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {\n                LWARNING << \"Descriptor[\" << n << \"] is invalid\";\n                continue;\n            }\n            if (desc.tag == AVB_DESCRIPTOR_TAG_HASHTREE) {\n                desc_partition_name =\n                        (const uint8_t*)descriptors[n] + sizeof(AvbHashtreeDescriptor);\n                if (!avb_hashtree_descriptor_validate_and_byteswap(\n                        (AvbHashtreeDescriptor*)descriptors[n], hashtree_desc.get())) {\n                    continue;\n                }\n                if (hashtree_desc->partition_name_len != partition_name.length()) {\n                    continue;\n                }\n                // Notes that desc_partition_name is not NUL-terminated.\n                std::string hashtree_partition_name((const char*)desc_partition_name,\n                                                    hashtree_desc->partition_name_len);\n                if (hashtree_partition_name == partition_name) {\n                    found = true;\n                }\n            }\n        }\n\n        if (found) break;\n    }\n\n    if (!found) {\n        LERROR << \"Hashtree descriptor not found: \" << partition_name;\n        return nullptr;\n    }\n\n    hashtree_desc->partition_name = partition_name;\n\n    const uint8_t* desc_salt = desc_partition_name + hashtree_desc->partition_name_len;\n    hashtree_desc->salt = BytesToHex(desc_salt, hashtree_desc->salt_len);\n\n    const uint8_t* desc_digest = desc_salt + hashtree_desc->salt_len;\n    hashtree_desc->root_digest = BytesToHex(desc_digest, hashtree_desc->root_digest_len);\n\n    return hashtree_desc;\n}\n\nbool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,\n                                   const std::vector<VBMetaData>& vbmeta_images,\n                                   const std::string& ab_suffix,\n                                   const std::string& ab_other_suffix) {\n    // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor\n    // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.\n    std::string partition_name = DeriveAvbPartitionName(*fstab_entry, ab_suffix, ab_other_suffix);\n\n    if (partition_name.empty()) {\n        LERROR << \"partition name is empty, cannot lookup AVB descriptors\";\n        return false;\n    }\n\n    std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =\n            GetHashtreeDescriptor(partition_name, vbmeta_images);\n    if (!hashtree_descriptor) {\n        return false;\n    }\n\n    // Converts HASHTREE descriptor to verity table to load into kernel.\n    // When success, the new device path will be returned, e.g., /dev/block/dm-2.\n    return HashtreeDmVeritySetup(fstab_entry, *hashtree_descriptor, wait_for_verity_dev);\n}\n\n// Converts a AVB partition_name (without A/B suffix) to a device partition name.\n// e.g.,       \"system\" => \"system_a\",\n//       \"system_other\" => \"system_b\".\n//\n// If the device is non-A/B, converts it to a partition name without suffix.\n// e.g.,       \"system\" => \"system\",\n//       \"system_other\" => \"system\".\nstd::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,\n                                         const std::string& ab_suffix,\n                                         const std::string& ab_other_suffix) {\n    bool is_other_slot = false;\n    std::string sanitized_partition_name(avb_partition_name);\n\n    auto other_suffix = sanitized_partition_name.rfind(\"_other\");\n    if (other_suffix != std::string::npos) {\n        sanitized_partition_name.erase(other_suffix);  // converts system_other => system\n        is_other_slot = true;\n    }\n\n    auto append_suffix = is_other_slot ? ab_other_suffix : ab_suffix;\n    return sanitized_partition_name + append_suffix;\n}\n\n// Converts fstab_entry.blk_device (with ab_suffix) to a AVB partition name.\n// e.g., \"/dev/block/by-name/system_a\", slot_select       => \"system\",\n//       \"/dev/block/by-name/system_b\", slot_select_other => \"system_other\".\n//\n// Or for a logical partition (with ab_suffix):\n// e.g., \"system_a\", slot_select       => \"system\",\n//       \"system_b\", slot_select_other => \"system_other\".\nstd::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,\n                                   const std::string& ab_other_suffix) {\n    std::string partition_name;\n    if (fstab_entry.fs_mgr_flags.logical) {\n        partition_name = fstab_entry.logical_partition_name;\n    } else {\n        partition_name = Basename(fstab_entry.blk_device);\n    }\n\n    if (fstab_entry.fs_mgr_flags.slot_select) {\n        auto found = partition_name.rfind(ab_suffix);\n        if (found != std::string::npos) {\n            partition_name.erase(found);  // converts system_a => system\n        }\n    } else if (fstab_entry.fs_mgr_flags.slot_select_other) {\n        auto found = partition_name.rfind(ab_other_suffix);\n        if (found != std::string::npos) {\n            partition_name.erase(found);  // converts system_b => system\n        }\n        partition_name += \"_other\";  // converts system => system_other\n    }\n\n    return partition_name;\n}\n\noff64_t GetTotalSize(int fd) {\n    off64_t saved_current = lseek64(fd, 0, SEEK_CUR);\n    if (saved_current == -1) {\n        PERROR << \"Failed to get current position\";\n        return -1;\n    }\n\n    // lseek64() returns the resulting offset location from the beginning of the file.\n    off64_t total_size = lseek64(fd, 0, SEEK_END);\n    if (total_size == -1) {\n        PERROR << \"Failed to lseek64 to end of the partition\";\n        return -1;\n    }\n\n    // Restores the original offset.\n    if (lseek64(fd, saved_current, SEEK_SET) == -1) {\n        PERROR << \"Failed to lseek64 to the original offset: \" << saved_current;\n    }\n\n    return total_size;\n}\n\nstd::unique_ptr<AvbFooter> GetAvbFooter(int fd) {\n    std::array<uint8_t, AVB_FOOTER_SIZE> footer_buf;\n    auto footer = std::make_unique<AvbFooter>();\n\n    off64_t footer_offset = GetTotalSize(fd) - AVB_FOOTER_SIZE;\n\n    ssize_t num_read =\n            TEMP_FAILURE_RETRY(pread64(fd, footer_buf.data(), AVB_FOOTER_SIZE, footer_offset));\n    if (num_read < 0 || num_read != AVB_FOOTER_SIZE) {\n        PERROR << \"Failed to read AVB footer at offset: \" << footer_offset;\n        return nullptr;\n    }\n\n    if (!avb_footer_validate_and_byteswap((const AvbFooter*)footer_buf.data(), footer.get())) {\n        PERROR << \"AVB footer verification failed at offset \" << footer_offset;\n        return nullptr;\n    }\n\n    return footer;\n}\n\nbool ValidatePublicKeyBlob(const uint8_t* key, size_t length,\n                           const std::string& expected_key_blob) {\n    if (expected_key_blob.empty()) {  // no expectation of the key, return true.\n        return true;\n    }\n    if (expected_key_blob.size() != length) {\n        return false;\n    }\n    if (0 == memcmp(key, expected_key_blob.data(), length)) {\n        return true;\n    }\n    return false;\n}\n\nbool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,\n                           const std::vector<std::string>& allowed_key_paths) {\n    std::string allowed_key_blob;\n    if (key_blob_to_validate.empty()) {\n        LWARNING << \"Failed to validate an empty key\";\n        return false;\n    }\n    for (const auto& path : allowed_key_paths) {\n        if (ReadFileToString(path, &allowed_key_blob)) {\n            if (key_blob_to_validate == allowed_key_blob) return true;\n        }\n    }\n    return false;\n}\n\nVBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,\n                                         const std::string& expected_public_key_blob,\n                                         std::string* out_public_key_data) {\n    const uint8_t* pk_data;\n    size_t pk_len;\n    ::AvbVBMetaVerifyResult vbmeta_ret;\n\n    vbmeta_ret = avb_vbmeta_image_verify(vbmeta.data(), vbmeta.size(), &pk_data, &pk_len);\n\n    if (out_public_key_data != nullptr) {\n        out_public_key_data->clear();\n        if (pk_len > 0) {\n            out_public_key_data->append(reinterpret_cast<const char*>(pk_data), pk_len);\n        }\n    }\n\n    switch (vbmeta_ret) {\n        case AVB_VBMETA_VERIFY_RESULT_OK:\n            if (pk_data == nullptr || pk_len <= 0) {\n                LERROR << vbmeta.partition()\n                       << \": Error verifying vbmeta image: failed to get public key\";\n                return VBMetaVerifyResult::kError;\n            }\n            if (!ValidatePublicKeyBlob(pk_data, pk_len, expected_public_key_blob)) {\n                LERROR << vbmeta.partition() << \": Error verifying vbmeta image: public key used to\"\n                       << \" sign data does not match key in chain descriptor\";\n                return VBMetaVerifyResult::kErrorVerification;\n            }\n            return VBMetaVerifyResult::kSuccess;\n        case AVB_VBMETA_VERIFY_RESULT_OK_NOT_SIGNED:\n        case AVB_VBMETA_VERIFY_RESULT_HASH_MISMATCH:\n        case AVB_VBMETA_VERIFY_RESULT_SIGNATURE_MISMATCH:\n            LERROR << vbmeta.partition() << \": Error verifying vbmeta image: \"\n                   << avb_vbmeta_verify_result_to_string(vbmeta_ret);\n            return VBMetaVerifyResult::kErrorVerification;\n        case AVB_VBMETA_VERIFY_RESULT_INVALID_VBMETA_HEADER:\n            // No way to continue this case.\n            LERROR << vbmeta.partition() << \": Error verifying vbmeta image: invalid vbmeta header\";\n            break;\n        case AVB_VBMETA_VERIFY_RESULT_UNSUPPORTED_VERSION:\n            // No way to continue this case.\n            LERROR << vbmeta.partition()\n                   << \": Error verifying vbmeta image: unsupported AVB version\";\n            break;\n        default:\n            LERROR << \"Unknown vbmeta image verify return value: \" << vbmeta_ret;\n            break;\n    }\n\n    return VBMetaVerifyResult::kError;\n}\n\nstd::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,\n                                             const std::string& expected_public_key_blob,\n                                             std::string* out_public_key_data,\n                                             VBMetaVerifyResult* out_verify_result) {\n    uint64_t vbmeta_offset = 0;\n    uint64_t vbmeta_size = VBMetaData::kMaxVBMetaSize;\n    bool is_vbmeta_partition = StartsWith(partition_name, \"vbmeta\");\n\n    if (out_verify_result) {\n        *out_verify_result = VBMetaVerifyResult::kError;\n    }\n\n    if (!is_vbmeta_partition) {\n        std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);\n        if (!footer) {\n            return nullptr;\n        }\n        vbmeta_offset = footer->vbmeta_offset;\n        vbmeta_size = footer->vbmeta_size;\n    }\n\n    if (vbmeta_size > VBMetaData::kMaxVBMetaSize) {\n        LERROR << \"VbMeta size in footer exceeds kMaxVBMetaSize\";\n        return nullptr;\n    }\n\n    auto vbmeta = std::make_unique<VBMetaData>(vbmeta_size, partition_name);\n    ssize_t num_read = TEMP_FAILURE_RETRY(pread64(fd, vbmeta->data(), vbmeta_size, vbmeta_offset));\n    // Allows partial read for vbmeta partition, because its vbmeta_size is kMaxVBMetaSize.\n    if (num_read < 0 || (!is_vbmeta_partition && static_cast<uint64_t>(num_read) != vbmeta_size)) {\n        PERROR << partition_name << \": Failed to read vbmeta at offset \" << vbmeta_offset\n               << \" with size \" << vbmeta_size;\n        return nullptr;\n    }\n\n    auto verify_result =\n            VerifyVBMetaSignature(*vbmeta, expected_public_key_blob, out_public_key_data);\n\n    if (out_verify_result != nullptr) {\n        *out_verify_result = verify_result;\n    }\n\n    if (verify_result == VBMetaVerifyResult::kSuccess ||\n        verify_result == VBMetaVerifyResult::kErrorVerification) {\n        return vbmeta;\n    }\n\n    return nullptr;\n}\n\nbool RollbackDetected(const std::string& partition_name ATTRIBUTE_UNUSED,\n                      uint64_t rollback_index ATTRIBUTE_UNUSED) {\n    // TODO(bowgotsai): Support rollback protection.\n    return false;\n}\n\nstd::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error) {\n    CHECK(fatal_error != nullptr);\n    std::vector<ChainInfo> chain_partitions;\n\n    size_t num_descriptors;\n    std::unique_ptr<const AvbDescriptor* [], decltype(&avb_free)> descriptors(\n            avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);\n\n    if (!descriptors || num_descriptors < 1) {\n        return {};\n    }\n\n    for (size_t i = 0; i < num_descriptors; i++) {\n        AvbDescriptor desc;\n        if (!avb_descriptor_validate_and_byteswap(descriptors[i], &desc)) {\n            LERROR << \"Descriptor[\" << i << \"] is invalid in vbmeta: \" << vbmeta.partition();\n            *fatal_error = true;\n            return {};\n        }\n        if (desc.tag == AVB_DESCRIPTOR_TAG_CHAIN_PARTITION) {\n            AvbChainPartitionDescriptor chain_desc;\n            if (!avb_chain_partition_descriptor_validate_and_byteswap(\n                        (AvbChainPartitionDescriptor*)descriptors[i], &chain_desc)) {\n                LERROR << \"Chain descriptor[\" << i\n                       << \"] is invalid in vbmeta: \" << vbmeta.partition();\n                *fatal_error = true;\n                return {};\n            }\n            const char* chain_partition_name =\n                    ((const char*)descriptors[i]) + sizeof(AvbChainPartitionDescriptor);\n            const char* chain_public_key_blob =\n                    chain_partition_name + chain_desc.partition_name_len;\n            chain_partitions.emplace_back(\n                    std::string(chain_partition_name, chain_desc.partition_name_len),\n                    std::string(chain_public_key_blob, chain_desc.public_key_len));\n        }\n    }\n\n    return chain_partitions;\n}\n\n// Loads the vbmeta from a given path.\nstd::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(\n        const std::string& image_path, const std::string& partition_name,\n        const std::string& expected_public_key_blob, bool allow_verification_error,\n        bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,\n        bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result) {\n    if (out_verify_result) {\n        *out_verify_result = VBMetaVerifyResult::kError;\n    }\n\n    // Ensures the device path (might be a symlink created by init) is ready to access.\n    if (!WaitForFile(image_path, 1s)) {\n        PERROR << \"No such path: \" << image_path;\n        return nullptr;\n    }\n\n    unique_fd fd(TEMP_FAILURE_RETRY(open(image_path.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) {\n        PERROR << \"Failed to open: \" << image_path;\n        return nullptr;\n    }\n\n    VBMetaVerifyResult verify_result;\n    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(\n            fd, partition_name, expected_public_key_blob, out_public_key_data, &verify_result);\n    if (!vbmeta) {\n        LERROR << partition_name << \": Failed to load vbmeta, result: \" << verify_result;\n        return nullptr;\n    }\n    vbmeta->set_vbmeta_path(image_path);\n\n    if (!allow_verification_error && verify_result == VBMetaVerifyResult::kErrorVerification) {\n        LERROR << partition_name << \": allow verification error is not allowed\";\n        return nullptr;\n    }\n\n    std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =\n            vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);\n    if (!vbmeta_header) {\n        LERROR << partition_name << \": Failed to get vbmeta header\";\n        return nullptr;\n    }\n\n    if (rollback_protection && RollbackDetected(partition_name, vbmeta_header->rollback_index)) {\n        return nullptr;\n    }\n\n    // vbmeta flags can only be set by the top-level vbmeta image.\n    if (is_chained_vbmeta && vbmeta_header->flags != 0) {\n        LERROR << partition_name << \": chained vbmeta image has non-zero flags\";\n        return nullptr;\n    }\n\n    // Checks if verification has been disabled by setting a bit in the image.\n    if (out_verification_disabled) {\n        if (vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED) {\n            LWARNING << \"VERIFICATION_DISABLED bit is set for partition: \" << partition_name;\n            *out_verification_disabled = true;\n        } else {\n            *out_verification_disabled = false;\n        }\n    }\n\n    if (out_verify_result) {\n        *out_verify_result = verify_result;\n    }\n\n    return vbmeta;\n}\n\nVBMetaVerifyResult LoadAndVerifyVbmetaByPartition(\n    const std::string& partition_name, const std::string& ab_suffix,\n    const std::string& ab_other_suffix, const std::string& expected_public_key_blob,\n    bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,\n    std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,\n    std::vector<VBMetaData>* out_vbmeta_images) {\n    auto image_path = device_path_constructor(\n        AvbPartitionToDevicePatition(partition_name, ab_suffix, ab_other_suffix));\n\n    bool verification_disabled = false;\n    VBMetaVerifyResult verify_result;\n    auto vbmeta = LoadAndVerifyVbmetaByPath(image_path, partition_name, expected_public_key_blob,\n                                            allow_verification_error, rollback_protection,\n                                            is_chained_vbmeta, nullptr /* out_public_key_data */,\n                                            &verification_disabled, &verify_result);\n\n    if (!vbmeta) {\n        return VBMetaVerifyResult::kError;\n    }\n    if (out_vbmeta_images) {\n        out_vbmeta_images->emplace_back(std::move(*vbmeta));\n    }\n\n    // Only loads chained vbmeta if AVB verification is NOT disabled.\n    if (!verification_disabled && load_chained_vbmeta) {\n        bool fatal_error = false;\n        auto chain_partitions = GetChainPartitionInfo(*out_vbmeta_images->rbegin(), &fatal_error);\n        if (fatal_error) {\n            return VBMetaVerifyResult::kError;\n        }\n        for (auto& chain : chain_partitions) {\n            auto sub_ret = LoadAndVerifyVbmetaByPartition(\n                chain.partition_name, ab_suffix, ab_other_suffix, chain.public_key_blob,\n                allow_verification_error, load_chained_vbmeta, rollback_protection,\n                device_path_constructor, true, /* is_chained_vbmeta */\n                out_vbmeta_images);\n            if (sub_ret != VBMetaVerifyResult::kSuccess) {\n                verify_result = sub_ret;  // might be 'ERROR' or 'ERROR VERIFICATION'.\n                if (verify_result == VBMetaVerifyResult::kError) {\n                    return verify_result;  // stop here if we got an 'ERROR'.\n                }\n            }\n        }\n    }\n\n    return verify_result;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/avb_util.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <ostream>\n#include <string>\n#include <vector>\n\n#include <fstab/fstab.h>\n#include <libavb/libavb.h>\n#include <libdm/dm.h>\n\n#include \"fs_avb/types.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nstruct ChainInfo {\n    std::string partition_name;\n    std::string public_key_blob;\n\n    ChainInfo(const std::string& chain_partition_name, const std::string& chain_public_key_blob)\n        : partition_name(chain_partition_name), public_key_blob(chain_public_key_blob) {}\n};\n\n// AvbHashtreeDescriptor to dm-verity table setup.\nstd::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(\n        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);\n\nbool ConstructVerityTable(const FsAvbHashtreeDescriptor& hashtree_desc,\n                          const std::string& blk_device, android::dm::DmTable* table);\n\nbool HashtreeDmVeritySetup(FstabEntry* fstab_entry, const FsAvbHashtreeDescriptor& hashtree_desc,\n                           bool wait_for_verity_dev);\n\n// Searches a Avb hashtree descriptor in vbmeta_images for fstab_entry, to enable dm-verity.\nbool LoadAvbHashtreeToEnableVerity(FstabEntry* fstab_entry, bool wait_for_verity_dev,\n                                   const std::vector<VBMetaData>& vbmeta_images,\n                                   const std::string& ab_suffix, const std::string& ab_other_suffix);\n\n// Converts AVB partition name to a device partition name.\nstd::string AvbPartitionToDevicePatition(const std::string& avb_partition_name,\n                                         const std::string& ab_suffix,\n                                         const std::string& ab_other_suffix);\n\n// Converts by-name symlink to AVB partition name.\nstd::string DeriveAvbPartitionName(const FstabEntry& fstab_entry, const std::string& ab_suffix,\n                                   const std::string& ab_other_suffix);\n\n// AvbFooter and AvbMetaImage maninpulations.\noff64_t GetTotalSize(int fd);\n\nstd::unique_ptr<AvbFooter> GetAvbFooter(int fd);\n\nstd::unique_ptr<VBMetaData> VerifyVBMetaData(int fd, const std::string& partition_name,\n                                             const std::string& expected_public_key_blob,\n                                             std::string* out_public_key_data,\n                                             VBMetaVerifyResult* out_verify_result);\n\nVBMetaVerifyResult VerifyVBMetaSignature(const VBMetaData& vbmeta,\n                                         const std::string& expected_public_key_blob,\n                                         std::string* out_public_key_data);\n\nbool ValidatePublicKeyBlob(const uint8_t* key, size_t length, const std::string& expected_key_blob);\n\nbool ValidatePublicKeyBlob(const std::string& key_blob_to_validate,\n                           const std::vector<std::string>& expected_key_paths);\n\n// Detects if whether a partition contains a rollback image.\nbool RollbackDetected(const std::string& partition_name, uint64_t rollback_index);\n\n// Extracts chain partition info.\nstd::vector<ChainInfo> GetChainPartitionInfo(const VBMetaData& vbmeta, bool* fatal_error);\n\n// Loads the single vbmeta from a given path.\nstd::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(\n        const std::string& image_path, const std::string& partition_name,\n        const std::string& expected_public_key_blob, bool allow_verification_error,\n        bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,\n        bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);\n\n// Loads the top-level vbmeta and all its chained vbmeta images.\n// The actual device path is constructed at runtime by:\n// partition_name, ab_suffix, ab_other_suffix, and device_path_constructor.\nVBMetaVerifyResult LoadAndVerifyVbmetaByPartition(\n    const std::string& partition_name, const std::string& ab_suffix,\n    const std::string& ab_other_suffix, const std::string& expected_public_key_blob,\n    bool allow_verification_error, bool load_chained_vbmeta, bool rollback_protection,\n    std::function<std::string(const std::string&)> device_path_constructor, bool is_chained_vbmeta,\n    std::vector<VBMetaData>* out_vbmeta_images);\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/fs_avb.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fs_avb/fs_avb.h\"\n\n#include <fcntl.h>\n#include <libgen.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <libavb/libavb.h>\n#include <libdm/dm.h>\n#include <libgsi/libgsi.h>\n\n#include \"avb_ops.h\"\n#include \"avb_util.h\"\n#include \"fs_avb/fs_avb_util.h\"\n#include \"sha.h\"\n#include \"util.h\"\n\nusing android::base::Basename;\nusing android::base::ParseUint;\nusing android::base::ReadFileToString;\nusing android::base::Split;\nusing android::base::StringPrintf;\n\nnamespace android {\nnamespace fs_mgr {\n\ntemplate <typename Hasher>\nstd::pair<size_t, bool> VerifyVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images,\n                                           const uint8_t* expected_digest) {\n    size_t total_size = 0;\n    Hasher hasher;\n    for (const auto& vbmeta : vbmeta_images) {\n        hasher.update(vbmeta.data(), vbmeta.size());\n        total_size += vbmeta.size();\n    }\n\n    bool matched = (memcmp(hasher.finalize(), expected_digest, Hasher::DIGEST_SIZE) == 0);\n\n    return std::make_pair(total_size, matched);\n}\n\ntemplate <typename Hasher>\nstd::pair<std::string, size_t> CalculateVbmetaDigest(const std::vector<VBMetaData>& vbmeta_images) {\n    std::string digest;\n    size_t total_size = 0;\n\n    Hasher hasher;\n    for (const auto& vbmeta : vbmeta_images) {\n        hasher.update(vbmeta.data(), vbmeta.size());\n        total_size += vbmeta.size();\n    }\n\n    // Converts digest bytes to a hex string.\n    digest = BytesToHex(hasher.finalize(), Hasher::DIGEST_SIZE);\n    return std::make_pair(digest, total_size);\n}\n\n// class AvbVerifier\n// -----------------\n// Reads the following values from kernel cmdline and provides the\n// VerifyVbmetaImages() to verify AvbSlotVerifyData.\n//   - androidboot.vbmeta.hash_alg\n//   - androidboot.vbmeta.size\n//   - androidboot.vbmeta.digest\nclass AvbVerifier {\n  public:\n    // The factory method to return a unique_ptr<AvbVerifier>\n    static std::unique_ptr<AvbVerifier> Create();\n    bool VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images);\n\n  protected:\n    AvbVerifier() = default;\n\n  private:\n    HashAlgorithm hash_alg_;\n    uint8_t digest_[SHA512_DIGEST_LENGTH];\n    size_t vbmeta_size_;\n};\n\nstd::unique_ptr<AvbVerifier> AvbVerifier::Create() {\n    std::unique_ptr<AvbVerifier> avb_verifier(new AvbVerifier());\n    if (!avb_verifier) {\n        LERROR << \"Failed to create unique_ptr<AvbVerifier>\";\n        return nullptr;\n    }\n\n    std::string value;\n    if (!fs_mgr_get_boot_config(\"vbmeta.size\", &value) ||\n        !ParseUint(value.c_str(), &avb_verifier->vbmeta_size_)) {\n        LERROR << \"Invalid hash size: \" << value.c_str();\n        return nullptr;\n    }\n\n    // Reads hash algorithm.\n    size_t expected_digest_size = 0;\n    std::string hash_alg;\n    fs_mgr_get_boot_config(\"vbmeta.hash_alg\", &hash_alg);\n    if (hash_alg == \"sha256\") {\n        expected_digest_size = SHA256_DIGEST_LENGTH * 2;\n        avb_verifier->hash_alg_ = HashAlgorithm::kSHA256;\n    } else if (hash_alg == \"sha512\") {\n        expected_digest_size = SHA512_DIGEST_LENGTH * 2;\n        avb_verifier->hash_alg_ = HashAlgorithm::kSHA512;\n    } else {\n        LERROR << \"Unknown hash algorithm: \" << hash_alg.c_str();\n        return nullptr;\n    }\n\n    // Reads digest.\n    std::string digest;\n    fs_mgr_get_boot_config(\"vbmeta.digest\", &digest);\n    if (digest.size() != expected_digest_size) {\n        LERROR << \"Unexpected digest size: \" << digest.size()\n               << \" (expected: \" << expected_digest_size << \")\";\n        return nullptr;\n    }\n\n    if (!HexToBytes(avb_verifier->digest_, sizeof(avb_verifier->digest_), digest)) {\n        LERROR << \"Hash digest contains non-hexidecimal character: \" << digest.c_str();\n        return nullptr;\n    }\n\n    return avb_verifier;\n}\n\nbool AvbVerifier::VerifyVbmetaImages(const std::vector<VBMetaData>& vbmeta_images) {\n    if (vbmeta_images.empty()) {\n        LERROR << \"No vbmeta images\";\n        return false;\n    }\n\n    size_t total_size = 0;\n    bool digest_matched = false;\n\n    if (hash_alg_ == HashAlgorithm::kSHA256) {\n        std::tie(total_size, digest_matched) =\n                VerifyVbmetaDigest<SHA256Hasher>(vbmeta_images, digest_);\n    } else if (hash_alg_ == HashAlgorithm::kSHA512) {\n        std::tie(total_size, digest_matched) =\n                VerifyVbmetaDigest<SHA512Hasher>(vbmeta_images, digest_);\n    }\n\n    if (total_size != vbmeta_size_) {\n        LERROR << \"total vbmeta size mismatch: \" << total_size << \" (expected: \" << vbmeta_size_\n               << \")\";\n        return false;\n    }\n\n    if (!digest_matched) {\n        LERROR << \"vbmeta digest mismatch\";\n        return false;\n    }\n\n    return true;\n}\n\n// class AvbHandle\n// ---------------\nAvbHandle::AvbHandle() : status_(AvbHandleStatus::kUninitialized) {\n    slot_suffix_ = fs_mgr_get_slot_suffix();\n    other_slot_suffix_ = fs_mgr_get_other_slot_suffix();\n}\n\nAvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(\n        const std::string& partition_name, const std::string& ab_suffix,\n        const std::string& ab_other_suffix, const std::string& expected_public_key_path,\n        const HashAlgorithm& hash_algorithm, bool allow_verification_error,\n        bool load_chained_vbmeta, bool rollback_protection,\n        std::function<std::string(const std::string&)> custom_device_path) {\n    AvbUniquePtr avb_handle(new AvbHandle());\n    if (!avb_handle) {\n        LERROR << \"Failed to allocate AvbHandle\";\n        return nullptr;\n    }\n\n    avb_handle->slot_suffix_ = ab_suffix;\n    avb_handle->other_slot_suffix_ = ab_other_suffix;\n\n    std::string expected_key_blob;\n    if (!expected_public_key_path.empty()) {\n        if (access(expected_public_key_path.c_str(), F_OK) != 0) {\n            LERROR << \"Expected public key path doesn't exist: \" << expected_public_key_path;\n            return nullptr;\n        } else if (!ReadFileToString(expected_public_key_path, &expected_key_blob)) {\n            LERROR << \"Failed to load: \" << expected_public_key_path;\n            return nullptr;\n        }\n    }\n\n    auto android_by_name_symlink = [](const std::string& partition_name_with_ab) {\n        return \"/dev/block/by-name/\" + partition_name_with_ab;\n    };\n\n    auto device_path = custom_device_path ? custom_device_path : android_by_name_symlink;\n\n    auto verify_result = LoadAndVerifyVbmetaByPartition(\n        partition_name, ab_suffix, ab_other_suffix, expected_key_blob, allow_verification_error,\n        load_chained_vbmeta, rollback_protection, device_path, false,\n        /* is_chained_vbmeta */ &avb_handle->vbmeta_images_);\n    switch (verify_result) {\n        case VBMetaVerifyResult::kSuccess:\n            avb_handle->status_ = AvbHandleStatus::kSuccess;\n            break;\n        case VBMetaVerifyResult::kErrorVerification:\n            avb_handle->status_ = AvbHandleStatus::kVerificationError;\n            break;\n        default:\n            LERROR << \"LoadAndVerifyVbmetaByPartition failed, result: \" << verify_result;\n            return nullptr;\n    }\n\n    // Validity check here because we have to use vbmeta_images_[0] below.\n    if (avb_handle->vbmeta_images_.size() < 1) {\n        LERROR << \"LoadAndVerifyVbmetaByPartition failed, no vbmeta loaded\";\n        return nullptr;\n    }\n\n    // Sets the MAJOR.MINOR for init to set it into \"ro.boot.avb_version\".\n    avb_handle->avb_version_ = StringPrintf(\"%d.%d\", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);\n\n    // Checks any disabled flag is set.\n    std::unique_ptr<AvbVBMetaImageHeader> vbmeta_header =\n            avb_handle->vbmeta_images_[0].GetVBMetaHeader();\n    bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header->flags &\n                                  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);\n    bool hashtree_disabled =\n            ((AvbVBMetaImageFlags)vbmeta_header->flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);\n    if (verification_disabled) {\n        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;\n    } else if (hashtree_disabled) {\n        avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;\n    }\n\n    // Calculates the summary info for all vbmeta_images_;\n    std::string digest;\n    size_t total_size;\n    if (hash_algorithm == HashAlgorithm::kSHA256) {\n        std::tie(digest, total_size) =\n                CalculateVbmetaDigest<SHA256Hasher>(avb_handle->vbmeta_images_);\n    } else if (hash_algorithm == HashAlgorithm::kSHA512) {\n        std::tie(digest, total_size) =\n                CalculateVbmetaDigest<SHA512Hasher>(avb_handle->vbmeta_images_);\n    } else {\n        LERROR << \"Invalid hash algorithm\";\n        return nullptr;\n    }\n    avb_handle->vbmeta_info_ = VBMetaInfo(digest, hash_algorithm, total_size);\n\n    LINFO << \"Returning avb_handle with status: \" << avb_handle->status_;\n    return avb_handle;\n}\n\nstatic bool IsAvbPermissive() {\n    if (IsDeviceUnlocked()) {\n        // Manually putting a file under metadata partition can enforce AVB verification.\n        if (!access(DSU_METADATA_PREFIX \"avb_enforce\", F_OK)) {\n            LINFO << \"Enforcing AVB verification when the device is unlocked\";\n            return false;\n        }\n        return true;\n    }\n    return false;\n}\n\nbool IsPublicKeyMatching(const FstabEntry& fstab_entry, const std::string& public_key_data,\n                         const std::vector<std::string>& preload_avb_key_blobs) {\n    // At least one of the following should be provided for public key matching.\n    if (preload_avb_key_blobs.empty() && fstab_entry.avb_keys.empty()) {\n        LERROR << \"avb_keys=/path/to/key(s) is missing for \" << fstab_entry.mount_point;\n        return false;\n    }\n\n    // Expected key shouldn't be empty.\n    if (public_key_data.empty()) {\n        LERROR << \"public key data shouldn't be empty for \" << fstab_entry.mount_point;\n        return false;\n    }\n\n    // Performs key matching for preload_avb_key_blobs first, if it is present.\n    if (!preload_avb_key_blobs.empty()) {\n        if (std::find(preload_avb_key_blobs.begin(), preload_avb_key_blobs.end(),\n                      public_key_data) != preload_avb_key_blobs.end()) {\n            return true;\n        }\n    }\n\n    // Performs key matching for fstab_entry.avb_keys if necessary.\n    // Note that it is intentional to match both preload_avb_key_blobs and fstab_entry.avb_keys.\n    // Some keys might only be available before init chroots into /system, e.g., /avb/key1\n    // in the first-stage ramdisk, while other keys might only be available after the chroot,\n    // e.g., /system/etc/avb/key2.\n    // fstab_entry.avb_keys might be either a directory containing multiple keys,\n    // or a string indicating multiple keys separated by ':'.\n    std::vector<std::string> allowed_avb_keys;\n    auto list_avb_keys_in_dir = ListFiles(fstab_entry.avb_keys);\n    if (list_avb_keys_in_dir.ok()) {\n        std::sort(list_avb_keys_in_dir->begin(), list_avb_keys_in_dir->end());\n        allowed_avb_keys = *list_avb_keys_in_dir;\n    } else {\n        allowed_avb_keys = Split(fstab_entry.avb_keys, \":\");\n    }\n    return ValidatePublicKeyBlob(public_key_data, allowed_avb_keys);\n}\n\nbool IsHashtreeDescriptorRootDigestMatching(const FstabEntry& fstab_entry,\n                                            const std::vector<VBMetaData>& vbmeta_images,\n                                            const std::string& ab_suffix,\n                                            const std::string& ab_other_suffix) {\n    // Read expected value of hashtree descriptor root digest from fstab_entry.\n    std::string root_digest_expected;\n    if (!ReadFileToString(fstab_entry.avb_hashtree_digest, &root_digest_expected)) {\n        LERROR << \"Failed to load expected root digest for \" << fstab_entry.mount_point;\n        return false;\n    }\n\n    // Read actual hashtree descriptor from vbmeta image.\n    std::string partition_name = DeriveAvbPartitionName(fstab_entry, ab_suffix, ab_other_suffix);\n    if (partition_name.empty()) {\n        LERROR << \"Failed to find partition name for \" << fstab_entry.mount_point;\n        return false;\n    }\n    std::unique_ptr<FsAvbHashtreeDescriptor> hashtree_descriptor =\n            android::fs_mgr::GetHashtreeDescriptor(partition_name, vbmeta_images);\n    if (!hashtree_descriptor) {\n        LERROR << \"Not found hashtree descriptor for \" << fstab_entry.mount_point;\n        return false;\n    }\n\n    // Performs hashtree descriptor root digest matching.\n    if (hashtree_descriptor->root_digest != root_digest_expected) {\n        LERROR << \"root digest (\" << hashtree_descriptor->root_digest\n               << \") is different from expected value (\" << root_digest_expected << \")\";\n        return false;\n    }\n\n    return true;\n}\n\nAvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,\n                                            const std::vector<std::string>& preload_avb_key_blobs) {\n    // Binds allow_verification_error and rollback_protection to device unlock state.\n    bool allow_verification_error = IsAvbPermissive();\n    bool rollback_protection = !allow_verification_error;\n\n    std::string public_key_data;\n    bool verification_disabled = false;\n    VBMetaVerifyResult verify_result = VBMetaVerifyResult::kError;\n    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(\n            fstab_entry.blk_device, \"\" /* partition_name, no need for a standalone path */,\n            \"\" /* expected_public_key_blob, */, allow_verification_error, rollback_protection,\n            false /* not is_chained_vbmeta */, &public_key_data, &verification_disabled,\n            &verify_result);\n\n    if (!vbmeta) {\n        LERROR << \"Failed to load vbmeta: \" << fstab_entry.blk_device;\n        return nullptr;\n    }\n\n    AvbUniquePtr avb_handle(new AvbHandle());\n    if (!avb_handle) {\n        LERROR << \"Failed to allocate AvbHandle\";\n        return nullptr;\n    }\n    avb_handle->vbmeta_images_.emplace_back(std::move(*vbmeta));\n\n    switch (verify_result) {\n        case VBMetaVerifyResult::kSuccess:\n            avb_handle->status_ = AvbHandleStatus::kSuccess;\n            break;\n        case VBMetaVerifyResult::kErrorVerification:\n            avb_handle->status_ = AvbHandleStatus::kVerificationError;\n            break;\n        default:\n            LERROR << \"LoadAndVerifyVbmetaByPath failed, result: \" << verify_result;\n            return nullptr;\n    }\n\n    // Verify vbmeta image checking by either public key or hashtree descriptor root digest.\n    if (!preload_avb_key_blobs.empty() || !fstab_entry.avb_keys.empty()) {\n        if (!IsPublicKeyMatching(fstab_entry, public_key_data, preload_avb_key_blobs)) {\n            avb_handle->status_ = AvbHandleStatus::kVerificationError;\n            LWARNING << \"Found unknown public key used to sign \" << fstab_entry.mount_point;\n            if (!allow_verification_error) {\n                LERROR << \"Unknown public key is not allowed\";\n                return nullptr;\n            }\n        }\n    } else if (!IsHashtreeDescriptorRootDigestMatching(fstab_entry, avb_handle->vbmeta_images_,\n                                                       avb_handle->slot_suffix_,\n                                                       avb_handle->other_slot_suffix_)) {\n        avb_handle->status_ = AvbHandleStatus::kVerificationError;\n        LWARNING << \"Found unknown hashtree descriptor root digest used on \"\n                 << fstab_entry.mount_point;\n        if (!allow_verification_error) {\n            LERROR << \"Verification based on root digest failed. Vbmeta image is not allowed.\";\n            return nullptr;\n        }\n    }\n\n    if (verification_disabled) {\n        LINFO << \"AVB verification disabled on: \" << fstab_entry.mount_point;\n        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;\n    }\n\n    LINFO << \"Returning avb_handle for '\" << fstab_entry.mount_point\n          << \"' with status: \" << avb_handle->status_;\n    return avb_handle;\n}\n\nAvbUniquePtr AvbHandle::LoadAndVerifyVbmeta(const std::string& slot_suffix) {\n    // Loads inline vbmeta images, starting from /vbmeta.\n    auto suffix = slot_suffix;\n    if (suffix.empty()) {\n        suffix = fs_mgr_get_slot_suffix();\n    }\n    auto other_suffix = android::fs_mgr::OtherSlotSuffix(suffix);\n    return LoadAndVerifyVbmeta(\"vbmeta\", suffix, other_suffix,\n                               {} /* expected_public_key, already checked by bootloader */,\n                               HashAlgorithm::kSHA256,\n                               IsAvbPermissive(), /* allow_verification_error */\n                               true,              /* load_chained_vbmeta */\n                               false, /* rollback_protection, already checked by bootloader */\n                               nullptr /* custom_device_path */);\n}\n\n// TODO(b/128807537): removes this function.\nAvbUniquePtr AvbHandle::Open() {\n    bool allow_verification_error = IsAvbPermissive();\n\n    AvbUniquePtr avb_handle(new AvbHandle());\n    if (!avb_handle) {\n        LERROR << \"Failed to allocate AvbHandle\";\n        return nullptr;\n    }\n\n    FsManagerAvbOps avb_ops;\n    AvbSlotVerifyFlags flags = allow_verification_error\n                                       ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR\n                                       : AVB_SLOT_VERIFY_FLAGS_NONE;\n    AvbSlotVerifyResult verify_result =\n            avb_ops.AvbSlotVerify(avb_handle->slot_suffix_, flags, &avb_handle->vbmeta_images_);\n\n    // Only allow the following verify results:\n    //   - AVB_SLOT_VERIFY_RESULT_OK.\n    //   - AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION (UNLOCKED only).\n    //     Might occur in either the top-level vbmeta or a chained vbmeta.\n    //   - AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED (UNLOCKED only).\n    //     Could only occur in a chained vbmeta. Because we have *no-op* operations in\n    //     FsManagerAvbOps such that avb_ops->validate_vbmeta_public_key() used to validate\n    //     the public key of the top-level vbmeta always pass in userspace here.\n    //\n    // The following verify result won't happen, because the *no-op* operation\n    // avb_ops->read_rollback_index() always returns the minimum value zero. So rollbacked\n    // vbmeta images, which should be caught in the bootloader stage, won't be detected here.\n    //   - AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX\n    switch (verify_result) {\n        case AVB_SLOT_VERIFY_RESULT_OK:\n            avb_handle->status_ = AvbHandleStatus::kSuccess;\n            break;\n        case AVB_SLOT_VERIFY_RESULT_ERROR_VERIFICATION:\n        case AVB_SLOT_VERIFY_RESULT_ERROR_PUBLIC_KEY_REJECTED:\n            if (!allow_verification_error) {\n                LERROR << \"ERROR_VERIFICATION / PUBLIC_KEY_REJECTED isn't allowed \";\n                return nullptr;\n            }\n            avb_handle->status_ = AvbHandleStatus::kVerificationError;\n            break;\n        default:\n            LERROR << \"avb_slot_verify failed, result: \" << verify_result;\n            return nullptr;\n    }\n\n    // Sets the MAJOR.MINOR for init to set it into \"ro.boot.avb_version\".\n    avb_handle->avb_version_ = StringPrintf(\"%d.%d\", AVB_VERSION_MAJOR, AVB_VERSION_MINOR);\n\n    // Verifies vbmeta structs against the digest passed from bootloader in kernel cmdline.\n    std::unique_ptr<AvbVerifier> avb_verifier = AvbVerifier::Create();\n    if (!avb_verifier || !avb_verifier->VerifyVbmetaImages(avb_handle->vbmeta_images_)) {\n        LERROR << \"Failed to verify vbmeta digest\";\n        if (!allow_verification_error) {\n            LERROR << \"vbmeta digest error isn't allowed \";\n            return nullptr;\n        }\n    }\n\n    // Checks whether FLAGS_VERIFICATION_DISABLED is set:\n    //   - Only the top-level vbmeta struct is read.\n    //   - vbmeta struct in other partitions are NOT processed, including AVB HASH descriptor(s)\n    //     and AVB HASHTREE descriptor(s).\n    AvbVBMetaImageHeader vbmeta_header;\n    avb_vbmeta_image_header_to_host_byte_order(\n            (AvbVBMetaImageHeader*)avb_handle->vbmeta_images_[0].data(), &vbmeta_header);\n    bool verification_disabled = ((AvbVBMetaImageFlags)vbmeta_header.flags &\n                                  AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);\n\n    // Checks whether FLAGS_HASHTREE_DISABLED is set.\n    //   - vbmeta struct in all partitions are still processed, just disable\n    //     dm-verity in the user space.\n    bool hashtree_disabled =\n            ((AvbVBMetaImageFlags)vbmeta_header.flags & AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);\n\n    if (verification_disabled) {\n        avb_handle->status_ = AvbHandleStatus::kVerificationDisabled;\n    } else if (hashtree_disabled) {\n        avb_handle->status_ = AvbHandleStatus::kHashtreeDisabled;\n    }\n\n    LINFO << \"Returning avb_handle with status: \" << avb_handle->status_;\n    return avb_handle;\n}\n\nAvbHashtreeResult AvbHandle::SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,\n                                                        bool wait_for_verity_dev) {\n    auto avb_handle = LoadAndVerifyVbmeta(*fstab_entry);\n    if (!avb_handle) {\n        return AvbHashtreeResult::kFail;\n    }\n\n    return avb_handle->SetUpAvbHashtree(fstab_entry, wait_for_verity_dev);\n}\n\nAvbHashtreeResult AvbHandle::SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev) {\n    if (!fstab_entry || status_ == AvbHandleStatus::kUninitialized || vbmeta_images_.size() < 1) {\n        return AvbHashtreeResult::kFail;\n    }\n\n    if (status_ == AvbHandleStatus::kHashtreeDisabled ||\n        status_ == AvbHandleStatus::kVerificationDisabled) {\n        LINFO << \"AVB HASHTREE disabled on: \" << fstab_entry->mount_point;\n        return AvbHashtreeResult::kDisabled;\n    }\n\n    if (!LoadAvbHashtreeToEnableVerity(fstab_entry, wait_for_verity_dev, vbmeta_images_,\n                                       slot_suffix_, other_slot_suffix_)) {\n        return AvbHashtreeResult::kFail;\n    }\n\n    return AvbHashtreeResult::kSuccess;\n}\n\nbool AvbHandle::TearDownAvbHashtree(FstabEntry* fstab_entry, bool wait) {\n    if (!fstab_entry) {\n        return false;\n    }\n\n    const std::string device_name(GetVerityDeviceName(*fstab_entry));\n\n    // TODO: remove duplicated code with UnmapDevice()\n    android::dm::DeviceMapper& dm = android::dm::DeviceMapper::Instance();\n    std::string path;\n    if (wait) {\n        dm.GetDmDevicePathByName(device_name, &path);\n    }\n    if (!dm.DeleteDevice(device_name)) {\n        return false;\n    }\n    if (!path.empty() && !WaitForFile(path, 1000ms, FileWaitMode::DoesNotExist)) {\n        return false;\n    }\n\n    return true;\n}\n\nstd::string AvbHandle::GetSecurityPatchLevel(const FstabEntry& fstab_entry) const {\n    if (vbmeta_images_.size() < 1) {\n        return \"\";\n    }\n    std::string avb_partition_name =\n            DeriveAvbPartitionName(fstab_entry, slot_suffix_, other_slot_suffix_);\n    auto avb_prop_name = \"com.android.build.\" + avb_partition_name + \".security_patch\";\n    return GetAvbPropertyDescriptor(avb_prop_name, vbmeta_images_);\n}\n\nbool AvbHandle::IsDeviceUnlocked() {\n    return android::fs_mgr::IsDeviceUnlocked();\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/fs_avb_util.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fs_avb/fs_avb_util.h\"\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <fstab/fstab.h>\n#include <libavb/libavb.h>\n#include <libdm/dm.h>\n\n#include \"avb_util.h\"\n#include \"util.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\n// Given a FstabEntry, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.\nstd::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,\n                                                const std::string& expected_public_key_blob,\n                                                std::string* out_public_key_data,\n                                                std::string* out_avb_partition_name,\n                                                VBMetaVerifyResult* out_verify_result) {\n    // Derives partition_name from blk_device to query the corresponding AVB HASHTREE descriptor\n    // to setup dm-verity. The partition_names in AVB descriptors are without A/B suffix.\n    std::string avb_partition_name = DeriveAvbPartitionName(fstab_entry, fs_mgr_get_slot_suffix(),\n                                                            fs_mgr_get_other_slot_suffix());\n    if (out_avb_partition_name) {\n        *out_avb_partition_name = avb_partition_name;\n    }\n\n    // Updates fstab_entry->blk_device from <partition> to /dev/block/dm-<N> if\n    // it's a logical partition.\n    std::string device_path = fstab_entry.blk_device;\n    if (fstab_entry.fs_mgr_flags.logical &&\n        !android::base::StartsWith(fstab_entry.blk_device, \"/\")) {\n        dm::DeviceMapper& dm = dm::DeviceMapper::Instance();\n        if (!dm.GetDmDevicePathByName(fstab_entry.blk_device, &device_path)) {\n            LERROR << \"Failed to resolve logical device path for: \" << fstab_entry.blk_device;\n            return nullptr;\n        }\n    }\n\n    return LoadAndVerifyVbmetaByPath(device_path, avb_partition_name, expected_public_key_blob,\n                                     true /* allow_verification_error */,\n                                     false /* rollback_protection */, false /* is_chained_vbmeta */,\n                                     out_public_key_data, nullptr /* out_verification_disabled */,\n                                     out_verify_result);\n}\n\n// Given a path, loads and verifies the vbmeta, to extract the Avb Hashtree descriptor.\nstd::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(\n        const std::string& avb_partition_name, VBMetaData&& vbmeta) {\n    if (!vbmeta.size()) return nullptr;\n\n    std::vector<VBMetaData> vbmeta_images;\n    vbmeta_images.emplace_back(std::move(vbmeta));\n    return GetHashtreeDescriptor(avb_partition_name, vbmeta_images);\n}\n\nstd::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(\n        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images) {\n    bool found = false;\n    const uint8_t* desc_partition_name;\n    auto hash_desc = std::make_unique<FsAvbHashDescriptor>();\n\n    for (const auto& vbmeta : vbmeta_images) {\n        size_t num_descriptors;\n        std::unique_ptr<const AvbDescriptor*[], decltype(&avb_free)> descriptors(\n                avb_descriptor_get_all(vbmeta.data(), vbmeta.size(), &num_descriptors), avb_free);\n\n        if (!descriptors || num_descriptors < 1) {\n            continue;\n        }\n\n        for (size_t n = 0; n < num_descriptors && !found; n++) {\n            AvbDescriptor desc;\n            if (!avb_descriptor_validate_and_byteswap(descriptors[n], &desc)) {\n                LWARNING << \"Descriptor[\" << n << \"] is invalid\";\n                continue;\n            }\n            if (desc.tag == AVB_DESCRIPTOR_TAG_HASH) {\n                desc_partition_name = (const uint8_t*)descriptors[n] + sizeof(AvbHashDescriptor);\n                if (!avb_hash_descriptor_validate_and_byteswap((AvbHashDescriptor*)descriptors[n],\n                                                               hash_desc.get())) {\n                    continue;\n                }\n                if (hash_desc->partition_name_len != partition_name.length()) {\n                    continue;\n                }\n                // Notes that desc_partition_name is not NUL-terminated.\n                std::string hash_partition_name((const char*)desc_partition_name,\n                                                hash_desc->partition_name_len);\n                if (hash_partition_name == partition_name) {\n                    found = true;\n                }\n            }\n        }\n\n        if (found) break;\n    }\n\n    if (!found) {\n        LERROR << \"Hash descriptor not found: \" << partition_name;\n        return nullptr;\n    }\n\n    hash_desc->partition_name = partition_name;\n\n    const uint8_t* desc_salt = desc_partition_name + hash_desc->partition_name_len;\n    hash_desc->salt = BytesToHex(desc_salt, hash_desc->salt_len);\n\n    const uint8_t* desc_digest = desc_salt + hash_desc->salt_len;\n    hash_desc->digest = BytesToHex(desc_digest, hash_desc->digest_len);\n\n    return hash_desc;\n}\n\n// Given a path, loads and verifies the vbmeta, to extract the Avb Hash descriptor.\nstd::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,\n                                                       VBMetaData&& vbmeta) {\n    if (!vbmeta.size()) return nullptr;\n\n    std::vector<VBMetaData> vbmeta_images;\n    vbmeta_images.emplace_back(std::move(vbmeta));\n    return GetHashDescriptor(avb_partition_name, vbmeta_images);\n}\n\nstd::string GetAvbPropertyDescriptor(const std::string& key,\n                                     const std::vector<VBMetaData>& vbmeta_images) {\n    size_t value_size;\n    for (const auto& vbmeta : vbmeta_images) {\n        const char* value = avb_property_lookup(vbmeta.data(), vbmeta.size(), key.data(),\n                                                key.size(), &value_size);\n        if (value != nullptr) {\n            return {value, value_size};\n        }\n    }\n    return \"\";\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/include/fs_avb/fs_avb.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <fs_avb/types.h>\n#include <fstab/fstab.h>\n#include <libavb/libavb.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nstruct VBMetaInfo {\n    std::string digest;\n    HashAlgorithm hash_algorithm;\n    size_t total_size;\n\n    VBMetaInfo() {}\n\n    VBMetaInfo(std::string digest_value, HashAlgorithm algorithm, size_t size)\n        : digest(std::move(digest_value)), hash_algorithm(algorithm), total_size(size) {}\n};\n\nclass FsManagerAvbOps;\n\nclass AvbHandle;\nusing AvbUniquePtr = std::unique_ptr<AvbHandle>;\n\n// Provides a factory method to return a unique_ptr pointing to itself and the\n// SetUpAvbHashtree() function to extract dm-verity parameters from AVB HASHTREE\n// descriptors to load verity table into kernel through ioctl.\nclass AvbHandle {\n  public:\n    // The factory methods to return a AvbUniquePtr that holds\n    // the verified AVB (external/avb) metadata of all verified partitions\n    // in vbmeta_images_.\n    //\n    // The metadata is checked against the following values from /proc/cmdline.\n    //   - androidboot.vbmeta.{hash_alg, size, digest}.\n    //\n    // A typical usage will be:\n    //   - AvbUniquePtr handle = AvbHandle::Open(); or\n    //   - AvbUniquePtr handle = AvbHandle::LoadAndVerifyVbmeta();\n    //\n    // Possible return values:\n    //   - nullptr: any error when reading and verifying the metadata,\n    //     e.g., I/O error, digest value mismatch, size mismatch, etc.\n    //\n    //   - a valid unique_ptr with status AvbHandleStatus::HashtreeDisabled:\n    //     to support the existing 'adb disable-verity' feature in Android.\n    //     It's very helpful for developers to make the filesystem writable to\n    //     allow replacing binaries on the device.\n    //\n    //   - a valid unique_ptr with status AvbHandleStatus::VerificationDisabled:\n    //     to support 'avbctl disable-verification': only the top-level\n    //     vbmeta is read, vbmeta structs in other partitions are not processed.\n    //     It's needed to bypass AVB when using the generic system.img to run\n    //     VTS for project Treble.\n    //\n    //   - a valid unique_ptr with status AvbHandleStatus::VerificationError:\n    //     there is verification error when libavb loads vbmeta from each\n    //     partition. This is only allowed when the device is unlocked.\n    //\n    //   - a valid unique_ptr with status AvbHandleStatus::Success: the metadata\n    //     is verified and can be trusted.\n    //\n    // TODO(bowgotsai): remove Open() and switch to LoadAndVerifyVbmeta().\n    static AvbUniquePtr Open();  // loads inline vbmeta, via libavb.\n    static AvbUniquePtr LoadAndVerifyVbmeta(const std::string& slot_suffix = {});\n\n    // The caller can specify optional preload_avb_key_blobs for public key matching.\n    // This is mostly for init to preload AVB keys before chroot into /system.\n    // Both preload_avb_key_blobs and fstab_entry.avb_keys (file paths) will be used\n    // for public key matching.\n    static AvbUniquePtr LoadAndVerifyVbmeta(  // loads offline vbmeta.\n            const FstabEntry& fstab_entry,\n            const std::vector<std::string>& preload_avb_key_blobs = {});\n\n    static AvbUniquePtr LoadAndVerifyVbmeta(    // loads offline vbmeta.\n            const std::string& partition_name, const std::string& ab_suffix,\n            const std::string& ab_other_suffix, const std::string& expected_public_key,\n            const HashAlgorithm& hash_algorithm, bool allow_verification_error,\n            bool load_chained_vbmeta, bool rollback_protection,\n            std::function<std::string(const std::string&)> custom_device_path = nullptr);\n\n    // Sets up dm-verity on the given fstab entry.\n    // The 'wait_for_verity_dev' parameter makes this function wait for the\n    // verity device to get created before return.\n    //\n    // Return value:\n    //   - kSuccess: successfully loads dm-verity table into kernel.\n    //   - kFailed: failed to setup dm-verity, e.g., vbmeta verification error,\n    //     failed to get the HASHTREE descriptor, runtime error when set up\n    //     device-mapper, etc.\n    //   - kDisabled: hashtree is disabled.\n    AvbHashtreeResult SetUpAvbHashtree(FstabEntry* fstab_entry, bool wait_for_verity_dev);\n\n    // Similar to above, but loads the offline vbmeta from the end of fstab_entry->blk_device.\n    static AvbHashtreeResult SetUpStandaloneAvbHashtree(FstabEntry* fstab_entry,\n                                                        bool wait_for_verity_dev = true);\n\n    // Tear down dm devices created by SetUp[Standalone]AvbHashtree\n    // The 'wait' parameter makes this function wait for the verity device to get destroyed\n    // before return.\n    static bool TearDownAvbHashtree(FstabEntry* fstab_entry, bool wait);\n\n    static bool IsDeviceUnlocked();\n\n    std::string GetSecurityPatchLevel(const FstabEntry& fstab_entry) const;\n\n    const std::string& avb_version() const { return avb_version_; }\n    const VBMetaInfo& vbmeta_info() const { return vbmeta_info_; }\n    AvbHandleStatus status() const { return status_; }\n\n    AvbHandle(const AvbHandle&) = delete;             // no copy\n    AvbHandle& operator=(const AvbHandle&) = delete;  // no assignment\n\n    AvbHandle(AvbHandle&&) noexcept = delete;             // no move\n    AvbHandle& operator=(AvbHandle&&) noexcept = delete;  // no move assignment\n\n  private:\n    AvbHandle();\n\n    std::vector<VBMetaData> vbmeta_images_;\n    VBMetaInfo vbmeta_info_;  // A summary info for vbmeta_images_.\n    AvbHandleStatus status_;\n    std::string avb_version_;\n    std::string slot_suffix_;\n    std::string other_slot_suffix_;\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/include/fs_avb/fs_avb_util.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <fs_avb/types.h>\n#include <fstab/fstab.h>\n#include <libavb/libavb.h>\n\nnamespace android {\nnamespace fs_mgr {\n\n// Given a FstabEntry, loads and verifies the vbmeta.\nstd::unique_ptr<VBMetaData> LoadAndVerifyVbmeta(const FstabEntry& fstab_entry,\n                                                const std::string& expected_public_key_blob,\n                                                std::string* out_public_key_data,\n                                                std::string* out_avb_partition_name,\n                                                VBMetaVerifyResult* out_verify_result);\n\n// Loads the single vbmeta from a given path.\nstd::unique_ptr<VBMetaData> LoadAndVerifyVbmetaByPath(\n        const std::string& image_path, const std::string& partition_name,\n        const std::string& expected_public_key_blob, bool allow_verification_error,\n        bool rollback_protection, bool is_chained_vbmeta, std::string* out_public_key_data,\n        bool* out_verification_disabled, VBMetaVerifyResult* out_verify_result);\n\n// Gets the hashtree descriptor for avb_partition_name from the vbmeta.\nstd::unique_ptr<FsAvbHashtreeDescriptor> GetHashtreeDescriptor(\n        const std::string& avb_partition_name, VBMetaData&& vbmeta);\n\nstd::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(\n        const std::string& partition_name, const std::vector<VBMetaData>& vbmeta_images);\n\n// Gets the hash descriptor for avb_partition_name from the vbmeta.\nstd::unique_ptr<FsAvbHashDescriptor> GetHashDescriptor(const std::string& avb_partition_name,\n                                                       VBMetaData&& vbmeta);\n\nstd::string GetAvbPropertyDescriptor(const std::string& key,\n                                     const std::vector<VBMetaData>& vbmeta_images);\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/include/fs_avb/types.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstring>\n#include <memory>\n#include <ostream>\n\n#include <libavb/libavb.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nenum class VBMetaVerifyResult {\n    kSuccess = 0,\n    kError = 1,\n    kErrorVerification = 2,\n};\n\nstd::ostream& operator<<(std::ostream& os, VBMetaVerifyResult);\n\nenum class AvbHashtreeResult {\n    kSuccess = 0,\n    kFail,\n    kDisabled,\n};\n\nenum class HashAlgorithm {\n    kInvalid = 0,\n    kSHA256 = 1,\n    kSHA512 = 2,\n};\n\nenum class AvbHandleStatus {\n    kSuccess = 0,\n    kUninitialized = 1,\n    kHashtreeDisabled = 2,\n    kVerificationDisabled = 3,\n    kVerificationError = 4,\n};\n\nstd::ostream& operator<<(std::ostream& os, AvbHandleStatus status);\n\nstruct FsAvbHashDescriptor : AvbHashDescriptor {\n    std::string partition_name;\n    std::string salt;\n    std::string digest;\n};\n\nstruct FsAvbHashtreeDescriptor : AvbHashtreeDescriptor {\n    std::string partition_name;\n    std::string salt;\n    std::string root_digest;\n};\n\nclass VBMetaData {\n  public:\n    // Constructors\n    VBMetaData() : vbmeta_ptr_(nullptr), vbmeta_size_(0){};\n\n    VBMetaData(const uint8_t* data, size_t size, const std::string& partition_name)\n        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),\n          vbmeta_size_(size),\n          partition_name_(partition_name) {\n        // The ownership of data is NOT transferred, i.e., the caller still\n        // needs to release the memory as we make a copy here.\n        std::memcpy(vbmeta_ptr_.get(), data, size * sizeof(uint8_t));\n    }\n\n    explicit VBMetaData(size_t size, const std::string& partition_name)\n        : vbmeta_ptr_(new (std::nothrow) uint8_t[size]),\n          vbmeta_size_(size),\n          partition_name_(partition_name) {}\n\n    // Extracts vbmeta header from the vbmeta buffer, set update_vbmeta_size to\n    // true to update vbmeta_size_ to the actual size with valid content.\n    std::unique_ptr<AvbVBMetaImageHeader> GetVBMetaHeader(bool update_vbmeta_size = false);\n\n    // Sets the vbmeta_path where we load the vbmeta data. Could be a partition or a file.\n    // e.g.,\n    // - /dev/block/by-name/system_a\n    // - /path/to/system_other.img.\n    void set_vbmeta_path(std::string vbmeta_path) { vbmeta_path_ = std::move(vbmeta_path); }\n\n    // Get methods for each data member.\n    const std::string& partition() const { return partition_name_; }\n    const std::string& vbmeta_path() const { return vbmeta_path_; }\n    uint8_t* data() const { return vbmeta_ptr_.get(); }\n    const size_t& size() const { return vbmeta_size_; }\n\n    // Maximum size of a vbmeta data - 64 KiB.\n    static const size_t kMaxVBMetaSize = 64 * 1024;\n\n  private:\n    std::unique_ptr<uint8_t[]> vbmeta_ptr_;\n    size_t vbmeta_size_;\n    std::string partition_name_;\n    std::string vbmeta_path_;\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/run_tests.sh",
    "content": "#!/bin/sh\n#\n# Run host tests\natest --host libfs_avb_test          # Tests public libfs_avb APIs.\n\n# Tests libfs_avb private APIs.\n# The tests need more time to finish, so increase the timeout to 5 mins.\n# The default timeout is only 60 seconds.\natest --host libfs_avb_internal_test -- --test-arg \\\n    com.android.tradefed.testtype.HostGTest:native-test-timeout:5m\n\n# Run device tests\natest libfs_avb_device_test          # Test public libfs_avb APIs on a device.\n"
  },
  {
    "path": "fs_mgr/libfs_avb/sha.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <openssl/sha.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nclass SHA256Hasher {\n  private:\n    SHA256_CTX sha256_ctx;\n    uint8_t hash[SHA256_DIGEST_LENGTH];\n\n  public:\n    enum { DIGEST_SIZE = SHA256_DIGEST_LENGTH };\n\n    SHA256Hasher() { SHA256_Init(&sha256_ctx); }\n\n    void update(const uint8_t* data, size_t data_size) {\n        SHA256_Update(&sha256_ctx, data, data_size);\n    }\n\n    const uint8_t* finalize() {\n        SHA256_Final(hash, &sha256_ctx);\n        return hash;\n    }\n};\n\nclass SHA512Hasher {\n  private:\n    SHA512_CTX sha512_ctx;\n    uint8_t hash[SHA512_DIGEST_LENGTH];\n\n  public:\n    enum { DIGEST_SIZE = SHA512_DIGEST_LENGTH };\n\n    SHA512Hasher() { SHA512_Init(&sha512_ctx); }\n\n    void update(const uint8_t* data, size_t data_size) {\n        SHA512_Update(&sha512_ctx, data, data_size);\n    }\n\n    const uint8_t* finalize() {\n        SHA512_Final(hash, &sha512_ctx);\n        return hash;\n    }\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/avb_util_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <endian.h>\n\n#include <random>\n\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <base/files/file_util.h>\n#include <libavb/libavb.h>\n\n#include \"avb_util.h\"\n#include \"fs_avb/fs_avb_util.h\"\n#include \"fs_avb_test_util.h\"\n\n// Target classes or functions to test:\nusing android::fs_mgr::AvbPartitionToDevicePatition;\nusing android::fs_mgr::DeriveAvbPartitionName;\nusing android::fs_mgr::FstabEntry;\nusing android::fs_mgr::GetAvbFooter;\nusing android::fs_mgr::GetAvbPropertyDescriptor;\nusing android::fs_mgr::GetChainPartitionInfo;\nusing android::fs_mgr::GetTotalSize;\nusing android::fs_mgr::LoadAndVerifyVbmetaByPartition;\nusing android::fs_mgr::LoadAndVerifyVbmetaByPath;\nusing android::fs_mgr::ValidatePublicKeyBlob;\nusing android::fs_mgr::VBMetaData;\nusing android::fs_mgr::VBMetaVerifyResult;\nusing android::fs_mgr::VerifyVBMetaData;\nusing android::fs_mgr::VerifyVBMetaSignature;\n\nnamespace fs_avb_host_test {\n\nclass AvbUtilTest : public BaseFsAvbTest {\n  public:\n    AvbUtilTest(){};\n\n  protected:\n    ~AvbUtilTest(){};\n    // Helper function for VerifyVBMetaSignature test. Modifies vbmeta.data()\n    // in a number of places at |offset| of size |length| and checks that\n    // VerifyVBMetaSignature() returns |expected_result|.\n    bool TestVBMetaModification(VBMetaVerifyResult expected_result, const VBMetaData& vbmeta,\n                                size_t offset, size_t length);\n    // Modifies a random bit for a file, in the range of [offset, offset + length - 1].\n    void ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length);\n\n    // Loads the content of avb_image_path and comparies it with the content of vbmeta.\n    bool CompareVBMeta(const base::FilePath& avb_image_path, const VBMetaData& expected_vbmeta);\n\n    // Sets the flas in vbmeta header, the image_path could be a vbmeta.img or a system.img.\n    void SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags);\n};\n\nvoid AvbUtilTest::SetVBMetaFlags(const base::FilePath& image_path, uint32_t flags) {\n    if (!base::PathExists(image_path)) return;\n\n    std::string image_file_name = image_path.RemoveExtension().BaseName().value();\n    bool is_vbmeta_partition =\n        android::base::StartsWithIgnoreCase(image_file_name, \"vbmeta\");\n\n    android::base::unique_fd fd(open(image_path.value().c_str(), O_RDWR | O_CLOEXEC));\n    EXPECT_TRUE(fd > 0);\n\n    uint64_t vbmeta_offset = 0;  // for vbmeta.img\n    if (!is_vbmeta_partition) {\n        std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);\n        EXPECT_NE(nullptr, footer);\n        vbmeta_offset = footer->vbmeta_offset;\n    }\n\n    auto flags_offset = vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags);\n    uint32_t flags_data = htobe32(flags);\n    EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));\n    EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));\n}\n\nTEST_F(AvbUtilTest, AvbPartitionToDevicePatition) {\n    EXPECT_EQ(\"system\", AvbPartitionToDevicePatition(\"system\", \"\", \"\"));\n    EXPECT_EQ(\"system\", AvbPartitionToDevicePatition(\"system\", \"\", \"_b\"));\n\n    EXPECT_EQ(\"system_a\", AvbPartitionToDevicePatition(\"system\", \"_a\", \"\"));\n    EXPECT_EQ(\"system_a\", AvbPartitionToDevicePatition(\"system\", \"_a\", \"_b\"));\n\n    EXPECT_EQ(\"system_b\", AvbPartitionToDevicePatition(\"system_other\", \"\", \"_b\"));\n    EXPECT_EQ(\"system_b\", AvbPartitionToDevicePatition(\"system_other\", \"_a\", \"_b\"));\n}\n\nTEST_F(AvbUtilTest, DeriveAvbPartitionName) {\n    // The fstab_entry to test.\n    FstabEntry fstab_entry = {\n            .blk_device = \"/dev/block/dm-1\",  // a dm-linear device (logical)\n            .logical_partition_name = \"system\",\n            .mount_point = \"/system\",\n            .fs_type = \"ext4\",\n    };\n\n    // Logical partitions.\n    // non-A/B\n    fstab_entry.fs_mgr_flags.logical = true;\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_dont_care\", \"_dont_care\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_b\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"\", \"\"));\n    // Active slot.\n    fstab_entry.fs_mgr_flags.slot_select = true;\n    fstab_entry.logical_partition_name = \"system_a\";\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_dont_care\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_b\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"\"));\n    EXPECT_EQ(\"system_a\", DeriveAvbPartitionName(fstab_entry, \"_wont_erase_a\", \"_dont_care\"));\n    // The other slot.\n    fstab_entry.fs_mgr_flags.slot_select = false;\n    fstab_entry.fs_mgr_flags.slot_select_other = true;\n    fstab_entry.logical_partition_name = \"system_b\";\n    EXPECT_EQ(\"system_other\", DeriveAvbPartitionName(fstab_entry, \"_dont_care\", \"_b\"));\n    EXPECT_EQ(\"system_other\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_b\"));\n    EXPECT_EQ(\"system_other\", DeriveAvbPartitionName(fstab_entry, \"\", \"_b\"));\n    EXPECT_EQ(\"system_b_other\", DeriveAvbPartitionName(fstab_entry, \"_dont_care\", \"_wont_erase_b\"));\n\n    // Non-logical partitions.\n    // non-A/B.\n    fstab_entry.fs_mgr_flags.logical = false;\n    fstab_entry.fs_mgr_flags.slot_select = false;\n    fstab_entry.fs_mgr_flags.slot_select_other = false;\n    fstab_entry.blk_device = \"/dev/block/by-name/system\";\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_dont_care\", \"_dont_care\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_b\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"\", \"\"));\n    // Active slot _a.\n    fstab_entry.fs_mgr_flags.slot_select = true;\n    fstab_entry.blk_device = \"/dev/block/by-name/system_a\";\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_dont_care\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_b\"));\n    EXPECT_EQ(\"system\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"\"));\n    EXPECT_EQ(\"system_a\", DeriveAvbPartitionName(fstab_entry, \"_wont_erase_a\", \"_dont_care\"));\n    // Inactive slot _b.\n    fstab_entry.fs_mgr_flags.slot_select = false;\n    fstab_entry.fs_mgr_flags.slot_select_other = true;\n    fstab_entry.blk_device = \"/dev/block/by-name/system_b\";\n    EXPECT_EQ(\"system_other\", DeriveAvbPartitionName(fstab_entry, \"dont_care\", \"_b\"));\n    EXPECT_EQ(\"system_other\", DeriveAvbPartitionName(fstab_entry, \"_a\", \"_b\"));\n    EXPECT_EQ(\"system_other\", DeriveAvbPartitionName(fstab_entry, \"\", \"_b\"));\n    EXPECT_EQ(\"system_b_other\", DeriveAvbPartitionName(fstab_entry, \"dont_care\", \"_wont_erase_b\"));\n}\n\nTEST_F(AvbUtilTest, GetFdTotalSize) {\n    // Generates a raw test.img via BaseFsAvbTest.\n    const size_t image_size = 5 * 1024 * 1024;\n    base::FilePath image_path = GenerateImage(\"test.img\", image_size);\n\n    // Checks file size is as expected via base::GetFileSize().\n    int64_t file_size;\n    ASSERT_TRUE(base::GetFileSize(image_path, &file_size));\n    EXPECT_EQ(image_size, file_size);\n\n    // Checks file size is expected via libfs_avb internal utils.\n    auto fd = OpenUniqueReadFd(image_path);\n    EXPECT_EQ(image_size, GetTotalSize(fd));\n}\n\nTEST_F(AvbUtilTest, GetFdTotalSizeWithOffset) {\n    // Generates a raw test.img via BaseFsAvbTest.\n    const size_t image_size = 10 * 1024 * 1024;\n    base::FilePath image_path = GenerateImage(\"test.img\", image_size);\n\n    // Checks file size is expected even with a non-zero offset at the beginning.\n    auto fd = OpenUniqueReadFd(image_path);\n    off_t initial_offset = 2019;\n    EXPECT_EQ(initial_offset, lseek(fd, initial_offset, SEEK_SET));\n    EXPECT_EQ(image_size, GetTotalSize(fd));            // checks that total size is still returned.\n    EXPECT_EQ(initial_offset, lseek(fd, 0, SEEK_CUR));  // checks original offset is restored.\n}\n\nTEST_F(AvbUtilTest, GetAvbFooter) {\n    // Generates a raw system.img\n    const size_t image_size = 10 * 1024 * 1024;\n    const size_t partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", image_size);\n    EXPECT_NE(0U, system_path.value().size());\n\n    // Checks image size is as expected.\n    int64_t file_size;\n    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));\n    EXPECT_EQ(image_size, file_size);\n\n    // Appends AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", partition_size, \"SHA512_RSA8192\", 20,\n                 data_dir_.Append(\"testkey_rsa8192.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Checks partition size is as expected, after adding footer.\n    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));\n    EXPECT_EQ(partition_size, file_size);\n\n    // Checks avb footer and avb vbmeta.\n    EXPECT_EQ(\n            \"Footer version:           1.0\\n\"\n            \"Image size:               15728640 bytes\\n\"\n            \"Original image size:      10485760 bytes\\n\"\n            \"VBMeta offset:            10661888\\n\"\n            \"VBMeta size:              3648 bytes\\n\"\n            \"--\\n\"\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     1088 bytes\\n\"\n            \"Auxiliary Block:          2304 bytes\\n\"\n            \"Public key (sha1):        5227b569de003adc7f8ec3fc03e05dfbd969abad\\n\"\n            \"Algorithm:                SHA512_RSA8192\\n\"\n            \"Rollback Index:           20\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Hashtree descriptor:\\n\"\n            \"      Version of dm-verity:  1\\n\"\n            \"      Image Size:            10485760 bytes\\n\"\n            \"      Tree Offset:           10485760\\n\"\n            \"      Tree Size:             86016 bytes\\n\"\n            \"      Data Block Size:       4096 bytes\\n\"\n            \"      Hash Block Size:       4096 bytes\\n\"\n            \"      FEC num roots:         2\\n\"\n            \"      FEC offset:            10571776\\n\"\n            \"      FEC size:              90112 bytes\\n\"\n            \"      Hash Algorithm:        sha1\\n\"\n            \"      Partition Name:        system\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\\n\"\n            \"      Flags:                 0\\n\",\n            InfoImage(system_path));\n\n    // Checks each field from GetAvbFooter(fd).\n    auto fd = OpenUniqueReadFd(system_path);\n    auto footer = GetAvbFooter(fd);\n    EXPECT_NE(nullptr, footer);\n    EXPECT_EQ(10485760, footer->original_image_size);\n    EXPECT_EQ(10661888, footer->vbmeta_offset);\n    EXPECT_EQ(3648, footer->vbmeta_size);\n}\n\nTEST_F(AvbUtilTest, GetAvbFooterErrorVerification) {\n    // Generates a raw system.img\n    const size_t image_size = 5 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", image_size);\n\n    // Checks each field from GetAvbFooter(fd).\n    auto fd = OpenUniqueReadFd(system_path);\n    auto footer = GetAvbFooter(fd);\n    EXPECT_EQ(nullptr, footer);\n}\n\nTEST_F(AvbUtilTest, GetAvbFooterInsufficientSize) {\n    // Generates a raw system.img\n    const size_t image_size = AVB_FOOTER_SIZE - 10;\n    base::FilePath system_path = GenerateImage(\"system.img\", image_size);\n\n    // Checks each field from GetAvbFooter(fd).\n    auto fd = OpenUniqueReadFd(system_path);\n    auto footer = GetAvbFooter(fd);\n    EXPECT_EQ(nullptr, footer);\n}\n\nTEST_F(AvbUtilTest, GetAvbPropertyDescriptor_Basic) {\n    // Makes a vbmeta.img with some properties.\n    GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA4096\", 0, data_dir_.Append(\"testkey_rsa4096.pem\"),\n                        {}, /* include_descriptor_image_paths */\n                        {}, /* chain_partitions */\n                        \"--prop foo:android \"\n                        \"--prop bar:treble \"\n                        \"--internal_release_string \\\"unit test\\\" \");\n    auto vbmeta = LoadVBMetaData(\"vbmeta.img\");\n\n    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.\n    std::vector<VBMetaData> vbmeta_images;\n    vbmeta_images.emplace_back(std::move(vbmeta));\n\n    EXPECT_EQ(\"android\", GetAvbPropertyDescriptor(\"foo\", vbmeta_images));\n    EXPECT_EQ(\"treble\", GetAvbPropertyDescriptor(\"bar\", vbmeta_images));\n    EXPECT_EQ(\"\", GetAvbPropertyDescriptor(\"non-existent\", vbmeta_images));\n}\n\nTEST_F(AvbUtilTest, GetAvbPropertyDescriptor_SecurityPatchLevel) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--prop com.android.build.system.security_patch:2019-04-05 \"\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    // Makes a vbmeta.img including the 'system' chained descriptor.\n    GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA4096\", 0, data_dir_.Append(\"testkey_rsa4096.pem\"),\n                        {boot_path},                         /* include_descriptor_image_paths */\n                        {{\"system\", 3, rsa4096_public_key}}, /* chain_partitions */\n                        \"--internal_release_string \\\"unit test\\\"\");\n\n    auto vbmeta = LoadVBMetaData(\"vbmeta.img\");\n    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, \"system-vbmeta.img\");\n\n    // Puts the vbmeta into a vector, for GetAvbPropertyDescriptor to use.\n    std::vector<VBMetaData> vbmeta_images;\n    vbmeta_images.emplace_back(std::move(vbmeta));\n    vbmeta_images.emplace_back(std::move(system_vbmeta));\n\n    EXPECT_EQ(\"2019-04-05\",\n              GetAvbPropertyDescriptor(\"com.android.build.system.security_patch\", vbmeta_images));\n}\n\nTEST_F(AvbUtilTest, GetVBMetaHeader) {\n    // Generates a raw boot.img\n    const size_t image_size = 5 * 1024 * 1024;\n    const size_t partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", image_size);\n    // Appends AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", partition_size, \"SHA256_RSA4096\", 10,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.\n    base::FilePath boot_vbmeta = ExtractVBMetaImage(boot_path, \"boot-vbmeta.img\");\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     576 bytes\\n\"\n            \"Auxiliary Block:          1216 bytes\\n\"\n            \"Public key (sha1):        2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"Algorithm:                SHA256_RSA4096\\n\"\n            \"Rollback Index:           10\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Hash descriptor:\\n\"\n            \"      Image Size:            5242880 bytes\\n\"\n            \"      Hash Algorithm:        sha256\\n\"\n            \"      Partition Name:        boot\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Digest:                \"\n            \"222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\\n\"\n            \"      Flags:                 0\\n\",\n            InfoImage(\"boot-vbmeta.img\"));\n\n    // Creates a VBMetaData with the content from boot-vbmeta.img.\n    std::string content;\n    EXPECT_TRUE(base::ReadFileToString(boot_vbmeta, &content));\n    VBMetaData vbmeta((uint8_t*)content.data(), content.size(), \"boot-vbmeta\");\n    EXPECT_EQ(content.size(), vbmeta.size());\n\n    // Checks each field returned from GetVBMetaHeader().\n    auto vbmeta_header = vbmeta.GetVBMetaHeader(false /* update_vbmeta_size */);\n    EXPECT_NE(nullptr, vbmeta_header);\n    EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);\n    EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);\n    EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);\n    EXPECT_EQ(0, vbmeta_header->hash_offset);\n    EXPECT_EQ(32, vbmeta_header->hash_size);\n    EXPECT_EQ(32, vbmeta_header->signature_offset);\n    EXPECT_EQ(512, vbmeta_header->signature_size);\n    EXPECT_EQ(176, vbmeta_header->public_key_offset);\n    EXPECT_EQ(1032, vbmeta_header->public_key_size);\n    EXPECT_EQ(0, vbmeta_header->descriptors_offset);\n    EXPECT_EQ(176, vbmeta_header->descriptors_size);\n    EXPECT_EQ(10, vbmeta_header->rollback_index);\n    EXPECT_EQ(0, vbmeta_header->flags);\n    EXPECT_EQ(\"unit test\", std::string((const char*)vbmeta_header->release_string));\n\n    // Appends some garbage to the end of the vbmeta buffer, checks it still can work.\n    std::string padding(2020, 'A');  // Generate a padding with length 2020.\n    std::string content_padding = content + padding;\n    VBMetaData vbmeta_padding((const uint8_t*)content_padding.data(), content_padding.size(),\n                              \"boot\");\n    EXPECT_EQ(content_padding.size(), vbmeta_padding.size());\n\n    // Checks each field still can be parsed properly, even with garbage padding.\n    vbmeta_header = vbmeta_padding.GetVBMetaHeader(false /* update_vbmeta_size */);\n    EXPECT_NE(nullptr, vbmeta_header);\n    EXPECT_EQ(576, vbmeta_header->authentication_data_block_size);\n    EXPECT_EQ(1216, vbmeta_header->auxiliary_data_block_size);\n    EXPECT_EQ(AVB_ALGORITHM_TYPE_SHA256_RSA4096, vbmeta_header->algorithm_type);\n    EXPECT_EQ(0, vbmeta_header->hash_offset);\n    EXPECT_EQ(32, vbmeta_header->hash_size);\n    EXPECT_EQ(32, vbmeta_header->signature_offset);\n    EXPECT_EQ(512, vbmeta_header->signature_size);\n    EXPECT_EQ(176, vbmeta_header->public_key_offset);\n    EXPECT_EQ(1032, vbmeta_header->public_key_size);\n    EXPECT_EQ(0, vbmeta_header->descriptors_offset);\n    EXPECT_EQ(176, vbmeta_header->descriptors_size);\n    EXPECT_EQ(10, vbmeta_header->rollback_index);\n    EXPECT_EQ(0, vbmeta_header->flags);\n    EXPECT_EQ(\"unit test\", std::string((const char*)vbmeta_header->release_string));\n\n    // Checks vbmeta size is updated to the actual size without padding.\n    vbmeta_header = vbmeta_padding.GetVBMetaHeader(true /* update_vbmeta_size */);\n    EXPECT_EQ(content_padding.size() - padding.size(), vbmeta_padding.size());\n}\n\nTEST_F(AvbUtilTest, ValidatePublicKeyBlob) {\n    // Generates a raw key.bin\n    const size_t key_size = 2048;\n    base::FilePath key_path = GenerateImage(\"key.bin\", key_size);\n\n    uint8_t key_data[key_size];\n    EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));\n\n    std::string expected_key_blob;\n    EXPECT_TRUE(base::ReadFileToString(key_path, &expected_key_blob));\n    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));\n\n    key_data[10] ^= 0x80;  // toggles a bit and expects a failure\n    EXPECT_FALSE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));\n    key_data[10] ^= 0x80;  // toggles the bit again, should pass\n    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));\n}\n\nTEST_F(AvbUtilTest, VerifyEmptyPublicKeyBlob) {\n    // Generates a raw key.bin\n    const size_t key_size = 2048;\n    base::FilePath key_path = GenerateImage(\"key.bin\", key_size);\n\n    uint8_t key_data[key_size];\n    EXPECT_EQ(key_size, base::ReadFile(key_path, (char*)key_data, key_size));\n\n    std::string expected_key_blob = \"\";  // empty means no expectation, thus return true.\n    EXPECT_TRUE(ValidatePublicKeyBlob(key_data, key_size, expected_key_blob));\n}\n\nTEST_F(AvbUtilTest, ValidatePublicKeyBlob_MultipleAllowedKeys) {\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    base::FilePath rsa8192_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa8192.pem\"));\n\n    std::vector<std::string> allowed_key_paths;\n    allowed_key_paths.push_back(rsa2048_public_key.value());\n    allowed_key_paths.push_back(rsa4096_public_key.value());\n\n    std::string expected_key_blob_2048;\n    EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));\n    std::string expected_key_blob_4096;\n    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));\n    std::string expected_key_blob_8192;\n    EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));\n\n    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_2048, allowed_key_paths));\n    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_4096, allowed_key_paths));\n\n    EXPECT_FALSE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));\n    EXPECT_FALSE(ValidatePublicKeyBlob(\"invalid_content\", allowed_key_paths));\n    EXPECT_FALSE(ValidatePublicKeyBlob(\"\", allowed_key_paths));\n\n    allowed_key_paths.push_back(rsa8192_public_key.value());\n    EXPECT_TRUE(ValidatePublicKeyBlob(expected_key_blob_8192, allowed_key_paths));\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaSignature) {\n    const size_t image_size = 10 * 1024 * 1024;\n    const size_t partition_size = 15 * 1024 * 1024;\n    auto signing_key = data_dir_.Append(\"testkey_rsa4096.pem\");\n    auto vbmeta = GenerateImageAndExtractVBMetaData(\"system\", image_size, partition_size,\n                                                    \"hashtree\", signing_key, \"SHA256_RSA4096\",\n                                                    10 /* rollback_index */);\n\n    auto expected_public_key_blob = ExtractPublicKeyAvbBlob(signing_key);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              VerifyVBMetaSignature(vbmeta, expected_public_key_blob,\n                                    nullptr /* out_public_key_data */));\n\n    // Converts the expected key into an 'unexpected' key.\n    expected_public_key_blob[10] ^= 0x80;\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,\n              VerifyVBMetaSignature(vbmeta, expected_public_key_blob,\n                                    nullptr /* out_public_key_data */));\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaSignatureOutputPublicKeyData) {\n    const size_t image_size = 10 * 1024 * 1024;\n    const size_t partition_size = 15 * 1024 * 1024;\n    auto signing_key = data_dir_.Append(\"testkey_rsa4096.pem\");\n    auto vbmeta = GenerateImageAndExtractVBMetaData(\"system\", image_size, partition_size,\n                                                    \"hashtree\", signing_key, \"SHA256_RSA4096\",\n                                                    10 /* rollback_index */);\n    std::string out_public_key_data;\n    auto expected_public_key_blob = ExtractPublicKeyAvbBlob(signing_key);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              VerifyVBMetaSignature(vbmeta, expected_public_key_blob, &out_public_key_data));\n    EXPECT_EQ(out_public_key_data, expected_public_key_blob);\n\n    // Converts the expected key into an 'unexpected' key.\n    expected_public_key_blob[10] ^= 0x80;\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,\n              VerifyVBMetaSignature(vbmeta, expected_public_key_blob, &out_public_key_data));\n    EXPECT_NE(out_public_key_data, expected_public_key_blob);\n}\n\nbool AvbUtilTest::TestVBMetaModification(VBMetaVerifyResult expected_result,\n                                         const VBMetaData& vbmeta, size_t offset, size_t length) {\n    uint8_t* d = reinterpret_cast<uint8_t*>(vbmeta.data());\n    const int kNumCheckIntervals = 8;\n\n    // Tests |kNumCheckIntervals| modifications in the start, middle, and\n    // end of the given sub-array at offset with size.\n    for (int n = 0; n <= kNumCheckIntervals; n++) {\n        size_t o = std::min(length * n / kNumCheckIntervals, length - 1) + offset;\n        d[o] ^= 0x80;\n        VBMetaVerifyResult result = VerifyVBMetaSignature(vbmeta, \"\" /* expected_public_key_blob */,\n                                                          nullptr /* out_public_key_data */);\n        d[o] ^= 0x80;\n        if (result != expected_result) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaSignatureWithModification) {\n    const size_t image_size = 10 * 1024 * 1024;\n    const size_t partition_size = 15 * 1024 * 1024;\n    auto signing_key = data_dir_.Append(\"testkey_rsa4096.pem\");\n    auto vbmeta = GenerateImageAndExtractVBMetaData(\"system\", image_size, partition_size,\n                                                    \"hashtree\", signing_key, \"SHA256_RSA4096\",\n                                                    10 /* rollback_index */);\n\n    auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);\n    size_t header_block_offset = 0;\n    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);\n    size_t auxiliary_block_offset =\n            authentication_block_offset + header->authentication_data_block_size;\n\n    // Should detect modifications in the auxiliary data block.\n    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,\n                                       auxiliary_block_offset, header->auxiliary_data_block_size));\n\n    // Sholud detect modifications in the hash part of authentication data block.\n    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,\n                                       authentication_block_offset + header->hash_offset,\n                                       header->hash_size));\n\n    // Sholud detect modifications in the signature part of authentication data block.\n    EXPECT_TRUE(TestVBMetaModification(VBMetaVerifyResult::kErrorVerification, vbmeta,\n                                       authentication_block_offset + header->signature_offset,\n                                       header->signature_size));\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaSignatureNotSigned) {\n    const size_t image_size = 10 * 1024 * 1024;\n    const size_t partition_size = 15 * 1024 * 1024;\n    auto vbmeta = GenerateImageAndExtractVBMetaData(\n            \"system\", image_size, partition_size, \"hashtree\", {} /* avb_signing_key */,\n            \"\" /* avb_algorithm */, 10 /* rollback_index */);\n\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,\n              VerifyVBMetaSignature(vbmeta, \"\" /* expected_public_key_blob */,\n                                    nullptr /* out_public_key_data */));\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaSignatureInvalidVBMeta) {\n    const size_t buffer_size = 5 * 1024 * 1024;\n    std::vector<uint8_t> vbmeta_buffer(buffer_size);\n    for (size_t n = 0; n < buffer_size; n++) {\n        vbmeta_buffer[n] = uint8_t(n);\n    }\n\n    VBMetaData invalid_vbmeta((const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(),\n                              \"invalid_vbmeta\");\n    EXPECT_EQ(VBMetaVerifyResult::kError,\n              VerifyVBMetaSignature(invalid_vbmeta, \"\" /* expected_public_key_blob */,\n                                    nullptr /* out_public_Key_data */));\n}\n\nbool AvbUtilTest::CompareVBMeta(const base::FilePath& avb_image_path,\n                                const VBMetaData& expected_vbmeta) {\n    if (!base::PathExists(avb_image_path)) return false;\n\n    std::string image_file_name = avb_image_path.RemoveExtension().BaseName().value();\n\n    base::FilePath extracted_vbmeta_path;\n    if (android::base::StartsWithIgnoreCase(image_file_name, \"vbmeta\")) {\n        extracted_vbmeta_path = avb_image_path;  // no need to extract if it's a vbmeta image.\n    } else {\n        extracted_vbmeta_path = ExtractVBMetaImage(avb_image_path, image_file_name + \"-vbmeta.img\");\n    }\n\n    // Gets file size of the vbmeta image.\n    int64_t extracted_vbmeta_size;\n    EXPECT_TRUE(base::GetFileSize(extracted_vbmeta_path, &extracted_vbmeta_size));\n\n    // Reads the vbmeta into a vector.\n    std::vector<uint8_t> extracted_vbmeta_content(extracted_vbmeta_size);\n    EXPECT_TRUE(base::ReadFile(extracted_vbmeta_path,\n                               reinterpret_cast<char*>(extracted_vbmeta_content.data()),\n                               extracted_vbmeta_size));\n\n    // Compares extracted_vbmeta_content with the expected_vbmeta.\n    EXPECT_EQ(expected_vbmeta.size(), extracted_vbmeta_size);\n    return memcmp(reinterpret_cast<void*>(extracted_vbmeta_content.data()),\n                  reinterpret_cast<void*>(expected_vbmeta.data()), extracted_vbmeta_size) == 0;\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaDataWithoutFooter) {\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     1088 bytes\\n\"\n            \"Auxiliary Block:          3840 bytes\\n\"\n            \"Public key (sha1):        5227b569de003adc7f8ec3fc03e05dfbd969abad\\n\"\n            \"Algorithm:                SHA256_RSA8192\\n\"\n            \"Rollback Index:           0\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Chain Partition descriptor:\\n\"\n            \"      Partition Name:          boot\\n\"\n            \"      Rollback Index Location: 1\\n\"\n            \"      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\\n\"\n            \"      Flags:                   0\\n\"\n            \"    Chain Partition descriptor:\\n\"\n            \"      Partition Name:          system\\n\"\n            \"      Rollback Index Location: 2\\n\"\n            \"      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"      Flags:                   0\\n\",\n            InfoImage(\"vbmeta.img\"));\n\n    android::base::unique_fd fd(open(vbmeta_path.value().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_TRUE(fd > 0);\n\n    VBMetaVerifyResult verify_result;\n    std::string out_public_key_data;\n    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(\n            fd, \"vbmeta\", \"\" /*expected_public_key_blob */, &out_public_key_data, &verify_result);\n    EXPECT_TRUE(vbmeta != nullptr);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);\n\n    auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append(\"testkey_rsa8192.pem\"));\n    EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);\n\n    // Checkes the returned vbmeta content is the same as that extracted via avbtool.\n    vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, *vbmeta));\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaDataWithFooter) {\n    const size_t image_size = 10 * 1024 * 1024;\n    const size_t partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", image_size);\n\n    // Appends AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", partition_size, \"SHA512_RSA8192\", 20,\n                 data_dir_.Append(\"testkey_rsa8192.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_TRUE(fd > 0);\n\n    VBMetaVerifyResult verify_result;\n    std::string out_public_key_data;\n    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(\n            fd, \"system\", \"\" /*expected_public_key_blob */, &out_public_key_data, &verify_result);\n    EXPECT_TRUE(vbmeta != nullptr);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);\n\n    auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append(\"testkey_rsa8192.pem\"));\n    EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);\n\n    // Checkes the returned vbmeta content is the same as that extracted via avbtool.\n    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));\n}\n\n// Modifies a random bit for a file, in the range of [offset, offset + length - 1].\n// Length < 0 means only resets previous modification without introducing new modification.\nvoid AvbUtilTest::ModifyFile(const base::FilePath& file_path, size_t offset, ssize_t length) {\n    static int last_modified_location = -1;\n    static std::string last_file_path;\n\n    int64_t file_size;\n    ASSERT_TRUE(base::GetFileSize(file_path, &file_size));\n\n    std::vector<uint8_t> file_content(file_size);\n    ASSERT_TRUE(base::ReadFile(file_path, reinterpret_cast<char*>(file_content.data()), file_size));\n\n    // Resets previous modification for consecutive calls on the same file.\n    if (last_file_path == file_path.value()) {\n        file_content[last_modified_location] ^= 0x80;\n    }\n\n    // Introduces a new modification.\n    if (length > 0) {\n        // mersenne_twister_engine seeded with the default seed source.\n        static std::mt19937 gen(std::random_device{}());\n        std::uniform_int_distribution<> rand_distribution(offset, offset + length - 1);\n        int modify_location = rand_distribution(gen);\n        file_content[modify_location] ^= 0x80;\n        last_file_path = file_path.value();\n        last_modified_location = modify_location;\n    }\n\n    ASSERT_EQ(file_size, static_cast<const size_t>(base::WriteFile(\n                                 file_path, reinterpret_cast<const char*>(file_content.data()),\n                                 file_content.size())));\n}\n\nTEST_F(AvbUtilTest, VerifyVBMetaDataError) {\n    const size_t image_size = 10 * 1024 * 1024;\n    const size_t partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", image_size);\n\n    // Appends AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", partition_size, \"SHA512_RSA8192\", 20,\n                 data_dir_.Append(\"testkey_rsa8192.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    android::base::unique_fd fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_TRUE(fd > 0);\n\n    std::unique_ptr<AvbFooter> footer = GetAvbFooter(fd);\n    EXPECT_TRUE(footer != nullptr);\n\n    VBMetaVerifyResult verify_result;\n    std::string out_public_key_data;\n    std::unique_ptr<VBMetaData> vbmeta = VerifyVBMetaData(\n            fd, \"system\", \"\" /*expected_public_key_blob */, &out_public_key_data, &verify_result);\n    ASSERT_EQ(0, close(fd.release()));\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);\n\n    auto rsa8192_public_key_blob = ExtractPublicKeyAvbBlob(data_dir_.Append(\"testkey_rsa8192.pem\"));\n    EXPECT_EQ(rsa8192_public_key_blob, out_public_key_data);\n\n    // Modifies hash and signature, checks there is verification error.\n    auto header = vbmeta->GetVBMetaHeader(true /* update_vbmeta_size */);\n    size_t header_block_offset = 0;\n    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);\n\n    // Modifies the hash.\n    ModifyFile(system_path,\n               footer->vbmeta_offset + authentication_block_offset + header->hash_offset,\n               header->hash_size);\n    android::base::unique_fd hash_modified_fd(\n            open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_TRUE(hash_modified_fd > 0);\n    // Should return ErrorVerification.\n    vbmeta = VerifyVBMetaData(hash_modified_fd, \"system\", \"\" /*expected_public_key_blob */,\n                              nullptr /* out_public_key_data */, &verify_result);\n    ASSERT_EQ(0, close(hash_modified_fd.release()));\n    EXPECT_NE(nullptr, vbmeta);\n    // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);\n\n    // Modifies the auxiliary data block.\n    size_t auxiliary_block_offset =\n            authentication_block_offset + header->authentication_data_block_size;\n    ModifyFile(system_path, footer->vbmeta_offset + auxiliary_block_offset,\n               header->auxiliary_data_block_size);\n    android::base::unique_fd aux_modified_fd(\n            open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_TRUE(aux_modified_fd > 0);\n    // Should return ErrorVerification.\n    vbmeta = VerifyVBMetaData(aux_modified_fd, \"system\", \"\" /*expected_public_key_blob */,\n                              nullptr /* out_public_key_data */, &verify_result);\n    ASSERT_EQ(0, close(aux_modified_fd.release()));\n    EXPECT_NE(nullptr, vbmeta);\n    // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);\n\n    // Resets previous modification by setting offset to -1, and checks the verification can pass.\n    ModifyFile(system_path, 0 /* offset */, -1 /* length */);\n    android::base::unique_fd ok_fd(open(system_path.value().c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_TRUE(ok_fd > 0);\n    // Should return ResultOK..\n    vbmeta = VerifyVBMetaData(ok_fd, \"system\", \"\" /*expected_public_key_blob */,\n                              nullptr /* out_public_key_data */, &verify_result);\n    ASSERT_EQ(0, close(ok_fd.release()));\n    EXPECT_NE(nullptr, vbmeta);\n    // EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta)); // b/187303962.\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);\n}\n\nTEST_F(AvbUtilTest, GetChainPartitionInfo) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    // Makes a vbmeta_system.img including the 'system' chained descriptor.\n    GenerateVBMetaImage(\"vbmeta_system.img\", \"SHA256_RSA4096\", 0,\n                        data_dir_.Append(\"testkey_rsa4096.pem\"),\n                        {},                                  /* include_descriptor_image_paths */\n                        {{\"system\", 3, rsa4096_public_key}}, /* chain_partitions */\n                        \"--internal_release_string \\\"unit test\\\"\");\n\n    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.\n    GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0, data_dir_.Append(\"testkey_rsa8192.pem\"),\n                        {},                               /* include_descriptor_image_paths */\n                        {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                         {\"vbmeta_system\", 2, rsa4096_public_key}},\n                        \"--internal_release_string \\\"unit test\\\"\");\n\n    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.\n    EXPECT_EQ(\"6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n    // Loads the key blobs for comparison.\n    std::string expected_key_blob_2048;\n    EXPECT_TRUE(base::ReadFileToString(rsa2048_public_key, &expected_key_blob_2048));\n    std::string expected_key_blob_4096;\n    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));\n\n    // Checks chain descriptors in vbmeta.img\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     1088 bytes\\n\"\n            \"Auxiliary Block:          3840 bytes\\n\"\n            \"Public key (sha1):        5227b569de003adc7f8ec3fc03e05dfbd969abad\\n\"\n            \"Algorithm:                SHA256_RSA8192\\n\"\n            \"Rollback Index:           0\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Chain Partition descriptor:\\n\"\n            \"      Partition Name:          boot\\n\"\n            \"      Rollback Index Location: 1\\n\"\n            \"      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\\n\"\n            \"      Flags:                   0\\n\"\n            \"    Chain Partition descriptor:\\n\"\n            \"      Partition Name:          vbmeta_system\\n\"\n            \"      Rollback Index Location: 2\\n\"\n            \"      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"      Flags:                   0\\n\",\n            InfoImage(\"vbmeta.img\"));\n\n    bool fatal_error = false;\n    auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData(\"vbmeta.img\"), &fatal_error);\n    EXPECT_EQ(2, chained_descriptors.size());  // contains 'boot' and 'vbmeta_system'.\n    EXPECT_EQ(false, fatal_error);\n\n    EXPECT_EQ(\"boot\", chained_descriptors[0].partition_name);\n    EXPECT_EQ(expected_key_blob_2048, chained_descriptors[0].public_key_blob);\n\n    EXPECT_EQ(\"vbmeta_system\", chained_descriptors[1].partition_name);\n    EXPECT_EQ(expected_key_blob_4096, chained_descriptors[1].public_key_blob);\n\n    // Checks chain descriptors in vbmeta_system.img\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     576 bytes\\n\"\n            \"Auxiliary Block:          2176 bytes\\n\"\n            \"Public key (sha1):        2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"Algorithm:                SHA256_RSA4096\\n\"\n            \"Rollback Index:           0\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Chain Partition descriptor:\\n\"\n            \"      Partition Name:          system\\n\"\n            \"      Rollback Index Location: 3\\n\"\n            \"      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"      Flags:                   0\\n\",\n            InfoImage(\"vbmeta_system.img\"));\n\n    chained_descriptors = GetChainPartitionInfo(LoadVBMetaData(\"vbmeta_system.img\"), &fatal_error);\n    EXPECT_EQ(1, chained_descriptors.size());  // contains 'system' only.\n    EXPECT_EQ(false, fatal_error);\n    EXPECT_EQ(\"system\", chained_descriptors[0].partition_name);\n    EXPECT_EQ(expected_key_blob_4096, chained_descriptors[0].public_key_blob);\n}\n\nTEST_F(AvbUtilTest, GetChainPartitionInfoNone) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA4096\", 10,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA8192\", 20,\n                 data_dir_.Append(\"testkey_rsa8192.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Makes a vbmeta.img including both 'boot' and 'system' descriptors.\n    GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA2048\", 0, data_dir_.Append(\"testkey_rsa2048.pem\"),\n                        {boot_path, system_path}, /* include_descriptor_image_paths */\n                        {},                       /* chain_partitions */\n                        \"--internal_release_string \\\"unit test\\\"\");\n    EXPECT_EQ(\"a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     320 bytes\\n\"\n            \"Auxiliary Block:          960 bytes\\n\"\n            \"Public key (sha1):        cdbb77177f731920bbe0a0f94f84d9038ae0617d\\n\"\n            \"Algorithm:                SHA256_RSA2048\\n\"\n            \"Rollback Index:           0\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Hash descriptor:\\n\"\n            \"      Image Size:            5242880 bytes\\n\"\n            \"      Hash Algorithm:        sha256\\n\"\n            \"      Partition Name:        boot\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Digest:                \"\n            \"222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\\n\"\n            \"      Flags:                 0\\n\"\n            \"    Hashtree descriptor:\\n\"\n            \"      Version of dm-verity:  1\\n\"\n            \"      Image Size:            10485760 bytes\\n\"\n            \"      Tree Offset:           10485760\\n\"\n            \"      Tree Size:             86016 bytes\\n\"\n            \"      Data Block Size:       4096 bytes\\n\"\n            \"      Hash Block Size:       4096 bytes\\n\"\n            \"      FEC num roots:         2\\n\"\n            \"      FEC offset:            10571776\\n\"\n            \"      FEC size:              90112 bytes\\n\"\n            \"      Hash Algorithm:        sha1\\n\"\n            \"      Partition Name:        system\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\\n\"\n            \"      Flags:                 0\\n\",\n            InfoImage(\"vbmeta.img\"));\n\n    // Checks none of chain descriptors is found.\n    bool fatal_error = false;\n    auto chained_descriptors = GetChainPartitionInfo(LoadVBMetaData(\"vbmeta.img\"), &fatal_error);\n    EXPECT_EQ(0, chained_descriptors.size());  // There is no chain descriptors.\n    EXPECT_EQ(false, fatal_error);\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPath) {\n    // Generates a raw system_other.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system_other.img\", system_image_size);\n\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system_other\", system_partition_size, \"SHA512_RSA4096\",\n                 20, data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    std::string expected_key_blob_4096 =\n            ExtractPublicKeyAvbBlob(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    bool verification_disabled;\n    VBMetaVerifyResult verify_result;\n    std::string out_public_key_data;\n    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            false /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, &out_public_key_data, &verification_disabled,\n            &verify_result);\n\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess, verify_result);\n    EXPECT_EQ(false, verification_disabled);\n    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);\n\n    EXPECT_EQ(2112UL, vbmeta->size());\n    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());\n    EXPECT_EQ(\"system_other\", vbmeta->partition());\n    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathErrorVerification) {\n    // Generates a raw system_other.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system_other.img\", system_image_size);\n\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system_other\", system_partition_size, \"SHA512_RSA4096\",\n                 20, data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    std::string expected_key_blob_4096 =\n            ExtractPublicKeyAvbBlob(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    // Modifies the auxiliary data of system_other.img\n    auto fd = OpenUniqueReadFd(system_path);\n    auto system_footer = GetAvbFooter(fd);\n    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, \"system_other-vbmeta.img\");\n    auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);\n    size_t header_block_offset = 0;\n    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);\n    size_t auxiliary_block_offset =\n        authentication_block_offset + system_header->authentication_data_block_size;\n\n    // Modifies the hash.\n    ModifyFile(\n        system_path,\n        (system_footer->vbmeta_offset + authentication_block_offset + system_header->hash_offset),\n        system_header->hash_size);\n\n    VBMetaVerifyResult verify_result;\n    // Not allow verification error.\n    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            false /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,\n            nullptr /* verification_disabled */, &verify_result);\n    EXPECT_EQ(nullptr, vbmeta);\n\n    // Allow verification error.\n    vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            true /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,\n            nullptr /* verification_disabled */, &verify_result);\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);\n\n    EXPECT_EQ(2112UL, vbmeta->size());\n    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());\n    EXPECT_EQ(\"system_other\", vbmeta->partition());\n    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));\n\n    // Modifies the auxiliary data block.\n    ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,\n               system_header->auxiliary_data_block_size);\n\n    // Not allow verification error.\n    vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            false /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,\n            nullptr /* verification_disabled */, &verify_result);\n    EXPECT_EQ(nullptr, vbmeta);\n\n    // Allow verification error.\n    vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            true /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,\n            nullptr /* verification_disabled */, &verify_result);\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathUnexpectedPublicKey) {\n    // Generates a raw system_other.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system_other.img\", system_image_size);\n\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system_other\", system_partition_size, \"SHA512_RSA4096\",\n                 20, data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    std::string unexpected_key_blob_2048 =\n            ExtractPublicKeyAvbBlob(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    std::string expected_key_blob_4096 =\n            ExtractPublicKeyAvbBlob(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    // Uses the correct expected public key.\n    VBMetaVerifyResult verify_result;\n    std::string out_public_key_data;\n    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            false /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, &out_public_key_data,\n            nullptr /* verification_disabled */, &verify_result);\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(verify_result, VBMetaVerifyResult::kSuccess);\n    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);\n    EXPECT_EQ(2112UL, vbmeta->size());\n    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());\n    EXPECT_EQ(\"system_other\", vbmeta->partition());\n    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));\n\n    // Uses the wrong expected public key with allow_verification_error set to false.\n    vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", unexpected_key_blob_2048,\n            false /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, &out_public_key_data,\n            nullptr /* verification_disabled */, &verify_result);\n    EXPECT_EQ(nullptr, vbmeta);\n    // Checks out_public_key_data is still loaded properly, if the error is due\n    // to an unexpected public key instead of vbmeta image verification error.\n    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);\n\n    // Uses the wrong expected public key with allow_verification_error set to true.\n    vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", unexpected_key_blob_2048,\n            true /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, &out_public_key_data,\n            nullptr /* verification_disabled */, &verify_result);\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(expected_key_blob_4096, out_public_key_data);\n    EXPECT_EQ(verify_result, VBMetaVerifyResult::kErrorVerification);\n    EXPECT_EQ(2112UL, vbmeta->size());\n    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());\n    EXPECT_EQ(\"system_other\", vbmeta->partition());\n    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPathVerificationDisabled) {\n    // Generates a raw system_other.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system_other.img\", system_image_size);\n\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system_other\", system_partition_size, \"SHA512_RSA4096\",\n                 20, data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    base::FilePath rsa4096_public_key =\n        ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    std::string expected_key_blob_4096;\n    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));\n\n    // Sets disabled flag and expect the returned verification_disabled is true.\n    SetVBMetaFlags(system_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);\n    bool verification_disabled;\n    VBMetaVerifyResult verify_result;\n    std::string out_public_key_data;\n    std::unique_ptr<VBMetaData> vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            true /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,\n            &verification_disabled, &verify_result);\n\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification, verify_result);\n    EXPECT_EQ(true, verification_disabled);  // should be true.\n\n    EXPECT_EQ(2112UL, vbmeta->size());\n    EXPECT_EQ(system_path.value(), vbmeta->vbmeta_path());\n    EXPECT_EQ(\"system_other\", vbmeta->partition());\n    EXPECT_TRUE(CompareVBMeta(system_path, *vbmeta));\n\n    // Since the vbmeta flags is modified, vbmeta will be nullptr\n    // if verification error isn't allowed.\n    vbmeta = LoadAndVerifyVbmetaByPath(\n            system_path.value(), \"system_other\", expected_key_blob_4096,\n            false /* allow_verification_error */, false /* rollback_protection */,\n            false /* is_chained_vbmeta */, nullptr /* out_public_key_data */,\n            &verification_disabled, &verify_result);\n    EXPECT_EQ(nullptr, vbmeta);\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartition) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    // Makes a vbmeta_system.img including the 'system' chained descriptor.\n    auto vbmeta_system_path = GenerateVBMetaImage(\n            \"vbmeta_system.img\", \"SHA256_RSA4096\", 0, data_dir_.Append(\"testkey_rsa4096.pem\"),\n            {},                                  /* include_descriptor_image_paths */\n            {{\"system\", 3, rsa4096_public_key}}, /* chain_partitions */\n            \"--internal_release_string \\\"unit test\\\"\");\n\n    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"vbmeta_system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n\n    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.\n    EXPECT_EQ(\"6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n\n    // Starts to test LoadAndVerifyVbmetaByPartition.\n    std::vector<VBMetaData> vbmeta_images;\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, false /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n\n    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system\n    // Binary comparison for each vbmeta image.\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));\n    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));\n    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));\n\n    // Skip loading chained vbmeta images.\n    vbmeta_images.clear();\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, false /* allow_verification_error */,\n                  false /* load_chained_vbmeta */, true /* rollback_protection */,\n                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));\n    // Only vbmeta is loaded.\n    EXPECT_EQ(1UL, vbmeta_images.size());\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionWithSuffixes) {\n    // Tests the following chained partitions.\n    // vbmeta_a.img\n    // |--> boot_b.img (boot_other)\n    // |--> vbmeta_system_b.img (vbmeta_system_other)\n    //      |--> system_a.img\n\n    // Generates a raw boot_b.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot_b.img\", boot_image_size);\n\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system_a.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system_a.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    // Makes a vbmeta_system_b.img including the 'system' chained descriptor.\n    auto vbmeta_system_path = GenerateVBMetaImage(\n            \"vbmeta_system_b.img\", \"SHA256_RSA4096\", 0, data_dir_.Append(\"testkey_rsa4096.pem\"),\n            {},                                  /* include_descriptor_image_paths */\n            {{\"system\", 3, rsa4096_public_key}}, /* chain_partitions */\n            \"--internal_release_string \\\"unit test\\\"\");\n\n    // Makes a vbmeta_a.img includeing 'boot_other' and 'vbmeta_system_other' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\n            \"vbmeta_a.img\", \"SHA256_RSA8192\", 0, data_dir_.Append(\"testkey_rsa8192.pem\"),\n            {},                                     /* include_descriptor_image_paths */\n            {{\"boot_other\", 1, rsa2048_public_key}, /* chain_partitions */\n             {\"vbmeta_system_other\", 2, rsa4096_public_key}},\n            \"--internal_release_string \\\"unit test\\\"\");\n\n    // Starts to test LoadAndVerifyVbmetaByPartition with ab_suffix and ab_other_suffix.\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n\n    std::vector<VBMetaData> vbmeta_images;\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"_a\" /* ab_suffix */, \"_b\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, false /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n\n    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot_other, vbmeta_system_other and system\n    // Binary comparison for each vbmeta image.\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));\n    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));\n    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));\n\n    // Skips loading chained vbmeta images.\n    vbmeta_images.clear();\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"_a\" /* ab_suffix */, \"_b\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, false /* allow_verification_error */,\n                  false /* load_chained_vbmeta */, true /* rollback_protection */,\n                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));\n    // Only vbmeta is loaded.\n    EXPECT_EQ(1UL, vbmeta_images.size());\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n\n    // Using an invalid suffix for 'other' slot, checks it returns error.\n    EXPECT_EQ(VBMetaVerifyResult::kError,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"_a\" /* ab_suffix */,\n                  \"_invalid_suffix\" /* other_suffix */, \"\" /* expected_public_key_blob*/,\n                  false /* allow_verification_error */, true /* load_chained_vbmeta */,\n                  true /* rollback_protection */, vbmeta_image_path, false /* is_chained_vbmeta*/,\n                  &vbmeta_images));\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionErrorVerification) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n\n    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.\n    EXPECT_EQ(\"abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n\n    auto vbmeta = LoadVBMetaData(\"vbmeta.img\");\n\n    // Modifies hash, checks there is error if allow_verification_error is false.\n    auto header = vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);\n    size_t header_block_offset = 0;\n    size_t authentication_block_offset = header_block_offset + sizeof(AvbVBMetaImageHeader);\n\n    // Modifies the hash.\n    ModifyFile(vbmeta_path, authentication_block_offset + header->hash_offset, header->hash_size);\n\n    // Starts to test LoadAndVerifyVbmetaByPartition.\n    std::vector<VBMetaData> vbmeta_images;\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n    EXPECT_EQ(VBMetaVerifyResult::kError,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, false /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n    // Stops to load vbmeta because the top-level vbmeta has verification error.\n    EXPECT_EQ(0UL, vbmeta_images.size());\n\n    // Tries again with verification error allowed.\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\", /* other_suffix */\n                  \"\" /* expected_public_key_blob*/, true /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n\n    EXPECT_EQ(3UL, vbmeta_images.size());  // vbmeta, boot, and system\n    // Binary comparison for each vbmeta image.\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));\n    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[2]));\n\n    // Resets the modification of the hash.\n    ModifyFile(vbmeta_path, 0 /* offset */, -1 /* length */);\n\n    // Modifies the auxiliary data of system.img\n    auto fd = OpenUniqueReadFd(system_path);\n    auto system_footer = GetAvbFooter(fd);\n    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, \"system-vbmeta.img\");\n    auto system_header = system_vbmeta.GetVBMetaHeader(true /* update_vbmeta_size */);\n    size_t auxiliary_block_offset =\n            authentication_block_offset + system_header->authentication_data_block_size;\n\n    // Modifies the auxiliary data block.\n    ModifyFile(system_path, system_footer->vbmeta_offset + auxiliary_block_offset,\n               system_header->auxiliary_data_block_size);\n    vbmeta_images.clear();\n    EXPECT_EQ(VBMetaVerifyResult::kError,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, false /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n    // 'vbmeta', 'boot' but no 'system', because of verification error.\n    EXPECT_EQ(2UL, vbmeta_images.size());\n    // Binary comparison for the loaded 'vbmeta' and 'boot'.\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));\n\n    // Resets the modification of the auxiliary data.\n    ModifyFile(system_path, 0 /* offset */, -1 /* length */);\n\n    // Sets the vbmeta header flags on a chained partition, which introduces an error.\n    ModifyFile(system_path, system_footer->vbmeta_offset + offsetof(AvbVBMetaImageHeader, flags),\n               sizeof(uint32_t));\n    EXPECT_EQ(VBMetaVerifyResult::kError,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, true /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionVerificationDisabled) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n        ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n        ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    // Makes a vbmeta_system.img including the 'system' chained descriptor.\n    auto vbmeta_system_path = GenerateVBMetaImage(\n        \"vbmeta_system.img\", \"SHA256_RSA4096\", 0, data_dir_.Append(\"testkey_rsa4096.pem\"),\n        {},                                  /* include_descriptor_image_paths */\n        {{\"system\", 3, rsa4096_public_key}}, /* chain_partitions */\n        \"--internal_release_string \\\"unit test\\\"\");\n\n    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"vbmeta_system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n\n    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.\n    EXPECT_EQ(\"6f4bf815a651aa35ec7102a88b7906b91aef284bc5e20d0bf527c7d460da3266\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n\n    // Starts to test LoadAndVerifyVbmetaByPartition.\n    std::vector<VBMetaData> vbmeta_images;\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, false /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n\n    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system\n    // Binary comparison for each vbmeta image.\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));\n    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));\n    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));\n\n    // Sets VERIFICATION_DISABLED to the top-level vbmeta.img\n    SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);\n    vbmeta_images.clear();\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, true /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n    EXPECT_EQ(1UL, vbmeta_images.size());  // Only vbmeta is loaded\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n\n    // HASHTREE_DISABLED still loads the chained vbmeta.\n    SetVBMetaFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);\n    vbmeta_images.clear();\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  \"\" /* expected_public_key_blob*/, true /* allow_verification_error */,\n                  true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path,\n                  false /* is_chained_vbmeta*/, &vbmeta_images));\n    EXPECT_EQ(4UL, vbmeta_images.size());  // vbmeta, boot, vbmeta_system and system\n    // Binary comparison for each vbmeta image.\n    EXPECT_TRUE(CompareVBMeta(vbmeta_path, vbmeta_images[0]));\n    EXPECT_TRUE(CompareVBMeta(boot_path, vbmeta_images[1]));\n    EXPECT_TRUE(CompareVBMeta(vbmeta_system_path, vbmeta_images[2]));\n    EXPECT_TRUE(CompareVBMeta(system_path, vbmeta_images[3]));\n}\n\nTEST_F(AvbUtilTest, LoadAndVerifyVbmetaByPartitionUnexpectedPublicKey) {\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    base::FilePath rsa8192_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa8192.pem\"));\n\n    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n    std::string expected_key_blob_4096;\n    EXPECT_TRUE(base::ReadFileToString(rsa4096_public_key, &expected_key_blob_4096));\n    std::string expected_key_blob_8192;\n    EXPECT_TRUE(base::ReadFileToString(rsa8192_public_key, &expected_key_blob_8192));\n\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n    std::vector<VBMetaData> vbmeta_images;\n    // Uses the correct expected public key.\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  expected_key_blob_8192, true /* allow_verification_error */,\n                  false /* load_chained_vbmeta */, true /* rollback_protection */,\n                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));\n\n    // Uses the wrong expected public key with allow_verification_error set to true.\n    vbmeta_images.clear();\n    EXPECT_EQ(VBMetaVerifyResult::kErrorVerification,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  expected_key_blob_4096, true /* allow_verification_error */,\n                  false /* load_chained_vbmeta */, true /* rollback_protection */,\n                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));\n\n    // Uses the wrong expected public key with allow_verification_error set to false.\n    vbmeta_images.clear();\n    EXPECT_EQ(VBMetaVerifyResult::kError,\n              LoadAndVerifyVbmetaByPartition(\n                  \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n                  expected_key_blob_4096, false /* allow_verification_error */,\n                  false /* load_chained_vbmeta */, true /* rollback_protection */,\n                  vbmeta_image_path, false /* is_chained_vbmeta*/, &vbmeta_images));\n}\n\n}  // namespace fs_avb_host_test\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/basic_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fs_avb_test_util.h\"\n\n#include <stdlib.h>\n\n#include <android-base/file.h>\n#include <base/files/file_util.h>\n\nnamespace fs_avb_host_test {\n\nTEST_F(BaseFsAvbTest, GenerateImage) {\n    const size_t image_size = 5 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", image_size);\n    EXPECT_NE(0U, boot_path.value().size());\n\n    // Checks file size is as expected.\n    int64_t file_size;\n    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));\n    EXPECT_EQ(file_size, image_size);\n\n    // Checks file content is as expected.\n    std::vector<uint8_t> expected_content;\n    expected_content.resize(image_size);\n    for (size_t n = 0; n < image_size; n++) {\n        expected_content[n] = uint8_t(n);\n    }\n    std::vector<uint8_t> actual_content;\n    actual_content.resize(image_size);\n    EXPECT_TRUE(\n            base::ReadFile(boot_path, reinterpret_cast<char*>(actual_content.data()), image_size));\n    EXPECT_EQ(expected_content, actual_content);\n}\n\nTEST_F(BaseFsAvbTest, GenerateVBMetaImage) {\n    GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA2048\", 0, data_dir_.Append(\"testkey_rsa2048.pem\"),\n                        {}, /* include_descriptor_image_paths */\n                        {}, /* chain_partitions */\n                        \"--internal_release_string \\\"unit test\\\"\");\n    EXPECT_EQ(\"5eba9ad4e775645e7eac441a563c200681ae868158d06f6a6cd36d06c07bd781\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     320 bytes\\n\"\n            \"Auxiliary Block:          576 bytes\\n\"\n            \"Public key (sha1):        cdbb77177f731920bbe0a0f94f84d9038ae0617d\\n\"\n            \"Algorithm:                SHA256_RSA2048\\n\"\n            \"Rollback Index:           0\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    (none)\\n\",\n            InfoImage(\"vbmeta.img\"));\n}\n\nTEST_F(BaseFsAvbTest, AddHashFooter) {\n    // Generates a raw boot.img\n    const size_t image_size = 5 * 1024 * 1024;\n    const size_t partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", image_size);\n    EXPECT_NE(0U, boot_path.value().size());\n    // Checks file size is as expected.\n    int64_t file_size;\n    ASSERT_TRUE(base::GetFileSize(boot_path, &file_size));\n    EXPECT_EQ(file_size, image_size);\n    // Appends AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", partition_size, \"SHA256_RSA4096\", 10,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.\n    ExtractVBMetaImage(boot_path, \"boot-vbmeta.img\");\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     576 bytes\\n\"\n            \"Auxiliary Block:          1216 bytes\\n\"\n            \"Public key (sha1):        2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"Algorithm:                SHA256_RSA4096\\n\"\n            \"Rollback Index:           10\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Hash descriptor:\\n\"\n            \"      Image Size:            5242880 bytes\\n\"\n            \"      Hash Algorithm:        sha256\\n\"\n            \"      Partition Name:        boot\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Digest:                \"\n            \"222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\\n\"\n            \"      Flags:                 0\\n\",\n            InfoImage(\"boot-vbmeta.img\"));\n}\n\nTEST_F(BaseFsAvbTest, AddHashtreeFooter) {\n    // Generates a raw system.img\n    const size_t image_size = 50 * 1024 * 1024;\n    const size_t partition_size = 60 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", image_size);\n    EXPECT_NE(0U, system_path.value().size());\n    // Checks file size is as expected.\n    int64_t file_size;\n    ASSERT_TRUE(base::GetFileSize(system_path, &file_size));\n    EXPECT_EQ(file_size, image_size);\n    // Appends AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", partition_size, \"SHA512_RSA8192\", 20,\n                 data_dir_.Append(\"testkey_rsa8192.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n    // Extracts system vbmeta from system.img into system-vbmeta.img.\n    ExtractVBMetaImage(system_path, \"system-vbmeta.img\");\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     1088 bytes\\n\"\n            \"Auxiliary Block:          2304 bytes\\n\"\n            \"Public key (sha1):        5227b569de003adc7f8ec3fc03e05dfbd969abad\\n\"\n            \"Algorithm:                SHA512_RSA8192\\n\"\n            \"Rollback Index:           20\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Hashtree descriptor:\\n\"\n            \"      Version of dm-verity:  1\\n\"\n            \"      Image Size:            52428800 bytes\\n\"\n            \"      Tree Offset:           52428800\\n\"\n            \"      Tree Size:             413696 bytes\\n\"\n            \"      Data Block Size:       4096 bytes\\n\"\n            \"      Hash Block Size:       4096 bytes\\n\"\n            \"      FEC num roots:         2\\n\"\n            \"      FEC offset:            52842496\\n\"\n            \"      FEC size:              417792 bytes\\n\"\n            \"      Hash Algorithm:        sha1\\n\"\n            \"      Partition Name:        system\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Root Digest:           d20d40c02298e385ab6d398a61a3b91dc9947d99\\n\"\n            \"      Flags:                 0\\n\",\n            InfoImage(\"system-vbmeta.img\"));\n}\n\nTEST_F(BaseFsAvbTest, GenerateVBMetaImageWithDescriptors) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA4096\", 10,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA8192\", 20,\n                 data_dir_.Append(\"testkey_rsa8192.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Makes a vbmeta.img including both 'boot' and 'system' descriptors.\n    GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA2048\", 0, data_dir_.Append(\"testkey_rsa2048.pem\"),\n                        {boot_path, system_path}, /* include_descriptor_image_paths */\n                        {},                       /* chain_partitions */\n                        \"--internal_release_string \\\"unit test\\\"\");\n    EXPECT_EQ(\"a069cbfc30c816cddf3b53f1ad53b7ca5d61a3d93845eb596bbb1b40caa1c62f\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     320 bytes\\n\"\n            \"Auxiliary Block:          960 bytes\\n\"\n            \"Public key (sha1):        cdbb77177f731920bbe0a0f94f84d9038ae0617d\\n\"\n            \"Algorithm:                SHA256_RSA2048\\n\"\n            \"Rollback Index:           0\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Hash descriptor:\\n\"\n            \"      Image Size:            5242880 bytes\\n\"\n            \"      Hash Algorithm:        sha256\\n\"\n            \"      Partition Name:        boot\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Digest:                \"\n            \"222dd01e98284a1fcd7781f85d1392e43a530511a64eff96db197db90ebc4df1\\n\"\n            \"      Flags:                 0\\n\"\n            \"    Hashtree descriptor:\\n\"\n            \"      Version of dm-verity:  1\\n\"\n            \"      Image Size:            10485760 bytes\\n\"\n            \"      Tree Offset:           10485760\\n\"\n            \"      Tree Size:             86016 bytes\\n\"\n            \"      Data Block Size:       4096 bytes\\n\"\n            \"      Hash Block Size:       4096 bytes\\n\"\n            \"      FEC num roots:         2\\n\"\n            \"      FEC offset:            10571776\\n\"\n            \"      FEC size:              90112 bytes\\n\"\n            \"      Hash Algorithm:        sha1\\n\"\n            \"      Partition Name:        system\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\\n\"\n            \"      Flags:                 0\\n\",\n            InfoImage(\"vbmeta.img\"));\n}\n\nTEST_F(BaseFsAvbTest, GenerateVBMetaImageWithChainDescriptors) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Make a vbmeta image with chain partitions.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0, data_dir_.Append(\"testkey_rsa8192.pem\"),\n                        {},                               /* include_descriptor_image_paths */\n                        {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                         {\"system\", 2, rsa4096_public_key}},\n                        \"--internal_release_string \\\"unit test\\\"\");\n\n    // vbmeta digest calculation includes the chained vbmeta from boot.img and system.img.\n    EXPECT_EQ(\"abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n    EXPECT_EQ(\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     1088 bytes\\n\"\n            \"Auxiliary Block:          3840 bytes\\n\"\n            \"Public key (sha1):        5227b569de003adc7f8ec3fc03e05dfbd969abad\\n\"\n            \"Algorithm:                SHA256_RSA8192\\n\"\n            \"Rollback Index:           0\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Chain Partition descriptor:\\n\"\n            \"      Partition Name:          boot\\n\"\n            \"      Rollback Index Location: 1\\n\"\n            \"      Public key (sha1):       cdbb77177f731920bbe0a0f94f84d9038ae0617d\\n\"\n            \"      Flags:                   0\\n\"\n            \"    Chain Partition descriptor:\\n\"\n            \"      Partition Name:          system\\n\"\n            \"      Rollback Index Location: 2\\n\"\n            \"      Public key (sha1):       2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"      Flags:                   0\\n\",\n            InfoImage(\"vbmeta.img\"));\n}\n\n}  // namespace fs_avb_host_test\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/data/testkey_rsa2048.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh\n4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ\ngXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt\nDfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM\nuXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct\nYbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn\nSXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd\njJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp\nz9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN\nmQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT\no/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG\nzGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9\n5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp\nBRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX\nvyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu\ni+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2\niQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW\nmIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY\nb7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy\noWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A\nlBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF\nnRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT\nPudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A\nvWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow\nGH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/data/testkey_rsa4096.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKQIBAAKCAgEA2ASv49OEbH4NiT3CjNMSVeliyfEPXswWcqtEfCxlSpS1FisA\nuwbvEwdTTPlkuSh6G4SYiNhnpCP5p0vcSg/3OhiuVKgV/rCtrDXaO60nvK/o0y83\nNNZRK2xaJ9eWBq9ruIDK+jC0sYWzTaqqwxY0Grjnx/r5CXerl5PrRK7PILzwgBHb\nIwxHcblt1ntgR4cWVpO3wiqasEwBDDDYk4fw7W6LvjBb9qav3YB8RV6PkZNeRP64\nggfuecq/MXNiWOPNxLzCER2hSr/+J32h9jWjXsrcVy8+8Mldhmr4r2an7c247aFf\nupuFGtUJrpROO8/LXMl5gPfMpkqoatjTMRH59gJjKhot0RpmGxZBvb33TcBK5SdJ\nX39Y4yct5clmDlI4Fjj7FutTP+b96aJeJVnYeUX/A0wmogBajsJRoRX5e/RcgZsY\nRzXYLQXprQ81dBWjjovMJ9p8XeT6BNMFC7o6sklFL0fHDUE/l4BNP8G1u3Bfpzev\nSCISRS71D4eS4oQB+RIPFBUkzomZ7rnEF3BwFeq+xmwfYrP0LRaH+1YeRauuMuRe\nke1TZl697a3mEjkNg8noa2wtpe7EWmaujJfXDWxJx/XEkjGLCe4z2qk3tkkY+A5g\nRcgzke8gVxC+eC2DJtbKYfkv4L8FMFJaEhwAp13MfC7FlYujO/BDLl7dANsCAwEA\nAQKCAgAWoL8P/WsktjuSwb5sY/vKtgzcHH1Ar942GsysuTXPDy686LpF3R8T/jNy\nn7k2UBAia8xSoWCR6BbRuHeV5oA+PLGeOpE7QaSfonB+yc+cy0x3Or3ssfqEsu/q\ntoGHp75/8DXS6WE0K04x94u1rdC9b9sPrrGBlWCLGzqM0kbuJfyHXdd3n2SofAUO\nb5QRSgxD+2tHUpEroHqHnWJCaf4J0QegX45yktlfOYNK/PHLDQXV8ly/ejc32M4Y\nTv7hUtOOJTuq8VCg9OWZm2Zo1QuM9XEJTPCp5l3+o5vzO6yhk2gotDvD32CdA+3k\ntLJRP54M1Sn+IXb1gGKN9rKAtGJbenWIPlNObhQgkbwG89Qd+5rfMXsiPv1Hl1tK\n+tqwjD82/H3/ElaaMnwHCpeoGSp95OblAoBjzjMP2KsbvKSdL8O/rf1c3uOw9+DF\ncth0SA8y3ZzI11gJtb2QMGUrCny5n4sPGGbc3x38NdLhwbkPKZy60OiT4g2kNpdY\ndIitmAML2otttiF4AJM6AraPk8YVzkPLTksoL3azPBya5lIoDI2H3QvTtSvpXkXP\nyKchsDSWYbdqfplqC/X0Djp2/Zd8jpN5I6+1aSmpTmbwx/JTllY1N89FRZLIdxoh\n2k81LPiXhE6uRbjioJUlbnEWIpY2y2N2Clmxpjh0/IcXd1XImQKCAQEA7Zai+yjj\n8xit24aO9Tf3mZBXBjSaDodjC2KS1yCcAIXp6S7aH0wZipyZpQjys3zaBQyMRYFG\nbQqIfVAa6inWyDoofbAJHMu5BVcHFBPZvSS5YhDjc8XZ5dqSCxzIz9opIqAbm+b4\naEV/3A3Jki5Dy8y/5j21GAK4Y4mqQOYzne7bDGi3Hyu041MGM4qfIcIkS5N1eHW4\nsDZJh6+K5tuxN5TX3nDZSpm9luNH8mLGgKAZ15b1LqXAtM5ycoBY9Hv082suPPom\nO+r0ybdRX6nDSH8+11y2KiP2kdVIUHCGkwlqgrux5YZyjCZPwOvEPhzSoOS+vBiF\nUVXA8idnxNLk1QKCAQEA6MIihDSXx+350fWqhQ/3Qc6gA/t2C15JwJ9+uFWA+gjd\nc/hn5HcmnmBJN4R04nLG/aU9SQur87a4mnC/Mp9JIARjHlZ/WNT4U0sJyPEVRg5U\nZ9VajAucWwi0JyJYCO1EMMy68Jp8qlTriK/L7nbD86JJ5ASxjojiN/0psK/Pk60F\nRr+shKPi3jRQ1BDjDtAxOfo4ctf/nFbUM4bY0FNPQMP7WesoSKU0NBCRR6d0d2tq\nYflMjIQHx+N74P5jEdSCHTVGQm+dj47pUt3lLPLWc0bX1G/GekwXP4NUsR/70Hsi\nbwxkNnK2TSGzkt2rcOnutP125rJu6WpV7SNrq9rm7wKCAQAfMROcnbWviKHqnDPQ\nhdR/2K9UJTvEhInASOS2UZWpi+s1rez9BuSjigOx4wbaAZ4t44PW7C3uyt84dHfU\nHkIQb3I5bg8ENMrJpK9NN33ykwuzkDwMSwFcZ+Gci97hSubzoMl/IkeiiN1MapL4\nGhLUgsD+3UMVL+Y9SymK8637IgyoCGdiND6/SXsa8SwLJo3VTjqx4eKpX7cvlSBL\nRrRxc50TmwUsAhsd4CDl9YnSATLjVvJBeYlfM2tbFPaYwl1aR8v+PWkfnK0efm60\nfHki33HEnGteBPKuGq4vwVYpn6bYGwQz+f6335/A2DMfZHFSpjVURHPcRcHbCMla\n0cUxAoIBAQC25eYNkO478mo+bBbEXJlkoqLmvjAyGrNFo48F9lpVH6Y0vNuWkXJN\nPUgLUhAu6RYotjGENqG17rz8zt/PPY9Ok2P3sOx8t00y1mIn/hlDZXs55FM0fOMu\nPZaiscAPs7HDzvyOmDah+fzi+ZD8H2M3DS2W+YE0iaeJa2vZJS2t02W0BGXiDI33\nIZDqMyLYvwwPjOnShJydEzXID4xLl0tNjzLxo3GSNA7jYqlmbtV8CXIc7rMSL6WV\nktIDKKJcnmpn3TcKeX6MEjaSIT82pNOS3fY3PmXuL+CMzfw8+u77Eecq78fHaTiL\nP5JGM93F6mzi19EY0tmInUBMCWtQLcENAoIBAQCg0KaOkb8T36qzPrtgbfou0E2D\nufdpL1ugmD4edOFKQB5fDFQhLnSEVSJq3KUg4kWsXapQdsBd6kLdxS+K6MQrLBzr\n4tf0c7UCF1AzWk6wXMExZ8mRb2RkGZYQB2DdyhFB3TPmnq9CW8JCq+6kxg/wkU4s\nvM4JXzgcqVoSf42QJl+B9waeWhg0BTWx01lal4ds88HvEKmE0ik5GwiDbr7EvDDw\nE6UbZtQcIoSTIIZDgYqVFfR2DAho3wXJRsOXh433lEJ8X7cCDzrngFbQnlKrpwML\nXgm0SIUc+Nf5poMM3rfLFK77t/ob4w+5PwRKcoSniyAxrHd6bwykYA8Vuydv\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/data/testkey_rsa8192.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIISKgIBAAKCBAEA0D3T+dISsmCHm797wsX0vVfqUWDJ/3mvDYozlCabDhnGLlSE\npAQbf1Z8Ts+OM4pVRHOJUJL0WebNdmPPGjsyWQz6zZE96lQZL3avCEXqYVQR66V5\n3wdK/ohaMSRnGyEMBrqkVVbF3gCr+/irxD3YK+VowO2WKs/6GrMdqTA8Y5CTF/Je\nptwsSg5MMjr6UaK4qDcrej3hkgBVGvRV3cj1snK6Br8HuYdFnpGGTS0d7UJlHFgl\ntrGHU/CBO923hkHgJaWEjC0giSGjhKKtLzrVcpDV2y/lWQP9T/T4djEAIaHqQ++P\nSdOSR6psIGR6hVgSigt7HCnE7nW711/rfV5Ur9EiVpB040mDImKZcy8//TMnXydN\n1KYTVd/34fdpzMpSw5iblErbwOLXVTUmOztYnpl41feHSv/jPesHstPlfklIF2vo\nGZEohf9scQvcuM7wEBfC/aTA9K39zMmkBbcvSZjLyhmcSZWMPPOZyIcl3zY53QhW\nQC/abmIcBfI1S4+r7mC4i2Jn++oEvuGNVGr2SY2Z0ZZxXGL1HI/08D/3+Tcumrcn\n4YjPK/DMFi0F+e+1x41lipuf+cx/2qRNQX/m02STrLYdM6e0g33KvlnFdi2b752y\n/OIaMwxDaJvunMh6EMDWKM1AHbY/ioAoK7eS26HeJLEDllqO4+SWP37c8lMvSEWy\n1GiErR0HcsOj/QwWGPFseoVroMiA2sUQ0Ic/tgVjCTlXg+12XpUnouIweCi8KcL/\nad2zJkju9hBhJLBQ/2GnivJi3lFgF4Gd//TSJ6rgWuXFfMKt/9z2Sz35ohEX4yA0\nflqlCeLInFEoevbz+XT9aRfDe65MZ79yw3TfP9CrV74hf1RRzveD4zpi3F+hcY2i\nJWsH7gROZeCm6fAX5Trecd3hOxJOfA4N4rvSSCq6BwCvebT8FY25Z/VF7cQrHYDS\nij5w6lqhMzXHeUEY90Ga9AK4XzaWwGgezq+R7Zs00YSKqFv9qYNKdR7tz3cjijWf\n9q/3R1uh6EQKTMZKo4SEClJiGyjOBvmPK09jMFZTJv00hDxagDPZBl7XpLDJ5/Ln\n1uppvLCNWWY1zeJfaElMyq3/PqKZLidF9rVoA1SIwk2lpdUvPote2oFiwCZoXlwZ\nJ2ncjmXgQNs76/8unDJA0rj4JPqccw4M5GxQ7okbgm3F4rmzriCuv8BeMSCkr2ry\n0mY3UhpohX4wCMq0G4x5sEUAz9FVVPZKjxnYBmLDzrJAR+4+G7gZsct01XDJYgDd\nJVYInFP22/cIre8VrFWYtHbgOFdNqUiVq58de6PdZG/E+uaWmEThSlRrgEjTxupi\nOXfgdKW/20j1qAtjOlqFwsY094Q5rqULQ6wPxQIDAQABAoIEAQChmkmlhrRBv42d\nfYUiyxK52b8ath0saJdDz6tlXmxYDgJxM9/XlORt9oTzeDknoEO5olu+rrx4BBgQ\ntzYiaiwRVXRREVTWQ7tjzRvaNL/GFkLt93XTccpuKwyrNE/bitLVagRbwcI+HZFa\nMknCOihHMHoRto8h3FKAY94xzSAgODMek1WG8jhgpCXXmVNnBPt+d4oDDIDAGAfz\nqgf03J5nhIb+80KgZOzPOKnbvJaL6EmlLHbgB3c42dzAw7hHtVmofYGWcvLb2MIY\nDVKO435/sQx1U/8NDH6JjVdACZjLgObXH9K3/Tt46DWPEcrPLmD8xhoc6gFM+Qr0\nAhkzKoBYDNk0CljbhdIBXjktXU6wRQFZ45uP2e4JZ4zrzGBLr/t4lTavZ0SQtLld\nA6kOsGh+dCWFDtnshxYnl/xad/yR+3a5zmDJbo/fJTBXrlf1B4rfQkFtK20etOPQ\nB++FC/rjh3Mm/Kb/p9Gz/2upZdArH97ZvD2LBFfj77lFmAhqAi3wCRlN+ekuYxaZ\nt1pBV9yXig8Dyldg1d7X8pOn2kyrF3rQUDDf4pa7x9vpnbkUlEUifoV9gnYsmdni\nqDzYBtTv2g6MKqwQySXaIUW0YOBPbOellWEwxJqGYQ7y4IfVHfM0iyHnehk2tZcr\n+XazLnwGe+Bz4vcguFhJXLyIu//lAOhZtbk6r1QJEUuxaOOQX3wzyceE6nkDsgmr\nP5dj3Zpd7fS2VV2vyGHIFnBJ88LRxreVvgr6Q28UT27SB82zMb7mRZTVE2zeuubT\n5D2D1XbZ0wBo6WiK6eRRrDQ2Haeetkj/uoRy6PWXwnAaTmmIrrXwLqaoJh/U1e+D\ntfsDLWd6IxLjfXvGglrHsrtAz0oprpixUTeVhgTrGk9IQRd5rvxuGUYhFujVaYI6\n+QUf+33AFdtncb8y9C9jZmgx8AKbJk+e73SLhB5JVos+WteU7b8d/Mim5mALjnO6\nZ1n/uimsT79sSDqy3XSymtKWXo/22UlrvGCpoEuELPMb6dSFWR7vwrsvhFngY4/K\nUnitnvxboEflQnaIQ4IfRLRzZsX+sC5Esqw9U5tHt4oI+91Dv3KbdbcERgV73K6B\nZQgC4lkAQquFXiZ5AICkxjiMyZwTtU9KJ7xv17Xu6oywF/3AtbVGETW1D+3maHsD\ny3DASWojyqZdLj+WGzKQRa+swgCDAYKeek2fIAXFSdF63zxJ2RxOJ4GijSaoh+mr\n4HVvcpDaTj+A8T1+QdByM4s98gu4GD7kVtVQGBZdWjutyHvh0hWv1gtVmbhQ/413\ngDMFFDzHIjLTYGYes4hHL22169jVR9sZ1eQxwvTIg3N4pD5cFm0rRuZZTS+oJToF\nG27aBFihAoICAQDyVB62ZDnbxQthk+zITKIzRUrJbLoXrUcANcSHfaN7inF87Ova\nze7ejT9DNSEhbtfZFJ1G6diOYoSw+2MzFXv0gEkLKY0dETydKgHEu6nVq5eivMgv\nD4hc9YkJMHDSlmv2FDkpL3AXCAmnW9rKp+ddttBZECnmlPEpHLoj6xgBw3pNa1Xs\nIcLVfdugH86Hexj6o0oKgYfcqrX8UUHtUI2/XQqgFrIj8ksjf1fFVWJRJFWmBXqp\nnMEsYarzATeM1kQ/kDeT1ZUpoGPQt02/XqXT4B5A3ATiEtpM2u+l48xtogWWg2Ry\nG9l938StAmhUiW1m7GnKE6EIFvQY85WvbzxOR0JYVUSr7MrasF6nnQlhYxFuIJoJ\n2h/KJQao5GCTvG4+GtbJJm4c2nyZgwyhizMsdgsdcls79aXiMkrZZkamLVUZWOtE\n3pA/oBuz2qnO9HwjbH1HGOccq0TXfmpFScEV3CQGYJdno6Fy7cbmupaL4U9agQ4e\nw+ygL18nq5HV++LStFnVrgs5YijjskfRdE9GUMVDh5pCsd9Y23Fymaad4O/2SRCC\nYkSsyH5OvyDOLpoyUJ6g6Q+45Hqm/3lG4YjNpzFUiMcnp7+3xU35qC0LK8xEfeei\nMs1mTVEiHNIp6xH/TqRdX73WD7+YuKZSLIfRG7dgrirU6w+mhhvxD51uHQKCAgEA\n2/1mBCR5qm3/0Lt++RQbeyE3tiw40UeyQqucG/+VvY77sSLkI/Lx8iwRlywXcLBn\n+A4TvgukmAdWzCs8ndgKNxPA+gfohvBsMOGN9KOB1Ug5vvg2J2kiI64vwYCwzhdZ\nNTUUmL+GMFHUqSsWYg6i7iBFcZmznr4W2T3bBxyTMZki7JStB86e35KXrzc2/W/b\n+/p5U2HCSazDHI5mMyuClHc6GmUSVJ7f7LHjL94jviNqobp0Vj603tScHISmNrZw\nTBavkvZGYXsoWKvqavk7jBB9QzaBL+unaFRslg5jTaiKnISj44Us1fjFKu84xifL\nnJaEzjDPt7PBxko7LPgEY7wF39nM9VpoetI7bwR6NwDLSX8UU97MGd+HY+MO1Wi1\npd2Lapwrx/EK7Oxz335VRK4Je0aZna4j2TyQdMJac9fsGPXv4ZsLfDLj/wD6l1j+\nlLLbBv3ImdSj32LBbhsgF4iCGeXO8HpPO+Q/h9XVsnY52Um2XdNMn03PCGm6ZvtM\n7DXiS+lPF90HjolJVHZTBNtdVRrLr53zLuWEfqT4FeKrDaxdtiXkxLjrB+5/VYu7\nntyk01ZQ63VNfEwS1irmKl9+qZkTHk3HHV9jNV5RzWViwmJI7Wpr1YzBwmcKCB1O\noGUADDs8QpnkCz0xkMVtYwHj9qKZlqfbHzrFDUUcF8kCggIAdYvUcgjf//ju8mA8\n5VQ3AcPE6TvycPW+kR2DvW12VcDsF/sc1UA7dHzziPhGn98SmNxlBjb8suSbFPZ8\nQhVT0WBBDkcTilwIGPx9ax7U3S6lGW2VdS6FqQH5fRmgQKZyrCVXLOEz8BgYBrSJ\nxu/3TQAWxH0QtibdbGHg8Pdi58gYlWFRhn9B8Slh1aRYHGPb1AhNLBd0/ddY+5G2\n9xSyDXdmZg1cUA+B3zAwNSqbzFxhp2zU+V1uXsbpk4KtnYV6CZM9QlrCRjTk9iNU\ndVXF/qaiRjfzrm4SsmEpCkEbsrp7F22Y1bkooORglMOsNAWNqfVXw4wN+syXj1ro\n6vZ8PERYrFyAOR1dsQMIhymnmTPjCpaJ4emKrhWTy20sY71thHakZWJc22YoNpbZ\nE6tgIVsJPTlxg/4+fyCCKj5wWr92nhsB1KBZPGO/zFhvMlJpvQ0tH8W2pbN2a0mI\n5x9FqALm/qjwCHfZItSwPM+ZozSht3cOkGHdcD5KXAXfcfsDJc4SHZKVIzq4NusN\n504R/jvD1GP8sglyG7omp75ckgzAmakLdxOP2HhQvIX9tcXpSirNJ6Sl2bwKuuMF\nwxo3r/o/9Y97e4LlfpEYp9eqMdcG+NpR993IwK0UhAWS9H5wdnWBSUHd5e4xtDUt\niILNRuO46g7R/AIhz1cSSraWWQkCggIBAMhhPP5C9yt9PIm1b0eTwCBctnFSQIKo\nKsA9rll2ab+bMLk9jc8M6MLszy0CtWso09sHf4YY9tifvrkEHRethEh8zscwUuYu\nsm2n1fTixk0ul6LSVgl54uXbMJayENn4PIKRkew8cA8tSma43497w37hmD+MgCb1\nALzqcco9hfmkgkI6fo1g8Ce3UEECKy2YKSmREdgYcK9JFQO61W6AkFWJcDxAmfzI\nJjFkKwsb7TSw79zWiEdSoM9jm7sCPKATd6Bm/ZAAkUUTuEFkfobn9Ax1rJN/Xxb2\nMKuAUtQv0NYY0gEVdG62jItuKLId6nncH8PG+rsRjPLIYpWqYdJpKx5pUnR+4AkQ\nS6CsRASwcF4PdBvDDBIFG6XpjFo4pPdQhDzL2sTF8b8SWSBLlJQbb7G6UNqgCSau\nSusCFpazvU5NfDmUMuctob2EYVaSXq9jGaj6bTUmDwXHwWilfIk9XfLxnYfXYrJ6\nxhdIpXGmHhuLQtAgK2O1JtLoPc9s9qP8/SkfP7xjjG6xHsP/WvL7QE1pPs9ZM/UI\nC01JNHFi9LKCn8o5mbZjN8jUowi7ffK+76wZUG1L7zM5ytWQOYwo0TQBfc8fpmFw\n+RBRJX2kJyDO27ExczoGOKjwqEDaODIB9+9zcCK0BgSoRibSm4ZBvoxzWWD65Kls\nxdPhZUHcFGW5AoICAQC8iG27aD8aRUt94Oek66gFOJx84QVZehWPqtZjWyVenDuc\nT8dink8oejGjcK2UJuQDa83azv90ocVqE0n0ronYyszt9Ib1jlYC+CK1Ar9TYGFg\nWU5OWEDyCzCpqW/w/aG68U8qhKm0MvkLJR+G6evan9TwEhFEVAm3iWllNXs9x29s\nBucwyMMC23zsimxYlS7dA4DtyvVA+zL1omLpSWHbU/qtuI3HV1NeJzsy+gC4mwPh\nj52tdl669fyWLzHzBRLeq6dVOedjnCo+jlU3dL20DEk9SaW08D1CPuZekV1jVPMw\nJoaDcIRh4KLtQ0BYZ7UJeFUTsx1CS/+UqzqYSPOi57a5kvr0Y8YwRnSB8dHVFttX\nJTv83wTQXHPFSBgfnHNe7lsRTfIQfuIkr2bpiU7h85UQ7LsqcI6YHaC07URcsGFF\nFrLWGh91qzAd1diSHla2RnY3n8PPuMnCkguNhLUrYdmyMol7FfWFa9lwplsuTzBq\nB6yj8iaiE3LL+Q/eulJ7S6QPfAI2bU0UJO23Y4koeoIibEEDMSCQ6KYZ2NClRRRT\nga5fS1YfkDFEcHUQ1/KIkdYHGBKBjoKGExzi8+CgiSySVSYDZl6wIOhLjH2OZ3ol\nldPN7iNAHirrxg9v8QO6OQlpLUk5Lhp/1dSlZ6sy3UjFqvax3tw6ZjrL88YP5g==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/fs_avb_device_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/properties.h>\n#include <fs_avb/fs_avb.h>\n#include <fs_avb/fs_avb_util.h>\n#include <fstab/fstab.h>\n#include <gtest/gtest.h>\n\n#include <sys/types.h>\n#include <unistd.h>\n\nusing android::fs_mgr::AvbHandle;\nusing android::fs_mgr::AvbHandleStatus;\nusing android::fs_mgr::Fstab;\nusing android::fs_mgr::FstabEntry;\nusing android::fs_mgr::VBMetaData;\nusing android::fs_mgr::VBMetaVerifyResult;\n\nnamespace fs_avb_device_test {\n\n// system vbmeta might not be at the end of /system when dynamic partition is\n// enabled. Therefore, disable it by default.\nTEST(FsAvbUtilTest, DISABLED_LoadAndVerifyVbmeta_SystemVbmeta) {\n    Fstab fstab;\n    EXPECT_TRUE(ReadDefaultFstab(&fstab));\n\n    FstabEntry* system_entry = GetEntryForMountPoint(&fstab, \"/system\");\n    EXPECT_NE(nullptr, system_entry);\n\n    std::string out_public_key_data;\n    std::string out_avb_partition_name;\n    VBMetaVerifyResult out_verify_result;\n    std::unique_ptr<VBMetaData> vbmeta =\n            LoadAndVerifyVbmeta(*system_entry, \"\" /* expected_public_key_blob */,\n                                &out_public_key_data, &out_avb_partition_name, &out_verify_result);\n\n    EXPECT_NE(nullptr, vbmeta);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);\n    EXPECT_EQ(\"system\", out_avb_partition_name);\n    EXPECT_NE(\"\", out_public_key_data);\n}\n\nTEST(FsAvbUtilTest, GetHashtreeDescriptor_SystemOther) {\n    // Non-A/B device doesn't have system_other partition.\n    if (fs_mgr_get_slot_suffix() == \"\") return;\n\n    // Skip running this test if system_other is a logical partition.\n    // Note that system_other is still a physical partition on \"retrofit\" devices.\n    if (android::base::GetBoolProperty(\"ro.boot.dynamic_partitions\", false) &&\n        !android::base::GetBoolProperty(\"ro.boot.dynamic_partitions_retrofit\", false)) {\n        return;\n    }\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(\"/system/etc/fstab.postinstall\", &fstab));\n\n    // It should have two lines in the fstab, the first for logical system_other,\n    // the other for physical system_other.\n    EXPECT_EQ(2UL, fstab.size());\n\n    // Use the 2nd fstab entry, which is for physical system_other partition.\n    FstabEntry* system_other = &fstab[1];\n    EXPECT_NE(nullptr, system_other);\n\n    std::string out_public_key_data;\n    std::string out_avb_partition_name;\n    VBMetaVerifyResult out_verify_result;\n    std::unique_ptr<VBMetaData> system_other_vbmeta =\n            LoadAndVerifyVbmeta(*system_other, \"\" /* expected_public_key_blob */,\n                                &out_public_key_data, &out_avb_partition_name, &out_verify_result);\n\n    EXPECT_NE(nullptr, system_other_vbmeta);\n    EXPECT_EQ(VBMetaVerifyResult::kSuccess, out_verify_result);\n    EXPECT_EQ(\"system_other\", out_avb_partition_name);\n    EXPECT_NE(\"\", out_public_key_data);\n\n    auto hashtree_desc =\n            GetHashtreeDescriptor(out_avb_partition_name, std::move(*system_other_vbmeta));\n    EXPECT_NE(nullptr, hashtree_desc);\n}\n\nTEST(AvbHandleTest, LoadAndVerifyVbmeta_SystemOther) {\n    // Non-A/B device doesn't have system_other partition.\n    if (fs_mgr_get_slot_suffix() == \"\") return;\n\n    // Skip running this test if system_other is a logical partition.\n    // Note that system_other is still a physical partition on \"retrofit\" devices.\n    if (android::base::GetBoolProperty(\"ro.boot.dynamic_partitions\", false) &&\n        !android::base::GetBoolProperty(\"ro.boot.dynamic_partitions_retrofit\", false)) {\n        return;\n    }\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(\"/system/etc/fstab.postinstall\", &fstab));\n\n    // It should have two lines in the fstab, the first for logical system_other,\n    // the other for physical system_other.\n    EXPECT_EQ(2UL, fstab.size());\n\n    // Use the 2nd fstab entry, which is for physical system_other partition.\n    FstabEntry* system_other_entry = &fstab[1];\n    // Assign the default key if it's not specified in the fstab.\n    if (system_other_entry->avb_keys.empty()) {\n        system_other_entry->avb_keys = \"/system/etc/security/avb/system_other.avbpubkey\";\n    }\n    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(*system_other_entry);\n    EXPECT_NE(nullptr, avb_handle) << \"Failed to load system_other vbmeta. Try 'adb root'?\";\n    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());\n}\n\nTEST(AvbHandleTest, GetSecurityPatchLevel) {\n    Fstab fstab;\n    EXPECT_TRUE(ReadDefaultFstab(&fstab));\n\n    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta();\n    EXPECT_NE(nullptr, avb_handle) << \"Failed to load inline vbmeta. Try 'adb root'?\";\n    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());\n\n    // Gets security patch level with format: YYYY-MM-DD (e.g., 2019-04-05).\n    FstabEntry* system_entry = GetEntryForMountPoint(&fstab, \"/system\");\n    EXPECT_NE(nullptr, system_entry);\n    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*system_entry).length());\n\n    FstabEntry* vendor_entry = GetEntryForMountPoint(&fstab, \"/vendor\");\n    EXPECT_NE(nullptr, vendor_entry);\n    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*vendor_entry).length());\n\n    FstabEntry* product_entry = GetEntryForMountPoint(&fstab, \"/product\");\n    EXPECT_NE(nullptr, product_entry);\n    EXPECT_EQ(10UL, avb_handle->GetSecurityPatchLevel(*product_entry).length());\n}\n\n}  // namespace fs_avb_device_test\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/fs_avb_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <endian.h>\n#include <stdlib.h>\n\n#include <android-base/file.h>\n#include <android-base/strings.h>\n#include <base/files/file_util.h>\n#include <fs_avb/fs_avb.h>\n#include <libavb/libavb.h>\n\n#include \"fs_avb_test_util.h\"\n\n// Target classes or functions to test:\nusing android::fs_mgr::AvbHandle;\nusing android::fs_mgr::AvbHandleStatus;\nusing android::fs_mgr::HashAlgorithm;\n\nnamespace fs_avb_host_test {\n\nclass PublicFsAvbTest : public BaseFsAvbTest {\n  public:\n    PublicFsAvbTest(){};\n\n  protected:\n    ~PublicFsAvbTest(){};\n    // Modifies |flags| field in the vbmeta header in an Avb image.\n    // e.g., AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED.\n    void ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path, uint32_t flags);\n};\n\nvoid PublicFsAvbTest::ModifyVBMetaHeaderFlags(const base::FilePath& vbmeta_image_path,\n                                              uint32_t flags) {\n    if (!base::PathExists(vbmeta_image_path)) return;\n\n    // Only support modifying the flags in vbmeta*.img.\n    std::string image_file_name = vbmeta_image_path.RemoveExtension().BaseName().value();\n    ASSERT_TRUE(android::base::StartsWithIgnoreCase(image_file_name, \"vbmeta\"));\n\n    android::base::unique_fd fd(open(vbmeta_image_path.value().c_str(), O_RDWR | O_CLOEXEC));\n    EXPECT_TRUE(fd > 0);\n\n    auto flags_offset = offsetof(AvbVBMetaImageHeader, flags);\n    uint32_t flags_data = htobe32(flags);\n    EXPECT_EQ(flags_offset, lseek64(fd, flags_offset, SEEK_SET));\n    EXPECT_EQ(sizeof flags_data, write(fd, &flags_data, sizeof flags_data));\n}\n\nTEST_F(PublicFsAvbTest, LoadAndVerifyVbmeta) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n\n    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.\n    EXPECT_EQ(\"abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n\n    // Invokes the public API from fs_avb.h.\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            \"\" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,\n            false /* allow_verification_error */, true /* load_chained_vbmeta */,\n            true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_NE(nullptr, avb_handle);\n    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());\n\n    // Checks the summary info for all vbmeta images.\n    // Checks the digest matches the value calculated by CalcVBMetaDigest().\n    EXPECT_EQ(\"abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f\",\n              avb_handle->vbmeta_info().digest);\n    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);\n    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);\n\n    // Skip loading chained vbmeta.\n    avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            \"\" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,\n            false /* allow_verification_error */, false /* load_chained_vbmeta */,\n            true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_NE(nullptr, avb_handle);\n    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());\n    EXPECT_EQ(\"5c31197992b3c72a854ec7dc0eb9609ffebcffab7917ffd381a99ecee328f09c\",\n              avb_handle->vbmeta_info().digest);\n    EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);\n    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);\n}\n\nTEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithModifications) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n\n    // Makes a vbmeta image includeing 'boot' and 'system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n\n    // Calculates the digest of all chained partitions, to ensure the chained is formed properly.\n    EXPECT_EQ(\"abbe11b316901f3336e26630f64c4732dadbe14532186ac8640e4141a403721f\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n\n    // Sets AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED in the vbmeta.img.\n    ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED);\n\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            \"\" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,\n            false /* allow_verification_error */, true /* load_chained_vbmeta */,\n            true /* rollback_protection */, vbmeta_image_path);\n    // Returns a null handler because allow_verification is not True.\n    EXPECT_EQ(nullptr, avb_handle);\n\n    // Try again with allow_verification_error set to true.\n    avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            \"\" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,\n            true /* allow_verification_error */, true /* load_chained_vbmeta */,\n            true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_NE(nullptr, avb_handle);\n    EXPECT_EQ(AvbHandleStatus::kHashtreeDisabled, avb_handle->status());\n\n    // Checks the summary info for all vbmeta images.\n    // Checks the digest matches the value calculated by CalcVBMetaDigest().\n    EXPECT_EQ(\"ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n    EXPECT_EQ(\"ae8f7ad95cbb7ce4f0feeeedc2a0a39824af5cd29dad4d028597cab4b8c2e83c\",\n              avb_handle->vbmeta_info().digest);\n    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);\n    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);\n\n    // Sets AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED in the vbmeta.img.\n    ModifyVBMetaHeaderFlags(vbmeta_path, AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED);\n    // Loads the vbmeta with allow_verification_error set to true.\n    avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            \"\" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,\n            true /* allow_verification_error */, true /* load_chained_vbmeta */,\n            true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_NE(nullptr, avb_handle);\n    EXPECT_EQ(AvbHandleStatus::kVerificationDisabled, avb_handle->status());\n    // Only the top-level vbmeta.img is loaded, when VERIFICATION_DISABLED is set.\n    // However, CalcVBMetaDigest() reads all vbmeta structs to calculate the digest,\n    // including vbmeta.img, boot.img and syste.img. So we don't compare the digest here.\n    EXPECT_EQ(5184UL, avb_handle->vbmeta_info().total_size);\n\n    // Sets a unknown flag in the vbmeta.imgm and expects to get\n    // AvbHandleStatus::kVerificationError.\n    ModifyVBMetaHeaderFlags(vbmeta_path, 0x10000000);\n    // Loads the vbmeta with allow_verification_error set to true.\n    avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            \"\" /* expected_public_key_blob*/, HashAlgorithm::kSHA256,\n            true /* allow_verification_error */, true /* load_chained_vbmeta */,\n            true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_NE(nullptr, avb_handle);\n    EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());\n    // Checks the digest matches the value calculated by CalcVBMetaDigest().\n    EXPECT_EQ(\"8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1\",\n              CalcVBMetaDigest(\"vbmeta.img\", \"sha256\"));\n    EXPECT_EQ(\"8fb99c4f54500053c3582df5eaf04e9a533137398879188aad9968ec19a664f1\",\n              avb_handle->vbmeta_info().digest);\n    EXPECT_EQ(8576UL, avb_handle->vbmeta_info().total_size);\n    EXPECT_EQ(HashAlgorithm::kSHA256, avb_handle->vbmeta_info().hash_algorithm);\n}\n\nTEST_F(PublicFsAvbTest, LoadAndVerifyVbmetaWithPublicKeys) {\n    // Generates a raw boot.img\n    const size_t boot_image_size = 5 * 1024 * 1024;\n    const size_t boot_partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", boot_image_size);\n\n    // Adds AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", boot_partition_size, \"SHA256_RSA2048\", 10,\n                 data_dir_.Append(\"testkey_rsa2048.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates a raw system.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Generates chain partition descriptors.\n    base::FilePath rsa2048_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa2048.pem\"));\n    base::FilePath rsa4096_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa4096.pem\"));\n    base::FilePath rsa8192_public_key =\n            ExtractPublicKeyAvb(data_dir_.Append(\"testkey_rsa8192.pem\"));\n\n    // Makes a vbmeta image includeing 'boot' and 'vbmeta_system' chained descriptors.\n    auto vbmeta_path = GenerateVBMetaImage(\"vbmeta.img\", \"SHA256_RSA8192\", 0,\n                                           data_dir_.Append(\"testkey_rsa8192.pem\"),\n                                           {}, /* include_descriptor_image_paths */\n                                           {{\"boot\", 1, rsa2048_public_key}, /* chain_partitions */\n                                            {\"system\", 2, rsa4096_public_key}},\n                                           \"--internal_release_string \\\"unit test\\\"\");\n\n    auto vbmeta_image_path = [this](const std::string& partition_name) {\n        return test_dir_.Append(partition_name + \".img\").value();\n    };\n    std::vector<VBMetaData> vbmeta_images;\n    // Uses the correct expected public key.\n    auto avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            rsa8192_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,\n            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_NE(nullptr, avb_handle);\n    EXPECT_EQ(AvbHandleStatus::kSuccess, avb_handle->status());\n\n    // Uses a non-existed public key.\n    avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            \"/path/to/non-existed/key\", HashAlgorithm::kSHA256, true /* allow_verification_error */,\n            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_EQ(nullptr, avb_handle);\n\n    // Uses an incorrect public key, with allow_verification_error false.\n    avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            rsa4096_public_key.value(), HashAlgorithm::kSHA256,\n            false /* allow_verification_error */, true /* load_chained_vbmeta */,\n            true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_EQ(nullptr, avb_handle);\n\n    // Uses an incorrect public key, with allow_verification_error true.\n    avb_handle = AvbHandle::LoadAndVerifyVbmeta(\n            \"vbmeta\" /* partition_name */, \"\" /* ab_suffix */, \"\" /* other_suffix */,\n            rsa4096_public_key.value(), HashAlgorithm::kSHA256, true /* allow_verification_error */,\n            true /* load_chained_vbmeta */, true /* rollback_protection */, vbmeta_image_path);\n    EXPECT_NE(nullptr, avb_handle);\n    EXPECT_EQ(AvbHandleStatus::kVerificationError, avb_handle->status());\n}\n\n}  // namespace fs_avb_host_test\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/fs_avb_test_util.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fs_avb_test_util.h\"\n\n#include <stdlib.h>\n\n#include <android-base/file.h>\n#include <android-base/strings.h>\n#include <android-base/stringprintf.h>\n#include <base/files/file_util.h>\n\nnamespace fs_avb_host_test {\n\n// Need to match the data setting in Android.bp:\n//     data: [\"tests/data/*\"]\nbase::FilePath BaseFsAvbTest::data_dir_ = base::FilePath(\"tests/data\");\n\nvoid BaseFsAvbTest::SetUp() {\n    // Changes current directory to test executable directory so that relative path\n    // references to test dependencies don't rely on being manually run from\n    // the executable directory. With this, we can just open \"./tests/data/testkey_rsa2048.pem\"\n    // from the source.\n    base::SetCurrentDirectory(base::FilePath(android::base::GetExecutableDirectory()));\n\n    // Creates a temporary directory, e.g., /tmp/libfs_avb-tests.XXXXXX to stash images in.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n    base::CreateTemporaryDirInDir(tmp_dir, \"libfs_avb-tests.\", &test_dir_);\n}\n\nvoid BaseFsAvbTest::TearDown() {\n    // Nukes temporary directory.\n    ASSERT_NE(std::string::npos, test_dir_.value().find(\"libfs_avb-tests\"));\n    ASSERT_TRUE(base::DeleteFile(test_dir_, true /* recursive */));\n}\n\nstd::string BaseFsAvbTest::CalcVBMetaDigest(const std::string& file_name,\n                                            const std::string& hash_algorithm) {\n    auto iter = vbmeta_images_.find(file_name);\n    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.\n\n    // Gets the image path from iterator->second.path: VBMetaImage.path.\n    base::FilePath image_path = iter->second.path;\n    base::FilePath vbmeta_digest_path = test_dir_.Append(\"vbmeta_digest\");\n    EXPECT_COMMAND(0,\n                   \"avbtool calculate_vbmeta_digest --image %s --hash_algorithm %s\"\n                   \" --output %s\",\n                   image_path.value().c_str(), hash_algorithm.c_str(),\n                   vbmeta_digest_path.value().c_str());\n    // Reads the content of the output digest file.\n    std::string vbmeta_digest_data;\n    EXPECT_TRUE(base::ReadFileToString(vbmeta_digest_path, &vbmeta_digest_data));\n    // Returns the trimmed digest.\n    return android::base::Trim(vbmeta_digest_data);\n}\n\nbase::FilePath BaseFsAvbTest::GenerateVBMetaImage(\n        const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,\n        const base::FilePath& key_path,\n        const std::vector<base::FilePath>& include_descriptor_image_paths,\n        const std::vector<ChainPartitionConfig>& chain_partitions,\n        const std::string& additional_options) {\n    // --algorithm and --key\n    std::string signing_options;\n    if (avb_algorithm == \"\") {\n        signing_options = \" --algorithm NONE \";\n    } else {\n        signing_options =\n                std::string(\" --algorithm \") + avb_algorithm + \" --key \" + key_path.value() + \" \";\n    }\n    // --include_descriptors_from_image\n    std::string include_descriptor_options;\n    for (const auto& path : include_descriptor_image_paths) {\n        include_descriptor_options += \" --include_descriptors_from_image \" + path.value();\n    }\n    // --chain_partitions\n    std::string chain_partition_options;\n    for (const auto& partition : chain_partitions) {\n        chain_partition_options += android::base::StringPrintf(\n                \" --chain_partition %s:%u:%s\", partition.partition_name.c_str(),\n                partition.rollback_index_location, partition.key_blob_path.value().c_str());\n    }\n    // Starts to 'make_vbmeta_image'.\n    VBMetaImage vbmeta_image;\n    vbmeta_image.path = test_dir_.Append(file_name);\n    EXPECT_COMMAND(0,\n                   \"avbtool make_vbmeta_image\"\n                   \" --rollback_index %\" PRIu64\n                   \" %s %s %s %s\"\n                   \" --output %s\",\n                   rollback_index, signing_options.c_str(), include_descriptor_options.c_str(),\n                   chain_partition_options.c_str(), additional_options.c_str(),\n                   vbmeta_image.path.value().c_str());\n    int64_t file_size;\n    EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));\n    vbmeta_image.content.resize(file_size);\n    EXPECT_TRUE(base::ReadFile(vbmeta_image.path,\n                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));\n    // Stores the generated vbmeta image into vbmeta_images_ member object.\n    vbmeta_images_.emplace(file_name, std::move(vbmeta_image));\n\n    return vbmeta_images_[file_name].path;  // returns the path.\n}\n\nbase::FilePath BaseFsAvbTest::ExtractVBMetaImage(const base::FilePath& image_path,\n                                                 const std::string& output_file_name,\n                                                 const size_t padding_size) {\n    VBMetaImage vbmeta_image;\n    vbmeta_image.path = test_dir_.Append(output_file_name);\n    GTEST_LOG_(INFO) << \"ExtractVBMetaImage: \" << image_path << \" to \" << output_file_name;\n    EXPECT_COMMAND(0,\n                   \"avbtool extract_vbmeta_image\"\n                   \" --image %s\"\n                   \" --output %s\"\n                   \" --padding_size %zu\",\n                   image_path.value().c_str(), vbmeta_image.path.value().c_str(), padding_size);\n    int64_t file_size;\n    EXPECT_TRUE(base::GetFileSize(vbmeta_image.path, &file_size));\n    vbmeta_image.content.resize(file_size);\n    EXPECT_TRUE(base::ReadFile(vbmeta_image.path,\n                               reinterpret_cast<char*>(vbmeta_image.content.data()), file_size));\n    // Stores the extracted vbmeta image into vbmeta_images_ member object.\n    vbmeta_images_.emplace(output_file_name, std::move(vbmeta_image));\n\n    // Returns the output file path.\n    return vbmeta_images_[output_file_name].path;\n}\n\n// Generates a file with name |file_name| of size |image_size| with\n// known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).\nbase::FilePath BaseFsAvbTest::GenerateImage(const std::string& file_name, size_t image_size,\n                                            uint8_t start_byte) {\n    std::vector<uint8_t> image;\n    image.resize(image_size);\n    for (size_t n = 0; n < image_size; n++) {\n        image[n] = uint8_t(n + start_byte);\n    }\n    base::FilePath image_path = test_dir_.Append(file_name);\n    EXPECT_EQ(image_size,\n              static_cast<const size_t>(base::WriteFile(\n                      image_path, reinterpret_cast<const char*>(image.data()), image.size())));\n    return image_path;\n}\n\nvoid BaseFsAvbTest::AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,\n                                 const std::string& partition_name, const uint64_t partition_size,\n                                 const std::string& avb_algorithm, uint64_t rollback_index,\n                                 const base::FilePath& key_path, const std::string& salt,\n                                 const std::string& additional_options) {\n    // 'add_hash_footer' or 'add_hashtree_footer'.\n    EXPECT_TRUE(footer_type == \"hash\" or footer_type == \"hashtree\");\n    std::string add_footer_option = \"add_\" + footer_type + \"_footer\";\n\n    std::string signing_options;\n    if (avb_algorithm == \"\") {\n        signing_options = \" --algorithm NONE \";\n    } else {\n        signing_options =\n                std::string(\" --algorithm \") + avb_algorithm + \" --key \" + key_path.value() + \" \";\n    }\n    EXPECT_COMMAND(0,\n                   \"avbtool %s\"\n                   \" --image %s\"\n                   \" --partition_name %s \"\n                   \" --partition_size %\" PRIu64 \" --rollback_index %\" PRIu64\n                   \" --salt %s\"\n                   \" %s %s\",\n                   add_footer_option.c_str(), image_path.value().c_str(), partition_name.c_str(),\n                   partition_size, rollback_index, salt.c_str(), signing_options.c_str(),\n                   additional_options.c_str());\n}\n\nVBMetaData BaseFsAvbTest::GenerateImageAndExtractVBMetaData(\n        const std::string& partition_name, const size_t image_size, const size_t partition_size,\n        const std::string& footer_type, const base::FilePath& avb_signing_key,\n        const std::string& avb_algorithm, const uint64_t rollback_index) {\n    // Generates a raw image first\n    base::FilePath image_path = GenerateImage(partition_name + \".img\", image_size);\n\n    // Appends AVB Hashtree Footer.\n    AddAvbFooter(image_path, footer_type, partition_name, partition_size, avb_algorithm,\n                 rollback_index, avb_signing_key, \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    // Extracts vbmeta from the ram image into another *-vbmeta.img.\n    auto vbmeta_image = ExtractVBMetaImage(image_path, partition_name + \"-vbmeta.img\");\n\n    // Loads *-vbmeta.img into a VBMetaData.\n    std::string vbmeta_buffer;\n    EXPECT_TRUE(base::ReadFileToString(vbmeta_image, &vbmeta_buffer));\n\n    return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};\n}\n\nVBMetaData BaseFsAvbTest::LoadVBMetaData(const std::string& file_name) {\n    auto iter = vbmeta_images_.find(file_name);\n    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.\n\n    // Gets the image path from iterator->second.path: VBMetaImage.path.\n    base::FilePath image_path = iter->second.path;\n\n    // Loads the vbmeta_image into a VBMetaData.\n    std::string vbmeta_buffer;\n    EXPECT_TRUE(base::ReadFileToString(image_path, &vbmeta_buffer));\n\n    std::string partition_name = image_path.RemoveExtension().BaseName().value();\n    return {(const uint8_t*)vbmeta_buffer.data(), vbmeta_buffer.size(), partition_name};\n}\n\nVBMetaData BaseFsAvbTest::ExtractAndLoadVBMetaData(const base::FilePath& image_path,\n                                                   const std::string& output_file_name) {\n    ExtractVBMetaImage(image_path, output_file_name);\n    return LoadVBMetaData(output_file_name);\n}\n\nstd::string BaseFsAvbTest::InfoImage(const base::FilePath& image_path) {\n    base::FilePath tmp_path = test_dir_.Append(\"info_output.txt\");\n    EXPECT_COMMAND(0, \"avbtool info_image --image %s --output %s\", image_path.value().c_str(),\n                   tmp_path.value().c_str());\n    std::string info_data;\n    EXPECT_TRUE(base::ReadFileToString(tmp_path, &info_data));\n    return info_data;\n}\n\nstd::string BaseFsAvbTest::InfoImage(const std::string& file_name) {\n    auto iter = vbmeta_images_.find(file_name);\n    EXPECT_NE(iter, vbmeta_images_.end());  // ensures file_name is generated before.\n    // Gets the image path from iterator->second.path: VBMetaImage.path.\n    base::FilePath image_path = iter->second.path;\n    return InfoImage(image_path);\n}\n\nbase::FilePath BaseFsAvbTest::ExtractPublicKeyAvb(const base::FilePath& key_path) {\n    std::string file_name = key_path.RemoveExtension().BaseName().value();\n    base::FilePath tmp_path = test_dir_.Append(file_name + \"public_key.bin\");\n    EXPECT_COMMAND(0,\n                   \"avbtool extract_public_key --key %s\"\n                   \" --output %s\",\n                   key_path.value().c_str(), tmp_path.value().c_str());\n    return tmp_path;\n}\n\nstd::string BaseFsAvbTest::ExtractPublicKeyAvbBlob(const base::FilePath& key_path) {\n    base::FilePath tmp_path = test_dir_.Append(\"public_key.bin\");\n    EXPECT_COMMAND(0,\n                   \"avbtool extract_public_key --key %s\"\n                   \" --output %s\",\n                   key_path.value().c_str(), tmp_path.value().c_str());\n    std::string key_data;\n    EXPECT_TRUE(base::ReadFileToString(tmp_path, &key_data));\n    return key_data;\n}\n\n}  // namespace fs_avb_host_test\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/fs_avb_test_util.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n#include <base/files/file_path.h>\n#include <fs_avb/types.h>\n#include <gtest/gtest.h>\n\n// Utility macro to run the command expressed by the printf()-style string\n// |command_format| using the system(3) utility function. Will assert unless\n// the command exits normally with exit status |expected_exit_status|.\n#define EXPECT_COMMAND(expected_exit_status, command_format, ...)                   \\\n    do {                                                                            \\\n        int rc = system(android::base::StringPrintf(command_format, ##__VA_ARGS__).c_str()); \\\n        EXPECT_TRUE(WIFEXITED(rc));                                                 \\\n        EXPECT_EQ(WEXITSTATUS(rc), expected_exit_status);                           \\\n    } while (0);\n\nusing android::fs_mgr::VBMetaData;\n\nnamespace fs_avb_host_test {\n\nstruct VBMetaImage {\n    // Path to vbmeta image generated with GenerateVBMetaImage().\n    base::FilePath path;\n    // Contents of the image generated with GenerateVBMetaImage().\n    std::vector<uint8_t> content;\n};\n\nstruct ChainPartitionConfig {\n    std::string partition_name;\n    uint32_t rollback_index_location;\n    base::FilePath key_blob_path;\n};\n\ninline android::base::unique_fd OpenUniqueReadFd(const base::FilePath& file_path) {\n    return android::base::unique_fd(open(file_path.value().c_str(), O_RDONLY | O_CLOEXEC));\n}\n\n/* Base-class used for unit test. */\nclass BaseFsAvbTest : public ::testing::Test {\n  public:\n    BaseFsAvbTest() {}\n\n  protected:\n    virtual ~BaseFsAvbTest() {}\n\n    // Calculates the vbmeta digest using 'avbtool calc_vbmeta_digest' command.\n    // Note that the calculation includes chained vbmeta images.\n    std::string CalcVBMetaDigest(const std::string& file_name, const std::string& hash_algorithm);\n\n    // Generates a vbmeta image with |file_name| by avbtool.\n    // The generated vbmeta image will be written to disk, see the\n    // |vbmeta_images_| variable for its path and the content.\n    base::FilePath GenerateVBMetaImage(\n            const std::string& file_name, const std::string& avb_algorithm, uint64_t rollback_index,\n            const base::FilePath& key_path,\n            const std::vector<base::FilePath>& include_descriptor_image_paths,\n            const std::vector<ChainPartitionConfig>& chain_partitions,\n            const std::string& additional_options = \"\");\n    // Similar to above, but extracts a vbmeta image from the given image_path.\n    // The extracted vbmeta image will be written to disk, with |output_file_name|.\n    // See the |vbmeta_images_| variable for its path and the content.\n    base::FilePath ExtractVBMetaImage(const base::FilePath& image_path,\n                                      const std::string& output_file_name,\n                                      const size_t padding_size = 0);\n\n    // Generate a file with name |file_name| of size |image_size| with\n    // known content (0x00 0x01 0x02 .. 0xff 0x00 0x01 ..).\n    base::FilePath GenerateImage(const std::string& file_name, size_t image_size,\n                                 uint8_t start_byte = 0);\n    // Invokes 'avbtool add_hash_footer' or 'avbtool add_hashtree_footer' to sign\n    // the |image_path|. The |footer_type| can be either \"hash\" or \"hashtree\".\n    void AddAvbFooter(const base::FilePath& image_path, const std::string& footer_type,\n                      const std::string& partition_name, const uint64_t partition_size,\n                      const std::string& avb_algorithm, uint64_t rollback_index,\n                      const base::FilePath& avb_signing_key, const std::string& salt = \"d00df00d\",\n                      const std::string& additional_options = \"\");\n\n    VBMetaData GenerateImageAndExtractVBMetaData(\n            const std::string& partition_name, const size_t image_size, const size_t partition_size,\n            const std::string& footer_type, const base::FilePath& avb_signing_key,\n            const std::string& avb_algorithm, const uint64_t rollback_index);\n\n    VBMetaData ExtractAndLoadVBMetaData(const base::FilePath& image_path,\n                                        const std::string& output_file_name);\n\n    VBMetaData LoadVBMetaData(const std::string& file_name);\n\n    // Returns the output of 'avbtool info_image' for the |image_path|.\n    std::string InfoImage(const base::FilePath& image_path);\n    // Same as above, but for an internal vbmeta image with |file_name| in |vbmeta_images_|.\n    std::string InfoImage(const std::string& file_name);\n\n    // Extracts public key blob in AVB format for a .pem key, then returns the\n    // file path: a .bin file.\n    base::FilePath ExtractPublicKeyAvb(const base::FilePath& key_path);\n    // Same as above, but returns the key blob binary instead.\n    std::string ExtractPublicKeyAvbBlob(const base::FilePath& key_path);\n\n    void SetUp() override;\n    void TearDown() override;\n\n    // Temporary directory created in SetUp().\n    base::FilePath test_dir_;\n    // Maps vbmeta image name (e.g., vbmeta_a.img, system_a.img) to VBMetaImage.\n    std::map<std::string, VBMetaImage> vbmeta_images_;\n\n    static base::FilePath data_dir_;\n};\n\n}  // namespace fs_avb_host_test\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/fs_avb_util_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fs_avb/fs_avb_util.h>\n\n#include \"fs_avb_test_util.h\"\n\nnamespace fs_avb_host_test {\n\nclass PublicFsAvbUtilTest : public BaseFsAvbTest {\n  public:\n    PublicFsAvbUtilTest(){};\n\n  protected:\n    ~PublicFsAvbUtilTest(){};\n};\n\nTEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor) {\n    // Generates a raw system_other.img, use a smaller size to speed-up unit test.\n    const size_t system_image_size = 10 * 1024 * 1024;\n    const size_t system_partition_size = 15 * 1024 * 1024;\n    base::FilePath system_path = GenerateImage(\"system.img\", system_image_size);\n\n    // Adds AVB Hashtree Footer.\n    AddAvbFooter(system_path, \"hashtree\", \"system\", system_partition_size, \"SHA512_RSA4096\", 20,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n\n    auto system_vbmeta = ExtractAndLoadVBMetaData(system_path, \"system-vbmeta.img\");\n\n    auto hashtree_desc =\n            GetHashtreeDescriptor(\"system\" /* avb_partition_name */, std::move(system_vbmeta));\n    EXPECT_NE(nullptr, hashtree_desc);\n\n    // Checks the returned hashtree_desc matches the following info returned by avbtool.\n    EXPECT_EQ(\n            \"Footer version:           1.0\\n\"\n            \"Image size:               15728640 bytes\\n\"\n            \"Original image size:      10485760 bytes\\n\"\n            \"VBMeta offset:            10661888\\n\"\n            \"VBMeta size:              2112 bytes\\n\"\n            \"--\\n\"\n            \"Minimum libavb version:   1.0\\n\"\n            \"Header Block:             256 bytes\\n\"\n            \"Authentication Block:     576 bytes\\n\"\n            \"Auxiliary Block:          1280 bytes\\n\"\n            \"Public key (sha1):        2597c218aae470a130f61162feaae70afd97f011\\n\"\n            \"Algorithm:                SHA512_RSA4096\\n\"\n            \"Rollback Index:           20\\n\"\n            \"Flags:                    0\\n\"\n            \"Rollback Index Location:  0\\n\"\n            \"Release String:           'unit test'\\n\"\n            \"Descriptors:\\n\"\n            \"    Hashtree descriptor:\\n\"\n            \"      Version of dm-verity:  1\\n\"\n            \"      Image Size:            10485760 bytes\\n\"\n            \"      Tree Offset:           10485760\\n\"\n            \"      Tree Size:             86016 bytes\\n\"\n            \"      Data Block Size:       4096 bytes\\n\"\n            \"      Hash Block Size:       4096 bytes\\n\"\n            \"      FEC num roots:         2\\n\"\n            \"      FEC offset:            10571776\\n\"\n            \"      FEC size:              90112 bytes\\n\"\n            \"      Hash Algorithm:        sha1\\n\"\n            \"      Partition Name:        system\\n\"\n            \"      Salt:                  d00df00d\\n\"\n            \"      Root Digest:           a3d5dd307341393d85de356c384ff543ec1ed81b\\n\"\n            \"      Flags:                 0\\n\",\n            InfoImage(system_path));\n\n    EXPECT_EQ(1UL, hashtree_desc->dm_verity_version);\n    EXPECT_EQ(10485760UL, hashtree_desc->image_size);\n    EXPECT_EQ(10485760UL, hashtree_desc->tree_offset);\n    EXPECT_EQ(86016UL, hashtree_desc->tree_size);\n    EXPECT_EQ(4096UL, hashtree_desc->data_block_size);\n    EXPECT_EQ(4096UL, hashtree_desc->hash_block_size);\n    EXPECT_EQ(2UL, hashtree_desc->fec_num_roots);\n    EXPECT_EQ(10571776UL, hashtree_desc->fec_offset);\n    EXPECT_EQ(90112UL, hashtree_desc->fec_size);\n    EXPECT_EQ(std::string(\"sha1\"),\n              std::string(reinterpret_cast<const char*>(hashtree_desc->hash_algorithm)));\n    EXPECT_EQ(std::string(\"system\").length(), hashtree_desc->partition_name_len);\n    EXPECT_EQ(hashtree_desc->partition_name, \"system\");\n    EXPECT_EQ(hashtree_desc->salt, \"d00df00d\");\n    EXPECT_EQ(hashtree_desc->root_digest, \"a3d5dd307341393d85de356c384ff543ec1ed81b\");\n\n    // Checks it's null if partition name doesn't match.\n    EXPECT_EQ(nullptr, GetHashtreeDescriptor(\"system_not_exist\" /* avb_partition_name */,\n                                             std::move(system_vbmeta)));\n}\n\nTEST_F(PublicFsAvbUtilTest, GetHashtreeDescriptor_NotFound) {\n    // Generates a raw boot.img\n    const size_t image_size = 5 * 1024 * 1024;\n    const size_t partition_size = 10 * 1024 * 1024;\n    base::FilePath boot_path = GenerateImage(\"boot.img\", image_size);\n    // Appends AVB Hash Footer.\n    AddAvbFooter(boot_path, \"hash\", \"boot\", partition_size, \"SHA256_RSA4096\", 10,\n                 data_dir_.Append(\"testkey_rsa4096.pem\"), \"d00df00d\",\n                 \"--internal_release_string \\\"unit test\\\"\");\n    // Extracts boot vbmeta from boot.img into boot-vbmeta.img.\n    auto boot_vbmeta = ExtractAndLoadVBMetaData(boot_path, \"boot-vbmeta.img\");\n\n    auto hashtree_desc =\n            GetHashtreeDescriptor(\"boot\" /* avb_partition_name */, std::move(boot_vbmeta));\n    EXPECT_EQ(nullptr, hashtree_desc);\n}\n\n}  // namespace fs_avb_host_test\n"
  },
  {
    "path": "fs_mgr/libfs_avb/tests/util_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <unistd.h>\n\n#include <algorithm>\n#include <future>\n#include <string>\n#include <thread>\n\n#include <android-base/strings.h>\n#include <base/files/file_util.h>\n\n#include \"fs_avb_test_util.h\"\n#include \"util.h\"\n\n// Target functions to test:\nusing android::fs_mgr::BytesToHex;\nusing android::fs_mgr::FileWaitMode;\nusing android::fs_mgr::HexToBytes;\nusing android::fs_mgr::ListFiles;\nusing android::fs_mgr::NibbleValue;\nusing android::fs_mgr::WaitForFile;\n\nnamespace fs_avb_host_test {\n\nTEST(BasicUtilTest, NibbleValue09) {\n    uint8_t value;\n\n    EXPECT_TRUE(NibbleValue('0', &value));\n    EXPECT_EQ(0, value);\n    EXPECT_TRUE(NibbleValue('1', &value));\n    EXPECT_EQ(1, value);\n    EXPECT_TRUE(NibbleValue('2', &value));\n    EXPECT_EQ(2, value);\n    EXPECT_TRUE(NibbleValue('3', &value));\n    EXPECT_EQ(3, value);\n    EXPECT_TRUE(NibbleValue('4', &value));\n    EXPECT_EQ(4, value);\n    EXPECT_TRUE(NibbleValue('5', &value));\n    EXPECT_EQ(5, value);\n    EXPECT_TRUE(NibbleValue('6', &value));\n    EXPECT_EQ(6, value);\n    EXPECT_TRUE(NibbleValue('7', &value));\n    EXPECT_EQ(7, value);\n    EXPECT_TRUE(NibbleValue('8', &value));\n    EXPECT_EQ(8, value);\n    EXPECT_TRUE(NibbleValue('9', &value));\n    EXPECT_EQ(9, value);\n}\n\nTEST(BasicUtilTest, NibbleValueAF) {\n    uint8_t value;\n\n    EXPECT_TRUE(NibbleValue('a', &value));\n    EXPECT_EQ(10, value);\n    EXPECT_TRUE(NibbleValue('b', &value));\n    EXPECT_EQ(11, value);\n    EXPECT_TRUE(NibbleValue('c', &value));\n    EXPECT_EQ(12, value);\n    EXPECT_TRUE(NibbleValue('d', &value));\n    EXPECT_EQ(13, value);\n    EXPECT_TRUE(NibbleValue('e', &value));\n    EXPECT_EQ(14, value);\n    EXPECT_TRUE(NibbleValue('f', &value));\n    EXPECT_EQ(15, value);\n\n    EXPECT_TRUE(NibbleValue('A', &value));\n    EXPECT_EQ(10, value);\n    EXPECT_TRUE(NibbleValue('B', &value));\n    EXPECT_EQ(11, value);\n    EXPECT_TRUE(NibbleValue('C', &value));\n    EXPECT_EQ(12, value);\n    EXPECT_TRUE(NibbleValue('D', &value));\n    EXPECT_EQ(13, value);\n    EXPECT_TRUE(NibbleValue('E', &value));\n    EXPECT_EQ(14, value);\n    EXPECT_TRUE(NibbleValue('F', &value));\n    EXPECT_EQ(15, value);\n}\n\nTEST(BasicUtilTest, NibbleValueInvalid) {\n    uint8_t value;\n\n    EXPECT_FALSE(NibbleValue('G', &value));\n    EXPECT_FALSE(NibbleValue('H', &value));\n    EXPECT_FALSE(NibbleValue('I', &value));\n    EXPECT_FALSE(NibbleValue('x', &value));\n    EXPECT_FALSE(NibbleValue('y', &value));\n    EXPECT_FALSE(NibbleValue('z', &value));\n}\n\nTEST(BasicUtilTest, HexToBytes) {\n    std::string hex = \"000102030405060708090A0B0C0D0E0F\";\n    uint8_t bytes[16];\n\n    EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));\n    for (size_t i = 0; i < sizeof(bytes); i++) {\n        EXPECT_EQ(i, bytes[i]);\n    }\n}\n\nTEST(BasicUtilTest, HexToBytes2) {\n    std::string hex = \"101112131415161718191A1B1C1D1E1F\";\n    uint8_t bytes[16];\n\n    EXPECT_TRUE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));\n    for (size_t i = 0; i < sizeof(bytes); i++) {\n        EXPECT_EQ(16 + i, bytes[i]);\n    }\n}\n\nTEST(BasicUtilTest, BytesToHex) {\n    const uint8_t bytes[16]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};\n\n    EXPECT_EQ(\"0102\", BytesToHex((uint8_t*)bytes, 2));\n    EXPECT_EQ(\"01020304\", BytesToHex((uint8_t*)bytes, 4));\n    EXPECT_EQ(\"0102030405060708\", BytesToHex((uint8_t*)bytes, 8));\n    EXPECT_EQ(\"0102030405060708090a0b0c0d0e0f10\", BytesToHex((uint8_t*)bytes, 16));\n\n    EXPECT_EQ(\"01\", BytesToHex((uint8_t*)bytes, 1));\n    EXPECT_EQ(\"010203\", BytesToHex((uint8_t*)bytes, 3));\n    EXPECT_EQ(\"0102030405\", BytesToHex((uint8_t*)bytes, 5));\n}\n\nTEST(BasicUtilTest, HexToBytesInValidOddLenHex) {\n    std::string hex = \"12345\";\n    uint8_t bytes[16];\n\n    EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));\n}\n\nTEST(BasicUtilTest, HexToBytesInsufficientByteLen) {\n    std::string hex = \"101112131415161718191A1B1C1D1E1F\";\n    uint8_t bytes[8];\n\n    EXPECT_FALSE(HexToBytes((uint8_t*)bytes, sizeof(bytes), hex));\n}\n\nTEST(BasicUtilTest, WaitForFile) {\n    // Gets system tmp dir.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n\n    // Waits this path.\n    base::FilePath wait_path = tmp_dir.Append(\"libfs_avb-test-exist-dir\");\n    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));\n\n    EXPECT_TRUE(base::CreateDirectory(wait_path));\n    EXPECT_TRUE(WaitForFile(wait_path.value(), 1s));\n\n    // Removes the wait_path.\n    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));\n}\n\nTEST(BasicUtilTest, WaitForFileNonExist) {\n    base::FilePath wait_path(\"/path/not/exist\");\n    EXPECT_FALSE(WaitForFile(wait_path.value(), 200ms));\n}\n\nTEST(BasicUtilTest, WaitForFileDeferCreation) {\n    // Gets system tmp dir.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n\n    // Waits this path.\n    base::FilePath wait_path = tmp_dir.Append(\"libfs_avb-test-exist-dir\");\n    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));\n    auto wait_file = std::async(WaitForFile, wait_path.value(), 500ms, FileWaitMode::Exists);\n\n    // Sleeps 100ms before creating the wait_path.\n    std::this_thread::sleep_for(100ms);\n    EXPECT_TRUE(base::CreateDirectory(wait_path));\n\n    // Checks WaitForFile() returns success.\n    EXPECT_TRUE(wait_file.get());\n\n    // Removes the wait_path.\n    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));\n}\n\nTEST(BasicUtilTest, WaitForFileDeferCreationFailure) {\n    // Gets system tmp dir.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n\n    // Waits this path.\n    base::FilePath wait_path = tmp_dir.Append(\"libfs_avb-test-exist-dir\");\n    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));\n    auto wait_file = std::async(WaitForFile, wait_path.value(), 50ms, FileWaitMode::Exists);\n\n    // Sleeps 100ms before creating the wait_path.\n    std::this_thread::sleep_for(100ms);\n    EXPECT_TRUE(base::CreateDirectory(wait_path));\n\n    // Checks WaitForFile() returns failure, because it only waits 50ms.\n    EXPECT_FALSE(wait_file.get());\n\n    // Removes the wait_path.\n    ASSERT_TRUE(base::DeleteFile(wait_path, false /* resursive */));\n}\n\nTEST(BasicUtilTest, ListFiles) {\n    // Gets system tmp dir.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n\n    // Creates a test dir for ListFiles testing.\n    base::FilePath test_dir;\n    ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, \"list-file-tests.\", &test_dir));\n\n    // Generates test files to list.\n    base::FilePath file_path_1 = test_dir.Append(\"1.txt\");\n    ASSERT_TRUE(base::WriteFile(file_path_1, \"1\", 1));\n    base::FilePath file_path_2 = test_dir.Append(\"2.txt\");\n    ASSERT_TRUE(base::WriteFile(file_path_2, \"22\", 2));\n    base::FilePath file_path_3 = test_dir.Append(\"3.txt\");\n    ASSERT_TRUE(base::WriteFile(file_path_3, \"333\", 3));\n\n    // List files for comparison.\n    auto result = ListFiles(test_dir.value());\n    ASSERT_RESULT_OK(result);\n    auto files = result.value();\n    EXPECT_EQ(3UL, files.size());\n    // Sort them offline for comparison.\n    std::sort(files.begin(), files.end());\n    EXPECT_EQ(file_path_1.value(), files[0]);\n    EXPECT_EQ(file_path_2.value(), files[1]);\n    EXPECT_EQ(file_path_3.value(), files[2]);\n\n    ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));\n}\n\nTEST(BasicUtilTest, ListFilesShouldDiscardSymlink) {\n    // Gets system tmp dir.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n\n    // Creates a test dir for ListFiles testing.\n    base::FilePath test_dir;\n    ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, \"list-file-tests.\", &test_dir));\n\n    // Generates test files to list.\n    base::FilePath file_path_1 = test_dir.Append(\"1.txt\");\n    ASSERT_TRUE(base::WriteFile(file_path_1, \"1\", 1));\n    base::FilePath file_path_2 = test_dir.Append(\"2.txt\");\n    ASSERT_TRUE(base::WriteFile(file_path_2, \"22\", 2));\n    // Creates a symlink and checks it won't be returned by ListFiles.\n    base::FilePath file_path_3 = test_dir.Append(\"3.txt\");\n    base::FilePath non_existent_target = test_dir.Append(\"non_existent_target.txt\");\n    ASSERT_TRUE(base::CreateSymbolicLink(non_existent_target, file_path_3));\n\n    // List files for comparison.\n    auto result = ListFiles(test_dir.value());\n    ASSERT_RESULT_OK(result);\n    auto files = result.value();\n    EXPECT_EQ(2UL, files.size());  // Should not include the symlink file.\n    // Sort them offline for comparison.\n    std::sort(files.begin(), files.end());\n    EXPECT_EQ(file_path_1.value(), files[0]);\n    EXPECT_EQ(file_path_2.value(), files[1]);\n\n    ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));\n}\n\nTEST(BasicUtilTest, ListFilesOpenDirFailure) {\n    // Gets system tmp dir.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n\n    // Generates test files to list.\n    base::FilePath no_such_dir = tmp_dir.Append(\"not_such_dir\");\n\n    auto fail = ListFiles(no_such_dir.value());\n    ASSERT_FALSE(fail.ok());\n    EXPECT_EQ(ENOENT, fail.error().code());\n    EXPECT_TRUE(android::base::StartsWith(fail.error().message(), \"Failed to opendir: \"));\n}\n\nTEST(BasicUtilTest, ListFilesEmptyDir) {\n    // Gets system tmp dir.\n    base::FilePath tmp_dir;\n    ASSERT_TRUE(GetTempDir(&tmp_dir));\n\n    // Creates a test dir for ListFiles testing.\n    base::FilePath test_dir;\n    ASSERT_TRUE(base::CreateTemporaryDirInDir(tmp_dir, \"list-file-tests.\", &test_dir));\n\n    // List files without sorting.\n    auto result = ListFiles(test_dir.value());\n    ASSERT_RESULT_OK(result);\n    auto files = result.value();\n    EXPECT_EQ(0UL, files.size());\n\n    ASSERT_TRUE(base::DeleteFile(test_dir, true /* resursive */));\n}\n\n}  // namespace fs_avb_host_test\n"
  },
  {
    "path": "fs_mgr/libfs_avb/types.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fs_avb/types.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\n// Helper functions to print enum class VBMetaVerifyResult.\nconst char* VBMetaVerifyResultToString(VBMetaVerifyResult result) {\n    // clang-format off\n    static const char* const name[] = {\n        \"ResultSuccess\",\n        \"ResultError\",\n        \"ResultErrorVerification\",\n        \"ResultUnknown\",\n    };\n    // clang-format on\n\n    uint32_t index = static_cast<uint32_t>(result);\n    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;\n    if (index >= unknown_index) {\n        index = unknown_index;\n    }\n\n    return name[index];\n}\n\nstd::ostream& operator<<(std::ostream& os, VBMetaVerifyResult result) {\n    os << VBMetaVerifyResultToString(result);\n    return os;\n}\n\n// Helper functions to dump enum class AvbHandleStatus.\nconst char* AvbHandleStatusToString(AvbHandleStatus status) {\n    // clang-format off\n    static const char* const name[] = {\n        \"Success\",\n        \"Uninitialized\",\n        \"HashtreeDisabled\",\n        \"VerificationDisabled\",\n        \"VerificationError\",\n        \"Unknown\",\n    };\n    // clang-format on\n\n    uint32_t index = static_cast<uint32_t>(status);\n    uint32_t unknown_index = sizeof(name) / sizeof(char*) - 1;\n    if (index >= unknown_index) {\n        index = unknown_index;\n    }\n\n    return name[index];\n}\n\nstd::ostream& operator<<(std::ostream& os, AvbHandleStatus status) {\n    os << AvbHandleStatusToString(status);\n    return os;\n}\n\n// class VBMetaData\n// ----------------\nstd::unique_ptr<AvbVBMetaImageHeader> VBMetaData::GetVBMetaHeader(bool update_vbmeta_size) {\n    auto vbmeta_header = std::make_unique<AvbVBMetaImageHeader>();\n\n    if (!vbmeta_header) return nullptr;\n\n    /* Byteswap the header. */\n    avb_vbmeta_image_header_to_host_byte_order((AvbVBMetaImageHeader*)vbmeta_ptr_.get(),\n                                               vbmeta_header.get());\n    if (update_vbmeta_size) {\n        vbmeta_size_ = sizeof(AvbVBMetaImageHeader) +\n                       vbmeta_header->authentication_data_block_size +\n                       vbmeta_header->auxiliary_data_block_size;\n    }\n\n    return vbmeta_header;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/util.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"util.h\"\n\n#include <dirent.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n\n#include <thread>\n\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n#include <linux/fs.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nbool NibbleValue(const char& c, uint8_t* value) {\n    CHECK(value != nullptr);\n\n    switch (c) {\n        case '0' ... '9':\n            *value = c - '0';\n            break;\n        case 'a' ... 'f':\n            *value = c - 'a' + 10;\n            break;\n        case 'A' ... 'F':\n            *value = c - 'A' + 10;\n            break;\n        default:\n            return false;\n    }\n\n    return true;\n}\n\nbool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex) {\n    CHECK(bytes != nullptr);\n\n    if (hex.size() % 2 != 0) {\n        return false;\n    }\n    if (hex.size() / 2 > bytes_len) {\n        return false;\n    }\n    for (size_t i = 0, j = 0, n = hex.size(); i < n; i += 2, ++j) {\n        uint8_t high;\n        if (!NibbleValue(hex[i], &high)) {\n            return false;\n        }\n        uint8_t low;\n        if (!NibbleValue(hex[i + 1], &low)) {\n            return false;\n        }\n        bytes[j] = (high << 4) | low;\n    }\n    return true;\n}\n\nstd::string BytesToHex(const uint8_t* bytes, size_t bytes_len) {\n    CHECK(bytes != nullptr);\n\n    static const char* hex_digits = \"0123456789abcdef\";\n    std::string hex;\n\n    for (size_t i = 0; i < bytes_len; i++) {\n        hex.push_back(hex_digits[(bytes[i] & 0xF0) >> 4]);\n        hex.push_back(hex_digits[bytes[i] & 0x0F]);\n    }\n    return hex;\n}\n\n// TODO: remove duplicate code with fs_mgr_wait_for_file\nbool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout,\n                 FileWaitMode file_wait_mode) {\n    auto start_time = std::chrono::steady_clock::now();\n\n    while (true) {\n        int rv = access(filename.c_str(), F_OK);\n        if (file_wait_mode == FileWaitMode::Exists) {\n            if (!rv || errno != ENOENT) return true;\n        } else if (file_wait_mode == FileWaitMode::DoesNotExist) {\n            if (rv && errno == ENOENT) return true;\n        }\n\n        std::this_thread::sleep_for(50ms);\n\n        auto now = std::chrono::steady_clock::now();\n        auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);\n        if (time_elapsed > relative_timeout) return false;\n    }\n}\n\nbool IsDeviceUnlocked() {\n    std::string verified_boot_state;\n\n    if (fs_mgr_get_boot_config(\"verifiedbootstate\", &verified_boot_state)) {\n        return verified_boot_state == \"orange\";\n    }\n    return false;\n}\n\nbool SetBlockDeviceReadOnly(const std::string& blockdev) {\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(blockdev.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) {\n        return false;\n    }\n\n    int ON = 1;\n    return ioctl(fd, BLKROSET, &ON) == 0;\n}\n\nResult<std::vector<std::string>> ListFiles(const std::string& dir) {\n    struct dirent* de;\n    std::vector<std::string> files;\n\n    std::unique_ptr<DIR, int (*)(DIR*)> dirp(opendir(dir.c_str()), closedir);\n    if (!dirp) {\n        return ErrnoError() << \"Failed to opendir: \" << dir;\n    }\n\n    while ((de = readdir(dirp.get()))) {\n        if (de->d_type != DT_REG) continue;\n        std::string full_path = android::base::StringPrintf(\"%s/%s\", dir.c_str(), de->d_name);\n        files.emplace_back(std::move(full_path));\n    }\n\n    return files;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfs_avb/util.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <chrono>\n#include <string>\n#include <vector>\n\n#include <android-base/result.h>\n\nusing android::base::ErrnoError;\nusing android::base::Result;\n\n#define FS_AVB_TAG \"[libfs_avb] \"\n\n// Logs a message to kernel\n#define LINFO LOG(INFO) << FS_AVB_TAG\n#define LWARNING LOG(WARNING) << FS_AVB_TAG\n#define LERROR LOG(ERROR) << FS_AVB_TAG\n#define LFATAL LOG(FATAL) << FS_AVB_TAG\n\n// Logs a message with strerror(errno) at the end\n#define PINFO PLOG(INFO) << FS_AVB_TAG\n#define PWARNING PLOG(WARNING) << FS_AVB_TAG\n#define PERROR PLOG(ERROR) << FS_AVB_TAG\n#define PFATAL PLOG(FATAL) << FS_AVB_TAG\n\nextern bool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);\n\nusing namespace std::chrono_literals;\n\nnamespace android {\nnamespace fs_mgr {\n\nbool NibbleValue(const char& c, uint8_t* value);\n\nbool HexToBytes(uint8_t* bytes, size_t bytes_len, const std::string& hex);\n\nstd::string BytesToHex(const uint8_t* bytes, size_t bytes_len);\n\nenum class FileWaitMode { Exists, DoesNotExist };\nbool WaitForFile(const std::string& filename, const std::chrono::milliseconds relative_timeout,\n                 FileWaitMode wait_mode = FileWaitMode::Exists);\n\nbool IsDeviceUnlocked();\n\nbool SetBlockDeviceReadOnly(const std::string& blockdev);\n\n// Returns a list of file under the dir, no order is guaranteed.\nResult<std::vector<std::string>> ListFiles(const std::string& dir);\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfstab/Android.bp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\n        \"Android-Apache-2.0\",\n        \"system_core_fs_mgr_license\",\n    ],\n}\n\ncc_library_static {\n    // Do not ever make this a shared library as long as it is vendor_available.\n    // It does not have a stable interface.\n    name: \"libfstab\",\n    vendor_available: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    host_supported: true,\n    defaults: [\"fs_mgr_defaults\"],\n    export_include_dirs: [\"include\"],\n    header_libs: [\n        \"libbase_headers\",\n        \"libgsi_headers\",\n    ],\n    srcs: [\n        \"fstab.cpp\",\n        \"boot_config.cpp\",\n        \"slotselect.cpp\",\n    ],\n    target: {\n        darwin: {\n            enabled: false,\n        },\n        vendor: {\n            cflags: [\n                // Skipping entries in fstab should only be done in a system\n                // process as the config file is in /system_ext.\n                // Remove the op from the vendor variant.\n                \"-DNO_SKIP_MOUNT\",\n            ],\n        },\n    },\n    apex_available: [\n        \"//apex_available:anyapex\",\n        \"//apex_available:platform\",\n    ],\n    min_sdk_version: \"31\",\n}\n"
  },
  {
    "path": "fs_mgr/libfstab/boot_config.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <iterator>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n\n#include \"fstab_priv.h\"\n#include \"logging_macros.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nconst std::string& GetAndroidDtDir() {\n    // Set once and saves time for subsequent calls to this function\n    static const std::string kAndroidDtDir = [] {\n        std::string android_dt_dir;\n        if ((GetBootconfig(\"androidboot.android_dt_dir\", &android_dt_dir) ||\n             GetKernelCmdline(\"androidboot.android_dt_dir\", &android_dt_dir)) &&\n            !android_dt_dir.empty()) {\n            // Ensure the returned path ends with a /\n            if (android_dt_dir.back() != '/') {\n                android_dt_dir.push_back('/');\n            }\n        } else {\n            // Fall back to the standard procfs-based path\n            android_dt_dir = \"/proc/device-tree/firmware/android/\";\n        }\n        LINFO << \"Using Android DT directory \" << android_dt_dir;\n        return android_dt_dir;\n    }();\n    return kAndroidDtDir;\n}\n\nvoid ImportBootconfigFromString(const std::string& bootconfig,\n                                const std::function<void(std::string, std::string)>& fn) {\n    for (std::string_view line : android::base::Split(bootconfig, \"\\n\")) {\n        const auto equal_pos = line.find('=');\n        std::string key = android::base::Trim(line.substr(0, equal_pos));\n        if (key.empty()) {\n            continue;\n        }\n        std::string value;\n        if (equal_pos != line.npos) {\n            value = android::base::Trim(line.substr(equal_pos + 1));\n            // If the value is a comma-delimited list, the kernel would insert a space between the\n            // list elements when read from /proc/bootconfig.\n            // BoardConfig.mk:\n            //      BOARD_BOOTCONFIG := key=value1,value2,value3\n            // /proc/bootconfig:\n            //      key = \"value1\", \"value2\", \"value3\"\n            if (key == \"androidboot.boot_device\" || key == \"androidboot.boot_devices\") {\n                // boot_device[s] is a special case where a list element can contain comma and the\n                // caller expects a space-delimited list, so don't remove space here.\n                value.erase(std::remove(value.begin(), value.end(), '\"'), value.end());\n            } else {\n                // In order to not break the expectations of existing code, we modify the value to\n                // keep the format consistent with the kernel cmdline by removing quote and space.\n                std::string_view sv(value);\n                android::base::ConsumePrefix(&sv, \"\\\"\");\n                android::base::ConsumeSuffix(&sv, \"\\\"\");\n                value = android::base::StringReplace(sv, R\"(\", \")\", \",\", true);\n            }\n        }\n        // \"key\" and \"key =\" means empty value.\n        fn(std::move(key), std::move(value));\n    }\n}\n\nbool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,\n                             std::string* out) {\n    bool found = false;\n    ImportBootconfigFromString(bootconfig, [&](std::string config_key, std::string value) {\n        if (!found && config_key == key) {\n            *out = std::move(value);\n            found = true;\n        }\n    });\n    return found;\n}\n\nvoid ImportBootconfig(const std::function<void(std::string, std::string)>& fn) {\n    std::string bootconfig;\n    android::base::ReadFileToString(\"/proc/bootconfig\", &bootconfig);\n    ImportBootconfigFromString(bootconfig, fn);\n}\n\nbool GetBootconfig(const std::string& key, std::string* out) {\n    std::string bootconfig;\n    android::base::ReadFileToString(\"/proc/bootconfig\", &bootconfig);\n    return GetBootconfigFromString(bootconfig, key, out);\n}\n\nvoid ImportKernelCmdlineFromString(const std::string& cmdline,\n                                   const std::function<void(std::string, std::string)>& fn) {\n    static constexpr char quote = '\"';\n\n    size_t base = 0;\n    while (true) {\n        // skip quoted spans\n        auto found = base;\n        while (((found = cmdline.find_first_of(\" \\\"\", found)) != cmdline.npos) &&\n               (cmdline[found] == quote)) {\n            // unbalanced quote is ok\n            if ((found = cmdline.find(quote, found + 1)) == cmdline.npos) break;\n            ++found;\n        }\n        std::string piece = cmdline.substr(base, found - base);\n        piece.erase(std::remove(piece.begin(), piece.end(), quote), piece.end());\n        auto equal_sign = piece.find('=');\n        if (equal_sign == piece.npos) {\n            if (!piece.empty()) {\n                // no difference between <key> and <key>=\n                fn(std::move(piece), \"\");\n            }\n        } else {\n            std::string value = piece.substr(equal_sign + 1);\n            piece.resize(equal_sign);\n            fn(std::move(piece), std::move(value));\n        }\n        if (found == cmdline.npos) break;\n        base = found + 1;\n    }\n}\n\nbool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,\n                                std::string* out) {\n    bool found = false;\n    ImportKernelCmdlineFromString(cmdline, [&](std::string config_key, std::string value) {\n        if (!found && config_key == key) {\n            *out = std::move(value);\n            found = true;\n        }\n    });\n    return found;\n}\n\nvoid ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn) {\n    std::string cmdline;\n    android::base::ReadFileToString(\"/proc/cmdline\", &cmdline);\n    ImportKernelCmdlineFromString(android::base::Trim(cmdline), fn);\n}\n\nbool GetKernelCmdline(const std::string& key, std::string* out) {\n    std::string cmdline;\n    android::base::ReadFileToString(\"/proc/cmdline\", &cmdline);\n    return GetKernelCmdlineFromString(android::base::Trim(cmdline), key, out);\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n\n// Tries to get the boot config value in device tree, properties, kernel bootconfig and kernel\n// cmdline (in that order).\n// Returns 'true' if successfully found, 'false' otherwise.\nbool fs_mgr_get_boot_config(const std::string& key, std::string* out_val) {\n    FSTAB_CHECK(out_val != nullptr);\n\n    // firstly, check the device tree\n    if (is_dt_compatible()) {\n        std::string file_name = android::fs_mgr::GetAndroidDtDir() + key;\n        if (android::base::ReadFileToString(file_name, out_val)) {\n            if (!out_val->empty()) {\n                out_val->pop_back();  // Trims the trailing '\\0' out.\n                return true;\n            }\n        }\n    }\n\n    // next, check if we have \"ro.boot\" property already\n    *out_val = android::base::GetProperty(\"ro.boot.\" + key, \"\");\n    if (!out_val->empty()) {\n        return true;\n    }\n\n    // next, check if we have the property in bootconfig\n    const std::string config_key = \"androidboot.\" + key;\n    if (android::fs_mgr::GetBootconfig(config_key, out_val)) {\n        return true;\n    }\n\n    // finally, fallback to kernel cmdline, properties may not be ready yet\n    if (android::fs_mgr::GetKernelCmdline(config_key, out_val)) {\n        return true;\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "fs_mgr/libfstab/fstab.cpp",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <ctype.h>\n#include <dirent.h>\n#include <errno.h>\n#include <fnmatch.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mount.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <array>\n#include <utility>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <libgsi/libgsi.h>\n\n#include \"fstab_priv.h\"\n#include \"logging_macros.h\"\n\nusing android::base::EndsWith;\nusing android::base::ParseByteCount;\nusing android::base::ParseInt;\nusing android::base::ReadFileToString;\nusing android::base::Readlink;\nusing android::base::Split;\nusing android::base::StartsWith;\n\nnamespace android {\nnamespace fs_mgr {\nnamespace {\n\nconstexpr char kProcMountsPath[] = \"/proc/mounts\";\n\nstruct FlagList {\n    const char* name;\n    uint64_t flag;\n};\n\nFlagList kMountFlagsList[] = {\n        {\"noatime\", MS_NOATIME},\n        {\"noexec\", MS_NOEXEC},\n        {\"nosuid\", MS_NOSUID},\n        {\"nodev\", MS_NODEV},\n        {\"nodiratime\", MS_NODIRATIME},\n        {\"ro\", MS_RDONLY},\n        {\"rw\", 0},\n        {\"sync\", MS_SYNCHRONOUS},\n        {\"remount\", MS_REMOUNT},\n        {\"bind\", MS_BIND},\n        {\"rec\", MS_REC},\n        {\"unbindable\", MS_UNBINDABLE},\n        {\"private\", MS_PRIVATE},\n        {\"slave\", MS_SLAVE},\n        {\"shared\", MS_SHARED},\n        {\"lazytime\", MS_LAZYTIME},\n        {\"nosymfollow\", MS_NOSYMFOLLOW},\n        {\"defaults\", 0},\n};\n\noff64_t CalculateZramSize(int percentage) {\n    off64_t total;\n\n    total = sysconf(_SC_PHYS_PAGES);\n    total *= percentage;\n    total /= 100;\n\n    total *= sysconf(_SC_PAGESIZE);\n\n    return total;\n}\n\n// Fills 'dt_value' with the underlying device tree value string without the trailing '\\0'.\n// Returns true if 'dt_value' has a valid string, 'false' otherwise.\nbool ReadDtFile(const std::string& file_name, std::string* dt_value) {\n    if (android::base::ReadFileToString(file_name, dt_value)) {\n        if (!dt_value->empty()) {\n            // Trim the trailing '\\0' out, otherwise the comparison will produce false-negatives.\n            dt_value->resize(dt_value->size() - 1);\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid ParseFileEncryption(const std::string& arg, FstabEntry* entry) {\n    entry->fs_mgr_flags.file_encryption = true;\n    entry->encryption_options = arg;\n}\n\nbool SetMountFlag(const std::string& flag, FstabEntry* entry) {\n    for (const auto& [name, value] : kMountFlagsList) {\n        if (flag == name) {\n            entry->flags |= value;\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid ParseMountFlags(const std::string& flags, FstabEntry* entry) {\n    std::string fs_options;\n    for (const auto& flag : Split(flags, \",\")) {\n        if (!SetMountFlag(flag, entry)) {\n            // Unknown flag, so it must be a filesystem specific option.\n            if (!fs_options.empty()) {\n                fs_options.append(\",\");  // appends a comma if not the first\n            }\n            fs_options.append(flag);\n\n            if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {\n                const auto arg = flag.substr(equal_sign + 1);\n                if (entry->fs_type == \"f2fs\" && StartsWith(flag, \"reserve_root=\")) {\n                    off64_t size_in_4k_blocks;\n                    if (!ParseInt(arg, &size_in_4k_blocks, static_cast<off64_t>(0),\n                                  std::numeric_limits<off64_t>::max() >> 12)) {\n                        LWARNING << \"Warning: reserve_root= flag malformed: \" << arg;\n                    } else {\n                        entry->reserved_size = size_in_4k_blocks << 12;\n                    }\n                } else if (StartsWith(flag, \"lowerdir=\")) {\n                    entry->lowerdir = arg;\n                }\n            }\n        }\n    }\n    entry->fs_options = std::move(fs_options);\n}\n\nvoid ParseUserDevices(const std::string& arg, FstabEntry* entry) {\n    auto param = Split(arg, \":\");\n    if (param.size() != 2) {\n        LWARNING << \"Warning: device= malformed: \" << arg;\n        return;\n    }\n\n    if (access(param[1].c_str(), F_OK) != 0) {\n        LWARNING << \"Warning: device does not exist : \" << param[1];\n        return;\n    }\n\n    if (param[0] == \"zoned\") {\n        // atgc in f2fs does not support a zoned device\n        auto options = Split(entry->fs_options, \",\");\n        options.erase(std::remove(options.begin(), options.end(), \"atgc\"), options.end());\n        entry->fs_options = android::base::Join(options, \",\");\n        LINFO << \"Removed ATGC in fs_options as \" << entry->fs_options << \" for zoned device\";\n        entry->fs_mgr_flags.is_zoned = true;\n    }\n    entry->user_devices.push_back(param[1]);\n    entry->device_aliased.push_back(param[0] == \"exp_alias\" ? 1 : 0);\n}\n\nbool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {\n    for (const auto& flag : Split(flags, \",\")) {\n        if (flag.empty() || flag == \"defaults\") continue;\n        std::string arg;\n        if (auto equal_sign = flag.find('='); equal_sign != std::string::npos) {\n            arg = flag.substr(equal_sign + 1);\n        }\n\n        // First handle flags that simply set a boolean.\n#define CheckFlag(flag_name, value)       \\\n    if (flag == flag_name) {              \\\n        entry->fs_mgr_flags.value = true; \\\n        continue;                         \\\n    }\n\n        CheckFlag(\"wait\", wait);\n        CheckFlag(\"check\", check);\n        CheckFlag(\"nonremovable\", nonremovable);\n        CheckFlag(\"recoveryonly\", recovery_only);\n        CheckFlag(\"noemulatedsd\", no_emulated_sd);\n        CheckFlag(\"notrim\", no_trim);\n        CheckFlag(\"formattable\", formattable);\n        CheckFlag(\"slotselect\", slot_select);\n        CheckFlag(\"latemount\", late_mount);\n        CheckFlag(\"nofail\", no_fail);\n        CheckFlag(\"quota\", quota);\n        CheckFlag(\"avb\", avb);\n        CheckFlag(\"logical\", logical);\n        CheckFlag(\"checkpoint=block\", checkpoint_blk);\n        CheckFlag(\"checkpoint=fs\", checkpoint_fs);\n        CheckFlag(\"first_stage_mount\", first_stage_mount);\n        CheckFlag(\"slotselect_other\", slot_select_other);\n        CheckFlag(\"fsverity\", fs_verity);\n        CheckFlag(\"metadata_csum\", ext_meta_csum);\n        CheckFlag(\"fscompress\", fs_compress);\n        CheckFlag(\"overlayfs_remove_missing_lowerdir\", overlayfs_remove_missing_lowerdir);\n\n#undef CheckFlag\n\n        // Then handle flags that take an argument.\n        if (StartsWith(flag, \"encryptable=\")) {\n            // The \"encryptable\" flag identifies adoptable storage volumes.  The\n            // argument to this flag is ignored, but it should be \"userdata\".\n            //\n            // Historical note: this flag was originally meant just for /data,\n            // to indicate that FDE (full disk encryption) can be enabled.\n            // Unfortunately, it was also overloaded to identify adoptable\n            // storage volumes.  Today, FDE is no longer supported, leaving only\n            // the adoptable storage volume meaning for this flag.\n            entry->fs_mgr_flags.crypt = true;\n        } else if (StartsWith(flag, \"forceencrypt=\") || StartsWith(flag, \"forcefdeorfbe=\")) {\n            LERROR << \"flag no longer supported: \" << flag;\n            return false;\n        } else if (StartsWith(flag, \"voldmanaged=\")) {\n            // The voldmanaged flag is followed by an = and the label, a colon and the partition\n            // number or the word \"auto\", e.g. voldmanaged=sdcard:3\n            entry->fs_mgr_flags.vold_managed = true;\n            auto parts = Split(arg, \":\");\n            if (parts.size() != 2) {\n                LWARNING << \"Warning: voldmanaged= flag malformed: \" << arg;\n                continue;\n            }\n\n            entry->label = std::move(parts[0]);\n            if (parts[1] == \"auto\") {\n                entry->partnum = -1;\n            } else {\n                if (!ParseInt(parts[1], &entry->partnum)) {\n                    entry->partnum = -1;\n                    LWARNING << \"Warning: voldmanaged= flag malformed: \" << arg;\n                    continue;\n                }\n            }\n        } else if (StartsWith(flag, \"length=\")) {\n            // The length flag is followed by an = and the size of the partition.\n            if (!ParseInt(arg, &entry->length)) {\n                LWARNING << \"Warning: length= flag malformed: \" << arg;\n            }\n        } else if (StartsWith(flag, \"swapprio=\")) {\n            if (!ParseInt(arg, &entry->swap_prio)) {\n                LWARNING << \"Warning: swapprio= flag malformed: \" << arg;\n            }\n        } else if (StartsWith(flag, \"zramsize=\")) {\n            if (!arg.empty() && arg.back() == '%') {\n                arg.pop_back();\n                int val;\n                if (ParseInt(arg, &val, 0, 200)) {\n                    entry->zram_size = CalculateZramSize(val);\n                } else {\n                    LWARNING << \"Warning: zramsize= flag malformed: \" << arg;\n                }\n            } else {\n                if (!ParseInt(arg, &entry->zram_size)) {\n                    LWARNING << \"Warning: zramsize= flag malformed: \" << arg;\n                }\n            }\n        } else if (StartsWith(flag, \"fileencryption=\") || flag == \"fileencryption\") {\n            // \"fileencryption\" enables file-based encryption.  It's normally followed by an = and\n            // then the encryption options.  But that can be omitted to use the default options.\n            ParseFileEncryption(arg, entry);\n        } else if (StartsWith(flag, \"max_comp_streams=\")) {\n            if (!ParseInt(arg, &entry->max_comp_streams)) {\n                LWARNING << \"Warning: max_comp_streams= flag malformed: \" << arg;\n            }\n        } else if (StartsWith(flag, \"reservedsize=\")) {\n            // The reserved flag is followed by an = and the reserved size of the partition.\n            uint64_t size;\n            if (!ParseByteCount(arg, &size)) {\n                LWARNING << \"Warning: reservedsize= flag malformed: \" << arg;\n            } else {\n                entry->reserved_size = static_cast<off64_t>(size);\n            }\n        } else if (StartsWith(flag, \"readahead_size_kb=\")) {\n            int val;\n            if (ParseInt(arg, &val, 0, 16 * 1024)) {\n                entry->readahead_size_kb = val;\n            } else {\n                LWARNING << \"Warning: readahead_size_kb= flag malformed (0 ~ 16MB): \" << arg;\n            }\n        } else if (StartsWith(flag, \"eraseblk=\")) {\n            // The erase block size flag is followed by an = and the flash erase block size. Get it,\n            // check that it is a power of 2 and at least 4096, and return it.\n            off64_t val;\n            if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {\n                LWARNING << \"Warning: eraseblk= flag malformed: \" << arg;\n            } else {\n                entry->erase_blk_size = val;\n            }\n        } else if (StartsWith(flag, \"logicalblk=\")) {\n            // The logical block size flag is followed by an = and the flash logical block size. Get\n            // it, check that it is a power of 2 and at least 4096, and return it.\n            off64_t val;\n            if (!ParseInt(arg, &val) || val < 4096 || (val & (val - 1)) != 0) {\n                LWARNING << \"Warning: logicalblk= flag malformed: \" << arg;\n            } else {\n                entry->logical_blk_size = val;\n            }\n        } else if (StartsWith(flag, \"avb_keys=\")) {  // must before the following \"avb\"\n            entry->avb_keys = arg;\n        } else if (StartsWith(flag, \"avb_hashtree_digest=\")) {\n            // \"avb_hashtree_digest\" must before the following \"avb\"\n            // The path where hex-encoded hashtree descriptor root digest is located.\n            entry->avb_hashtree_digest = arg;\n        } else if (StartsWith(flag, \"avb\")) {\n            entry->fs_mgr_flags.avb = true;\n            entry->vbmeta_partition = arg;\n        } else if (StartsWith(flag, \"keydirectory=\")) {\n            // The keydirectory flag enables metadata encryption.  It is\n            // followed by an = and the directory containing the metadata\n            // encryption key.\n            entry->metadata_key_dir = arg;\n        } else if (StartsWith(flag, \"metadata_encryption=\")) {\n            // The metadata_encryption flag specifies the cipher and flags to\n            // use for metadata encryption, if the defaults aren't sufficient.\n            // It doesn't actually enable metadata encryption; that is done by\n            // \"keydirectory\".\n            entry->metadata_encryption_options = arg;\n        } else if (StartsWith(flag, \"sysfs_path=\")) {\n            // The path to trigger device gc by idle-maint of vold.\n            entry->sysfs_path = arg;\n        } else if (StartsWith(flag, \"zram_backingdev_size=\")) {\n            if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {\n                LWARNING << \"Warning: zram_backingdev_size= flag malformed: \" << arg;\n            }\n        } else if (StartsWith(flag, \"device=\")) {\n            ParseUserDevices(arg, entry);\n        } else {\n            LWARNING << \"Warning: unknown flag: \" << flag;\n        }\n    }\n\n    // FDE is no longer supported, so reject \"encryptable\" when used without\n    // \"vold_managed\".  For now skip this check when in recovery mode, since\n    // some recovery fstabs still contain the FDE options since they didn't do\n    // anything in recovery mode anyway (except possibly to cause the\n    // reservation of a crypto footer) and thus never got removed.\n    if (entry->fs_mgr_flags.crypt && !entry->fs_mgr_flags.vold_managed && !InRecovery()) {\n        LERROR << \"FDE is no longer supported; 'encryptable' can only be used for adoptable \"\n                  \"storage\";\n        return false;\n    }\n    return true;\n}\n\nbool IsDtFstabCompatible() {\n    std::string dt_value;\n    std::string file_name = GetAndroidDtDir() + \"fstab/compatible\";\n\n    if (ReadDtFile(file_name, &dt_value) && dt_value == \"android,fstab\") {\n        // If there's no status property or its set to \"ok\" or \"okay\", then we use the DT fstab.\n        std::string status_value;\n        std::string status_file_name = GetAndroidDtDir() + \"fstab/status\";\n        return !ReadDtFile(status_file_name, &status_value) || status_value == \"ok\" ||\n               status_value == \"okay\";\n    }\n\n    return false;\n}\n\nstd::string ReadFstabFromDt() {\n    if (!is_dt_compatible() || !IsDtFstabCompatible()) {\n        return {};\n    }\n\n    std::string fstabdir_name = GetAndroidDtDir() + \"fstab\";\n    std::unique_ptr<DIR, int (*)(DIR*)> fstabdir(opendir(fstabdir_name.c_str()), closedir);\n    if (!fstabdir) return {};\n\n    dirent* dp;\n    // Each element in fstab_dt_entries is <mount point, the line format in fstab file>.\n    std::vector<std::pair<std::string, std::string>> fstab_dt_entries;\n    while ((dp = readdir(fstabdir.get())) != NULL) {\n        // skip over name, compatible and .\n        if (dp->d_type != DT_DIR || dp->d_name[0] == '.') continue;\n\n        // create <dev> <mnt_point>  <type>  <mnt_flags>  <fsmgr_flags>\\n\n        std::vector<std::string> fstab_entry;\n        std::string file_name;\n        std::string value;\n        // skip a partition entry if the status property is present and not set to ok\n        file_name = android::base::StringPrintf(\"%s/%s/status\", fstabdir_name.c_str(), dp->d_name);\n        if (ReadDtFile(file_name, &value)) {\n            if (value != \"okay\" && value != \"ok\") {\n                LINFO << \"dt_fstab: Skip disabled entry for partition \" << dp->d_name;\n                continue;\n            }\n        }\n\n        file_name = android::base::StringPrintf(\"%s/%s/dev\", fstabdir_name.c_str(), dp->d_name);\n        if (!ReadDtFile(file_name, &value)) {\n            LERROR << \"dt_fstab: Failed to find device for partition \" << dp->d_name;\n            return {};\n        }\n        fstab_entry.push_back(value);\n\n        std::string mount_point;\n        file_name =\n                android::base::StringPrintf(\"%s/%s/mnt_point\", fstabdir_name.c_str(), dp->d_name);\n        if (ReadDtFile(file_name, &value)) {\n            LINFO << \"dt_fstab: Using a specified mount point \" << value << \" for \" << dp->d_name;\n            mount_point = value;\n        } else {\n            mount_point = android::base::StringPrintf(\"/%s\", dp->d_name);\n        }\n        fstab_entry.push_back(mount_point);\n\n        file_name = android::base::StringPrintf(\"%s/%s/type\", fstabdir_name.c_str(), dp->d_name);\n        if (!ReadDtFile(file_name, &value)) {\n            LERROR << \"dt_fstab: Failed to find type for partition \" << dp->d_name;\n            return {};\n        }\n        fstab_entry.push_back(value);\n\n        file_name =\n                android::base::StringPrintf(\"%s/%s/mnt_flags\", fstabdir_name.c_str(), dp->d_name);\n        if (!ReadDtFile(file_name, &value)) {\n            LERROR << \"dt_fstab: Failed to find type for partition \" << dp->d_name;\n            return {};\n        }\n        fstab_entry.push_back(value);\n\n        file_name =\n                android::base::StringPrintf(\"%s/%s/fsmgr_flags\", fstabdir_name.c_str(), dp->d_name);\n        if (!ReadDtFile(file_name, &value)) {\n            LERROR << \"dt_fstab: Failed to find type for partition \" << dp->d_name;\n            return {};\n        }\n        fstab_entry.push_back(value);\n        // Adds a fstab_entry to fstab_dt_entries, to be sorted by mount_point later.\n        fstab_dt_entries.emplace_back(mount_point, android::base::Join(fstab_entry, \" \"));\n    }\n\n    // Sort fstab_dt entries, to ensure /vendor is mounted before /vendor/abc is attempted.\n    std::sort(fstab_dt_entries.begin(), fstab_dt_entries.end(),\n              [](const auto& a, const auto& b) { return a.first < b.first; });\n\n    std::string fstab_result;\n    for (const auto& [_, dt_entry] : fstab_dt_entries) {\n        fstab_result += dt_entry + \"\\n\";\n    }\n    return fstab_result;\n}\n\n/* Extracts <device>s from the by-name symlinks specified in a fstab:\n *   /dev/block/<type>/<device>/by-name/<partition>\n *\n * <type> can be: platform, pci or vbd.\n *\n * For example, given the following entries in the input fstab:\n *   /dev/block/platform/soc/1da4000.ufshc/by-name/system\n *   /dev/block/pci/soc.0/f9824900.sdhci/by-name/vendor\n * it returns a set { \"soc/1da4000.ufshc\", \"soc.0/f9824900.sdhci\" }.\n */\nstd::set<std::string> ExtraBootDevices(const Fstab& fstab) {\n    std::set<std::string> boot_devices;\n\n    for (const auto& entry : fstab) {\n        std::string blk_device = entry.blk_device;\n        // Skips blk_device that doesn't conform to the format.\n        if (!android::base::StartsWith(blk_device, \"/dev/block\") ||\n            android::base::StartsWith(blk_device, \"/dev/block/by-name\") ||\n            android::base::StartsWith(blk_device, \"/dev/block/bootdevice/by-name\")) {\n            continue;\n        }\n        // Skips non-by_name blk_device.\n        // /dev/block/<type>/<device>/by-name/<partition>\n        //                           ^ slash_by_name\n        auto slash_by_name = blk_device.find(\"/by-name\");\n        if (slash_by_name == std::string::npos) continue;\n        blk_device.erase(slash_by_name);  // erases /by-name/<partition>\n\n        // Erases /dev/block/, now we have <type>/<device>\n        blk_device.erase(0, std::string(\"/dev/block/\").size());\n\n        // <type>/<device>\n        //       ^ first_slash\n        auto first_slash = blk_device.find('/');\n        if (first_slash == std::string::npos) continue;\n\n        auto boot_device = blk_device.substr(first_slash + 1);\n        if (!boot_device.empty()) boot_devices.insert(std::move(boot_device));\n    }\n\n    return boot_devices;\n}\n\n// Helper class that maps Fstab* -> FstabEntry; const Fstab* -> const FstabEntry.\ntemplate <typename FstabPtr>\nstruct FstabPtrEntry {\n    using is_const_fstab = std::is_const<std::remove_pointer_t<FstabPtr>>;\n    using type = std::conditional_t<is_const_fstab::value, const FstabEntry, FstabEntry>;\n};\n\ntemplate <typename FstabPtr, typename FstabPtrEntryType = typename FstabPtrEntry<FstabPtr>::type,\n          typename Pred>\nstd::vector<FstabPtrEntryType*> GetEntriesByPred(FstabPtr fstab, const Pred& pred) {\n    if (fstab == nullptr) {\n        return {};\n    }\n    std::vector<FstabPtrEntryType*> entries;\n    for (FstabPtrEntryType& entry : *fstab) {\n        if (pred(entry)) {\n            entries.push_back(&entry);\n        }\n    }\n    return entries;\n}\n\n}  // namespace\n\n// Return the path to the recovery fstab file.  There may be multiple fstab files;\n// the one that is returned will be the first that exists of recovery.fstab.<fstab_suffix>,\n// recovery.fstab.<hardware>, and recovery.fstab.<hardware.platform>.\nstd::string GetRecoveryFstabPath() {\n    for (const char* prop : {\"fstab_suffix\", \"hardware\", \"hardware.platform\"}) {\n        std::string suffix;\n\n        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;\n\n        std::string fstab_path = \"/etc/recovery.fstab.\" + suffix;\n        if (access(fstab_path.c_str(), F_OK) == 0) {\n            return fstab_path;\n        }\n    }\n\n    return \"/etc/recovery.fstab\";\n}\n\n// Return the path to the fstab file.  There may be multiple fstab files; the\n// one that is returned will be the first that exists of fstab.<fstab_suffix>,\n// fstab.<hardware>, and fstab.<hardware.platform>.  The fstab is searched for\n// in /odm/etc/ and /vendor/etc/, as well as in the locations where it may be in\n// the first stage ramdisk during early boot.  Previously, the first stage\n// ramdisk's copy of the fstab had to be located in the root directory, but now\n// the system/etc directory is supported too and is the preferred location.\nstd::string GetFstabPath() {\n    if (InRecovery()) {\n        return GetRecoveryFstabPath();\n    }\n    for (const char* prop : {\"fstab_suffix\", \"hardware\", \"hardware.platform\"}) {\n        std::string suffix;\n\n        if (!fs_mgr_get_boot_config(prop, &suffix)) continue;\n\n        for (const char* prefix : {// late-boot/post-boot locations\n                                   \"/odm/etc/fstab.\", \"/vendor/etc/fstab.\",\n                                   // early boot locations\n                                   \"/system/etc/fstab.\", \"/first_stage_ramdisk/system/etc/fstab.\",\n                                   \"/fstab.\", \"/first_stage_ramdisk/fstab.\"}) {\n            std::string fstab_path = prefix + suffix;\n            if (access(fstab_path.c_str(), F_OK) == 0) {\n                return fstab_path;\n            }\n        }\n    }\n\n    return \"\";\n}\n\nbool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out) {\n    const int expected_fields = proc_mounts ? 4 : 5;\n\n    Fstab fstab;\n\n    for (const auto& line : android::base::Split(fstab_str, \"\\n\")) {\n        auto fields = android::base::Tokenize(line, \" \\t\");\n\n        // Ignore empty lines and comments.\n        if (fields.empty() || android::base::StartsWith(fields.front(), '#')) {\n            continue;\n        }\n\n        if (fields.size() < expected_fields) {\n            LERROR << \"Error parsing fstab: expected \" << expected_fields << \" fields, got \"\n                   << fields.size();\n            return false;\n        }\n\n        FstabEntry entry;\n        auto it = fields.begin();\n\n        entry.blk_device = std::move(*it++);\n        entry.mount_point = std::move(*it++);\n        entry.fs_type = std::move(*it++);\n        ParseMountFlags(std::move(*it++), &entry);\n\n        // For /proc/mounts, ignore everything after mnt_freq and mnt_passno\n        if (!proc_mounts && !ParseFsMgrFlags(std::move(*it++), &entry)) {\n            LERROR << \"Error parsing fs_mgr_flags\";\n            return false;\n        }\n\n        if (entry.fs_mgr_flags.logical) {\n            entry.logical_partition_name = entry.blk_device;\n        }\n\n        fstab.emplace_back(std::move(entry));\n    }\n\n    if (fstab.empty()) {\n        LERROR << \"No entries found in fstab\";\n        return false;\n    }\n\n    /* If an A/B partition, modify block device to be the real block device */\n    if (!fs_mgr_update_for_slotselect(&fstab)) {\n        LERROR << \"Error updating for slotselect\";\n        return false;\n    }\n\n    *fstab_out = std::move(fstab);\n    return true;\n}\n\nvoid TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,\n                          const std::vector<std::string>& dsu_partitions) {\n    static constexpr char kDsuKeysDir[] = \"/avb\";\n    for (auto&& partition : dsu_partitions) {\n        if (!EndsWith(partition, gsi::kDsuPostfix)) {\n            continue;\n        }\n        // scratch is handled by fs_mgr_overlayfs\n        if (partition == android::gsi::kDsuScratch) {\n            continue;\n        }\n        // Convert userdata partition.\n        if (partition == android::gsi::kDsuUserdata) {\n            for (auto&& entry : GetEntriesForMountPoint(fstab, \"/data\")) {\n                entry->blk_device = android::gsi::kDsuUserdata;\n                entry->fs_mgr_flags.logical = true;\n                entry->fs_mgr_flags.formattable = true;\n                if (!entry->metadata_key_dir.empty()) {\n                    entry->metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);\n                }\n            }\n            continue;\n        }\n        // Convert RO partitions.\n        //\n        // dsu_partition_name = corresponding_partition_name + kDsuPostfix\n        // e.g.\n        //    system_gsi for system\n        //    product_gsi for product\n        //    vendor_gsi for vendor\n        std::string lp_name = partition.substr(0, partition.length() - strlen(gsi::kDsuPostfix));\n        std::string mount_point = \"/\" + lp_name;\n\n        // List of fs_type entries we're lacking, need to synthesis these later.\n        std::vector<std::string> lack_fs_list = {\"ext4\", \"erofs\"};\n\n        // Only support early mount (first_stage_mount) partitions.\n        auto pred = [&mount_point](const FstabEntry& entry) {\n            return entry.fs_mgr_flags.first_stage_mount && entry.mount_point == mount_point;\n        };\n\n        // Transform all matching entries and assume they are all adjacent for simplicity.\n        for (auto&& entry : GetEntriesByPred(fstab, pred)) {\n            // .blk_device is replaced with the DSU partition.\n            entry->blk_device = partition;\n            // .avb_keys hints first_stage_mount to load the chained-vbmeta image from partition\n            // footer. See aosp/932779 for more details.\n            entry->avb_keys = kDsuKeysDir;\n            // .logical_partition_name is required to look up AVB Hashtree descriptors.\n            entry->logical_partition_name = lp_name;\n            entry->fs_mgr_flags.logical = true;\n            entry->fs_mgr_flags.slot_select = false;\n            entry->fs_mgr_flags.slot_select_other = false;\n\n            if (auto it = std::find(lack_fs_list.begin(), lack_fs_list.end(), entry->fs_type);\n                it != lack_fs_list.end()) {\n                lack_fs_list.erase(it);\n            }\n        }\n\n        if (!lack_fs_list.empty()) {\n            // Insert at the end of the existing mountpoint group, or at the end of fstab.\n            // We assume there is at most one matching mountpoint group, which is the common case.\n            auto it = std::find_if_not(std::find_if(fstab->begin(), fstab->end(), pred),\n                                       fstab->end(), pred);\n            for (const auto& fs_type : lack_fs_list) {\n                it = std::next(fstab->insert(it, {.blk_device = partition,\n                                                  .logical_partition_name = lp_name,\n                                                  .mount_point = mount_point,\n                                                  .fs_type = fs_type,\n                                                  .flags = MS_RDONLY,\n                                                  .avb_keys = kDsuKeysDir,\n                                                  .fs_mgr_flags{\n                                                          .wait = true,\n                                                          .logical = true,\n                                                          .first_stage_mount = true,\n                                                  }}));\n            }\n        }\n    }\n}\n\nvoid EnableMandatoryFlags(Fstab* fstab) {\n    // Devices launched in R and after must support fs_verity. Set flag to cause tune2fs\n    // to enable the feature on userdata and metadata partitions.\n    if (android::base::GetIntProperty(\"ro.product.first_api_level\", 0) >= 30) {\n        // Devices launched in R and after should enable fs_verity on userdata.\n        // A better alternative would be to enable on mkfs at the beginning.\n        std::vector<FstabEntry*> data_entries = GetEntriesForMountPoint(fstab, \"/data\");\n        for (auto&& entry : data_entries) {\n            // Besides ext4, f2fs is also supported. But the image is already created with verity\n            // turned on when it was first introduced.\n            if (entry->fs_type == \"ext4\") {\n                entry->fs_mgr_flags.fs_verity = true;\n            }\n        }\n        // Devices shipping with S and earlier likely do not already have fs_verity enabled via\n        // mkfs, so enable it here.\n        std::vector<FstabEntry*> metadata_entries = GetEntriesForMountPoint(fstab, \"/metadata\");\n        for (auto&& entry : metadata_entries) {\n            entry->fs_mgr_flags.fs_verity = true;\n        }\n    }\n}\n\nstatic bool ReadFstabFromFileCommon(const std::string& path, Fstab* fstab_out) {\n    std::string fstab_str;\n    if (!android::base::ReadFileToString(path, &fstab_str, /* follow_symlinks = */ true)) {\n        PERROR << __FUNCTION__ << \"(): failed to read file: '\" << path << \"'\";\n        return false;\n    }\n\n    Fstab fstab;\n    if (!ParseFstabFromString(fstab_str, path == kProcMountsPath, &fstab)) {\n        LERROR << __FUNCTION__ << \"(): failed to load fstab from : '\" << path << \"'\";\n        return false;\n    }\n\n    EnableMandatoryFlags(&fstab);\n\n    *fstab_out = std::move(fstab);\n    return true;\n}\n\nbool ReadFstabFromFile(const std::string& path, Fstab* fstab) {\n    if (!ReadFstabFromFileCommon(path, fstab)) {\n        return false;\n    }\n    if (path != kProcMountsPath && !InRecovery()) {\n        if (!access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {\n            std::string dsu_slot;\n            if (!android::gsi::GetActiveDsu(&dsu_slot)) {\n                PERROR << __FUNCTION__ << \"(): failed to get active DSU slot\";\n                return false;\n            }\n            std::string lp_names;\n            if (!ReadFileToString(gsi::kGsiLpNamesFile, &lp_names)) {\n                PERROR << __FUNCTION__ << \"(): failed to read DSU LP names\";\n                return false;\n            }\n            TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, \",\"));\n        } else if (errno != ENOENT) {\n            PERROR << __FUNCTION__ << \"(): failed to access() DSU booted indicator\";\n            return false;\n        }\n\n        SkipMountingPartitions(fstab, false /* verbose */);\n    }\n    return true;\n}\n\nbool ReadFstabFromProcMounts(Fstab* fstab) {\n    // Don't call `ReadFstabFromFile` because the code for `path != kProcMountsPath` has an extra\n    // code size cost, even if it's never executed.\n    return ReadFstabFromFileCommon(kProcMountsPath, fstab);\n}\n\n// Returns fstab entries parsed from the device tree if they exist\nbool ReadFstabFromDt(Fstab* fstab, bool verbose) {\n    std::string fstab_buf = ReadFstabFromDt();\n    if (fstab_buf.empty()) {\n        if (verbose) LINFO << __FUNCTION__ << \"(): failed to read fstab from dt\";\n        return false;\n    }\n\n    if (!ParseFstabFromString(fstab_buf, /* proc_mounts = */ false, fstab)) {\n        if (verbose) {\n            LERROR << __FUNCTION__ << \"(): failed to load fstab from kernel:\" << std::endl\n                   << fstab_buf;\n        }\n        return false;\n    }\n\n    SkipMountingPartitions(fstab, verbose);\n\n    return true;\n}\n\n#ifdef NO_SKIP_MOUNT\nstatic constexpr bool kNoSkipMount = true;\n#else\nstatic constexpr bool kNoSkipMount = false;\n#endif\n\n// For GSI to skip mounting /product and /system_ext, until there are well-defined interfaces\n// between them and /system. Otherwise, the GSI flashed on /system might not be able to work with\n// device-specific /product and /system_ext. skip_mount.cfg belongs to system_ext partition because\n// only common files for all targets can be put into system partition. It is under\n// /system/system_ext because GSI is a single system.img that includes the contents of system_ext\n// partition and product partition under /system/system_ext and /system/product, respectively.\nbool SkipMountingPartitions(Fstab* fstab, bool verbose) {\n    if (kNoSkipMount) {\n        return true;\n    }\n\n    static constexpr char kSkipMountConfig[] = \"/system/system_ext/etc/init/config/skip_mount.cfg\";\n\n    std::string skip_mount_config;\n    auto save_errno = errno;\n    if (!ReadFileToString(kSkipMountConfig, &skip_mount_config)) {\n        errno = save_errno;  // missing file is expected\n        return true;\n    }\n    return SkipMountWithConfig(skip_mount_config, fstab, verbose);\n}\n\nbool SkipMountWithConfig(const std::string& skip_mount_config, Fstab* fstab, bool verbose) {\n    std::vector<std::string> skip_mount_patterns;\n    for (const auto& line : Split(skip_mount_config, \"\\n\")) {\n        if (line.empty() || StartsWith(line, \"#\")) {\n            continue;\n        }\n        skip_mount_patterns.push_back(line);\n    }\n\n    // Returns false if mount_point matches any of the skip mount patterns, so that the FstabEntry\n    // would be partitioned to the second group.\n    auto glob_pattern_mismatch = [&skip_mount_patterns](const FstabEntry& entry) -> bool {\n        for (const auto& pattern : skip_mount_patterns) {\n            if (!fnmatch(pattern.c_str(), entry.mount_point.c_str(), 0 /* flags */)) {\n                return false;\n            }\n        }\n        return true;\n    };\n    auto remove_from = std::stable_partition(fstab->begin(), fstab->end(), glob_pattern_mismatch);\n    if (verbose) {\n        for (auto it = remove_from; it != fstab->end(); ++it) {\n            LINFO << \"Skip mounting mountpoint: \" << it->mount_point;\n        }\n    }\n    fstab->erase(remove_from, fstab->end());\n    return true;\n}\n\n// Loads the fstab file and combines with fstab entries passed in from device tree.\nbool ReadDefaultFstab(Fstab* fstab) {\n    fstab->clear();\n    ReadFstabFromDt(fstab, false /* verbose */);\n\n    Fstab default_fstab;\n    const std::string default_fstab_path = GetFstabPath();\n    if (!default_fstab_path.empty() && ReadFstabFromFile(default_fstab_path, &default_fstab)) {\n        fstab->insert(fstab->end(), std::make_move_iterator(default_fstab.begin()),\n                      std::make_move_iterator(default_fstab.end()));\n    } else {\n        LINFO << __FUNCTION__ << \"(): failed to find device default fstab\";\n    }\n\n    return !fstab->empty();\n}\n\nstd::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path) {\n    return GetEntriesByPred(fstab,\n                            [&path](const FstabEntry& entry) { return entry.mount_point == path; });\n}\n\nFstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string_view path,\n                                  const std::string_view fstype) {\n    auto&& vec = GetEntriesByPred(fstab, [&path, fstype](const FstabEntry& entry) {\n        return entry.mount_point == path && entry.fs_type == fstype;\n    });\n    return vec.empty() ? nullptr : vec.front();\n}\n\nstd::vector<const FstabEntry*> GetEntriesForMountPoint(const Fstab* fstab,\n                                                       const std::string& path) {\n    return GetEntriesByPred(fstab,\n                            [&path](const FstabEntry& entry) { return entry.mount_point == path; });\n}\n\nFstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path) {\n    std::vector<FstabEntry*> entries = GetEntriesForMountPoint(fstab, path);\n    return entries.empty() ? nullptr : entries.front();\n}\n\nconst FstabEntry* GetEntryForMountPoint(const Fstab* fstab, const std::string& path) {\n    std::vector<const FstabEntry*> entries = GetEntriesForMountPoint(fstab, path);\n    return entries.empty() ? nullptr : entries.front();\n}\n\nstd::set<std::string> GetBootDevices() {\n    std::set<std::string> boot_devices;\n    // First check bootconfig, then kernel commandline, then the device tree\n    std::string value;\n    if (GetBootconfig(\"androidboot.boot_devices\", &value) ||\n        GetBootconfig(\"androidboot.boot_device\", &value)) {\n        // split by spaces and trim the trailing comma.\n        for (std::string_view device : android::base::Split(value, \" \")) {\n            base::ConsumeSuffix(&device, \",\");\n            boot_devices.emplace(device);\n        }\n        return boot_devices;\n    }\n\n    const std::string dt_file_name = GetAndroidDtDir() + \"boot_devices\";\n    if (GetKernelCmdline(\"androidboot.boot_devices\", &value) || ReadDtFile(dt_file_name, &value)) {\n        auto boot_devices_list = Split(value, \",\");\n        return {std::make_move_iterator(boot_devices_list.begin()),\n                std::make_move_iterator(boot_devices_list.end())};\n    }\n\n    ImportKernelCmdline([&](std::string key, std::string value) {\n        if (key == \"androidboot.boot_device\") {\n            boot_devices.emplace(std::move(value));\n        }\n    });\n    if (!boot_devices.empty()) {\n        return boot_devices;\n    }\n\n    // Fallback to extract boot devices from fstab.\n    Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        return {};\n    }\n\n    return ExtraBootDevices(fstab);\n}\n\nstd::string GetBootPartUuid() {\n    std::string boot_part_uuid;\n\n    if (GetBootconfig(\"androidboot.boot_part_uuid\", &boot_part_uuid)) {\n        return boot_part_uuid;\n    }\n\n    ImportKernelCmdline([&](std::string key, std::string value) {\n        if (key == \"androidboot.boot_part_uuid\") {\n            boot_part_uuid = value;\n        }\n    });\n\n    return boot_part_uuid;\n}\n\nstd::string GetVerityDeviceName(const FstabEntry& entry) {\n    std::string base_device;\n    if (entry.mount_point == \"/\") {\n        // When using system-as-root, the device name is fixed as \"vroot\".\n        if (entry.fs_mgr_flags.avb) {\n            return \"vroot\";\n        }\n        base_device = \"system\";\n    } else {\n        base_device = android::base::Basename(entry.mount_point);\n    }\n    return base_device + \"-verity\";\n}\n\nbool InRecovery() {\n    // Check the existence of recovery binary instead of using the compile time\n    // __ANDROID_RECOVERY__ macro.\n    // If BOARD_USES_RECOVERY_AS_BOOT is true, both normal and recovery boot\n    // mode would use the same init binary, which would mean during normal boot\n    // the '/init' binary is actually a symlink pointing to\n    // init_second_stage.recovery, which would be compiled with\n    // __ANDROID_RECOVERY__ defined.\n    return access(\"/system/bin/recovery\", F_OK) == 0 || access(\"/sbin/recovery\", F_OK) == 0;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n\nbool is_dt_compatible() {\n    std::string file_name = android::fs_mgr::GetAndroidDtDir() + \"compatible\";\n    std::string dt_value;\n    if (android::fs_mgr::ReadDtFile(file_name, &dt_value)) {\n        if (dt_value == \"android,firmware\") {\n            return true;\n        }\n    }\n\n    return false;\n}\n"
  },
  {
    "path": "fs_mgr/libfstab/fstab_priv.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <string>\n\n#include <fstab/fstab.h>\n\n// Do not include logging_macros.h here as this header is used by fs_mgr, too.\n\nbool fs_mgr_get_boot_config(const std::string& key, std::string* out_val);\n\nbool fs_mgr_update_for_slotselect(android::fs_mgr::Fstab* fstab);\nbool is_dt_compatible();\n\nnamespace android {\nnamespace fs_mgr {\n\nbool InRecovery();\nbool ParseFstabFromString(const std::string& fstab_str, bool proc_mounts, Fstab* fstab_out);\nbool SkipMountWithConfig(const std::string& skip_config, Fstab* fstab, bool verbose);\nstd::string GetFstabPath();\n\nvoid ImportBootconfigFromString(const std::string& bootconfig,\n                                const std::function<void(std::string, std::string)>& fn);\n\nvoid ImportKernelCmdlineFromString(const std::string& cmdline,\n                                   const std::function<void(std::string, std::string)>& fn);\n\nbool GetKernelCmdlineFromString(const std::string& cmdline, const std::string& key,\n                                std::string* out);\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfstab/fuzz/Android.bp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_fuzz {\n  name: \"libfstab_fuzzer\",\n  srcs: [\n    \"fs_mgr_fstab_fuzzer.cpp\",\n  ],\n  static_libs: [\n    \"libfstab\",\n  ],\n  shared_libs: [\n    \"libbase\",\n  ],\n\n  dictionary: \"fstab.dict\",\n  fuzz_config: {\n    cc: [\n      \"yochiang@google.com\",\n    ],\n  },\n}\n"
  },
  {
    "path": "fs_mgr/libfstab/fuzz/fs_mgr_fstab_fuzzer.cpp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <string>\n#include <vector>\n\n#include <fstab/fstab.h>\n#include <fuzzer/FuzzedDataProvider.h>\n\n#include \"../fstab_priv.h\"\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider fdp(data, size);\n\n    std::string make_fstab_str = fdp.ConsumeRandomLengthString();\n    std::string dsu_slot = fdp.ConsumeRandomLengthString(30);\n    std::vector<std::string> dsu_partitions = {\n            fdp.ConsumeRandomLengthString(30),\n            fdp.ConsumeRandomLengthString(30),\n    };\n    std::string skip_mount_config = fdp.ConsumeRemainingBytesAsString();\n\n    android::fs_mgr::Fstab fstab;\n    android::fs_mgr::ParseFstabFromString(make_fstab_str, /* proc_mounts = */ false, &fstab);\n    android::fs_mgr::TransformFstabForDsu(&fstab, dsu_slot, dsu_partitions);\n    android::fs_mgr::SkipMountWithConfig(skip_mount_config, &fstab, /* verbose = */ false);\n\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/libfstab/fuzz/fstab.dict",
    "content": "\"#\"\n\"=\"\n\",\"\n\"f2fs\"\n\n# mount flags\n\"noatime\"\n\"noexec\"\n\"nosuid\"\n\"nodev\"\n\"nodiratime\"\n\"ro\"\n\"rw\"\n\"sync\"\n\"remount\"\n\"bind\"\n\"rec\"\n\"unbindable\"\n\"private\"\n\"slave\"\n\"shared\"\n\"defaults\"\n\n# fs_mgr flags\n\"wait\"\n\"check\"\n\"nonremovable\"\n\"recoveryonly\"\n\"noemulatedsd\"\n\"notrim\"\n\"verify\"\n\"formattable\"\n\"slotselect\"\n\"latemount\"\n\"nofail\"\n\"verifyatboot\"\n\"quota\"\n\"avb\"\n\"logical\"\n\"checkpoint=block\"\n\"checkpoint=fs\"\n\"first_stage_mount\"\n\"slotselect_other\"\n\"fsverity\"\n\"metadata_csum\"\n\"fscompress\"\n\"overlayfs_remove_missing_lowerdir\"\n\n# fs_mgr flags that expect an argument\n\"reserve_root=\"\n\"lowerdir=\"\n\"encryptable=\"\n\"voldmanaged=\"\n\"length=\"\n\"swapprio=\"\n\"zramsize=\"\n\"forceencrypt=\"\n\"fileencryption=\"\n\"forcefdeorfbe=\"\n\"max_comp_streams=\"\n\"reservedsize=\"\n\"readahead_size_kb=\"\n\"eraseblk=\"\n\"logicalblk=\"\n\"avb_keys=\"\n\"avb=\"\n\"keydirectory=\"\n\"metadata_encryption=\"\n\"sysfs_path=\"\n\"zram_backingdev_size=\"\n"
  },
  {
    "path": "fs_mgr/libfstab/include/fstab/fstab.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <functional>\n#include <set>\n#include <string>\n#include <vector>\n\nstd::string fs_mgr_get_slot_suffix();\nstd::string fs_mgr_get_other_slot_suffix();\n\nnamespace android {\nnamespace fs_mgr {\n\nstruct FstabEntry {\n    std::string blk_device;\n    std::vector<std::string> user_devices;\n    std::vector<int> device_aliased;\n    std::string logical_partition_name;\n    std::string mount_point;\n    std::string fs_type;\n    unsigned long flags = 0;\n    std::string fs_options;\n    std::string fs_checkpoint_opts;\n    std::string metadata_key_dir;\n    std::string metadata_encryption_options;\n    off64_t length = 0;\n    std::string label;\n    int partnum = -1;\n    int swap_prio = -1;\n    int max_comp_streams = 0;\n    off64_t zram_size = 0;\n    off64_t reserved_size = 0;\n    off64_t readahead_size_kb = -1;\n    std::string encryption_options;\n    off64_t erase_blk_size = 0;\n    off64_t logical_blk_size = 0;\n    std::string sysfs_path;\n    std::string vbmeta_partition;\n    uint64_t zram_backingdev_size = 0;\n    std::string avb_keys;\n    std::string lowerdir;\n    std::string avb_hashtree_digest;\n\n    struct FsMgrFlags {\n        bool wait : 1;\n        bool check : 1;\n        bool crypt : 1;  // Now only used to identify adoptable storage volumes\n        bool nonremovable : 1;\n        bool vold_managed : 1;\n        bool recovery_only : 1;\n        bool no_emulated_sd : 1;  // No emulated sdcard daemon; sd card is the only external\n                                  // storage.\n        bool no_trim : 1;\n        bool file_encryption : 1;\n        bool formattable : 1;\n        bool slot_select : 1;\n        bool late_mount : 1;\n        bool no_fail : 1;\n        bool quota : 1;\n        bool avb : 1;\n        bool logical : 1;\n        bool checkpoint_blk : 1;\n        bool checkpoint_fs : 1;\n        bool first_stage_mount : 1;\n        bool slot_select_other : 1;\n        bool fs_verity : 1;\n        bool ext_meta_csum : 1;\n        bool fs_compress : 1;\n        bool overlayfs_remove_missing_lowerdir : 1;\n        bool is_zoned : 1;\n    } fs_mgr_flags = {};\n\n    bool is_encryptable() const { return fs_mgr_flags.crypt; }\n};\n\n// An Fstab is a collection of FstabEntry structs.\n// The entries must be kept in the same order as they were seen in the fstab.\n// Unless explicitly requested, a lookup on mount point should always return the 1st one.\nusing Fstab = std::vector<FstabEntry>;\n\nbool ReadFstabFromFile(const std::string& path, Fstab* fstab);\nbool ReadFstabFromProcMounts(Fstab* fstab);\nbool ReadFstabFromDt(Fstab* fstab, bool verbose = true);\nbool ReadDefaultFstab(Fstab* fstab);\nbool SkipMountingPartitions(Fstab* fstab, bool verbose = false);\n\n// The Fstab can contain multiple entries for the same mount point with different configurations.\nstd::vector<FstabEntry*> GetEntriesForMountPoint(Fstab* fstab, const std::string& path);\n\n// Like GetEntriesForMountPoint() but return only the first entry or nullptr if no entry is found.\nFstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string& path);\nconst FstabEntry* GetEntryForMountPoint(const Fstab* fstab, const std::string& path);\n\nFstabEntry* GetEntryForMountPoint(Fstab* fstab, const std::string_view path,\n                                  const std::string_view fstype);\n\n// This method builds DSU fstab entries and transfer the fstab.\n//\n// fstab points to the unmodified fstab.\n//\n// dsu_partitions contains partition names, e.g.\n//     dsu_partitions[0] = \"system_gsi\"\n//     dsu_partitions[1] = \"userdata_gsi\"\n//     dsu_partitions[2] = ...\nvoid TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,\n                          const std::vector<std::string>& dsu_partitions);\n\nstd::set<std::string> GetBootDevices();\n\n// Get the Partition UUID the kernel loaded from if the bootloader passed it.\n//\n// If the kernel's Partition UUID is provided then we can use this to help\n// identify which block device contains the filesystems we care about.\n//\n// NOTE: Nothing secures a UUID other than the convention that two disks\n// aren't supposed to both have the same UUID. We still need other mechanisms\n// to ensure we've got the right disk.\nstd::string GetBootPartUuid();\n\n// Return the name of the dm-verity device for the given fstab entry. This does\n// not check whether the device is valid or exists; it merely returns the\n// expected name.\nstd::string GetVerityDeviceName(const FstabEntry& entry);\n\n// Returns the Android Device Tree directory as specified in the kernel bootconfig or cmdline.\n// If the platform does not configure a custom DT path, returns the standard one (based in procfs).\nconst std::string& GetAndroidDtDir();\n\n// Import the kernel bootconfig by calling the callback |fn| with each key-value pair.\nvoid ImportBootconfig(const std::function<void(std::string, std::string)>& fn);\n\n// Get the kernel bootconfig value for |key|.\n// Returns true if |key| is found in bootconfig.\n// Otherwise returns false and |*out| is not modified.\nbool GetBootconfig(const std::string& key, std::string* out);\n\n// Import the kernel cmdline by calling the callback |fn| with each key-value pair.\nvoid ImportKernelCmdline(const std::function<void(std::string, std::string)>& fn);\n\n// Get the kernel cmdline value for |key|.\n// Returns true if |key| is found in the kernel cmdline.\n// Otherwise returns false and |*out| is not modified.\nbool GetKernelCmdline(const std::string& key, std::string* out);\n\n// Return the \"other\" slot for the given slot suffix.\nstd::string OtherSlotSuffix(const std::string& suffix);\n\nbool GetBootconfigFromString(const std::string& bootconfig, const std::string& key,\n                             std::string* out);\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libfstab/logging_macros.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android-base/logging.h>\n\n#define FSTAB_TAG \"[libfstab] \"\n\n/* The CHECK() in logging.h will use program invocation name as the tag.\n * Thus, the log will have prefix \"init: \" when libfs_mgr is statically\n * linked in the init process. This might be opaque when debugging.\n * Append a library name tag at the end of the abort message to aid debugging.\n */\n#define FSTAB_CHECK(x) CHECK(x) << \"in \" << FSTAB_TAG\n\n// Logs a message to kernel\n#define LINFO LOG(INFO) << FSTAB_TAG\n#define LWARNING LOG(WARNING) << FSTAB_TAG\n#define LERROR LOG(ERROR) << FSTAB_TAG\n#define LFATAL LOG(FATAL) << FSTAB_TAG\n\n// Logs a message with strerror(errno) at the end\n#define PINFO PLOG(INFO) << FSTAB_TAG\n#define PWARNING PLOG(WARNING) << FSTAB_TAG\n#define PERROR PLOG(ERROR) << FSTAB_TAG\n#define PFATAL PLOG(FATAL) << FSTAB_TAG\n"
  },
  {
    "path": "fs_mgr/libfstab/slotselect.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n\n#include <string>\n\n#include \"fstab_priv.h\"\n#include \"logging_macros.h\"\n\n// Realistically, this file should be part of the android::fs_mgr namespace;\nusing namespace android::fs_mgr;\n\n// https://source.android.com/devices/tech/ota/ab/ab_implement#partitions\n// All partitions that are A/B-ed should be named as follows (slots are always\n// named a, b, etc.): boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.\nstatic std::string other_suffix(const std::string& slot_suffix) {\n    if (slot_suffix == \"_a\") {\n        return \"_b\";\n    }\n    if (slot_suffix == \"_b\") {\n        return \"_a\";\n    }\n    return \"\";\n}\n\n// Returns \"_b\" or \"_a\", which is *the other* slot of androidboot.slot_suffix\n// in kernel cmdline, or an empty string if that parameter does not exist.\nstd::string fs_mgr_get_other_slot_suffix() {\n    return other_suffix(fs_mgr_get_slot_suffix());\n}\n\n// Returns \"_a\" or \"_b\" based on androidboot.slot_suffix in kernel cmdline, or an empty string\n// if that parameter does not exist.\nstd::string fs_mgr_get_slot_suffix() {\n    std::string ab_suffix;\n\n    fs_mgr_get_boot_config(\"slot_suffix\", &ab_suffix);\n    return ab_suffix;\n}\n\n// Updates |fstab| for slot_suffix. Returns true on success, false on error.\nbool fs_mgr_update_for_slotselect(Fstab* fstab) {\n    std::string ab_suffix;\n\n    for (auto& entry : *fstab) {\n        if (!entry.fs_mgr_flags.slot_select && !entry.fs_mgr_flags.slot_select_other) {\n            continue;\n        }\n\n        if (ab_suffix.empty()) {\n            ab_suffix = fs_mgr_get_slot_suffix();\n            // Return false if failed to get ab_suffix when MF_SLOTSELECT is specified.\n            if (ab_suffix.empty()) return false;\n        }\n\n        const auto& update_suffix =\n                entry.fs_mgr_flags.slot_select ? ab_suffix : other_suffix(ab_suffix);\n        entry.blk_device = entry.blk_device + update_suffix;\n        entry.logical_partition_name = entry.logical_partition_name + update_suffix;\n    }\n    return true;\n}\n\nnamespace android {\nnamespace fs_mgr {\n\nstd::string OtherSlotSuffix(const std::string& suffix) {\n    return other_suffix(suffix);\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/Android.bp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nliblp_lib_deps = [\n    \"libbase\",\n    \"liblog\",\n    \"libcrypto_utils\",\n    \"libsparse\",\n    \"libext4_utils\",\n    \"libz\",\n]\n\ncc_library {\n    name: \"liblp\",\n    host_supported: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    defaults: [\"fs_mgr_defaults\"],\n    cppflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n    ],\n    srcs: [\n        \"builder.cpp\",\n        \"super_layout_builder.cpp\",\n        \"images.cpp\",\n        \"partition_opener.cpp\",\n        \"property_fetcher.cpp\",\n        \"reader.cpp\",\n        \"utility.cpp\",\n        \"writer.cpp\",\n    ],\n    shared_libs: [\n        \"libcrypto\",\n    ] + liblp_lib_deps,\n    target: {\n        windows: {\n            enabled: true,\n        },\n        android: {\n            shared_libs: [\n                \"libcutils\",\n            ],\n        },\n    },\n    export_include_dirs: [\"include\"],\n}\n\ncc_defaults {\n    name: \"liblp_test_defaults\",\n    defaults: [\"fs_mgr_defaults\"],\n    cppflags: [\n        \"-Wno-unused-parameter\",\n    ],\n    static_libs: [\n        \"libcutils\",\n        \"libgmock\",\n        \"liblp\",\n        \"libcrypto_static\",\n    ] + liblp_lib_deps,\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    target: {\n        android: {\n            srcs: [\n                \"device_test.cpp\",\n                \"io_test.cpp\",\n            ],\n            static_libs: [\n                \"libfs_mgr\",\n            ],\n        }\n    },\n    stl: \"libc++_static\",\n    srcs: [\n        \"builder_test.cpp\",\n        \"super_layout_builder_test.cpp\",\n        \"utility_test.cpp\",\n        \":TestPartitionOpener_group\",\n    ],\n}\n\ncc_test {\n    name: \"liblp_test\",\n    defaults: [\"liblp_test_defaults\"],\n    test_suites: [\"device-tests\"],\n    auto_gen_config: true,\n    require_root: true,\n    host_supported: true\n}\n\ncc_test {\n    name: \"vts_core_liblp_test\",\n    defaults: [\"liblp_test_defaults\"],\n    test_suites: [\"vts\"],\n    auto_gen_config: true,\n    test_options: {\n        min_shipping_api_level: 29,\n    },\n    require_root: true,\n}\n\ncc_test {\n    name: \"vts_kernel_liblp_test\",\n    defaults: [\"liblp_test_defaults\"],\n}\n\nfilegroup {\n   name: \"TestPartitionOpener_group\",\n   srcs: [ \"test_partition_opener.cpp\"],\n}\n"
  },
  {
    "path": "fs_mgr/liblp/OWNERS",
    "content": "# Bug component: 391836\ndvander@google.com\n"
  },
  {
    "path": "fs_mgr/liblp/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"liblp_test\"\n    }\n  ],\n  \"hwasan-presubmit\": [\n    {\n      \"name\": \"liblp_test\"\n    }\n  ]\n}\n"
  },
  {
    "path": "fs_mgr/liblp/builder.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"liblp/builder.h\"\n\n#include <string.h>\n\n#include <algorithm>\n#include <limits>\n\n#include <android-base/unique_fd.h>\n\n#include \"liblp/liblp.h\"\n#include \"liblp/property_fetcher.h\"\n#include \"reader.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nstd::ostream& operator<<(std::ostream& os, const Extent& extent) {\n    switch (extent.GetExtentType()) {\n        case ExtentType::kZero: {\n            os << \"type: Zero\";\n            break;\n        }\n        case ExtentType::kLinear: {\n            auto linear_extent = static_cast<const LinearExtent*>(&extent);\n            os << \"type: Linear, physical sectors: \" << linear_extent->physical_sector()\n               << \", end sectors: \" << linear_extent->end_sector();\n            break;\n        }\n    }\n    return os;\n}\n\nbool LinearExtent::AddTo(LpMetadata* out) const {\n    if (device_index_ >= out->block_devices.size()) {\n        LERROR << \"Extent references unknown block device.\";\n        return false;\n    }\n    out->extents.emplace_back(\n            LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_LINEAR, physical_sector_, device_index_});\n    return true;\n}\n\nbool LinearExtent::operator==(const android::fs_mgr::Extent& other) const {\n    if (other.GetExtentType() != ExtentType::kLinear) {\n        return false;\n    }\n\n    auto other_ptr = static_cast<const LinearExtent*>(&other);\n    return num_sectors_ == other_ptr->num_sectors_ &&\n           physical_sector_ == other_ptr->physical_sector_ &&\n           device_index_ == other_ptr->device_index_;\n}\n\nbool LinearExtent::OverlapsWith(const LinearExtent& other) const {\n    if (device_index_ != other.device_index()) {\n        return false;\n    }\n    return physical_sector() < other.end_sector() && other.physical_sector() < end_sector();\n}\n\nbool LinearExtent::OverlapsWith(const Interval& interval) const {\n    if (device_index_ != interval.device_index) {\n        return false;\n    }\n    return physical_sector() < interval.end && interval.start < end_sector();\n}\n\nInterval LinearExtent::AsInterval() const {\n    return Interval(device_index(), physical_sector(), end_sector());\n}\n\nbool ZeroExtent::AddTo(LpMetadata* out) const {\n    out->extents.emplace_back(LpMetadataExtent{num_sectors_, LP_TARGET_TYPE_ZERO, 0, 0});\n    return true;\n}\n\nbool ZeroExtent::operator==(const android::fs_mgr::Extent& other) const {\n    return other.GetExtentType() == ExtentType::kZero && num_sectors_ == other.num_sectors();\n}\n\nPartition::Partition(std::string_view name, std::string_view group_name, uint32_t attributes)\n    : name_(name), group_name_(group_name), attributes_(attributes), size_(0) {}\n\nvoid Partition::AddExtent(std::unique_ptr<Extent>&& extent) {\n    size_ += extent->num_sectors() * LP_SECTOR_SIZE;\n\n    if (LinearExtent* new_extent = extent->AsLinearExtent()) {\n        if (!extents_.empty() && extents_.back()->AsLinearExtent()) {\n            LinearExtent* prev_extent = extents_.back()->AsLinearExtent();\n            if (prev_extent->end_sector() == new_extent->physical_sector() &&\n                prev_extent->device_index() == new_extent->device_index()) {\n                // If the previous extent can be merged into this new one, do so\n                // to avoid creating unnecessary extents.\n                extent = std::make_unique<LinearExtent>(\n                        prev_extent->num_sectors() + new_extent->num_sectors(),\n                        prev_extent->device_index(), prev_extent->physical_sector());\n                extents_.pop_back();\n            }\n        }\n    }\n    extents_.push_back(std::move(extent));\n}\n\nvoid Partition::RemoveExtents() {\n    size_ = 0;\n    extents_.clear();\n}\n\nvoid Partition::ShrinkTo(uint64_t aligned_size) {\n    if (aligned_size == 0) {\n        RemoveExtents();\n        return;\n    }\n\n    // Remove or shrink extents of any kind until the total partition size is\n    // equal to the requested size.\n    uint64_t sectors_to_remove = (size_ - aligned_size) / LP_SECTOR_SIZE;\n    while (sectors_to_remove) {\n        Extent* extent = extents_.back().get();\n        if (extent->num_sectors() > sectors_to_remove) {\n            size_ -= sectors_to_remove * LP_SECTOR_SIZE;\n            extent->set_num_sectors(extent->num_sectors() - sectors_to_remove);\n            break;\n        }\n        size_ -= (extent->num_sectors() * LP_SECTOR_SIZE);\n        sectors_to_remove -= extent->num_sectors();\n        extents_.pop_back();\n    }\n    DCHECK(size_ == aligned_size);\n}\n\nPartition Partition::GetBeginningExtents(uint64_t aligned_size) const {\n    Partition p(name_, group_name_, attributes_);\n    for (const auto& extent : extents_) {\n        auto le = extent->AsLinearExtent();\n        if (le) {\n            p.AddExtent(std::make_unique<LinearExtent>(*le));\n        } else {\n            p.AddExtent(std::make_unique<ZeroExtent>(extent->num_sectors()));\n        }\n    }\n    p.ShrinkTo(aligned_size);\n    return p;\n}\n\nuint64_t Partition::BytesOnDisk() const {\n    uint64_t sectors = 0;\n    for (const auto& extent : extents_) {\n        if (!extent->AsLinearExtent()) {\n            continue;\n        }\n        sectors += extent->num_sectors();\n    }\n    return sectors * LP_SECTOR_SIZE;\n}\n\nstd::unique_ptr<MetadataBuilder> MetadataBuilder::New(const IPartitionOpener& opener,\n                                                      const std::string& super_partition,\n                                                      uint32_t slot_number) {\n    std::unique_ptr<LpMetadata> metadata = ReadMetadata(opener, super_partition, slot_number);\n    if (!metadata) {\n        return nullptr;\n    }\n    return New(*metadata.get(), &opener);\n}\n\nstd::unique_ptr<MetadataBuilder> MetadataBuilder::New(const std::string& super_partition,\n                                                      uint32_t slot_number) {\n    return New(PartitionOpener(), super_partition, slot_number);\n}\n\nstd::unique_ptr<MetadataBuilder> MetadataBuilder::New(\n        const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,\n        uint32_t metadata_max_size, uint32_t metadata_slot_count) {\n    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());\n    if (!builder->Init(block_devices, super_partition, metadata_max_size, metadata_slot_count)) {\n        return nullptr;\n    }\n    return builder;\n}\n\nstd::unique_ptr<MetadataBuilder> MetadataBuilder::New(const LpMetadata& metadata,\n                                                      const IPartitionOpener* opener) {\n    std::unique_ptr<MetadataBuilder> builder(new MetadataBuilder());\n    if (!builder->Init(metadata)) {\n        return nullptr;\n    }\n    if (opener) {\n        for (size_t i = 0; i < builder->block_devices_.size(); i++) {\n            std::string partition_name = builder->GetBlockDevicePartitionName(i);\n            BlockDeviceInfo device_info;\n            if (opener->GetInfo(partition_name, &device_info)) {\n                builder->UpdateBlockDeviceInfo(i, device_info);\n            }\n        }\n    }\n    return builder;\n}\n\nstd::unique_ptr<MetadataBuilder> MetadataBuilder::NewForUpdate(const IPartitionOpener& opener,\n                                                               const std::string& source_partition,\n                                                               uint32_t source_slot_number,\n                                                               uint32_t target_slot_number,\n                                                               bool always_keep_source_slot) {\n    auto metadata = ReadMetadata(opener, source_partition, source_slot_number);\n    if (!metadata) {\n        return nullptr;\n    }\n\n    // On retrofit DAP devices, modify the metadata so that it is suitable for being written\n    // to the target slot later. We detect retrofit DAP devices by checking the super partition\n    // name and system properties.\n    // See comments for UpdateMetadataForOtherSuper.\n    auto super_device = GetMetadataSuperBlockDevice(*metadata.get());\n    if (android::fs_mgr::GetBlockDevicePartitionName(*super_device) != \"super\" &&\n        IsRetrofitDynamicPartitionsDevice()) {\n        if (!UpdateMetadataForOtherSuper(metadata.get(), source_slot_number, target_slot_number)) {\n            return nullptr;\n        }\n    }\n\n    if (IPropertyFetcher::GetInstance()->GetBoolProperty(\"ro.virtual_ab.enabled\", false)) {\n        if (always_keep_source_slot) {\n            // always_keep_source_slot implies the target build does not support snapshots.\n            // Clear unsupported attributes.\n            SetMetadataHeaderV0(metadata.get());\n        } else {\n            // !always_keep_source_slot implies the target build supports snapshots. Do snapshot\n            // updates.\n            if (!UpdateMetadataForInPlaceSnapshot(metadata.get(), source_slot_number,\n                                                  target_slot_number)) {\n                return nullptr;\n            }\n        }\n    }\n\n    return New(*metadata.get(), &opener);\n}\n\n// For retrofit DAP devices, there are (conceptually) two super partitions. We'll need to translate\n// block device and group names to update their slot suffixes.\n// (On the other hand, On non-retrofit DAP devices there is only one location for metadata: the\n// super partition. update_engine will remove and resize partitions as needed.)\nbool MetadataBuilder::UpdateMetadataForOtherSuper(LpMetadata* metadata, uint32_t source_slot_number,\n                                                  uint32_t target_slot_number) {\n    // Clear partitions and extents, since they have no meaning on the target\n    // slot. We also clear groups since they are re-added during OTA.\n    metadata->partitions.clear();\n    metadata->extents.clear();\n    metadata->groups.clear();\n\n    std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);\n    std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);\n\n    // Translate block devices.\n    auto source_block_devices = std::move(metadata->block_devices);\n    for (const auto& source_block_device : source_block_devices) {\n        std::string partition_name =\n                android::fs_mgr::GetBlockDevicePartitionName(source_block_device);\n        std::string slot_suffix = GetPartitionSlotSuffix(partition_name);\n        if (slot_suffix.empty() || slot_suffix != source_slot_suffix) {\n            // This should never happen. It means that the source metadata\n            // refers to a target or unknown block device.\n            LERROR << \"Invalid block device for slot \" << source_slot_suffix << \": \"\n                   << partition_name;\n            return false;\n        }\n        std::string new_name =\n                partition_name.substr(0, partition_name.size() - slot_suffix.size()) +\n                target_slot_suffix;\n\n        auto new_device = source_block_device;\n        if (!UpdateBlockDevicePartitionName(&new_device, new_name)) {\n            LERROR << \"Partition name too long: \" << new_name;\n            return false;\n        }\n        metadata->block_devices.emplace_back(new_device);\n    }\n\n    return true;\n}\n\nMetadataBuilder::MetadataBuilder() : auto_slot_suffixing_(false) {\n    memset(&geometry_, 0, sizeof(geometry_));\n    geometry_.magic = LP_METADATA_GEOMETRY_MAGIC;\n    geometry_.struct_size = sizeof(geometry_);\n\n    memset(&header_, 0, sizeof(header_));\n    header_.magic = LP_METADATA_HEADER_MAGIC;\n    header_.major_version = LP_METADATA_MAJOR_VERSION;\n    header_.minor_version = LP_METADATA_MINOR_VERSION_MIN;\n    header_.header_size = sizeof(LpMetadataHeaderV1_0);\n    header_.partitions.entry_size = sizeof(LpMetadataPartition);\n    header_.extents.entry_size = sizeof(LpMetadataExtent);\n    header_.groups.entry_size = sizeof(LpMetadataPartitionGroup);\n    header_.block_devices.entry_size = sizeof(LpMetadataBlockDevice);\n}\n\nbool MetadataBuilder::Init(const LpMetadata& metadata) {\n    geometry_ = metadata.geometry;\n    block_devices_ = metadata.block_devices;\n\n    // Bump the version as necessary to copy any newer fields.\n    if (metadata.header.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {\n        RequireExpandedMetadataHeader();\n        header_.flags = metadata.header.flags;\n    }\n\n    for (const auto& group : metadata.groups) {\n        std::string group_name = GetPartitionGroupName(group);\n        if (!AddGroup(group_name, group.maximum_size)) {\n            return false;\n        }\n    }\n\n    for (const auto& partition : metadata.partitions) {\n        std::string group_name = GetPartitionGroupName(metadata.groups[partition.group_index]);\n        Partition* builder =\n                AddPartition(GetPartitionName(partition), group_name, partition.attributes);\n        if (!builder) {\n            return false;\n        }\n        ImportExtents(builder, metadata, partition);\n    }\n    return true;\n}\n\nvoid MetadataBuilder::ImportExtents(Partition* dest, const LpMetadata& metadata,\n                                    const LpMetadataPartition& source) {\n    for (size_t i = 0; i < source.num_extents; i++) {\n        const LpMetadataExtent& extent = metadata.extents[source.first_extent_index + i];\n        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {\n            auto copy = std::make_unique<LinearExtent>(extent.num_sectors, extent.target_source,\n                                                       extent.target_data);\n            dest->AddExtent(std::move(copy));\n        } else if (extent.target_type == LP_TARGET_TYPE_ZERO) {\n            auto copy = std::make_unique<ZeroExtent>(extent.num_sectors);\n            dest->AddExtent(std::move(copy));\n        }\n    }\n}\n\nstatic bool VerifyDeviceProperties(const BlockDeviceInfo& device_info) {\n    if (device_info.logical_block_size == 0) {\n        LERROR << \"Block device \" << device_info.partition_name\n               << \" logical block size must not be zero.\";\n        return false;\n    }\n    if (device_info.logical_block_size % LP_SECTOR_SIZE != 0) {\n        LERROR << \"Block device \" << device_info.partition_name\n               << \" logical block size must be a multiple of 512.\";\n        return false;\n    }\n    if (device_info.size % device_info.logical_block_size != 0) {\n        LERROR << \"Block device \" << device_info.partition_name\n               << \" size must be a multiple of its block size.\";\n        return false;\n    }\n    if (device_info.alignment_offset % LP_SECTOR_SIZE != 0) {\n        LERROR << \"Block device \" << device_info.partition_name\n               << \" alignment offset is not sector-aligned.\";\n        return false;\n    }\n    if (device_info.alignment % LP_SECTOR_SIZE != 0) {\n        LERROR << \"Block device \" << device_info.partition_name\n               << \" partition alignment is not sector-aligned.\";\n        return false;\n    }\n    return true;\n}\n\nbool MetadataBuilder::Init(const std::vector<BlockDeviceInfo>& block_devices,\n                           const std::string& super_partition, uint32_t metadata_max_size,\n                           uint32_t metadata_slot_count) {\n    if (metadata_max_size < sizeof(LpMetadataHeader)) {\n        LERROR << \"Invalid metadata maximum size.\";\n        return false;\n    }\n    if (metadata_slot_count == 0) {\n        LERROR << \"Invalid metadata slot count.\";\n        return false;\n    }\n    if (block_devices.empty()) {\n        LERROR << \"No block devices were specified.\";\n        return false;\n    }\n\n    // Align the metadata size up to the nearest sector.\n    if (!AlignTo(metadata_max_size, LP_SECTOR_SIZE, &metadata_max_size)) {\n        LERROR << \"Max metadata size \" << metadata_max_size << \" is too large.\";\n        return false;\n    }\n\n    // Validate and build the block device list.\n    uint32_t logical_block_size = 0;\n    for (const auto& device_info : block_devices) {\n        if (!VerifyDeviceProperties(device_info)) {\n            return false;\n        }\n\n        if (!logical_block_size) {\n            logical_block_size = device_info.logical_block_size;\n        }\n        if (logical_block_size != device_info.logical_block_size) {\n            LERROR << \"All partitions must have the same logical block size.\";\n            return false;\n        }\n\n        LpMetadataBlockDevice out = {};\n        out.alignment = device_info.alignment;\n        out.alignment_offset = device_info.alignment_offset;\n        out.size = device_info.size;\n        if (device_info.partition_name.size() > sizeof(out.partition_name)) {\n            LERROR << \"Partition name \" << device_info.partition_name << \" exceeds maximum length.\";\n            return false;\n        }\n        strncpy(out.partition_name, device_info.partition_name.c_str(), sizeof(out.partition_name));\n\n        // In the case of the super partition, this field will be adjusted\n        // later. For all partitions, the first 512 bytes are considered\n        // untouched to be compatible code that looks for an MBR. Thus we\n        // start counting free sectors at sector 1, not 0.\n        uint64_t free_area_start = LP_SECTOR_SIZE;\n        bool ok;\n        if (out.alignment) {\n            ok = AlignTo(free_area_start, out.alignment, &free_area_start);\n        } else {\n            ok = AlignTo(free_area_start, logical_block_size, &free_area_start);\n        }\n        if (!ok) {\n            LERROR << \"Integer overflow computing free area start\";\n            return false;\n        }\n        out.first_logical_sector = free_area_start / LP_SECTOR_SIZE;\n\n        // There must be one logical block of space available.\n        uint64_t minimum_size = out.first_logical_sector * LP_SECTOR_SIZE + logical_block_size;\n        if (device_info.size < minimum_size) {\n            LERROR << \"Block device \" << device_info.partition_name\n                   << \" is too small to hold any logical partitions.\";\n            return false;\n        }\n\n        // The \"root\" of the super partition is always listed first.\n        if (device_info.partition_name == super_partition) {\n            block_devices_.emplace(block_devices_.begin(), out);\n        } else {\n            block_devices_.emplace_back(out);\n        }\n    }\n    if (GetBlockDevicePartitionName(0) != super_partition) {\n        LERROR << \"No super partition was specified.\";\n        return false;\n    }\n\n    LpMetadataBlockDevice& super = block_devices_[0];\n\n    // We reserve a geometry block (4KB) plus space for each copy of the\n    // maximum size of a metadata blob. Then, we double that space since\n    // we store a backup copy of everything.\n    uint64_t total_reserved = GetTotalMetadataSize(metadata_max_size, metadata_slot_count);\n    if (super.size < total_reserved) {\n        LERROR << \"Attempting to create metadata on a block device that is too small.\";\n        return false;\n    }\n\n    // Compute the first free sector, factoring in alignment.\n    uint64_t free_area_start = total_reserved;\n    bool ok;\n    if (super.alignment) {\n        ok = AlignTo(free_area_start, super.alignment, &free_area_start);\n    } else {\n        ok = AlignTo(free_area_start, logical_block_size, &free_area_start);\n    }\n    if (!ok) {\n        LERROR << \"Integer overflow computing free area start\";\n        return false;\n    }\n    super.first_logical_sector = free_area_start / LP_SECTOR_SIZE;\n\n    // There must be one logical block of free space remaining (enough for one partition).\n    uint64_t minimum_disk_size = (super.first_logical_sector * LP_SECTOR_SIZE) + logical_block_size;\n    if (super.size < minimum_disk_size) {\n        LERROR << \"Device must be at least \" << minimum_disk_size << \" bytes, only has \"\n               << super.size;\n        return false;\n    }\n\n    geometry_.metadata_max_size = metadata_max_size;\n    geometry_.metadata_slot_count = metadata_slot_count;\n    geometry_.logical_block_size = logical_block_size;\n\n    if (!AddGroup(std::string(kDefaultGroup), 0)) {\n        return false;\n    }\n    return true;\n}\n\nbool MetadataBuilder::AddGroup(std::string_view group_name, uint64_t maximum_size) {\n    if (FindGroup(group_name)) {\n        LERROR << \"Group already exists: \" << group_name;\n        return false;\n    }\n    groups_.push_back(std::make_unique<PartitionGroup>(group_name, maximum_size));\n    return true;\n}\n\nPartition* MetadataBuilder::AddPartition(const std::string& name, uint32_t attributes) {\n    return AddPartition(name, kDefaultGroup, attributes);\n}\n\nPartition* MetadataBuilder::AddPartition(std::string_view name, std::string_view group_name,\n                                         uint32_t attributes) {\n    if (name.empty()) {\n        LERROR << \"Partition must have a non-empty name.\";\n        return nullptr;\n    }\n    if (FindPartition(name)) {\n        LERROR << \"Attempting to create duplication partition with name: \" << name;\n        return nullptr;\n    }\n    if (!FindGroup(group_name)) {\n        LERROR << \"Could not find partition group: \" << group_name;\n        return nullptr;\n    }\n    partitions_.push_back(std::make_unique<Partition>(name, group_name, attributes));\n    return partitions_.back().get();\n}\n\nPartition* MetadataBuilder::FindPartition(std::string_view name) const {\n    for (const auto& partition : partitions_) {\n        if (partition->name() == name) {\n            return partition.get();\n        }\n    }\n    return nullptr;\n}\n\nPartitionGroup* MetadataBuilder::FindGroup(std::string_view group_name) const {\n    for (const auto& group : groups_) {\n        if (group->name() == group_name) {\n            return group.get();\n        }\n    }\n    return nullptr;\n}\n\nuint64_t MetadataBuilder::TotalSizeOfGroup(PartitionGroup* group) const {\n    uint64_t total = 0;\n    for (const auto& partition : partitions_) {\n        if (partition->group_name() != group->name()) {\n            continue;\n        }\n        total += partition->BytesOnDisk();\n    }\n    return total;\n}\n\nvoid MetadataBuilder::RemovePartition(std::string_view name) {\n    for (auto iter = partitions_.begin(); iter != partitions_.end(); iter++) {\n        if ((*iter)->name() == name) {\n            partitions_.erase(iter);\n            return;\n        }\n    }\n}\n\nvoid MetadataBuilder::ExtentsToFreeList(const std::vector<Interval>& extents,\n                                        std::vector<Interval>* free_regions) const {\n    // Convert the extent list into a list of gaps between the extents; i.e.,\n    // the list of ranges that are free on the disk.\n    for (size_t i = 1; i < extents.size(); i++) {\n        const Interval& previous = extents[i - 1];\n        const Interval& current = extents[i];\n        DCHECK(previous.device_index == current.device_index);\n\n        uint64_t aligned;\n        if (!AlignSector(block_devices_[current.device_index], previous.end, &aligned)) {\n            LERROR << \"Sector \" << previous.end << \" caused integer overflow.\";\n            continue;\n        }\n        if (aligned >= current.start) {\n            // There is no gap between these two extents, try the next one.\n            // Note that we check with >= instead of >, since alignment may\n            // bump the ending sector past the beginning of the next extent.\n            continue;\n        }\n\n        // The new interval represents the free space starting at the end of\n        // the previous interval, and ending at the start of the next interval.\n        free_regions->emplace_back(current.device_index, aligned, current.start);\n    }\n}\n\nauto MetadataBuilder::GetFreeRegions() const -> std::vector<Interval> {\n    std::vector<Interval> free_regions;\n\n    // Collect all extents in the partition table, per-device, then sort them\n    // by starting sector.\n    std::vector<std::vector<Interval>> device_extents(block_devices_.size());\n    for (const auto& partition : partitions_) {\n        for (const auto& extent : partition->extents()) {\n            LinearExtent* linear = extent->AsLinearExtent();\n            if (!linear) {\n                continue;\n            }\n            CHECK(linear->device_index() < device_extents.size());\n            auto& extents = device_extents[linear->device_index()];\n            extents.emplace_back(linear->device_index(), linear->physical_sector(),\n                                 linear->physical_sector() + extent->num_sectors());\n        }\n    }\n\n    // Add 0-length intervals for the first and last sectors. This will cause\n    // ExtentToFreeList() to treat the space in between as available.\n    for (size_t i = 0; i < device_extents.size(); i++) {\n        auto& extents = device_extents[i];\n        const auto& block_device = block_devices_[i];\n\n        uint64_t first_sector = block_device.first_logical_sector;\n        uint64_t last_sector = block_device.size / LP_SECTOR_SIZE;\n        extents.emplace_back(i, first_sector, first_sector);\n        extents.emplace_back(i, last_sector, last_sector);\n\n        std::sort(extents.begin(), extents.end());\n        ExtentsToFreeList(extents, &free_regions);\n    }\n    return free_regions;\n}\n\nbool MetadataBuilder::ValidatePartitionSizeChange(Partition* partition, uint64_t old_size,\n                                                  uint64_t new_size, bool force_check) {\n    PartitionGroup* group = FindGroup(partition->group_name());\n    CHECK(group);\n\n    if (!force_check && new_size <= old_size) {\n        return true;\n    }\n\n    // Figure out how much we need to allocate, and whether our group has\n    // enough space remaining.\n    uint64_t space_needed = new_size - old_size;\n    if (group->maximum_size() > 0) {\n        uint64_t group_size = TotalSizeOfGroup(group);\n        if (group_size >= group->maximum_size() ||\n            group->maximum_size() - group_size < space_needed) {\n            LERROR << \"Partition \" << partition->name() << \" is part of group \" << group->name()\n                   << \" which does not have enough space free (\" << space_needed << \" requested, \"\n                   << group_size << \" used out of \" << group->maximum_size() << \")\";\n            return false;\n        }\n    }\n    return true;\n}\n\nInterval Interval::Intersect(const Interval& a, const Interval& b) {\n    Interval ret = a;\n    if (a.device_index != b.device_index) {\n        ret.start = ret.end = a.start;  // set length to 0 to indicate no intersection.\n        return ret;\n    }\n    ret.start = std::max(a.start, b.start);\n    ret.end = std::max(ret.start, std::min(a.end, b.end));\n    return ret;\n}\n\nstd::vector<Interval> Interval::Intersect(const std::vector<Interval>& a,\n                                          const std::vector<Interval>& b) {\n    std::vector<Interval> ret;\n    for (const Interval& a_interval : a) {\n        for (const Interval& b_interval : b) {\n            auto intersect = Intersect(a_interval, b_interval);\n            if (intersect.length() > 0) ret.emplace_back(std::move(intersect));\n        }\n    }\n    return ret;\n}\n\nstd::unique_ptr<Extent> Interval::AsExtent() const {\n    return std::make_unique<LinearExtent>(length(), device_index, start);\n}\n\nbool MetadataBuilder::GrowPartition(Partition* partition, uint64_t aligned_size,\n                                    const std::vector<Interval>& free_region_hint) {\n    uint64_t space_needed = aligned_size - partition->size();\n    uint64_t sectors_needed = space_needed / LP_SECTOR_SIZE;\n    DCHECK(sectors_needed * LP_SECTOR_SIZE == space_needed);\n\n    std::vector<Interval> free_regions = GetFreeRegions();\n    if (!free_region_hint.empty())\n        free_regions = Interval::Intersect(free_regions, free_region_hint);\n\n    const uint64_t sectors_per_block = geometry_.logical_block_size / LP_SECTOR_SIZE;\n    CHECK_NE(sectors_per_block, 0);\n    CHECK(sectors_needed % sectors_per_block == 0);\n\n    if (IsABDevice() && ShouldHalveSuper() && GetPartitionSlotSuffix(partition->name()) == \"_b\") {\n        // Allocate \"a\" partitions top-down and \"b\" partitions bottom-up, to\n        // minimize fragmentation during OTA.\n        free_regions = PrioritizeSecondHalfOfSuper(free_regions);\n    }\n\n    // Note we store new extents in a temporary vector, and only commit them\n    // if we are guaranteed enough free space.\n    std::vector<std::unique_ptr<LinearExtent>> new_extents;\n\n    // If the last extent in the partition has a size < alignment, then the\n    // difference is unallocatable due to being misaligned. We peek for that\n    // case here to avoid wasting space.\n    if (auto extent = ExtendFinalExtent(partition, free_regions, sectors_needed)) {\n        sectors_needed -= extent->num_sectors();\n        new_extents.emplace_back(std::move(extent));\n    }\n\n    for (auto& region : free_regions) {\n        // Note: this comes first, since we may enter the loop not needing any\n        // more sectors.\n        if (!sectors_needed) {\n            break;\n        }\n\n        if (region.length() % sectors_per_block != 0) {\n            // This should never happen, because it would imply that we\n            // once allocated an extent that was not a multiple of the\n            // block size. That extent would be rejected by DM_TABLE_LOAD.\n            LERROR << \"Region \" << region.start << \"..\" << region.end\n                   << \" is not a multiple of the block size, \" << sectors_per_block;\n\n            // If for some reason the final region is mis-sized we still want\n            // to be able to grow partitions. So just to be safe, round the\n            // region down to the nearest block.\n            region.end = region.start + (region.length() / sectors_per_block) * sectors_per_block;\n            if (!region.length()) {\n                continue;\n            }\n        }\n\n        uint64_t sectors = std::min(sectors_needed, region.length());\n        CHECK(sectors % sectors_per_block == 0);\n\n        auto extent = std::make_unique<LinearExtent>(sectors, region.device_index, region.start);\n        new_extents.push_back(std::move(extent));\n        sectors_needed -= sectors;\n    }\n    if (sectors_needed) {\n        LERROR << \"Not enough free space to expand partition: \" << partition->name();\n        return false;\n    }\n\n    // Everything succeeded, so commit the new extents.\n    for (auto& extent : new_extents) {\n        partition->AddExtent(std::move(extent));\n    }\n    return true;\n}\n\nstd::vector<Interval> MetadataBuilder::PrioritizeSecondHalfOfSuper(\n        const std::vector<Interval>& free_list) {\n    const auto& super = block_devices_[0];\n    uint64_t first_sector = super.first_logical_sector;\n    uint64_t last_sector = super.size / LP_SECTOR_SIZE;\n    uint64_t midpoint = first_sector + (last_sector - first_sector) / 2;\n\n    // Choose an aligned sector for the midpoint. This could lead to one half\n    // being slightly larger than the other, but this will not restrict the\n    // size of partitions (it might lead to one extra extent if \"B\" overflows).\n    if (!AlignSector(super, midpoint, &midpoint)) {\n        LERROR << \"Unexpected integer overflow aligning midpoint \" << midpoint;\n        return free_list;\n    }\n\n    std::vector<Interval> first_half;\n    std::vector<Interval> second_half;\n    for (const auto& region : free_list) {\n        // Note: deprioritze if not the main super partition. Even though we\n        // don't call this for retrofit devices, we will allow adding additional\n        // block devices on non-retrofit devices.\n        if (region.device_index != 0 || region.end <= midpoint) {\n            first_half.emplace_back(region);\n            continue;\n        }\n        if (region.start < midpoint && region.end > midpoint) {\n            // Split this into two regions.\n            first_half.emplace_back(region.device_index, region.start, midpoint);\n            second_half.emplace_back(region.device_index, midpoint, region.end);\n        } else {\n            second_half.emplace_back(region);\n        }\n    }\n    second_half.insert(second_half.end(), first_half.begin(), first_half.end());\n    return second_half;\n}\n\nstd::unique_ptr<LinearExtent> MetadataBuilder::ExtendFinalExtent(\n        Partition* partition, const std::vector<Interval>& free_list,\n        uint64_t sectors_needed) const {\n    if (partition->extents().empty()) {\n        return nullptr;\n    }\n    LinearExtent* extent = partition->extents().back()->AsLinearExtent();\n    if (!extent) {\n        return nullptr;\n    }\n\n    // If the sector ends where the next aligned chunk begins, then there's\n    // no missing gap to try and allocate.\n    const auto& block_device = block_devices_[extent->device_index()];\n    uint64_t next_aligned_sector;\n    if (!AlignSector(block_device, extent->end_sector(), &next_aligned_sector)) {\n        LERROR << \"Integer overflow aligning sector \" << extent->end_sector();\n        return nullptr;\n    }\n    if (extent->end_sector() == next_aligned_sector) {\n        return nullptr;\n    }\n\n    uint64_t num_sectors = std::min(next_aligned_sector - extent->end_sector(), sectors_needed);\n    auto new_extent = std::make_unique<LinearExtent>(num_sectors, extent->device_index(),\n                                                     extent->end_sector());\n    if (IsAnyRegionAllocated(*new_extent.get()) ||\n        IsAnyRegionCovered(free_list, *new_extent.get())) {\n        LERROR << \"Misaligned region \" << new_extent->physical_sector() << \"..\"\n               << new_extent->end_sector() << \" was allocated or marked allocatable.\";\n        return nullptr;\n    }\n    return new_extent;\n}\n\nbool MetadataBuilder::IsAnyRegionCovered(const std::vector<Interval>& regions,\n                                         const LinearExtent& candidate) const {\n    for (const auto& region : regions) {\n        if (candidate.OverlapsWith(region)) {\n            return true;\n        }\n    }\n    return false;\n}\n\nbool MetadataBuilder::IsAnyRegionAllocated(const LinearExtent& candidate) const {\n    for (const auto& partition : partitions_) {\n        for (const auto& extent : partition->extents()) {\n            LinearExtent* linear = extent->AsLinearExtent();\n            if (!linear) {\n                continue;\n            }\n            if (linear->OverlapsWith(candidate)) {\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\nvoid MetadataBuilder::ShrinkPartition(Partition* partition, uint64_t aligned_size) {\n    partition->ShrinkTo(aligned_size);\n}\n\nstd::unique_ptr<LpMetadata> MetadataBuilder::Export() {\n    if (!ValidatePartitionGroups()) {\n        return nullptr;\n    }\n\n    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();\n    metadata->header = header_;\n    metadata->geometry = geometry_;\n\n    // Assign this early so the extent table can read it.\n    for (const auto& block_device : block_devices_) {\n        metadata->block_devices.emplace_back(block_device);\n        if (auto_slot_suffixing_) {\n            metadata->block_devices.back().flags |= LP_BLOCK_DEVICE_SLOT_SUFFIXED;\n        }\n    }\n\n    std::map<std::string, size_t> group_indices;\n    for (const auto& group : groups_) {\n        LpMetadataPartitionGroup out = {};\n\n        if (group->name().size() > sizeof(out.name)) {\n            LERROR << \"Partition group name is too long: \" << group->name();\n            return nullptr;\n        }\n        if (auto_slot_suffixing_ && group->name() != kDefaultGroup) {\n            out.flags |= LP_GROUP_SLOT_SUFFIXED;\n        }\n        strncpy(out.name, group->name().c_str(), sizeof(out.name));\n        out.maximum_size = group->maximum_size();\n\n        group_indices[group->name()] = metadata->groups.size();\n        metadata->groups.push_back(out);\n    }\n\n    // Flatten the partition and extent structures into an LpMetadata, which\n    // makes it very easy to validate, serialize, or pass on to device-mapper.\n    for (const auto& partition : partitions_) {\n        LpMetadataPartition part;\n        memset(&part, 0, sizeof(part));\n\n        if (partition->name().size() > sizeof(part.name)) {\n            LERROR << \"Partition name is too long: \" << partition->name();\n            return nullptr;\n        }\n        if (partition->attributes() & ~(LP_PARTITION_ATTRIBUTE_MASK)) {\n            LERROR << \"Partition \" << partition->name() << \" has unsupported attribute.\";\n            return nullptr;\n        }\n\n        if (partition->attributes() & LP_PARTITION_ATTRIBUTE_MASK_V1) {\n            static const uint16_t kMinVersion = LP_METADATA_VERSION_FOR_UPDATED_ATTR;\n            metadata->header.minor_version = std::max(metadata->header.minor_version, kMinVersion);\n        }\n\n        strncpy(part.name, partition->name().c_str(), sizeof(part.name));\n        part.first_extent_index = static_cast<uint32_t>(metadata->extents.size());\n        part.num_extents = static_cast<uint32_t>(partition->extents().size());\n        part.attributes = partition->attributes();\n        if (auto_slot_suffixing_) {\n            part.attributes |= LP_PARTITION_ATTR_SLOT_SUFFIXED;\n        }\n\n        auto iter = group_indices.find(partition->group_name());\n        if (iter == group_indices.end()) {\n            LERROR << \"Partition \" << partition->name() << \" is a member of unknown group \"\n                   << partition->group_name();\n            return nullptr;\n        }\n        part.group_index = iter->second;\n\n        for (const auto& extent : partition->extents()) {\n            if (!extent->AddTo(metadata.get())) {\n                return nullptr;\n            }\n        }\n        metadata->partitions.push_back(part);\n    }\n\n    metadata->header.partitions.num_entries = static_cast<uint32_t>(metadata->partitions.size());\n    metadata->header.extents.num_entries = static_cast<uint32_t>(metadata->extents.size());\n    metadata->header.groups.num_entries = static_cast<uint32_t>(metadata->groups.size());\n    metadata->header.block_devices.num_entries =\n            static_cast<uint32_t>(metadata->block_devices.size());\n    return metadata;\n}\n\nvoid MetadataBuilder::RequireExpandedMetadataHeader() {\n    if (header_.minor_version >= LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {\n        return;\n    }\n    header_.minor_version = LP_METADATA_VERSION_FOR_EXPANDED_HEADER;\n    header_.header_size = sizeof(LpMetadataHeaderV1_2);\n}\n\nuint64_t MetadataBuilder::AllocatableSpace() const {\n    uint64_t total_size = 0;\n    for (const auto& block_device : block_devices_) {\n        total_size += block_device.size - (block_device.first_logical_sector * LP_SECTOR_SIZE);\n    }\n    return total_size;\n}\n\nuint64_t MetadataBuilder::UsedSpace() const {\n    uint64_t size = 0;\n    for (const auto& partition : partitions_) {\n        size += partition->size();\n    }\n    return size;\n}\n\nbool MetadataBuilder::AlignSector(const LpMetadataBlockDevice& block_device, uint64_t sector,\n                                  uint64_t* out) const {\n    // Note: when reading alignment info from the Kernel, we don't assume it\n    // is aligned to the sector size, so we round up to the nearest sector.\n    uint64_t lba = sector * LP_SECTOR_SIZE;\n    if (!AlignTo(lba, block_device.alignment, out)) {\n        return false;\n    }\n    if (!AlignTo(*out, LP_SECTOR_SIZE, out)) {\n        return false;\n    }\n    *out /= LP_SECTOR_SIZE;\n    return true;\n}\n\nbool MetadataBuilder::FindBlockDeviceByName(const std::string& partition_name,\n                                            uint32_t* index) const {\n    for (size_t i = 0; i < block_devices_.size(); i++) {\n        if (GetBlockDevicePartitionName(i) == partition_name) {\n            *index = i;\n            return true;\n        }\n    }\n    return false;\n}\n\nbool MetadataBuilder::HasBlockDevice(const std::string& partition_name) const {\n    uint32_t index;\n    return FindBlockDeviceByName(partition_name, &index);\n}\n\nbool MetadataBuilder::GetBlockDeviceInfo(const std::string& partition_name,\n                                         BlockDeviceInfo* info) const {\n    uint32_t index;\n    if (!FindBlockDeviceByName(partition_name, &index)) {\n        LERROR << \"No device named \" << partition_name;\n        return false;\n    }\n    info->size = block_devices_[index].size;\n    info->alignment = block_devices_[index].alignment;\n    info->alignment_offset = block_devices_[index].alignment_offset;\n    info->logical_block_size = geometry_.logical_block_size;\n    info->partition_name = partition_name;\n    return true;\n}\n\nbool MetadataBuilder::UpdateBlockDeviceInfo(const std::string& partition_name,\n                                            const BlockDeviceInfo& device_info) {\n    uint32_t index;\n    if (!FindBlockDeviceByName(partition_name, &index)) {\n        LERROR << \"No device named \" << partition_name;\n        return false;\n    }\n    return UpdateBlockDeviceInfo(index, device_info);\n}\n\nbool MetadataBuilder::UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& device_info) {\n    CHECK(index < block_devices_.size());\n\n    LpMetadataBlockDevice& block_device = block_devices_[index];\n    if (device_info.size != block_device.size) {\n        LERROR << \"Device size does not match (got \" << device_info.size << \", expected \"\n               << block_device.size << \")\";\n        return false;\n    }\n    if (geometry_.logical_block_size % device_info.logical_block_size) {\n        LERROR << \"Device logical block size is misaligned (block size=\"\n               << device_info.logical_block_size << \", alignment=\" << geometry_.logical_block_size\n               << \")\";\n        return false;\n    }\n\n    // The kernel does not guarantee these values are present, so we only\n    // replace existing values if the new values are non-zero.\n    if (device_info.alignment) {\n        block_device.alignment = device_info.alignment;\n    }\n    if (device_info.alignment_offset) {\n        block_device.alignment_offset = device_info.alignment_offset;\n    }\n    return true;\n}\n\nbool MetadataBuilder::ResizePartition(Partition* partition, uint64_t requested_size,\n                                      const std::vector<Interval>& free_region_hint) {\n    // Align the space needed up to the nearest sector.\n    uint64_t aligned_size;\n    if (!AlignTo(requested_size, geometry_.logical_block_size, &aligned_size)) {\n        LERROR << \"Cannot resize partition \" << partition->name() << \" to \" << requested_size\n               << \" bytes; integer overflow.\";\n        return false;\n    }\n    uint64_t old_size = partition->size();\n\n    if (!ValidatePartitionSizeChange(partition, old_size, aligned_size, false)) {\n        return false;\n    }\n\n    if (aligned_size > old_size) {\n        if (!GrowPartition(partition, aligned_size, free_region_hint)) {\n            return false;\n        }\n    } else if (aligned_size < partition->size()) {\n        ShrinkPartition(partition, aligned_size);\n    }\n\n    if (partition->size() != old_size) {\n        LINFO << \"Partition \" << partition->name() << \" will resize from \" << old_size\n              << \" bytes to \" << aligned_size << \" bytes\";\n    }\n    return true;\n}\n\nstd::vector<std::string> MetadataBuilder::ListGroups() const {\n    std::vector<std::string> names;\n    for (const auto& group : groups_) {\n        names.emplace_back(group->name());\n    }\n    return names;\n}\n\nvoid MetadataBuilder::RemoveGroupAndPartitions(std::string_view group_name) {\n    if (group_name == kDefaultGroup) {\n        // Cannot remove the default group.\n        return;\n    }\n    std::vector<std::string> partition_names;\n    for (const auto& partition : partitions_) {\n        if (partition->group_name() == group_name) {\n            partition_names.emplace_back(partition->name());\n        }\n    }\n\n    for (const auto& partition_name : partition_names) {\n        RemovePartition(partition_name);\n    }\n    for (auto iter = groups_.begin(); iter != groups_.end(); iter++) {\n        if ((*iter)->name() == group_name) {\n            groups_.erase(iter);\n            break;\n        }\n    }\n}\n\nstatic bool CompareBlockDevices(const LpMetadataBlockDevice& first,\n                                const LpMetadataBlockDevice& second) {\n    // Note: we don't compare alignment, since it's a performance thing and\n    // won't affect whether old extents continue to work.\n    return first.first_logical_sector == second.first_logical_sector && first.size == second.size &&\n           android::fs_mgr::GetBlockDevicePartitionName(first) ==\n                   android::fs_mgr::GetBlockDevicePartitionName(second);\n}\n\nbool MetadataBuilder::ImportPartitions(const LpMetadata& metadata,\n                                       const std::set<std::string>& partition_names) {\n    // The block device list must be identical. We do not try to be clever and\n    // allow ordering changes or changes that don't affect partitions. This\n    // process is designed to allow the most common flashing scenarios and more\n    // complex ones should require a wipe.\n    if (metadata.block_devices.size() != block_devices_.size()) {\n        LINFO << \"Block device tables does not match.\";\n        return false;\n    }\n    for (size_t i = 0; i < metadata.block_devices.size(); i++) {\n        const LpMetadataBlockDevice& old_device = metadata.block_devices[i];\n        const LpMetadataBlockDevice& new_device = block_devices_[i];\n        if (!CompareBlockDevices(old_device, new_device)) {\n            LINFO << \"Block device tables do not match\";\n            return false;\n        }\n    }\n\n    // Import named partitions. Note that we do not attempt to merge group\n    // information here. If the device changed its group names, the old\n    // partitions will fail to merge. The same could happen if the group\n    // allocation sizes change.\n    for (const auto& partition : metadata.partitions) {\n        std::string partition_name = GetPartitionName(partition);\n        if (partition_names.find(partition_name) == partition_names.end()) {\n            continue;\n        }\n        if (!ImportPartition(metadata, partition)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool MetadataBuilder::ImportPartition(const LpMetadata& metadata,\n                                      const LpMetadataPartition& source) {\n    std::string partition_name = GetPartitionName(source);\n    Partition* partition = FindPartition(partition_name);\n    if (!partition) {\n        std::string group_name = GetPartitionGroupName(metadata.groups[source.group_index]);\n        partition = AddPartition(partition_name, group_name, source.attributes);\n        if (!partition) {\n            return false;\n        }\n    }\n    if (partition->size() > 0) {\n        LINFO << \"Importing partition table would overwrite non-empty partition: \"\n              << partition_name;\n        return false;\n    }\n\n    ImportExtents(partition, metadata, source);\n\n    // Note: we've already increased the partition size by calling\n    // ImportExtents(). In order to figure out the size before that,\n    // we would have to iterate the extents and add up the linear\n    // segments. Instead, we just force ValidatePartitionSizeChange\n    // to check if the current configuration is acceptable.\n    if (!ValidatePartitionSizeChange(partition, partition->size(), partition->size(), true)) {\n        partition->RemoveExtents();\n        return false;\n    }\n    return true;\n}\n\nvoid MetadataBuilder::SetAutoSlotSuffixing() {\n    auto_slot_suffixing_ = true;\n}\n\nvoid MetadataBuilder::SetVirtualABDeviceFlag() {\n    RequireExpandedMetadataHeader();\n    header_.flags |= LP_HEADER_FLAG_VIRTUAL_AB_DEVICE;\n}\n\nvoid MetadataBuilder::SetOverlaysActiveFlag(bool flag) {\n    RequireExpandedMetadataHeader();\n    if (flag) {\n        header_.flags |= LP_HEADER_FLAG_OVERLAYS_ACTIVE;\n    } else {\n        header_.flags &= ~LP_HEADER_FLAG_OVERLAYS_ACTIVE;\n    }\n}\n\nbool MetadataBuilder::IsABDevice() {\n    return !IPropertyFetcher::GetInstance()->GetProperty(\"ro.boot.slot_suffix\", \"\").empty();\n}\n\nbool MetadataBuilder::IsRetrofitDynamicPartitionsDevice() {\n    return IPropertyFetcher::GetInstance()->GetBoolProperty(\"ro.boot.dynamic_partitions_retrofit\",\n                                                            false);\n}\n\nbool MetadataBuilder::ShouldHalveSuper() const {\n    return GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&\n           !IPropertyFetcher::GetInstance()->GetBoolProperty(\"ro.virtual_ab.enabled\", false);\n}\n\nbool MetadataBuilder::AddLinearExtent(Partition* partition, const std::string& block_device,\n                                      uint64_t num_sectors, uint64_t physical_sector) {\n    uint32_t device_index;\n    if (!FindBlockDeviceByName(block_device, &device_index)) {\n        LERROR << \"Could not find backing block device for extent: \" << block_device;\n        return false;\n    }\n\n    auto extent = std::make_unique<LinearExtent>(num_sectors, device_index, physical_sector);\n    partition->AddExtent(std::move(extent));\n    return true;\n}\n\nstd::vector<Partition*> MetadataBuilder::ListPartitionsInGroup(std::string_view group_name) {\n    std::vector<Partition*> partitions;\n    for (const auto& partition : partitions_) {\n        if (partition->group_name() == group_name) {\n            partitions.emplace_back(partition.get());\n        }\n    }\n    return partitions;\n}\n\nbool MetadataBuilder::ChangePartitionGroup(Partition* partition, std::string_view group_name) {\n    if (!FindGroup(group_name)) {\n        LERROR << \"Partition cannot change to unknown group: \" << group_name;\n        return false;\n    }\n    partition->set_group_name(group_name);\n    return true;\n}\n\nbool MetadataBuilder::ValidatePartitionGroups() const {\n    for (const auto& group : groups_) {\n        if (!group->maximum_size()) {\n            continue;\n        }\n        uint64_t used = TotalSizeOfGroup(group.get());\n        if (used > group->maximum_size()) {\n            LERROR << \"Partition group \" << group->name() << \" exceeds maximum size (\" << used\n                   << \" bytes used, maximum \" << group->maximum_size() << \")\";\n            return false;\n        }\n    }\n    return true;\n}\n\nbool MetadataBuilder::ChangeGroupSize(const std::string& group_name, uint64_t maximum_size) {\n    if (group_name == kDefaultGroup) {\n        LERROR << \"Cannot change the size of the default group\";\n        return false;\n    }\n    PartitionGroup* group = FindGroup(group_name);\n    if (!group) {\n        LERROR << \"Cannot change size of unknown partition group: \" << group_name;\n        return false;\n    }\n    group->set_maximum_size(maximum_size);\n    return true;\n}\n\nstd::string MetadataBuilder::GetBlockDevicePartitionName(uint64_t index) const {\n    return index < block_devices_.size()\n                   ? android::fs_mgr::GetBlockDevicePartitionName(block_devices_[index])\n                   : \"\";\n}\n\nuint64_t MetadataBuilder::logical_block_size() const {\n    return geometry_.logical_block_size;\n}\n\nbool MetadataBuilder::VerifyExtentsAgainstSourceMetadata(\n        const MetadataBuilder& source_metadata, uint32_t source_slot_number,\n        const MetadataBuilder& target_metadata, uint32_t target_slot_number,\n        const std::vector<std::string>& partitions) {\n    for (const auto& base_name : partitions) {\n        // Find the partition in metadata with the slot suffix.\n        auto target_partition_name = base_name + SlotSuffixForSlotNumber(target_slot_number);\n        const auto target_partition = target_metadata.FindPartition(target_partition_name);\n        if (!target_partition) {\n            LERROR << \"Failed to find partition \" << target_partition_name << \" in metadata slot \"\n                   << target_slot_number;\n            return false;\n        }\n\n        auto source_partition_name = base_name + SlotSuffixForSlotNumber(source_slot_number);\n        const auto source_partition = source_metadata.FindPartition(source_partition_name);\n        if (!source_partition) {\n            LERROR << \"Failed to find partition \" << source_partition << \" in metadata slot \"\n                   << source_slot_number;\n            return false;\n        }\n\n        // We expect the partitions in the target metadata to have the identical extents as the\n        // one in the source metadata. Because they are copied in NewForUpdate.\n        if (target_partition->extents().size() != source_partition->extents().size()) {\n            LERROR << \"Extents count mismatch for partition \" << base_name << \" target slot has \"\n                   << target_partition->extents().size() << \", source slot has \"\n                   << source_partition->extents().size();\n            return false;\n        }\n\n        for (size_t i = 0; i < target_partition->extents().size(); i++) {\n            const auto& src_extent = *source_partition->extents()[i];\n            const auto& tgt_extent = *target_partition->extents()[i];\n            if (tgt_extent != src_extent) {\n                LERROR << \"Extents \" << i << \" is different for partition \" << base_name;\n                LERROR << \"tgt extent \" << tgt_extent << \"; src extent \" << src_extent;\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/builder_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/properties.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <liblp/builder.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"liblp_test.h\"\n#include \"utility.h\"\n\nusing namespace std;\nusing namespace android::storage_literals;\nusing namespace android::fs_mgr;\nusing namespace android::fs_mgr::testing;\nusing ::testing::_;\nusing ::testing::AnyNumber;\nusing ::testing::ElementsAre;\nusing ::testing::NiceMock;\nusing ::testing::Return;\nusing android::base::GetProperty;\n\nclass Environment : public ::testing::Environment {\n  public:\n    void SetUp() override { ResetMockPropertyFetcher(); }\n};\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n\nclass BuilderTest : public LiblpTest {};\n\nTEST_F(BuilderTest, BuildBasic) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* partition = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n    EXPECT_EQ(partition->name(), \"system\");\n    EXPECT_EQ(partition->attributes(), LP_PARTITION_ATTR_READONLY);\n    EXPECT_EQ(partition->size(), 0);\n    EXPECT_EQ(builder->FindPartition(\"system\"), partition);\n\n    builder->RemovePartition(\"system\");\n    EXPECT_EQ(builder->FindPartition(\"system\"), nullptr);\n}\n\nTEST_F(BuilderTest, ResizePartition) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, 65536), true);\n    EXPECT_EQ(system->size(), 65536);\n    ASSERT_EQ(system->extents().size(), 1);\n\n    LinearExtent* extent = system->extents()[0]->AsLinearExtent();\n    ASSERT_NE(extent, nullptr);\n    EXPECT_EQ(extent->num_sectors(), 65536 / LP_SECTOR_SIZE);\n    // The first logical sector will be:\n    //      (LP_PARTITION_RESERVED_BYTES + 4096*2 + 1024*4) / 512\n    // Or, in terms of sectors (reserved + geometry + metadata):\n    //      (8 + 16 + 8) = 32\n    EXPECT_EQ(extent->physical_sector(), 32);\n\n    // Test resizing to the same size.\n    EXPECT_EQ(builder->ResizePartition(system, 65536), true);\n    EXPECT_EQ(system->size(), 65536);\n    EXPECT_EQ(system->extents().size(), 1);\n    EXPECT_EQ(system->extents()[0]->num_sectors(), 65536 / LP_SECTOR_SIZE);\n    // Test resizing to a smaller size.\n    EXPECT_EQ(builder->ResizePartition(system, 0), true);\n    EXPECT_EQ(system->size(), 0);\n    EXPECT_EQ(system->extents().size(), 0);\n    // Test resizing to a greater size.\n    builder->ResizePartition(system, 131072);\n    EXPECT_EQ(system->size(), 131072);\n    EXPECT_EQ(system->extents().size(), 1);\n    EXPECT_EQ(system->extents()[0]->num_sectors(), 131072 / LP_SECTOR_SIZE);\n    // Test resizing again, that the extents are merged together.\n    builder->ResizePartition(system, 1024 * 256);\n    EXPECT_EQ(system->size(), 1024 * 256);\n    EXPECT_EQ(system->extents().size(), 1);\n    EXPECT_EQ(system->extents()[0]->num_sectors(), (1024 * 256) / LP_SECTOR_SIZE);\n\n    // Test shrinking within the same extent.\n    builder->ResizePartition(system, 32768);\n    EXPECT_EQ(system->size(), 32768);\n    EXPECT_EQ(system->extents().size(), 1);\n    extent = system->extents()[0]->AsLinearExtent();\n    ASSERT_NE(extent, nullptr);\n    EXPECT_EQ(extent->num_sectors(), 32768 / LP_SECTOR_SIZE);\n    EXPECT_EQ(extent->physical_sector(), 32);\n\n    auto exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    ASSERT_EQ(FindPartition(*exported.get(), \"not found\"), nullptr);\n    auto entry = FindPartition(*exported.get(), \"system\");\n    ASSERT_NE(entry, nullptr);\n    ASSERT_EQ(GetPartitionSize(*exported.get(), *entry), 32768);\n\n    // Test shrinking to 0.\n    builder->ResizePartition(system, 0);\n    EXPECT_EQ(system->size(), 0);\n    EXPECT_EQ(system->extents().size(), 0);\n}\n\nTEST_F(BuilderTest, PartitionAlignment) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    // Test that we align up to one sector.\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, 10000), true);\n    EXPECT_EQ(system->size(), 12288);\n    EXPECT_EQ(system->extents().size(), 1);\n\n    builder->ResizePartition(system, 7000);\n    EXPECT_EQ(system->size(), 8192);\n    EXPECT_EQ(system->extents().size(), 1);\n}\n\nTEST_F(BuilderTest, DiskAlignment) {\n    static const uint64_t kDiskSize = 1000000;\n    static const uint32_t kMetadataSize = 1024;\n    static const uint32_t kMetadataSlots = 2;\n\n    unique_ptr<MetadataBuilder> builder =\n            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);\n    ASSERT_EQ(builder, nullptr);\n}\n\nTEST_F(BuilderTest, MetadataAlignment) {\n    // Make sure metadata sizes get aligned up.\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1000, 2);\n    ASSERT_NE(builder, nullptr);\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    EXPECT_EQ(exported->geometry.metadata_max_size, 1024);\n}\n\nTEST_F(BuilderTest, InternalAlignment) {\n    // Test the metadata fitting within alignment.\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 768 * 1024, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    auto super_device = GetMetadataSuperBlockDevice(*exported.get());\n    ASSERT_NE(super_device, nullptr);\n    EXPECT_EQ(super_device->first_logical_sector, 1536);\n\n    // Test a large alignment offset thrown in.\n    device_info.alignment_offset = 753664;\n    builder = MetadataBuilder::New(device_info, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n    exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    super_device = GetMetadataSuperBlockDevice(*exported.get());\n    ASSERT_NE(super_device, nullptr);\n    EXPECT_EQ(super_device->first_logical_sector, 1536);\n\n    // Alignment offset without alignment is ignored.\n    device_info.alignment = 0;\n    builder = MetadataBuilder::New(device_info, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    // Test a small alignment with an alignment offset.\n    device_info.alignment = 12 * 1024;\n    device_info.alignment_offset = 3 * 1024;\n    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);\n    ASSERT_NE(builder, nullptr);\n    exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    super_device = GetMetadataSuperBlockDevice(*exported.get());\n    ASSERT_NE(super_device, nullptr);\n    EXPECT_EQ(super_device->first_logical_sector, 168);\n\n    // Test a small alignment with no alignment offset.\n    device_info.alignment = 11 * 1024;\n    builder = MetadataBuilder::New(device_info, 16 * 1024, 2);\n    ASSERT_NE(builder, nullptr);\n    exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    super_device = GetMetadataSuperBlockDevice(*exported.get());\n    ASSERT_NE(super_device, nullptr);\n    EXPECT_EQ(super_device->first_logical_sector, 154);\n}\n\nTEST_F(BuilderTest, InternalPartitionAlignment) {\n    BlockDeviceInfo device_info(\"super\", 512 * 1024 * 1024, 768 * 1024, 753664, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 32 * 1024, 2);\n\n    Partition* a = builder->AddPartition(\"a\", 0);\n    ASSERT_NE(a, nullptr);\n    Partition* b = builder->AddPartition(\"b\", 0);\n    ASSERT_NE(b, nullptr);\n\n    // Add a bunch of small extents to each, interleaving.\n    for (size_t i = 0; i < 10; i++) {\n        ASSERT_TRUE(builder->ResizePartition(a, a->size() + 4096));\n        ASSERT_TRUE(builder->ResizePartition(b, b->size() + 4096));\n    }\n    EXPECT_EQ(a->size(), 40960);\n    EXPECT_EQ(b->size(), 40960);\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    // Check that each starting sector is aligned.\n    for (const auto& extent : exported->extents) {\n        ASSERT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);\n        EXPECT_EQ(extent.num_sectors, 80);\n\n        uint64_t aligned_lba;\n        uint64_t lba = extent.target_data * LP_SECTOR_SIZE;\n        ASSERT_TRUE(AlignTo(lba, device_info.alignment, &aligned_lba));\n        EXPECT_EQ(lba, aligned_lba);\n    }\n\n    // Check one extent.\n    EXPECT_EQ(exported->extents.back().target_data, 3072);\n}\n\nTEST_F(BuilderTest, UseAllDiskSpace) {\n    static constexpr uint64_t total = 1024 * 1024;\n    static constexpr uint64_t metadata = 1024;\n    static constexpr uint64_t slots = 2;\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(total, metadata, slots);\n    // We reserve a geometry block (4KB) plus space for each copy of the\n    // maximum size of a metadata blob. Then, we double that space since\n    // we store a backup copy of everything.\n    static constexpr uint64_t geometry = 4 * 1024;\n    static constexpr uint64_t allocatable =\n            total - (metadata * slots + geometry) * 2 - LP_PARTITION_RESERVED_BYTES;\n    EXPECT_EQ(builder->AllocatableSpace(), allocatable);\n    EXPECT_EQ(builder->UsedSpace(), 0);\n\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, allocatable), true);\n    EXPECT_EQ(system->size(), allocatable);\n    EXPECT_EQ(builder->UsedSpace(), allocatable);\n    EXPECT_EQ(builder->AllocatableSpace(), allocatable);\n    EXPECT_EQ(builder->ResizePartition(system, allocatable + 1), false);\n    EXPECT_EQ(system->size(), allocatable);\n    EXPECT_EQ(builder->UsedSpace(), allocatable);\n    EXPECT_EQ(builder->AllocatableSpace(), allocatable);\n}\n\nTEST_F(BuilderTest, BuildComplex) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    Partition* vendor = builder->AddPartition(\"vendor\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    ASSERT_NE(vendor, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, 65536), true);\n    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);\n    EXPECT_EQ(builder->ResizePartition(system, 98304), true);\n    EXPECT_EQ(system->size(), 98304);\n    EXPECT_EQ(vendor->size(), 32768);\n\n    // We now expect to have 3 extents total: 2 for system, 1 for vendor, since\n    // our allocation strategy is greedy/first-fit.\n    ASSERT_EQ(system->extents().size(), 2);\n    ASSERT_EQ(vendor->extents().size(), 1);\n\n    LinearExtent* system1 = system->extents()[0]->AsLinearExtent();\n    LinearExtent* system2 = system->extents()[1]->AsLinearExtent();\n    LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();\n    ASSERT_NE(system1, nullptr);\n    ASSERT_NE(system2, nullptr);\n    ASSERT_NE(vendor1, nullptr);\n    EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);\n    EXPECT_EQ(system1->physical_sector(), 32);\n    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);\n    EXPECT_EQ(system2->physical_sector(), 224);\n    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);\n    EXPECT_EQ(vendor1->physical_sector(), 160);\n    EXPECT_EQ(system1->physical_sector() + system1->num_sectors(), vendor1->physical_sector());\n    EXPECT_EQ(vendor1->physical_sector() + vendor1->num_sectors(), system2->physical_sector());\n}\n\nTEST_F(BuilderTest, AddInvalidPartition) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n\n    Partition* partition = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n\n    // Duplicate name.\n    partition = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    EXPECT_EQ(partition, nullptr);\n\n    // Empty name.\n    partition = builder->AddPartition(\"\", LP_PARTITION_ATTR_READONLY);\n    EXPECT_EQ(partition, nullptr);\n}\n\nTEST_F(BuilderTest, BuilderExport) {\n    static const uint64_t kDiskSize = 1024 * 1024;\n    static const uint32_t kMetadataSize = 1024;\n    static const uint32_t kMetadataSlots = 2;\n    unique_ptr<MetadataBuilder> builder =\n            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);\n\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    Partition* vendor = builder->AddPartition(\"vendor\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    ASSERT_NE(vendor, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, 65536), true);\n    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);\n    EXPECT_EQ(builder->ResizePartition(system, 98304), true);\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    EXPECT_NE(exported, nullptr);\n\n    auto super_device = GetMetadataSuperBlockDevice(*exported.get());\n    ASSERT_NE(super_device, nullptr);\n\n    // Verify geometry. Some details of this may change if we change the\n    // metadata structures. So in addition to checking the exact values, we\n    // also check that they are internally consistent after.\n    const LpMetadataGeometry& geometry = exported->geometry;\n    EXPECT_EQ(geometry.magic, LP_METADATA_GEOMETRY_MAGIC);\n    EXPECT_EQ(geometry.struct_size, sizeof(geometry));\n    EXPECT_EQ(geometry.metadata_max_size, 1024);\n    EXPECT_EQ(geometry.metadata_slot_count, 2);\n    EXPECT_EQ(super_device->first_logical_sector, 32);\n\n    static const size_t kMetadataSpace =\n            ((kMetadataSize * kMetadataSlots) + LP_METADATA_GEOMETRY_SIZE) * 2;\n    EXPECT_GE(super_device->first_logical_sector * LP_SECTOR_SIZE, kMetadataSpace);\n\n    // Verify header.\n    const LpMetadataHeader& header = exported->header;\n    EXPECT_EQ(header.magic, LP_METADATA_HEADER_MAGIC);\n    EXPECT_EQ(header.major_version, LP_METADATA_MAJOR_VERSION);\n    EXPECT_EQ(header.minor_version, LP_METADATA_MINOR_VERSION_MIN);\n    EXPECT_EQ(header.header_size, sizeof(LpMetadataHeaderV1_0));\n\n    ASSERT_EQ(exported->partitions.size(), 2);\n    ASSERT_EQ(exported->extents.size(), 3);\n\n    for (const auto& partition : exported->partitions) {\n        Partition* original = builder->FindPartition(GetPartitionName(partition));\n        ASSERT_NE(original, nullptr);\n        for (size_t i = 0; i < partition.num_extents; i++) {\n            const auto& extent = exported->extents[partition.first_extent_index + i];\n            LinearExtent* original_extent = original->extents()[i]->AsLinearExtent();\n            EXPECT_EQ(extent.num_sectors, original_extent->num_sectors());\n            EXPECT_EQ(extent.target_type, LP_TARGET_TYPE_LINEAR);\n            EXPECT_EQ(extent.target_data, original_extent->physical_sector());\n        }\n        EXPECT_EQ(partition.attributes, original->attributes());\n    }\n}\n\nTEST_F(BuilderTest, BuilderImport) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    Partition* vendor = builder->AddPartition(\"vendor\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    ASSERT_NE(vendor, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, 65536), true);\n    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);\n    EXPECT_EQ(builder->ResizePartition(system, 98304), true);\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    builder = MetadataBuilder::New(*exported.get());\n    ASSERT_NE(builder, nullptr);\n    system = builder->FindPartition(\"system\");\n    ASSERT_NE(system, nullptr);\n    vendor = builder->FindPartition(\"vendor\");\n    ASSERT_NE(vendor, nullptr);\n\n    EXPECT_EQ(system->size(), 98304);\n    ASSERT_EQ(system->extents().size(), 2);\n    EXPECT_EQ(system->attributes(), LP_PARTITION_ATTR_READONLY);\n    EXPECT_EQ(vendor->size(), 32768);\n    ASSERT_EQ(vendor->extents().size(), 1);\n    EXPECT_EQ(vendor->attributes(), LP_PARTITION_ATTR_READONLY);\n\n    LinearExtent* system1 = system->extents()[0]->AsLinearExtent();\n    LinearExtent* system2 = system->extents()[1]->AsLinearExtent();\n    LinearExtent* vendor1 = vendor->extents()[0]->AsLinearExtent();\n    EXPECT_EQ(system1->num_sectors(), 65536 / LP_SECTOR_SIZE);\n    EXPECT_EQ(system1->physical_sector(), 32);\n    EXPECT_EQ(system2->num_sectors(), 32768 / LP_SECTOR_SIZE);\n    EXPECT_EQ(system2->physical_sector(), 224);\n    EXPECT_EQ(vendor1->num_sectors(), 32768 / LP_SECTOR_SIZE);\n}\n\nTEST_F(BuilderTest, ExportNameTooLong) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n\n    std::string name = \"abcdefghijklmnopqrstuvwxyz0123456789\";\n    Partition* system = builder->AddPartition(name + name, LP_PARTITION_ATTR_READONLY);\n    EXPECT_NE(system, nullptr);\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    EXPECT_EQ(exported, nullptr);\n}\n\nTEST_F(BuilderTest, MetadataTooLarge) {\n    static const size_t kDiskSize = 128 * 1024;\n    static const size_t kMetadataSize = 64 * 1024;\n\n    // No space to store metadata + geometry.\n    BlockDeviceInfo device_info(\"super\", kDiskSize, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, kMetadataSize, 1);\n    EXPECT_EQ(builder, nullptr);\n\n    // No space to store metadata + geometry + one free sector.\n    device_info.size += LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2);\n    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);\n    EXPECT_EQ(builder, nullptr);\n\n    // Space for metadata + geometry + one free block.\n    device_info.size += device_info.logical_block_size;\n    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);\n    EXPECT_NE(builder, nullptr);\n\n    // Test with alignment.\n    device_info.alignment = 131072;\n    builder = MetadataBuilder::New(device_info, kMetadataSize, 1);\n    EXPECT_EQ(builder, nullptr);\n}\n\nTEST_F(BuilderTest, UpdateBlockDeviceInfo) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 4096, 1024, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n\n    BlockDeviceInfo new_info;\n    ASSERT_TRUE(builder->GetBlockDeviceInfo(\"super\", &new_info));\n\n    EXPECT_EQ(new_info.size, device_info.size);\n    EXPECT_EQ(new_info.alignment, device_info.alignment);\n    EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);\n    EXPECT_EQ(new_info.logical_block_size, device_info.logical_block_size);\n\n    device_info.alignment = 0;\n    device_info.alignment_offset = 2048;\n    ASSERT_TRUE(builder->UpdateBlockDeviceInfo(\"super\", device_info));\n    ASSERT_TRUE(builder->GetBlockDeviceInfo(\"super\", &new_info));\n    EXPECT_EQ(new_info.alignment, 4096);\n    EXPECT_EQ(new_info.alignment_offset, device_info.alignment_offset);\n\n    device_info.alignment = 8192;\n    device_info.alignment_offset = 0;\n    ASSERT_TRUE(builder->UpdateBlockDeviceInfo(\"super\", device_info));\n    ASSERT_TRUE(builder->GetBlockDeviceInfo(\"super\", &new_info));\n    EXPECT_EQ(new_info.alignment, 8192);\n    EXPECT_EQ(new_info.alignment_offset, 2048);\n\n    new_info.size += 4096;\n    ASSERT_FALSE(builder->UpdateBlockDeviceInfo(\"super\", new_info));\n    ASSERT_TRUE(builder->GetBlockDeviceInfo(\"super\", &new_info));\n    EXPECT_EQ(new_info.size, 1024 * 1024);\n\n    new_info.logical_block_size = 512;\n    ASSERT_TRUE(builder->UpdateBlockDeviceInfo(\"super\", new_info));\n    ASSERT_TRUE(builder->GetBlockDeviceInfo(\"super\", &new_info));\n    EXPECT_EQ(new_info.logical_block_size, 4096);\n\n    new_info.logical_block_size = 7;\n    ASSERT_FALSE(builder->UpdateBlockDeviceInfo(\"super\", new_info));\n    ASSERT_TRUE(builder->GetBlockDeviceInfo(\"super\", &new_info));\n    EXPECT_EQ(new_info.logical_block_size, 4096);\n}\n\nTEST_F(BuilderTest, InvalidBlockSize) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 513);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    EXPECT_EQ(builder, nullptr);\n}\n\nTEST_F(BuilderTest, AlignedExtentSize) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* partition = builder->AddPartition(\"system\", 0);\n    ASSERT_NE(partition, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(partition, 512));\n    EXPECT_EQ(partition->size(), 4096);\n}\n\nTEST_F(BuilderTest, AlignedFreeSpace) {\n    // Only one sector free - at least one block is required.\n    BlockDeviceInfo device_info(\"super\", 10240, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 512, 1);\n    ASSERT_EQ(builder, nullptr);\n}\n\nTEST_F(BuilderTest, HasDefaultGroup) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n\n    EXPECT_FALSE(builder->AddGroup(\"default\", 0));\n}\n\nTEST_F(BuilderTest, GroupSizeLimits) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"google\", 16384));\n\n    Partition* partition = builder->AddPartition(\"system\", \"google\", 0);\n    ASSERT_NE(partition, nullptr);\n    EXPECT_TRUE(builder->ResizePartition(partition, 8192));\n    EXPECT_EQ(partition->size(), 8192);\n    EXPECT_TRUE(builder->ResizePartition(partition, 16384));\n    EXPECT_EQ(partition->size(), 16384);\n    EXPECT_FALSE(builder->ResizePartition(partition, 32768));\n    EXPECT_EQ(partition->size(), 16384);\n}\n\nTEST_F(BuilderTest, ListPartitionsInGroup) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"groupA\", 16384));\n    ASSERT_TRUE(builder->AddGroup(\"groupB\", 16384));\n\n    Partition* system = builder->AddPartition(\"system\", \"groupA\", 0);\n    Partition* vendor = builder->AddPartition(\"vendor\", \"groupA\", 0);\n    Partition* product = builder->AddPartition(\"product\", \"groupB\", 0);\n    ASSERT_NE(system, nullptr);\n    ASSERT_NE(vendor, nullptr);\n    ASSERT_NE(product, nullptr);\n\n    auto groupA = builder->ListPartitionsInGroup(\"groupA\");\n    auto groupB = builder->ListPartitionsInGroup(\"groupB\");\n    auto groupC = builder->ListPartitionsInGroup(\"groupC\");\n    ASSERT_THAT(groupA, ElementsAre(system, vendor));\n    ASSERT_THAT(groupB, ElementsAre(product));\n    ASSERT_TRUE(groupC.empty());\n}\n\nTEST_F(BuilderTest, ChangeGroups) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"groupA\", 16384));\n    ASSERT_TRUE(builder->AddGroup(\"groupB\", 32768));\n\n    Partition* system = builder->AddPartition(\"system\", \"groupA\", 0);\n    Partition* vendor = builder->AddPartition(\"vendor\", \"groupB\", 0);\n    ASSERT_NE(system, nullptr);\n    ASSERT_NE(vendor, nullptr);\n    ASSERT_NE(builder->Export(), nullptr);\n\n    ASSERT_FALSE(builder->ChangePartitionGroup(system, \"groupXYZ\"));\n    ASSERT_TRUE(builder->ChangePartitionGroup(system, \"groupB\"));\n    ASSERT_NE(builder->Export(), nullptr);\n\n    // Violate group constraint by reassigning groups.\n    ASSERT_TRUE(builder->ResizePartition(system, 16384 + 4096));\n    ASSERT_TRUE(builder->ChangePartitionGroup(system, \"groupA\"));\n    ASSERT_EQ(builder->Export(), nullptr);\n\n    ASSERT_FALSE(builder->ChangeGroupSize(\"default\", 2));\n    ASSERT_FALSE(builder->ChangeGroupSize(\"unknown\", 2));\n    ASSERT_TRUE(builder->ChangeGroupSize(\"groupA\", 32768));\n    ASSERT_NE(builder->Export(), nullptr);\n}\n\nTEST_F(BuilderTest, RemoveAndAddFirstPartition) {\n    auto builder = MetadataBuilder::New(10_GiB, 65536, 2);\n    ASSERT_NE(nullptr, builder);\n    ASSERT_TRUE(builder->AddGroup(\"foo_a\", 5_GiB));\n    ASSERT_TRUE(builder->AddGroup(\"foo_b\", 5_GiB));\n    android::fs_mgr::Partition* p;\n    p = builder->AddPartition(\"system_a\", \"foo_a\", 0);\n    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));\n    p = builder->AddPartition(\"vendor_a\", \"foo_a\", 0);\n    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));\n    p = builder->AddPartition(\"system_b\", \"foo_b\", 0);\n    ASSERT_TRUE(p && builder->ResizePartition(p, 2_GiB));\n    p = builder->AddPartition(\"vendor_b\", \"foo_b\", 0);\n    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));\n\n    builder->RemovePartition(\"system_a\");\n    builder->RemovePartition(\"vendor_a\");\n    p = builder->AddPartition(\"system_a\", \"foo_a\", 0);\n    ASSERT_TRUE(p && builder->ResizePartition(p, 3_GiB));\n    p = builder->AddPartition(\"vendor_a\", \"foo_a\", 0);\n    ASSERT_TRUE(p && builder->ResizePartition(p, 1_GiB));\n}\n\nTEST_F(BuilderTest, ListGroups) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(builder->AddGroup(\"example\", 0));\n\n    std::vector<std::string> groups = builder->ListGroups();\n    ASSERT_THAT(groups, ElementsAre(\"default\", \"example\"));\n}\n\nTEST_F(BuilderTest, RemoveGroupAndPartitions) {\n    BlockDeviceInfo device_info(\"super\", 1024 * 1024, 0, 0, 4096);\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(device_info, 1024, 1);\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(builder->AddGroup(\"example\", 0));\n    ASSERT_NE(builder->AddPartition(\"system\", \"default\", 0), nullptr);\n    ASSERT_NE(builder->AddPartition(\"vendor\", \"example\", 0), nullptr);\n\n    builder->RemoveGroupAndPartitions(\"example\");\n    ASSERT_NE(builder->FindPartition(\"system\"), nullptr);\n    ASSERT_EQ(builder->FindPartition(\"vendor\"), nullptr);\n    ASSERT_THAT(builder->ListGroups(), ElementsAre(\"default\"));\n\n    builder->RemoveGroupAndPartitions(\"default\");\n    ASSERT_NE(builder->FindPartition(\"system\"), nullptr);\n}\n\nTEST_F(BuilderTest, MultipleBlockDevices) {\n    std::vector<BlockDeviceInfo> partitions = {\n            BlockDeviceInfo(\"system_a\", 256_MiB, 786432, 229376, 4096),\n            BlockDeviceInfo(\"vendor_a\", 128_MiB, 786432, 753664, 4096),\n            BlockDeviceInfo(\"product_a\", 64_MiB, 786432, 753664, 4096),\n    };\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(partitions, \"system_a\", 65536, 2);\n    ASSERT_NE(builder, nullptr);\n    EXPECT_EQ(builder->AllocatableSpace(), 467402752);\n\n    // Create a partition that spans 3 devices.\n    Partition* p = builder->AddPartition(\"system_a\", 0);\n    ASSERT_NE(p, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(p, 466976768));\n\n    unique_ptr<LpMetadata> metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n    ASSERT_EQ(metadata->block_devices.size(), 3);\n    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), \"system_a\");\n    EXPECT_EQ(metadata->block_devices[0].size, 256_MiB);\n    EXPECT_EQ(metadata->block_devices[0].alignment, 786432);\n    EXPECT_EQ(metadata->block_devices[0].alignment_offset, 229376);\n    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[1]), \"vendor_a\");\n    EXPECT_EQ(metadata->block_devices[1].size, 128_MiB);\n    EXPECT_EQ(metadata->block_devices[1].alignment, 786432);\n    EXPECT_EQ(metadata->block_devices[1].alignment_offset, 753664);\n    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[2]), \"product_a\");\n    EXPECT_EQ(metadata->block_devices[2].size, 64_MiB);\n    EXPECT_EQ(metadata->block_devices[2].alignment, 786432);\n    EXPECT_EQ(metadata->block_devices[2].alignment_offset, 753664);\n    ASSERT_EQ(metadata->extents.size(), 3);\n    EXPECT_EQ(metadata->extents[0].num_sectors, 522752);\n    EXPECT_EQ(metadata->extents[0].target_type, LP_TARGET_TYPE_LINEAR);\n    EXPECT_EQ(metadata->extents[0].target_data, 1536);\n    EXPECT_EQ(metadata->extents[0].target_source, 0);\n    EXPECT_EQ(metadata->extents[1].num_sectors, 260608);\n    EXPECT_EQ(metadata->extents[1].target_type, LP_TARGET_TYPE_LINEAR);\n    EXPECT_EQ(metadata->extents[1].target_data, 1536);\n    EXPECT_EQ(metadata->extents[1].target_source, 1);\n    EXPECT_EQ(metadata->extents[2].num_sectors, 128704);\n    EXPECT_EQ(metadata->extents[2].target_type, LP_TARGET_TYPE_LINEAR);\n    EXPECT_EQ(metadata->extents[2].target_data, 1536);\n    EXPECT_EQ(metadata->extents[2].target_source, 2);\n}\n\nTEST_F(BuilderTest, ImportPartitionsOk) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    Partition* vendor = builder->AddPartition(\"vendor\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    ASSERT_NE(vendor, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, 65536), true);\n    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);\n    EXPECT_EQ(builder->ResizePartition(system, 98304), true);\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->ImportPartitions(*exported.get(), {\"vendor\"}));\n    EXPECT_NE(builder->FindPartition(\"vendor\"), nullptr);\n    EXPECT_EQ(builder->FindPartition(\"system\"), nullptr);\n\n    unique_ptr<LpMetadata> new_metadata = builder->Export();\n    ASSERT_NE(new_metadata, nullptr);\n\n    ASSERT_EQ(exported->partitions.size(), static_cast<size_t>(2));\n    ASSERT_EQ(GetPartitionName(exported->partitions[1]), \"vendor\");\n    ASSERT_EQ(new_metadata->partitions.size(), static_cast<size_t>(1));\n    ASSERT_EQ(GetPartitionName(new_metadata->partitions[0]), \"vendor\");\n\n    const LpMetadataExtent& extent_a =\n            exported->extents[exported->partitions[1].first_extent_index];\n    const LpMetadataExtent& extent_b =\n            new_metadata->extents[new_metadata->partitions[0].first_extent_index];\n    EXPECT_EQ(extent_a.num_sectors, extent_b.num_sectors);\n    EXPECT_EQ(extent_a.target_type, extent_b.target_type);\n    EXPECT_EQ(extent_a.target_data, extent_b.target_data);\n    EXPECT_EQ(extent_a.target_source, extent_b.target_source);\n}\n\nTEST_F(BuilderTest, ImportPartitionsFail) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_READONLY);\n    Partition* vendor = builder->AddPartition(\"vendor\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system, nullptr);\n    ASSERT_NE(vendor, nullptr);\n    EXPECT_EQ(builder->ResizePartition(system, 65536), true);\n    EXPECT_EQ(builder->ResizePartition(vendor, 32768), true);\n    EXPECT_EQ(builder->ResizePartition(system, 98304), true);\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    // Different device size.\n    builder = MetadataBuilder::New(1024 * 2048, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n    EXPECT_FALSE(builder->ImportPartitions(*exported.get(), {\"system\"}));\n}\n\nTEST_F(BuilderTest, ABExtents) {\n    BlockDeviceInfo device_info(\"super\", 10_GiB, 768 * 1024, 0, 4096);\n\n    // A and B slots should be allocated from separate halves of the partition,\n    // to mitigate allocating too many extents. (b/120433288)\n    ON_CALL(*GetMockedPropertyFetcher(), GetProperty(\"ro.boot.slot_suffix\", _))\n            .WillByDefault(Return(\"_a\"));\n\n    auto builder = MetadataBuilder::New(device_info, 65536, 2);\n    ASSERT_NE(builder, nullptr);\n    Partition* system_a = builder->AddPartition(\"system_a\", 0);\n    ASSERT_NE(system_a, nullptr);\n    Partition* system_b = builder->AddPartition(\"system_b\", 0);\n    ASSERT_NE(system_b, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(system_a, 2_GiB));\n    ASSERT_TRUE(builder->ResizePartition(system_b, 2_GiB));\n\n    builder->RemovePartition(\"system_a\");\n    system_a = builder->AddPartition(\"system_a\", 0);\n    ASSERT_NE(system_a, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(system_a, 3_GiB));\n\n    EXPECT_EQ(system_a->extents().size(), static_cast<size_t>(1));\n    EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(1));\n    ASSERT_TRUE(builder->ResizePartition(system_b, 6_GiB));\n    EXPECT_EQ(system_b->extents().size(), static_cast<size_t>(2));\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    ASSERT_EQ(exported->extents.size(), static_cast<size_t>(3));\n    EXPECT_EQ(exported->extents[0].target_data, 10487808);\n    EXPECT_EQ(exported->extents[0].num_sectors, 10483712);\n    EXPECT_EQ(exported->extents[1].target_data, 6292992);\n    EXPECT_EQ(exported->extents[1].num_sectors, 2099200);\n    EXPECT_EQ(exported->extents[2].target_data, 1536);\n    EXPECT_EQ(exported->extents[2].num_sectors, 6291456);\n}\n\nTEST_F(BuilderTest, PartialExtents) {\n    // super has a minimum extent size of 768KiB.\n    BlockDeviceInfo device_info(\"super\", 1_GiB, 768 * 1024, 0, 4096);\n    auto builder = MetadataBuilder::New(device_info, 65536, 1);\n    ASSERT_NE(builder, nullptr);\n    Partition* system = builder->AddPartition(\"system\", 0);\n    ASSERT_NE(system, nullptr);\n    Partition* vendor = builder->AddPartition(\"vendor\", 0);\n    ASSERT_NE(vendor, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment + 4096));\n    ASSERT_TRUE(builder->ResizePartition(vendor, device_info.alignment));\n    ASSERT_EQ(system->size(), device_info.alignment + 4096);\n    ASSERT_EQ(vendor->size(), device_info.alignment);\n\n    ASSERT_TRUE(builder->ResizePartition(system, device_info.alignment * 2));\n    ASSERT_EQ(system->extents().size(), static_cast<size_t>(1));\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    ASSERT_EQ(exported->extents.size(), static_cast<size_t>(2));\n    EXPECT_EQ(exported->extents[0].target_data, 1536);\n    EXPECT_EQ(exported->extents[0].num_sectors, 3072);\n    EXPECT_EQ(exported->extents[1].target_data, 4608);\n    EXPECT_EQ(exported->extents[1].num_sectors, 1536);\n}\n\nTEST_F(BuilderTest, UpdateSuper) {\n    // Build the on-disk metadata that we saw before flashing.\n    auto builder = MetadataBuilder::New(8145338368ULL, 65536, 3);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"google_dynamic_partitions_a\", 4068474880ULL));\n    ASSERT_TRUE(builder->AddGroup(\"google_dynamic_partitions_b\", 4068474880ULL));\n\n    Partition* partition = builder->AddPartition(\"system_a\", \"google_dynamic_partitions_a\",\n                                                 LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(partition, \"super\", 1901568, 3608576));\n\n    partition = builder->AddPartition(\"vendor_a\", \"google_dynamic_partitions_a\",\n                                      LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(partition, \"super\", 1521664, 5510144));\n\n    partition = builder->AddPartition(\"product_a\", \"google_dynamic_partitions_a\",\n                                      LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(partition, \"super\", 3606528, 2048));\n\n    partition = builder->AddPartition(\"system_b\", \"google_dynamic_partitions_b\",\n                                      LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(partition, \"super\", 1901568, 7955456));\n\n    partition = builder->AddPartition(\"vendor_b\", \"google_dynamic_partitions_b\",\n                                      LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(partition, \"super\", 1521664, 9857024));\n\n    partition = builder->AddPartition(\"product_b\", \"google_dynamic_partitions_b\",\n                                      LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(partition, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(partition, \"super\", 3606528, 11378688));\n\n    auto on_disk = builder->Export();\n    ASSERT_NE(on_disk, nullptr);\n\n    // Build the super_empty from the new build.\n    builder = MetadataBuilder::New(8145338368ULL, 65536, 3);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"google_dynamic_partitions_a\", 4068474880ULL));\n    ASSERT_TRUE(builder->AddGroup(\"google_dynamic_partitions_b\", 4068474880ULL));\n    ASSERT_NE(builder->AddPartition(\"system_a\", \"google_dynamic_partitions_a\",\n                                    LP_PARTITION_ATTR_READONLY),\n              nullptr);\n    ASSERT_NE(builder->AddPartition(\"system_b\", \"google_dynamic_partitions_b\",\n                                    LP_PARTITION_ATTR_READONLY),\n              nullptr);\n    ASSERT_NE(builder->AddPartition(\"vendor_a\", \"google_dynamic_partitions_a\",\n                                    LP_PARTITION_ATTR_READONLY),\n              nullptr);\n    ASSERT_NE(builder->AddPartition(\"vendor_b\", \"google_dynamic_partitions_b\",\n                                    LP_PARTITION_ATTR_READONLY),\n              nullptr);\n    ASSERT_NE(builder->AddPartition(\"product_a\", \"google_dynamic_partitions_a\",\n                                    LP_PARTITION_ATTR_READONLY),\n              nullptr);\n    ASSERT_NE(builder->AddPartition(\"product_b\", \"google_dynamic_partitions_b\",\n                                    LP_PARTITION_ATTR_READONLY),\n              nullptr);\n\n    std::set<std::string> partitions_to_keep{\"system_a\", \"vendor_a\", \"product_a\"};\n    ASSERT_TRUE(builder->ImportPartitions(*on_disk.get(), partitions_to_keep));\n}\n\n// Interval has operator< defined; it is not appropriate to re-define Interval::operator== that\n// compares device index.\nnamespace android {\nnamespace fs_mgr {\nbool operator==(const Interval& a, const Interval& b) {\n    return a.device_index == b.device_index && a.start == b.start && a.end == b.end;\n}\n}  // namespace fs_mgr\n}  // namespace android\n\nTEST_F(BuilderTest, Interval) {\n    EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 100)).length());\n    EXPECT_EQ(Interval(0, 100, 150),\n              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 150)));\n    EXPECT_EQ(Interval(0, 100, 200),\n              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 200)));\n    EXPECT_EQ(Interval(0, 100, 200),\n              Interval::Intersect(Interval(0, 100, 200), Interval(0, 50, 250)));\n    EXPECT_EQ(Interval(0, 100, 200),\n              Interval::Intersect(Interval(0, 100, 200), Interval(0, 100, 200)));\n    EXPECT_EQ(Interval(0, 150, 200),\n              Interval::Intersect(Interval(0, 100, 200), Interval(0, 150, 250)));\n    EXPECT_EQ(0u, Interval::Intersect(Interval(0, 100, 200), Interval(0, 200, 250)).length());\n\n    auto v = Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50), Interval(0, 100, 150)},\n                                 std::vector<Interval>{Interval(0, 25, 125)});\n    ASSERT_EQ(2, v.size());\n    EXPECT_EQ(Interval(0, 25, 50), v[0]);\n    EXPECT_EQ(Interval(0, 100, 125), v[1]);\n\n    EXPECT_EQ(0u, Interval::Intersect(std::vector<Interval>{Interval(0, 0, 50)},\n                                      std::vector<Interval>{Interval(0, 100, 150)})\n                          .size());\n}\n\nTEST_F(BuilderTest, ExpandedHeader) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    builder->RequireExpandedMetadataHeader();\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));\n\n    exported->header.flags = 0x5e5e5e5e;\n\n    builder = MetadataBuilder::New(*exported.get());\n    exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    EXPECT_EQ(exported->header.header_size, sizeof(LpMetadataHeaderV1_2));\n    EXPECT_EQ(exported->header.flags, 0x5e5e5e5e);\n}\n\nstatic Interval ToInterval(const std::unique_ptr<Extent>& extent) {\n    if (LinearExtent* le = extent->AsLinearExtent()) {\n        return le->AsInterval();\n    }\n    return {0, 0, 0};\n}\n\nstatic void AddPartition(const std::unique_ptr<MetadataBuilder>& builder,\n                         const std::string& partition_name, const std::string& group_name,\n                         uint64_t num_sectors, uint64_t start_sector,\n                         std::vector<Interval>* intervals = nullptr) {\n    Partition* p = builder->AddPartition(partition_name, group_name, 0);\n    ASSERT_NE(p, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(p, \"super\", num_sectors, start_sector));\n    ASSERT_EQ(p->extents().size(), 1);\n\n    if (!intervals) {\n        return;\n    }\n\n    auto new_interval = ToInterval(p->extents().back());\n    std::vector<Interval> new_intervals = {new_interval};\n\n    auto overlap = Interval::Intersect(*intervals, new_intervals);\n    ASSERT_TRUE(overlap.empty());\n\n    intervals->push_back(new_interval);\n}\n\nTEST_F(BuilderTest, CollidedExtents) {\n    BlockDeviceInfo super(\"super\", 8_GiB, 786432, 229376, 4096);\n    std::vector<BlockDeviceInfo> block_devices = {super};\n\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, \"super\", 65536, 2);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"group\", 0));\n\n    std::vector<Interval> old_intervals;\n    AddPartition(builder, \"system\", \"group\", 10229008, 2048, &old_intervals);\n    AddPartition(builder, \"test_a\", \"group\", 648, 12709888, &old_intervals);\n    AddPartition(builder, \"test_b\", \"group\", 625184, 12711936, &old_intervals);\n    AddPartition(builder, \"test_c\", \"group\", 130912, 13338624, &old_intervals);\n    AddPartition(builder, \"test_d\", \"group\", 888, 13469696, &old_intervals);\n    AddPartition(builder, \"test_e\", \"group\", 888, 13471744, &old_intervals);\n    AddPartition(builder, \"test_f\", \"group\", 888, 13475840, &old_intervals);\n    AddPartition(builder, \"test_g\", \"group\", 888, 13477888, &old_intervals);\n\n    // Don't track the first vendor interval, since it will get extended.\n    AddPartition(builder, \"vendor\", \"group\", 2477920, 10231808, nullptr);\n\n    std::vector<Interval> new_intervals;\n\n    Partition* p = builder->FindPartition(\"vendor\");\n    ASSERT_NE(p, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(p, 1282031616));\n    ASSERT_GE(p->extents().size(), 1);\n    for (const auto& extent : p->extents()) {\n        new_intervals.push_back(ToInterval(extent));\n    }\n\n    std::vector<Interval> overlap = Interval::Intersect(old_intervals, new_intervals);\n    ASSERT_TRUE(overlap.empty());\n}\n\nTEST_F(BuilderTest, LinearExtentOverlap) {\n    LinearExtent extent(20, 0, 10);\n\n    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 10}));\n    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{50, 0, 10}));\n    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 0, 30}));\n    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{10, 0, 0}));\n    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 0}));\n    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{40, 0, 0}));\n    EXPECT_TRUE(extent.OverlapsWith(LinearExtent{20, 0, 15}));\n\n    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 0}));\n    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{50, 1, 10}));\n    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{40, 1, 0}));\n    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 15}));\n    EXPECT_FALSE(extent.OverlapsWith(LinearExtent{20, 1, 10}));\n}\n\nTEST_F(BuilderTest, AlignFreeRegion) {\n    BlockDeviceInfo super(\"super\", 8_GiB, 786432, 0, 4096);\n    std::vector<BlockDeviceInfo> block_devices = {super};\n\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, \"super\", 65536, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* p = builder->AddPartition(\"system\", \"default\", 0);\n    ASSERT_NE(p, nullptr);\n    ASSERT_TRUE(builder->AddLinearExtent(p, \"super\", 64, (super.alignment + 4096) / 512));\n\n    p = builder->AddPartition(\"vendor\", \"default\", 0);\n    ASSERT_NE(p, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(p, 2_GiB));\n\n    const auto& extents = p->extents();\n    ASSERT_EQ(extents.size(), 2);\n\n    LinearExtent* e1 = extents[0]->AsLinearExtent();\n    ASSERT_NE(e1, nullptr);\n    LinearExtent* e2 = extents[1]->AsLinearExtent();\n    ASSERT_NE(e2, nullptr);\n\n    // The misaligned partition starting at sector 1544 should not cause any\n    // overlap with previous extents. We should see vendor punch a hole where\n    // \"system\" is, extending the hole up to the next aligned block.\n    EXPECT_EQ(e1->physical_sector(), 1536);\n    EXPECT_EQ(e1->end_sector(), 1544);\n    EXPECT_EQ(e2->physical_sector(), 3072);\n    EXPECT_EQ(e2->end_sector(), 4197368);\n}\n\nTEST_F(BuilderTest, ResizeOverflow) {\n    BlockDeviceInfo super(\"super\", 8_GiB, 786432, 229376, 4096);\n    std::vector<BlockDeviceInfo> block_devices = {super};\n\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(block_devices, \"super\", 65536, 2);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"group\", 0));\n\n    Partition* p = builder->AddPartition(\"system\", \"default\", 0);\n    ASSERT_NE(p, nullptr);\n    ASSERT_FALSE(builder->ResizePartition(p, 18446744073709551615ULL));\n}\n\nTEST_F(BuilderTest, VerifyExtent) {\n    auto source_builder = MetadataBuilder::New(4096 * 50, 40960, 2);\n    ASSERT_NE(source_builder, nullptr);\n    ASSERT_TRUE(source_builder->AddGroup(\"test_group_a\", 40960));\n    ASSERT_TRUE(source_builder->AddGroup(\"test_group_b\", 40960));\n    AddPartition(source_builder, \"system_a\", \"test_group_a\", 8192, 2048);\n    AddPartition(source_builder, \"vendor_a\", \"test_group_a\", 10240, 10240);\n    AddPartition(source_builder, \"system_b\", \"test_group_b\", 8192, 20480);\n\n    auto target_builder = MetadataBuilder::New(4096 * 50, 40960, 2);\n    ASSERT_NE(target_builder, nullptr);\n    ASSERT_TRUE(target_builder->AddGroup(\"test_group_b\", 40960));\n    AddPartition(target_builder, \"system_b\", \"test_group_b\", 8192, 2048);\n    AddPartition(target_builder, \"vendor_b\", \"test_group_b\", 10240, 10240);\n\n    ASSERT_TRUE(MetadataBuilder::VerifyExtentsAgainstSourceMetadata(\n            *source_builder, 0, *target_builder, 1, std::vector<std::string>{\"system\", \"vendor\"}));\n\n    target_builder->RemovePartition(\"vendor_b\");\n    ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(\n            *source_builder, 0, *target_builder, 1, std::vector<std::string>{\"vendor\"}));\n\n    AddPartition(target_builder, \"vendor_b\", \"test_group_b\", 1000, 10240);\n    ASSERT_FALSE(target_builder->VerifyExtentsAgainstSourceMetadata(\n            *source_builder, 0, *target_builder, 1, std::vector<std::string>{\"vendor\"}));\n}\n"
  },
  {
    "path": "fs_mgr/liblp/device_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/properties.h>\n#include <fs_mgr.h>\n#include <fstab/fstab.h>\n#include <gtest/gtest.h>\n#include <liblp/liblp.h>\n#include <liblp/metadata_format.h>\n#include <liblp/partition_opener.h>\n#include <liblp/property_fetcher.h>\n\n#include \"liblp_test.h\"\n\nusing namespace android::fs_mgr;\nusing namespace android::fs_mgr::testing;\nusing ::testing::Return;\n\n// Compliance test on the actual device with dynamic partitions.\nclass DeviceTest : public LiblpTest {\n  public:\n    void SetUp() override {\n        // Read real properties.\n        IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());\n        if (!IPropertyFetcher::GetInstance()->GetBoolProperty(\"ro.boot.dynamic_partitions\",\n                                                              false)) {\n            GTEST_SKIP() << \"Device doesn't have dynamic partitions enabled, skipping\";\n        }\n    }\n};\n\nTEST_F(DeviceTest, BlockDeviceInfo) {\n    PartitionOpener opener;\n    BlockDeviceInfo device_info;\n    ASSERT_TRUE(opener.GetInfo(fs_mgr_get_super_partition_name(), &device_info));\n\n    // Check that the device doesn't give us some weird inefficient\n    // alignment.\n    EXPECT_EQ(device_info.alignment % LP_SECTOR_SIZE, 0);\n    EXPECT_EQ(device_info.logical_block_size % LP_SECTOR_SIZE, 0);\n}\n\nTEST_F(DeviceTest, ReadSuperPartitionCurrentSlot) {\n    auto slot_suffix = fs_mgr_get_slot_suffix();\n    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);\n    auto super_name = fs_mgr_get_super_partition_name(slot_number);\n    auto metadata = ReadMetadata(super_name, slot_number);\n    EXPECT_NE(metadata, nullptr);\n}\n\nTEST_F(DeviceTest, ReadSuperPartitionOtherSlot) {\n    auto other_slot_suffix = fs_mgr_get_other_slot_suffix();\n    if (other_slot_suffix.empty()) {\n        GTEST_SKIP() << \"No other slot, skipping\";\n    }\n    if (IPropertyFetcher::GetInstance()->GetBoolProperty(\"ro.boot.dynamic_partitions_retrofit\",\n                                                         false)) {\n        GTEST_SKIP() << \"Device with retrofit dynamic partition may not have metadata at other \"\n                     << \"slot, skipping\";\n    }\n\n    auto other_slot_number = SlotNumberForSlotSuffix(other_slot_suffix);\n    auto other_super_name = fs_mgr_get_super_partition_name(other_slot_number);\n    auto other_metadata = ReadMetadata(other_super_name, other_slot_number);\n    EXPECT_NE(other_metadata, nullptr);\n}\n"
  },
  {
    "path": "fs_mgr/liblp/fuzzer/Android.bp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n}\n\ncc_defaults {\n    name: \"liblp_fuzz_defaults\",\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    shared_libs: [\n        \"liblp\",\n        \"libbase\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libcutils\",\n    ],\n    include_dirs: [\n        \"system/core/fs_mgr/liblp\",\n    ],\n    fuzz_config: {\n        cc: [\n            \"android-systems-storage@google.com\",\n        ],\n        componentid: 59148,\n        hotlists: [\"4593311\"],\n        description: \"The fuzzers target the APIs of all liblp modules\",\n        vector: \"local_no_privileges_required\",\n        service_privilege: \"privileged\",\n        users: \"multi_user\",\n        fuzzed_code_usage: \"shipped\",\n    },\n}\n\ncc_fuzz {\n    name: \"liblp_builder_fuzzer\",\n    srcs: [\"liblp_builder_fuzzer.cpp\"],\n    defaults: [\"liblp_fuzz_defaults\"],\n}\n\ncc_fuzz {\n    name: \"liblp_super_layout_builder_fuzzer\",\n    srcs: [\"liblp_super_layout_builder_fuzzer.cpp\"],\n    defaults: [\"liblp_fuzz_defaults\"],\n}\n\npython_binary_host {\n    name: \"image_gen_rand\",\n    srcs: [\"image_gen_rand.py\"],\n}\n\ngenrule_defaults {\n    name: \"test_data_gen_defaults\",\n    tools: [\n        \"image_gen_rand\",\n    ],\n}\n\n// Fake dtb image.\ngenrule {\n    name: \"test_dtb\",\n    defaults: [\"test_data_gen_defaults\"],\n    out: [\"test_dtb.img\"],\n    cmd: \"$(location image_gen_rand) --seed dtb --length 1024 > $(out)\",\n}\n\n// Fake bootconfig image.\ngenrule {\n    name: \"test_bootconfig\",\n    defaults: [\"test_data_gen_defaults\"],\n    out: [\"test_bootconfig.img\"],\n    cmd: \"$(location image_gen_rand) --seed bootconfig --length 1024 > $(out)\",\n}\n\n// Fake vendor ramdisk with type \"none\".\ngenrule {\n    name: \"test_vendor_ramdisk_none\",\n    defaults: [\"test_data_gen_defaults\"],\n    out: [\"test_vendor_ramdisk_none.img\"],\n    cmd: \"$(location image_gen_rand) --seed vendor_ramdisk_none --length 1024 > $(out)\",\n}\n\n// Fake vendor ramdisk with type \"platform\".\ngenrule {\n    name: \"test_vendor_ramdisk_platform\",\n    defaults: [\"test_data_gen_defaults\"],\n    out: [\"test_vendor_ramdisk_platform.img\"],\n    cmd: \"$(location image_gen_rand) --seed vendor_ramdisk_platform --length 1024 > $(out)\",\n}\n\n// Fake replacement ramdisk.\ngenrule {\n    name: \"test_vendor_ramdisk_replace\",\n    defaults: [\"test_data_gen_defaults\"],\n    out: [\"test_vendor_ramdisk_replace.img\"],\n    cmd: \"$(location image_gen_rand) --seed replace --length 3072 > $(out)\",\n}\n\n// Genrules for test vendor boot images.\nfastboot_sign_test_image = \"$(location avbtool) add_hash_footer --salt 00 --image $(out) \" +\n    \"--partition_name vendor_boot --partition_size $$(( 1 * 1024 * 1024 ))\"\n\ngenrule_defaults {\n    name: \"test_vendor_boot_gen_defaults\",\n    defaults: [\"test_data_gen_defaults\"],\n    tools: [\n        \"avbtool\",\n        \"mkbootimg\",\n    ],\n}\n\ngenrule {\n    name: \"test_vendor_boot_v3\",\n    defaults: [\"test_vendor_boot_gen_defaults\"],\n    out: [\"test_vendor_boot_v3.img\"],\n    srcs: [\n        \":test_dtb\",\n        \":test_vendor_ramdisk_none\",\n    ],\n    cmd: \"$(location mkbootimg) --header_version 3 \" +\n        \"--vendor_ramdisk $(location :test_vendor_ramdisk_none) \" +\n        \"--dtb $(location :test_dtb) \" +\n        \"--vendor_boot $(out) && \" +\n        fastboot_sign_test_image,\n}\n\ngenrule {\n    name: \"test_vendor_boot_v4_without_frag\",\n    defaults: [\"test_vendor_boot_gen_defaults\"],\n    out: [\"test_vendor_boot_v4_without_frag.img\"],\n    srcs: [\n        \":test_dtb\",\n        \":test_vendor_ramdisk_none\",\n        \":test_bootconfig\",\n    ],\n    cmd: \"$(location mkbootimg) --header_version 4 \" +\n        \"--vendor_ramdisk $(location :test_vendor_ramdisk_none) \" +\n        \"--dtb $(location :test_dtb) \" +\n        \"--vendor_bootconfig $(location :test_bootconfig) \" +\n        \"--vendor_boot $(out) && \" +\n        fastboot_sign_test_image,\n}\n\ngenrule {\n    name: \"test_vendor_boot_v4_with_frag\",\n    defaults: [\"test_vendor_boot_gen_defaults\"],\n    out: [\"test_vendor_boot_v4_with_frag.img\"],\n    srcs: [\n        \":test_dtb\",\n        \":test_vendor_ramdisk_none\",\n        \":test_vendor_ramdisk_platform\",\n        \":test_bootconfig\",\n    ],\n    cmd: \"$(location mkbootimg) --header_version 4 \" +\n        \"--dtb $(location :test_dtb) \" +\n        \"--vendor_bootconfig $(location :test_bootconfig) \" +\n        \"--ramdisk_type none --ramdisk_name none_ramdisk \" +\n        \"--vendor_ramdisk_fragment $(location :test_vendor_ramdisk_none) \" +\n        \"--ramdisk_type platform --ramdisk_name platform_ramdisk \" +\n        \"--vendor_ramdisk_fragment $(location :test_vendor_ramdisk_platform) \" +\n        \"--vendor_boot $(out) && \" +\n        fastboot_sign_test_image,\n}\n\ncc_fuzz {\n    name: \"liblp_apis_fuzzer\",\n    srcs: [\n        \"liblp_apis_fuzzer.cpp\",\n        \":TestPartitionOpener_group\",\n    ],\n    defaults: [\"liblp_fuzz_defaults\"],\n    shared_libs: [\n        \"libsparse\",\n    ],\n    data: [\n        \":test_dtb\",\n        \":test_bootconfig\",\n        \":test_vendor_ramdisk_none\",\n        \":test_vendor_ramdisk_platform\",\n        \":test_vendor_ramdisk_replace\",\n        \":test_vendor_boot_v3\",\n        \":test_vendor_boot_v4_without_frag\",\n        \":test_vendor_boot_v4_with_frag\",\n    ],\n    cflags: [\n        \"-Wno-unused-parameter\",\n    ],\n}\n"
  },
  {
    "path": "fs_mgr/liblp/fuzzer/README.md",
    "content": "# Fuzzers for liblp\n## Table of contents\n+  [liblp_builder_fuzzer](#Builder)\n+  [liblp_super_layout_builder_fuzzer](#SuperBuilder)\n+  [liblp_apis_fuzzer](#APIs)\n\n# <a  name=\"Builder\"></a> Fuzzer for LiblpBuilder\n\nLiblpBuilder supports the following parameters:\n1. kAttributeTypes (parameter name: \"attribute\")\n2. blockDevSize (parameter name: \"blockdev_size\")\n3. metadataMaxSize (parameter name: \"metadata_max_size\")\n4. metadataSlotCount (parameter name: \"metadata_slot_count\")\n5. partitionName (parameter name: \"partition_name\")\n6. superBlockDeviceName (parameter name: \"block_device_name\")\n7. blockDeviceInfoSize (parameter name: \"block_device_info_size\")\n8. alignment (parameter name: \"alignment\")\n9. alignmentOffset (parameter name: \"alignment_offset\")\n10. logicalBlockSize (parameter name: \"logical_block_size\")\n11. maxMetadataSize (parameter name: \"max_metadata_size\")\n12. deviceIndex (parameter name: \"device_index\")\n13. start (parameter name: \"start\")\n14. end (parameter name: \"end\")\n15. addedGroupName (parameter name: \"group_name\")\n16. partitionGroupName (parameter name: \"partition_name\")\n17. numSectors (parameter name: \"num_sectors\")\n18. physicalSector (parameter name: \"physical_sector\")\n19. resizedPartitionSize (parameter name: \"requested_size\")\n\n| Parameter| Valid Values| Configured Value|\n|------------- |-------------| ----- |\n|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|\n|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|\n|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|\n|`metadataSlotCount`| Integer value from `0` to `2` |Value obtained from FuzzedDataProvider|\n|`partitionName`| String |Value obtained from FuzzedDataProvider|\n|`superBlockDeviceName`| String |Value obtained from FuzzedDataProvider|\n|`blockDeviceInfoSize`| Integer |Value obtained from FuzzedDataProvider|\n|`alignment`| Integer |Value obtained from FuzzedDataProvider|\n|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|\n|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|\n|`maxMetadataSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|\n|`deviceIndex`| Integer |Value obtained from FuzzedDataProvider|\n|`start`| Integer |Value obtained from FuzzedDataProvider|\n|`end`| Integer |Value obtained from FuzzedDataProvider|\n|`partitionGroupName`| String |Value obtained from FuzzedDataProvider|\n|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|\n|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|\n|`resizedPartitionSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|\n\n#### Steps to run\n1. Build the fuzzer\n```\n  $ mm -j$(nproc) liblp_builder_fuzzer\n```\n2. Run on device\n```\n  $ adb sync data\n  $ adb shell /data/fuzz/arm64/liblp_builder_fuzzer/liblp_builder_fuzzer\n```\n\n# <a  name=\"SuperBuilder\"></a> Fuzzer for LiblpSuperLayoutBuilder\n\nSuperLayoutBuilder supports the following parameters:\n1. kAttributeTypes (parameter name: \"attribute\")\n2. blockDevSize (parameter name: \"blockdev_size\")\n3. metadataMaxSize (parameter name: \"metadata_max_size\")\n4. partitionName (parameter name: \"partition_name\")\n5. data (parameter name: \"data\")\n6. imageName (parameter name: \"image_name\")\n\n| Parameter| Valid Values| Configured Value|\n|------------- |-------------| ----- |\n|`kAttributeTypes`| 1.`LP_PARTITION_ATTR_NONE`,<br/> 2.`LP_PARTITION_ATTR_READONLY`,<br/> 3.`LP_PARTITION_ATTR_SLOT_SUFFIXED`,<br/> 4.`LP_PARTITION_ATTR_UPDATED`,<br/> 5.`LP_PARTITION_ATTR_DISABLED`|Value obtained from FuzzedDataProvider|\n|`blockDevSize`| Integer value from `0` to `100000`|Value obtained from FuzzedDataProvider|\n|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|\n|`partitionName`| String |Value obtained from FuzzedDataProvider|\n|`data`| String |Value obtained from FuzzedDataProvider|\n|`imageName`| String |Value obtained from FuzzedDataProvider|\n\n#### Steps to run\n1. Build the fuzzer\n```\n  $ mm -j$(nproc) liblp_super_layout_builder_fuzzer\n```\n2. Run on device\n```\n  $ adb sync data\n  $ adb shell /data/fuzz/arm64/liblp_super_layout_builder_fuzzer/liblp_super_layout_builder_fuzzer\n```\n\n# <a  name=\"APIs\"></a> Fuzzer for LiblpApis\n\nLiblpAPIs supports the following parameters:\n1. blockDeviceInfoSize (parameter name: \"block_device_info_size\")\n2. alignment (parameter name: \"alignment\")\n3. alignmentOffset (parameter name: \"alignment_offset\")\n4. logicalBlockSize (parameter name: \"logical_block_size\")\n5. blockDevSize (parameter name: \"blockdev_size\")\n6. metadataMaxSize (parameter name: \"metadata_max_size\")\n7. blockDeviceInfoName (parameter name: \"block_device_info_name\")\n8. numSectors (parameter name: \"num_sectors\")\n9. physicalSector (parameter name: \"physical_sector\")\n10. sparsify (parameter name: \"sparsify\")\n11. buffer (parameter name: \"data\")\n\n| Parameter| Valid Values| Configured Value|\n|------------- |-------------| ----- |\n|`blockDeviceInfoSize`| Integer |Value obtained from FuzzedDataProvider|\n|`alignment`| Integer |Value obtained from FuzzedDataProvider|\n|`alignmentOffset`| Integer |Value obtained from FuzzedDataProvider|\n|`logicalBlockSize`| Integer |Value obtained from FuzzedDataProvider|\n|`blockDevSize`| Integer value in multiples of `LP_SECTOR_SIZE`|Value obtained from FuzzedDataProvider|\n|`metadataMaxSize`| Integer value from `0` to `10000` |Value obtained from FuzzedDataProvider|\n|`blockDeviceInfoName`| String |Value obtained from FuzzedDataProvider|\n|`numSectors`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|\n|`physicalSector`| Integer value from `1` to `1000000` |Value obtained from FuzzedDataProvider|\n|`alignment`| Bool |Value obtained from FuzzedDataProvider|\n|`alignment`| Vector |Value obtained from FuzzedDataProvider|\n\n#### Steps to run\n1. Build the fuzzer\n```\n  $ mm -j$(nproc) liblp_apis_fuzzer\n```\n2. Run on device\n```\n  $ adb sync data\n  $ adb shell /data/fuzz/arm64/liblp_apis_fuzzer/liblp_apis_fuzzer\n```\n"
  },
  {
    "path": "fs_mgr/liblp/fuzzer/image_gen_rand.py",
    "content": "#!/usr/bin/env python3\n\n# Copyright (C) 2023 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"\nWrite given number of random bytes, generated with optional seed.\n\"\"\"\n\nimport random, argparse\n\nif __name__ == '__main__':\n  parser = argparse.ArgumentParser(description=__doc__)\n  parser.add_argument('--seed', help='Seed to random generator')\n  parser.add_argument('--length', type=int, required=True, help='Length of output')\n  args = parser.parse_args()\n\n  if args.seed:\n    random.seed(args.seed)\n\n  print(''.join(chr(random.randrange(0,0xff)) for _ in range(args.length)))\n"
  },
  {
    "path": "fs_mgr/liblp/fuzzer/liblp_apis_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/file.h>\n#include <fcntl.h>\n#include <fuzzer/FuzzedDataProvider.h>\n#include <liblp/builder.h>\n#include <liblp/partition_opener.h>\n#include <linux/memfd.h>\n#include <sys/syscall.h>\n#include <writer.h>\n#include \"images.h\"\n#include \"test_partition_opener.h\"\n\nusing namespace std;\nusing namespace android;\nusing namespace android::fs_mgr;\nusing unique_fd = android::base::unique_fd;\n\nstatic constexpr size_t kDiskSize = 131072;\nstatic constexpr size_t kMetadataSize = 512;\nstatic constexpr size_t kMetadataSlots = 2;\nstatic constexpr uint32_t kMaxBytes = 20;\nstatic constexpr uint32_t kValidAlignment = 0;\nstatic constexpr uint32_t kValidAlignmentOffset = 0;\nstatic constexpr uint32_t kValidLogicalBlockSize = 4096;\nstatic constexpr uint32_t kMinMetadataSize = 0;\nstatic constexpr uint32_t kMaxMetadataSize = 10000;\nstatic constexpr uint32_t kMinFactor = 0;\nstatic constexpr uint32_t kMaxFactor = 10;\nstatic constexpr uint32_t kMetadataGeometrySize = 4096;\nstatic constexpr uint64_t kValidNumSectors = 1901568;\nstatic constexpr uint64_t kValidPhysicalSector = 3608576;\nstatic constexpr uint64_t kMinSectorValue = 1;\nstatic constexpr uint64_t kMaxSectorValue = 1000000;\nstatic constexpr uint64_t kMaxBufferSize = 100000;\n\nconst string kImageFile = \"image_file\";\nconst string kSuperName = \"super\";\nconst string kSystemPartitionName = \"system\";\nconst string kPartitionName = \"builder_partition\";\nconst string kSuperPartitionName = \"super_partition\";\n\nconst string kSuffix[] = {\"_a\", \"_b\", \"a\", \"b\"};\n\nclass LiplpApisFuzzer {\n  public:\n    LiplpApisFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};\n    void process();\n\n  private:\n    void setupBuilder();\n    BlockDeviceInfo getBlockDevice();\n    FuzzedDataProvider mFdp;\n    unique_ptr<MetadataBuilder> mBuilder;\n    string mBlockDeviceInfoName;\n    string mSuperPartitionName;\n    string mPartitionName;\n    const string mImagePaths[10] = {\n            \"data/test_dtb.img\",\n            \"data/test_bootconfig.img\",\n            \"data/test_vendor_ramdisk_none.img\",\n            \"data/test_vendor_ramdisk_platform.img\",\n            \"data/test_vendor_ramdisk_replace.img\",\n            \"data/test_vendor_boot_v4_with_frag.img\",\n            \"data/test_vendor_boot_v4_without_frag.img\",\n            \"data/test_vendor_boot_v3.img\",\n            \"dev/null\",\n            mFdp.ConsumeRandomLengthString(kMaxBytes),\n    };\n};\n\nBlockDeviceInfo LiplpApisFuzzer::getBlockDevice() {\n    mBlockDeviceInfoName =\n            mFdp.ConsumeBool() ? kSuperName : mFdp.ConsumeRandomLengthString(kMaxBytes);\n    uint64_t blockDeviceInfoSize =\n            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint64_t>() : kDiskSize;\n    uint32_t alignment = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignment;\n    uint32_t alignmentOffset =\n            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignmentOffset;\n    uint32_t logicalBlockSize =\n            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidLogicalBlockSize;\n\n    BlockDeviceInfo superInfo{mBlockDeviceInfoName, blockDeviceInfoSize, alignment, alignmentOffset,\n                              logicalBlockSize};\n    return superInfo;\n}\n\nvoid LiplpApisFuzzer::setupBuilder() {\n    uint64_t randomBlockDevSize =\n            mFdp.ConsumeIntegralInRange<uint64_t>(kMinFactor, kMaxFactor) * LP_SECTOR_SIZE;\n    uint64_t blockDevSize = mFdp.ConsumeBool() ? randomBlockDevSize : kDiskSize;\n    uint32_t randomMetadataMaxSize =\n            mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataSize, kMaxMetadataSize);\n    uint32_t metadataMaxSize = mFdp.ConsumeBool() ? kMetadataSize : randomMetadataMaxSize;\n    uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;\n    mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);\n\n    if (mBuilder.get()) {\n        mBuilder->AddPartition(kSystemPartitionName, LP_PARTITION_ATTR_READONLY);\n\n        mPartitionName =\n                mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kPartitionName;\n        if (!mPartitionName.size()) {\n            mPartitionName = kPartitionName;\n        }\n        mSuperPartitionName = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes)\n                                                 : kSuperPartitionName;\n        if (!mSuperPartitionName.size()) {\n            mSuperPartitionName = kSuperPartitionName;\n        }\n\n        Partition* super = mBuilder->AddPartition(mSuperPartitionName, LP_PARTITION_ATTR_READONLY);\n        mBuilder->AddPartition(mPartitionName, LP_PARTITION_ATTR_READONLY);\n\n        if (super) {\n            int64_t numSectors = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(\n                                                              kMinSectorValue, kMaxSectorValue)\n                                                    : kValidNumSectors;\n            int64_t physicalSector = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint64_t>(\n                                                                  kMinSectorValue, kMaxSectorValue)\n                                                        : kValidPhysicalSector;\n            mBuilder->AddLinearExtent(super, mBlockDeviceInfoName, numSectors, physicalSector);\n        }\n    }\n}\n\nvoid LiplpApisFuzzer::process() {\n    BlockDeviceInfo superInfo = getBlockDevice();\n    unique_fd fd(syscall(__NR_memfd_create, \"image_file\", MFD_ALLOW_SEALING));\n    setupBuilder();\n\n    TestPartitionOpener opener(\n            {{mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kSuperName, fd}},\n            {{mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kSuperName,\n              superInfo}});\n\n    if (mBuilder.get()) {\n        unique_ptr<LpMetadata> metadata = mBuilder->Export();\n        const LpMetadata& metadataValue = *metadata.get();\n\n        map<string, string> images = {};\n        if (mFdp.ConsumeBool()) {\n            images[mSuperPartitionName] = mFdp.PickValueInArray(mImagePaths);\n        }\n\n        while (mFdp.remaining_bytes()) {\n            auto invokeAPIs = mFdp.PickValueInArray<const function<void()>>({\n                    [&]() { WriteToImageFile(fd, metadataValue); },\n                    [&]() { WriteToImageFile(kImageFile.c_str(), metadataValue); },\n                    [&]() { FlashPartitionTable(opener, kSuperName, metadataValue); },\n                    [&]() {\n                        UpdatePartitionTable(opener, mPartitionName, metadataValue,\n                                             mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);\n                    },\n                    [&]() {\n                        ReadMetadata(mPartitionName, mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);\n                    },\n                    [&]() { FlashPartitionTable(mPartitionName, metadataValue); },\n                    [&]() {\n                        UpdatePartitionTable(mPartitionName, metadataValue,\n                                             mFdp.ConsumeBool() ? 0 : 1 /* slot_number */);\n                    },\n                    [&]() {\n                        WriteToImageFile(kImageFile.c_str(), metadataValue,\n                                         metadata->geometry.logical_block_size, images,\n                                         mFdp.ConsumeBool() ? true : false /* sparsify */);\n                    },\n\n                    [&]() {\n                        WriteSplitImageFiles(kImageFile.c_str(), metadataValue,\n                                             metadata->geometry.logical_block_size, images,\n                                             mFdp.ConsumeBool() ? true : false /* sparsify */);\n                    },\n                    [&]() { ReadFromImageFile(kImageFile.c_str()); },\n                    [&]() { IsEmptySuperImage(kImageFile.c_str()); },\n                    [&]() {\n                        uint64_t bufferSize = mFdp.ConsumeIntegralInRange<uint64_t>(\n                                2 * kMetadataGeometrySize, kMaxBufferSize);\n                        vector<uint8_t> buffer = mFdp.ConsumeBytes<uint8_t>(kMaxBytes);\n                        buffer.resize(bufferSize);\n                        ReadFromImageBlob(buffer.data(), buffer.size());\n                    },\n                    [&]() {\n                        uint32_t groupVectorSize = metadata->groups.size();\n                        uint32_t randomGroupIndex =\n                                mFdp.ConsumeIntegralInRange<uint32_t>(0, groupVectorSize - 1);\n                        GetPartitionGroupName(metadata->groups[randomGroupIndex]);\n                    },\n                    [&]() {\n                        uint32_t blockDeviceVectorSize = metadata->block_devices.size();\n                        uint32_t randomBlockDeviceIndex =\n                                mFdp.ConsumeIntegralInRange<uint32_t>(0, blockDeviceVectorSize - 1);\n                        GetBlockDevicePartitionName(\n                                metadata->block_devices[randomBlockDeviceIndex]);\n                    },\n                    [&]() { GetMetadataSuperBlockDevice(metadataValue); },\n                    [&]() {\n                        string suffix = mFdp.ConsumeBool()\n                                                ? mFdp.PickValueInArray<string>(kSuffix)\n                                                : mFdp.ConsumeRandomLengthString(kMaxBytes);\n                        SlotNumberForSlotSuffix(suffix);\n                    },\n                    [&]() {\n                        auto entry = FindPartition(metadataValue, kSystemPartitionName);\n                        GetPartitionSize(metadataValue, *entry);\n                    },\n                    [&]() { GetPartitionSlotSuffix(mPartitionName); },\n                    [&]() { FindPartition(metadataValue, mPartitionName); },\n                    [&]() {\n                        uint32_t partitionVectorSize = metadata->partitions.size();\n                        uint32_t randomPartitionIndex =\n                                mFdp.ConsumeIntegralInRange<uint32_t>(0, partitionVectorSize - 1);\n                        GetPartitionName(metadata->partitions[randomPartitionIndex]);\n                    },\n                    [&]() { GetTotalSuperPartitionSize(metadataValue); },\n                    [&]() { GetBlockDevicePartitionNames(metadataValue); },\n            });\n            invokeAPIs();\n        }\n        remove(kImageFile.c_str());\n    }\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    LiplpApisFuzzer liplpApisFuzzer(data, size);\n    liplpApisFuzzer.process();\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/liblp/fuzzer/liblp_builder_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n#include <functional>\n#include <fuzzer/FuzzedDataProvider.h>\n#include <liblp/builder.h>\n#include <liblp/property_fetcher.h>\n#include <storage_literals/storage_literals.h>\n\nusing namespace android::fs_mgr;\nusing namespace std;\nusing namespace android::storage_literals;\n\nstatic constexpr uint64_t kValidBlockSize = 4096 * 50;\nstatic constexpr uint64_t kBlockDeviceInfoSize = 1024 * 1024;\nstatic constexpr uint64_t kValidBlockDeviceInfoSize = 8_GiB;\nstatic constexpr uint64_t kValidMaxGroupSize = 40960;\nstatic constexpr uint64_t kMinBlockDevValue = 1;\nstatic constexpr uint64_t kMaxBlockDevValue = 100000;\nstatic constexpr uint64_t kMinSectorValue = 1;\nstatic constexpr uint64_t kMaxSectorValue = 1000000;\nstatic constexpr uint64_t kMinValue = 0;\nstatic constexpr uint64_t kMaxValue = 10000;\nstatic constexpr uint64_t kValidNumSectors = 1901568;\nstatic constexpr uint64_t kValidPhysicalSector = 3608576;\nstatic constexpr uint64_t kMinElements = 0;\nstatic constexpr uint64_t kMaxElements = 10;\nstatic constexpr uint32_t kValidAlignment = 786432;\nstatic constexpr uint32_t kValidMetadataSize = 40960;\nstatic constexpr uint32_t kValidAlignmentOffset = 229376;\nstatic constexpr uint32_t kValidLogicalBlockSize = 4096;\nstatic constexpr uint32_t kValidMaxMetadataSize = 65536;\nstatic constexpr uint32_t kMinMetadataValue = 0;\nstatic constexpr uint32_t kMaxMetadataValue = 10000;\nstatic constexpr uint32_t kZeroAlignment = 0;\nstatic constexpr uint32_t kZeroAlignmentOffset = 0;\nstatic constexpr uint32_t kMaxBytes = 20;\nstatic constexpr uint32_t kMinBuilder = 0;\nstatic constexpr uint32_t kMaxBuilder = 4;\n\nconst uint64_t kAttributeTypes[] = {\n        LP_PARTITION_ATTR_NONE,    LP_PARTITION_ATTR_READONLY, LP_PARTITION_ATTR_SLOT_SUFFIXED,\n        LP_PARTITION_ATTR_UPDATED, LP_PARTITION_ATTR_DISABLED,\n};\n\nconst string kFuzzPartitionName = \"fuzz_partition_name\";\nconst string kSuperPartitionName = \"super_partition\";\nconst string kDeviceInfoName = \"super\";\nconst string kDefaultGroupName = \"default\";\n\nclass BuilderFuzzer {\n  public:\n    BuilderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};\n    void process();\n\n  private:\n    FuzzedDataProvider mFdp;\n    void invokeBuilderAPIs();\n    void selectRandomBuilder(int32_t randomBuilder, string superBlockDeviceName);\n    void setupBuilder(string superBlockDeviceName);\n    void callChangePartitionGroup();\n    void callVerifyExtentsAgainstSourceMetadata();\n    vector<BlockDeviceInfo> mBlockDevices;\n    unique_ptr<MetadataBuilder> mBuilder;\n    string mResizePartitionName;\n    string mGroupNames[4] = {\n            \"default\",\n            \"group_a\",\n            \"group_b\",\n            mFdp.ConsumeRandomLengthString(kMaxBytes),\n    };\n    string mPartitionNames[5] = {\n            \"system_a\",\n            \"vendor_a\",\n            \"system_b\",\n            \"vendor_b\",\n            mFdp.ConsumeRandomLengthString(kMaxBytes),\n    };\n    Partition* mPartition;\n    Partition* mFuzzPartition;\n    Partition* mResizePartition;\n    template <typename T>\n    T getParamValue(T validValue) {\n        T parameter = validValue;\n        if (mFdp.ConsumeBool()) {\n            parameter = mFdp.ConsumeIntegralInRange<T>(kMinValue, kMaxValue);\n        }\n        return parameter;\n    }\n};\n\nvoid BuilderFuzzer::selectRandomBuilder(int32_t randomBuilder, string superBlockDeviceName) {\n    switch (randomBuilder) {\n        case 0: {\n            uint32_t maxMetadataSize = getParamValue(kValidMaxMetadataSize);\n            uint32_t numSlots = mFdp.ConsumeBool() ? 0 : 1;\n            mBuilder = MetadataBuilder::New(mBlockDevices, superBlockDeviceName, maxMetadataSize,\n                                            numSlots);\n            break;\n        }\n        case 1: {\n            uint64_t blockDevSize =\n                    mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);\n            uint32_t metadataMaxSize =\n                    mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);\n            uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;\n            mBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);\n            break;\n        }\n        case 2: {\n            uint64_t blockDevSize = getParamValue(kValidBlockSize);\n            uint32_t metadataSize = getParamValue(kValidMetadataSize);\n            uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;\n            mBuilder = MetadataBuilder::New(blockDevSize, metadataSize, metadataSlotCount);\n            break;\n        }\n        case 3: {\n            string superPartitionName = mFdp.ConsumeBool()\n                                                ? kSuperPartitionName\n                                                : mFdp.ConsumeRandomLengthString(kMaxBytes);\n            mBuilder = MetadataBuilder::New(PartitionOpener(), superPartitionName,\n                                            mFdp.ConsumeIntegralInRange(0, 1) /* slot_number */);\n            break;\n        }\n        case 4: {\n            string superPartitionName = mFdp.ConsumeBool()\n                                                ? kSuperPartitionName\n                                                : mFdp.ConsumeRandomLengthString(kMaxBytes);\n            mBuilder = MetadataBuilder::New(\n                    superPartitionName,\n                    mFdp.ConsumeIntegralInRange<uint32_t>(0, 1) /* slot_number */);\n            break;\n        }\n    }\n}\n\nvoid BuilderFuzzer::setupBuilder(string superBlockDeviceName) {\n    uint64_t blockDeviceInfoSize =\n            mFdp.ConsumeBool()\n                    ? mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue)\n                    : kValidBlockDeviceInfoSize;\n    uint32_t alignment = mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignment;\n    uint32_t alignmentOffset =\n            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>() : kValidAlignmentOffset;\n    uint32_t logicalBlockSize = mFdp.ConsumeBool() ? mFdp.ConsumeIntegralInRange<uint32_t>(\n                                                             kMinBlockDevValue, kMaxBlockDevValue)\n                                                   : kValidLogicalBlockSize;\n\n    BlockDeviceInfo super(superBlockDeviceName, blockDeviceInfoSize, alignment, alignmentOffset,\n                          logicalBlockSize);\n    mBlockDevices.push_back(super);\n\n    mBuilder->AddGroup(kDefaultGroupName, mFdp.ConsumeIntegral<uint64_t>() /* max_size */);\n    mPartition = mBuilder->AddPartition(kSuperPartitionName, LP_PARTITION_ATTR_READONLY);\n\n    mFuzzPartition = mBuilder->AddPartition(kFuzzPartitionName, kDefaultGroupName,\n                                            LP_PARTITION_ATTR_READONLY);\n\n    string mResizePartitionName = mFdp.ConsumeRandomLengthString(kMaxBytes);\n    if (!mResizePartitionName.size()) {\n        mResizePartitionName = \"resize_partition\";\n    }\n    mResizePartition = mBuilder->AddPartition(mResizePartitionName, kDefaultGroupName,\n                                              LP_PARTITION_ATTR_READONLY);\n\n    string changePartitionDeviceInfoName =\n            mFdp.ConsumeBool() ? kDeviceInfoName : mFdp.ConsumeRandomLengthString(kMaxBytes);\n    BlockDeviceInfo changePartitionDeviceInfo(\n            changePartitionDeviceInfoName,\n            mFdp.ConsumeBool()\n                    ? mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue)\n                    : kBlockDeviceInfoSize /* size */,\n            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()\n                               : kZeroAlignmentOffset /* alignment */,\n            mFdp.ConsumeBool() ? mFdp.ConsumeIntegral<uint32_t>()\n                               : kZeroAlignmentOffset /* alignment_offset */,\n            mFdp.ConsumeBool()\n                    ? mFdp.ConsumeIntegralInRange<uint32_t>(kMinBlockDevValue, kMaxBlockDevValue)\n                    : kValidLogicalBlockSize);\n    mBlockDevices.push_back(changePartitionDeviceInfo);\n}\n\nvoid BuilderFuzzer::callChangePartitionGroup() {\n    string group1 = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : \"group1\";\n    uint64_t group1Size = getParamValue(0);\n\n    string group2 = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : \"group2\";\n    uint64_t group2Size = getParamValue(0);\n\n    bool group1Added = mBuilder->AddGroup(group1, group1Size);\n    bool group2Added = mBuilder->AddGroup(group2, group2Size);\n\n    string changeGroupPartitionName = mFdp.ConsumeRandomLengthString(kMaxBytes);\n    if (changeGroupPartitionName.size() && group1Added && group2Added) {\n        Partition* changeGroupPartition = mBuilder->AddPartition(changeGroupPartitionName, group1,\n                                                                 LP_PARTITION_ATTR_READONLY);\n        if (changeGroupPartition) {\n            mBuilder->ChangePartitionGroup(changeGroupPartition, group2);\n        }\n    }\n}\n\nvoid BuilderFuzzer::callVerifyExtentsAgainstSourceMetadata() {\n    uint64_t sourceBlockDevSize = getParamValue(kValidBlockSize);\n    uint32_t sourceMetadataMaxSize = getParamValue(kValidMetadataSize);\n    uint32_t sourceSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);\n    auto sourceBuilder =\n            MetadataBuilder::New(sourceBlockDevSize, sourceMetadataMaxSize, sourceSlotCount);\n\n    uint64_t targetBlockDevSize = getParamValue(kValidBlockSize);\n    uint32_t targetMetadataMaxSize = getParamValue(kValidMetadataSize);\n    uint32_t targetSlotCount = mFdp.ConsumeIntegralInRange<uint32_t>(0, 1);\n    auto targetBuilder =\n            MetadataBuilder::New(targetBlockDevSize, targetMetadataMaxSize, targetSlotCount);\n\n    if (sourceBuilder && targetBuilder) {\n        int64_t sourceGroups = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);\n        for (int64_t idx = 0; idx < sourceGroups; ++idx) {\n            sourceBuilder->AddGroup(\n                    mFdp.PickValueInArray(mGroupNames),\n                    mFdp.ConsumeBool() ? kValidMaxGroupSize : mFdp.ConsumeIntegral<uint64_t>());\n        }\n\n        int64_t sourcePartitions = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);\n        for (int64_t idx = 0; idx < sourcePartitions; ++idx) {\n            sourceBuilder->AddPartition(mFdp.PickValueInArray(mPartitionNames),\n                                        LP_PARTITION_ATTR_READONLY);\n        }\n\n        int64_t targetGroups = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);\n        for (int64_t idx = 0; idx < targetGroups; ++idx) {\n            targetBuilder->AddGroup(\n                    mFdp.PickValueInArray(mGroupNames),\n                    mFdp.ConsumeBool() ? kValidMaxGroupSize : mFdp.ConsumeIntegral<uint64_t>());\n        }\n\n        int64_t targetPartitions = mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);\n        for (int64_t idx = 0; idx < targetPartitions; ++idx) {\n            targetBuilder->AddPartition(mFdp.PickValueInArray(mPartitionNames),\n                                        LP_PARTITION_ATTR_READONLY);\n        }\n\n        MetadataBuilder::VerifyExtentsAgainstSourceMetadata(\n                *sourceBuilder, mFdp.ConsumeBool() ? 0 : 1 /* source_slot_number */, *targetBuilder,\n                mFdp.ConsumeBool() ? 0 : 1 /* target_slot_number */,\n                vector<string>{\"system\", \"vendor\", mFdp.ConsumeRandomLengthString(kMaxBytes)});\n    }\n}\n\nvoid BuilderFuzzer::invokeBuilderAPIs() {\n    string superBlockDeviceName =\n            mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes) : kDeviceInfoName;\n    uint32_t randomBuilder = mFdp.ConsumeIntegralInRange<uint32_t>(kMinBuilder, kMaxBuilder);\n    selectRandomBuilder(randomBuilder, superBlockDeviceName);\n\n    if (mBuilder.get()) {\n        setupBuilder(superBlockDeviceName);\n\n        while (mFdp.remaining_bytes()) {\n            auto invokeAPIs = mFdp.PickValueInArray<const function<void()>>({\n                    [&]() { callChangePartitionGroup(); },\n                    [&]() {\n                        string addedGroupName = mFdp.PickValueInArray(mGroupNames);\n                        mBuilder->AddGroup(addedGroupName,\n                                           mFdp.ConsumeIntegralInRange<uint64_t>(\n                                                   kMinValue, kMaxValue) /* max_size */);\n                    },\n                    [&]() {\n                        string partitionName = mFdp.PickValueInArray(mPartitionNames);\n                        Partition* addedPartition = mBuilder->AddPartition(\n                                partitionName, mFdp.PickValueInArray(kAttributeTypes));\n                    },\n                    [&]() {\n                        int64_t numSectors = mFdp.ConsumeBool()\n                                                     ? mFdp.ConsumeIntegralInRange<uint64_t>(\n                                                               kMinSectorValue, kMaxSectorValue)\n                                                     : kValidNumSectors;\n                        int64_t physicalSector = mFdp.ConsumeBool()\n                                                         ? mFdp.ConsumeIntegralInRange<uint64_t>(\n                                                                   kMinSectorValue, kMaxSectorValue)\n                                                         : kValidPhysicalSector;\n\n                        int64_t numExtents =\n                                mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);\n                        if (mFuzzPartition) {\n                            bool extentAdded = false;\n                            for (int64_t i = 0; i <= numExtents; ++i) {\n                                extentAdded =\n                                        mBuilder->AddLinearExtent(mFuzzPartition, kDeviceInfoName,\n                                                                  numSectors, physicalSector);\n                            }\n\n                            if (extentAdded) {\n                                unique_ptr<LpMetadata> metadata = mBuilder->Export();\n                                uint64_t alignedSize =\n                                        mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);\n                                mFuzzPartition->GetBeginningExtents(LP_SECTOR_SIZE * numExtents);\n                            }\n                        }\n                    },\n                    [&]() { callVerifyExtentsAgainstSourceMetadata(); },\n                    [&]() { mBuilder->ListPartitionsInGroup(mFdp.PickValueInArray(mGroupNames)); },\n                    [&]() {\n                        int64_t maxSize = mFdp.ConsumeIntegral<uint64_t>();\n                        mBuilder->ChangeGroupSize(mFdp.PickValueInArray(mGroupNames), maxSize);\n                    },\n                    [&]() {\n                        string deviceInfoName = mFdp.ConsumeBool()\n                                                        ? kDeviceInfoName\n                                                        : mFdp.ConsumeRandomLengthString(kMaxBytes);\n                        mBuilder->GetBlockDeviceInfo(deviceInfoName, &mBlockDevices[1]);\n                    },\n                    [&]() {\n                        string deviceInfoName = mFdp.ConsumeBool()\n                                                        ? kDeviceInfoName\n                                                        : mFdp.ConsumeRandomLengthString(kMaxBytes);\n                        mBuilder->UpdateBlockDeviceInfo(deviceInfoName, mBlockDevices[1]);\n                    },\n                    [&]() {\n                        unique_ptr<LpMetadata> metadata = mBuilder->Export();\n                        mBuilder->ImportPartitions(*metadata.get(),\n                                                   {mFdp.PickValueInArray(mPartitionNames)});\n                    },\n                    [&]() { mBuilder->HasBlockDevice(mFdp.PickValueInArray(mPartitionNames)); },\n                    [&]() { mBuilder->SetVirtualABDeviceFlag(); },\n                    [&]() { mBuilder->SetAutoSlotSuffixing(); },\n                    [&]() { mBuilder->ListGroups(); },\n                    [&]() { mBuilder->UsedSpace(); },\n                    [&]() { mBuilder->RequireExpandedMetadataHeader(); },\n                    [&]() {\n                        uint64_t resizedPartitionSize = getParamValue(0);\n                        mBuilder->ResizePartition(mResizePartition, resizedPartitionSize);\n                    },\n                    [&]() {\n                        uint32_t sourceSlot = mFdp.ConsumeBool() ? 0 : 1;\n                        uint32_t targetSlot = mFdp.ConsumeBool() ? 0 : 1;\n                        PartitionOpener partitionOpener;\n                        string sourcePartition =\n                                mFdp.ConsumeBool() ? kFuzzPartitionName : kDeviceInfoName;\n\n                        MetadataBuilder::NewForUpdate(partitionOpener, sourcePartition, sourceSlot,\n                                                      targetSlot);\n                        partitionOpener.GetDeviceString(mFdp.PickValueInArray(mPartitionNames));\n                    },\n                    [&]() {\n                        unique_ptr<LpMetadata> metadata = mBuilder->Export();\n                        MetadataBuilder::New(*metadata.get());\n                    },\n                    [&]() { mBuilder->AllocatableSpace(); },\n                    [&]() {\n                        PartitionOpener pOpener;\n                        string superPartitionName =\n                                mFdp.ConsumeBool() ? kSuperPartitionName\n                                                   : mFdp.ConsumeRandomLengthString(kMaxBytes);\n                        pOpener.Open(superPartitionName, O_RDONLY);\n                        pOpener.GetInfo(superPartitionName, &mBlockDevices[0]);\n                    },\n                    [&]() {\n                        PartitionOpener pOpener;\n                        string superPartitionName =\n                                mFdp.ConsumeBool() ? kSuperPartitionName\n                                                   : mFdp.ConsumeRandomLengthString(kMaxBytes);\n                        pOpener.Open(superPartitionName, O_RDONLY);\n                        pOpener.GetDeviceString(superPartitionName);\n                    },\n                    [&]() {\n                        Interval::Intersect(\n                                Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,\n                                         mFdp.ConsumeIntegral<uint64_t>() /* start */,\n                                         mFdp.ConsumeIntegral<uint64_t>()) /* end */,\n                                Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,\n                                         mFdp.ConsumeIntegral<uint64_t>() /* start */,\n                                         mFdp.ConsumeIntegral<uint64_t>() /* end */));\n                    },\n                    [&]() {\n                        vector<Interval> intervalVectorA;\n                        int64_t internalVectorAElements =\n                                mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);\n                        for (int64_t idx = 0; idx < internalVectorAElements; ++idx) {\n                            intervalVectorA.push_back(\n                                    Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,\n                                             mFdp.ConsumeIntegral<uint64_t>() /* start */,\n                                             mFdp.ConsumeIntegral<uint64_t>() /* end */));\n                        }\n\n                        vector<Interval> intervalVectorB;\n                        int64_t internalVectorBElements =\n                                mFdp.ConsumeIntegralInRange<int64_t>(kMinElements, kMaxElements);\n                        for (int64_t idx = 0; idx < internalVectorBElements; ++idx) {\n                            intervalVectorB.push_back(\n                                    Interval(mFdp.ConsumeIntegral<uint64_t>() /* device _index */,\n                                             mFdp.ConsumeIntegral<uint64_t>() /* start */,\n                                             mFdp.ConsumeIntegral<uint64_t>() /* end */));\n                        }\n\n                        Interval::Intersect(intervalVectorA, intervalVectorB);\n                    },\n                    [&]() {\n                        uint64_t numSectors =\n                                mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);\n                        uint32_t deviceIndex =\n                                mFdp.ConsumeIntegralInRange<uint32_t>(kMinValue, kMaxValue);\n                        uint64_t physicalSector =\n                                mFdp.ConsumeIntegralInRange<uint64_t>(kMinValue, kMaxValue);\n                        LinearExtent extent(numSectors, deviceIndex, physicalSector);\n                        extent.AsInterval();\n                    },\n                    [&]() {\n                        IPropertyFetcher::OverrideForTesting(std::make_unique<PropertyFetcher>());\n                    },\n            });\n            invokeAPIs();\n        }\n        if (mFdp.ConsumeBool()) {\n            mBuilder->RemoveGroupAndPartitions(mFdp.PickValueInArray(mGroupNames));\n        } else {\n            string removePartition = mFdp.PickValueInArray(mPartitionNames);\n            mBuilder->RemovePartition(removePartition);\n        }\n    }\n}\n\nvoid BuilderFuzzer::process() {\n    invokeBuilderAPIs();\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    BuilderFuzzer builderFuzzer(data, size);\n    builderFuzzer.process();\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/liblp/fuzzer/liblp_super_layout_builder_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at:\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\n#include <android-base/unique_fd.h>\n#include <fcntl.h>\n#include <functional>\n#include <fuzzer/FuzzedDataProvider.h>\n#include <liblp/metadata_format.h>\n#include <liblp/super_layout_builder.h>\n#include <linux/memfd.h>\n#include <storage_literals/storage_literals.h>\n#include <sys/syscall.h>\n\nusing namespace android::fs_mgr;\nusing namespace std;\nusing unique_fd = android::base::unique_fd;\nusing namespace android::storage_literals;\n\nstatic constexpr uint64_t kSuperLayoutValidBlockDevSize = 4_MiB;\nstatic constexpr uint64_t kMinBlockDevValue = 0;\nstatic constexpr uint64_t kMaxBlockDevValue = 100000;\nstatic constexpr uint64_t kMinElements = 0;\nstatic constexpr uint64_t kMaxElements = 10;\nstatic constexpr uint32_t kSuperLayoutValidMetadataSize = 8_KiB;\nstatic constexpr uint32_t kMinMetadataValue = 0;\nstatic constexpr uint32_t kMaxMetadataValue = 10000;\nstatic constexpr uint32_t kMaxBytes = 20;\nstatic constexpr uint32_t kMinOpen = 0;\nstatic constexpr uint32_t kMaxOpen = 2;\n\nconst uint64_t kAttributeTypes[] = {\n        LP_PARTITION_ATTR_NONE,    LP_PARTITION_ATTR_READONLY, LP_PARTITION_ATTR_SLOT_SUFFIXED,\n        LP_PARTITION_ATTR_UPDATED, LP_PARTITION_ATTR_DISABLED,\n};\n\nclass SuperLayoutBuilderFuzzer {\n  public:\n    SuperLayoutBuilderFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){};\n    void process();\n\n  private:\n    FuzzedDataProvider mFdp;\n    void invokeSuperLayoutBuilderAPIs();\n    void callRandomOpen(int32_t open);\n    void addMultiplePartitions(int32_t numPartitions);\n    void setupSuperLayoutBuilder();\n    SuperLayoutBuilder mSuperLayoutBuilder;\n    unique_ptr<MetadataBuilder> mSuperBuilder;\n    unique_ptr<LpMetadata> mMetadata;\n    bool mOpenSuccess = false;\n};\n\nvoid SuperLayoutBuilderFuzzer::setupSuperLayoutBuilder() {\n    uint64_t randomBlockDevSize =\n            mFdp.ConsumeIntegralInRange<uint64_t>(kMinBlockDevValue, kMaxBlockDevValue);\n    uint64_t blockDevSize = mFdp.ConsumeBool() ? kSuperLayoutValidBlockDevSize : randomBlockDevSize;\n    uint32_t randomMetadataMaxSize =\n            mFdp.ConsumeIntegralInRange<uint32_t>(kMinMetadataValue, kMaxMetadataValue);\n    uint32_t metadataMaxSize =\n            mFdp.ConsumeBool() ? kSuperLayoutValidMetadataSize : randomMetadataMaxSize;\n    uint32_t metadataSlotCount = mFdp.ConsumeBool() ? 0 : 1;\n    mSuperBuilder = MetadataBuilder::New(blockDevSize, metadataMaxSize, metadataSlotCount);\n\n    if (mSuperBuilder.get()) {\n        if (mFdp.ConsumeBool()) {\n            int32_t numPartitions =\n                    mFdp.ConsumeIntegralInRange<int32_t>(kMinElements, kMaxElements);\n            addMultiplePartitions(numPartitions);\n        }\n\n        uint32_t randomOpen = mFdp.ConsumeIntegralInRange<uint32_t>(kMinOpen, kMaxOpen);\n        callRandomOpen(randomOpen);\n    }\n}\n\nvoid SuperLayoutBuilderFuzzer::addMultiplePartitions(int32_t numPartitions) {\n    for (int32_t idx = 0; idx < numPartitions; ++idx) {\n        string partitionName = mFdp.ConsumeBool() ? mFdp.ConsumeRandomLengthString(kMaxBytes)\n                                                  : \"builder_partition\";\n        mSuperBuilder->AddPartition(partitionName, mFdp.PickValueInArray(kAttributeTypes));\n    }\n}\n\nvoid SuperLayoutBuilderFuzzer::callRandomOpen(int32_t open) {\n    mMetadata = mSuperBuilder->Export();\n    switch (open) {\n        case 0: {\n            vector<uint8_t> imageData = mFdp.ConsumeBytes<uint8_t>(kMaxBytes);\n            mOpenSuccess = mSuperLayoutBuilder.Open((void*)(imageData.data()), imageData.size());\n            break;\n        }\n        case 1: {\n            mOpenSuccess = mSuperLayoutBuilder.Open(*mMetadata.get());\n            break;\n        }\n        case 2: {\n            unique_fd fd(syscall(__NR_memfd_create, \"image_file\", 0));\n            WriteToImageFile(fd, *mMetadata.get());\n            mOpenSuccess = mSuperLayoutBuilder.Open(fd);\n            break;\n        }\n    }\n}\n\nvoid SuperLayoutBuilderFuzzer::invokeSuperLayoutBuilderAPIs() {\n    string imageName = mFdp.ConsumeRandomLengthString(kMaxBytes);\n    string fuzzPartitionName =\n            mFdp.ConsumeBool() ? \"builder_partition\" : mFdp.ConsumeRandomLengthString(kMaxBytes);\n    if (!fuzzPartitionName.size()) {\n        fuzzPartitionName = \"builder_partition\";\n    }\n    setupSuperLayoutBuilder();\n    if (mOpenSuccess) {\n        while (mFdp.remaining_bytes()) {\n            auto invokeSuperAPIs = mFdp.PickValueInArray<const function<void()>>({\n                    [&]() { mSuperLayoutBuilder.GetImageLayout(); },\n                    [&]() {\n                        mSuperLayoutBuilder.AddPartition(fuzzPartitionName, imageName,\n                                                         mFdp.ConsumeIntegral<uint64_t>());\n                    },\n            });\n            invokeSuperAPIs();\n        }\n    }\n}\n\nvoid SuperLayoutBuilderFuzzer::process() {\n    invokeSuperLayoutBuilderAPIs();\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    SuperLayoutBuilderFuzzer superLayoutBuilderFuzzer(data, size);\n    superLayoutBuilderFuzzer.process();\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/liblp/images.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"images.h\"\n\n#include <limits.h>\n#include <sys/stat.h>\n\n#include <android-base/file.h>\n\n#include \"reader.h\"\n#include \"utility.h\"\n#include \"writer.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nusing android::base::borrowed_fd;\nusing android::base::unique_fd;\n\n#if defined(_WIN32)\nstatic const int O_NOFOLLOW = 0;\n#endif\n\nstatic bool IsEmptySuperImage(borrowed_fd fd) {\n    struct stat s;\n    if (fstat(fd.get(), &s) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" fstat failed\";\n        return false;\n    }\n    if (s.st_size < LP_METADATA_GEOMETRY_SIZE) {\n        return false;\n    }\n\n    // Rewind back to the start, read the geometry struct.\n    LpMetadataGeometry geometry = {};\n    if (SeekFile64(fd.get(), 0, SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed\";\n        return false;\n    }\n    if (!android::base::ReadFully(fd, &geometry, sizeof(geometry))) {\n        PERROR << __PRETTY_FUNCTION__ << \" read failed\";\n        return false;\n    }\n    return geometry.magic == LP_METADATA_GEOMETRY_MAGIC;\n}\n\nbool IsEmptySuperImage(const std::string& file) {\n    unique_fd fd = GetControlFileOrOpen(file, O_RDONLY | O_CLOEXEC);\n    if (fd < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" open failed\";\n        return false;\n    }\n    return IsEmptySuperImage(fd);\n}\n\nstd::unique_ptr<LpMetadata> ReadFromImageFile(int fd) {\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);\n    if (SeekFile64(fd, 0, SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed\";\n        return nullptr;\n    }\n    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {\n        PERROR << __PRETTY_FUNCTION__ << \" read failed\";\n        return nullptr;\n    }\n    LpMetadataGeometry geometry;\n    if (!ParseGeometry(buffer.get(), &geometry)) {\n        return nullptr;\n    }\n    return ParseMetadata(geometry, fd);\n}\n\nstd::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes) {\n    if (bytes < LP_METADATA_GEOMETRY_SIZE) {\n        LERROR << __PRETTY_FUNCTION__ << \": \" << bytes << \" is smaller than geometry header\";\n        return nullptr;\n    }\n\n    LpMetadataGeometry geometry;\n    if (!ParseGeometry(data, &geometry)) {\n        return nullptr;\n    }\n\n    const uint8_t* metadata_buffer =\n            reinterpret_cast<const uint8_t*>(data) + LP_METADATA_GEOMETRY_SIZE;\n    size_t metadata_buffer_size = bytes - LP_METADATA_GEOMETRY_SIZE;\n    return ParseMetadata(geometry, metadata_buffer, metadata_buffer_size);\n}\n\nstd::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file) {\n    unique_fd fd = GetControlFileOrOpen(image_file.c_str(), O_RDONLY | O_CLOEXEC);\n    if (fd < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" open failed: \" << image_file;\n        return nullptr;\n    }\n    return ReadFromImageFile(fd);\n}\n\nbool WriteToImageFile(borrowed_fd fd, const LpMetadata& input) {\n    std::string geometry = SerializeGeometry(input.geometry);\n    std::string metadata = SerializeMetadata(input);\n\n    std::string everything = geometry + metadata;\n\n    if (!android::base::WriteFully(fd, everything.data(), everything.size())) {\n        PERROR << __PRETTY_FUNCTION__ << \" write \" << everything.size() << \" bytes failed\";\n        return false;\n    }\n    return true;\n}\n\n#if !defined(_WIN32)\nbool FsyncDirectory(const char* dirname) {\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname, O_RDONLY | O_CLOEXEC)));\n    if (fd == -1) {\n        PLOG(ERROR) << \"Failed to open \" << dirname;\n        return false;\n    }\n    if (fsync(fd) == -1) {\n        if (errno == EROFS || errno == EINVAL) {\n            PLOG(WARNING) << \"Skip fsync \" << dirname\n                          << \" on a file system does not support synchronization\";\n        } else {\n            PLOG(ERROR) << \"Failed to fsync \" << dirname;\n            return false;\n        }\n    }\n    return true;\n}\n#endif\n\nbool WriteToImageFile(const std::string& file, const LpMetadata& input) {\n    const auto parent_dir = base::Dirname(file);\n    TemporaryFile tmpfile(parent_dir);\n    if (!WriteToImageFile(tmpfile.fd, input)) {\n        PLOG(ERROR) << \"Failed to write geometry data to tmpfile \" << tmpfile.path;\n        return false;\n    }\n\n#if !defined(_WIN32)\n    fsync(tmpfile.fd);\n#endif\n    const auto err = rename(tmpfile.path, file.c_str());\n    if (err != 0) {\n        PLOG(ERROR) << \"Failed to rename tmp geometry file \" << tmpfile.path << \" to \" << file;\n        return false;\n    }\n#if !defined(_WIN32)\n    FsyncDirectory(parent_dir.c_str());\n#endif\n    return true;\n}\n\nImageBuilder::ImageBuilder(const LpMetadata& metadata, uint32_t block_size,\n                           const std::map<std::string, std::string>& images, bool sparsify)\n    : metadata_(metadata),\n      geometry_(metadata.geometry),\n      block_size_(block_size),\n      sparsify_(sparsify),\n      images_(images) {\n    uint64_t total_size = GetTotalSuperPartitionSize(metadata);\n    if (block_size % LP_SECTOR_SIZE != 0) {\n        LERROR << \"Block size must be a multiple of the sector size, \" << LP_SECTOR_SIZE;\n        return;\n    }\n    if (total_size % block_size != 0) {\n        LERROR << \"Device size must be a multiple of the block size, \" << block_size;\n        return;\n    }\n    if (metadata.geometry.metadata_max_size % block_size != 0) {\n        LERROR << \"Metadata max size must be a multiple of the block size, \" << block_size;\n        return;\n    }\n    if (LP_METADATA_GEOMETRY_SIZE % block_size != 0) {\n        LERROR << \"Geometry size is not a multiple of the block size, \" << block_size;\n        return;\n    }\n    if (LP_PARTITION_RESERVED_BYTES % block_size != 0) {\n        LERROR << \"Reserved size is not a multiple of the block size, \" << block_size;\n        return;\n    }\n\n    uint64_t num_blocks = total_size / block_size;\n    if (num_blocks >= UINT_MAX) {\n        // libsparse counts blocks in unsigned 32-bit integers, so we check to\n        // make sure we're not going to overflow.\n        LERROR << \"Block device is too large to encode with libsparse.\";\n        return;\n    }\n\n    for (const auto& block_device : metadata.block_devices) {\n        SparsePtr file(sparse_file_new(block_size_, block_device.size), sparse_file_destroy);\n        if (!file) {\n            LERROR << \"Could not allocate sparse file of size \" << block_device.size;\n            return;\n        }\n        device_images_.emplace_back(std::move(file));\n    }\n}\n\nbool ImageBuilder::IsValid() const {\n    return device_images_.size() == metadata_.block_devices.size();\n}\n\nbool ImageBuilder::Export(const std::string& file) {\n    unique_fd fd(open(file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_BINARY, 0644));\n    if (fd < 0) {\n        PERROR << \"open failed: \" << file;\n        return false;\n    }\n    if (device_images_.size() > 1) {\n        LERROR << \"Cannot export to a single image on retrofit builds.\";\n        return false;\n    }\n    // No gzip compression; no checksum.\n    int ret = sparse_file_write(device_images_[0].get(), fd, false, sparsify_, false);\n    if (ret != 0) {\n        LERROR << \"sparse_file_write failed (error code \" << ret << \")\";\n        return false;\n    }\n    return true;\n}\n\nbool ImageBuilder::ExportFiles(const std::string& output_dir) {\n    for (size_t i = 0; i < device_images_.size(); i++) {\n        std::string name = GetBlockDevicePartitionName(metadata_.block_devices[i]);\n        std::string file_name = \"super_\" + name + \".img\";\n        std::string file_path = output_dir + \"/\" + file_name;\n\n        static const int kOpenFlags =\n                O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;\n        unique_fd fd(open(file_path.c_str(), kOpenFlags, 0644));\n        if (fd < 0) {\n            PERROR << \"open failed: \" << file_path;\n            return false;\n        }\n        // No gzip compression; no checksum.\n        int ret = sparse_file_write(device_images_[i].get(), fd, false, sparsify_, false);\n        if (ret != 0) {\n            LERROR << \"sparse_file_write failed (error code \" << ret << \")\";\n            return false;\n        }\n    }\n    return true;\n}\n\nbool ImageBuilder::AddData(sparse_file* file, const std::string& blob, uint64_t sector) {\n    uint32_t block;\n    if (!SectorToBlock(sector, &block)) {\n        return false;\n    }\n    void* data = const_cast<char*>(blob.data());\n    int ret = sparse_file_add_data(file, data, blob.size(), block);\n    if (ret != 0) {\n        LERROR << \"sparse_file_add_data failed (error code \" << ret << \")\";\n        return false;\n    }\n    return true;\n}\n\nbool ImageBuilder::SectorToBlock(uint64_t sector, uint32_t* block) {\n    // The caller must ensure that the metadata has an alignment that is a\n    // multiple of the block size. liblp will take care of the rest, ensuring\n    // that all partitions are on an aligned boundary. Therefore all writes\n    // should be block-aligned, and if they are not, the table was misconfigured.\n    // Note that the default alignment is 1MiB, which is a multiple of the\n    // default block size (4096).\n    if ((sector * LP_SECTOR_SIZE) % block_size_ != 0) {\n        LERROR << \"sector \" << sector << \" is not aligned to block size \" << block_size_;\n        return false;\n    }\n    *block = (sector * LP_SECTOR_SIZE) / block_size_;\n    return true;\n}\n\nuint64_t ImageBuilder::BlockToSector(uint64_t block) const {\n    return (block * block_size_) / LP_SECTOR_SIZE;\n}\n\nbool ImageBuilder::Build() {\n    if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {\n        LERROR << \"Could not add initial sparse block for reserved zeroes\";\n        return false;\n    }\n\n    std::string geometry_blob = SerializeGeometry(geometry_);\n    std::string metadata_blob = SerializeMetadata(metadata_);\n    metadata_blob.resize(geometry_.metadata_max_size);\n\n    // Two copies of geometry, then two copies of each metadata slot.\n    all_metadata_ += geometry_blob + geometry_blob;\n    for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {\n        all_metadata_ += metadata_blob;\n    }\n\n    uint64_t first_sector = LP_PARTITION_RESERVED_BYTES / LP_SECTOR_SIZE;\n    if (!AddData(device_images_[0].get(), all_metadata_, first_sector)) {\n        return false;\n    }\n\n    if (!CheckExtentOrdering()) {\n        return false;\n    }\n\n    for (const auto& partition : metadata_.partitions) {\n        auto iter = images_.find(GetPartitionName(partition));\n        if (iter == images_.end()) {\n            continue;\n        }\n        if (!AddPartitionImage(partition, iter->second)) {\n            return false;\n        }\n        images_.erase(iter);\n    }\n\n    if (!images_.empty()) {\n        LERROR << \"Partition image was specified but no partition was found.\";\n        return false;\n    }\n    return true;\n}\n\nstatic inline bool HasFillValue(uint32_t* buffer, size_t count) {\n    uint32_t fill_value = buffer[0];\n    for (size_t i = 1; i < count; i++) {\n        if (fill_value != buffer[i]) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool ImageBuilder::AddPartitionImage(const LpMetadataPartition& partition,\n                                     const std::string& file) {\n    if (partition.num_extents == 0) {\n        LERROR << \"Partition size is zero: \" << GetPartitionName(partition);\n        return false;\n    }\n\n    // Track which extent we're processing.\n    uint32_t extent_index = partition.first_extent_index;\n\n    const LpMetadataExtent& extent = metadata_.extents[extent_index];\n    if (extent.target_type != LP_TARGET_TYPE_LINEAR) {\n        LERROR << \"Partition should only have linear extents: \" << GetPartitionName(partition);\n        return false;\n    }\n\n    int fd = OpenImageFile(file);\n    if (fd < 0) {\n        LERROR << \"Could not open image for partition: \" << GetPartitionName(partition);\n        return false;\n    }\n\n    // Make sure the image does not exceed the partition size.\n    uint64_t file_length;\n    if (!GetDescriptorSize(fd, &file_length)) {\n        LERROR << \"Could not compute image size\";\n        return false;\n    }\n    uint64_t partition_size = ComputePartitionSize(partition);\n    if (file_length > partition_size) {\n        LERROR << \"Image for partition '\" << GetPartitionName(partition)\n               << \"' is greater than its size (\" << file_length << \", expected \" << partition_size\n               << \")\";\n        return false;\n    }\n    if (SeekFile64(fd, 0, SEEK_SET)) {\n        PERROR << \"lseek failed\";\n        return false;\n    }\n\n    // We track the current logical sector and the position the current extent\n    // ends at.\n    uint64_t output_sector = 0;\n    uint64_t extent_last_sector = extent.num_sectors;\n\n    // We also track the output device and the current output block within that\n    // device.\n    uint32_t output_block;\n    if (!SectorToBlock(extent.target_data, &output_block)) {\n        return false;\n    }\n    sparse_file* output_device = device_images_[extent.target_source].get();\n\n    // Proceed to read the file and build sparse images.\n    uint64_t pos = 0;\n    uint64_t remaining = file_length;\n    while (remaining) {\n        // Check if we need to advance to the next extent.\n        if (output_sector == extent_last_sector) {\n            extent_index++;\n            if (extent_index >= partition.first_extent_index + partition.num_extents) {\n                LERROR << \"image is larger than extent table\";\n                return false;\n            }\n\n            const LpMetadataExtent& extent = metadata_.extents[extent_index];\n            extent_last_sector += extent.num_sectors;\n            output_device = device_images_[extent.target_source].get();\n            if (!SectorToBlock(extent.target_data, &output_block)) {\n                return false;\n            }\n        }\n\n        uint32_t buffer[block_size_ / sizeof(uint32_t)];\n        size_t read_size = remaining >= sizeof(buffer) ? sizeof(buffer) : size_t(remaining);\n        if (!android::base::ReadFully(fd, buffer, sizeof(buffer))) {\n            PERROR << \"read failed\";\n            return false;\n        }\n        if (read_size != sizeof(buffer) || !HasFillValue(buffer, read_size / sizeof(uint32_t))) {\n            int rv = sparse_file_add_fd(output_device, fd, pos, read_size, output_block);\n            if (rv) {\n                LERROR << \"sparse_file_add_fd failed with code: \" << rv;\n                return false;\n            }\n        } else {\n            int rv = sparse_file_add_fill(output_device, buffer[0], read_size, output_block);\n            if (rv) {\n                LERROR << \"sparse_file_add_fill failed with code: \" << rv;\n                return false;\n            }\n        }\n        pos += read_size;\n        remaining -= read_size;\n        output_sector += block_size_ / LP_SECTOR_SIZE;\n        output_block++;\n    }\n\n    return true;\n}\n\nuint64_t ImageBuilder::ComputePartitionSize(const LpMetadataPartition& partition) const {\n    uint64_t sectors = 0;\n    for (size_t i = 0; i < partition.num_extents; i++) {\n        sectors += metadata_.extents[partition.first_extent_index + i].num_sectors;\n    }\n    return sectors * LP_SECTOR_SIZE;\n}\n\n// For simplicity, we don't allow serializing any configuration: extents must\n// be ordered, such that any extent at position I in the table occurs *before*\n// any extent after position I, for the same block device. We validate that\n// here.\n//\n// Without this, it would be more difficult to find the appropriate extent for\n// an output block. With this guarantee it is a linear walk.\nbool ImageBuilder::CheckExtentOrdering() {\n    std::vector<uint64_t> last_sectors(metadata_.block_devices.size());\n\n    for (const auto& extent : metadata_.extents) {\n        if (extent.target_type != LP_TARGET_TYPE_LINEAR) {\n            LERROR << \"Extents must all be type linear.\";\n            return false;\n        }\n        if (extent.target_data <= last_sectors[extent.target_source]) {\n            LERROR << \"Extents must appear in increasing order.\";\n            return false;\n        }\n        if ((extent.num_sectors * LP_SECTOR_SIZE) % block_size_ != 0) {\n            LERROR << \"Extents must be aligned to the block size.\";\n            return false;\n        }\n        last_sectors[extent.target_source] = extent.target_data;\n    }\n    return true;\n}\n\nint ImageBuilder::OpenImageFile(const std::string& file) {\n    unique_fd source_fd = GetControlFileOrOpen(file.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY);\n    if (source_fd < 0) {\n        PERROR << \"open image file failed: \" << file;\n        return -1;\n    }\n\n    SparsePtr source(sparse_file_import(source_fd, true, true), sparse_file_destroy);\n    if (!source) {\n        int fd = source_fd.get();\n        temp_fds_.push_back(std::move(source_fd));\n        return fd;\n    }\n\n    TemporaryFile tf;\n    if (tf.fd < 0) {\n        PERROR << \"make temporary file failed\";\n        return -1;\n    }\n\n    // We temporarily unsparse the file, rather than try to merge its chunks.\n    int rv = sparse_file_write(source.get(), tf.fd, false, false, false);\n    if (rv) {\n        LERROR << \"sparse_file_write failed with code: \" << rv;\n        return -1;\n    }\n    temp_fds_.push_back(android::base::unique_fd(tf.release()));\n    return temp_fds_.back().get();\n}\n\nbool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,\n                      const std::map<std::string, std::string>& images, bool sparsify) {\n    ImageBuilder builder(metadata, block_size, images, sparsify);\n    return builder.IsValid() && builder.Build() && builder.Export(file);\n}\n\nbool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,\n                          uint32_t block_size, const std::map<std::string, std::string>& images,\n                          bool sparsify) {\n    ImageBuilder builder(metadata, block_size, images, sparsify);\n    return builder.IsValid() && builder.Build() && builder.ExportFiles(output_dir);\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/images.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdint.h>\n#include <map>\n#include <memory>\n#include <string>\n\n#include <android-base/unique_fd.h>\n#include <liblp/liblp.h>\n#include <sparse/sparse.h>\n\nnamespace android {\nnamespace fs_mgr {\n\n// Helper function to serialize geometry and metadata to a normal file, for\n// flashing or debugging.\nstd::unique_ptr<LpMetadata> ReadFromImageFile(int fd);\n\n// We use an object to build the image file since it requires that data\n// pointers be held alive until the sparse file is destroyed. It's easier\n// to do this when the data pointers are all in one place.\nclass ImageBuilder {\n  public:\n    ImageBuilder(const LpMetadata& metadata, uint32_t block_size,\n                 const std::map<std::string, std::string>& images, bool sparsify);\n\n    bool Build();\n    bool Export(const std::string& file);\n    bool ExportFiles(const std::string& dir);\n    bool IsValid() const;\n\n    using SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;\n    const std::vector<SparsePtr>& device_images() const { return device_images_; }\n\n  private:\n    bool AddData(sparse_file* file, const std::string& blob, uint64_t sector);\n    bool AddPartitionImage(const LpMetadataPartition& partition, const std::string& file);\n    int OpenImageFile(const std::string& file);\n    bool SectorToBlock(uint64_t sector, uint32_t* block);\n    uint64_t BlockToSector(uint64_t block) const;\n    bool CheckExtentOrdering();\n    uint64_t ComputePartitionSize(const LpMetadataPartition& partition) const;\n\n    const LpMetadata& metadata_;\n    const LpMetadataGeometry& geometry_;\n    uint32_t block_size_;\n    bool sparsify_;\n\n    std::vector<SparsePtr> device_images_;\n    std::string all_metadata_;\n    std::map<std::string, std::string> images_;\n    std::vector<android::base::unique_fd> temp_fds_;\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/include/liblp/builder.h",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#ifndef LIBLP_METADATA_BUILDER_H\n#define LIBLP_METADATA_BUILDER_H\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <map>\n#include <memory>\n#include <optional>\n#include <set>\n#include <string_view>\n\n#include \"liblp.h\"\n#include \"partition_opener.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nclass LinearExtent;\nstruct Interval;\n\n// By default, partitions are aligned on a 1MiB boundary.\nstatic constexpr uint32_t kDefaultPartitionAlignment = 1024 * 1024;\nstatic constexpr uint32_t kDefaultBlockSize = 4096;\n\n// Name of the default group in a metadata.\nstatic constexpr std::string_view kDefaultGroup = \"default\";\n\nenum class ExtentType {\n    kZero,\n    kLinear,\n};\n\n// Abstraction around dm-targets that can be encoded into logical partition tables.\nclass Extent {\n  public:\n    explicit Extent(uint64_t num_sectors) : num_sectors_(num_sectors) {}\n    virtual ~Extent() {}\n\n    virtual bool AddTo(LpMetadata* out) const = 0;\n    virtual LinearExtent* AsLinearExtent() { return nullptr; }\n    virtual ExtentType GetExtentType() const = 0;\n\n    virtual bool operator==(const Extent& other) const = 0;\n    virtual bool operator!=(const Extent& other) const { return !(*this == other); }\n\n    uint64_t num_sectors() const { return num_sectors_; }\n    void set_num_sectors(uint64_t num_sectors) { num_sectors_ = num_sectors; }\n\n  protected:\n    uint64_t num_sectors_;\n};\n\nstd::ostream& operator<<(std::ostream& os, const Extent& extent);\n\n// This corresponds to a dm-linear target.\nclass LinearExtent final : public Extent {\n  public:\n    LinearExtent(uint64_t num_sectors, uint32_t device_index, uint64_t physical_sector)\n        : Extent(num_sectors), device_index_(device_index), physical_sector_(physical_sector) {}\n\n    bool AddTo(LpMetadata* metadata) const override;\n    LinearExtent* AsLinearExtent() override { return this; }\n    ExtentType GetExtentType() const override { return ExtentType::kLinear; }\n\n    bool operator==(const Extent& other) const override;\n\n    uint64_t physical_sector() const { return physical_sector_; }\n    uint64_t end_sector() const { return physical_sector_ + num_sectors_; }\n    uint32_t device_index() const { return device_index_; }\n\n    bool OverlapsWith(const LinearExtent& other) const;\n    bool OverlapsWith(const Interval& interval) const;\n\n    Interval AsInterval() const;\n\n  private:\n    uint32_t device_index_;\n    uint64_t physical_sector_;\n};\n\n// This corresponds to a dm-zero target.\nclass ZeroExtent final : public Extent {\n  public:\n    explicit ZeroExtent(uint64_t num_sectors) : Extent(num_sectors) {}\n\n    bool AddTo(LpMetadata* out) const override;\n    ExtentType GetExtentType() const override { return ExtentType::kZero; }\n\n    bool operator==(const Extent& other) const override;\n};\n\nclass PartitionGroup final {\n    friend class MetadataBuilder;\n\n  public:\n    explicit PartitionGroup(std::string_view name, uint64_t maximum_size)\n        : name_(name), maximum_size_(maximum_size) {}\n\n    const std::string& name() const { return name_; }\n    uint64_t maximum_size() const { return maximum_size_; }\n\n  private:\n    void set_maximum_size(uint64_t maximum_size) { maximum_size_ = maximum_size; }\n\n    std::string name_;\n    uint64_t maximum_size_;\n};\n\nclass Partition final {\n    friend class MetadataBuilder;\n\n  public:\n    Partition(std::string_view name, std::string_view group_name, uint32_t attributes);\n\n    // Add a raw extent.\n    void AddExtent(std::unique_ptr<Extent>&& extent);\n\n    // Remove all extents from this partition.\n    void RemoveExtents();\n\n    // Compute the size used by linear extents. This is the same as size(),\n    // but does not factor in extents which do not take up space.\n    uint64_t BytesOnDisk() const;\n\n    const std::string& name() const { return name_; }\n    const std::string& group_name() const { return group_name_; }\n    uint32_t attributes() const { return attributes_; }\n    void set_attributes(uint32_t attributes) { attributes_ = attributes; }\n    const std::vector<std::unique_ptr<Extent>>& extents() const { return extents_; }\n    uint64_t size() const { return size_; }\n\n    // Return a copy of *this, but with extents that includes only the first\n    // |aligned_size| bytes. |aligned_size| should be aligned to\n    // logical_block_size() of the MetadataBuilder that this partition belongs\n    // to.\n    Partition GetBeginningExtents(uint64_t aligned_size) const;\n\n  private:\n    void ShrinkTo(uint64_t aligned_size);\n    void set_group_name(std::string_view group_name) { group_name_ = group_name; }\n\n    std::string name_;\n    std::string group_name_;\n    std::vector<std::unique_ptr<Extent>> extents_;\n    uint32_t attributes_;\n    uint64_t size_;\n};\n\n// An interval in the metadata. This is similar to a LinearExtent with one difference.\n// LinearExtent represents a \"used\" region in the metadata, while Interval can also represent\n// an \"unused\" region.\nstruct Interval {\n    uint32_t device_index;\n    uint64_t start;\n    uint64_t end;\n\n    Interval(uint32_t device_index, uint64_t start, uint64_t end)\n        : device_index(device_index), start(start), end(end) {}\n    uint64_t length() const { return end - start; }\n\n    // Note: the device index is not included in sorting (intervals are\n    // sorted in per-device lists).\n    bool operator<(const Interval& other) const {\n        return (start == other.start) ? end < other.end : start < other.start;\n    }\n\n    std::unique_ptr<Extent> AsExtent() const;\n\n    // Intersect |a| with |b|.\n    // If no intersection, result has 0 length().\n    static Interval Intersect(const Interval& a, const Interval& b);\n\n    // Intersect two lists of intervals, and store result to |a|.\n    static std::vector<Interval> Intersect(const std::vector<Interval>& a,\n                                           const std::vector<Interval>& b);\n};\n\nclass MetadataBuilder {\n  public:\n    // Construct an empty logical partition table builder given the specified\n    // map of partitions that are available for storing logical partitions.\n    //\n    // At least one partition in the list must be the \"super\" device, where\n    // metadata will be stored.\n    //\n    // If the parameters would yield invalid metadata, nullptr is returned. This\n    // could happen if the super device is too small to store all required\n    // metadata.\n    static std::unique_ptr<MetadataBuilder> New(const std::vector<BlockDeviceInfo>& block_devices,\n                                                const std::string& super_partition,\n                                                uint32_t metadata_max_size,\n                                                uint32_t metadata_slot_count);\n\n    // Import an existing table for modification. This reads metadata off the\n    // given block device and imports it. It also adjusts alignment information\n    // based on run-time values in the operating system.\n    static std::unique_ptr<MetadataBuilder> New(const IPartitionOpener& opener,\n                                                const std::string& super_partition,\n                                                uint32_t slot_number);\n\n    // Same as above, but use the default PartitionOpener.\n    static std::unique_ptr<MetadataBuilder> New(const std::string& super_partition,\n                                                uint32_t slot_number);\n\n    // This is when performing an A/B update. The source partition must be a\n    // super partition. On a normal device, the metadata for the source slot\n    // is imported and the target slot is ignored. On a retrofit device, the\n    // metadata may not have the target slot's devices listed yet, in which\n    // case, it is automatically upgraded to include all available block\n    // devices.\n    // If |always_keep_source_slot| is set, on a Virtual A/B device\n    // - source slot partitions are kept.\n    // - UPDATED flag is cleared.\n    // This is useful when applying a downgrade package.\n    static std::unique_ptr<MetadataBuilder> NewForUpdate(const IPartitionOpener& opener,\n                                                         const std::string& source_partition,\n                                                         uint32_t source_slot_number,\n                                                         uint32_t target_slot_number,\n                                                         bool always_keep_source_slot = false);\n\n    // Import an existing table for modification. If the table is not valid, for\n    // example it contains duplicate partition names, then nullptr is returned.\n    //\n    // If an IPartitionOpener is specified, then block device informatiom will\n    // be updated.\n    static std::unique_ptr<MetadataBuilder> New(const LpMetadata& metadata,\n                                                const IPartitionOpener* opener = nullptr);\n\n    // Helper function for a single super partition, for tests.\n    static std::unique_ptr<MetadataBuilder> New(const BlockDeviceInfo& device_info,\n                                                uint32_t metadata_max_size,\n                                                uint32_t metadata_slot_count) {\n        return New({device_info}, device_info.partition_name, metadata_max_size,\n                   metadata_slot_count);\n    }\n\n    // Wrapper around New() with a BlockDeviceInfo that only specifies a device\n    // size. This is a convenience method for tests.\n    static std::unique_ptr<MetadataBuilder> New(uint64_t blockdev_size, uint32_t metadata_max_size,\n                                                uint32_t metadata_slot_count) {\n        BlockDeviceInfo device_info(LP_METADATA_DEFAULT_PARTITION_NAME, blockdev_size, 0, 0,\n                                    kDefaultBlockSize);\n        return New(device_info, metadata_max_size, metadata_slot_count);\n    }\n\n    // Verifies that the given partitions in the metadata have the same extents as the source\n    // metadata.\n    static bool VerifyExtentsAgainstSourceMetadata(const MetadataBuilder& source_metadata,\n                                                   uint32_t source_slot_number,\n                                                   const MetadataBuilder& target_metadata,\n                                                   uint32_t target_slot_number,\n                                                   const std::vector<std::string>& partitions);\n\n    // Define a new partition group. By default there is one group called\n    // \"default\", with an unrestricted size. A non-zero size will restrict the\n    // total space used by all partitions in the group.\n    //\n    // This can fail and return false if the group already exists.\n    bool AddGroup(std::string_view group_name, uint64_t maximum_size);\n\n    // Export metadata so it can be serialized to an image, to disk, or mounted\n    // via device-mapper.\n    std::unique_ptr<LpMetadata> Export();\n\n    // Add a partition, returning a handle so it can be sized as needed. If a\n    // partition with the given name already exists, nullptr is returned.\n    Partition* AddPartition(std::string_view name, std::string_view group_name,\n                            uint32_t attributes);\n\n    // Same as AddPartition above, but uses the default partition group which\n    // has no size restrictions.\n    Partition* AddPartition(const std::string& name, uint32_t attributes);\n\n    // Delete a partition by name if it exists.\n    void RemovePartition(std::string_view name);\n\n    // Find a partition by name. If no partition is found, nullptr is returned.\n    Partition* FindPartition(std::string_view name) const;\n\n    // Find a group by name. If no group is found, nullptr is returned.\n    PartitionGroup* FindGroup(std::string_view name) const;\n\n    // Add a predetermined extent to a partition.\n    bool AddLinearExtent(Partition* partition, const std::string& block_device,\n                         uint64_t num_sectors, uint64_t physical_sector);\n\n    // Grow or shrink a partition to the requested size. This size will be\n    // rounded UP to the nearest block (512 bytes).\n    //\n    // When growing a partition, a greedy algorithm is used to find free gaps\n    // in the partition table and allocate them. If not enough space can be\n    // allocated, false is returned, and the parition table will not be\n    // modified.\n    //\n    // Note, this is an in-memory operation, and it does not alter the\n    // underlying filesystem or contents of the partition on disk.\n    //\n    // If |free_region_hint| is not empty, it will only try to allocate extents\n    // in regions within the list.\n    bool ResizePartition(Partition* partition, uint64_t requested_size,\n                         const std::vector<Interval>& free_region_hint = {});\n\n    // Return the list of partitions belonging to a group.\n    std::vector<Partition*> ListPartitionsInGroup(std::string_view group_name);\n\n    // Changes a partition's group. Size constraints will not be checked until\n    // the metadata is exported, to avoid errors during potential group and\n    // size shuffling operations. This will return false if the new group does\n    // not exist.\n    bool ChangePartitionGroup(Partition* partition, std::string_view group_name);\n\n    // Changes the size of a partition group. Size constraints will not be\n    // checked until metadata is exported, to avoid errors during group\n    // reshuffling. This will return false if the group does not exist, or if\n    // the group name is \"default\".\n    bool ChangeGroupSize(const std::string& group_name, uint64_t maximum_size);\n\n    // Amount of space that can be allocated to logical partitions.\n    uint64_t AllocatableSpace() const;\n    uint64_t UsedSpace() const;\n\n    // Return a list of all group names.\n    std::vector<std::string> ListGroups() const;\n\n    // Remove all partitions belonging to a group, then remove the group.\n    void RemoveGroupAndPartitions(std::string_view group_name);\n\n    // Set the LP_METADATA_AUTO_SLOT_SUFFIXING flag.\n    void SetAutoSlotSuffixing();\n    // Set the LP_HEADER_FLAG_VIRTUAL_AB_DEVICE flag.\n    void SetVirtualABDeviceFlag();\n    // Set or unset the LP_HEADER_FLAG_OVERLAYS_ACTIVE flag.\n    void SetOverlaysActiveFlag(bool flag);\n\n    bool GetBlockDeviceInfo(const std::string& partition_name, BlockDeviceInfo* info) const;\n    bool UpdateBlockDeviceInfo(const std::string& partition_name, const BlockDeviceInfo& info);\n\n    // Require the expanded metadata header. This is exposed for testing, and\n    // is normally only called as needed by other methods.\n    void RequireExpandedMetadataHeader();\n\n    // Attempt to preserve the named partitions from an older metadata. If this\n    // is not possible (for example, the block device list has changed) then\n    // false is returned.\n    bool ImportPartitions(const LpMetadata& metadata, const std::set<std::string>& partition_names);\n\n    // Return true if a block device is found, else false.\n    bool HasBlockDevice(const std::string& partition_name) const;\n\n    // Return the name of the block device at |index|.\n    std::string GetBlockDevicePartitionName(uint64_t index) const;\n\n    // Return the list of free regions not occupied by extents in the metadata.\n    std::vector<Interval> GetFreeRegions() const;\n\n    uint64_t logical_block_size() const;\n\n  private:\n    MetadataBuilder();\n    MetadataBuilder(const MetadataBuilder&) = delete;\n    MetadataBuilder(MetadataBuilder&&) = delete;\n    MetadataBuilder& operator=(const MetadataBuilder&) = delete;\n    MetadataBuilder& operator=(MetadataBuilder&&) = delete;\n    bool Init(const std::vector<BlockDeviceInfo>& block_devices, const std::string& super_partition,\n              uint32_t metadata_max_size, uint32_t metadata_slot_count);\n    bool Init(const LpMetadata& metadata);\n    bool GrowPartition(Partition* partition, uint64_t aligned_size,\n                       const std::vector<Interval>& free_region_hint);\n    void ShrinkPartition(Partition* partition, uint64_t aligned_size);\n    bool AlignSector(const LpMetadataBlockDevice& device, uint64_t sector, uint64_t* out) const;\n    uint64_t TotalSizeOfGroup(PartitionGroup* group) const;\n    bool UpdateBlockDeviceInfo(size_t index, const BlockDeviceInfo& info);\n    bool FindBlockDeviceByName(const std::string& partition_name, uint32_t* index) const;\n    bool ValidatePartitionSizeChange(Partition* partition, uint64_t old_size, uint64_t new_size,\n                                     bool force_check);\n    void ImportExtents(Partition* dest, const LpMetadata& metadata,\n                       const LpMetadataPartition& source);\n    bool ImportPartition(const LpMetadata& metadata, const LpMetadataPartition& source);\n\n    // Return true if the device is an AB device.\n    static bool IsABDevice();\n\n    // Return true if the device is retrofitting dynamic partitions.\n    static bool IsRetrofitDynamicPartitionsDevice();\n\n    // Return true if _b partitions should be prioritized at the second half of the device.\n    bool ShouldHalveSuper() const;\n\n    bool ValidatePartitionGroups() const;\n\n    bool IsAnyRegionCovered(const std::vector<Interval>& regions,\n                            const LinearExtent& candidate) const;\n    bool IsAnyRegionAllocated(const LinearExtent& candidate) const;\n    void ExtentsToFreeList(const std::vector<Interval>& extents,\n                           std::vector<Interval>* free_regions) const;\n    std::vector<Interval> PrioritizeSecondHalfOfSuper(const std::vector<Interval>& free_list);\n    std::unique_ptr<LinearExtent> ExtendFinalExtent(Partition* partition,\n                                                    const std::vector<Interval>& free_list,\n                                                    uint64_t sectors_needed) const;\n\n    static bool UpdateMetadataForOtherSuper(LpMetadata* metadata, uint32_t source_slot_number,\n                                            uint32_t target_slot_number);\n\n    LpMetadataGeometry geometry_;\n    LpMetadataHeader header_;\n    std::vector<std::unique_ptr<Partition>> partitions_;\n    std::vector<std::unique_ptr<PartitionGroup>> groups_;\n    std::vector<LpMetadataBlockDevice> block_devices_;\n    bool auto_slot_suffixing_;\n};\n\n// Read BlockDeviceInfo for a given block device. This always returns false\n// for non-Linux operating systems.\nbool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info);\n\n}  // namespace fs_mgr\n}  // namespace android\n\n#endif /* LIBLP_METADATA_BUILDER_H */\n"
  },
  {
    "path": "fs_mgr/liblp/include/liblp/liblp.h",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#ifndef LIBLP_LIBLP_H\n#define LIBLP_LIBLP_H\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <map>\n#include <memory>\n#include <string>\n\n#include <android-base/unique_fd.h>\n\n#include \"metadata_format.h\"\n#include \"partition_opener.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\n// Helper structure for easily interpreting deserialized metadata, or\n// re-serializing metadata.\nstruct LpMetadata {\n    LpMetadataGeometry geometry;\n    LpMetadataHeader header;\n    std::vector<LpMetadataPartition> partitions;\n    std::vector<LpMetadataExtent> extents;\n    std::vector<LpMetadataPartitionGroup> groups;\n    std::vector<LpMetadataBlockDevice> block_devices;\n};\n\n// Place an initial partition table on the device. This will overwrite the\n// existing geometry, and should not be used for normal partition table\n// updates. False can be returned if the geometry is incompatible with the\n// block device or an I/O error occurs.\nbool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,\n                         const LpMetadata& metadata);\n\n// Update the partition table for a given metadata slot number. False is\n// returned if an error occurs, which can include:\n//  - Invalid slot number.\n//  - I/O error.\n//  - Corrupt or missing metadata geometry on disk.\n//  - Incompatible geometry.\nbool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,\n                          const LpMetadata& metadata, uint32_t slot_number);\n\n// Read logical partition metadata from its predetermined location on a block\n// device. If readback fails, we also attempt to load from a backup copy.\nstd::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,\n                                         const std::string& super_partition, uint32_t slot_number);\n\n// Helper functions that use the default PartitionOpener.\nbool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata);\nbool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,\n                          uint32_t slot_number);\nstd::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number);\n\n// Returns whether an image is an \"empty\" image or not. An empty image contains\n// only metadata. Unlike a flashed block device, there are no reserved bytes or\n// backup sections, and only one slot is stored (even if multiple slots are\n// supported). It is a format specifically for storing only metadata.\nbool IsEmptySuperImage(const std::string& file);\n\n// Read/Write logical partition metadata and contents to an image file, for\n// flashing.\nbool WriteToImageFile(const std::string& file, const LpMetadata& metadata, uint32_t block_size,\n                      const std::map<std::string, std::string>& images, bool sparsify);\n\n// Read/Write logical partition metadata to an image file, for producing a\n// super_empty.img (for fastboot wipe-super/update-super) or for diagnostics.\nbool WriteToImageFile(const std::string& file, const LpMetadata& metadata);\nbool WriteToImageFile(android::base::borrowed_fd fd, const LpMetadata& metadata);\nstd::unique_ptr<LpMetadata> ReadFromImageFile(const std::string& image_file);\nstd::unique_ptr<LpMetadata> ReadFromImageBlob(const void* data, size_t bytes);\n\n// Similar to WriteToSparseFile, this will generate an image that can be\n// flashed to a device directly. However unlike WriteToSparseFile, it\n// is intended for retrofit devices, and will generate one sparse file per\n// block device (each named super_<name>.img) and placed in the specified\n// output folder.\nbool WriteSplitImageFiles(const std::string& output_dir, const LpMetadata& metadata,\n                          uint32_t block_size, const std::map<std::string, std::string>& images,\n                          bool sparsify);\n\n// Helper to extract safe C++ strings from partition info.\nstd::string GetPartitionName(const LpMetadataPartition& partition);\nstd::string GetPartitionGroupName(const LpMetadataPartitionGroup& group);\nstd::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device);\n\n// Return the block device that houses the super partition metadata; returns\n// null on failure.\nconst LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata);\n\n// Return the total size of all partitions comprising the super partition.\nuint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata);\n\n// Get the list of block device names required by the given metadata.\nstd::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata);\n\n// Slot suffix helpers.\nuint32_t SlotNumberForSlotSuffix(const std::string& suffix);\nstd::string SlotSuffixForSlotNumber(uint32_t slot_number);\nstd::string GetPartitionSlotSuffix(const std::string& partition_name);\n\n// Helpers for common functions.\nconst LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name);\nuint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition);\n\n}  // namespace fs_mgr\n}  // namespace android\n\n#endif  // LIBLP_LIBLP_H\n"
  },
  {
    "path": "fs_mgr/liblp/include/liblp/metadata_format.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef LOGICAL_PARTITION_METADATA_FORMAT_H_\n#define LOGICAL_PARTITION_METADATA_FORMAT_H_\n\n#ifdef __cplusplus\n#include <string>\n#include <vector>\n#endif\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* Magic signature for LpMetadataGeometry. */\n#define LP_METADATA_GEOMETRY_MAGIC 0x616c4467\n\n/* Space reserved for geometry information. */\n#define LP_METADATA_GEOMETRY_SIZE 4096\n\n/* Magic signature for LpMetadataHeader. */\n#define LP_METADATA_HEADER_MAGIC 0x414C5030\n\n/* Current metadata version. */\n#define LP_METADATA_MAJOR_VERSION 10\n#define LP_METADATA_MINOR_VERSION_MIN 0\n#define LP_METADATA_MINOR_VERSION_MAX 2\n\n/* Metadata version needed to use the UPDATED partition attribute. */\n#define LP_METADATA_VERSION_FOR_UPDATED_ATTR 1\n\n/* Metadata version needed for the new expanded header struct. */\n#define LP_METADATA_VERSION_FOR_EXPANDED_HEADER 2\n\n/* Attributes for the LpMetadataPartition::attributes field.\n *\n * READONLY - The partition should not be considered writable. When used with\n * device mapper, the block device will be created as read-only.\n */\n#define LP_PARTITION_ATTR_NONE 0x0\n#define LP_PARTITION_ATTR_READONLY (1 << 0)\n\n/* This flag is only intended to be used with super_empty.img and super.img on\n * retrofit devices. On these devices there are A and B super partitions, and\n * we don't know ahead of time which slot the image will be applied to.\n *\n * If set, the partition name needs a slot suffix applied. The slot suffix is\n * determined by the metadata slot number (0 = _a, 1 = _b).\n */\n#define LP_PARTITION_ATTR_SLOT_SUFFIXED (1 << 1)\n\n/* This flag is applied automatically when using MetadataBuilder::NewForUpdate.\n * It signals that the partition was created (or modified) for a snapshot-based\n * update. If this flag is not present, the partition was likely flashed via\n * fastboot.\n */\n#define LP_PARTITION_ATTR_UPDATED (1 << 2)\n\n/* This flag marks a partition as disabled. It should not be used or mapped. */\n#define LP_PARTITION_ATTR_DISABLED (1 << 3)\n\n/* Mask that defines all valid attributes. When changing this, make sure to\n * update ParseMetadata().\n */\n#define LP_PARTITION_ATTRIBUTE_MASK_V0 \\\n    (LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED)\n#define LP_PARTITION_ATTRIBUTE_MASK_V1 (LP_PARTITION_ATTR_UPDATED | LP_PARTITION_ATTR_DISABLED)\n#define LP_PARTITION_ATTRIBUTE_MASK \\\n    (LP_PARTITION_ATTRIBUTE_MASK_V0 | LP_PARTITION_ATTRIBUTE_MASK_V1)\n\n/* Default name of the physical partition that holds logical partition entries.\n * The layout of this partition will look like:\n *\n *     +--------------------+\n *     | Disk Geometry      |\n *     +--------------------+\n *     | Geometry Backup    |\n *     +--------------------+\n *     | Metadata           |\n *     +--------------------+\n *     | Backup Metadata    |\n *     +--------------------+\n *     | Logical Partitions |\n *     +--------------------+\n */\n#define LP_METADATA_DEFAULT_PARTITION_NAME \"super\"\n\n/* Size of a sector is always 512 bytes for compatibility with the Linux kernel. */\n#define LP_SECTOR_SIZE 512\n\n/* Amount of space reserved at the start of every super partition to avoid\n * creating an accidental boot sector.\n */\n#define LP_PARTITION_RESERVED_BYTES 4096\n\n/* This structure is stored at block 0 in the first 4096 bytes of the\n * partition, and again in the following block. It is never modified and\n * describes how logical partition information can be located.\n */\ntypedef struct LpMetadataGeometry {\n    /*  0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */\n    uint32_t magic;\n\n    /*  4: Size of the LpMetadataGeometry struct. */\n    uint32_t struct_size;\n\n    /*  8: SHA256 checksum of this struct, with this field set to 0. */\n    uint8_t checksum[32];\n\n    /* 40: Maximum amount of space a single copy of the metadata can use. This\n     * must be a multiple of LP_SECTOR_SIZE.\n     */\n    uint32_t metadata_max_size;\n\n    /* 44: Number of copies of the metadata to keep. For A/B devices, this\n     * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B\n     * it will be 1. A backup copy of each slot is kept, so if this is \"2\",\n     * there will be four copies total.\n     */\n    uint32_t metadata_slot_count;\n\n    /* 48: Logical block size. This is the minimal alignment for partition and\n     * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that\n     * this must be equal across all LUNs that comprise the super partition,\n     * and thus this field is stored in the geometry, not per-device.\n     */\n    uint32_t logical_block_size;\n} __attribute__((packed)) LpMetadataGeometry;\n\n/* The logical partition metadata has a number of tables; they are described\n * in the header via the following structure.\n *\n * The size of the table can be computed by multiplying entry_size by\n * num_entries, and the result must not overflow a 32-bit signed integer.\n */\ntypedef struct LpMetadataTableDescriptor {\n    /*  0: Location of the table, relative to end of the metadata header. */\n    uint32_t offset;\n    /*  4: Number of entries in the table. */\n    uint32_t num_entries;\n    /*  8: Size of each entry in the table, in bytes. */\n    uint32_t entry_size;\n} __attribute__((packed)) LpMetadataTableDescriptor;\n\n/* Binary format for the header of the logical partition metadata format.\n *\n * The format has three sections. The header must occur first, and the\n * proceeding tables may be placed in any order after.\n *\n *  +-----------------------------------------+\n *  | Header data - fixed size                |\n *  +-----------------------------------------+\n *  | Partition table - variable size         |\n *  +-----------------------------------------+\n *  | Partition table extents - variable size |\n *  +-----------------------------------------+\n *\n * The \"Header\" portion is described by LpMetadataHeader. It will always\n * precede the other three blocks.\n *\n * All fields are stored in little-endian byte order when serialized.\n *\n * This struct is versioned; see the |major_version| and |minor_version|\n * fields.\n */\ntypedef struct LpMetadataHeader {\n    /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */\n    uint32_t magic;\n\n    /*  4: Version number required to read this metadata. If the version is not\n     * equal to the library version, the metadata should be considered\n     * incompatible.\n     */\n    uint16_t major_version;\n\n    /*  6: Minor version. A library supporting newer features should be able to\n     * read metadata with an older minor version. However, an older library\n     * should not support reading metadata if its minor version is higher.\n     */\n    uint16_t minor_version;\n\n    /*  8: The size of this header struct. */\n    uint32_t header_size;\n\n    /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as\n     * if this field were set to 0.\n     */\n    uint8_t header_checksum[32];\n\n    /* 44: The total size of all tables. This size is contiguous; tables may not\n     * have gaps in between, and they immediately follow the header.\n     */\n    uint32_t tables_size;\n\n    /* 48: SHA256 checksum of all table contents. */\n    uint8_t tables_checksum[32];\n\n    /* 80: Partition table descriptor. */\n    LpMetadataTableDescriptor partitions;\n    /* 92: Extent table descriptor. */\n    LpMetadataTableDescriptor extents;\n    /* 104: Updateable group descriptor. */\n    LpMetadataTableDescriptor groups;\n    /* 116: Block device table. */\n    LpMetadataTableDescriptor block_devices;\n\n    /* Everything past here is header version 1.2+, and is only included if\n     * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must\n     * zero these additional fields.\n     */\n\n    /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are\n     * independent of the version number and intended to be informational only.\n     * New flags can be added without bumping the version.\n     */\n    uint32_t flags;\n\n    /* 132: Reserved (zero), pad to 256 bytes. */\n    uint8_t reserved[124];\n} __attribute__((packed)) LpMetadataHeader;\n\n/* This device uses Virtual A/B. Note that on retrofit devices, the expanded\n * header may not be present.\n */\n#define LP_HEADER_FLAG_VIRTUAL_AB_DEVICE 0x1\n\n/* This device has overlays activated via \"adb remount\". */\n#define LP_HEADER_FLAG_OVERLAYS_ACTIVE 0x2\n\n/* This struct defines a logical partition entry, similar to what would be\n * present in a GUID Partition Table.\n */\ntypedef struct LpMetadataPartition {\n    /*  0: Name of this partition in ASCII characters. Any unused characters in\n     * the buffer must be set to 0. Characters may only be alphanumeric or _.\n     * The name must include at least one ASCII character, and it must be unique\n     * across all partition names. The length (36) is the same as the maximum\n     * length of a GPT partition name.\n     */\n    char name[36];\n\n    /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */\n    uint32_t attributes;\n\n    /* 40: Index of the first extent owned by this partition. The extent will\n     * start at logical sector 0. Gaps between extents are not allowed.\n     */\n    uint32_t first_extent_index;\n\n    /* 44: Number of extents in the partition. Every partition must have at\n     * least one extent.\n     */\n    uint32_t num_extents;\n\n    /* 48: Group this partition belongs to. */\n    uint32_t group_index;\n} __attribute__((packed)) LpMetadataPartition;\n\n/* This extent is a dm-linear target, and the index is an index into the\n * LinearExtent table.\n */\n#define LP_TARGET_TYPE_LINEAR 0\n\n/* This extent is a dm-zero target. The index is ignored and must be 0. */\n#define LP_TARGET_TYPE_ZERO 1\n\n/* This struct defines an extent entry in the extent table block. */\ntypedef struct LpMetadataExtent {\n    /*  0: Length of this extent, in 512-byte sectors. */\n    uint64_t num_sectors;\n\n    /*  8: Target type for device-mapper (see LP_TARGET_TYPE_* values). */\n    uint32_t target_type;\n\n    /* 12: Contents depends on target_type.\n     *\n     * LINEAR: The sector on the physical partition that this extent maps onto.\n     * ZERO: This field must be 0.\n     */\n    uint64_t target_data;\n\n    /* 20: Contents depends on target_type.\n     *\n     * LINEAR: Must be an index into the block devices table.\n     * ZERO: This field must be 0.\n     */\n    uint32_t target_source;\n} __attribute__((packed)) LpMetadataExtent;\n\n/* This struct defines an entry in the groups table. Each group has a maximum\n * size, and partitions in a group must not exceed that size. There is always\n * a \"default\" group of unlimited size, which is used when not using update\n * groups or when using overlayfs or fastbootd.\n */\ntypedef struct LpMetadataPartitionGroup {\n    /*  0: Name of this group. Any unused characters must be 0. */\n    char name[36];\n\n    /* 36: Flags (see LP_GROUP_*). */\n    uint32_t flags;\n\n    /* 40: Maximum size in bytes. If 0, the group has no maximum size. */\n    uint64_t maximum_size;\n} __attribute__((packed)) LpMetadataPartitionGroup;\n\n/* This flag is only intended to be used with super_empty.img and super.img on\n * retrofit devices. If set, the group needs a slot suffix to be interpreted\n * correctly. The suffix is automatically applied by ReadMetadata().\n */\n#define LP_GROUP_SLOT_SUFFIXED (1 << 0)\n\n/* This struct defines an entry in the block_devices table. There must be at\n * least one device, and the first device must represent the partition holding\n * the super metadata.\n */\ntypedef struct LpMetadataBlockDevice {\n    /* 0: First usable sector for allocating logical partitions. this will be\n     * the first sector after the initial geometry blocks, followed by the\n     * space consumed by metadata_max_size*metadata_slot_count*2.\n     */\n    uint64_t first_logical_sector;\n\n    /* 8: Alignment for defining partitions or partition extents. For example,\n     * an alignment of 1MiB will require that all partitions have a size evenly\n     * divisible by 1MiB, and that the smallest unit the partition can grow by\n     * is 1MiB.\n     *\n     * Alignment is normally determined at runtime when growing or adding\n     * partitions. If for some reason the alignment cannot be determined, then\n     * this predefined alignment in the geometry is used instead. By default\n     * it is set to 1MiB.\n     */\n    uint32_t alignment;\n\n    /* 12: Alignment offset for \"stacked\" devices. For example, if the \"super\"\n     * partition itself is not aligned within the parent block device's\n     * partition table, then we adjust for this in deciding where to place\n     * |first_logical_sector|.\n     *\n     * Similar to |alignment|, this will be derived from the operating system.\n     * If it cannot be determined, it is assumed to be 0.\n     */\n    uint32_t alignment_offset;\n\n    /* 16: Block device size, as specified when the metadata was created. This\n     * can be used to verify the geometry against a target device.\n     */\n    uint64_t size;\n\n    /* 24: Partition name in the GPT. Any unused characters must be 0. */\n    char partition_name[36];\n\n    /* 60: Flags (see LP_BLOCK_DEVICE_* flags below). */\n    uint32_t flags;\n} __attribute__((packed)) LpMetadataBlockDevice;\n\n/* This flag is only intended to be used with super_empty.img and super.img on\n * retrofit devices. On these devices there are A and B super partitions, and\n * we don't know ahead of time which slot the image will be applied to.\n *\n * If set, the block device needs a slot suffix applied before being used with\n * IPartitionOpener. The slot suffix is determined by the metadata slot number\n * (0 = _a, 1 = _b).\n */\n#define LP_BLOCK_DEVICE_SLOT_SUFFIXED (1 << 0)\n\n/* For ease of writing compatibility checks, the original metadata header is\n * preserved below, and typedefs are provided for the current version.\n */\ntypedef struct LpMetadataHeaderV1_0 {\n    uint32_t magic;\n    uint16_t major_version;\n    uint16_t minor_version;\n    uint32_t header_size;\n    uint8_t header_checksum[32];\n    uint32_t tables_size;\n    uint8_t tables_checksum[32];\n    LpMetadataTableDescriptor partitions;\n    LpMetadataTableDescriptor extents;\n    LpMetadataTableDescriptor groups;\n    LpMetadataTableDescriptor block_devices;\n} __attribute__((packed)) LpMetadataHeaderV1_0;\n\ntypedef LpMetadataHeader LpMetadataHeaderV1_2;\n\n#ifdef __cplusplus\n} /* extern \"C\" */\n#endif\n\n#endif /* LOGICAL_PARTITION_METADATA_FORMAT_H_ */\n"
  },
  {
    "path": "fs_mgr/liblp/include/liblp/mock_property_fetcher.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <gmock/gmock.h>\n\n#include <liblp/property_fetcher.h>\n\nnamespace android {\nnamespace fs_mgr {\nnamespace testing {\n\nclass MockPropertyFetcher : public IPropertyFetcher {\n  public:\n    MOCK_METHOD2(GetProperty, std::string(const std::string&, const std::string&));\n    MOCK_METHOD2(GetBoolProperty, bool(const std::string&, bool));\n\n    // By default, return default_value for all functions.\n    MockPropertyFetcher() {\n        using ::testing::_;\n        using ::testing::Invoke;\n        ON_CALL(*this, GetProperty(_, _)).WillByDefault(Invoke([](const auto&, const auto& def) {\n            return def;\n        }));\n        ON_CALL(*this, GetBoolProperty(_, _)).WillByDefault(Invoke([](const auto&, auto def) {\n            return def;\n        }));\n    }\n};\n\nstatic inline void ResetMockPropertyFetcher() {\n    IPropertyFetcher::OverrideForTesting(\n            std::make_unique<::testing::NiceMock<MockPropertyFetcher>>());\n}\n\nstatic inline MockPropertyFetcher* GetMockedPropertyFetcher() {\n    return static_cast<MockPropertyFetcher*>(IPropertyFetcher::GetInstance());\n}\n\n}  // namespace testing\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/include/liblp/partition_opener.h",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <stdint.h>\n\n#include <string>\n\n#include <android-base/unique_fd.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nstruct BlockDeviceInfo {\n    BlockDeviceInfo() : size(0), alignment(0), alignment_offset(0), logical_block_size(0) {}\n    BlockDeviceInfo(const std::string& partition_name, uint64_t size, uint32_t alignment,\n                    uint32_t alignment_offset, uint32_t logical_block_size)\n        : size(size),\n          alignment(alignment),\n          alignment_offset(alignment_offset),\n          logical_block_size(logical_block_size),\n          partition_name(partition_name) {}\n    // Size of the block device, in bytes.\n    uint64_t size;\n    // Optimal target alignment, in bytes. Partition extents will be aligned to\n    // this value by default. This value must be 0 or a multiple of 512.\n    uint32_t alignment;\n    // Alignment offset to parent device (if any), in bytes. The sector at\n    // |alignment_offset| on the target device is correctly aligned on its\n    // parent device. This value must be 0 or a multiple of 512.\n    uint32_t alignment_offset;\n    // Block size, for aligning extent sizes and partition sizes.\n    uint32_t logical_block_size;\n    // The physical partition name for this block device, as it would appear in\n    // the GPT or under /dev/block/by-name.\n    std::string partition_name;\n};\n\n// Test-friendly interface for interacting with partitions.\nclass IPartitionOpener {\n  public:\n    virtual ~IPartitionOpener() = default;\n\n    // Open the given named physical partition with the provided open() flags.\n    // The name can be an absolute path if the full path is already known.\n    virtual android::base::unique_fd Open(const std::string& partition_name, int flags) const = 0;\n\n    // Return block device information about the given named physical partition.\n    // The name can be an absolute path if the full path is already known.\n    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const = 0;\n\n    // Return a path that can be used to pass the block device to device-mapper.\n    // This must either result in an absolute path, or a major:minor device\n    // sequence.\n    virtual std::string GetDeviceString(const std::string& partition_name) const = 0;\n};\n\n// Helper class to implement IPartitionOpener. If |partition_name| is not an\n// absolute path, /dev/block/by-name/ will be prepended.\nclass PartitionOpener : public IPartitionOpener {\n  public:\n    virtual android::base::unique_fd Open(const std::string& partition_name,\n                                          int flags) const override;\n    virtual bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;\n    virtual std::string GetDeviceString(const std::string& partition_name) const override;\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/include/liblp/property_fetcher.h",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <memory>\n\nnamespace android {\nnamespace fs_mgr {\n\nclass IPropertyFetcher {\n  public:\n    virtual ~IPropertyFetcher() = default;\n    virtual std::string GetProperty(const std::string& key, const std::string& defaultValue) = 0;\n    virtual bool GetBoolProperty(const std::string& key, bool defaultValue) = 0;\n\n    static IPropertyFetcher* GetInstance();\n    static void OverrideForTesting(std::unique_ptr<IPropertyFetcher>&&);\n};\n\nclass PropertyFetcher : public IPropertyFetcher {\n  public:\n    ~PropertyFetcher() = default;\n    std::string GetProperty(const std::string& key, const std::string& defaultValue) override;\n    bool GetBoolProperty(const std::string& key, bool defaultValue) override;\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/include/liblp/super_layout_builder.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#pragma once\n\n#include <stdint.h>\n\n#include <memory>\n#include <ostream>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include <android-base/unique_fd.h>\n#include <liblp/builder.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nstruct SuperImageExtent {\n    enum class Type { INVALID, DATA, PARTITION, ZERO, DONTCARE };\n\n    SuperImageExtent(const SuperImageExtent& other) = default;\n    SuperImageExtent(SuperImageExtent&& other) = default;\n    SuperImageExtent(uint64_t offset, uint64_t size, Type type)\n        : offset(offset), size(size), type(type) {}\n\n    SuperImageExtent(uint64_t offset, std::shared_ptr<std::string> blob)\n        : SuperImageExtent(offset, blob->size(), Type::DATA) {\n        this->blob = blob;\n    }\n\n    SuperImageExtent(uint64_t offset, uint64_t size, const std::string& image_name,\n                     uint64_t image_offset)\n        : SuperImageExtent(offset, size, Type::PARTITION) {\n        this->image_name = image_name;\n        this->image_offset = image_offset;\n    }\n\n    SuperImageExtent& operator=(const SuperImageExtent& other) = default;\n    SuperImageExtent& operator=(SuperImageExtent&& other) = default;\n\n    bool operator<(const SuperImageExtent& other) const { return offset < other.offset; }\n    bool operator==(const SuperImageExtent& other) const;\n\n    // Location, size, and type of the extent.\n    uint64_t offset = 0;\n    uint64_t size = 0;\n    Type type = Type::INVALID;\n\n    // If type == DATA, this contains the bytes to write.\n    std::shared_ptr<std::string> blob;\n    // If type == PARTITION, this contains the partition image name and\n    // offset within that file.\n    std::string image_name;\n    uint64_t image_offset = 0;\n};\n\n// The SuperLayoutBuilder allows building a sparse view of a super image. This\n// is useful for efficient flashing, eg to bypass fastbootd and directly flash\n// super without physically building and storing the image.\nclass SuperLayoutBuilder final {\n  public:\n    // Open a super_empty.img, return false on failure. This must be called to\n    // initialize the tool. If it returns false, either the image failed to\n    // parse, or the tool is not compatible with how the device is configured\n    // (in which case fastbootd should be preferred).\n    [[nodiscard]] bool Open(android::base::borrowed_fd fd);\n    [[nodiscard]] bool Open(const void* data, size_t bytes);\n    [[nodiscard]] bool Open(const LpMetadata& metadata);\n\n    // Add a partition's image and size to the work list. If false is returned,\n    // there was either a duplicate partition or not enough space in super.\n    bool AddPartition(const std::string& partition_name, const std::string& image_name,\n                      uint64_t partition_size);\n\n    // Return the list of extents describing the super image. If this list is\n    // empty, then there was an unrecoverable error in building the list.\n    std::vector<SuperImageExtent> GetImageLayout();\n\n    // Return the current metadata.\n    std::unique_ptr<LpMetadata> Export() const { return builder_->Export(); }\n\n  private:\n    std::unique_ptr<MetadataBuilder> builder_;\n    std::unordered_map<std::string, std::string> image_map_;\n};\n\nstd::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent);\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/io_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fcntl.h>\n#include <linux/memfd.h>\n#include <stdio.h>\n#include <sys/syscall.h>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n#include <fs_mgr.h>\n#include <fstab/fstab.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <liblp/builder.h>\n\n#include \"images.h\"\n#include \"liblp_test.h\"\n#include \"reader.h\"\n#include \"test_partition_opener.h\"\n#include \"utility.h\"\n#include \"writer.h\"\n\nusing namespace std;\nusing namespace android::fs_mgr;\nusing namespace android::fs_mgr::testing;\nusing ::testing::_;\nusing ::testing::Return;\nusing unique_fd = android::base::unique_fd;\nusing android::base::GetProperty;\n\n// Our tests assume a 128KiB disk with two 512 byte metadata slots.\nstatic const size_t kDiskSize = 131072;\nstatic const size_t kMetadataSize = 512;\nstatic const size_t kMetadataSlots = 2;\nstatic const BlockDeviceInfo kSuperInfo{\"super\", kDiskSize, 0, 0, 4096};\n\n// Helper function for creating an in-memory file descriptor. This lets us\n// simulate read/writing logical partition metadata as if we had a block device\n// for a physical partition.\nstatic unique_fd CreateFakeDisk(off_t size) {\n    unique_fd fd(syscall(__NR_memfd_create, \"fake_disk\", MFD_ALLOW_SEALING));\n    if (fd < 0) {\n        perror(\"memfd_create\");\n        return {};\n    }\n    if (ftruncate(fd, size) < 0) {\n        perror(\"ftruncate\");\n        return {};\n    }\n    // Prevent anything from accidentally growing/shrinking the file, as it\n    // would not be allowed on an actual partition.\n    if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {\n        perror(\"fcntl\");\n        return {};\n    }\n    // Write garbage to the \"disk\" so we can tell what has been zeroed or not.\n    unique_ptr<uint8_t[]> buffer = make_unique<uint8_t[]>(size);\n    memset(buffer.get(), 0xcc, size);\n    if (!android::base::WriteFully(fd, buffer.get(), size)) {\n        return {};\n    }\n    return fd;\n}\n\n// Create a disk of the default size.\nstatic unique_fd CreateFakeDisk() {\n    return CreateFakeDisk(kDiskSize);\n}\n\n// Create a MetadataBuilder around some default sizes.\nstatic unique_ptr<MetadataBuilder> CreateDefaultBuilder() {\n    unique_ptr<MetadataBuilder> builder =\n            MetadataBuilder::New(kDiskSize, kMetadataSize, kMetadataSlots);\n    return builder;\n}\n\nclass DefaultPartitionOpener final : public TestPartitionOpener {\n  public:\n    explicit DefaultPartitionOpener(int fd)\n        : TestPartitionOpener({{\"super\", fd}}, {{\"super\", kSuperInfo}}) {}\n};\n\nstatic bool AddDefaultPartitions(MetadataBuilder* builder) {\n    Partition* system = builder->AddPartition(\"system\", LP_PARTITION_ATTR_NONE);\n    if (!system) {\n        return false;\n    }\n    return builder->ResizePartition(system, 24 * 1024);\n}\n\n// Create a temporary disk and flash it with the default partition setup.\nstatic unique_fd CreateFlashedDisk() {\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    if (!builder || !AddDefaultPartitions(builder.get())) {\n        return {};\n    }\n    unique_fd fd = CreateFakeDisk();\n    if (fd < 0) {\n        return {};\n    }\n    // Export and flash.\n    unique_ptr<LpMetadata> exported = builder->Export();\n    if (!exported) {\n        return {};\n    }\n\n    DefaultPartitionOpener opener(fd);\n    if (!FlashPartitionTable(opener, \"super\", *exported.get())) {\n        return {};\n    }\n    return fd;\n}\n\n// Test that our CreateFakeDisk() function works.\nTEST_F(LiblpTest, CreateFakeDisk) {\n    unique_fd fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    uint64_t size;\n    ASSERT_TRUE(GetDescriptorSize(fd, &size));\n    ASSERT_EQ(size, kDiskSize);\n\n    DefaultPartitionOpener opener(fd);\n\n    // Verify that we can't read unwritten metadata.\n    ASSERT_EQ(ReadMetadata(opener, \"super\", 1), nullptr);\n}\n\n// Flashing metadata should not work if the metadata was created for a larger\n// disk than the destination disk.\nTEST_F(LiblpTest, ExportDiskTooSmall) {\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(kDiskSize + 4096, 512, 2);\n    ASSERT_NE(builder, nullptr);\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    // A larger geometry should fail to flash, since there won't be enough\n    // space to store the logical partition range that was specified.\n    unique_fd fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    EXPECT_FALSE(FlashPartitionTable(opener, \"super\", *exported.get()));\n}\n\n// Test the basics of flashing a partition and reading it back.\nTEST_F(LiblpTest, FlashAndReadback) {\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(AddDefaultPartitions(builder.get()));\n\n    unique_fd fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    // Export and flash.\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    ASSERT_TRUE(FlashPartitionTable(opener, \"super\", *exported.get()));\n\n    // Read back. Note that some fields are only filled in during\n    // serialization, so exported and imported will not be identical. For\n    // example, table sizes and checksums are computed in WritePartitionTable.\n    // Therefore we check on a field-by-field basis.\n    unique_ptr<LpMetadata> imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n\n    // Check geometry and header.\n    EXPECT_EQ(exported->geometry.metadata_max_size, imported->geometry.metadata_max_size);\n    EXPECT_EQ(exported->geometry.metadata_slot_count, imported->geometry.metadata_slot_count);\n    EXPECT_EQ(exported->header.major_version, imported->header.major_version);\n    EXPECT_EQ(exported->header.minor_version, imported->header.minor_version);\n    EXPECT_EQ(exported->header.header_size, imported->header.header_size);\n\n    // Check partition tables.\n    ASSERT_EQ(exported->partitions.size(), imported->partitions.size());\n    EXPECT_EQ(GetPartitionName(exported->partitions[0]), GetPartitionName(imported->partitions[0]));\n    EXPECT_EQ(exported->partitions[0].attributes, imported->partitions[0].attributes);\n    EXPECT_EQ(exported->partitions[0].first_extent_index,\n              imported->partitions[0].first_extent_index);\n    EXPECT_EQ(exported->partitions[0].num_extents, imported->partitions[0].num_extents);\n\n    // Check extent tables.\n    ASSERT_EQ(exported->extents.size(), imported->extents.size());\n    EXPECT_EQ(exported->extents[0].num_sectors, imported->extents[0].num_sectors);\n    EXPECT_EQ(exported->extents[0].target_type, imported->extents[0].target_type);\n    EXPECT_EQ(exported->extents[0].target_data, imported->extents[0].target_data);\n\n    // Check block devices table.\n    ASSERT_EQ(exported->block_devices.size(), imported->block_devices.size());\n    EXPECT_EQ(exported->block_devices[0].first_logical_sector,\n              imported->block_devices[0].first_logical_sector);\n}\n\n// Test that we can update metadata slots without disturbing others.\nTEST_F(LiblpTest, UpdateAnyMetadataSlot) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    unique_ptr<LpMetadata> imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_EQ(imported->partitions.size(), 1);\n    EXPECT_EQ(GetPartitionName(imported->partitions[0]), \"system\");\n\n    // Change the name before writing to the next slot.\n    strncpy(imported->partitions[0].name, \"vendor\", sizeof(imported->partitions[0].name));\n    ASSERT_TRUE(UpdatePartitionTable(opener, \"super\", *imported.get(), 1));\n\n    // Read back the original slot, make sure it hasn't changed.\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_EQ(imported->partitions.size(), 1);\n    EXPECT_EQ(GetPartitionName(imported->partitions[0]), \"system\");\n\n    // Now read back the new slot, and verify that it has a different name.\n    imported = ReadMetadata(opener, \"super\", 1);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_EQ(imported->partitions.size(), 1);\n    EXPECT_EQ(GetPartitionName(imported->partitions[0]), \"vendor\");\n\n    auto super_device = GetMetadataSuperBlockDevice(*imported.get());\n    ASSERT_NE(super_device, nullptr);\n\n    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;\n\n    // Verify that we didn't overwrite anything in the logical paritition area.\n    // We expect the disk to be filled with 0xcc on creation so we can read\n    // this back and compare it.\n    char expected[LP_SECTOR_SIZE];\n    memset(expected, 0xcc, sizeof(expected));\n    for (uint64_t i = super_device->first_logical_sector; i < last_sector; i++) {\n        char buffer[LP_SECTOR_SIZE];\n        ASSERT_GE(lseek(fd, i * LP_SECTOR_SIZE, SEEK_SET), 0);\n        ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));\n        ASSERT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);\n    }\n}\n\nTEST_F(LiblpTest, InvalidMetadataSlot) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    // Make sure all slots are filled.\n    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(metadata, nullptr);\n    for (uint32_t i = 1; i < kMetadataSlots; i++) {\n        ASSERT_TRUE(UpdatePartitionTable(opener, \"super\", *metadata.get(), i));\n    }\n\n    // Verify that we can't read unavailable slots.\n    EXPECT_EQ(ReadMetadata(opener, \"super\", kMetadataSlots), nullptr);\n}\n\n// Test that updating a metadata slot does not allow it to be computed based\n// on mismatching geometry.\nTEST_F(LiblpTest, NoChangingGeometry) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    unique_ptr<LpMetadata> imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_TRUE(UpdatePartitionTable(opener, \"super\", *imported.get(), 1));\n\n    imported->geometry.metadata_max_size += LP_SECTOR_SIZE;\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *imported.get(), 1));\n\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    imported->geometry.metadata_slot_count++;\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *imported.get(), 1));\n\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_EQ(imported->block_devices.size(), 1);\n    imported->block_devices[0].first_logical_sector++;\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *imported.get(), 1));\n\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n}\n\n// Test that changing one bit of metadata is enough to break the checksum.\nTEST_F(LiblpTest, BitFlipGeometry) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    LpMetadataGeometry geometry;\n    ASSERT_GE(lseek(fd, 0, SEEK_SET), 0);\n    ASSERT_TRUE(android::base::ReadFully(fd, &geometry, sizeof(geometry)));\n\n    LpMetadataGeometry bad_geometry = geometry;\n    bad_geometry.metadata_slot_count++;\n    ASSERT_TRUE(android::base::WriteFully(fd, &bad_geometry, sizeof(bad_geometry)));\n\n    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(metadata, nullptr);\n    EXPECT_EQ(metadata->geometry.metadata_slot_count, 2);\n}\n\nTEST_F(LiblpTest, ReadBackupGeometry) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    char corruption[LP_METADATA_GEOMETRY_SIZE];\n    memset(corruption, 0xff, sizeof(corruption));\n\n    // Corrupt the primary geometry.\n    ASSERT_GE(lseek(fd, GetPrimaryGeometryOffset(), SEEK_SET), 0);\n    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));\n    EXPECT_NE(ReadMetadata(opener, \"super\", 0), nullptr);\n\n    // Corrupt the backup geometry.\n    ASSERT_GE(lseek(fd, GetBackupGeometryOffset(), SEEK_SET), 0);\n    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));\n    EXPECT_EQ(ReadMetadata(opener, \"super\", 0), nullptr);\n}\n\nTEST_F(LiblpTest, ReadBackupMetadata) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    unique_ptr<LpMetadata> metadata = ReadMetadata(opener, \"super\", 0);\n\n    char corruption[kMetadataSize];\n    memset(corruption, 0xff, sizeof(corruption));\n\n    off_t offset = GetPrimaryMetadataOffset(metadata->geometry, 0);\n\n    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);\n    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));\n    EXPECT_NE(ReadMetadata(opener, \"super\", 0), nullptr);\n\n    offset = GetBackupMetadataOffset(metadata->geometry, 0);\n\n    // Corrupt the backup metadata.\n    ASSERT_GE(lseek(fd, offset, SEEK_SET), 0);\n    ASSERT_TRUE(android::base::WriteFully(fd, corruption, sizeof(corruption)));\n    EXPECT_EQ(ReadMetadata(opener, \"super\", 0), nullptr);\n}\n\n// Test that we don't attempt to write metadata if it would overflow its\n// reserved space.\nTEST_F(LiblpTest, TooManyPartitions) {\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    ASSERT_NE(builder, nullptr);\n\n    // Compute the maximum number of partitions we can fit in 512 bytes of\n    // metadata. By default there is the header, one partition group, and a\n    // block device entry.\n    static const size_t kMaxPartitionTableSize = kMetadataSize - sizeof(LpMetadataHeaderV1_0) -\n                                                 sizeof(LpMetadataPartitionGroup) -\n                                                 sizeof(LpMetadataBlockDevice);\n    size_t max_partitions = kMaxPartitionTableSize / sizeof(LpMetadataPartition);\n\n    // Add this number of partitions.\n    Partition* partition = nullptr;\n    for (size_t i = 0; i < max_partitions; i++) {\n        partition = builder->AddPartition(to_string(i), LP_PARTITION_ATTR_NONE);\n        ASSERT_NE(partition, nullptr);\n    }\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    unique_fd fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    // Check that we are able to write our table.\n    ASSERT_TRUE(FlashPartitionTable(opener, \"super\", *exported.get()));\n\n    // Check that adding one more partition overflows the metadata allotment.\n    partition = builder->AddPartition(\"final\", LP_PARTITION_ATTR_NONE);\n    EXPECT_NE(partition, nullptr);\n\n    exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    // The new table should be too large to be written.\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *exported.get(), 1));\n\n    auto super_device = GetMetadataSuperBlockDevice(*exported.get());\n    ASSERT_NE(super_device, nullptr);\n\n    // Check that the first and last logical sectors weren't touched when we\n    // wrote this almost-full metadata.\n    char expected[LP_SECTOR_SIZE];\n    memset(expected, 0xcc, sizeof(expected));\n    char buffer[LP_SECTOR_SIZE];\n    ASSERT_GE(lseek(fd, super_device->first_logical_sector * LP_SECTOR_SIZE, SEEK_SET), 0);\n    ASSERT_TRUE(android::base::ReadFully(fd, buffer, sizeof(buffer)));\n    EXPECT_EQ(memcmp(expected, buffer, LP_SECTOR_SIZE), 0);\n}\n\n// Test that we can read and write image files.\nTEST_F(LiblpTest, ImageFiles) {\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(AddDefaultPartitions(builder.get()));\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    unique_fd fd(syscall(__NR_memfd_create, \"image_file\", 0));\n    ASSERT_GE(fd, 0);\n    ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));\n\n    unique_ptr<LpMetadata> imported = ReadFromImageFile(fd);\n    ASSERT_NE(imported, nullptr);\n}\n\n// Test that we can read images from buffers.\nTEST_F(LiblpTest, ImageFilesInMemory) {\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(AddDefaultPartitions(builder.get()));\n    unique_ptr<LpMetadata> exported = builder->Export();\n\n    unique_fd fd(syscall(__NR_memfd_create, \"image_file\", 0));\n    ASSERT_GE(fd, 0);\n    ASSERT_TRUE(WriteToImageFile(fd, *exported.get()));\n\n    int64_t offset = SeekFile64(fd, 0, SEEK_CUR);\n    ASSERT_GE(offset, 0);\n    ASSERT_EQ(SeekFile64(fd, 0, SEEK_SET), 0);\n\n    size_t bytes = static_cast<size_t>(offset);\n    std::unique_ptr<char[]> buffer = std::make_unique<char[]>(bytes);\n    ASSERT_TRUE(android::base::ReadFully(fd, buffer.get(), bytes));\n    ASSERT_NE(ReadFromImageBlob(buffer.get(), bytes), nullptr);\n}\n\nclass BadWriter {\n  public:\n    // When requested, write garbage instead of the requested bytes, then\n    // return false.\n    bool operator()(int fd, const std::string& blob) {\n        write_count_++;\n        if (write_count_ == fail_on_write_) {\n            std::unique_ptr<char[]> new_data = std::make_unique<char[]>(blob.size());\n            memset(new_data.get(), 0xe5, blob.size());\n            EXPECT_TRUE(android::base::WriteFully(fd, new_data.get(), blob.size()));\n            return false;\n        } else {\n            if (!android::base::WriteFully(fd, blob.data(), blob.size())) {\n                return false;\n            }\n            return fail_after_write_ != write_count_;\n        }\n    }\n    void Reset() {\n        fail_on_write_ = 0;\n        fail_after_write_ = 0;\n        write_count_ = 0;\n    }\n    void FailOnWrite(int number) {\n        Reset();\n        fail_on_write_ = number;\n    }\n    void FailAfterWrite(int number) {\n        Reset();\n        fail_after_write_ = number;\n    }\n\n  private:\n    int fail_on_write_ = 0;\n    int fail_after_write_ = 0;\n    int write_count_ = 0;\n};\n\n// Test that an interrupted flash operation on the \"primary\" copy of metadata\n// is not fatal.\nTEST_F(LiblpTest, UpdatePrimaryMetadataFailure) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    BadWriter writer;\n\n    // Read and write it back.\n    writer.FailOnWrite(1);\n    unique_ptr<LpMetadata> imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *imported.get(), 0, writer));\n\n    // We should still be able to read the backup copy.\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n\n    // Flash again, this time fail the backup copy. We should still be able\n    // to read the primary.\n    writer.FailOnWrite(3);\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *imported.get(), 0, writer));\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n}\n\n// Test that an interrupted flash operation on the \"backup\" copy of metadata\n// is not fatal.\nTEST_F(LiblpTest, UpdateBackupMetadataFailure) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    BadWriter writer;\n\n    // Read and write it back.\n    writer.FailOnWrite(2);\n    unique_ptr<LpMetadata> imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *imported.get(), 0, writer));\n\n    // We should still be able to read the primary copy.\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n\n    // Flash again, this time fail the primary copy. We should still be able\n    // to read the primary.\n    writer.FailOnWrite(2);\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *imported.get(), 0, writer));\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n}\n\n// Test that an interrupted write *in between* writing metadata will read\n// the correct metadata copy. The primary is always considered newer than\n// the backup.\nTEST_F(LiblpTest, UpdateMetadataCleanFailure) {\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    BadWriter writer;\n\n    // Change the name of the existing partition.\n    unique_ptr<LpMetadata> new_table = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(new_table, nullptr);\n    ASSERT_GE(new_table->partitions.size(), 1);\n    new_table->partitions[0].name[0]++;\n\n    // Flash it, but fail to write the backup copy.\n    writer.FailAfterWrite(2);\n    ASSERT_FALSE(UpdatePartitionTable(opener, \"super\", *new_table.get(), 0, writer));\n\n    // When we read back, we should get the updated primary copy.\n    unique_ptr<LpMetadata> imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_GE(new_table->partitions.size(), 1);\n    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));\n\n    // Flash again. After, the backup and primary copy should be coherent.\n    // Note that the sync step should have used the primary to sync, not\n    // the backup.\n    writer.Reset();\n    ASSERT_TRUE(UpdatePartitionTable(opener, \"super\", *new_table.get(), 0, writer));\n\n    imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    ASSERT_GE(new_table->partitions.size(), 1);\n    ASSERT_EQ(GetPartitionName(new_table->partitions[0]), GetPartitionName(imported->partitions[0]));\n}\n\n// Test that writing a sparse image can be read back.\nTEST_F(LiblpTest, FlashSparseImage) {\n    unique_fd fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    BlockDeviceInfo device_info(\"super\", kDiskSize, 0, 0, 512);\n    unique_ptr<MetadataBuilder> builder =\n            MetadataBuilder::New(device_info, kMetadataSize, kMetadataSlots);\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(AddDefaultPartitions(builder.get()));\n\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n\n    // Build the sparse file.\n    ImageBuilder sparse(*exported.get(), 512, {}, true /* sparsify */);\n    ASSERT_TRUE(sparse.IsValid());\n    ASSERT_TRUE(sparse.Build());\n\n    const auto& images = sparse.device_images();\n    ASSERT_EQ(images.size(), static_cast<size_t>(1));\n\n    // Write it to the fake disk.\n    ASSERT_NE(lseek(fd.get(), 0, SEEK_SET), -1);\n    int ret = sparse_file_write(images[0].get(), fd.get(), false, false, false);\n    ASSERT_EQ(ret, 0);\n\n    // Verify that we can read both sets of metadata.\n    LpMetadataGeometry geometry;\n    ASSERT_TRUE(ReadPrimaryGeometry(fd.get(), &geometry));\n    ASSERT_TRUE(ReadBackupGeometry(fd.get(), &geometry));\n    ASSERT_NE(ReadPrimaryMetadata(fd.get(), geometry, 0), nullptr);\n    ASSERT_NE(ReadBackupMetadata(fd.get(), geometry, 0), nullptr);\n}\n\nTEST_F(LiblpTest, AutoSlotSuffixing) {\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(AddDefaultPartitions(builder.get()));\n    ASSERT_TRUE(builder->AddGroup(\"example\", 0));\n    builder->SetAutoSlotSuffixing();\n\n    auto fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    // Note: we bind the same fd to both names, since we want to make sure the\n    // exact same bits are getting read back in each test.\n    TestPartitionOpener opener({{\"super_a\", fd}, {\"super_b\", fd}},\n                               {{\"super_a\", kSuperInfo}, {\"super_b\", kSuperInfo}});\n    auto exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    ASSERT_TRUE(FlashPartitionTable(opener, \"super_a\", *exported.get()));\n\n    auto metadata = ReadMetadata(opener, \"super_b\", 1);\n    ASSERT_NE(metadata, nullptr);\n    ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));\n    EXPECT_EQ(GetPartitionName(metadata->partitions[0]), \"system_b\");\n    ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));\n    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), \"super_b\");\n    ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));\n    EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), \"default\");\n    EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), \"example_b\");\n    EXPECT_EQ(metadata->groups[0].flags, 0);\n    EXPECT_EQ(metadata->groups[1].flags, 0);\n\n    metadata = ReadMetadata(opener, \"super_a\", 0);\n    ASSERT_NE(metadata, nullptr);\n    ASSERT_EQ(metadata->partitions.size(), static_cast<size_t>(1));\n    EXPECT_EQ(GetPartitionName(metadata->partitions[0]), \"system_a\");\n    ASSERT_EQ(metadata->block_devices.size(), static_cast<size_t>(1));\n    EXPECT_EQ(GetBlockDevicePartitionName(metadata->block_devices[0]), \"super_a\");\n    ASSERT_EQ(metadata->groups.size(), static_cast<size_t>(2));\n    EXPECT_EQ(GetPartitionGroupName(metadata->groups[0]), \"default\");\n    EXPECT_EQ(GetPartitionGroupName(metadata->groups[1]), \"example_a\");\n    EXPECT_EQ(metadata->groups[0].flags, 0);\n    EXPECT_EQ(metadata->groups[1].flags, 0);\n}\n\nTEST_F(LiblpTest, UpdateRetrofit) {\n    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty(\"ro.boot.dynamic_partitions_retrofit\", _))\n            .WillByDefault(Return(true));\n\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(AddDefaultPartitions(builder.get()));\n    ASSERT_TRUE(builder->AddGroup(\"example\", 0));\n    builder->SetAutoSlotSuffixing();\n\n    auto fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    // Note: we bind the same fd to both names, since we want to make sure the\n    // exact same bits are getting read back in each test.\n    TestPartitionOpener opener({{\"super_a\", fd}, {\"super_b\", fd}},\n                               {{\"super_a\", kSuperInfo}, {\"super_b\", kSuperInfo}});\n    auto exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    ASSERT_TRUE(FlashPartitionTable(opener, \"super_a\", *exported.get()));\n\n    builder = MetadataBuilder::NewForUpdate(opener, \"super_a\", 0, 1);\n    ASSERT_NE(builder, nullptr);\n    auto updated = builder->Export();\n    ASSERT_NE(updated, nullptr);\n    ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));\n    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), \"super_b\");\n    ASSERT_TRUE(updated->groups.empty());\n    ASSERT_TRUE(updated->partitions.empty());\n    ASSERT_TRUE(updated->extents.empty());\n}\n\nTEST_F(LiblpTest, UpdateNonRetrofit) {\n    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty(\"ro.boot.dynamic_partitions_retrofit\", _))\n            .WillByDefault(Return(false));\n\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n    auto builder = MetadataBuilder::NewForUpdate(opener, \"super\", 0, 1);\n    ASSERT_NE(builder, nullptr);\n    auto updated = builder->Export();\n    ASSERT_NE(updated, nullptr);\n    ASSERT_EQ(updated->block_devices.size(), static_cast<size_t>(1));\n    EXPECT_EQ(GetBlockDevicePartitionName(updated->block_devices[0]), \"super\");\n}\n\nTEST_F(LiblpTest, UpdateVirtualAB) {\n    ON_CALL(*GetMockedPropertyFetcher(), GetBoolProperty(\"ro.virtual_ab.enabled\", _))\n            .WillByDefault(Return(true));\n\n    unique_fd fd = CreateFlashedDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n    auto builder = MetadataBuilder::NewForUpdate(opener, \"super\", 0, 1);\n    ASSERT_NE(builder, nullptr);\n    auto updated = builder->Export();\n    ASSERT_NE(updated, nullptr);\n    ASSERT_TRUE(UpdatePartitionTable(opener, \"super\", *updated.get(), 1));\n\n    // Validate old slot.\n    auto metadata = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(metadata, nullptr);\n    ASSERT_EQ(metadata->header.minor_version, 0);\n    ASSERT_GE(metadata->partitions.size(), 1);\n    ASSERT_EQ(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);\n\n    // Validate new slot.\n    metadata = ReadMetadata(opener, \"super\", 1);\n    ASSERT_NE(metadata, nullptr);\n    ASSERT_EQ(metadata->header.minor_version, 1);\n    ASSERT_GE(metadata->partitions.size(), 1);\n    ASSERT_NE(metadata->partitions[0].attributes & LP_PARTITION_ATTR_UPDATED, 0);\n}\n\nTEST_F(LiblpTest, ReadExpandedHeader) {\n    unique_ptr<MetadataBuilder> builder = CreateDefaultBuilder();\n    ASSERT_NE(builder, nullptr);\n    ASSERT_TRUE(AddDefaultPartitions(builder.get()));\n\n    builder->RequireExpandedMetadataHeader();\n\n    unique_fd fd = CreateFakeDisk();\n    ASSERT_GE(fd, 0);\n\n    DefaultPartitionOpener opener(fd);\n\n    // Export and flash.\n    unique_ptr<LpMetadata> exported = builder->Export();\n    ASSERT_NE(exported, nullptr);\n    exported->header.flags = 0x5e5e5e5e;\n    ASSERT_TRUE(FlashPartitionTable(opener, \"super\", *exported.get()));\n\n    unique_ptr<LpMetadata> imported = ReadMetadata(opener, \"super\", 0);\n    ASSERT_NE(imported, nullptr);\n    EXPECT_EQ(imported->header.header_size, sizeof(LpMetadataHeaderV1_2));\n    EXPECT_EQ(imported->header.header_size, exported->header.header_size);\n    EXPECT_EQ(imported->header.flags, exported->header.flags);\n}\n"
  },
  {
    "path": "fs_mgr/liblp/liblp_test.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <gtest/gtest.h>\n\n#include <liblp/mock_property_fetcher.h>\n\nnamespace android {\nnamespace fs_mgr {\nnamespace testing {\n\nclass LiblpTest : public ::testing::Test {\n  public:\n    void SetUp() override { ResetMockPropertyFetcher(); }\n    void TearDown() override { ResetMockPropertyFetcher(); }\n};\n\n}  // namespace testing\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/partition_opener.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"liblp/partition_opener.h\"\n\n#if defined(__linux__)\n#include <linux/fs.h>\n#endif\n#if !defined(_WIN32)\n#include <sys/ioctl.h>\n#endif\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/strings.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nusing android::base::unique_fd;\n\nnamespace {\n\nstd::string GetPartitionAbsolutePath(const std::string& path) {\n#if !defined(__ANDROID__)\n    return path;\n#else\n    if (android::base::StartsWith(path, \"/\")) {\n        return path;\n    }\n\n    auto by_name = \"/dev/block/by-name/\" + path;\n    if (access(by_name.c_str(), F_OK) != 0) {\n        // If the by-name symlink doesn't exist, as a special case we allow\n        // certain devices to be used as partition names. This can happen if a\n        // Dynamic System Update is installed to an sdcard, which won't be in\n        // the boot device list.\n        //\n        // mmcblk* is allowed because most devices in /dev/block are not valid for\n        // storing fiemaps.\n        if (android::base::StartsWith(path, \"mmcblk\")) {\n            return \"/dev/block/\" + path;\n        }\n    }\n    return by_name;\n#endif\n}\n\nbool GetBlockDeviceInfo(const std::string& block_device, BlockDeviceInfo* device_info) {\n#if defined(__linux__)\n    unique_fd fd = GetControlFileOrOpen(block_device.c_str(), O_RDONLY);\n    if (fd < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \"open '\" << block_device << \"' failed\";\n        return false;\n    }\n    if (!GetDescriptorSize(fd, &device_info->size)) {\n        return false;\n    }\n    if (ioctl(fd, BLKIOMIN, &device_info->alignment) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \"BLKIOMIN failed on \" << block_device;\n        return false;\n    }\n\n    int alignment_offset;\n    if (ioctl(fd, BLKALIGNOFF, &alignment_offset) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \"BLKALIGNOFF failed on \" << block_device;\n        return false;\n    }\n    // The kernel can return -1 here when misaligned devices are stacked (i.e.\n    // device-mapper).\n    if (alignment_offset == -1) {\n        alignment_offset = 0;\n    }\n\n    int logical_block_size;\n    if (ioctl(fd, BLKSSZGET, &logical_block_size) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \"BLKSSZGET failed on \" << block_device;\n        return false;\n    }\n\n    device_info->alignment_offset = static_cast<uint32_t>(alignment_offset);\n    device_info->logical_block_size = static_cast<uint32_t>(logical_block_size);\n    device_info->partition_name = android::base::Basename(block_device);\n    return true;\n#else\n    (void)block_device;\n    (void)device_info;\n    LERROR << __PRETTY_FUNCTION__ << \": Not supported on this operating system.\";\n    return false;\n#endif\n}\n\n}  // namespace\n\nunique_fd PartitionOpener::Open(const std::string& partition_name, int flags) const {\n    std::string path = GetPartitionAbsolutePath(partition_name);\n    return GetControlFileOrOpen(path.c_str(), flags | O_CLOEXEC);\n}\n\nbool PartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {\n    std::string path = GetPartitionAbsolutePath(partition_name);\n    return GetBlockDeviceInfo(path, info);\n}\n\nstd::string PartitionOpener::GetDeviceString(const std::string& partition_name) const {\n    return GetPartitionAbsolutePath(partition_name);\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/property_fetcher.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"liblp/property_fetcher.h\"\n\n#include <memory>\n\n#include <android-base/properties.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nstd::string PropertyFetcher::GetProperty(const std::string& key, const std::string& default_value) {\n    return android::base::GetProperty(key, default_value);\n}\n\nbool PropertyFetcher::GetBoolProperty(const std::string& key, bool default_value) {\n    return android::base::GetBoolProperty(key, default_value);\n}\n\nstatic std::unique_ptr<IPropertyFetcher>* GetInstanceAllocation() {\n    static std::unique_ptr<IPropertyFetcher> instance = std::make_unique<PropertyFetcher>();\n    return &instance;\n}\n\nIPropertyFetcher* IPropertyFetcher::GetInstance() {\n    return GetInstanceAllocation()->get();\n}\n\nvoid IPropertyFetcher::OverrideForTesting(std::unique_ptr<IPropertyFetcher>&& fetcher) {\n    GetInstanceAllocation()->swap(fetcher);\n    fetcher.reset();\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/reader.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"reader.h\"\n\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <functional>\n\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nstatic_assert(sizeof(LpMetadataHeaderV1_0) == offsetof(LpMetadataHeader, flags),\n              \"Incorrect LpMetadataHeader v0 size\");\n\n// Helper class for reading descriptors and memory buffers in the same manner.\nclass Reader {\n  public:\n    virtual ~Reader(){};\n    virtual bool ReadFully(void* buffer, size_t length) = 0;\n};\n\nclass FileReader final : public Reader {\n  public:\n    explicit FileReader(int fd) : fd_(fd) {}\n    bool ReadFully(void* buffer, size_t length) override {\n        return android::base::ReadFully(fd_, buffer, length);\n    }\n\n  private:\n    int fd_;\n};\n\nclass MemoryReader final : public Reader {\n  public:\n    MemoryReader(const void* buffer, size_t size)\n        : buffer_(reinterpret_cast<const uint8_t*>(buffer)), size_(size), pos_(0) {}\n    bool ReadFully(void* out, size_t length) override {\n        if (size_ - pos_ < length) {\n            errno = EINVAL;\n            return false;\n        }\n        memcpy(out, buffer_ + pos_, length);\n        pos_ += length;\n        return true;\n    }\n\n  private:\n    const uint8_t* buffer_;\n    size_t size_;\n    size_t pos_;\n};\n\nbool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry) {\n    static_assert(sizeof(*geometry) <= LP_METADATA_GEOMETRY_SIZE);\n    memcpy(geometry, buffer, sizeof(*geometry));\n\n    // Check the magic signature.\n    if (geometry->magic != LP_METADATA_GEOMETRY_MAGIC) {\n        LERROR << \"Logical partition metadata has invalid geometry magic signature.\";\n        return false;\n    }\n    // Reject if the struct size is larger than what we compiled. This is so we\n    // can compute a checksum with the |struct_size| field rather than using\n    // sizeof.\n    if (geometry->struct_size > sizeof(LpMetadataGeometry)) {\n        LERROR << \"Logical partition metadata has unrecognized fields.\";\n        return false;\n    }\n    // Recompute and check the CRC32.\n    {\n        LpMetadataGeometry temp = *geometry;\n        memset(&temp.checksum, 0, sizeof(temp.checksum));\n        SHA256(&temp, temp.struct_size, temp.checksum);\n        if (memcmp(temp.checksum, geometry->checksum, sizeof(temp.checksum)) != 0) {\n            LERROR << \"Logical partition metadata has invalid geometry checksum.\";\n            return false;\n        }\n    }\n    // Check that the struct size is equal (this will have to change if we ever\n    // change the struct size in a release).\n    if (geometry->struct_size != sizeof(LpMetadataGeometry)) {\n        LERROR << \"Logical partition metadata has invalid struct size.\";\n        return false;\n    }\n    if (geometry->metadata_slot_count == 0) {\n        LERROR << \"Logical partition metadata has invalid slot count.\";\n        return false;\n    }\n    if (geometry->metadata_max_size % LP_SECTOR_SIZE != 0) {\n        LERROR << \"Metadata max size is not sector-aligned.\";\n        return false;\n    }\n    return true;\n}\n\nbool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry) {\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);\n    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed\";\n        return false;\n    }\n    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {\n        PERROR << __PRETTY_FUNCTION__ << \" read \" << LP_METADATA_GEOMETRY_SIZE << \" bytes failed\";\n        return false;\n    }\n    return ParseGeometry(buffer.get(), geometry);\n}\n\nbool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry) {\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(LP_METADATA_GEOMETRY_SIZE);\n    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed\";\n        return false;\n    }\n    if (!android::base::ReadFully(fd, buffer.get(), LP_METADATA_GEOMETRY_SIZE)) {\n        PERROR << __PRETTY_FUNCTION__ << \" backup read \" << LP_METADATA_GEOMETRY_SIZE\n               << \" bytes failed\";\n        return false;\n    }\n    return ParseGeometry(buffer.get(), geometry);\n}\n\n// Read and validate geometry information from a block device that holds\n// logical partitions. If the information is corrupted, this will attempt\n// to read it from a secondary backup location.\nbool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry) {\n    if (ReadPrimaryGeometry(fd, geometry)) {\n        return true;\n    }\n    return ReadBackupGeometry(fd, geometry);\n}\n\nstatic bool ValidateTableBounds(const LpMetadataHeader& header,\n                                const LpMetadataTableDescriptor& table) {\n    if (table.offset > header.tables_size) {\n        return false;\n    }\n    uint64_t table_size = uint64_t(table.num_entries) * table.entry_size;\n    if (header.tables_size - table.offset < table_size) {\n        return false;\n    }\n    return true;\n}\n\nstatic bool ReadMetadataHeader(Reader* reader, LpMetadata* metadata) {\n    // Note we zero the struct since older files will result in a partial read.\n    LpMetadataHeader& header = metadata->header;\n    memset(&header, 0, sizeof(header));\n\n    if (!reader->ReadFully(&header, sizeof(LpMetadataHeaderV1_0))) {\n        PERROR << __PRETTY_FUNCTION__ << \" read failed\";\n        return false;\n    }\n\n    // Do basic validity checks before computing the checksum.\n    if (header.magic != LP_METADATA_HEADER_MAGIC) {\n        LERROR << \"Logical partition metadata has invalid magic value.\";\n        return false;\n    }\n    if (header.major_version != LP_METADATA_MAJOR_VERSION ||\n        header.minor_version > LP_METADATA_MINOR_VERSION_MAX) {\n        LERROR << \"Logical partition metadata has incompatible version.\";\n        return false;\n    }\n\n    // Validate the header struct size against the reported version.\n    uint32_t expected_struct_size = sizeof(header);\n    if (header.minor_version < LP_METADATA_VERSION_FOR_EXPANDED_HEADER) {\n        expected_struct_size = sizeof(LpMetadataHeaderV1_0);\n    }\n    if (header.header_size != expected_struct_size) {\n        LERROR << \"Invalid partition metadata header struct size.\";\n        return false;\n    }\n\n    // Read in any remaining fields, the last step needed before checksumming.\n    if (size_t remaining_bytes = header.header_size - sizeof(LpMetadataHeaderV1_0)) {\n        uint8_t* offset = reinterpret_cast<uint8_t*>(&header) + sizeof(LpMetadataHeaderV1_0);\n        if (!reader->ReadFully(offset, remaining_bytes)) {\n            PERROR << __PRETTY_FUNCTION__ << \" read failed\";\n            return false;\n        }\n    }\n\n    // To compute the header's checksum, we have to temporarily set its checksum\n    // field to 0. Note that we must only compute up to |header_size|.\n    {\n        LpMetadataHeader temp = header;\n        memset(&temp.header_checksum, 0, sizeof(temp.header_checksum));\n        SHA256(&temp, temp.header_size, temp.header_checksum);\n        if (memcmp(temp.header_checksum, header.header_checksum, sizeof(temp.header_checksum)) !=\n            0) {\n            LERROR << \"Logical partition metadata has invalid checksum.\";\n            return false;\n        }\n    }\n\n    if (!ValidateTableBounds(header, header.partitions) ||\n        !ValidateTableBounds(header, header.extents) ||\n        !ValidateTableBounds(header, header.groups) ||\n        !ValidateTableBounds(header, header.block_devices)) {\n        LERROR << \"Logical partition metadata has invalid table bounds.\";\n        return false;\n    }\n    // Check that table entry sizes can accomodate their respective structs. If\n    // table sizes change, these checks will have to be adjusted.\n    if (header.partitions.entry_size != sizeof(LpMetadataPartition)) {\n        LERROR << \"Logical partition metadata has invalid partition table entry size.\";\n        return false;\n    }\n    if (header.extents.entry_size != sizeof(LpMetadataExtent)) {\n        LERROR << \"Logical partition metadata has invalid extent table entry size.\";\n        return false;\n    }\n    if (header.groups.entry_size != sizeof(LpMetadataPartitionGroup)) {\n        LERROR << \"Logical partition metadata has invalid group table entry size.\";\n        return false;\n    }\n    return true;\n}\n\n// Parse and validate all metadata at the current position in the given file\n// descriptor.\nstatic std::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry,\n                                                 Reader* reader) {\n    // First read and validate the header.\n    std::unique_ptr<LpMetadata> metadata = std::make_unique<LpMetadata>();\n\n    metadata->geometry = geometry;\n    if (!ReadMetadataHeader(reader, metadata.get())) {\n        return nullptr;\n    }\n\n    LpMetadataHeader& header = metadata->header;\n\n    // Check the table size.\n    if (header.tables_size > geometry.metadata_max_size) {\n        LERROR << \"Invalid partition metadata header table size.\";\n        return nullptr;\n    }\n\n    // Read the metadata payload. Allocation is fallible since the table size\n    // could be large.\n    std::unique_ptr<uint8_t[]> buffer(new (std::nothrow) uint8_t[header.tables_size]);\n    if (!buffer) {\n        LERROR << \"Out of memory reading logical partition tables.\";\n        return nullptr;\n    }\n    if (!reader->ReadFully(buffer.get(), header.tables_size)) {\n        PERROR << __PRETTY_FUNCTION__ << \" read \" << header.tables_size << \"bytes failed\";\n        return nullptr;\n    }\n\n    uint8_t checksum[32];\n    SHA256(buffer.get(), header.tables_size, checksum);\n    if (memcmp(checksum, header.tables_checksum, sizeof(checksum)) != 0) {\n        LERROR << \"Logical partition metadata has invalid table checksum.\";\n        return nullptr;\n    }\n\n    uint32_t valid_attributes = LP_PARTITION_ATTRIBUTE_MASK_V0;\n    if (metadata->header.minor_version >= LP_METADATA_VERSION_FOR_UPDATED_ATTR) {\n        valid_attributes |= LP_PARTITION_ATTRIBUTE_MASK_V1;\n    }\n\n    // ValidateTableSize ensured that |cursor| is valid for the number of\n    // entries in the table.\n    uint8_t* cursor = buffer.get() + header.partitions.offset;\n    for (size_t i = 0; i < header.partitions.num_entries; i++) {\n        LpMetadataPartition partition;\n        memcpy(&partition, cursor, sizeof(partition));\n        cursor += header.partitions.entry_size;\n\n        if (partition.attributes & ~valid_attributes) {\n            LERROR << \"Logical partition has invalid attribute set.\";\n            return nullptr;\n        }\n        if (partition.first_extent_index + partition.num_extents < partition.first_extent_index) {\n            LERROR << \"Logical partition first_extent_index + num_extents overflowed.\";\n            return nullptr;\n        }\n        if (partition.first_extent_index + partition.num_extents > header.extents.num_entries) {\n            LERROR << \"Logical partition has invalid extent list.\";\n            return nullptr;\n        }\n        if (partition.group_index >= header.groups.num_entries) {\n            LERROR << \"Logical partition has invalid group index.\";\n            return nullptr;\n        }\n\n        metadata->partitions.push_back(partition);\n    }\n\n    cursor = buffer.get() + header.extents.offset;\n    for (size_t i = 0; i < header.extents.num_entries; i++) {\n        LpMetadataExtent extent;\n        memcpy(&extent, cursor, sizeof(extent));\n        cursor += header.extents.entry_size;\n\n        if (extent.target_type == LP_TARGET_TYPE_LINEAR &&\n            extent.target_source >= header.block_devices.num_entries) {\n            LERROR << \"Logical partition extent has invalid block device.\";\n            return nullptr;\n        }\n\n        metadata->extents.push_back(extent);\n    }\n\n    cursor = buffer.get() + header.groups.offset;\n    for (size_t i = 0; i < header.groups.num_entries; i++) {\n        LpMetadataPartitionGroup group = {};\n        memcpy(&group, cursor, sizeof(group));\n        cursor += header.groups.entry_size;\n\n        metadata->groups.push_back(group);\n    }\n\n    cursor = buffer.get() + header.block_devices.offset;\n    for (size_t i = 0; i < header.block_devices.num_entries; i++) {\n        LpMetadataBlockDevice device = {};\n        memcpy(&device, cursor, sizeof(device));\n        cursor += header.block_devices.entry_size;\n\n        metadata->block_devices.push_back(device);\n    }\n\n    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(*metadata.get());\n    if (!super_device) {\n        LERROR << \"Metadata does not specify a super device.\";\n        return nullptr;\n    }\n\n    // Check that the metadata area and logical partition areas don't overlap.\n    uint64_t metadata_region =\n            GetTotalMetadataSize(geometry.metadata_max_size, geometry.metadata_slot_count);\n    if (metadata_region > super_device->first_logical_sector * LP_SECTOR_SIZE) {\n        LERROR << \"Logical partition metadata overlaps with logical partition contents.\";\n        return nullptr;\n    }\n    return metadata;\n}\n\nstd::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,\n                                          size_t size) {\n    MemoryReader reader(buffer, size);\n    return ParseMetadata(geometry, &reader);\n}\n\nstd::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd) {\n    FileReader reader(fd);\n    return ParseMetadata(geometry, &reader);\n}\n\nstd::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,\n                                                uint32_t slot_number) {\n    int64_t offset = GetPrimaryMetadataOffset(geometry, slot_number);\n    if (SeekFile64(fd, offset, SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed: offset \" << offset;\n        return nullptr;\n    }\n    return ParseMetadata(geometry, fd);\n}\n\nstd::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,\n                                               uint32_t slot_number) {\n    int64_t offset = GetBackupMetadataOffset(geometry, slot_number);\n    if (SeekFile64(fd, offset, SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed: offset \" << offset;\n        return nullptr;\n    }\n    return ParseMetadata(geometry, fd);\n}\n\nnamespace {\n\nbool AdjustMetadataForSlot(LpMetadata* metadata, uint32_t slot_number) {\n    std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);\n    for (auto& partition : metadata->partitions) {\n        if (!(partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED)) {\n            continue;\n        }\n        std::string partition_name = GetPartitionName(partition) + slot_suffix;\n        if (partition_name.size() > sizeof(partition.name)) {\n            LERROR << __PRETTY_FUNCTION__ << \" partition name too long: \" << partition_name;\n            return false;\n        }\n        strncpy(partition.name, partition_name.c_str(), sizeof(partition.name));\n        partition.attributes &= ~LP_PARTITION_ATTR_SLOT_SUFFIXED;\n    }\n    for (auto& block_device : metadata->block_devices) {\n        if (!(block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED)) {\n            continue;\n        }\n        std::string partition_name = GetBlockDevicePartitionName(block_device) + slot_suffix;\n        if (!UpdateBlockDevicePartitionName(&block_device, partition_name)) {\n            LERROR << __PRETTY_FUNCTION__ << \" partition name too long: \" << partition_name;\n            return false;\n        }\n        block_device.flags &= ~LP_BLOCK_DEVICE_SLOT_SUFFIXED;\n    }\n    for (auto& group : metadata->groups) {\n        if (!(group.flags & LP_GROUP_SLOT_SUFFIXED)) {\n            continue;\n        }\n        std::string group_name = GetPartitionGroupName(group) + slot_suffix;\n        if (!UpdatePartitionGroupName(&group, group_name)) {\n            LERROR << __PRETTY_FUNCTION__ << \" group name too long: \" << group_name;\n            return false;\n        }\n        group.flags &= ~LP_GROUP_SLOT_SUFFIXED;\n    }\n    return true;\n}\n\n}  // namespace\n\nstd::unique_ptr<LpMetadata> ReadMetadata(const IPartitionOpener& opener,\n                                         const std::string& super_partition, uint32_t slot_number) {\n    android::base::unique_fd fd = opener.Open(super_partition, O_RDONLY);\n    if (fd < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" open failed: \" << super_partition;\n        return nullptr;\n    }\n\n    LpMetadataGeometry geometry;\n    if (!ReadLogicalPartitionGeometry(fd, &geometry)) {\n        return nullptr;\n    }\n    if (slot_number >= geometry.metadata_slot_count) {\n        LERROR << __PRETTY_FUNCTION__ << \" invalid metadata slot number\";\n        return nullptr;\n    }\n\n    std::vector<int64_t> offsets = {\n            GetPrimaryMetadataOffset(geometry, slot_number),\n            GetBackupMetadataOffset(geometry, slot_number),\n    };\n    std::unique_ptr<LpMetadata> metadata;\n\n    for (const auto& offset : offsets) {\n        if (SeekFile64(fd, offset, SEEK_SET) < 0) {\n            PERROR << __PRETTY_FUNCTION__ << \" lseek failed, offset \" << offset;\n            continue;\n        }\n        if ((metadata = ParseMetadata(geometry, fd)) != nullptr) {\n            break;\n        }\n    }\n    if (!metadata || !AdjustMetadataForSlot(metadata.get(), slot_number)) {\n        return nullptr;\n    }\n    return metadata;\n}\n\nstd::unique_ptr<LpMetadata> ReadMetadata(const std::string& super_partition, uint32_t slot_number) {\n    return ReadMetadata(PartitionOpener(), super_partition, slot_number);\n}\n\nstatic std::string NameFromFixedArray(const char* name, size_t buffer_size) {\n    // If the end of the buffer has a null character, it's safe to assume the\n    // buffer is null terminated. Otherwise, we cap the string to the input\n    // buffer size.\n    if (name[buffer_size - 1] == '\\0') {\n        return std::string(name);\n    }\n    return std::string(name, buffer_size);\n}\n\nstd::string GetPartitionName(const LpMetadataPartition& partition) {\n    return NameFromFixedArray(partition.name, sizeof(partition.name));\n}\n\nstd::string GetPartitionGroupName(const LpMetadataPartitionGroup& group) {\n    return NameFromFixedArray(group.name, sizeof(group.name));\n}\n\nstd::string GetBlockDevicePartitionName(const LpMetadataBlockDevice& block_device) {\n    return NameFromFixedArray(block_device.partition_name, sizeof(block_device.partition_name));\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/reader.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef LIBLP_READER_H_\n#define LIBLP_READER_H_\n\n#include <stddef.h>\n\n#include <memory>\n\n#include <liblp/liblp.h>\n\nnamespace android {\nnamespace fs_mgr {\n\n// Parse an LpMetadataGeometry from a buffer. The buffer must be at least\n// LP_METADATA_GEOMETRY_SIZE bytes in size.\nbool ParseGeometry(const void* buffer, LpMetadataGeometry* geometry);\n\n// Helper functions for manually reading geometry and metadata.\nstd::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, int fd);\nstd::unique_ptr<LpMetadata> ParseMetadata(const LpMetadataGeometry& geometry, const void* buffer,\n                                          size_t size);\nbool ReadLogicalPartitionGeometry(int fd, LpMetadataGeometry* geometry);\nbool ReadPrimaryGeometry(int fd, LpMetadataGeometry* geometry);\nbool ReadBackupGeometry(int fd, LpMetadataGeometry* geometry);\n\n// These functions assume a valid geometry and slot number, and do not obey\n// auto-slot-suffixing. They are used for tests and for checking whether\n// the metadata is coherent across primary and backup copies.\nstd::unique_ptr<LpMetadata> ReadPrimaryMetadata(int fd, const LpMetadataGeometry& geometry,\n                                                uint32_t slot_number);\nstd::unique_ptr<LpMetadata> ReadBackupMetadata(int fd, const LpMetadataGeometry& geometry,\n                                               uint32_t slot_number);\n\n}  // namespace fs_mgr\n}  // namespace android\n\n#endif /* LIBLP_READER_H_ */\n"
  },
  {
    "path": "fs_mgr/liblp/super_layout_builder.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#include <liblp/super_layout_builder.h>\n\n#include <liblp/liblp.h>\n\n#include <algorithm>\n\n#include \"images.h\"\n#include \"utility.h\"\n#include \"writer.h\"\n\nusing android::base::borrowed_fd;\nusing android::base::unique_fd;\n\nnamespace android {\nnamespace fs_mgr {\n\nbool SuperLayoutBuilder::Open(borrowed_fd fd) {\n    auto metadata = ReadFromImageFile(fd.get());\n    if (!metadata) {\n        return false;\n    }\n    return Open(*metadata.get());\n}\n\nbool SuperLayoutBuilder::Open(const void* data, size_t size) {\n    auto metadata = ReadFromImageBlob(data, size);\n    if (!metadata) {\n        return false;\n    }\n    return Open(*metadata.get());\n}\n\nbool SuperLayoutBuilder::Open(const LpMetadata& metadata) {\n    for (const auto& partition : metadata.partitions) {\n        if (partition.attributes & LP_PARTITION_ATTR_SLOT_SUFFIXED) {\n            LOG(ERROR) << \"Retrofit devices are not supported\";\n            return false;\n        }\n        if (!(partition.attributes & LP_PARTITION_ATTR_READONLY)) {\n            LOG(ERROR) << \"Writable partitions are not supported\";\n            return false;\n        }\n    }\n    if (!metadata.extents.empty()) {\n        LOG(ERROR) << \"Partitions that already have extents are not supported\";\n        // should never be true of super_empty.img.\n        return false;\n    }\n    if (metadata.block_devices.size() != 1) {\n        LOG(ERROR) << \"Only one 'super' is supported\";\n        return false;\n    }\n\n    builder_ = MetadataBuilder::New(metadata);\n    return !!builder_;\n}\n\nbool SuperLayoutBuilder::AddPartition(const std::string& partition_name,\n                                      const std::string& image_name, uint64_t partition_size) {\n    auto p = builder_->FindPartition(partition_name);\n    if (!p) {\n        return false;\n    }\n    if (!builder_->ResizePartition(p, partition_size)) {\n        return false;\n    }\n    image_map_.emplace(partition_name, image_name);\n    return true;\n}\n\n// Fill the space between each extent, if any, with either a fill or dontcare\n// extent. The caller constructs a sample extent to re-use.\nstatic bool AddGapExtents(std::vector<SuperImageExtent>* extents, SuperImageExtent::Type gap_type) {\n    std::vector<SuperImageExtent> old = std::move(*extents);\n    std::sort(old.begin(), old.end());\n\n    *extents = {};\n\n    uint64_t current_offset = 0;\n    for (const auto& extent : old) {\n        // Check for overlapping extents - this would be a serious error.\n        if (current_offset > extent.offset) {\n            LOG(INFO) << \"Overlapping extents detected; cannot layout temporary super image\";\n            return false;\n        }\n\n        if (extent.offset != current_offset) {\n            uint64_t gap_size = extent.offset - current_offset;\n            extents->emplace_back(current_offset, gap_size, gap_type);\n            current_offset = extent.offset;\n        }\n\n        extents->emplace_back(extent);\n        current_offset += extent.size;\n    }\n    return true;\n}\n\nstd::vector<SuperImageExtent> SuperLayoutBuilder::GetImageLayout() {\n    auto metadata = builder_->Export();\n    if (!metadata) {\n        return {};\n    }\n\n    std::vector<SuperImageExtent> extents;\n\n    // Write the primary and backup copies of geometry.\n    std::string geometry_bytes = SerializeGeometry(metadata->geometry);\n    auto blob = std::make_shared<std::string>(std::move(geometry_bytes));\n\n    extents.emplace_back(0, GetPrimaryGeometryOffset(), SuperImageExtent::Type::ZERO);\n    extents.emplace_back(GetPrimaryGeometryOffset(), blob);\n    extents.emplace_back(GetBackupGeometryOffset(), blob);\n\n    // Write the primary and backup copies of each metadata slot. When flashing,\n    // all metadata copies are the same, even for different slots.\n    std::string metadata_bytes = SerializeMetadata(*metadata.get());\n\n    // Align metadata size to 4KB. This makes the layout easily compatible with\n    // libsparse.\n    static constexpr size_t kSparseAlignment = 4096;\n    size_t metadata_aligned_bytes;\n    if (!AlignTo(metadata_bytes.size(), kSparseAlignment, &metadata_aligned_bytes)) {\n        LOG(ERROR) << \"Unable to align metadata size \" << metadata_bytes.size() << \" to \"\n                   << kSparseAlignment;\n        return {};\n    }\n    metadata_bytes.resize(metadata_aligned_bytes, '\\0');\n\n    // However, alignment can cause larger-than-supported metadata blocks. Fall\n    // back to fastbootd/update-super.\n    if (metadata_bytes.size() > metadata->geometry.metadata_max_size) {\n        LOG(VERBOSE) << \"Aligned metadata size \" << metadata_bytes.size()\n                     << \" is larger than maximum metadata size \"\n                     << metadata->geometry.metadata_max_size;\n        return {};\n    }\n\n    blob = std::make_shared<std::string>(std::move(metadata_bytes));\n    for (uint32_t i = 0; i < metadata->geometry.metadata_slot_count; i++) {\n        int64_t metadata_primary = GetPrimaryMetadataOffset(metadata->geometry, i);\n        int64_t metadata_backup = GetBackupMetadataOffset(metadata->geometry, i);\n        extents.emplace_back(metadata_primary, blob);\n        extents.emplace_back(metadata_backup, blob);\n    }\n\n    // Add extents for each partition.\n    for (const auto& partition : metadata->partitions) {\n        auto partition_name = GetPartitionName(partition);\n        auto image_name_iter = image_map_.find(partition_name);\n        if (image_name_iter == image_map_.end()) {\n            if (partition.num_extents != 0) {\n                LOG(ERROR) << \"Partition \" << partition_name\n                           << \" has extents but no image filename\";\n                return {};\n            }\n            continue;\n        }\n        const auto& image_name = image_name_iter->second;\n\n        uint64_t image_offset = 0;\n        for (uint32_t i = 0; i < partition.num_extents; i++) {\n            const auto& e = metadata->extents[partition.first_extent_index + i];\n\n            if (e.target_type != LP_TARGET_TYPE_LINEAR) {\n                // Any type other than LINEAR isn't understood here. We don't even\n                // bother with ZERO, which is never generated.\n                LOG(INFO) << \"Unknown extent type from liblp: \" << e.target_type;\n                return {};\n            }\n\n            uint64_t size = e.num_sectors * LP_SECTOR_SIZE;\n            uint64_t super_offset = e.target_data * LP_SECTOR_SIZE;\n            extents.emplace_back(super_offset, size, image_name, image_offset);\n\n            image_offset += size;\n        }\n    }\n\n    if (!AddGapExtents(&extents, SuperImageExtent::Type::DONTCARE)) {\n        return {};\n    }\n    return extents;\n}\n\nbool SuperImageExtent::operator==(const SuperImageExtent& other) const {\n    if (offset != other.offset) {\n        return false;\n    }\n    if (size != other.size) {\n        return false;\n    }\n    if (type != other.type) {\n        return false;\n    }\n    switch (type) {\n        case Type::DATA:\n            return *blob == *other.blob;\n        case Type::PARTITION:\n            return image_name == other.image_name && image_offset == other.image_offset;\n        default:\n            return true;\n    }\n}\n\nstd::ostream& operator<<(std::ostream& stream, const SuperImageExtent& extent) {\n    stream << \"extent:\" << extent.offset << \":\" << extent.size << \":\";\n    switch (extent.type) {\n        case SuperImageExtent::Type::DATA:\n            stream << \"data\";\n            break;\n        case SuperImageExtent::Type::PARTITION:\n            stream << \"partition:\" << extent.image_name << \":\" << extent.image_offset;\n            break;\n        case SuperImageExtent::Type::ZERO:\n            stream << \"zero\";\n            break;\n        case SuperImageExtent::Type::DONTCARE:\n            stream << \"dontcare\";\n            break;\n        default:\n            stream << \"invalid\";\n    }\n    return stream;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/super_layout_builder_test.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <liblp/builder.h>\n#include <liblp/super_layout_builder.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"images.h\"\n#include \"writer.h\"\n\nusing namespace android::fs_mgr;\nusing namespace android::storage_literals;\n\nTEST(SuperImageTool, Layout) {\n    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* p = builder->AddPartition(\"system_a\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(p, nullptr);\n\n    auto metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n\n    SuperLayoutBuilder tool;\n    ASSERT_TRUE(tool.Open(*metadata.get()));\n    ASSERT_TRUE(tool.AddPartition(\"system_a\", \"system.img\", 16_KiB));\n\n    // Get a copy of the metadata we'd expect if flashing.\n    ASSERT_TRUE(builder->ResizePartition(p, 16_KiB));\n    metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n\n    auto geometry_blob = std::make_shared<std::string>(SerializeGeometry(metadata->geometry));\n    auto metadata_blob = std::make_shared<std::string>(SerializeMetadata(*metadata.get()));\n    metadata_blob->resize(4_KiB, '\\0');\n\n    auto extents = tool.GetImageLayout();\n    ASSERT_EQ(extents.size(), 12);\n    EXPECT_EQ(extents[0], SuperImageExtent(0, 4096, SuperImageExtent::Type::ZERO));\n    EXPECT_EQ(extents[1], SuperImageExtent(4096, geometry_blob));\n    EXPECT_EQ(extents[2], SuperImageExtent(8192, geometry_blob));\n    EXPECT_EQ(extents[3], SuperImageExtent(12288, metadata_blob));\n    EXPECT_EQ(extents[4], SuperImageExtent(16384, 4096, SuperImageExtent::Type::DONTCARE));\n    EXPECT_EQ(extents[5], SuperImageExtent(20480, metadata_blob));\n    EXPECT_EQ(extents[6], SuperImageExtent(24576, 4096, SuperImageExtent::Type::DONTCARE));\n    EXPECT_EQ(extents[7], SuperImageExtent(28672, metadata_blob));\n    EXPECT_EQ(extents[8], SuperImageExtent(32768, 4096, SuperImageExtent::Type::DONTCARE));\n    EXPECT_EQ(extents[9], SuperImageExtent(36864, metadata_blob));\n    EXPECT_EQ(extents[10], SuperImageExtent(40960, 4096, SuperImageExtent::Type::DONTCARE));\n    EXPECT_EQ(extents[11], SuperImageExtent(45056, 16384, \"system.img\", 0));\n}\n\nTEST(SuperImageTool, NoWritablePartitions) {\n    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* p = builder->AddPartition(\"system_a\", 0);\n    ASSERT_NE(p, nullptr);\n\n    auto metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n\n    SuperLayoutBuilder tool;\n    ASSERT_FALSE(tool.Open(*metadata.get()));\n}\n\nTEST(SuperImageTool, NoRetrofit) {\n    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* p = builder->AddPartition(\"system_a\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(p, nullptr);\n\n    auto metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n\n    // Add an extra block device.\n    metadata->block_devices.emplace_back(metadata->block_devices[0]);\n\n    SuperLayoutBuilder tool;\n    ASSERT_FALSE(tool.Open(*metadata.get()));\n}\n\nTEST(SuperImageTool, NoRetrofit2) {\n    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* p = builder->AddPartition(\n            \"system_a\", LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_SLOT_SUFFIXED);\n    ASSERT_NE(p, nullptr);\n\n    auto metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n\n    SuperLayoutBuilder tool;\n    ASSERT_FALSE(tool.Open(*metadata.get()));\n}\n\nTEST(SuperImageTool, NoFixedPartitions) {\n    auto builder = MetadataBuilder::New(4_MiB, 8_KiB, 2);\n    ASSERT_NE(builder, nullptr);\n\n    Partition* p = builder->AddPartition(\"system_a\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(p, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(p, 4_KiB));\n\n    auto metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n\n    SuperLayoutBuilder tool;\n    ASSERT_FALSE(tool.Open(*metadata.get()));\n}\n\nTEST(SuperImageTool, LargeAlignedMetadata) {\n    auto builder = MetadataBuilder::New(4_MiB, 512, 2);\n    ASSERT_NE(builder, nullptr);\n\n    auto metadata = builder->Export();\n    ASSERT_NE(metadata, nullptr);\n\n    SuperLayoutBuilder tool;\n    ASSERT_TRUE(tool.Open(*metadata.get()));\n\n    auto extents = tool.GetImageLayout();\n    ASSERT_TRUE(extents.empty());\n}\n"
  },
  {
    "path": "fs_mgr/liblp/test_partition_opener.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"test_partition_opener.h\"\n\n#include <errno.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nusing android::base::unique_fd;\n\nTestPartitionOpener::TestPartitionOpener(\n        const std::map<std::string, int>& partition_map,\n        const std::map<std::string, BlockDeviceInfo>& partition_info)\n    : partition_map_(partition_map), partition_info_(partition_info) {}\n\nunique_fd TestPartitionOpener::Open(const std::string& partition_name, int flags) const {\n    auto iter = partition_map_.find(partition_name);\n    if (iter == partition_map_.end()) {\n        errno = ENOENT;\n        return {};\n    }\n    return unique_fd{dup(iter->second)};\n}\n\nbool TestPartitionOpener::GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const {\n    auto iter = partition_info_.find(partition_name);\n    if (iter == partition_info_.end()) {\n        errno = ENOENT;\n        return false;\n    }\n    *info = iter->second;\n    return true;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/test_partition_opener.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <string>\n\n#include <android-base/unique_fd.h>\n#include <liblp/partition_opener.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nclass TestPartitionOpener : public PartitionOpener {\n  public:\n    explicit TestPartitionOpener(const std::map<std::string, int>& partition_map,\n                                 const std::map<std::string, BlockDeviceInfo>& partition_info = {});\n\n    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;\n    bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override;\n\n  private:\n    std::map<std::string, int> partition_map_;\n    std::map<std::string, BlockDeviceInfo> partition_info_;\n};\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/utility.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdint.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#if defined(__linux__)\n#include <linux/fs.h>\n#include <sys/ioctl.h>\n#endif\n\n#include <algorithm>\n#include <map>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <ext4_utils/ext4_utils.h>\n#include <openssl/sha.h>\n\n#ifdef __ANDROID__\n#include <cutils/android_get_control_file.h>\n#endif\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nbool GetDescriptorSize(int fd, uint64_t* size) {\n#if !defined(_WIN32)\n    struct stat s;\n    if (fstat(fd, &s) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \"fstat failed\";\n        return false;\n    }\n\n    if (S_ISBLK(s.st_mode)) {\n        *size = get_block_device_size(fd);\n        return *size != 0;\n    }\n#endif\n\n    int64_t result = SeekFile64(fd, 0, SEEK_END);\n    if (result == -1) {\n        PERROR << __PRETTY_FUNCTION__ << \"lseek failed\";\n        return false;\n    }\n\n    *size = result;\n    return true;\n}\n\nint64_t SeekFile64(int fd, int64_t offset, int whence) {\n    static_assert(sizeof(off_t) == sizeof(int64_t), \"Need 64-bit lseek\");\n    return lseek(fd, offset, whence);\n}\n\nint64_t GetPrimaryGeometryOffset() {\n    return LP_PARTITION_RESERVED_BYTES;\n}\n\nint64_t GetBackupGeometryOffset() {\n    return GetPrimaryGeometryOffset() + LP_METADATA_GEOMETRY_SIZE;\n}\n\nint64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {\n    CHECK(slot_number < geometry.metadata_slot_count);\n    int64_t offset = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +\n                     geometry.metadata_max_size * slot_number;\n    return offset;\n}\n\nint64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number) {\n    CHECK(slot_number < geometry.metadata_slot_count);\n    int64_t start = LP_PARTITION_RESERVED_BYTES + (LP_METADATA_GEOMETRY_SIZE * 2) +\n                    int64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;\n    return start + int64_t(geometry.metadata_max_size * slot_number);\n}\n\nuint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots) {\n    return LP_PARTITION_RESERVED_BYTES +\n           (LP_METADATA_GEOMETRY_SIZE + metadata_max_size * max_slots) * 2;\n}\n\nconst LpMetadataBlockDevice* GetMetadataSuperBlockDevice(const LpMetadata& metadata) {\n    if (metadata.block_devices.empty()) {\n        return nullptr;\n    }\n    return &metadata.block_devices[0];\n}\n\nvoid SHA256(const void* data, size_t length, uint8_t out[32]) {\n    SHA256_CTX c;\n    SHA256_Init(&c);\n    SHA256_Update(&c, data, length);\n    SHA256_Final(out, &c);\n}\n\nuint32_t SlotNumberForSlotSuffix(const std::string& suffix) {\n    if (suffix.empty() || suffix == \"a\" || suffix == \"_a\") {\n        return 0;\n    } else if (suffix == \"b\" || suffix == \"_b\") {\n        return 1;\n    } else {\n        LERROR << __PRETTY_FUNCTION__ << \"slot '\" << suffix\n               << \"' does not have a recognized format.\";\n        return 0;\n    }\n}\n\nuint64_t GetTotalSuperPartitionSize(const LpMetadata& metadata) {\n    uint64_t size = 0;\n    for (const auto& block_device : metadata.block_devices) {\n        size += block_device.size;\n    }\n    return size;\n}\n\nstd::vector<std::string> GetBlockDevicePartitionNames(const LpMetadata& metadata) {\n    std::vector<std::string> list;\n    for (const auto& block_device : metadata.block_devices) {\n        list.emplace_back(GetBlockDevicePartitionName(block_device));\n    }\n    return list;\n}\n\nconst LpMetadataPartition* FindPartition(const LpMetadata& metadata, const std::string& name) {\n    for (const auto& partition : metadata.partitions) {\n        if (GetPartitionName(partition) == name) {\n            return &partition;\n        }\n    }\n    return nullptr;\n}\n\nuint64_t GetPartitionSize(const LpMetadata& metadata, const LpMetadataPartition& partition) {\n    uint64_t total_size = 0;\n    for (uint32_t i = 0; i < partition.num_extents; i++) {\n        const auto& extent = metadata.extents[partition.first_extent_index + i];\n        total_size += extent.num_sectors * LP_SECTOR_SIZE;\n    }\n    return total_size;\n}\n\nstd::string GetPartitionSlotSuffix(const std::string& partition_name) {\n    if (partition_name.size() <= 2) {\n        return \"\";\n    }\n    std::string suffix = partition_name.substr(partition_name.size() - 2);\n    return (suffix == \"_a\" || suffix == \"_b\") ? suffix : \"\";\n}\n\nstd::string SlotSuffixForSlotNumber(uint32_t slot_number) {\n    CHECK(slot_number == 0 || slot_number == 1);\n    return (slot_number == 0) ? \"_a\" : \"_b\";\n}\n\nbool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name) {\n    if (name.size() > sizeof(device->partition_name)) {\n        return false;\n    }\n    strncpy(device->partition_name, name.c_str(), sizeof(device->partition_name));\n    return true;\n}\n\nbool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name) {\n    if (name.size() > sizeof(group->name)) {\n        return false;\n    }\n    strncpy(group->name, name.c_str(), sizeof(group->name));\n    return true;\n}\n\nbool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name) {\n    if (name.size() > sizeof(partition->name)) {\n        return false;\n    }\n    strncpy(partition->name, name.c_str(), sizeof(partition->name));\n    return true;\n}\n\nbool SetBlockReadonly(int fd, bool readonly) {\n#if defined(__linux__)\n    int val = readonly;\n    return ioctl(fd, BLKROSET, &val) == 0;\n#else\n    (void)fd;\n    (void)readonly;\n    return true;\n#endif\n}\n\nbase::unique_fd GetControlFileOrOpen(std::string_view path, int flags) {\n#if defined(__ANDROID__)\n    int fd = android_get_control_file(path.data());\n    if (fd >= 0) {\n        int newfd = TEMP_FAILURE_RETRY(dup(fd));\n        if (newfd >= 0) {\n            return base::unique_fd(newfd);\n        }\n        PERROR << \"Cannot dup fd for already controlled file: \" << path << \", reopening...\";\n    }\n#endif\n    return base::unique_fd(open(path.data(), flags));\n}\n\nbool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,\n                                      uint32_t target_slot_number) {\n    std::string source_slot_suffix = SlotSuffixForSlotNumber(source_slot_number);\n    std::string target_slot_suffix = SlotSuffixForSlotNumber(target_slot_number);\n\n    // There can be leftover groups with target suffix on retrofit devices.\n    // They are useless now, so delete.\n    std::vector<LpMetadataPartitionGroup*> new_group_ptrs;\n    for (auto& group : metadata->groups) {\n        std::string group_name = GetPartitionGroupName(group);\n        std::string slot_suffix = GetPartitionSlotSuffix(group_name);\n        // Don't add groups with target slot suffix.\n        if (slot_suffix == target_slot_suffix) continue;\n        // Replace source slot suffix with target slot suffix.\n        if (slot_suffix == source_slot_suffix) {\n            std::string new_name = group_name.substr(0, group_name.size() - slot_suffix.size()) +\n                                   target_slot_suffix;\n            if (!UpdatePartitionGroupName(&group, new_name)) {\n                LERROR << \"Group name too long: \" << new_name;\n                return false;\n            }\n        }\n        new_group_ptrs.push_back(&group);\n    }\n\n    std::vector<LpMetadataPartition*> new_partition_ptrs;\n    for (auto& partition : metadata->partitions) {\n        std::string partition_name = GetPartitionName(partition);\n        std::string slot_suffix = GetPartitionSlotSuffix(partition_name);\n        // Don't add partitions with target slot suffix.\n        if (slot_suffix == target_slot_suffix) continue;\n        // Replace source slot suffix with target slot suffix.\n        if (slot_suffix == source_slot_suffix) {\n            std::string new_name =\n                    partition_name.substr(0, partition_name.size() - slot_suffix.size()) +\n                    target_slot_suffix;\n            if (!UpdatePartitionName(&partition, new_name)) {\n                LERROR << \"Partition name too long: \" << new_name;\n                return false;\n            }\n        }\n        // Update group index.\n        auto it = std::find(new_group_ptrs.begin(), new_group_ptrs.end(),\n                            &metadata->groups[partition.group_index]);\n        if (it == new_group_ptrs.end()) {\n            LWARN << \"Removing partition \" << partition_name << \" from group \"\n                  << GetPartitionGroupName(metadata->groups[partition.group_index])\n                  << \"; this partition should not belong to this group!\";\n            continue;  // not adding to new_partition_ptrs\n        }\n        partition.attributes |= LP_PARTITION_ATTR_UPDATED;\n        partition.group_index = std::distance(new_group_ptrs.begin(), it);\n        new_partition_ptrs.push_back(&partition);\n    }\n\n    std::vector<LpMetadataPartition> new_partitions;\n    for (auto* p : new_partition_ptrs) new_partitions.emplace_back(std::move(*p));\n    metadata->partitions = std::move(new_partitions);\n\n    std::vector<LpMetadataPartitionGroup> new_groups;\n    for (auto* g : new_group_ptrs) new_groups.emplace_back(std::move(*g));\n    metadata->groups = std::move(new_groups);\n\n    return true;\n}\n\ninline std::string ToHexString(uint64_t value) {\n    return android::base::StringPrintf(\"0x%\" PRIx64, value);\n}\n\nvoid SetMetadataHeaderV0(LpMetadata* metadata) {\n    if (metadata->header.minor_version <= LP_METADATA_MINOR_VERSION_MIN) {\n        return;\n    }\n    LINFO << \"Forcefully setting metadata header version \" << LP_METADATA_MAJOR_VERSION << \".\"\n          << metadata->header.minor_version << \" to \" << LP_METADATA_MAJOR_VERSION << \".\"\n          << LP_METADATA_MINOR_VERSION_MIN;\n    metadata->header.minor_version = LP_METADATA_MINOR_VERSION_MIN;\n    metadata->header.header_size = sizeof(LpMetadataHeaderV1_0);\n\n    // Retrofit Virtual A/B devices should have version 10.1, so flags shouldn't be set.\n    // Warn if this is the case, but zero it out anyways.\n    if (metadata->header.flags) {\n        LWARN << \"Zeroing unexpected flags: \" << ToHexString(metadata->header.flags);\n    }\n\n    // Zero out all fields beyond LpMetadataHeaderV0.\n    static_assert(sizeof(metadata->header) > sizeof(LpMetadataHeaderV1_0));\n    memset(reinterpret_cast<uint8_t*>(&metadata->header) + sizeof(LpMetadataHeaderV1_0), 0,\n           sizeof(metadata->header) - sizeof(LpMetadataHeaderV1_0));\n\n    // Clear partition attributes unknown to V0.\n    // On retrofit Virtual A/B devices, UPDATED flag may be set, so only log info here.\n    for (auto& partition : metadata->partitions) {\n        if (partition.attributes & ~LP_PARTITION_ATTRIBUTE_MASK_V0) {\n            LINFO << \"Clearing \" << GetPartitionName(partition)\n                  << \" partition attribute: \" << ToHexString(partition.attributes);\n        }\n\n        partition.attributes &= LP_PARTITION_ATTRIBUTE_MASK_V0;\n    }\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/utility.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef LIBLP_UTILITY_H\n#define LIBLP_UTILITY_H\n\n#include <stddef.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <limits>\n#include <string>\n#include <string_view>\n\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n\n#include \"liblp/liblp.h\"\n\n#define LP_TAG \"[liblp] \"\n#define LWARN LOG(WARNING) << LP_TAG\n#define LINFO LOG(INFO) << LP_TAG\n#define LERROR LOG(ERROR) << LP_TAG\n#define PWARNING PLOG(WARNING) << LP_TAG\n#define PERROR PLOG(ERROR) << LP_TAG\n\nnamespace android {\nnamespace fs_mgr {\n\n// Determine the size of a block device (or file). Logs and returns false on\n// error. After calling this, the position of |fd| may have changed.\nbool GetDescriptorSize(int fd, uint64_t* size);\n\n// Return the offset of the primary or backup geometry.\nint64_t GetPrimaryGeometryOffset();\nint64_t GetBackupGeometryOffset();\n\n// Return the offset of a primary metadata slot, relative to the start of the\n// device.\nint64_t GetPrimaryMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);\n\n// Return the offset of a backup metadata slot, relative to the end of the\n// device.\nint64_t GetBackupMetadataOffset(const LpMetadataGeometry& geometry, uint32_t slot_number);\n\n// Return the total space at the start of the super partition that must be set\n// aside from headers/metadata and backups.\nuint64_t GetTotalMetadataSize(uint32_t metadata_max_size, uint32_t max_slots);\n\n// Cross-platform helper for lseek64().\nint64_t SeekFile64(int fd, int64_t offset, int whence);\n\n// Compute a SHA256 hash.\nvoid SHA256(const void* data, size_t length, uint8_t out[32]);\n\n// Align |base| such that it is evenly divisible by |alignment|, which does not\n// have to be a power of two. Return false on overflow.\ntemplate <typename T>\nbool AlignTo(T base, uint32_t alignment, T* out) {\n    static_assert(std::numeric_limits<T>::is_integer);\n    static_assert(!std::numeric_limits<T>::is_signed);\n    if (!alignment) {\n        *out = base;\n        return true;\n    }\n    T remainder = base % alignment;\n    if (remainder == 0) {\n        *out = base;\n        return true;\n    }\n    T to_add = alignment - remainder;\n    if (to_add > std::numeric_limits<T>::max() - base) {\n        return false;\n    }\n    *out = base + to_add;\n    return true;\n}\n\n// Update names from C++ strings.\nbool UpdateBlockDevicePartitionName(LpMetadataBlockDevice* device, const std::string& name);\nbool UpdatePartitionGroupName(LpMetadataPartitionGroup* group, const std::string& name);\nbool UpdatePartitionName(LpMetadataPartition* partition, const std::string& name);\n\n// Call BLKROSET ioctl on fd so that fd is readonly / read-writable.\nbool SetBlockReadonly(int fd, bool readonly);\n\n::android::base::unique_fd GetControlFileOrOpen(std::string_view path, int flags);\n\n// For Virtual A/B updates, modify |metadata| so that it can be written to |target_slot_number|.\nbool UpdateMetadataForInPlaceSnapshot(LpMetadata* metadata, uint32_t source_slot_number,\n                                      uint32_t target_slot_number);\n\n// Forcefully set metadata header version to 1.0, clearing any incompatible flags and attributes\n// so that when downgrading to a build with liblp V0, the device still boots.\nvoid SetMetadataHeaderV0(LpMetadata* metadata);\n\n}  // namespace fs_mgr\n}  // namespace android\n\n#endif  // LIBLP_UTILITY_H\n"
  },
  {
    "path": "fs_mgr/liblp/utility_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <optional>\n\n#include <gtest/gtest.h>\n#include <liblp/builder.h>\n#include <liblp/liblp.h>\n\n#include \"utility.h\"\n\nusing namespace android;\nusing namespace android::fs_mgr;\n\nTEST(liblp, SlotNumberForSlotSuffix) {\n    EXPECT_EQ(SlotNumberForSlotSuffix(\"\"), 0);\n    EXPECT_EQ(SlotNumberForSlotSuffix(\"a\"), 0);\n    EXPECT_EQ(SlotNumberForSlotSuffix(\"_a\"), 0);\n    EXPECT_EQ(SlotNumberForSlotSuffix(\"b\"), 1);\n    EXPECT_EQ(SlotNumberForSlotSuffix(\"_b\"), 1);\n    EXPECT_EQ(SlotNumberForSlotSuffix(\"_c\"), 0);\n    EXPECT_EQ(SlotNumberForSlotSuffix(\"_d\"), 0);\n}\n\nTEST(liblp, SlotSuffixForSlotNumber) {\n    EXPECT_EQ(SlotSuffixForSlotNumber(0), \"_a\");\n    EXPECT_EQ(SlotSuffixForSlotNumber(1), \"_b\");\n}\n\nTEST(liblp, GetMetadataOffset) {\n    LpMetadataGeometry geometry = {LP_METADATA_GEOMETRY_MAGIC,\n                                   sizeof(geometry),\n                                   {0},\n                                   16384,\n                                   4,\n                                   4096};\n    static const uint64_t start = LP_PARTITION_RESERVED_BYTES;\n    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 0), start + 8192);\n    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 1), start + 8192 + 16384);\n    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 2), start + 8192 + 16384 * 2);\n    EXPECT_EQ(GetPrimaryMetadataOffset(geometry, 3), start + 8192 + 16384 * 3);\n\n    static const uint64_t backup_start = start + 8192 + 16384 * 4;\n    EXPECT_EQ(GetBackupMetadataOffset(geometry, 3), backup_start + 16384 * 3);\n    EXPECT_EQ(GetBackupMetadataOffset(geometry, 2), backup_start + 16384 * 2);\n    EXPECT_EQ(GetBackupMetadataOffset(geometry, 1), backup_start + 16384 * 1);\n    EXPECT_EQ(GetBackupMetadataOffset(geometry, 0), backup_start + 16384 * 0);\n}\n\nstd::optional<uint64_t> AlignTo(uint64_t base, uint32_t alignment) {\n    uint64_t r;\n    if (!AlignTo(base, alignment, &r)) {\n        return {};\n    }\n    return {r};\n}\n\nTEST(liblp, AlignTo) {\n    EXPECT_EQ(AlignTo(37, 0), std::optional<uint64_t>(37));\n    EXPECT_EQ(AlignTo(1024, 1024), std::optional<uint64_t>(1024));\n    EXPECT_EQ(AlignTo(555, 1024), std::optional<uint64_t>(1024));\n    EXPECT_EQ(AlignTo(555, 1000), std::optional<uint64_t>(1000));\n    EXPECT_EQ(AlignTo(0, 1024), std::optional<uint64_t>(0));\n    EXPECT_EQ(AlignTo(54, 32), std::optional<uint64_t>(64));\n    EXPECT_EQ(AlignTo(32, 32), std::optional<uint64_t>(32));\n    EXPECT_EQ(AlignTo(17, 32), std::optional<uint64_t>(32));\n\n    auto u32limit = std::numeric_limits<uint32_t>::max();\n    auto u64limit = std::numeric_limits<uint64_t>::max();\n    EXPECT_EQ(AlignTo(u64limit - u32limit + 1, u32limit), std::optional<uint64_t>{u64limit});\n    EXPECT_EQ(AlignTo(std::numeric_limits<uint64_t>::max(), 2), std::optional<uint64_t>{});\n}\n\nTEST(liblp, GetPartitionSlotSuffix) {\n    EXPECT_EQ(GetPartitionSlotSuffix(\"system\"), \"\");\n    EXPECT_EQ(GetPartitionSlotSuffix(\"_\"), \"\");\n    EXPECT_EQ(GetPartitionSlotSuffix(\"_a\"), \"\");\n    EXPECT_EQ(GetPartitionSlotSuffix(\"system_a\"), \"_a\");\n    EXPECT_EQ(GetPartitionSlotSuffix(\"system_b\"), \"_b\");\n}\n\nnamespace android {\nnamespace fs_mgr {\n// Equality comparison for testing. In reality, equality of device_index doesn't\n// necessary mean equality of the block device.\nbool operator==(const LinearExtent& l, const LinearExtent& r) {\n    return l.device_index() == r.device_index() && l.physical_sector() == r.physical_sector() &&\n           l.end_sector() == r.end_sector();\n}\n}  // namespace fs_mgr\n}  // namespace android\n\nstatic std::vector<LinearExtent> GetPartitionExtents(Partition* p) {\n    std::vector<LinearExtent> extents;\n    for (auto&& extent : p->extents()) {\n        auto linear_extent = extent->AsLinearExtent();\n        if (!linear_extent) return {};\n        extents.push_back(*linear_extent);\n    }\n    return extents;\n}\n\nTEST(liblp, UpdateMetadataForInPlaceSnapshot) {\n    using std::unique_ptr;\n\n    unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(1024 * 1024, 1024, 2);\n    ASSERT_NE(builder, nullptr);\n\n    ASSERT_TRUE(builder->AddGroup(\"group_a\", 256 * 1024));\n    Partition* system_a = builder->AddPartition(\"system_a\", \"group_a\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_a, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(system_a, 40 * 1024));\n    Partition* vendor_a = builder->AddPartition(\"vendor_a\", \"group_a\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(vendor_a, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(vendor_a, 20 * 1024));\n\n    ASSERT_TRUE(builder->AddGroup(\"group_b\", 258 * 1024));\n    Partition* system_b = builder->AddPartition(\"system_b\", \"group_b\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_b, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(system_b, 36 * 1024));\n    Partition* vendor_b = builder->AddPartition(\"vendor_b\", \"group_b\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(vendor_b, nullptr);\n    ASSERT_TRUE(builder->ResizePartition(vendor_b, 32 * 1024));\n\n    auto system_a_extents = GetPartitionExtents(system_a);\n    ASSERT_FALSE(system_a_extents.empty());\n\n    auto vendor_a_extents = GetPartitionExtents(vendor_a);\n    ASSERT_FALSE(vendor_a_extents.empty());\n\n    auto metadata = builder->Export();\n    ASSERT_NE(nullptr, metadata);\n\n    ASSERT_TRUE(UpdateMetadataForInPlaceSnapshot(metadata.get(), 0, 1));\n\n    auto new_builder = MetadataBuilder::New(*metadata);\n    ASSERT_NE(nullptr, new_builder);\n\n    EXPECT_EQ(nullptr, new_builder->FindGroup(\"group_a\"));\n    EXPECT_EQ(nullptr, new_builder->FindPartition(\"system_a\"));\n    EXPECT_EQ(nullptr, new_builder->FindPartition(\"vendor_a\"));\n\n    auto group_b = new_builder->FindGroup(\"group_b\");\n    ASSERT_NE(nullptr, group_b);\n    ASSERT_EQ(256 * 1024, group_b->maximum_size());\n\n    auto new_system_b = new_builder->FindPartition(\"system_b\");\n    ASSERT_NE(nullptr, new_system_b);\n    EXPECT_EQ(40 * 1024, new_system_b->size());\n    auto new_system_b_extents = GetPartitionExtents(new_system_b);\n    EXPECT_EQ(system_a_extents, new_system_b_extents);\n\n    auto new_vendor_b = new_builder->FindPartition(\"vendor_b\");\n    ASSERT_NE(nullptr, new_vendor_b);\n    EXPECT_EQ(20 * 1024, new_vendor_b->size());\n    auto new_vendor_b_extents = GetPartitionExtents(new_vendor_b);\n    EXPECT_EQ(vendor_a_extents, new_vendor_b_extents);\n}\n"
  },
  {
    "path": "fs_mgr/liblp/writer.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"writer.h\"\n\n#include <inttypes.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n\n#include \"reader.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nstd::string SerializeGeometry(const LpMetadataGeometry& input) {\n    LpMetadataGeometry geometry = input;\n    memset(geometry.checksum, 0, sizeof(geometry.checksum));\n    SHA256(&geometry, sizeof(geometry), geometry.checksum);\n\n    std::string blob(reinterpret_cast<const char*>(&geometry), sizeof(geometry));\n    blob.resize(LP_METADATA_GEOMETRY_SIZE);\n    return blob;\n}\n\nstatic bool CompareGeometry(const LpMetadataGeometry& g1, const LpMetadataGeometry& g2) {\n    return g1.metadata_max_size == g2.metadata_max_size &&\n           g1.metadata_slot_count == g2.metadata_slot_count &&\n           g1.logical_block_size == g2.logical_block_size;\n}\n\nstd::string SerializeMetadata(const LpMetadata& input) {\n    LpMetadata metadata = input;\n    LpMetadataHeader& header = metadata.header;\n\n    // Serialize individual tables.\n    std::string partitions(reinterpret_cast<const char*>(metadata.partitions.data()),\n                           metadata.partitions.size() * sizeof(LpMetadataPartition));\n    std::string extents(reinterpret_cast<const char*>(metadata.extents.data()),\n                        metadata.extents.size() * sizeof(LpMetadataExtent));\n    std::string groups(reinterpret_cast<const char*>(metadata.groups.data()),\n                       metadata.groups.size() * sizeof(LpMetadataPartitionGroup));\n    std::string block_devices(reinterpret_cast<const char*>(metadata.block_devices.data()),\n                              metadata.block_devices.size() * sizeof(LpMetadataBlockDevice));\n\n    // Compute positions of tables.\n    header.partitions.offset = 0;\n    header.extents.offset = header.partitions.offset + partitions.size();\n    header.groups.offset = header.extents.offset + extents.size();\n    header.block_devices.offset = header.groups.offset + groups.size();\n    header.tables_size = header.block_devices.offset + block_devices.size();\n\n    // Compute payload checksum.\n    std::string tables = partitions + extents + groups + block_devices;\n    SHA256(tables.data(), tables.size(), header.tables_checksum);\n\n    // Compute header checksum.\n    memset(header.header_checksum, 0, sizeof(header.header_checksum));\n    SHA256(&header, header.header_size, header.header_checksum);\n\n    std::string header_blob =\n            std::string(reinterpret_cast<const char*>(&header), header.header_size);\n    return header_blob + tables;\n}\n\n// Perform checks so we don't accidentally overwrite valid metadata with\n// potentially invalid metadata, or random partition data with metadata.\nstatic bool ValidateAndSerializeMetadata([[maybe_unused]] const IPartitionOpener& opener,\n                                         const LpMetadata& metadata, const std::string& slot_suffix,\n                                         std::string* blob) {\n    const LpMetadataGeometry& geometry = metadata.geometry;\n\n    *blob = SerializeMetadata(metadata);\n\n    // Make sure we're writing within the space reserved.\n    if (blob->size() > geometry.metadata_max_size) {\n        LERROR << \"Logical partition metadata is too large. \" << blob->size() << \" > \"\n               << geometry.metadata_max_size;\n        return false;\n    }\n\n    // Make sure the device has enough space to store two backup copies of the\n    // metadata.\n    uint64_t reserved_size = LP_METADATA_GEOMETRY_SIZE +\n                             uint64_t(geometry.metadata_max_size) * geometry.metadata_slot_count;\n    uint64_t total_reserved = LP_PARTITION_RESERVED_BYTES + reserved_size * 2;\n\n    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);\n    if (!super_device) {\n        LERROR << \"Logical partition metadata does not have a super block device.\";\n        return false;\n    }\n\n    if (total_reserved > super_device->first_logical_sector * LP_SECTOR_SIZE) {\n        LERROR << \"Not enough space to store all logical partition metadata slots.\";\n        return false;\n    }\n    for (const auto& block_device : metadata.block_devices) {\n        std::string partition_name = GetBlockDevicePartitionName(block_device);\n        if (block_device.flags & LP_BLOCK_DEVICE_SLOT_SUFFIXED) {\n            if (slot_suffix.empty()) {\n                LERROR << \"Block device \" << partition_name << \" requires a slot suffix,\"\n                       << \" which could not be derived from the super partition name.\";\n                return false;\n            }\n            partition_name += slot_suffix;\n        }\n\n        if ((block_device.first_logical_sector + 1) * LP_SECTOR_SIZE > block_device.size) {\n            LERROR << \"Block device \" << partition_name << \" has invalid first sector \"\n                   << block_device.first_logical_sector << \" for size \" << block_device.size;\n            return false;\n        }\n\n        // When flashing on the device, check partition sizes. Don't do this on\n        // the host since there is no way to verify.\n#if defined(__ANDROID__)\n        BlockDeviceInfo info;\n        if (!opener.GetInfo(partition_name, &info)) {\n            PERROR << partition_name << \": ioctl\";\n            return false;\n        }\n        if (info.size != block_device.size) {\n            LERROR << \"Block device \" << partition_name << \" size mismatch (expected\"\n                   << block_device.size << \", got \" << info.size << \")\";\n            return false;\n        }\n#endif\n    }\n\n    // Make sure all partition entries reference valid extents.\n    for (const auto& partition : metadata.partitions) {\n        if (partition.first_extent_index + partition.num_extents > metadata.extents.size()) {\n            LERROR << \"Partition references invalid extent.\";\n            return false;\n        }\n    }\n\n    // Make sure all linear extents have a valid range.\n    uint64_t last_sector = super_device->size / LP_SECTOR_SIZE;\n    for (const auto& extent : metadata.extents) {\n        if (extent.target_type == LP_TARGET_TYPE_LINEAR) {\n            uint64_t physical_sector = extent.target_data;\n            if (physical_sector < super_device->first_logical_sector ||\n                physical_sector + extent.num_sectors > last_sector) {\n                LERROR << \"Extent table entry is out of bounds.\";\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\n// Check that the given region is within metadata bounds.\nstatic bool ValidateMetadataRegion(const LpMetadata& metadata, uint64_t start, size_t size) {\n    const LpMetadataBlockDevice* super_device = GetMetadataSuperBlockDevice(metadata);\n    if (!super_device) {\n        LERROR << __PRETTY_FUNCTION__ << \" could not locate super block device in metadata\";\n        return false;\n    }\n    if (start + size >= super_device->first_logical_sector * LP_SECTOR_SIZE) {\n        LERROR << __PRETTY_FUNCTION__ << \" write of \" << size << \" bytes at \" << start\n               << \" overlaps with logical partition contents\";\n        return false;\n    }\n    return true;\n}\n\nstatic bool WritePrimaryMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,\n                                 const std::string& blob,\n                                 const std::function<bool(int, const std::string&)>& writer) {\n    int64_t primary_offset = GetPrimaryMetadataOffset(metadata.geometry, slot_number);\n    if (!ValidateMetadataRegion(metadata, primary_offset, blob.size())) {\n        return false;\n    }\n    if (SeekFile64(fd, primary_offset, SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed: offset \" << primary_offset;\n        return false;\n    }\n    if (!writer(fd, blob)) {\n        PERROR << __PRETTY_FUNCTION__ << \" write \" << blob.size() << \" bytes failed\";\n        return false;\n    }\n    return true;\n}\n\nstatic bool WriteBackupMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,\n                                const std::string& blob,\n                                const std::function<bool(int, const std::string&)>& writer) {\n    int64_t backup_offset = GetBackupMetadataOffset(metadata.geometry, slot_number);\n    if (!ValidateMetadataRegion(metadata, backup_offset, blob.size())) {\n        return false;\n    }\n    if (SeekFile64(fd, backup_offset, SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed: offset \" << backup_offset;\n        return false;\n    }\n    if (!writer(fd, blob)) {\n        PERROR << __PRETTY_FUNCTION__ << \" backup write \" << blob.size() << \" bytes failed\";\n        return false;\n    }\n    return true;\n}\n\nstatic bool WriteMetadata(int fd, const LpMetadata& metadata, uint32_t slot_number,\n                          const std::string& blob,\n                          const std::function<bool(int, const std::string&)>& writer) {\n    // Make sure we're writing to a valid metadata slot.\n    if (slot_number >= metadata.geometry.metadata_slot_count) {\n        LERROR << \"Invalid logical partition metadata slot number.\";\n        return false;\n    }\n    if (!WritePrimaryMetadata(fd, metadata, slot_number, blob, writer)) {\n        return false;\n    }\n    if (!WriteBackupMetadata(fd, metadata, slot_number, blob, writer)) {\n        return false;\n    }\n    return true;\n}\n\nstatic bool DefaultWriter(int fd, const std::string& blob) {\n    return android::base::WriteFully(fd, blob.data(), blob.size());\n}\n\n#if defined(_WIN32)\nstatic const int O_SYNC = 0;\n#endif\n\nbool FlashPartitionTable(const IPartitionOpener& opener, const std::string& super_partition,\n                         const LpMetadata& metadata) {\n    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);\n    if (fd < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" open failed: \" << super_partition;\n        return false;\n    }\n\n    // This is only used in update_engine and fastbootd, where the super\n    // partition should be specified as a name (or by-name link), and\n    // therefore, we should be able to extract a slot suffix.\n    std::string slot_suffix = GetPartitionSlotSuffix(super_partition);\n\n    // Before writing geometry and/or logical partition tables, perform some\n    // basic checks that the geometry and tables are coherent, and will fit\n    // on the given block device.\n    std::string metadata_blob;\n    if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &metadata_blob)) {\n        return false;\n    }\n\n    // On retrofit devices, super_partition is system_other and might be set to readonly by\n    // fs_mgr_set_blk_ro(). Unset readonly so that fd can be written to.\n    if (!SetBlockReadonly(fd.get(), false)) {\n        PWARNING << __PRETTY_FUNCTION__ << \" BLKROSET 0 failed: \" << super_partition;\n    }\n\n    // Write zeroes to the first block.\n    std::string zeroes(LP_PARTITION_RESERVED_BYTES, 0);\n    if (SeekFile64(fd, 0, SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed: offset 0\";\n        return false;\n    }\n    if (!android::base::WriteFully(fd, zeroes.data(), zeroes.size())) {\n        PERROR << __PRETTY_FUNCTION__ << \" write \" << zeroes.size() << \" bytes failed\";\n        return false;\n    }\n\n    LWARN << \"Flashing new logical partition geometry to \" << super_partition;\n\n    // Write geometry to the primary and backup locations.\n    std::string blob = SerializeGeometry(metadata.geometry);\n    if (SeekFile64(fd, GetPrimaryGeometryOffset(), SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed: primary geometry\";\n        return false;\n    }\n    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {\n        PERROR << __PRETTY_FUNCTION__ << \" write \" << blob.size() << \" bytes failed\";\n        return false;\n    }\n    if (SeekFile64(fd, GetBackupGeometryOffset(), SEEK_SET) < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" lseek failed: backup geometry\";\n        return false;\n    }\n    if (!android::base::WriteFully(fd, blob.data(), blob.size())) {\n        PERROR << __PRETTY_FUNCTION__ << \" backup write \" << blob.size() << \" bytes failed\";\n        return false;\n    }\n\n    bool ok = true;\n    for (size_t i = 0; i < metadata.geometry.metadata_slot_count; i++) {\n        ok &= WriteMetadata(fd, metadata, i, metadata_blob, DefaultWriter);\n    }\n    return ok;\n}\n\nbool FlashPartitionTable(const std::string& super_partition, const LpMetadata& metadata) {\n    return FlashPartitionTable(PartitionOpener(), super_partition, metadata);\n}\n\nstatic bool CompareMetadata(const LpMetadata& a, const LpMetadata& b) {\n    return !memcmp(a.header.header_checksum, b.header.header_checksum,\n                   sizeof(a.header.header_checksum));\n}\n\nbool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,\n                          const LpMetadata& metadata, uint32_t slot_number,\n                          const std::function<bool(int, const std::string&)>& writer) {\n    android::base::unique_fd fd = opener.Open(super_partition, O_RDWR | O_SYNC);\n    if (fd < 0) {\n        PERROR << __PRETTY_FUNCTION__ << \" open failed: \" << super_partition;\n        return false;\n    }\n\n    std::string slot_suffix = SlotSuffixForSlotNumber(slot_number);\n\n    // Before writing geometry and/or logical partition tables, perform some\n    // basic checks that the geometry and tables are coherent, and will fit\n    // on the given block device.\n    std::string blob;\n    if (!ValidateAndSerializeMetadata(opener, metadata, slot_suffix, &blob)) {\n        return false;\n    }\n\n    // Verify that the old geometry is identical. If it's not, then we might be\n    // writing a table that was built for a different device, so we must reject\n    // it.\n    const LpMetadataGeometry& geometry = metadata.geometry;\n    LpMetadataGeometry old_geometry;\n    if (!ReadLogicalPartitionGeometry(fd, &old_geometry)) {\n        return false;\n    }\n    if (!CompareGeometry(geometry, old_geometry)) {\n        LERROR << \"Incompatible geometry in new logical partition metadata\";\n        return false;\n    }\n\n    // Validate the slot number now, before we call Read*Metadata.\n    if (slot_number >= geometry.metadata_slot_count) {\n        LERROR << \"Invalid logical partition metadata slot number.\";\n        return false;\n    }\n\n    // Try to read both existing copies of the metadata, if any.\n    std::unique_ptr<LpMetadata> primary = ReadPrimaryMetadata(fd, geometry, slot_number);\n    std::unique_ptr<LpMetadata> backup = ReadBackupMetadata(fd, geometry, slot_number);\n\n    if (primary && (!backup || !CompareMetadata(*primary.get(), *backup.get()))) {\n        // If the backup copy does not match the primary copy, we first\n        // synchronize the backup copy. This guarantees that a partial write\n        // still leaves one copy intact.\n        std::string old_blob;\n        if (!ValidateAndSerializeMetadata(opener, *primary.get(), slot_suffix, &old_blob)) {\n            LERROR << \"Error serializing primary metadata to repair corrupted backup\";\n            return false;\n        }\n        if (!WriteBackupMetadata(fd, metadata, slot_number, old_blob, writer)) {\n            LERROR << \"Error writing primary metadata to repair corrupted backup\";\n            return false;\n        }\n    } else if (backup && !primary) {\n        // The backup copy is coherent, and the primary is not. Sync it for\n        // safety.\n        std::string old_blob;\n        if (!ValidateAndSerializeMetadata(opener, *backup.get(), slot_suffix, &old_blob)) {\n            LERROR << \"Error serializing backup metadata to repair corrupted primary\";\n            return false;\n        }\n        if (!WritePrimaryMetadata(fd, metadata, slot_number, old_blob, writer)) {\n            LERROR << \"Error writing backup metadata to repair corrupted primary\";\n            return false;\n        }\n    }\n\n    // Both copies should now be in sync, so we can continue the update.\n    if (!WriteMetadata(fd, metadata, slot_number, blob, writer)) {\n        return false;\n    }\n\n    LINFO << \"Updated logical partition table at slot \" << slot_number << \" on device \"\n          << super_partition;\n    return true;\n}\n\nbool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,\n                          const LpMetadata& metadata, uint32_t slot_number) {\n    return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);\n}\n\nbool UpdatePartitionTable(const std::string& super_partition, const LpMetadata& metadata,\n                          uint32_t slot_number) {\n    PartitionOpener opener;\n    return UpdatePartitionTable(opener, super_partition, metadata, slot_number, DefaultWriter);\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/liblp/writer.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef LIBLP_WRITER_H\n#define LIBLP_WRITER_H\n\n#include <functional>\n#include <string>\n\n#include <liblp/liblp.h>\n\nnamespace android {\nnamespace fs_mgr {\n\nstd::string SerializeGeometry(const LpMetadataGeometry& input);\nstd::string SerializeMetadata(const LpMetadata& input);\n\n// These variants are for testing only. The path-based functions should be used\n// for actual operation, so that open() is called with the correct flags.\nbool UpdatePartitionTable(const IPartitionOpener& opener, const std::string& super_partition,\n                          const LpMetadata& metadata, uint32_t slot_number,\n                          const std::function<bool(int, const std::string&)>& writer);\n\n}  // namespace fs_mgr\n}  // namespace android\n\n#endif /* LIBLP_WRITER_H */\n"
  },
  {
    "path": "fs_mgr/libsnapshot/Android.bp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libsnapshot_defaults\",\n    defaults: [\"fs_mgr_defaults\"],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libchrome\",\n        \"libcutils\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libbrotli\",\n        \"libdm\",\n        \"libfstab\",\n        \"update_metadata-protos\",\n    ],\n    whole_static_libs: [\n        \"libbrotli\",\n        \"libcutils\",\n        \"libext2_uuid\",\n        \"libext4_utils\",\n        \"libfstab\",\n        \"libsnapuserd_client\",\n        \"libz\",\n    ],\n    header_libs: [\n        \"libfiemap_headers\",\n        \"libstorage_literals_headers\",\n        \"libupdate_engine_headers\",\n    ],\n    export_static_lib_headers: [\n        \"update_metadata-protos\",\n    ],\n    export_header_lib_headers: [\n        \"libfiemap_headers\",\n    ],\n    export_include_dirs: [\"include\"],\n    proto: {\n        type: \"lite\",\n        export_proto_headers: true,\n        canonical_path_from_root: false,\n    },\n}\n\ncc_defaults {\n    name: \"libsnapshot_hal_deps\",\n    cflags: [\n        \"-DLIBSNAPSHOT_USE_HAL\",\n    ],\n    shared_libs: [\n        \"android.hardware.boot@1.0\",\n        \"android.hardware.boot@1.1\",\n        \"android.hardware.boot-V1-ndk\",\n        \"libboot_control_client\",\n    ],\n}\n\nfilegroup {\n    name: \"libsnapshot_sources\",\n    srcs: [\n        \"android/snapshot/snapshot.proto\",\n        \"device_info.cpp\",\n        \"snapshot.cpp\",\n        \"snapshot_stats.cpp\",\n        \"snapshot_stub.cpp\",\n        \"snapshot_metadata_updater.cpp\",\n        \"partition_cow_creator.cpp\",\n        \"return.cpp\",\n        \"utility.cpp\",\n        \"scratch_super.cpp\",\n    ],\n}\n\ncc_library_headers {\n    name: \"libsnapshot_headers\",\n    recovery_available: true,\n    defaults: [\"libsnapshot_defaults\"],\n}\n\ncc_library_static {\n    name: \"libsnapshot_static\",\n    defaults: [\n        \"libsnapshot_defaults\",\n        \"libsnapshot_hal_deps\",\n    ],\n    srcs: [\":libsnapshot_sources\"],\n    static_libs: [\n        \"libfs_mgr_binder\",\n    ],\n    whole_static_libs: [\n        \"libselinux\",\n    ],\n}\n\ncc_library {\n    name: \"libsnapshot\",\n    defaults: [\n        \"libsnapshot_defaults\",\n        \"libsnapshot_cow_defaults\",\n        \"libsnapshot_hal_deps\",\n    ],\n    srcs: [\":libsnapshot_sources\"],\n    shared_libs: [\n        \"libfs_mgr_binder\",\n        \"liblp\",\n        \"libprotobuf-cpp-lite\",\n    ],\n    static_libs: [\n        \"libsnapshot_cow\",\n    ],\n    whole_static_libs: [\n        \"libselinux\",\n    ],\n}\n\ncc_library_static {\n    name: \"libsnapshot_init\",\n    native_coverage: true,\n    defaults: [\"libsnapshot_defaults\"],\n    srcs: [\":libsnapshot_sources\"],\n    ramdisk_available: true,\n    recovery_available: true,\n    cflags: [\n        \"-DLIBSNAPSHOT_NO_COW_WRITE\",\n    ],\n    static_libs: [\n        \"libfs_mgr\",\n        \"libselinux\",\n    ],\n}\n\ncc_library_static {\n    name: \"libsnapshot_nobinder\",\n    defaults: [\n        \"libsnapshot_defaults\",\n        \"libsnapshot_hal_deps\",\n    ],\n    srcs: [\":libsnapshot_sources\"],\n    recovery_available: true,\n    cflags: [\n        \"-DLIBSNAPSHOT_NO_COW_WRITE\",\n    ],\n    static_libs: [\n        \"libfs_mgr\",\n    ],\n    whole_static_libs: [\n        \"libselinux\",\n    ],\n}\n\ncc_defaults {\n    name: \"libsnapshot_cow_defaults\",\n    defaults: [\n        \"fs_mgr_defaults\",\n    ],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libbrotli\",\n        \"libz\",\n        \"liblz4\",\n        \"libzstd\",\n    ],\n    header_libs: [\n        \"libupdate_engine_headers\",\n    ],\n}\n\ncc_library_static {\n    name: \"libsnapshot_cow\",\n    defaults: [\n        \"libsnapshot_cow_defaults\",\n    ],\n    srcs: [\n        \"libsnapshot_cow/cow_compress.cpp\",\n        \"libsnapshot_cow/cow_decompress.cpp\",\n        \"libsnapshot_cow/cow_format.cpp\",\n        \"libsnapshot_cow/cow_reader.cpp\",\n        \"libsnapshot_cow/parser_v2.cpp\",\n        \"libsnapshot_cow/parser_v3.cpp\",\n        \"libsnapshot_cow/snapshot_reader.cpp\",\n        \"libsnapshot_cow/writer_base.cpp\",\n        \"libsnapshot_cow/writer_v2.cpp\",\n        \"libsnapshot_cow/writer_v3.cpp\",\n    ],\n\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    export_include_dirs: [\"include\"],\n    host_supported: true,\n    recovery_available: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n}\n\ncc_library_static {\n    name: \"libsnapshot_test_helpers\",\n    defaults: [\"libsnapshot_defaults\"],\n    export_include_dirs: [\n        \"include_test\",\n    ],\n    srcs: [\n        \"android/snapshot/snapshot.proto\",\n        \"test_helpers.cpp\",\n    ],\n    shared_libs: [\n        \"android.hardware.boot@1.1\",\n        \"libcrypto\",\n    ],\n    export_shared_lib_headers: [\n        \"android.hardware.boot@1.1\",\n    ],\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libstorage_literals_headers\",\n    ],\n    static_libs: [\n        \"libfs_mgr\",\n        \"libgmock\",\n        \"libgtest\",\n        \"libselinux\",\n    ],\n}\n\ncc_defaults {\n    name: \"libsnapshot_test_defaults\",\n    defaults: [\n        \"libsnapshot_defaults\",\n        \"libsnapshot_cow_defaults\",\n    ],\n    srcs: [\n        \"partition_cow_creator_test.cpp\",\n        \"snapshot_metadata_updater_test.cpp\",\n        \"snapshot_test.cpp\",\n    ],\n    shared_libs: [\n        \"libbinder\",\n        \"libcrypto\",\n        \"libhidlbase\",\n        \"libprotobuf-cpp-lite\",\n        \"libutils\",\n        \"libz\",\n    ],\n    static_libs: [\n        \"android.hardware.boot@1.0\",\n        \"android.hardware.boot@1.1\",\n        \"android.hardware.boot-V1-ndk\",\n        \"libbrotli\",\n        \"libfs_mgr_binder\",\n        \"libgflags\",\n        \"libgsi\",\n        \"libgmock\",\n        \"liblp\",\n        \"libsnapshot_static\",\n        \"libsnapshot_cow\",\n        \"libsnapshot_test_helpers\",\n        \"libsparse\",\n    ],\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    auto_gen_config: true,\n    require_root: true,\n}\n\ncc_test {\n    name: \"vts_libsnapshot_test\",\n    defaults: [\n        \"libsnapshot_test_defaults\",\n        \"libsnapshot_hal_deps\",\n    ],\n    test_suites: [\n        \"vts\",\n        \"general-tests\",\n    ],\n    compile_multilib: \"first\",\n    test_options: {\n        min_shipping_api_level: 30,\n        test_runner_options: [\n            {\n                name: \"force-no-test-error\",\n                value: \"false\",\n            },\n        ],\n    },\n}\n\ncc_test {\n    name: \"vab_legacy_tests\",\n    defaults: [\n        \"libsnapshot_test_defaults\",\n        \"libsnapshot_hal_deps\",\n    ],\n    cppflags: [\n        \"-DLIBSNAPSHOT_TEST_VAB_LEGACY\",\n    ],\n    test_suites: [\n        \"general-tests\",\n    ],\n    compile_multilib: \"64\",\n    test_options: {\n        // Legacy VAB launched in Android R.\n        min_shipping_api_level: 30,\n        test_runner_options: [\n            {\n                name: \"force-no-test-error\",\n                value: \"false\",\n            },\n        ],\n    },\n}\n\ncc_test {\n    name: \"vts_ota_config_test\",\n    srcs: [\n        \"vts_ota_config_test.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    test_suites: [\n        \"vts\",\n    ],\n    test_options: {\n        min_shipping_api_level: 33,\n    },\n    auto_gen_config: true,\n    require_root: true,\n}\n\ncc_binary {\n    name: \"snapshotctl\",\n    defaults: [\n        \"libsnapshot_cow_defaults\",\n        \"libsnapshot_hal_deps\",\n    ],\n    srcs: [\n        \"snapshotctl.cpp\",\n        \"scratch_super.cpp\",\n        \"android/snapshot/snapshot.proto\",\n    ],\n    static_libs: [\n        \"libbrotli\",\n        \"libfstab\",\n        \"libz\",\n        \"libavb\",\n        \"libfs_avb\",\n        \"libcrypto_static\",\n        \"update_metadata-protos\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libext2_uuid\",\n        \"libext4_utils\",\n        \"libfs_mgr_binder\",\n        \"libhidlbase\",\n        \"liblog\",\n        \"liblp\",\n        \"libprotobuf-cpp-lite\",\n        \"libsnapshot\",\n        \"libstatslog\",\n        \"libutils\",\n    ],\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    product_variables: {\n        debuggable: {\n            cppflags: [\n                \"-DSNAPSHOTCTL_USERDEBUG_OR_ENG\",\n            ],\n            shared_libs: [\n                \"android.hardware.boot@1.0\",\n                \"android.hardware.boot@1.1\",\n                \"android.hardware.boot-V1-ndk\",\n                \"libboot_control_client\",\n            ],\n        },\n    },\n}\n\ncc_test {\n    name: \"cow_api_test\",\n    defaults: [\n        \"fs_mgr_defaults\",\n        \"libsnapshot_cow_defaults\",\n    ],\n    srcs: [\n        \"libsnapshot_cow/snapshot_reader_test.cpp\",\n        \"libsnapshot_cow/test_v2.cpp\",\n        \"libsnapshot_cow/test_v3.cpp\",\n    ],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcrypto\",\n        \"liblog\",\n        \"libz\",\n    ],\n    static_libs: [\n        \"libbrotli\",\n        \"libgtest\",\n        \"libsnapshot_cow\",\n    ],\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n    test_suites: [\n        \"general-tests\",\n    ],\n    test_options: {\n        min_shipping_api_level: 30,\n    },\n    data: [\n        \"tools/testdata/cow_v2\",\n        \"tools/testdata/incompressible_block\",\n    ],\n    auto_gen_config: true,\n    require_root: false,\n    host_supported: true,\n}\n\ncc_binary {\n    name: \"inspect_cow\",\n    host_supported: true,\n    device_supported: true,\n    defaults: [\"libsnapshot_cow_defaults\"],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"libbrotli\",\n        \"libcrypto_static\",\n        \"liblog\",\n        \"libgflags\",\n        \"libsnapshot_cow\",\n        \"libz\",\n    ],\n    shared_libs: [\n    ],\n    srcs: [\n        \"libsnapshot_cow/inspect_cow.cpp\",\n    ],\n}\n\ncc_binary {\n    name: \"create_snapshot\",\n    host_supported: true,\n    device_supported: false,\n\n    srcs: [\n        \"libsnapshot_cow/create_cow.cpp\",\n        \"android/snapshot/snapshot.proto\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    static_libs: [\n        \"liblog\",\n        \"libbase\",\n        \"libfstab\",\n        \"libext4_utils\",\n        \"libsnapshot_cow\",\n        \"libcrypto\",\n        \"libbrotli\",\n        \"libz\",\n        \"libdm\",\n        \"liblz4\",\n        \"libzstd\",\n        \"libgflags\",\n        \"libavb\",\n        \"libext2_uuid\",\n        \"libfs_avb\",\n        \"libcrypto\",\n        \"libprotobuf-cpp-lite\",\n    ],\n    shared_libs: [\n    ],\n\n    header_libs: [\n        \"libstorage_literals_headers\",\n    ],\n\n    dist: {\n        targets: [\n            \"sdk\",\n            \"sdk-repo-platform-tools\",\n            \"sdk_repo\",\n        ],\n    },\n    target: {\n        darwin: {\n            enabled: false,\n        },\n        windows: {\n            enabled: false,\n        },\n    },\n    stl: \"libc++_static\",\n    static_executable: true,\n}\n\npython_library_host {\n    name: \"snapshot_proto_python\",\n    srcs: [\n        \"android/snapshot/snapshot.proto\",\n    ],\n    proto: {\n        canonical_path_from_root: false,\n    },\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/OWNERS",
    "content": "# Bug component: 1014951\nbalsini@google.com\ndvander@google.com\nelsk@google.com\nakailash@google.com\nzhangkelvin@google.com\n"
  },
  {
    "path": "fs_mgr/libsnapshot/android/snapshot/snapshot.proto",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsyntax = \"proto3\";\npackage android.snapshot;\n\noption optimize_for = LITE_RUNTIME;\n\n// Next: 4\nenum SnapshotState {\n    // No snapshot is found.\n    NONE = 0;\n\n    // The snapshot has been created and possibly written to. Rollbacks are\n    // possible by destroying the snapshot.\n    CREATED = 1;\n\n    // Changes are being merged. No rollbacks are possible beyond this point.\n    MERGING = 2;\n\n    // Changes have been merged, Future reboots may map the base device\n    // directly.\n    MERGE_COMPLETED = 3;\n}\n\n// Next: 3\nenum MergePhase {\n    // No merge is in progress.\n    NO_MERGE = 0;\n\n    // Shrunk partitions can merge.\n    FIRST_PHASE = 1;\n\n    // Grown partitions can merge.\n    SECOND_PHASE = 2;\n}\n\n// Next: 13\nmessage SnapshotStatus {\n    // Name of the snapshot. This is usually the name of the snapshotted\n    // logical partition; for example, \"system_b\".\n    string name = 1;\n\n    SnapshotState state = 2;\n\n    // Size of the full (base) device.\n    uint64 device_size = 3;\n\n    // Size of the snapshot. This is the sum of lengths of ranges in the base\n    // device that needs to be snapshotted during the update.\n    // This must be less than or equal to |device_size|.\n    // This value is 0 if no snapshot is needed for this device because\n    // no changes\n    uint64 snapshot_size = 4;\n\n    // Size of the \"COW partition\". A COW partition is a special logical\n    // partition represented in the super partition metadata. This partition and\n    // the \"COW image\" form the \"COW device\" that supports the snapshot device.\n    //\n    // When SnapshotManager creates a COW device, it first searches for unused\n    // blocks in the super partition, and use those before creating the COW\n    // image if the COW partition is not big enough.\n    //\n    // This value is 0 if no space in super is left for the COW partition.\n    // |cow_partition_size + cow_file_size| must not be zero if |snapshot_size|\n    // is non-zero.\n    uint64 cow_partition_size = 5;\n\n    // Size of the \"COW file\", or \"COW image\". A COW file / image is created\n    // when the \"COW partition\" is not big enough to store changes to the\n    // snapshot device.\n    //\n    // This value is 0 if |cow_partition_size| is big enough to hold all changes\n    // to the snapshot device.\n    uint64 cow_file_size = 6;\n\n    // Sectors allocated for the COW device. Recording this value right after\n    // the update and before the merge allows us to infer the progress of the\n    // merge process.\n    // This is non-zero when |state| == MERGING or MERGE_COMPLETED.\n    uint64 sectors_allocated = 7;\n\n    // Metadata sectors allocated for the COW device. Recording this value right\n    // before the update and before the merge allows us to infer the progress of\n    // the merge process.\n    // This is non-zero when |state| == MERGING or MERGE_COMPLETED.\n    uint64 metadata_sectors = 8;\n\n    // True if using snapuserd, false otherwise.\n    bool using_snapuserd = 9;\n\n    // The old partition size (if none existed, this will be zero).\n    uint64 old_partition_size = 10;\n\n    // Compression algorithm (none, lz4, zstd).\n    string compression_algorithm = 11;\n\n    // Estimated COW size from OTA manifest.\n    uint64 estimated_cow_size = 12;\n\n    // Enable multi-threaded compression\n    bool enable_threading = 13;\n\n    // Enable batching for COW writes\n    bool batched_writes = 14;\n\n    // Size of v3 operation buffer. Needs to be determined during writer initialization\n    uint64 estimated_ops_buffer_size = 15;\n\n    // Max bytes to be compressed at once (4k, 8k, 16k, 32k, 64k, 128k)\n    uint64 compression_factor = 16;\n\n    // Default value is 32, can be set lower for low mem devices\n    uint32 read_ahead_size = 17;\n\n    reserved 18;\n\n    // Blocks size to be verified at once\n    uint64 verify_block_size = 19;\n\n    // Default value is 2, configures threads to do verification phase\n    uint32 num_verify_threads = 20;\n}\n\n// Next: 8\nenum UpdateState {\n    // No update or merge is in progress.\n    None = 0;\n\n    // An update is applying; snapshots may already exist.\n    Initiated = 1;\n\n    // An update is pending, but has not been successfully booted yet.\n    Unverified = 2;\n\n    // The kernel is merging in the background.\n    Merging = 3;\n\n    // Post-merge cleanup steps could not be completed due to a transient\n    // error, but the next reboot will finish any pending operations.\n    MergeNeedsReboot = 4;\n\n    // Merging is complete, and needs to be acknowledged.\n    MergeCompleted = 5;\n\n    // Merging failed due to an unrecoverable error.\n    MergeFailed = 6;\n\n    // The update was implicitly cancelled, either by a rollback or a flash\n    // operation via fastboot. This state can only be returned by WaitForMerge.\n    Cancelled = 7;\n};\n\n// Next 14:\n//\n// To understand the source of each failure, read snapshot.cpp. To handle new\n// sources of failure, avoid reusing an existing code; add a new code instead.\nenum MergeFailureCode {\n    Ok = 0;\n    ReadStatus = 1;\n    GetTableInfo = 2;\n    UnknownTable = 3;\n    GetTableParams = 4;\n    ActivateNewTable = 5;\n    AcquireLock = 6;\n    ListSnapshots = 7;\n    WriteStatus = 8;\n    UnknownTargetType = 9;\n    QuerySnapshotStatus = 10;\n    ExpectedMergeTarget = 11;\n    UnmergedSectorsAfterCompletion = 12;\n    UnexpectedMergeState = 13;\n    GetCowPathConsistencyCheck = 14;\n    OpenCowConsistencyCheck = 15;\n    ParseCowConsistencyCheck = 16;\n    OpenCowDirectConsistencyCheck = 17;\n    MemAlignConsistencyCheck = 18;\n    DirectReadConsistencyCheck = 19;\n    WrongMergeCountConsistencyCheck = 20;\n};\n\n// Next: 8\nmessage SnapshotUpdateStatus {\n    UpdateState state = 1;\n\n    // Total number of sectors allocated in the COW files before performing the\n    // merge operation.  This field is used to keep track of the total number\n    // of sectors modified to monitor and show the progress of the merge during\n    // an update.\n    uint64 sectors_allocated = 2;\n\n    // Total number of sectors of all the snapshot devices.\n    uint64 total_sectors = 3;\n\n    // Sectors allocated for metadata in all the snapshot devices.\n    uint64 metadata_sectors = 4;\n\n    // Whether compression/dm-user was used for any snapshots.\n    bool using_snapuserd = 5;\n\n    // Merge phase (if state == MERGING).\n    MergePhase merge_phase = 6;\n\n    // Merge failure code, filled if state == MergeFailed.\n    MergeFailureCode merge_failure_code = 7;\n\n    // Source build fingerprint.\n    string source_build_fingerprint = 8;\n\n    // user-space snapshots\n    bool userspace_snapshots = 9;\n\n    // io_uring support\n    bool io_uring_enabled = 10;\n\n    // legacy dm-snapshot based snapuserd\n    bool legacy_snapuserd = 11;\n\n    // Enable direct reads from source device\n    bool o_direct = 12;\n\n    // Number of cow operations to be merged at once\n    uint32 cow_op_merge_size = 13;\n\n    // Number of worker threads to serve I/O from dm-user\n    uint32 num_worker_threads = 14;\n}\n\n// Next: 10\nmessage SnapshotMergeReport {\n    // Status of the update after the merge attempts.\n    UpdateState state = 1;\n\n    // Number of reboots that occurred after issuing and before completeing the\n    // merge of all the snapshot devices.\n    int32 resume_count = 2;\n\n    // Total size of all the COW images before the update.\n    uint64 cow_file_size = 3;\n\n    // Whether compression/dm-user was used for any snapshots.\n    bool compression_enabled = 4;\n\n    // Total size used by COWs, including /data and the super partition.\n    uint64 total_cow_size_bytes = 5;\n\n    // Sum of the estimated COW fields in the OTA manifest.\n    uint64 estimated_cow_size_bytes = 6;\n\n    // Time from boot to sys.boot_completed, in milliseconds.\n    uint32 boot_complete_time_ms = 7;\n\n    // Time from sys.boot_completed to merge start, in milliseconds.\n    uint32 boot_complete_to_merge_start_time_ms = 8;\n\n    // Merge failure code, filled if the merge failed at any time (regardless\n    // of whether it succeeded at a later time).\n    MergeFailureCode merge_failure_code = 9;\n\n    // The source fingerprint at the time the OTA was downloaded.\n    string source_build_fingerprint = 10;\n\n    // Whether this update attempt uses userspace snapshots.\n    bool userspace_snapshots_used = 11;\n\n    // Whether this update attempt uses XOR compression.\n    bool xor_compression_used = 12;\n\n    // Whether this update attempt used io_uring.\n    bool iouring_used = 13;\n\n    // Size of v3 operation buffer. Needs to be determined during writer initialization\n    uint64 estimated_op_count_max = 14;\n}\n\nmessage VerityHash {\n  // Partition name\n  string partition_name = 1;\n\n  // Salt used for verity hashes\n  string salt = 2;\n\n  // sha256 hash values of each block in the image\n  repeated bytes block_hash = 3;\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/device_info.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"device_info.h\"\n#include \"scratch_super.h\"\n\n#include <android-base/logging.h>\n#include <fs_mgr.h>\n#include <fs_mgr_overlayfs.h>\n#include <libfiemap/image_manager.h>\n\nnamespace android {\nnamespace snapshot {\n\n#ifdef LIBSNAPSHOT_USE_HAL\nusing android::hal::BootControlClient;\nusing android::hal::BootControlVersion;\nusing android::hal::CommandResult;\n#endif\n\nusing namespace std::chrono_literals;\nusing namespace std::string_literals;\n\n#ifdef __ANDROID_RECOVERY__\nconstexpr bool kIsRecovery = true;\n#else\nconstexpr bool kIsRecovery = false;\n#endif\n\nDeviceInfo::DeviceInfo() {\n    std::string scratch_device = android::snapshot::GetScratchOtaMetadataPartition();\n    if (!scratch_device.empty()) {\n        std::string scratch_metadata =\n                android::snapshot::MapScratchOtaMetadataPartition(scratch_device);\n        if (!scratch_metadata.empty()) {\n            SetMetadataDir(scratch_metadata);\n            SetTempMetadata();\n        }\n    }\n}\n\nstd::string DeviceInfo::GetMetadataDir() const {\n    return metadata_dir_;\n}\n\nvoid DeviceInfo::SetMetadataDir(const std::string& value) {\n    metadata_dir_ = value;\n}\n\nstd::string DeviceInfo::GetSlotSuffix() const {\n    return fs_mgr_get_slot_suffix();\n}\n\nstd::string DeviceInfo::GetOtherSlotSuffix() const {\n    return fs_mgr_get_other_slot_suffix();\n}\n\nconst android::fs_mgr::IPartitionOpener& DeviceInfo::GetPartitionOpener() const {\n    return opener_;\n}\n\nstd::string DeviceInfo::GetSuperDevice(uint32_t slot) const {\n    return fs_mgr_get_super_partition_name(slot);\n}\n\nbool DeviceInfo::IsOverlayfsSetup() const {\n    return fs_mgr_overlayfs_is_setup();\n}\n\n#ifdef LIBSNAPSHOT_USE_HAL\nbool DeviceInfo::EnsureBootHal() {\n    if (!boot_control_) {\n        auto hal = BootControlClient::WaitForService();\n        if (!hal) {\n            LOG(ERROR) << \"Could not find IBootControl HAL\";\n            return false;\n        }\n        if (hal->GetVersion() < BootControlVersion::BOOTCTL_V1_1) {\n            LOG(ERROR) << \"Could not find IBootControl 1.1 HAL\";\n            return false;\n        }\n        boot_control_ = std::move(hal);\n    }\n    return true;\n}\n#endif\n\nbool DeviceInfo::SetBootControlMergeStatus([[maybe_unused]] MergeStatus status) {\n#ifdef LIBSNAPSHOT_USE_HAL\n    if (!EnsureBootHal()) {\n        return false;\n    }\n    const auto ret = boot_control_->SetSnapshotMergeStatus(status);\n    if (!ret.IsOk()) {\n        LOG(ERROR) << \"Unable to set the snapshot merge status \" << ret.errMsg;\n        return false;\n    }\n    return true;\n#else\n    LOG(ERROR) << \"HAL support not enabled.\";\n    return false;\n#endif\n}\n\nbool DeviceInfo::IsRecovery() const {\n    return kIsRecovery;\n}\n\nbool DeviceInfo::IsFirstStageInit() const {\n    return first_stage_init_;\n}\n\nbool DeviceInfo::SetActiveBootSlot([[maybe_unused]] unsigned int slot) {\n#ifdef LIBSNAPSHOT_USE_HAL\n    if (!EnsureBootHal()) {\n        return false;\n    }\n\n    CommandResult result = boot_control_->SetActiveBootSlot(slot);\n    if (!result.success) {\n        LOG(ERROR) << \"Error setting slot \" << slot << \" active: \" << result.errMsg;\n        return false;\n    }\n    return true;\n#else\n    LOG(ERROR) << \"HAL support not enabled.\";\n    return false;\n#endif\n}\n\nbool DeviceInfo::SetSlotAsUnbootable([[maybe_unused]] unsigned int slot) {\n#ifdef LIBSNAPSHOT_USE_HAL\n    if (!EnsureBootHal()) {\n        return false;\n    }\n\n    CommandResult result = boot_control_->MarkSlotUnbootable(slot);\n    if (!result.success) {\n        LOG(ERROR) << \"Error setting slot \" << slot << \" unbootable: \" << result.errMsg;\n        return false;\n    }\n    return true;\n#else\n    LOG(ERROR) << \"HAL support not enabled.\";\n    return false;\n#endif\n}\n\nstd::unique_ptr<android::fiemap::IImageManager> DeviceInfo::OpenImageManager() const {\n    return IDeviceInfo::OpenImageManager(\"ota\");\n}\n\nstd::unique_ptr<android::fiemap::IImageManager> ISnapshotManager::IDeviceInfo::OpenImageManager(\n        const std::string& gsid_dir) const {\n    if (IsRecovery() || IsFirstStageInit()) {\n        android::fiemap::ImageManager::DeviceInfo device_info = {\n                .is_recovery = {IsRecovery()},\n        };\n        return android::fiemap::ImageManager::Open(gsid_dir, device_info);\n    } else {\n        // For now, use a preset timeout.\n        return android::fiemap::IImageManager::Open(gsid_dir, 15000ms);\n    }\n}\n\nandroid::dm::IDeviceMapper& DeviceInfo::GetDeviceMapper() {\n    return android::dm::DeviceMapper::Instance();\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/device_info.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <string>\n\n#ifdef LIBSNAPSHOT_USE_HAL\n#include <BootControlClient.h>\n#endif\n#include <liblp/partition_opener.h>\n#include <libsnapshot/snapshot.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass DeviceInfo final : public SnapshotManager::IDeviceInfo {\n    using MergeStatus = ::aidl::android::hardware::boot::MergeStatus;\n\n  public:\n    DeviceInfo();\n    std::string GetMetadataDir() const override;\n    std::string GetSlotSuffix() const override;\n    std::string GetOtherSlotSuffix() const override;\n    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override;\n    std::string GetSuperDevice(uint32_t slot) const override;\n    bool IsOverlayfsSetup() const override;\n    bool SetBootControlMergeStatus(MergeStatus status) override;\n    bool SetActiveBootSlot(unsigned int slot) override;\n    bool SetSlotAsUnbootable(unsigned int slot) override;\n    bool IsRecovery() const override;\n    std::unique_ptr<IImageManager> OpenImageManager() const override;\n    bool IsFirstStageInit() const override;\n    android::dm::IDeviceMapper& GetDeviceMapper() override;\n    void SetMetadataDir(const std::string& value);\n    void set_first_stage_init(bool value) { first_stage_init_ = value; }\n    bool IsTempMetadata() const override { return temp_metadata_; }\n    void SetTempMetadata() { temp_metadata_ = true; }\n\n  private:\n    bool EnsureBootHal();\n\n    android::fs_mgr::PartitionOpener opener_;\n    bool first_stage_init_ = false;\n    // Default value\n    std::string metadata_dir_ = \"/metadata/ota\";\n    bool temp_metadata_ = false;\n#ifdef LIBSNAPSHOT_USE_HAL\n    std::unique_ptr<::android::hal::BootControlClient> boot_control_;\n#endif\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/dm_snapshot_internals.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <android-base/logging.h>\n#include <stdint.h>\n\n#include <limits>\n#include <optional>\n#include <unordered_set>\n\nnamespace android {\nnamespace snapshot {\n\nclass DmSnapCowSizeCalculator {\n  public:\n    DmSnapCowSizeCalculator(unsigned int sector_bytes, unsigned int chunk_sectors)\n        : sector_bytes_(sector_bytes),\n          chunk_sectors_(chunk_sectors),\n          exceptions_per_chunk(chunk_sectors_ * sector_bytes_ / exception_size_bytes) {}\n\n    void WriteByte(uint64_t address) { WriteSector(address / sector_bytes_); }\n    void WriteSector(uint64_t sector) { WriteChunk(sector / chunk_sectors_); }\n    void WriteChunk(uint64_t chunk_id) {\n        if (!valid_) {\n            return;\n        }\n\n        if (chunk_id > std::numeric_limits<uint32_t>::max()) {\n            LOG(ERROR) << \"Chunk exceeds maximum size: \" << chunk_id;\n            valid_ = false;\n            return;\n        }\n        if (modified_chunks_.count(chunk_id) > 0) {\n            return;\n        }\n\n        modified_chunks_.emplace(chunk_id);\n    }\n\n    std::optional<uint64_t> cow_size_bytes() const {\n        auto sectors = cow_size_sectors();\n        if (!sectors) {\n            return std::nullopt;\n        }\n        return sectors.value() * sector_bytes_;\n    }\n    std::optional<uint64_t> cow_size_sectors() const {\n        auto chunks = cow_size_chunks();\n        if (!chunks) {\n            return std::nullopt;\n        }\n        return chunks.value() * chunk_sectors_;\n    }\n\n    /*\n     * The COW device has a precise internal structure as follows:\n     *\n     * - header (1 chunk)\n     * - #0 map and chunks\n     *   - map (1 chunk)\n     *   - chunks addressable by previous map (exceptions_per_chunk)\n     * - #1 map and chunks\n     *   - map (1 chunk)\n     *   - chunks addressable by previous map (exceptions_per_chunk)\n     * ...\n     * - #n: map and chunks\n     *   - map (1 chunk)\n     *   - chunks addressable by previous map (exceptions_per_chunk)\n     * - 1 extra chunk\n     */\n    std::optional<uint64_t> cow_size_chunks() const {\n        if (!valid_) {\n            LOG(ERROR) << \"Invalid COW size.\";\n            return std::nullopt;\n        }\n\n        uint64_t cow_chunks = 0;\n\n        /* disk header + padding = 1 chunk */\n        cow_chunks += 1;\n\n        /* snapshot modified chunks */\n        cow_chunks += modified_chunks_.size();\n\n        /* snapshot chunks index metadata */\n        cow_chunks += 1 + modified_chunks_.size() / exceptions_per_chunk;\n\n        return cow_chunks;\n    }\n\n  private:\n    /*\n     * Size of each sector in bytes.\n     */\n    const uint64_t sector_bytes_;\n\n    /*\n     * Size of each chunk in sectors.\n     */\n    const uint64_t chunk_sectors_;\n\n    /*\n     * The COW device stores tables to map the modified chunks. Each table has\n     * the size of exactly 1 chunk.\n     * Each entry of the table is called exception and the number of exceptions\n     * that each table can contain determines the number of data chunks that\n     * separate two consecutive tables. This value is then fundamental to\n     * compute the space overhead introduced by the tables in COW devices.\n     */\n    const uint64_t exceptions_per_chunk;\n\n    /*\n     * Each row of the table (called exception in the kernel) contains two\n     * 64 bit indices to identify the corresponding chunk, and this 128 bit\n     * pair is constant in size.\n     */\n    static constexpr unsigned int exception_size_bytes = 64 * 2 / 8;\n\n    /*\n     * Validity check for the container.\n     * It may happen that the caller attempts the write of an invalid chunk\n     * identifier, and this misbehavior is accounted and stored in this value.\n     */\n    bool valid_ = true;\n\n    /*\n     * |modified_chunks_| is a container that keeps trace of the modified\n     * chunks.\n     */\n    std::unordered_set<uint32_t> modified_chunks_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/auto_device.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <string>\n\n#include <android-base/macros.h>\n\nnamespace android {\nnamespace snapshot {\n\n// An abstract \"device\" that will be cleaned up (unmapped, unmounted, etc.) upon\n// destruction.\nstruct AutoDevice {\n    virtual ~AutoDevice(){};\n    void Release();\n\n    bool HasDevice() const { return !name_.empty(); }\n\n  protected:\n    AutoDevice(const std::string& name) : name_(name) {}\n    std::string name_;\n\n  private:\n    DISALLOW_COPY_AND_ASSIGN(AutoDevice);\n    AutoDevice(AutoDevice&& other) = delete;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/cow_compress.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <memory>\n#include <vector>\n\n#include \"libsnapshot/cow_format.h\"\n\nnamespace android {\nnamespace snapshot {\n\nclass ICompressor {\n  public:\n    explicit ICompressor(const int32_t compression_level, const uint32_t block_size)\n        : compression_level_(compression_level), block_size_(block_size) {}\n\n    virtual ~ICompressor() {}\n    // Factory methods for compression methods.\n    static std::unique_ptr<ICompressor> Gz(const int32_t compression_level,\n                                           const uint32_t block_size);\n    static std::unique_ptr<ICompressor> Brotli(const int32_t compression_level,\n                                               const uint32_t block_size);\n    static std::unique_ptr<ICompressor> Lz4(const int32_t compression_level,\n                                            const uint32_t block_size);\n    static std::unique_ptr<ICompressor> Zstd(const int32_t compression_level,\n                                             const uint32_t block_size);\n\n    static std::unique_ptr<ICompressor> Create(CowCompression compression,\n                                               const uint32_t block_size);\n\n    int32_t GetCompressionLevel() const { return compression_level_; }\n    uint32_t GetBlockSize() const { return block_size_; }\n    [[nodiscard]] virtual std::vector<uint8_t> Compress(const void* data, size_t length) const = 0;\n\n  private:\n    const int32_t compression_level_;\n    const uint32_t block_size_;\n};\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/cow_format.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n\n#include <limits>\n#include <optional>\n#include <string_view>\n#include <type_traits>\n\nnamespace android {\nnamespace snapshot {\n\nstruct CowOperationV3;\ntypedef CowOperationV3 CowOperation;\n\nstatic constexpr uint64_t kCowMagicNumber = 0x436f77634f572121ULL;\nstatic constexpr uint32_t kCowVersionMajor = 2;\nstatic constexpr uint32_t kCowVersionMinor = 0;\n\nstatic constexpr uint32_t kMinCowVersion = 1;\nstatic constexpr uint32_t kMaxCowVersion = 3;\n\n// This header appears as the first sequence of bytes in the COW. All fields\n// in the layout are little-endian encoded. The on-disk layout is:\n//\n//      +-----------------------+\n//      |     Header (fixed)    |\n//      +-----------------------+\n//      |     Scratch space     |\n//      +-----------------------+\n//      | Operation  (variable) |\n//      | Data       (variable) |\n//      +-----------------------+\n//      |    Footer (fixed)     |\n//      +-----------------------+\n//\n// After the header is a 2mb scratch space that is used to read ahead data during merge operations\n//\n// The operations begin immediately after the scratch space, and the \"raw data\"\n// immediately follows the operation which refers to it. While streaming\n// an OTA, we can immediately write the op and data, syncing after each pair,\n// while storing operation metadata in memory. At the end, we compute data and\n// hashes for the footer, which is placed at the tail end of the file.\n//\n// A missing or corrupt footer likely indicates that writing was cut off\n// between writing the last operation/data pair, or the footer itself. In this\n// case, the safest way to proceed is to assume the last operation is faulty.\n\nstruct CowHeaderPrefix {\n    uint64_t magic;\n    uint16_t major_version;\n    uint16_t minor_version;\n    uint16_t header_size;  // size of CowHeader.\n} __attribute__((packed));\n\nstruct CowHeader {\n    CowHeaderPrefix prefix;\n\n    // Size of footer struct\n    uint16_t footer_size;\n\n    // Size of op struct\n    uint16_t op_size;\n\n    // The size of block operations, in bytes.\n    uint32_t block_size;\n\n    // The number of ops to cluster together. 0 For no clustering. Cannot be 1.\n    uint32_t cluster_ops;\n\n    // Tracks merge operations completed\n    uint64_t num_merge_ops;\n\n    // Scratch space used during merge\n    uint32_t buffer_size;\n\n} __attribute__((packed));\n\n// Resume point structure used for resume buffer\nstruct ResumePoint {\n    // monotonically increasing value used by update_engine\n    uint64_t label;\n    // Index of last CowOperation guaranteed to be resumable\n    uint64_t op_index;\n} __attribute__((packed));\n\nstatic constexpr uint8_t kNumResumePoints = 4;\n\nstruct CowHeaderV3 : public CowHeader {\n    // Number of sequence data stored (each of which is a 32 bit integer)\n    uint64_t sequence_data_count;\n    // Number of currently written resume points &&\n    uint32_t resume_point_count;\n    // Number of max resume points that can be written\n    uint32_t resume_point_max;\n    // Number of CowOperationV3 structs in the operation buffer, currently and total\n    // region size.\n    uint64_t op_count;\n    uint64_t op_count_max;\n    // Compression Algorithm\n    uint32_t compression_algorithm;\n    // Max compression size supported\n    uint32_t max_compression_size;\n} __attribute__((packed));\n\nenum class CowOperationType : uint8_t {\n    kCowCopyOp = 1,\n    kCowReplaceOp = 2,\n    kCowZeroOp = 3,\n    kCowLabelOp = 4,\n    kCowClusterOp = 5,\n    kCowXorOp = 6,\n    kCowSequenceOp = 7,\n    kCowFooterOp = std::numeric_limits<uint8_t>::max(),\n};\n\nstatic constexpr CowOperationType kCowCopyOp = CowOperationType::kCowCopyOp;\nstatic constexpr CowOperationType kCowReplaceOp = CowOperationType::kCowReplaceOp;\nstatic constexpr CowOperationType kCowZeroOp = CowOperationType::kCowZeroOp;\nstatic constexpr CowOperationType kCowLabelOp = CowOperationType::kCowLabelOp;\nstatic constexpr CowOperationType kCowClusterOp = CowOperationType::kCowClusterOp;\nstatic constexpr CowOperationType kCowXorOp = CowOperationType::kCowXorOp;\nstatic constexpr CowOperationType kCowSequenceOp = CowOperationType::kCowSequenceOp;\nstatic constexpr CowOperationType kCowFooterOp = CowOperationType::kCowFooterOp;\n\n// This structure is the same size of a normal Operation, but is repurposed for the footer.\nstruct CowFooterOperation {\n    // The operation code (always kCowFooterOp).\n    CowOperationType type;\n\n    // If this operation reads from the data section of the COW, this contains\n    // the compression type of that data (see constants below).\n    uint8_t compression;\n\n    // Length of Footer Data. Currently 64.\n    uint16_t data_length;\n\n    // The amount of file space used by Cow operations\n    uint64_t ops_size;\n\n    // The number of cow operations in the file\n    uint64_t num_ops;\n} __attribute__((packed));\n\n// V2 version of COW. On disk format for older devices\nstruct CowOperationV2 {\n    // The operation code (see the constants and structures below).\n    CowOperationType type;\n\n    // If this operation reads from the data section of the COW, this contains\n    // the compression type of that data (see constants below).\n    uint8_t compression;\n\n    // If this operation reads from the data section of the COW, this contains\n    // the length.\n    uint16_t data_length;\n\n    // The block of data in the new image that this operation modifies.\n    uint64_t new_block;\n\n    // The value of |source| depends on the operation code.\n    //\n    // For copy operations, this is a block location in the source image.\n    //\n    // For replace operations, this is a byte offset within the COW's data\n    // sections (eg, not landing within the header or metadata). It is an\n    // absolute position within the image.\n    //\n    // For zero operations (replace with all zeroes), this is unused and must\n    // be zero.\n    //\n    // For Label operations, this is the value of the applied label.\n    //\n    // For Cluster operations, this is the length of the following data region\n    //\n    // For Xor operations, this is the byte location in the source image.\n    uint64_t source;\n} __attribute__((packed));\n\nstatic constexpr uint64_t kCowOpSourceInfoDataMask = (1ULL << 48) - 1;\nstatic constexpr uint64_t kCowOpSourceInfoTypeBit = 60;\nstatic constexpr uint64_t kCowOpSourceInfoTypeNumBits = 4;\nstatic constexpr uint64_t kCowOpSourceInfoTypeMask = (1ULL << kCowOpSourceInfoTypeNumBits) - 1;\n\nstatic constexpr uint64_t kCowOpSourceInfoCompressionBit = 57;\nstatic constexpr uint64_t kCowOpSourceInfoCompressionNumBits = 3;\nstatic constexpr uint64_t kCowOpSourceInfoCompressionMask =\n        ((1ULL << kCowOpSourceInfoCompressionNumBits) - 1);\n\n// The on disk format of cow (currently ==  CowOperation)\nstruct CowOperationV3 {\n    // If this operation reads from the data section of the COW, this contains\n    // the length.\n    uint32_t data_length;\n\n    // The block of data in the new image that this operation modifies.\n    uint32_t new_block;\n\n    // source_info with have the following layout\n    // |--- 4 bits -- | --------- 3 bits ------ | --- 9 bits --- | --- 48 bits ---|\n    // |--- type ---  | -- compression factor --| --- unused --- | --- source --- |\n    //\n    // The value of |source| depends on the operation code.\n    //\n    // CopyOp: a 32-bit block location in the source image.\n    // ReplaceOp: an absolute byte offset within the COW's data section.\n    // XorOp: an absolute byte offset in the source image.\n    // ZeroOp: unused\n    // LabelOp: a 64-bit opaque identifier.\n    //\n    // For ops other than Label:\n    //  Bits 47-62 are reserved and must be zero.\n    // A block is compressed if it’s data is < block_sz\n    //\n    // Bits [57-59] represents the compression factor.\n    //\n    //       Compression - factor\n    // ==========================\n    // 000 -  4k\n    // 001 -  8k\n    // 010 -  16k\n    // ...\n    // 110 -  256k\n    //\n    uint64_t source_info_;\n    constexpr uint64_t source() const { return source_info_ & kCowOpSourceInfoDataMask; }\n    constexpr void set_source(uint64_t source) {\n        // Clear the first 48 bit first\n        source_info_ &= ~kCowOpSourceInfoDataMask;\n        // Set the actual source field\n        source_info_ |= source & kCowOpSourceInfoDataMask;\n    }\n    constexpr CowOperationType type() const {\n        // this is a mask to grab the first 4 bits of a 64 bit integer\n        const auto type = (source_info_ >> kCowOpSourceInfoTypeBit) & kCowOpSourceInfoTypeMask;\n        return static_cast<CowOperationType>(type);\n    }\n    constexpr void set_type(CowOperationType type) {\n        // Clear the top 4 bits first\n        source_info_ &= ((1ULL << kCowOpSourceInfoTypeBit) - 1);\n        // set the actual type bits\n        source_info_ |= (static_cast<uint64_t>(type) & kCowOpSourceInfoTypeMask)\n                        << kCowOpSourceInfoTypeBit;\n    }\n    constexpr void set_compression_bits(uint8_t compression_factor) {\n        // Clear the 3 bits from bit 57 - [57-59]\n        source_info_ &= ~(kCowOpSourceInfoCompressionMask << kCowOpSourceInfoCompressionBit);\n        // Set the actual compression factor\n        source_info_ |=\n                (static_cast<uint64_t>(compression_factor) & kCowOpSourceInfoCompressionMask)\n                << kCowOpSourceInfoCompressionBit;\n    }\n    constexpr uint8_t compression_bits() const {\n        // Grab the 3 bits from [57-59]\n        const auto compression_factor =\n                (source_info_ >> kCowOpSourceInfoCompressionBit) & kCowOpSourceInfoCompressionMask;\n        return static_cast<uint8_t>(compression_factor);\n    }\n} __attribute__((packed));\n\n// Ensure that getters/setters added to CowOperationV3 does not increases size\n// of CowOperationV3 struct(no virtual method tables added).\nstatic_assert(std::is_trivially_copyable_v<CowOperationV3>);\nstatic_assert(std::is_standard_layout_v<CowOperationV3>);\nstatic_assert(sizeof(CowOperationV2) == sizeof(CowFooterOperation));\n\nenum CowCompressionAlgorithm : uint8_t {\n    kCowCompressNone = 0,\n    kCowCompressGz = 1,\n    kCowCompressBrotli = 2,\n    kCowCompressLz4 = 3,\n    kCowCompressZstd = 4,\n};\nstruct CowCompression {\n    CowCompressionAlgorithm algorithm = kCowCompressNone;\n    int32_t compression_level = 0;\n};\n\nstatic constexpr uint8_t kCowReadAheadNotStarted = 0;\nstatic constexpr uint8_t kCowReadAheadInProgress = 1;\nstatic constexpr uint8_t kCowReadAheadDone = 2;\n\nstatic constexpr off_t GetSequenceOffset(const CowHeaderV3& header) {\n    return header.prefix.header_size + header.buffer_size;\n}\n\nstatic constexpr off_t GetResumeOffset(const CowHeaderV3& header) {\n    return GetSequenceOffset(header) + (header.sequence_data_count * sizeof(uint32_t));\n}\n\nstatic constexpr off_t GetOpOffset(uint32_t op_index, const CowHeaderV3& header) {\n    return GetResumeOffset(header) + (header.resume_point_max * sizeof(ResumePoint)) +\n           (op_index * sizeof(CowOperationV3));\n}\n\nstatic constexpr off_t GetDataOffset(const CowHeaderV3& header) {\n    return GetOpOffset(header.op_count_max, header);\n}\n\nstruct CowFooter {\n    CowFooterOperation op;\n    uint8_t unused[64];\n} __attribute__((packed));\n\nstruct ScratchMetadata {\n    // Block of data in the image that operation modifies\n    // and read-ahead thread stores the modified data\n    // in the scratch space\n    uint64_t new_block;\n    // Offset within the file to read the data\n    uint64_t file_offset;\n} __attribute__((packed));\n\nstruct BufferState {\n    uint8_t read_ahead_state;\n} __attribute__((packed));\n\nconstexpr size_t GetCowOpSize(size_t version) {\n    if (version == 3) {\n        return sizeof(CowOperationV3);\n    } else if (version == 2 || version == 1) {\n        return sizeof(CowOperationV2);\n    } else {\n        return 0;\n    }\n}\n\n// 2MB Scratch space used for read-ahead\nstatic constexpr uint64_t BUFFER_REGION_DEFAULT_SIZE = (1ULL << 21);\n\nstd::ostream& operator<<(std::ostream& os, CowOperationV2 const& arg);\n\nstd::ostream& operator<<(std::ostream& os, CowOperationV3 const& arg);\n\nstd::ostream& operator<<(std::ostream& os, ResumePoint const& arg);\n\nstd::ostream& operator<<(std::ostream& os, CowOperationType cow_type);\n\nint64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_size);\nint64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_size);\n\n// Ops that are internal to the Cow Format and not OTA data\nbool IsMetadataOp(const CowOperation& op);\n// Ops that have dependencies on old blocks, and must take care in their merge order\nbool IsOrderedOp(const CowOperation& op);\n\n// Convert compression name to internal value.\nstd::optional<CowCompressionAlgorithm> CompressionAlgorithmFromString(std::string_view name);\n\n// Return block size used for compression\nsize_t CowOpCompressionSize(const CowOperation* op, size_t block_size);\n\n// Return the relative offset of the I/O block which the CowOperation\n// multi-block compression\nbool GetBlockOffset(const CowOperation* op, uint64_t io_block, size_t block_size, off_t* offset);\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n\n#include <memory>\n#include <optional>\n#include <unordered_map>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <libsnapshot/cow_format.h>\n\nnamespace chromeos_update_engine {\nclass FileDescriptor;\n}  // namespace chromeos_update_engine\n\nnamespace android {\nnamespace snapshot {\n\nclass ICowOpIter;\n\n// Interface for reading from a snapuserd COW.\nclass ICowReader {\n  public:\n    using FileDescriptor = chromeos_update_engine::FileDescriptor;\n\n    virtual ~ICowReader() {}\n\n    // Return the file header.\n    virtual CowHeader& GetHeader() = 0;\n\n    // Return the file footer.\n    virtual bool GetFooter(CowFooter* footer) = 0;\n    virtual bool VerifyMergeOps() = 0;\n\n    // Return the last valid label\n    virtual bool GetLastLabel(uint64_t* label) = 0;\n\n    // Return an iterator for retrieving CowOperation entries.\n    virtual std::unique_ptr<ICowOpIter> GetOpIter(bool merge_progress) = 0;\n\n    // Return an iterator for retrieving CowOperation entries in reverse merge order\n    virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress) = 0;\n\n    // Return an iterator for retrieving CowOperation entries in merge order\n    virtual std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress) = 0;\n\n    // Get decoded bytes from the data section, handling any decompression.\n    //\n    // If ignore_bytes is non-zero, it specifies the initial number of bytes\n    // to skip writing to |buffer|.\n    //\n    // Returns the number of bytes written to |buffer|, or -1 on failure.\n    // errno is NOT set.\n    //\n    // Partial reads are not possible unless |buffer_size| is less than the\n    // operation block size.\n    //\n    // The operation pointer must derive from ICowOpIter::Get().\n    virtual ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,\n                             size_t ignore_bytes = 0) = 0;\n\n    // Get the absolute source offset, in bytes, of a CowOperation. Returns\n    // false if the operation does not read from source partitions.\n    virtual bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) = 0;\n};\n\nstatic constexpr uint64_t GetBlockFromOffset(const CowHeader& header, uint64_t offset) {\n    return offset / header.block_size;\n}\n\nstatic constexpr uint64_t GetBlockRelativeOffset(const CowHeader& header, uint64_t offset) {\n    return offset % header.block_size;\n}\n\n// Iterate over a sequence of COW operations. The iterator is bidirectional.\nclass ICowOpIter {\n  public:\n    virtual ~ICowOpIter() {}\n\n    // Returns true if the iterator is at the end of the operation list.\n    // If true, Get() and Next() must not be called.\n    virtual bool AtEnd() = 0;\n\n    // Read the current operation.\n    virtual const CowOperation* Get() = 0;\n\n    // Advance to the next item.\n    virtual void Next() = 0;\n\n    // Returns true if the iterator is at the beginning of the operation list.\n    // If true, Prev() must not be called; Get() however will be valid if\n    // AtEnd() is not true.\n    virtual bool AtBegin() = 0;\n\n    // Advance to the previous item.\n    virtual void Prev() = 0;\n};\n\nclass CowReader final : public ICowReader {\n  public:\n    enum class ReaderFlags {\n        DEFAULT = 0,\n        USERSPACE_MERGE = 1,\n    };\n\n    CowReader(ReaderFlags reader_flag = ReaderFlags::DEFAULT, bool is_merge = false);\n    ~CowReader() { owned_fd_ = {}; }\n\n    // Parse the COW, optionally, up to the given label. If no label is\n    // specified, the COW must have an intact footer.\n    bool Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label = {});\n    bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});\n\n    bool InitForMerge(android::base::unique_fd&& fd);\n\n    bool VerifyMergeOps() override;\n    bool GetFooter(CowFooter* footer) override;\n    bool GetLastLabel(uint64_t* label) override;\n    bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) override;\n\n    // Create a CowOpIter object which contains footer_.num_ops\n    // CowOperation objects. Get() returns a unique CowOperation object\n    // whose lifetime depends on the CowOpIter object; the return\n    // value of these will never be null.\n    std::unique_ptr<ICowOpIter> GetOpIter(bool merge_progress = false) override;\n    std::unique_ptr<ICowOpIter> GetRevMergeOpIter(bool ignore_progress = false) override;\n    std::unique_ptr<ICowOpIter> GetMergeOpIter(bool ignore_progress = false) override;\n\n    ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size,\n                     size_t ignore_bytes = 0) override;\n\n    CowHeader& GetHeader() override { return header_; }\n    const CowHeaderV3& header_v3() const { return header_; }\n\n    bool GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read);\n    bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);\n\n    // Returns the total number of data ops that should be merged. This is the\n    // count of the merge sequence before removing already-merged operations.\n    // It may be different than the actual data op count, for example, if there\n    // are duplicate ops in the stream.\n    uint64_t get_num_total_data_ops() { return num_total_data_ops_; }\n\n    uint64_t get_num_ordered_ops_to_merge() { return num_ordered_ops_to_merge_; }\n\n    void CloseCowFd() { owned_fd_ = {}; }\n\n    // Creates a clone of the current CowReader without the file handlers\n    std::unique_ptr<CowReader> CloneCowReader();\n\n    // Get the max compression size\n    uint32_t GetMaxCompressionSize();\n\n    void UpdateMergeOpsCompleted(int num_merge_ops) { header_.num_merge_ops += num_merge_ops; }\n\n  private:\n    bool ParseV2(android::base::borrowed_fd fd, std::optional<uint64_t> label);\n    bool PrepMergeOps();\n    // sequence data is stored as an operation with actual data residing in the data offset.\n    bool GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,\n                           std::unordered_map<uint32_t, int>* block_map);\n    // v3 of the cow writes sequence data within its own separate sequence buffer.\n    bool GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,\n                         std::unordered_map<uint32_t, int>* block_map);\n    uint64_t FindNumCopyops();\n    uint8_t GetCompressionType();\n\n    android::base::unique_fd owned_fd_;\n    android::base::borrowed_fd fd_;\n    CowHeaderV3 header_;\n    std::optional<CowFooter> footer_;\n    uint64_t fd_size_;\n    std::optional<uint64_t> last_label_;\n    std::shared_ptr<std::vector<CowOperation>> ops_;\n    uint64_t merge_op_start_{};\n    std::shared_ptr<std::vector<int>> block_pos_index_;\n    uint64_t num_total_data_ops_{};\n    uint64_t num_ordered_ops_to_merge_{};\n    bool has_seq_ops_{};\n    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_;\n    ReaderFlags reader_flag_;\n    bool is_merge_{};\n};\n\n// Though this function takes in a CowHeaderV3, the struct could be populated as a v1/v2 CowHeader.\n// The extra fields will just be filled as 0. V3 header is strictly a superset of v1/v2 header and\n// contains all of the latter's field\nbool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header);\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h",
    "content": "// copyright (c) 2019 the android open source project\n//\n// licensed under the apache license, version 2.0 (the \"license\");\n// you may not use this file except in compliance with the license.\n// you may obtain a copy of the license at\n//\n//      http://www.apache.org/licenses/license-2.0\n//\n// unless required by applicable law or agreed to in writing, software\n// distributed under the license is distributed on an \"as is\" basis,\n// without warranties or conditions of any kind, either express or implied.\n// see the license for the specific language governing permissions and\n// limitations under the license.\n\n#pragma once\n\n#include <libsnapshot/cow_compress.h>\n\n#include <stdint.h>\n\n#include <condition_variable>\n#include <cstdint>\n#include <memory>\n#include <mutex>\n#include <optional>\n#include <queue>\n#include <string>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_reader.h>\n\nnamespace android {\nnamespace snapshot {\nstruct CowSizeInfo {\n    uint64_t cow_size;\n    uint64_t op_count_max;\n};\nstruct CowOptions {\n    uint32_t block_size = 4096;\n    std::string compression;\n\n    // Maximum number of blocks that can be written.\n    std::optional<uint64_t> max_blocks;\n\n    // Number of CowOperations in a cluster. 0 for no clustering. Cannot be 1.\n    uint32_t cluster_ops = 1024;\n\n    bool scratch_space = true;\n\n    // Preset the number of merged ops. Only useful for testing.\n    uint64_t num_merge_ops = 0;\n\n    // Number of threads for compression\n    uint16_t num_compress_threads = 0;\n\n    // Batch write cluster ops\n    bool batch_write = false;\n\n    // Size of the cow operation buffer; used in v3 only.\n    uint64_t op_count_max = 0;\n\n    // Compression factor\n    uint64_t compression_factor = 4096;\n};\n\n// Interface for writing to a snapuserd COW. All operations are ordered; merges\n// will occur in the sequence they were added to the COW.\nclass ICowWriter {\n  public:\n    using FileDescriptor = chromeos_update_engine::FileDescriptor;\n\n    virtual ~ICowWriter() {}\n\n    // Encode an operation that copies the contents of |old_block| to the\n    // location of |new_block|. 'num_blocks' is the number of contiguous\n    // COPY operations from |old_block| to |new_block|.\n    virtual bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;\n\n    // Encode a sequence of raw blocks. |size| must be a multiple of the block size.\n    virtual bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;\n\n    // Add a sequence of xor'd blocks. |size| must be a multiple of the block size.\n    virtual bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,\n                              uint32_t old_block, uint16_t offset) = 0;\n\n    // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.\n    virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;\n\n    // Add a label to the op sequence.\n    virtual bool AddLabel(uint64_t label) = 0;\n\n    // Add sequence data for op merging. Data is a list of the destination block numbers.\n    virtual bool AddSequenceData(size_t num_ops, const uint32_t* data) = 0;\n\n    // Flush all pending writes. This must be called before closing the writer\n    // to ensure that the correct headers and footers are written.\n    virtual bool Finalize() = 0;\n\n    // Return number of bytes the cow image occupies on disk + the size of sequence && ops buffer\n    // The latter two fields are used in v3 cow format and left as 0 for v2 cow format\n    virtual CowSizeInfo GetCowSizeInfo() const = 0;\n\n    virtual uint32_t GetBlockSize() const = 0;\n    virtual std::optional<uint32_t> GetMaxBlocks() const = 0;\n\n    // Open an ICowReader for this writer. The reader will be a snapshot of the\n    // current operations in the writer; new writes after OpenReader() will not\n    // be reflected.\n    virtual std::unique_ptr<ICowReader> OpenReader() = 0;\n\n    // Open a file descriptor. This allows reading and seeing through the cow\n    // as if it were a normal file. The optional source_device must be a valid\n    // path if the CowReader contains any copy or xor operations.\n    virtual std::unique_ptr<FileDescriptor> OpenFileDescriptor(\n            const std::optional<std::string>& source_device) = 0;\n};\n\nclass CompressWorker {\n  public:\n    CompressWorker(std::unique_ptr<ICompressor>&& compressor);\n    bool RunThread();\n    void EnqueueCompressBlocks(const void* buffer, size_t block_size, size_t num_blocks);\n    bool GetCompressedBuffers(std::vector<std::vector<uint8_t>>* compressed_buf);\n    void Finalize();\n    static uint32_t GetDefaultCompressionLevel(CowCompressionAlgorithm compression);\n\n    static bool CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,\n                               size_t num_blocks,\n                               std::vector<std::vector<uint8_t>>* compressed_data);\n\n  private:\n    struct CompressWork {\n        const void* buffer;\n        size_t num_blocks;\n        size_t block_size;\n        bool compression_status = false;\n        std::vector<std::vector<uint8_t>> compressed_data;\n    };\n\n    std::unique_ptr<ICompressor> compressor_;\n\n    std::queue<CompressWork> work_queue_;\n    std::queue<CompressWork> compressed_queue_;\n    std::mutex lock_;\n    std::condition_variable cv_;\n    bool stopped_ = false;\n    size_t total_submitted_ = 0;\n    size_t total_processed_ = 0;\n\n    bool CompressBlocks(const void* buffer, size_t num_blocks, size_t block_size,\n                        std::vector<std::vector<uint8_t>>* compressed_data);\n};\n\n// Create an ICowWriter not backed by any file. This is useful for estimating\n// the final size of a cow file.\nstd::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options);\n\n// Create an ICowWriter of the given version and options. If a label is given,\n// the writer is opened in append mode.\nstd::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,\n                                            android::base::unique_fd&& fd,\n                                            std::optional<uint64_t> label = {});\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/mock_cow_writer.h",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <gmock/gmock.h>\n#include <libsnapshot/cow_writer.h>\n\nnamespace android::snapshot {\n\nclass MockCowWriter : public ICowWriter {\n  public:\n    using FileDescriptor = chromeos_update_engine::FileDescriptor;\n\n    MOCK_METHOD(bool, Finalize, (), (override));\n    MOCK_METHOD(CowSizeInfo, GetCowSizeInfo, (), (const, override));\n\n    MOCK_METHOD(bool, AddCopy, (uint64_t, uint64_t, uint64_t), (override));\n    MOCK_METHOD(bool, AddRawBlocks, (uint64_t, const void*, size_t), (override));\n    MOCK_METHOD(bool, AddXorBlocks, (uint32_t, const void*, size_t, uint32_t, uint16_t),\n                (override));\n    MOCK_METHOD(bool, AddZeroBlocks, (uint64_t, uint64_t), (override));\n    MOCK_METHOD(bool, AddLabel, (uint64_t), (override));\n    MOCK_METHOD(bool, AddSequenceData, (size_t, const uint32_t*), (override));\n    MOCK_METHOD(uint32_t, GetBlockSize, (), (override, const));\n    MOCK_METHOD(std::optional<uint32_t>, GetMaxBlocks, (), (override, const));\n\n    MOCK_METHOD(std::unique_ptr<ICowReader>, OpenReader, (), (override));\n    MOCK_METHOD(std::unique_ptr<FileDescriptor>, OpenFileDescriptor,\n                (const std::optional<std::string>&), (override));\n};\n\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <libsnapshot/snapshot.h>\n\n#include <gmock/gmock.h>\n\nnamespace android::snapshot {\n\nclass MockDeviceInfo : public SnapshotManager::IDeviceInfo {\n  public:\n    MOCK_METHOD(std::string, GetMetadataDir, (), (const, override));\n    MOCK_METHOD(std::string, GetSlotSuffix, (), (const, override));\n    MOCK_METHOD(std::string, GetOtherSlotSuffix, (), (const, override));\n    MOCK_METHOD(std::string, GetSuperDevice, (uint32_t slot), (const, override));\n    MOCK_METHOD(const android::fs_mgr::IPartitionOpener&, GetPartitionOpener, (), (const));\n    MOCK_METHOD(bool, IsOverlayfsSetup, (), (const, override));\n    MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));\n    MOCK_METHOD(bool, SetActiveBootSlot, (unsigned int slot), (override));\n    MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));\n    MOCK_METHOD(bool, IsRecovery, (), (const, override));\n    MOCK_METHOD(bool, IsFirstStageInit, (), (const, override));\n    MOCK_METHOD(std::unique_ptr<android::fiemap::IImageManager>, OpenImageManager, (),\n                (const, override));\n};\n\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <libsnapshot/snapshot.h>\n#include <payload_consumer/file_descriptor.h>\n\n#include <gmock/gmock.h>\n\nnamespace android::snapshot {\n\nclass MockSnapshotManager : public ISnapshotManager {\n  public:\n    MOCK_METHOD(bool, BeginUpdate, (), (override));\n    MOCK_METHOD(bool, CancelUpdate, (), (override));\n    MOCK_METHOD(bool, FinishedSnapshotWrites, (bool wipe), (override));\n    MOCK_METHOD(void, UpdateCowStats, (ISnapshotMergeStats * stats), (override));\n    MOCK_METHOD(MergeFailureCode, ReadMergeFailureCode, (), (override));\n    MOCK_METHOD(bool, InitiateMerge, (), (override));\n\n    MOCK_METHOD(UpdateState, ProcessUpdateState,\n                (const std::function<bool()>& callback, const std::function<bool()>& before_cancel),\n                (override));\n    MOCK_METHOD(UpdateState, GetUpdateState, (double* progress), (override));\n    MOCK_METHOD(bool, UpdateUsesCompression, (), (override));\n    MOCK_METHOD(bool, UpdateUsesUserSnapshots, (), (override));\n    MOCK_METHOD(Return, CreateUpdateSnapshots,\n                (const chromeos_update_engine::DeltaArchiveManifest& manifest), (override));\n    MOCK_METHOD(bool, MapUpdateSnapshot,\n                (const android::fs_mgr::CreateLogicalPartitionParams& params,\n                 std::string* snapshot_path),\n                (override));\n    MOCK_METHOD(std::unique_ptr<ICowWriter>, OpenSnapshotWriter,\n                (const android::fs_mgr::CreateLogicalPartitionParams& params,\n                 std::optional<uint64_t>),\n                (override));\n    MOCK_METHOD(bool, UnmapUpdateSnapshot, (const std::string& target_partition_name), (override));\n    MOCK_METHOD(bool, NeedSnapshotsInFirstStageMount, (), (override));\n    MOCK_METHOD(bool, CreateLogicalAndSnapshotPartitions,\n                (const std::string& super_device, const std::chrono::milliseconds& timeout_ms),\n                (override));\n    MOCK_METHOD(bool, MapAllSnapshots, (const std::chrono::milliseconds& timeout_ms), (override));\n    MOCK_METHOD(bool, UnmapAllSnapshots, (), (override));\n    MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));\n    MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));\n    MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));\n    MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices,\n                (const std::unique_ptr<AutoDevice>& metadata_device), (override));\n    MOCK_METHOD(bool, Dump, (std::ostream & os), (override));\n    MOCK_METHOD(std::unique_ptr<AutoDevice>, EnsureMetadataMounted, (), (override));\n    MOCK_METHOD(ISnapshotMergeStats*, GetSnapshotMergeStatsInstance, (), (override));\n    MOCK_METHOD(std::string, ReadSourceBuildFingerprint, (), (override));\n    MOCK_METHOD(void, SetMergeStatsFeatures, (ISnapshotMergeStats*), (override));\n    MOCK_METHOD(bool, IsCancelUpdateSafe, (), (override));\n};\n\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot_merge_stats.h",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <memory>\n\n#include <gmock/gmock.h>\n#include <libsnapshot/snapshot_stats.h>\n\nnamespace android::snapshot {\n\nclass MockSnapshotMergeStats final : public ISnapshotMergeStats {\n  public:\n    virtual ~MockSnapshotMergeStats() = default;\n    // Called when merge starts or resumes.\n    MOCK_METHOD(bool, Start, (), (override));\n    MOCK_METHOD(void, set_state, (android::snapshot::UpdateState), (override));\n    MOCK_METHOD(void, set_boot_complete_time_ms, (uint32_t), (override));\n    MOCK_METHOD(void, set_boot_complete_to_merge_start_time_ms, (uint32_t), (override));\n    MOCK_METHOD(void, set_merge_failure_code, (MergeFailureCode), (override));\n    MOCK_METHOD(void, set_source_build_fingerprint, (const std::string&), (override));\n    MOCK_METHOD(uint64_t, cow_file_size, (), (override));\n    MOCK_METHOD(uint64_t, total_cow_size_bytes, (), (override));\n    MOCK_METHOD(uint64_t, estimated_cow_size_bytes, (), (override));\n    MOCK_METHOD(uint32_t, boot_complete_time_ms, (), (override));\n    MOCK_METHOD(uint32_t, boot_complete_to_merge_start_time_ms, (), (override));\n    MOCK_METHOD(std::string, source_build_fingerprint, (), (override));\n    MOCK_METHOD(MergeFailureCode, merge_failure_code, (), (override));\n    MOCK_METHOD(std::unique_ptr<Result>, Finish, (), (override));\n    MOCK_METHOD(bool, WriteState, (), (override));\n    MOCK_METHOD(SnapshotMergeReport*, report, (), (override));\n\n    using ISnapshotMergeStats::Result;\n    // Return nullptr if any failure.\n};\n\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/return.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n#include <string.h>\n\n#include <libfiemap/fiemap_status.h>\n\nnamespace android::snapshot {\n\n// SnapshotManager functions return either bool or Return objects. \"Return\" types provides\n// more information about the reason of the failure.\nclass Return {\n    using FiemapStatus = android::fiemap::FiemapStatus;\n\n  public:\n    enum class ErrorCode : int32_t {\n        SUCCESS = static_cast<int32_t>(FiemapStatus::ErrorCode::SUCCESS),\n        ERROR = static_cast<int32_t>(FiemapStatus::ErrorCode::ERROR),\n        NO_SPACE = static_cast<int32_t>(FiemapStatus::ErrorCode::NO_SPACE),\n    };\n    ErrorCode error_code() const { return error_code_; }\n    bool is_ok() const { return error_code() == ErrorCode::SUCCESS; }\n    operator bool() const { return is_ok(); }\n    // Total required size on /userdata.\n    uint64_t required_size() const { return required_size_; }\n    std::string string() const;\n\n    static Return Ok() { return Return(ErrorCode::SUCCESS); }\n    static Return Error() { return Return(ErrorCode::ERROR); }\n    static Return NoSpace(uint64_t size) { return Return(ErrorCode::NO_SPACE, size); }\n    // Does not set required_size_ properly even when status.error_code() == NO_SPACE.\n    explicit Return(const FiemapStatus& status)\n        : error_code_(FromFiemapStatusErrorCode(status.error_code())), required_size_(0) {}\n\n  private:\n    ErrorCode error_code_;\n    uint64_t required_size_;\n    Return(ErrorCode error_code, uint64_t required_size = 0)\n        : error_code_(error_code), required_size_(required_size) {}\n\n    // FiemapStatus::ErrorCode -> ErrorCode\n    static ErrorCode FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code);\n};\n\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/snapshot.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <map>\n#include <memory>\n#include <optional>\n#include <ostream>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <android/snapshot/snapshot.pb.h>\n#include <fs_mgr_dm_linear.h>\n#include <libdm/dm.h>\n#include <libfiemap/image_manager.h>\n#include <liblp/builder.h>\n#include <liblp/liblp.h>\n#include <libsnapshot/auto_device.h>\n#include <libsnapshot/cow_writer.h>\n#include <libsnapshot/return.h>\n#include <snapuserd/snapuserd_client.h>\n#include <update_engine/update_metadata.pb.h>\n\n#ifndef FRIEND_TEST\n#define FRIEND_TEST(test_set_name, individual_test) \\\n    friend class test_set_name##_##individual_test##_Test\n#define DEFINED_FRIEND_TEST\n#endif\n\nnamespace aidl::android::hardware::boot {\nenum class MergeStatus;\n}\n\nnamespace android {\n\nnamespace fiemap {\nclass IImageManager;\n}  // namespace fiemap\n\nnamespace fs_mgr {\nstruct CreateLogicalPartitionParams;\nclass IPartitionOpener;\n}  // namespace fs_mgr\n\n// Forward declare IBootControl types since we cannot include only the headers\n// with Soong. Note: keep the enum width in sync.\n\nnamespace snapshot {\n\nstruct AutoDeleteCowImage;\nstruct AutoDeleteSnapshot;\nstruct AutoDeviceList;\nstruct PartitionCowCreator;\nclass ISnapshotMergeStats;\nclass SnapshotMergeStats;\nclass SnapshotStatus;\n\nusing std::chrono::duration_cast;\nusing namespace std::chrono_literals;\n\nstatic constexpr const std::string_view kCowGroupName = \"cow\";\nstatic constexpr char kVirtualAbCompressionProp[] = \"ro.virtual_ab.compression.enabled\";\n\nbool OptimizeSourceCopyOperation(const chromeos_update_engine::InstallOperation& operation,\n                                 chromeos_update_engine::InstallOperation* optimized);\n\nenum class CreateResult : unsigned int {\n    ERROR,\n    CREATED,\n    NOT_CREATED,\n};\n\nenum class CancelResult : unsigned int {\n    OK,\n    ERROR,\n    LIVE_SNAPSHOTS,\n    NEEDS_MERGE,\n};\n\nclass ISnapshotManager {\n  public:\n    // Dependency injection for testing.\n    class IDeviceInfo {\n      public:\n        using IImageManager = android::fiemap::IImageManager;\n        using MergeStatus = aidl::android::hardware::boot::MergeStatus;\n\n        virtual ~IDeviceInfo() {}\n        virtual std::string GetMetadataDir() const = 0;\n        virtual std::string GetSlotSuffix() const = 0;\n        virtual std::string GetOtherSlotSuffix() const = 0;\n        virtual std::string GetSuperDevice(uint32_t slot) const = 0;\n        virtual const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const = 0;\n        virtual bool IsOverlayfsSetup() const = 0;\n        virtual bool SetBootControlMergeStatus(MergeStatus status) = 0;\n        virtual bool SetActiveBootSlot(unsigned int slot) = 0;\n        virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;\n        virtual bool IsRecovery() const = 0;\n        virtual bool IsTestDevice() const { return false; }\n        virtual bool IsFirstStageInit() const = 0;\n        virtual std::unique_ptr<IImageManager> OpenImageManager() const = 0;\n        virtual android::dm::IDeviceMapper& GetDeviceMapper() = 0;\n        virtual bool IsTempMetadata() const = 0;\n\n        // Helper method for implementing OpenImageManager.\n        std::unique_ptr<IImageManager> OpenImageManager(const std::string& gsid_dir) const;\n    };\n    virtual ~ISnapshotManager() = default;\n\n    // Begin an update. This must be called before creating any snapshots. It\n    // will fail if GetUpdateState() != None.\n    virtual bool BeginUpdate() = 0;\n\n    // Cancel an update; any snapshots will be deleted. This is allowed if the\n    // state == Initiated, None, or Unverified (before rebooting to the new\n    // slot).\n    //\n    // In recovery, it will cancel an update even if a merge is in progress.\n    // Thus, it should only be called if a new OTA will be sideloaded. The\n    // safety can be checked via IsCancelUpdateSafe().\n    virtual bool CancelUpdate() = 0;\n\n    // Mark snapshot writes as having completed. After this, new snapshots cannot\n    // be created, and the device must either cancel the OTA (either before\n    // rebooting or after rolling back), or merge the OTA.\n    // Before calling this function, all snapshots must be mapped.\n    // If |wipe| is set to true, wipe is scheduled after reboot, and snapshots\n    // may need to be merged before wiping.\n    virtual bool FinishedSnapshotWrites(bool wipe) = 0;\n\n    // Set feature flags on an ISnapshotMergeStats object.\n    virtual void SetMergeStatsFeatures(ISnapshotMergeStats* stats) = 0;\n\n    // Update an ISnapshotMergeStats object with statistics about COW usage.\n    // This should be called before the merge begins as otherwise snapshots\n    // may be deleted.\n    virtual void UpdateCowStats(ISnapshotMergeStats* stats) = 0;\n\n    // Initiate a merge on all snapshot devices. This should only be used after an\n    // update has been marked successful after booting.\n    virtual bool InitiateMerge() = 0;\n\n    // Perform any necessary post-boot actions. This should be run soon after\n    // /data is mounted.\n    //\n    // If a merge is in progress, this function will block until the merge is\n    // completed.\n    //    - Callback is called periodically during the merge. If callback()\n    //      returns false during the merge, ProcessUpdateState() will pause\n    //      and returns Merging.\n    // If a merge or update was cancelled, this will clean up any\n    // update artifacts and return.\n    //\n    // Note that after calling this, GetUpdateState() may still return that a\n    // merge is in progress:\n    //   MergeFailed indicates that a fatal error occurred. WaitForMerge() may\n    //   called any number of times again to attempt to make more progress, but\n    //   we do not expect it to succeed if a catastrophic error occurred.\n    //\n    //   MergeNeedsReboot indicates that the merge has completed, but cleanup\n    //   failed. This can happen if for some reason resources were not closed\n    //   properly. In this case another reboot is needed before we can take\n    //   another OTA. However, WaitForMerge() can be called again without\n    //   rebooting, to attempt to finish cleanup anyway.\n    //\n    //   MergeCompleted indicates that the update has fully completed.\n    //   GetUpdateState will return None, and a new update can begin.\n    //\n    // The optional callback allows the caller to periodically check the\n    // progress with GetUpdateState().\n    virtual UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},\n                                           const std::function<bool()>& before_cancel = {}) = 0;\n\n    // If ProcessUpdateState() returned MergeFailed, this returns the appropriate\n    // code. Otherwise, MergeFailureCode::Ok is returned.\n    virtual MergeFailureCode ReadMergeFailureCode() = 0;\n\n    // If an update is in progress, return the source build fingerprint.\n    virtual std::string ReadSourceBuildFingerprint() = 0;\n\n    // Find the status of the current update, if any.\n    //\n    // |progress| depends on the returned status:\n    //   Merging: Value in the range [0, 100]\n    //   MergeCompleted: 100\n    //   Other: 0\n    virtual UpdateState GetUpdateState(double* progress = nullptr) = 0;\n\n    // Returns true if compression is enabled for the current update. This always returns false if\n    // UpdateState is None, or no snapshots have been created.\n    virtual bool UpdateUsesCompression() = 0;\n\n    // Returns true if userspace snapshots is enabled for the current update.\n    virtual bool UpdateUsesUserSnapshots() = 0;\n\n    // Create necessary COW device / files for OTA clients. New logical partitions will be added to\n    // group \"cow\" in target_metadata. Regions of partitions of current_metadata will be\n    // \"write-protected\" and snapshotted.\n    virtual Return CreateUpdateSnapshots(\n            const chromeos_update_engine::DeltaArchiveManifest& manifest) = 0;\n\n    // Map a snapshotted partition for OTA clients to write to. Write-protected regions are\n    // determined previously in CreateSnapshots.\n    //\n    // |snapshot_path| must not be nullptr.\n    //\n    // This method will return false if ro.virtual_ab.compression.enabled is true.\n    virtual bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,\n                                   std::string* snapshot_path) = 0;\n\n    // Create an ICowWriter to build a snapshot against a target partition. The partition name\n    // must be suffixed. If a source partition exists, it must be specified as well. The source\n    // partition will only be used if raw bytes are needed. The source partition should be an\n    // absolute path to the device, not a partition name.\n    virtual std::unique_ptr<ICowWriter> OpenSnapshotWriter(\n            const android::fs_mgr::CreateLogicalPartitionParams& params,\n            std::optional<uint64_t> label = {}) = 0;\n\n    // Unmap a snapshot device or CowWriter that was previously opened with MapUpdateSnapshot,\n    // OpenSnapshotWriter. All outstanding open descriptors, writers, or\n    // readers must be deleted before this is called.\n    virtual bool UnmapUpdateSnapshot(const std::string& target_partition_name) = 0;\n\n    // If this returns true, first-stage mount must call\n    // CreateLogicalAndSnapshotPartitions rather than CreateLogicalPartitions.\n    virtual bool NeedSnapshotsInFirstStageMount() = 0;\n\n    // Perform first-stage mapping of snapshot targets. This replaces init's\n    // call to CreateLogicalPartitions when snapshots are present.\n    virtual bool CreateLogicalAndSnapshotPartitions(\n            const std::string& super_device, const std::chrono::milliseconds& timeout_ms = {}) = 0;\n\n    // Map all snapshots. This is analogous to CreateLogicalAndSnapshotPartitions, except it maps\n    // the target slot rather than the current slot. It should only be used immediately after\n    // applying an update, before rebooting to the new slot.\n    virtual bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) = 0;\n\n    // Unmap all snapshots. This should be called to undo MapAllSnapshots().\n    virtual bool UnmapAllSnapshots() = 0;\n\n    // This method should be called preceding any wipe or flash of metadata or\n    // userdata. It is only valid in recovery or fastbootd, and it ensures that\n    // a merge has been completed.\n    //\n    // When userdata will be wiped or flashed, it is necessary to clean up any\n    // snapshot state. If a merge is in progress, the merge must be finished.\n    // If a snapshot is present but not yet merged, the slot must be marked as\n    // unbootable.\n    //\n    // Returns true on success (or nothing to do), false on failure. The\n    // optional callback fires periodically to query progress via GetUpdateState.\n    virtual bool HandleImminentDataWipe(const std::function<void()>& callback = {}) = 0;\n\n    // Force a merge to complete in recovery. This is similar to HandleImminentDataWipe\n    // but does not expect a data wipe after.\n    virtual bool FinishMergeInRecovery() = 0;\n\n    // This method is only allowed in recovery and is used as a helper to\n    // initialize the snapshot devices as a requirement to mount a snapshotted\n    // /system in recovery.\n    // This function returns:\n    // - CreateResult::CREATED if snapshot devices were successfully created;\n    // - CreateResult::NOT_CREATED if it was not necessary to create snapshot\n    // devices;\n    // - CreateResult::ERROR if a fatal error occurred, mounting /system should\n    // be aborted.\n    // This function mounts /metadata when called, and unmounts /metadata upon\n    // return.\n    virtual CreateResult RecoveryCreateSnapshotDevices() = 0;\n\n    // Same as RecoveryCreateSnapshotDevices(), but does not auto mount/umount\n    // /metadata.\n    virtual CreateResult RecoveryCreateSnapshotDevices(\n            const std::unique_ptr<AutoDevice>& metadata_device) = 0;\n\n    // Dump debug information.\n    virtual bool Dump(std::ostream& os) = 0;\n\n    // Ensure metadata directory is mounted in recovery. When the returned\n    // AutoDevice is destroyed, the metadata directory is automatically\n    // unmounted.\n    // Return nullptr if any failure.\n    // In Android mode, Return an AutoDevice that does nothing\n    // In recovery, return an AutoDevice that does nothing if metadata entry\n    // is not found in fstab.\n    // Note: if this function is called the second time before the AutoDevice returned from the\n    // first call is destroyed, the device will be unmounted when any of these AutoDevices is\n    // destroyed. For example:\n    //   auto a = mgr->EnsureMetadataMounted(); // mounts\n    //   auto b = mgr->EnsureMetadataMounted(); // does nothing\n    //   b.reset() // unmounts\n    //   a.reset() // does nothing\n    virtual std::unique_ptr<AutoDevice> EnsureMetadataMounted() = 0;\n\n    // Return the associated ISnapshotMergeStats instance. Never null.\n    virtual ISnapshotMergeStats* GetSnapshotMergeStatsInstance() = 0;\n\n    // Return whether cancelling an update is safe. This is for use in recovery.\n    virtual bool IsCancelUpdateSafe() = 0;\n};\n\nclass SnapshotManager final : public ISnapshotManager {\n    using CreateLogicalPartitionParams = android::fs_mgr::CreateLogicalPartitionParams;\n    using IPartitionOpener = android::fs_mgr::IPartitionOpener;\n    using LpMetadata = android::fs_mgr::LpMetadata;\n    using MetadataBuilder = android::fs_mgr::MetadataBuilder;\n    using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;\n    using MergeStatus = aidl::android::hardware::boot::MergeStatus;\n    using FiemapStatus = android::fiemap::FiemapStatus;\n\n    friend class SnapshotMergeStats;\n\n  public:\n    ~SnapshotManager();\n\n    // Return a new SnapshotManager instance, or null on error. The device\n    // pointer is owned for the lifetime of SnapshotManager. If null, a default\n    // instance will be created.\n    static std::unique_ptr<SnapshotManager> New(IDeviceInfo* device = nullptr);\n\n    // This is similar to New(), except designed specifically for first-stage\n    // init or recovery.\n    static std::unique_ptr<SnapshotManager> NewForFirstStageMount(IDeviceInfo* device = nullptr);\n\n    // Helper function for first-stage init to check whether a SnapshotManager\n    // might be needed to perform first-stage mounts.\n    static bool IsSnapshotManagerNeeded();\n\n    // Map the temp OTA metadata partition from super\n    static bool MapTempOtaMetadataPartitionIfNeeded(\n            const std::function<bool(const std::string&)>& init);\n\n    // Helper function for second stage init to restorecon on the rollback indicator.\n    static std::string GetGlobalRollbackIndicatorPath();\n\n    // Populate |snapuserd_argv| with the necessary arguments to restart snapuserd\n    // after loading selinux policy.\n    bool PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv);\n\n    // If snapuserd from first stage init was started from system partition.\n    bool MarkSnapuserdFromSystem();\n\n    // Detach dm-user devices from the first stage snapuserd. Load\n    // new dm-user tables after loading selinux policy.\n    bool DetachFirstStageSnapuserdForSelinux();\n\n    // Perform the transition from the selinux stage of snapuserd into the\n    // second-stage of snapuserd. This process involves re-creating the dm-user\n    // table entries for each device, so that they connect to the new daemon.\n    // Once all new tables have been activated, we ask the first-stage daemon\n    // to cleanly exit.\n    bool PerformSecondStageInitTransition();\n\n    // ISnapshotManager overrides.\n    bool BeginUpdate() override;\n    bool CancelUpdate() override;\n    bool FinishedSnapshotWrites(bool wipe) override;\n    void UpdateCowStats(ISnapshotMergeStats* stats) override;\n    MergeFailureCode ReadMergeFailureCode() override;\n    bool InitiateMerge() override;\n    UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},\n                                   const std::function<bool()>& before_cancel = {}) override;\n    UpdateState GetUpdateState(double* progress = nullptr) override;\n    bool UpdateUsesCompression() override;\n    bool UpdateUsesUserSnapshots() override;\n    Return CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) override;\n    bool MapUpdateSnapshot(const CreateLogicalPartitionParams& params,\n                           std::string* snapshot_path) override;\n    std::unique_ptr<ICowWriter> OpenSnapshotWriter(\n            const android::fs_mgr::CreateLogicalPartitionParams& params,\n            std::optional<uint64_t> label) override;\n    bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;\n    bool NeedSnapshotsInFirstStageMount() override;\n    bool CreateLogicalAndSnapshotPartitions(\n            const std::string& super_device,\n            const std::chrono::milliseconds& timeout_ms = {}) override;\n    bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;\n    bool FinishMergeInRecovery() override;\n    CreateResult RecoveryCreateSnapshotDevices() override;\n    CreateResult RecoveryCreateSnapshotDevices(\n            const std::unique_ptr<AutoDevice>& metadata_device) override;\n    bool Dump(std::ostream& os) override;\n    std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;\n    ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;\n    bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;\n    bool UnmapAllSnapshots() override;\n    std::string ReadSourceBuildFingerprint() override;\n    void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;\n    bool IsCancelUpdateSafe() override;\n\n    // We can't use WaitForFile during first-stage init, because ueventd is not\n    // running and therefore will not automatically create symlinks. Instead,\n    // we let init provide us with the correct function to use to ensure\n    // uevents have been processed and symlink/mknod calls completed.\n    void SetUeventRegenCallback(std::function<bool(const std::string&)> callback) {\n        uevent_regen_callback_ = callback;\n    }\n\n    // If true, compression is enabled for this update. This is used by\n    // first-stage to decide whether to launch snapuserd.\n    bool IsSnapuserdRequired();\n\n    // This is primarily invoked during device reboot after an OTA update.\n    //\n    // a: Check if the partitions are mounted off snapshots.\n    //\n    // b: Store all dynamic partitions which are mounted off snapshots. This\n    // is used to unmount the partition.\n    bool IsUserspaceSnapshotUpdateInProgress(std::vector<std::string>& dynamic_partitions);\n\n    // Pause the snapshot merge.\n    bool PauseSnapshotMerge();\n\n    // Resume the snapshot merge.\n    bool ResumeSnapshotMerge();\n\n    enum class SnapshotDriver {\n        DM_SNAPSHOT,\n        DM_USER,\n    };\n\n    // Add new public entries above this line.\n\n  private:\n    FRIEND_TEST(SnapshotTest, CleanFirstStageMount);\n    FRIEND_TEST(SnapshotTest, CreateSnapshot);\n    FRIEND_TEST(SnapshotTest, FirstStageMountAfterRollback);\n    FRIEND_TEST(SnapshotTest, FirstStageMountAndMerge);\n    FRIEND_TEST(SnapshotTest, FlagCheck);\n    FRIEND_TEST(SnapshotTest, FlashSuperDuringMerge);\n    FRIEND_TEST(SnapshotTest, FlashSuperDuringUpdate);\n    FRIEND_TEST(SnapshotTest, MapPartialSnapshot);\n    FRIEND_TEST(SnapshotTest, MapSnapshot);\n    FRIEND_TEST(SnapshotTest, Merge);\n    FRIEND_TEST(SnapshotTest, MergeFailureCode);\n    FRIEND_TEST(SnapshotTest, NoMergeBeforeReboot);\n    FRIEND_TEST(SnapshotTest, UpdateBootControlHal);\n    FRIEND_TEST(SnapshotTest, BootSnapshotWithoutSlotSwitch);\n    FRIEND_TEST(SnapshotUpdateTest, AddPartition);\n    FRIEND_TEST(SnapshotUpdateTest, ConsistencyCheckResume);\n    FRIEND_TEST(SnapshotUpdateTest, DaemonTransition);\n    FRIEND_TEST(SnapshotUpdateTest, DataWipeAfterRollback);\n    FRIEND_TEST(SnapshotUpdateTest, DataWipeRollbackInRecovery);\n    FRIEND_TEST(SnapshotUpdateTest, DataWipeWithStaleSnapshots);\n    FRIEND_TEST(SnapshotUpdateTest, FlagCheck);\n    FRIEND_TEST(SnapshotUpdateTest, FullUpdateFlow);\n    FRIEND_TEST(SnapshotUpdateTest, MergeCannotRemoveCow);\n    FRIEND_TEST(SnapshotUpdateTest, MergeInRecovery);\n    FRIEND_TEST(SnapshotUpdateTest, QueryStatusError);\n    FRIEND_TEST(SnapshotUpdateTest, SnapshotStatusFileWithoutCow);\n    FRIEND_TEST(SnapshotUpdateTest, SpaceSwapUpdate);\n    FRIEND_TEST(SnapshotUpdateTest, InterruptMergeDuringPhaseUpdate);\n    FRIEND_TEST(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch);\n    FRIEND_TEST(SnapshotUpdateTest, CancelInRecovery);\n    friend class SnapshotTest;\n    friend class SnapshotUpdateTest;\n    friend class FlashAfterUpdateTest;\n    friend class LockTestConsumer;\n    friend class SnapshotFuzzEnv;\n    friend class MapSnapshots;\n    friend struct AutoDeleteCowImage;\n    friend struct AutoDeleteSnapshot;\n    friend struct PartitionCowCreator;\n\n    using DmTargetSnapshot = android::dm::DmTargetSnapshot;\n    using IImageManager = android::fiemap::IImageManager;\n    using TargetInfo = android::dm::DeviceMapper::TargetInfo;\n\n    explicit SnapshotManager(IDeviceInfo* info);\n\n    // This is created lazily since it can connect via binder.\n    bool EnsureImageManager();\n\n    // Ensure we're connected to snapuserd.\n    bool EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms = 10s);\n\n    // Helpers for first-stage init.\n    const std::unique_ptr<IDeviceInfo>& device() const { return device_; }\n\n    // Helper functions for tests.\n    IImageManager* image_manager() const { return images_.get(); }\n    void set_use_first_stage_snapuserd(bool value) { use_first_stage_snapuserd_ = value; }\n\n    // Since libsnapshot is included into multiple processes, we flock() our\n    // files for simple synchronization. LockedFile is a helper to assist with\n    // this. It also serves as a proof-of-lock for some functions.\n    class LockedFile final {\n      public:\n        LockedFile(const std::string& path, android::base::unique_fd&& fd, int lock_mode)\n            : path_(path), fd_(std::move(fd)), lock_mode_(lock_mode) {}\n        ~LockedFile();\n        int lock_mode() const { return lock_mode_; }\n\n      private:\n        std::string path_;\n        android::base::unique_fd fd_;\n        int lock_mode_;\n    };\n    static std::unique_ptr<LockedFile> OpenFile(const std::string& file, int lock_flags);\n\n    SnapshotDriver GetSnapshotDriver(LockedFile* lock);\n\n    // Create a new snapshot record. This creates the backing COW store and\n    // persists information needed to map the device. The device can be mapped\n    // with MapSnapshot().\n    //\n    // |status|.device_size should be the size of the base_device that will be passed\n    // via MapDevice(). |status|.snapshot_size should be the number of bytes in the\n    // base device, starting from 0, that will be snapshotted. |status|.cow_file_size\n    // should be the amount of space that will be allocated to store snapshot\n    // deltas.\n    //\n    // If |status|.snapshot_size < |status|.device_size, then the device will always\n    // be mapped with two table entries: a dm-snapshot range covering\n    // snapshot_size, and a dm-linear range covering the remainder.\n    //\n    // All sizes are specified in bytes, and the device, snapshot, COW partition and COW file sizes\n    // must be a multiple of the sector size (512 bytes).\n    bool CreateSnapshot(LockedFile* lock, PartitionCowCreator* cow_creator, SnapshotStatus* status);\n\n    // |name| should be the base partition name (e.g. \"system_a\"). Create the\n    // backing COW image using the size previously passed to CreateSnapshot().\n    Return CreateCowImage(LockedFile* lock, const std::string& name);\n\n    // Map a snapshot device that was previously created with CreateSnapshot.\n    // If a merge was previously initiated, the device-mapper table will have a\n    // snapshot-merge target instead of a snapshot target. If the timeout\n    // parameter greater than zero, this function will wait the given amount\n    // of time for |dev_path| to become available, and fail otherwise. If\n    // timeout_ms is 0, then no wait will occur and |dev_path| may not yet\n    // exist on return.\n    bool MapSnapshot(LockedFile* lock, const std::string& name, const std::string& base_device,\n                     const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,\n                     std::string* dev_path);\n\n    // Create a dm-user device for a given snapshot.\n    bool MapDmUserCow(LockedFile* lock, const std::string& name, const std::string& cow_file,\n                      const std::string& base_device, const std::string& base_path_merge,\n                      const std::chrono::milliseconds& timeout_ms, std::string* path);\n\n    // Map the source device used for dm-user.\n    bool MapSourceDevice(LockedFile* lock, const std::string& name,\n                         const std::chrono::milliseconds& timeout_ms, std::string* path);\n\n    // Map a COW image that was previous created with CreateCowImage.\n    std::optional<std::string> MapCowImage(const std::string& name,\n                                           const std::chrono::milliseconds& timeout_ms);\n\n    // Remove the backing copy-on-write image and snapshot states for the named snapshot. The\n    // caller is responsible for ensuring that the snapshot is unmapped.\n    bool DeleteSnapshot(LockedFile* lock, const std::string& name);\n\n    // Unmap a snapshot device previously mapped with MapSnapshotDevice().\n    bool UnmapSnapshot(LockedFile* lock, const std::string& name);\n\n    // Unmap a COW image device previously mapped with MapCowImage().\n    bool UnmapCowImage(const std::string& name);\n\n    // Unmap a COW and remove it from a MetadataBuilder.\n    void UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata);\n\n    // Remove invalid snapshots if any\n    void RemoveInvalidSnapshots(LockedFile* lock);\n\n    // Unmap and remove all known snapshots.\n    bool RemoveAllSnapshots(LockedFile* lock);\n\n    // Boot device off snapshots without slot switch\n    bool BootFromSnapshotsWithoutSlotSwitch();\n\n    // Remove kBootSnapshotsWithoutSlotSwitch so that device can boot\n    // without snapshots on the current slot\n    bool PrepareDeviceToBootWithoutSnapshot();\n\n    // Is the kBootSnapshotsWithoutSlotSwitch present\n    bool IsSnapshotWithoutSlotSwitch();\n\n    // List the known snapshot names.\n    bool ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,\n                       const std::string& suffix = \"\");\n\n    // Check for a cancelled or rolled back merge, returning true if such a\n    // condition was detected and handled.\n    bool HandleCancelledUpdate(LockedFile* lock, const std::function<bool()>& before_cancel);\n\n    // Helper for HandleCancelledUpdate. Assumes booting from new slot.\n    bool AreAllSnapshotsCancelled(LockedFile* lock);\n\n    // Determine whether partition names in |snapshots| have been flashed and\n    // store result to |out|.\n    // Return true if values are successfully retrieved and false on error\n    // (e.g. super partition metadata cannot be read). When it returns true,\n    // |out| stores true for partitions that have been flashed and false for\n    // partitions that have not been flashed.\n    bool GetSnapshotFlashingStatus(LockedFile* lock, const std::vector<std::string>& snapshots,\n                                   std::map<std::string, bool>* out);\n\n    // Remove artifacts created by the update process, such as snapshots, and\n    // set the update state to None.\n    bool RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog = {});\n\n    // Interact with /metadata/ota.\n    std::unique_ptr<LockedFile> OpenLock(int lock_flags);\n    std::unique_ptr<LockedFile> LockShared();\n    std::unique_ptr<LockedFile> LockExclusive();\n    std::string GetLockPath() const;\n\n    // Interact with /metadata/ota/state.\n    UpdateState ReadUpdateState(LockedFile* file);\n    SnapshotUpdateStatus ReadSnapshotUpdateStatus(LockedFile* file);\n    bool WriteUpdateState(LockedFile* file, UpdateState state,\n                          MergeFailureCode failure_code = MergeFailureCode::Ok);\n    bool WriteSnapshotUpdateStatus(LockedFile* file, const SnapshotUpdateStatus& status);\n    std::string GetStateFilePath() const;\n\n    // Interact with /metadata/ota/merge_state.\n    // This file contains information related to the snapshot merge process.\n    std::string GetMergeStateFilePath() const;\n\n    // Helpers for merging.\n    MergeFailureCode MergeSecondPhaseSnapshots(LockedFile* lock);\n    MergeFailureCode SwitchSnapshotToMerge(LockedFile* lock, const std::string& name);\n    MergeFailureCode RewriteSnapshotDeviceTable(const std::string& dm_name);\n    bool MarkSnapshotMergeCompleted(LockedFile* snapshot_lock, const std::string& snapshot_name);\n    void AcknowledgeMergeSuccess(LockedFile* lock);\n    void AcknowledgeMergeFailure(MergeFailureCode failure_code);\n    MergePhase DecideMergePhase(const SnapshotStatus& status);\n    std::unique_ptr<LpMetadata> ReadCurrentMetadata();\n\n    enum class MetadataPartitionState {\n        // Partition does not exist.\n        None,\n        // Partition is flashed.\n        Flashed,\n        // Partition is created by OTA client.\n        Updated,\n    };\n    // Helper function to check the state of a partition as described in metadata.\n    MetadataPartitionState GetMetadataPartitionState(const LpMetadata& metadata,\n                                                     const std::string& name);\n\n    // Note that these require the name of the device containing the snapshot,\n    // which may be the \"inner\" device. Use GetsnapshotDeviecName().\n    bool QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,\n                             DmTargetSnapshot::Status* status);\n    bool IsSnapshotDevice(const std::string& dm_name, TargetInfo* target = nullptr);\n\n    // Internal callback for when merging is complete.\n    bool OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,\n                                 const SnapshotStatus& status);\n    bool CollapseSnapshotDevice(LockedFile* lock, const std::string& name,\n                                const SnapshotStatus& status);\n\n    struct [[nodiscard]] MergeResult {\n        explicit MergeResult(UpdateState state,\n                             MergeFailureCode failure_code = MergeFailureCode::Ok)\n            : state(state), failure_code(failure_code) {}\n        UpdateState state;\n        MergeFailureCode failure_code;\n    };\n\n    // Only the following UpdateStates are used here:\n    //   UpdateState::Merging\n    //   UpdateState::MergeCompleted\n    //   UpdateState::MergeFailed\n    //   UpdateState::MergeNeedsReboot\n    MergeResult CheckMergeState(const std::function<bool()>& before_cancel);\n    MergeResult CheckMergeState(LockedFile* lock, const std::function<bool()>& before_cancel);\n    MergeResult CheckTargetMergeState(LockedFile* lock, const std::string& name,\n                                      const SnapshotUpdateStatus& update_status);\n\n    auto UpdateStateToStr(enum UpdateState state);\n    // Get status or table information about a device-mapper node with a single target.\n    enum class TableQuery {\n        Table,\n        Status,\n    };\n    bool GetSingleTarget(const std::string& dm_name, TableQuery query,\n                         android::dm::DeviceMapper::TargetInfo* target);\n\n    // Interact with status files under /metadata/ota/snapshots.\n    bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);\n    bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);\n    std::string GetSnapshotStatusFilePath(const std::string& name);\n\n    std::string GetSnapshotBootIndicatorPath();\n    std::string GetRollbackIndicatorPath();\n    std::string GetForwardMergeIndicatorPath();\n    std::string GetOldPartitionMetadataPath();\n    std::string GetBootSnapshotsWithoutSlotSwitchPath();\n    std::string GetSnapuserdFromSystemPath();\n\n    bool HasForwardMergeIndicator();\n\n    const LpMetadata* ReadOldPartitionMetadata(LockedFile* lock);\n\n    bool MapAllPartitions(LockedFile* lock, const std::string& super_device, uint32_t slot,\n                          const std::chrono::milliseconds& timeout_ms);\n\n    // Reason for calling MapPartitionWithSnapshot.\n    enum class SnapshotContext {\n        // For writing or verification (during update_engine).\n        Update,\n\n        // For mounting a full readable device.\n        Mount,\n    };\n\n    struct SnapshotPaths {\n        // Target/base device (eg system_b), always present.\n        std::string target_device;\n\n        // COW name (eg system_cow). Not present if no COW is needed.\n        std::string cow_device_name;\n\n        // dm-snapshot instance. Not present in Update mode for VABC.\n        std::string snapshot_device;\n    };\n\n    // Helpers for OpenSnapshotWriter.\n    std::unique_ptr<ICowWriter> OpenCompressedSnapshotWriter(LockedFile* lock,\n                                                             const SnapshotStatus& status,\n                                                             const SnapshotPaths& paths,\n                                                             std::optional<uint64_t> label);\n\n    // Map the base device, COW devices, and snapshot device.\n    bool MapPartitionWithSnapshot(LockedFile* lock, CreateLogicalPartitionParams params,\n                                  SnapshotContext context, SnapshotPaths* paths);\n\n    // Map the COW devices, including the partition in super and the images.\n    // |params|:\n    //    - |partition_name| should be the name of the top-level partition (e.g. system_b),\n    //            not system_b-cow-img\n    //    - |device_name| and |partition| is ignored\n    //    - |timeout_ms| and the rest is respected\n    // Return the path in |cow_device_path| (e.g. /dev/block/dm-1) and major:minor in\n    // |cow_device_string|\n    bool MapCowDevices(LockedFile* lock, const CreateLogicalPartitionParams& params,\n                       const SnapshotStatus& snapshot_status, AutoDeviceList* created_devices,\n                       std::string* cow_name);\n\n    // The reverse of MapCowDevices.\n    bool UnmapCowDevices(LockedFile* lock, const std::string& name);\n\n    // The reverse of MapPartitionWithSnapshot.\n    bool UnmapPartitionWithSnapshot(LockedFile* lock, const std::string& target_partition_name);\n\n    // Unmap a dm-user device through snapuserd.\n    bool UnmapDmUserDevice(const std::string& dm_user_name);\n\n    // Unmap a dm-user device for user space snapshots\n    bool UnmapUserspaceSnapshotDevice(LockedFile* lock, const std::string& snapshot_name);\n\n    CancelResult TryCancelUpdate();\n    CancelResult IsCancelUpdateSafe(UpdateState state);\n\n    // Helper for CreateUpdateSnapshots.\n    // Creates all underlying images, COW partitions and snapshot files. Does not initialize them.\n    Return CreateUpdateSnapshotsInternal(\n            LockedFile* lock, const DeltaArchiveManifest& manifest,\n            PartitionCowCreator* cow_creator, AutoDeviceList* created_devices,\n            std::map<std::string, SnapshotStatus>* all_snapshot_status);\n\n    // Initialize snapshots so that they can be mapped later.\n    // Map the COW partition and zero-initialize the header.\n    Return InitializeUpdateSnapshots(\n            LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,\n            const LpMetadata* exported_target_metadata, const std::string& target_suffix,\n            const std::map<std::string, SnapshotStatus>& all_snapshot_status);\n\n    // Implementation of UnmapAllSnapshots(), with the lock provided.\n    bool UnmapAllSnapshots(LockedFile* lock);\n\n    // Unmap all partitions that were mapped by CreateLogicalAndSnapshotPartitions.\n    // This should only be called in recovery.\n    bool UnmapAllPartitionsInRecovery();\n\n    // Check no snapshot overflows. Note that this returns false negatives if the snapshot\n    // overflows, then is remapped and not written afterwards.\n    bool EnsureNoOverflowSnapshot(LockedFile* lock);\n\n    enum class Slot { Unknown, Source, Target };\n    friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot);\n    Slot GetCurrentSlot();\n\n    // Return the suffix we expect snapshots to have.\n    std::string GetSnapshotSlotSuffix();\n\n    std::string ReadUpdateSourceSlotSuffix();\n\n    // Helper for RemoveAllSnapshots.\n    // Check whether |name| should be deleted as a snapshot name.\n    bool ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status, Slot current_slot,\n                              const std::string& name);\n\n    // Create or delete forward merge indicator given |wipe|. Iff wipe is scheduled,\n    // allow forward merge on FDR.\n    bool UpdateForwardMergeIndicator(bool wipe);\n\n    // Helper for HandleImminentDataWipe.\n    // Call ProcessUpdateState and handle states with special rules before data wipe.\n    UpdateState ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback);\n\n    // Return device string of a mapped image, or if it is not available, the mapped image path.\n    bool GetMappedImageDeviceStringOrPath(const std::string& device_name,\n                                          std::string* device_string_or_mapped_path);\n\n    // Same as above, but for paths only (no major:minor device strings).\n    bool GetMappedImageDevicePath(const std::string& device_name, std::string* device_path);\n\n    // Wait for a device to be created by ueventd (eg, its symlink or node to be populated).\n    // This is needed for any code that uses device-mapper path in first-stage init. If\n    // |timeout_ms| is empty or the given device is not a path, WaitForDevice immediately\n    // returns true.\n    bool WaitForDevice(const std::string& device, std::chrono::milliseconds timeout_ms);\n\n    enum class InitTransition { SELINUX_DETACH, SECOND_STAGE };\n\n    // Initiate the transition from first-stage to second-stage snapuserd. This\n    // process involves re-creating the dm-user table entries for each device,\n    // so that they connect to the new daemon. Once all new tables have been\n    // activated, we ask the first-stage daemon to cleanly exit.\n    //\n    // If the mode is SELINUX_DETACH, snapuserd_argv must be non-null and will\n    // be populated with a list of snapuserd arguments to pass to execve(). It\n    // is otherwise ignored.\n    bool PerformInitTransition(InitTransition transition,\n                               std::vector<std::string>* snapuserd_argv = nullptr);\n\n    SnapuserdClient* snapuserd_client() const { return snapuserd_client_.get(); }\n\n    // Helper of UpdateUsesCompression\n    bool UpdateUsesCompression(LockedFile* lock);\n    // Locked and unlocked functions to test whether the current update uses\n    // userspace snapshots.\n    bool UpdateUsesUserSnapshots(LockedFile* lock);\n\n    // Check if io_uring API's need to be used\n    bool UpdateUsesIouring(LockedFile* lock);\n\n    // Check if direct reads are enabled for the source image\n    bool UpdateUsesODirect(LockedFile* lock);\n\n    // Get value of maximum cow op merge size\n    uint32_t GetUpdateCowOpMergeSize(LockedFile* lock);\n\n    // Get number of threads to perform post OTA boot verification\n    uint32_t GetUpdateWorkerCount(LockedFile* lock);\n\n    // Wrapper around libdm, with diagnostics.\n    bool DeleteDeviceIfExists(const std::string& name,\n                              const std::chrono::milliseconds& timeout_ms = {});\n\n    // Set read-ahead size during OTA\n    void SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb);\n\n    // Returns true post OTA reboot if legacy snapuserd is required\n    bool IsLegacySnapuserdPostReboot();\n\n    android::dm::IDeviceMapper& dm_;\n    std::unique_ptr<IDeviceInfo> device_;\n    std::string metadata_dir_;\n    std::unique_ptr<IImageManager> images_;\n    bool use_first_stage_snapuserd_ = false;\n    std::function<bool(const std::string&)> uevent_regen_callback_;\n    std::unique_ptr<SnapuserdClient> snapuserd_client_;\n    std::unique_ptr<LpMetadata> old_partition_metadata_;\n    std::optional<bool> is_snapshot_userspace_;\n    std::optional<bool> is_legacy_snapuserd_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n\n#ifdef DEFINED_FRIEND_TEST\n#undef DEFINED_FRIEND_TEST\n#undef FRIEND_TEST\n#endif\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/snapshot_stats.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <chrono>\n#include <memory>\n\n#include <android/snapshot/snapshot.pb.h>\n#include <libsnapshot/snapshot.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass ISnapshotMergeStats {\n  public:\n    virtual ~ISnapshotMergeStats() = default;\n    // Called when merge starts or resumes.\n    virtual bool Start() = 0;\n    virtual void set_state(android::snapshot::UpdateState state) = 0;\n    virtual void set_boot_complete_time_ms(uint32_t ms) = 0;\n    virtual void set_boot_complete_to_merge_start_time_ms(uint32_t ms) = 0;\n    virtual void set_merge_failure_code(MergeFailureCode code) = 0;\n    virtual void set_source_build_fingerprint(const std::string& fingerprint) = 0;\n    virtual uint64_t cow_file_size() = 0;\n    virtual uint64_t total_cow_size_bytes() = 0;\n    virtual uint64_t estimated_cow_size_bytes() = 0;\n    virtual uint32_t boot_complete_time_ms() = 0;\n    virtual uint32_t boot_complete_to_merge_start_time_ms() = 0;\n    virtual MergeFailureCode merge_failure_code() = 0;\n    virtual std::string source_build_fingerprint() = 0;\n\n    // Called when merge ends. Properly clean up permanent storage.\n    class Result {\n      public:\n        virtual ~Result() {}\n        virtual const SnapshotMergeReport& report() const = 0;\n        // Time between successful Start() / Resume() to Finish().\n        virtual std::chrono::steady_clock::duration merge_time() const = 0;\n    };\n    // Return nullptr if any failure.\n    virtual std::unique_ptr<Result> Finish() = 0;\n\n    // Return the underlying implementation.\n    virtual SnapshotMergeReport* report() = 0;\n\n    // Write out the current state. This should be called when data might be lost that\n    // cannot be recovered (eg the COW sizes).\n    virtual bool WriteState() = 0;\n};\n\nclass SnapshotMergeStats : public ISnapshotMergeStats {\n  public:\n    // Not thread safe.\n    static SnapshotMergeStats* GetInstance(SnapshotManager& manager);\n    SnapshotMergeStats(const std::string& path);\n\n    // ISnapshotMergeStats overrides\n    bool Start() override;\n    void set_state(android::snapshot::UpdateState state) override;\n    uint64_t cow_file_size() override;\n    uint64_t total_cow_size_bytes() override;\n    uint64_t estimated_cow_size_bytes() override;\n    void set_boot_complete_time_ms(uint32_t ms) override;\n    uint32_t boot_complete_time_ms() override;\n    void set_boot_complete_to_merge_start_time_ms(uint32_t ms) override;\n    uint32_t boot_complete_to_merge_start_time_ms() override;\n    void set_merge_failure_code(MergeFailureCode code) override;\n    MergeFailureCode merge_failure_code() override;\n    void set_source_build_fingerprint(const std::string& fingerprint) override;\n    std::string source_build_fingerprint() override;\n    std::unique_ptr<Result> Finish() override;\n    bool WriteState() override;\n\n    // Access the underlying report before it is finished.\n    SnapshotMergeReport* report() override { return &report_; }\n\n  private:\n    bool ReadState();\n    bool DeleteState();\n\n    std::string path_;\n    SnapshotMergeReport report_;\n    // Time of the last successful Start() / Resume() call.\n    std::chrono::time_point<std::chrono::steady_clock> start_time_;\n    bool running_{false};\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <libsnapshot/snapshot.h>\n#include <payload_consumer/file_descriptor.h>\n\nnamespace android::snapshot {\n\nclass SnapshotManagerStub : public ISnapshotManager {\n  public:\n    // Create a stubbed snapshot manager. All calls into the stub fails.\n    static std::unique_ptr<ISnapshotManager> New();\n\n    // ISnapshotManager overrides.\n    bool BeginUpdate() override;\n    bool CancelUpdate() override;\n    bool FinishedSnapshotWrites(bool wipe) override;\n    void UpdateCowStats(ISnapshotMergeStats* stats) override;\n    MergeFailureCode ReadMergeFailureCode() override;\n    bool InitiateMerge() override;\n    UpdateState ProcessUpdateState(const std::function<bool()>& callback = {},\n                                   const std::function<bool()>& before_cancel = {}) override;\n    UpdateState GetUpdateState(double* progress = nullptr) override;\n    bool UpdateUsesCompression() override;\n    bool UpdateUsesUserSnapshots() override;\n    Return CreateUpdateSnapshots(\n            const chromeos_update_engine::DeltaArchiveManifest& manifest) override;\n    bool MapUpdateSnapshot(const android::fs_mgr::CreateLogicalPartitionParams& params,\n                           std::string* snapshot_path) override;\n    std::unique_ptr<ICowWriter> OpenSnapshotWriter(\n            const android::fs_mgr::CreateLogicalPartitionParams& params,\n            std::optional<uint64_t> label) override;\n    bool UnmapUpdateSnapshot(const std::string& target_partition_name) override;\n    bool NeedSnapshotsInFirstStageMount() override;\n    bool CreateLogicalAndSnapshotPartitions(\n            const std::string& super_device,\n            const std::chrono::milliseconds& timeout_ms = {}) override;\n    bool HandleImminentDataWipe(const std::function<void()>& callback = {}) override;\n    bool FinishMergeInRecovery() override;\n    CreateResult RecoveryCreateSnapshotDevices() override;\n    CreateResult RecoveryCreateSnapshotDevices(\n            const std::unique_ptr<AutoDevice>& metadata_device) override;\n    bool Dump(std::ostream& os) override;\n    std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;\n    ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;\n    bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;\n    bool UnmapAllSnapshots() override;\n    std::string ReadSourceBuildFingerprint() override;\n    void SetMergeStatsFeatures(ISnapshotMergeStats* stats) override;\n    bool IsCancelUpdateSafe() override;\n};\n\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <memory>\n#include <optional>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include <android-base/file.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <libfiemap/image_manager.h>\n#include <liblp/mock_property_fetcher.h>\n#include <liblp/partition_opener.h>\n#include <libsnapshot/snapshot.h>\n#include <storage_literals/storage_literals.h>\n#include <update_engine/update_metadata.pb.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing aidl::android::hardware::boot::MergeStatus;\nusing android::fs_mgr::IPropertyFetcher;\nusing android::fs_mgr::MetadataBuilder;\nusing android::fs_mgr::testing::MockPropertyFetcher;\nusing chromeos_update_engine::DeltaArchiveManifest;\nusing chromeos_update_engine::PartitionUpdate;\nusing testing::_;\nusing testing::AssertionResult;\nusing testing::NiceMock;\n\nusing namespace android::storage_literals;\nusing namespace std::string_literals;\n\n// These are not reset between each test because it's expensive to create\n// these resources (starting+connecting to gsid, zero-filling images).\nextern std::unique_ptr<SnapshotManager> sm;\nextern class TestDeviceInfo* test_device;\nextern std::string fake_super;\nstatic constexpr uint64_t kSuperSize = 32_MiB + 4_KiB;\nstatic constexpr uint64_t kGroupSize = 32_MiB;\n\n// Redirect requests for \"super\" to our fake super partition.\nclass TestPartitionOpener final : public android::fs_mgr::PartitionOpener {\n  public:\n    explicit TestPartitionOpener(const std::string& fake_super_path)\n        : fake_super_path_(fake_super_path) {}\n\n    android::base::unique_fd Open(const std::string& partition_name, int flags) const override;\n    bool GetInfo(const std::string& partition_name,\n                 android::fs_mgr::BlockDeviceInfo* info) const override;\n    std::string GetDeviceString(const std::string& partition_name) const override;\n\n  private:\n    std::string fake_super_path_;\n};\n\nclass TestDeviceInfo : public SnapshotManager::IDeviceInfo {\n  public:\n    TestDeviceInfo() {}\n    explicit TestDeviceInfo(const std::string& fake_super) { set_fake_super(fake_super); }\n    TestDeviceInfo(const std::string& fake_super, const std::string& slot_suffix)\n        : TestDeviceInfo(fake_super) {\n        set_slot_suffix(slot_suffix);\n    }\n    std::string GetMetadataDir() const override { return metadata_dir_; }\n    std::string GetSlotSuffix() const override { return slot_suffix_; }\n    std::string GetOtherSlotSuffix() const override { return slot_suffix_ == \"_a\" ? \"_b\" : \"_a\"; }\n    std::string GetSuperDevice([[maybe_unused]] uint32_t slot) const override { return \"super\"; }\n    const android::fs_mgr::IPartitionOpener& GetPartitionOpener() const override {\n        return *opener_.get();\n    }\n    bool SetBootControlMergeStatus(MergeStatus status) override {\n        merge_status_ = status;\n        return true;\n    }\n    bool IsOverlayfsSetup() const override { return false; }\n    bool IsRecovery() const override { return recovery_; }\n    bool SetActiveBootSlot([[maybe_unused]] unsigned int slot) override { return true; }\n    bool SetSlotAsUnbootable(unsigned int slot) override {\n        unbootable_slots_.insert(slot);\n        return true;\n    }\n    bool IsTestDevice() const override { return true; }\n    bool IsFirstStageInit() const override { return first_stage_init_; }\n    std::unique_ptr<IImageManager> OpenImageManager() const override {\n        return IDeviceInfo::OpenImageManager(\"ota/test\");\n    }\n    android::dm::IDeviceMapper& GetDeviceMapper() override {\n        if (dm_) {\n            return *dm_;\n        }\n        return android::dm::DeviceMapper::Instance();\n    }\n\n    bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }\n\n    void set_slot_suffix(const std::string& suffix) { slot_suffix_ = suffix; }\n    void set_fake_super(const std::string& path) {\n        opener_ = std::make_unique<TestPartitionOpener>(path);\n    }\n    void set_recovery(bool value) { recovery_ = value; }\n    void set_first_stage_init(bool value) { first_stage_init_ = value; }\n    void set_dm(android::dm::IDeviceMapper* dm) { dm_ = dm; }\n\n    MergeStatus merge_status() const { return merge_status_; }\n    bool IsTempMetadata() const override { return temp_metadata_; }\n\n  private:\n    std::string slot_suffix_ = \"_a\";\n    std::unique_ptr<TestPartitionOpener> opener_;\n    MergeStatus merge_status_;\n    bool recovery_ = false;\n    bool first_stage_init_ = false;\n    std::unordered_set<uint32_t> unbootable_slots_;\n    android::dm::IDeviceMapper* dm_ = nullptr;\n    std::string metadata_dir_ = \"/metadata/ota/test\";\n    bool temp_metadata_ = false;\n};\n\nclass DeviceMapperWrapper : public android::dm::IDeviceMapper {\n    using DmDeviceState = android::dm::DmDeviceState;\n    using DmTable = android::dm::DmTable;\n\n  public:\n    DeviceMapperWrapper() : impl_(android::dm::DeviceMapper::Instance()) {}\n    explicit DeviceMapperWrapper(android::dm::IDeviceMapper& impl) : impl_(impl) {}\n\n    virtual bool CreateDevice(const std::string& name, const DmTable& table, std::string* path,\n                              const std::chrono::milliseconds& timeout_ms) override {\n        return impl_.CreateDevice(name, table, path, timeout_ms);\n    }\n    virtual DmDeviceState GetState(const std::string& name) const override {\n        return impl_.GetState(name);\n    }\n    virtual bool LoadTable(const std::string& name, const DmTable& table) {\n        return impl_.LoadTable(name, table);\n    }\n    virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) {\n        return impl_.LoadTableAndActivate(name, table);\n    }\n    virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) {\n        return impl_.GetTableInfo(name, table);\n    }\n    virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) {\n        return impl_.GetTableStatus(name, table);\n    }\n    virtual bool GetTableStatusIma(const std::string& name, std::vector<TargetInfo>* table) {\n        return impl_.GetTableStatusIma(name, table);\n    }\n    virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) {\n        return impl_.GetDmDevicePathByName(name, path);\n    }\n    virtual bool GetDeviceString(const std::string& name, std::string* dev) {\n        return impl_.GetDeviceString(name, dev);\n    }\n    virtual bool DeleteDeviceIfExists(const std::string& name) {\n        return impl_.DeleteDeviceIfExists(name);\n    }\n\n  private:\n    android::dm::IDeviceMapper& impl_;\n};\n\nclass SnapshotTestPropertyFetcher : public android::fs_mgr::IPropertyFetcher {\n  public:\n    explicit SnapshotTestPropertyFetcher(const std::string& slot_suffix,\n                                         std::unordered_map<std::string, std::string>&& props = {});\n\n    std::string GetProperty(const std::string& key, const std::string& defaultValue) override;\n    bool GetBoolProperty(const std::string& key, bool defaultValue) override;\n\n    static void SetUp(const std::string& slot_suffix = \"_a\") { Reset(slot_suffix); }\n    static void TearDown() { Reset(\"_a\"); }\n\n  private:\n    static void Reset(const std::string& slot_suffix) {\n        IPropertyFetcher::OverrideForTesting(\n                std::make_unique<SnapshotTestPropertyFetcher>(slot_suffix));\n    }\n\n  private:\n    std::unordered_map<std::string, std::string> properties_;\n};\n\n// Helper for error-spam-free cleanup.\nvoid DeleteBackingImage(android::fiemap::IImageManager* manager, const std::string& name);\n\n// Write some random data to the given device.\n// If expect_size is not specified, will write until reaching end of the device.\n// Expect space of |path| is multiple of 4K.\nbool WriteRandomData(const std::string& path, std::optional<size_t> expect_size = std::nullopt,\n                     std::string* hash = nullptr);\nstd::string HashSnapshot(ICowWriter::FileDescriptor* writer);\n\nstd::string ToHexString(const uint8_t* buf, size_t len);\n\nstd::optional<std::string> GetHash(const std::string& path);\n\n// Add partitions and groups described by |manifest|.\nAssertionResult FillFakeMetadata(MetadataBuilder* builder, const DeltaArchiveManifest& manifest,\n                                 const std::string& suffix);\n\n// In the update package metadata, set a partition with the given size.\nvoid SetSize(PartitionUpdate* partition_update, uint64_t size);\n\n// Get partition size from update package metadata.\nuint64_t GetSize(PartitionUpdate* partition_update);\n\nbool IsVirtualAbEnabled();\n\n#define SKIP_IF_NON_VIRTUAL_AB()                                                        \\\n    do {                                                                                \\\n        if (!IsVirtualAbEnabled()) GTEST_SKIP() << \"Test for Virtual A/B devices only\"; \\\n    } while (0)\n\n#define RETURN_IF_NON_VIRTUAL_AB_MSG(msg) \\\n    do {                                  \\\n        if (!IsVirtualAbEnabled()) {      \\\n            std::cerr << (msg);           \\\n            return;                       \\\n        }                                 \\\n    } while (0)\n\n#define RETURN_IF_NON_VIRTUAL_AB() RETURN_IF_NON_VIRTUAL_AB_MSG(\"\")\n\n#define SKIP_IF_VENDOR_ON_ANDROID_S()                                        \\\n    do {                                                                     \\\n        if (IsVendorFromAndroid12())                                         \\\n            GTEST_SKIP() << \"Skip test as Vendor partition is on Android S\"; \\\n    } while (0)\n\n#define RETURN_IF_VENDOR_ON_ANDROID_S_MSG(msg) \\\n    do {                                       \\\n        if (IsVendorFromAndroid12()) {         \\\n            std::cerr << (msg);                \\\n            return;                            \\\n        }                                      \\\n    } while (0)\n\n#define RETURN_IF_VENDOR_ON_ANDROID_S() RETURN_IF_VENDOR_ON_ANDROID_S_MSG(\"\")\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/cow_compress.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <cstdint>\n#include <limits>\n#include <memory>\n#include <queue>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <brotli/encode.h>\n#include <libsnapshot/cow_compress.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_reader.h>\n#include <libsnapshot/cow_writer.h>\n#include <lz4.h>\n#include <zlib.h>\n#include <zstd.h>\n\nnamespace android {\nnamespace snapshot {\n\nstd::optional<CowCompressionAlgorithm> CompressionAlgorithmFromString(std::string_view name) {\n    if (name == \"gz\") {\n        return {kCowCompressGz};\n    } else if (name == \"brotli\") {\n        return {kCowCompressBrotli};\n    } else if (name == \"lz4\") {\n        return {kCowCompressLz4};\n    } else if (name == \"zstd\") {\n        return {kCowCompressZstd};\n    } else if (name == \"none\" || name.empty()) {\n        return {kCowCompressNone};\n    } else {\n        LOG(ERROR) << \"unable to determine default compression algorithm for: \" << name;\n        return {};\n    }\n}\n\nstd::unique_ptr<ICompressor> ICompressor::Create(CowCompression compression,\n                                                 const uint32_t block_size) {\n    switch (compression.algorithm) {\n        case kCowCompressLz4:\n            return ICompressor::Lz4(compression.compression_level, block_size);\n        case kCowCompressBrotli:\n            return ICompressor::Brotli(compression.compression_level, block_size);\n        case kCowCompressGz:\n            return ICompressor::Gz(compression.compression_level, block_size);\n        case kCowCompressZstd:\n            return ICompressor::Zstd(compression.compression_level, block_size);\n        case kCowCompressNone:\n            return nullptr;\n    }\n    return nullptr;\n}\n\n// 1. Default compression level is determined by compression algorithm\n// 2. There might be compatibility issues if a value is changed here, as  some older versions of\n// Android will assume a different compression level, causing cow_size estimation differences that\n// will lead to OTA failure. Ensure that the device and OTA package use the same compression level\n// for OTA to succeed.\nuint32_t CompressWorker::GetDefaultCompressionLevel(CowCompressionAlgorithm compression) {\n    switch (compression) {\n        case kCowCompressGz: {\n            return Z_BEST_COMPRESSION;\n        }\n        case kCowCompressBrotli: {\n            return BROTLI_DEFAULT_QUALITY;\n        }\n        case kCowCompressLz4: {\n            break;\n        }\n        case kCowCompressZstd: {\n            return ZSTD_defaultCLevel();\n        }\n        case kCowCompressNone: {\n            break;\n        }\n    }\n    return 0;\n}\n\nclass GzCompressor final : public ICompressor {\n  public:\n    GzCompressor(int32_t compression_level, const uint32_t block_size)\n        : ICompressor(compression_level, block_size){};\n\n    std::vector<uint8_t> Compress(const void* data, size_t length) const override {\n        const auto bound = compressBound(length);\n        std::vector<uint8_t> buffer(bound, '\\0');\n\n        uLongf dest_len = bound;\n        auto rv = compress2(buffer.data(), &dest_len, reinterpret_cast<const Bytef*>(data), length,\n                            GetCompressionLevel());\n        if (rv != Z_OK) {\n            LOG(ERROR) << \"compress2 returned: \" << rv;\n            return {};\n        }\n        buffer.resize(dest_len);\n        return buffer;\n    };\n};\n\nclass Lz4Compressor final : public ICompressor {\n  public:\n    Lz4Compressor(int32_t compression_level, const uint32_t block_size)\n        : ICompressor(compression_level, block_size){};\n\n    std::vector<uint8_t> Compress(const void* data, size_t length) const override {\n        const auto bound = LZ4_compressBound(length);\n        if (!bound) {\n            LOG(ERROR) << \"LZ4_compressBound returned 0\";\n            return {};\n        }\n        std::vector<uint8_t> buffer(bound, '\\0');\n\n        const auto compressed_size =\n                LZ4_compress_default(static_cast<const char*>(data),\n                                     reinterpret_cast<char*>(buffer.data()), length, buffer.size());\n        if (compressed_size <= 0) {\n            LOG(ERROR) << \"LZ4_compress_default failed, input size: \" << length\n                       << \", compression bound: \" << bound << \", ret: \" << compressed_size;\n            return {};\n        }\n        // Don't run compression if the compressed output is larger\n        if (compressed_size >= length) {\n            buffer.resize(length);\n            memcpy(buffer.data(), data, length);\n        } else {\n            buffer.resize(compressed_size);\n        }\n        return buffer;\n    };\n};\n\nclass BrotliCompressor final : public ICompressor {\n  public:\n    BrotliCompressor(int32_t compression_level, const uint32_t block_size)\n        : ICompressor(compression_level, block_size){};\n\n    std::vector<uint8_t> Compress(const void* data, size_t length) const override {\n        const auto bound = BrotliEncoderMaxCompressedSize(length);\n        if (!bound) {\n            LOG(ERROR) << \"BrotliEncoderMaxCompressedSize returned 0\";\n            return {};\n        }\n        std::vector<uint8_t> buffer(bound, '\\0');\n\n        size_t encoded_size = bound;\n        auto rv = BrotliEncoderCompress(\n                GetCompressionLevel(), BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, length,\n                reinterpret_cast<const uint8_t*>(data), &encoded_size, buffer.data());\n        if (!rv) {\n            LOG(ERROR) << \"BrotliEncoderCompress failed\";\n            return {};\n        }\n        buffer.resize(encoded_size);\n        return buffer;\n    };\n};\n\nclass ZstdCompressor final : public ICompressor {\n  public:\n    ZstdCompressor(int32_t compression_level, const uint32_t block_size)\n        : ICompressor(compression_level, block_size),\n          zstd_context_(ZSTD_createCCtx(), ZSTD_freeCCtx) {\n        ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_compressionLevel, compression_level);\n        ZSTD_CCtx_setParameter(zstd_context_.get(), ZSTD_c_windowLog, log2(GetBlockSize()));\n    };\n\n    std::vector<uint8_t> Compress(const void* data, size_t length) const override {\n        std::vector<uint8_t> buffer(ZSTD_compressBound(length), '\\0');\n        const auto compressed_size =\n                ZSTD_compress2(zstd_context_.get(), buffer.data(), buffer.size(), data, length);\n        if (compressed_size <= 0) {\n            LOG(ERROR) << \"ZSTD compression failed \" << compressed_size;\n            return {};\n        }\n        // Don't run compression if the compressed output is larger\n        if (compressed_size >= length) {\n            buffer.resize(length);\n            memcpy(buffer.data(), data, length);\n        } else {\n            buffer.resize(compressed_size);\n        }\n        return buffer;\n    };\n\n  private:\n    std::unique_ptr<ZSTD_CCtx, decltype(&ZSTD_freeCCtx)> zstd_context_;\n};\n\nbool CompressWorker::CompressBlocks(const void* buffer, size_t num_blocks, size_t block_size,\n                                    std::vector<std::vector<uint8_t>>* compressed_data) {\n    return CompressBlocks(compressor_.get(), block_size, buffer, num_blocks, compressed_data);\n}\n\nbool CompressWorker::CompressBlocks(ICompressor* compressor, size_t block_size, const void* buffer,\n                                    size_t num_blocks,\n                                    std::vector<std::vector<uint8_t>>* compressed_data) {\n    const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer);\n    while (num_blocks) {\n        auto data = compressor->Compress(iter, block_size);\n        if (data.empty()) {\n            PLOG(ERROR) << \"CompressBlocks: Compression failed\";\n            return false;\n        }\n        if (data.size() > std::numeric_limits<uint32_t>::max()) {\n            LOG(ERROR) << \"Compressed block is too large: \" << data.size();\n            return false;\n        }\n\n        compressed_data->emplace_back(std::move(data));\n        num_blocks -= 1;\n        iter += block_size;\n    }\n    return true;\n}\n\nbool CompressWorker::RunThread() {\n    while (true) {\n        // Wait for work\n        CompressWork blocks;\n        {\n            std::unique_lock<std::mutex> lock(lock_);\n            while (work_queue_.empty() && !stopped_) {\n                cv_.wait(lock);\n            }\n\n            if (stopped_) {\n                return true;\n            }\n\n            blocks = std::move(work_queue_.front());\n            work_queue_.pop();\n        }\n\n        // Compress blocks\n        bool ret = CompressBlocks(blocks.buffer, blocks.num_blocks, blocks.block_size,\n                                  &blocks.compressed_data);\n        blocks.compression_status = ret;\n        {\n            std::lock_guard<std::mutex> lock(lock_);\n            compressed_queue_.push(std::move(blocks));\n        }\n\n        // Notify completion\n        cv_.notify_all();\n\n        if (!ret) {\n            LOG(ERROR) << \"CompressBlocks failed\";\n            return false;\n        }\n    }\n\n    return true;\n}\n\nvoid CompressWorker::EnqueueCompressBlocks(const void* buffer, size_t block_size,\n                                           size_t num_blocks) {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n\n        CompressWork blocks = {};\n        blocks.buffer = buffer;\n        blocks.block_size = block_size;\n        blocks.num_blocks = num_blocks;\n        work_queue_.push(std::move(blocks));\n        total_submitted_ += 1;\n    }\n    cv_.notify_all();\n}\n\nbool CompressWorker::GetCompressedBuffers(std::vector<std::vector<uint8_t>>* compressed_buf) {\n    while (true) {\n        std::unique_lock<std::mutex> lock(lock_);\n        while ((total_submitted_ != total_processed_) && compressed_queue_.empty() && !stopped_) {\n            cv_.wait(lock);\n        }\n        while (compressed_queue_.size() > 0) {\n            CompressWork blocks = std::move(compressed_queue_.front());\n            compressed_queue_.pop();\n            total_processed_ += 1;\n\n            if (blocks.compression_status) {\n                compressed_buf->insert(compressed_buf->end(),\n                                       std::make_move_iterator(blocks.compressed_data.begin()),\n                                       std::make_move_iterator(blocks.compressed_data.end()));\n            } else {\n                LOG(ERROR) << \"Block compression failed\";\n                return false;\n            }\n        }\n        if ((total_submitted_ == total_processed_) || stopped_) {\n            total_submitted_ = 0;\n            total_processed_ = 0;\n            return true;\n        }\n    }\n}\n\nstd::unique_ptr<ICompressor> ICompressor::Brotli(const int32_t compression_level,\n                                                 const uint32_t block_size) {\n    return std::make_unique<BrotliCompressor>(compression_level, block_size);\n}\n\nstd::unique_ptr<ICompressor> ICompressor::Gz(const int32_t compression_level,\n                                             const uint32_t block_size) {\n    return std::make_unique<GzCompressor>(compression_level, block_size);\n}\n\nstd::unique_ptr<ICompressor> ICompressor::Lz4(const int32_t compression_level,\n                                              const uint32_t block_size) {\n    return std::make_unique<Lz4Compressor>(compression_level, block_size);\n}\n\nstd::unique_ptr<ICompressor> ICompressor::Zstd(const int32_t compression_level,\n                                               const uint32_t block_size) {\n    return std::make_unique<ZstdCompressor>(compression_level, block_size);\n}\n\nvoid CompressWorker::Finalize() {\n    {\n        std::unique_lock<std::mutex> lock(lock_);\n        stopped_ = true;\n    }\n    cv_.notify_all();\n}\n\nCompressWorker::CompressWorker(std::unique_ptr<ICompressor>&& compressor)\n    : compressor_(std::move(compressor)) {}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"cow_decompress.h\"\n\n#include <array>\n#include <cstring>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include <android-base/logging.h>\n#include <brotli/decode.h>\n#include <lz4.h>\n#include <zlib.h>\n#include <zstd.h>\n\nnamespace android {\nnamespace snapshot {\n\nssize_t IByteStream::ReadFully(void* buffer, size_t buffer_size) {\n    size_t stream_remaining = Size();\n\n    char* buffer_start = reinterpret_cast<char*>(buffer);\n    char* buffer_pos = buffer_start;\n    size_t buffer_remaining = buffer_size;\n    while (stream_remaining) {\n        const size_t to_read = std::min(buffer_remaining, stream_remaining);\n        const ssize_t actual_read = Read(buffer_pos, to_read);\n        if (actual_read < 0) {\n            return -1;\n        }\n        if (!actual_read) {\n            LOG(ERROR) << \"Stream ended prematurely\";\n            return -1;\n        }\n        CHECK_LE(actual_read, to_read);\n\n        stream_remaining -= actual_read;\n        buffer_pos += actual_read;\n        buffer_remaining -= actual_read;\n    }\n    return buffer_pos - buffer_start;\n}\n\nstd::unique_ptr<IDecompressor> IDecompressor::FromString(std::string_view compressor) {\n    if (compressor == \"lz4\") {\n        return IDecompressor::Lz4();\n    } else if (compressor == \"brotli\") {\n        return IDecompressor::Brotli();\n    } else if (compressor == \"gz\") {\n        return IDecompressor::Gz();\n    } else if (compressor == \"zstd\") {\n        return IDecompressor::Zstd();\n    } else {\n        return nullptr;\n    }\n}\n\n// Read chunks of the COW and incrementally stream them to the decoder.\nclass StreamDecompressor : public IDecompressor {\n  public:\n    ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,\n                       size_t ignore_bytes) override;\n\n    virtual bool Init() = 0;\n    virtual bool PartialDecompress(const uint8_t* data, size_t length) = 0;\n    bool OutputFull() const { return !ignore_bytes_ && !output_buffer_remaining_; }\n\n  protected:\n    size_t stream_remaining_;\n    uint8_t* output_buffer_ = nullptr;\n    size_t output_buffer_remaining_ = 0;\n    size_t ignore_bytes_ = 0;\n    bool decompressor_ended_ = false;\n};\n\nstatic constexpr size_t kChunkSize = 4096;\n\nssize_t StreamDecompressor::Decompress(void* buffer, size_t buffer_size, size_t,\n                                       size_t ignore_bytes) {\n    if (!Init()) {\n        return false;\n    }\n\n    stream_remaining_ = stream_->Size();\n    output_buffer_ = reinterpret_cast<uint8_t*>(buffer);\n    output_buffer_remaining_ = buffer_size;\n    ignore_bytes_ = ignore_bytes;\n\n    uint8_t chunk[kChunkSize];\n    while (stream_remaining_ && output_buffer_remaining_ && !decompressor_ended_) {\n        size_t max_read = std::min(stream_remaining_, sizeof(chunk));\n        ssize_t read = stream_->Read(chunk, max_read);\n        if (read < 0) {\n            return -1;\n        }\n        if (!read) {\n            LOG(ERROR) << \"Stream ended prematurely\";\n            return -1;\n        }\n        if (!PartialDecompress(chunk, read)) {\n            return -1;\n        }\n        stream_remaining_ -= read;\n    }\n\n    if (stream_remaining_) {\n        if (decompressor_ended_ && !OutputFull()) {\n            // If there's more input in the stream, but we haven't finished\n            // consuming ignored bytes or available output space yet, then\n            // something weird happened. Report it and fail.\n            LOG(ERROR) << \"Decompressor terminated early\";\n            return -1;\n        }\n    } else {\n        if (!decompressor_ended_ && !OutputFull()) {\n            // The stream ended, but the decoder doesn't think so, and there are\n            // more bytes in the output buffer.\n            LOG(ERROR) << \"Decompressor expected more bytes\";\n            return -1;\n        }\n    }\n    return buffer_size - output_buffer_remaining_;\n}\n\nclass GzDecompressor final : public StreamDecompressor {\n  public:\n    ~GzDecompressor();\n\n    bool Init() override;\n    bool PartialDecompress(const uint8_t* data, size_t length) override;\n\n  private:\n    z_stream z_ = {};\n};\n\nbool GzDecompressor::Init() {\n    if (int rv = inflateInit(&z_); rv != Z_OK) {\n        LOG(ERROR) << \"inflateInit returned error code \" << rv;\n        return false;\n    }\n    return true;\n}\n\nGzDecompressor::~GzDecompressor() {\n    inflateEnd(&z_);\n}\n\nbool GzDecompressor::PartialDecompress(const uint8_t* data, size_t length) {\n    z_.next_in = reinterpret_cast<Bytef*>(const_cast<uint8_t*>(data));\n    z_.avail_in = length;\n\n    // If we're asked to ignore starting bytes, we sink those into the output\n    // repeatedly until there is nothing left to ignore.\n    while (ignore_bytes_ && z_.avail_in) {\n        std::array<Bytef, kChunkSize> ignore_buffer;\n        size_t max_ignore = std::min(ignore_bytes_, ignore_buffer.size());\n        z_.next_out = ignore_buffer.data();\n        z_.avail_out = max_ignore;\n\n        int rv = inflate(&z_, Z_NO_FLUSH);\n        if (rv != Z_OK && rv != Z_STREAM_END) {\n            LOG(ERROR) << \"inflate returned error code \" << rv;\n            return false;\n        }\n\n        size_t returned = max_ignore - z_.avail_out;\n        CHECK_LE(returned, ignore_bytes_);\n\n        ignore_bytes_ -= returned;\n\n        if (rv == Z_STREAM_END) {\n            decompressor_ended_ = true;\n            return true;\n        }\n    }\n\n    z_.next_out = reinterpret_cast<Bytef*>(output_buffer_);\n    z_.avail_out = output_buffer_remaining_;\n\n    while (z_.avail_in && z_.avail_out) {\n        // Decompress.\n        int rv = inflate(&z_, Z_NO_FLUSH);\n        if (rv != Z_OK && rv != Z_STREAM_END) {\n            LOG(ERROR) << \"inflate returned error code \" << rv;\n            return false;\n        }\n\n        size_t returned = output_buffer_remaining_ - z_.avail_out;\n        CHECK_LE(returned, output_buffer_remaining_);\n\n        output_buffer_ += returned;\n        output_buffer_remaining_ -= returned;\n\n        if (rv == Z_STREAM_END) {\n            decompressor_ended_ = true;\n            return true;\n        }\n    }\n    return true;\n}\n\nclass BrotliDecompressor final : public StreamDecompressor {\n  public:\n    ~BrotliDecompressor();\n\n    bool Init() override;\n    bool PartialDecompress(const uint8_t* data, size_t length) override;\n\n  private:\n    BrotliDecoderState* decoder_ = nullptr;\n};\n\nbool BrotliDecompressor::Init() {\n    decoder_ = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);\n    return true;\n}\n\nBrotliDecompressor::~BrotliDecompressor() {\n    if (decoder_) {\n        BrotliDecoderDestroyInstance(decoder_);\n    }\n}\n\nbool BrotliDecompressor::PartialDecompress(const uint8_t* data, size_t length) {\n    size_t available_in = length;\n    const uint8_t* next_in = data;\n\n    while (available_in && ignore_bytes_ && !BrotliDecoderIsFinished(decoder_)) {\n        std::array<uint8_t, kChunkSize> ignore_buffer;\n        size_t max_ignore = std::min(ignore_bytes_, ignore_buffer.size());\n        size_t ignore_size = max_ignore;\n\n        uint8_t* ignore_buffer_ptr = ignore_buffer.data();\n        auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in, &ignore_size,\n                                               &ignore_buffer_ptr, nullptr);\n        if (r == BROTLI_DECODER_RESULT_ERROR) {\n            LOG(ERROR) << \"brotli decode failed\";\n            return false;\n        } else if (r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && available_in) {\n            LOG(ERROR) << \"brotli unexpected needs more input\";\n            return false;\n        }\n        ignore_bytes_ -= max_ignore - ignore_size;\n    }\n\n    while (available_in && !BrotliDecoderIsFinished(decoder_)) {\n        auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,\n                                               &output_buffer_remaining_, &output_buffer_, nullptr);\n        if (r == BROTLI_DECODER_RESULT_ERROR) {\n            LOG(ERROR) << \"brotli decode failed\";\n            return false;\n        } else if (r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && available_in) {\n            LOG(ERROR) << \"brotli unexpected needs more input\";\n            return false;\n        }\n    }\n\n    decompressor_ended_ = BrotliDecoderIsFinished(decoder_);\n    return true;\n}\n\nclass Lz4Decompressor final : public IDecompressor {\n  public:\n    ~Lz4Decompressor() override = default;\n\n    ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,\n                       size_t ignore_bytes) override {\n        std::string input_buffer(stream_->Size(), '\\0');\n        ssize_t streamed_in = stream_->ReadFully(input_buffer.data(), input_buffer.size());\n        if (streamed_in < 0) {\n            return -1;\n        }\n        CHECK_EQ(streamed_in, stream_->Size());\n\n        char* decode_buffer = reinterpret_cast<char*>(buffer);\n        size_t decode_buffer_size = buffer_size;\n\n        // It's unclear if LZ4 can exactly satisfy a partial decode request, so\n        // if we get one, create a temporary buffer.\n        std::string temp;\n        if (buffer_size < decompressed_size) {\n            temp.resize(decompressed_size, '\\0');\n            decode_buffer = temp.data();\n            decode_buffer_size = temp.size();\n        }\n\n        const int bytes_decompressed = LZ4_decompress_safe(input_buffer.data(), decode_buffer,\n                                                           input_buffer.size(), decode_buffer_size);\n        if (bytes_decompressed < 0) {\n            LOG(ERROR) << \"Failed to decompress LZ4 block, code: \" << bytes_decompressed;\n            return -1;\n        }\n        if (bytes_decompressed != decompressed_size) {\n            LOG(ERROR) << \"Failed to decompress LZ4 block, expected output size: \"\n                       << bytes_decompressed << \", actual: \" << bytes_decompressed;\n            return -1;\n        }\n        CHECK_LE(bytes_decompressed, decode_buffer_size);\n\n        if (ignore_bytes > bytes_decompressed) {\n            LOG(ERROR) << \"Ignoring more bytes than exist in stream (ignoring \" << ignore_bytes\n                       << \", got \" << bytes_decompressed << \")\";\n            return -1;\n        }\n\n        if (temp.empty()) {\n            // LZ4's API has no way to sink out the first N bytes of decoding,\n            // so we read them all in and memmove() to drop the partial read.\n            if (ignore_bytes) {\n                memmove(decode_buffer, decode_buffer + ignore_bytes,\n                        bytes_decompressed - ignore_bytes);\n            }\n            return bytes_decompressed - ignore_bytes;\n        }\n\n        size_t max_copy = std::min(bytes_decompressed - ignore_bytes, buffer_size);\n        memcpy(buffer, temp.data() + ignore_bytes, max_copy);\n        return max_copy;\n    }\n};\n\nclass ZstdDecompressor final : public IDecompressor {\n  public:\n    ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,\n                       size_t ignore_bytes = 0) override {\n        if (buffer_size < decompressed_size - ignore_bytes) {\n            LOG(INFO) << \"buffer size \" << buffer_size\n                      << \" is not large enough to hold decompressed data. Decompressed size \"\n                      << decompressed_size << \", ignore_bytes \" << ignore_bytes;\n            return -1;\n        }\n        if (ignore_bytes == 0) {\n            if (!Decompress(buffer, decompressed_size)) {\n                return -1;\n            }\n            return decompressed_size;\n        }\n        std::vector<unsigned char> ignore_buf(decompressed_size);\n        if (!Decompress(ignore_buf.data(), decompressed_size)) {\n            return -1;\n        }\n        memcpy(buffer, ignore_buf.data() + ignore_bytes, buffer_size);\n        return decompressed_size;\n    }\n    bool Decompress(void* output_buffer, const size_t output_size) {\n        std::string input_buffer;\n        input_buffer.resize(stream_->Size());\n        size_t bytes_read = stream_->Read(input_buffer.data(), input_buffer.size());\n        if (bytes_read != input_buffer.size()) {\n            LOG(ERROR) << \"Failed to read all input at once. Expected: \" << input_buffer.size()\n                       << \" actual: \" << bytes_read;\n            return false;\n        }\n        const auto bytes_decompressed = ZSTD_decompress(output_buffer, output_size,\n                                                        input_buffer.data(), input_buffer.size());\n        if (bytes_decompressed != output_size) {\n            LOG(ERROR) << \"Failed to decompress ZSTD block, expected output size: \" << output_size\n                       << \", actual: \" << bytes_decompressed;\n            return false;\n        }\n        return true;\n    }\n};\n\nstd::unique_ptr<IDecompressor> IDecompressor::Brotli() {\n    return std::make_unique<BrotliDecompressor>();\n}\n\nstd::unique_ptr<IDecompressor> IDecompressor::Gz() {\n    return std::make_unique<GzDecompressor>();\n}\n\nstd::unique_ptr<IDecompressor> IDecompressor::Lz4() {\n    return std::make_unique<Lz4Decompressor>();\n}\n\nstd::unique_ptr<IDecompressor> IDecompressor::Zstd() {\n    return std::make_unique<ZstdDecompressor>();\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/cow_decompress.h",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <libsnapshot/cow_reader.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass IByteStream {\n  public:\n    virtual ~IByteStream() {}\n\n    // Read up to |length| bytes, storing the number of bytes read in the out-\n    // parameter. If the end of the stream is reached, 0 is returned. On error,\n    // -1 is returned. errno is NOT set.\n    virtual ssize_t Read(void* buffer, size_t length) = 0;\n\n    // Size of the stream.\n    virtual size_t Size() const = 0;\n\n    // Helper for Read(). Read the entire stream into |buffer|, up to |length|\n    // bytes.\n    ssize_t ReadFully(void* buffer, size_t length);\n};\n\nclass IDecompressor {\n  public:\n    virtual ~IDecompressor() {}\n\n    // Factory methods for decompression methods.\n    static std::unique_ptr<IDecompressor> Uncompressed();\n    static std::unique_ptr<IDecompressor> Gz();\n    static std::unique_ptr<IDecompressor> Brotli();\n    static std::unique_ptr<IDecompressor> Lz4();\n    static std::unique_ptr<IDecompressor> Zstd();\n\n    static std::unique_ptr<IDecompressor> FromString(std::string_view compressor);\n\n    // Decompress at most |buffer_size| bytes, ignoring the first |ignore_bytes|\n    // of the decoded stream. |buffer_size| must be at least one byte.\n    // |decompressed_size| is the expected total size if the entire stream were\n    // decompressed.\n    //\n    // Returns the number of bytes written to |buffer|, or -1 on error. errno\n    // is NOT set.\n    virtual ssize_t Decompress(void* buffer, size_t buffer_size, size_t decompressed_size,\n                               size_t ignore_bytes = 0) = 0;\n\n    void set_stream(IByteStream* stream) { stream_ = stream; }\n\n  protected:\n    IByteStream* stream_ = nullptr;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/cow_format.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <libsnapshot/cow_format.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <libsnapshot/cow_format.h>\n#include <storage_literals/storage_literals.h>\n#include \"writer_v2.h\"\n#include \"writer_v3.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\nusing namespace android::storage_literals;\n\nstd::ostream& EmitCowTypeString(std::ostream& os, CowOperationType cow_type) {\n    switch (cow_type) {\n        case kCowCopyOp:\n            return os << \"kCowCopyOp\";\n        case kCowReplaceOp:\n            return os << \"kCowReplaceOp\";\n        case kCowZeroOp:\n            return os << \"kZeroOp\";\n        case kCowFooterOp:\n            return os << \"kCowFooterOp\";\n        case kCowLabelOp:\n            return os << \"kCowLabelOp\";\n        case kCowClusterOp:\n            return os << \"kCowClusterOp\";\n        case kCowXorOp:\n            return os << \"kCowXorOp\";\n        case kCowSequenceOp:\n            return os << \"kCowSequenceOp\";\n        default:\n            return os << (int)cow_type << \"unknown\";\n    }\n}\n\nstd::ostream& operator<<(std::ostream& os, CowOperationType cow_type) {\n    return EmitCowTypeString(os, cow_type);\n}\n\nstd::ostream& operator<<(std::ostream& os, CowOperationV2 const& op) {\n    os << \"CowOperationV2(\";\n    EmitCowTypeString(os, op.type) << \", \";\n    switch (op.compression) {\n        case kCowCompressNone:\n            os << \"uncompressed, \";\n            break;\n        case kCowCompressGz:\n            os << \"gz, \";\n            break;\n        case kCowCompressBrotli:\n            os << \"brotli, \";\n            break;\n        case kCowCompressLz4:\n            os << \"lz4, \";\n            break;\n        case kCowCompressZstd:\n            os << \"zstd, \";\n            break;\n    }\n    os << \"data_length:\" << op.data_length << \", \";\n    os << \"new_block:\" << op.new_block << \", \";\n    os << \"source:\" << op.source;\n    os << \")\";\n    return os;\n}\n\nstd::ostream& operator<<(std::ostream& os, CowOperationV3 const& op) {\n    os << \"CowOperation(\";\n    EmitCowTypeString(os, op.type());\n    if (op.type() == kCowReplaceOp || op.type() == kCowXorOp || op.type() == kCowSequenceOp) {\n        os << \", data_length:\" << op.data_length;\n    }\n    if (op.type() != kCowClusterOp && op.type() != kCowSequenceOp && op.type() != kCowLabelOp) {\n        os << \", new_block:\" << op.new_block;\n    }\n    if (op.type() == kCowXorOp || op.type() == kCowReplaceOp || op.type() == kCowCopyOp) {\n        os << \", source:\" << op.source();\n    } else if (op.type() == kCowClusterOp) {\n        os << \", cluster_data:\" << op.source();\n    }\n    // V3 op stores resume points in header, so CowOp can never be Label.\n    os << \")\";\n    return os;\n}\n\nstd::ostream& operator<<(std::ostream& os, ResumePoint const& resume_point) {\n    os << \"ResumePoint(\" << resume_point.label << \" , \" << resume_point.op_index << \")\";\n    return os;\n}\n\nint64_t GetNextOpOffset(const CowOperationV2& op, uint32_t cluster_ops) {\n    if (op.type == kCowClusterOp) {\n        return op.source;\n    } else if ((op.type == kCowReplaceOp || op.type == kCowXorOp) && cluster_ops == 0) {\n        return op.data_length;\n    } else {\n        return 0;\n    }\n}\n\nint64_t GetNextDataOffset(const CowOperationV2& op, uint32_t cluster_ops) {\n    if (op.type == kCowClusterOp) {\n        return cluster_ops * sizeof(CowOperationV2);\n    } else if (cluster_ops == 0) {\n        return sizeof(CowOperationV2);\n    } else {\n        return 0;\n    }\n}\n\nbool IsMetadataOp(const CowOperation& op) {\n    switch (op.type()) {\n        case kCowLabelOp:\n        case kCowClusterOp:\n        case kCowFooterOp:\n        case kCowSequenceOp:\n            return true;\n        default:\n            return false;\n    }\n}\n\nbool IsOrderedOp(const CowOperation& op) {\n    switch (op.type()) {\n        case kCowCopyOp:\n        case kCowXorOp:\n            return true;\n        default:\n            return false;\n    }\n}\n\nstd::unique_ptr<ICowWriter> CreateCowWriter(uint32_t version, const CowOptions& options,\n                                            unique_fd&& fd, std::optional<uint64_t> label) {\n    std::unique_ptr<CowWriterBase> base;\n    switch (version) {\n        case 1:\n        case 2:\n            base = std::make_unique<CowWriterV2>(options, std::move(fd));\n            break;\n        case 3:\n            base = std::make_unique<CowWriterV3>(options, std::move(fd));\n            break;\n        default:\n            LOG(ERROR) << \"Cannot create unknown cow version: \" << version;\n            return nullptr;\n    }\n    if (!base->Initialize(label)) {\n        return nullptr;\n    }\n    return base;\n}\n\nstd::unique_ptr<ICowWriter> CreateCowEstimator(uint32_t version, const CowOptions& options) {\n    return CreateCowWriter(version, options, unique_fd{-1}, std::nullopt);\n}\n\nsize_t CowOpCompressionSize(const CowOperation* op, size_t block_size) {\n    uint8_t compression_bits = op->compression_bits();\n    return (block_size << compression_bits);\n}\n\nbool GetBlockOffset(const CowOperation* op, uint64_t io_block, size_t block_size, off_t* offset) {\n    const uint64_t new_block = op->new_block;\n\n    if (op->type() != kCowReplaceOp || io_block < new_block) {\n        LOG(VERBOSE) << \"Invalid IO request for block: \" << io_block\n                     << \" CowOperation: new_block: \" << new_block;\n        return false;\n    }\n\n    // Get the actual compression size\n    const size_t compression_size = CowOpCompressionSize(op, block_size);\n    // Find the number of blocks spanned\n    const size_t num_blocks = compression_size / block_size;\n    // Find the distance of the I/O block which this\n    // CowOperation encompasses\n    const size_t block_distance = io_block - new_block;\n    // Check if this block is within this range;\n    // if so, return the relative offset\n    if (block_distance < num_blocks) {\n        *offset = block_distance * block_size;\n        return true;\n    }\n\n    return false;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <optional>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_reader.h>\n#include <storage_literals/storage_literals.h>\n#include <zlib.h>\n\n#include \"cow_decompress.h\"\n#include \"parser_v2.h\"\n#include \"parser_v3.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android::storage_literals;\n\nbool ReadCowHeader(android::base::borrowed_fd fd, CowHeaderV3* header) {\n    if (lseek(fd.get(), 0, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"lseek header failed\";\n        return false;\n    }\n\n    memset(header, 0, sizeof(*header));\n\n    if (!android::base::ReadFully(fd, &header->prefix, sizeof(header->prefix))) {\n        return false;\n    }\n    if (header->prefix.magic != kCowMagicNumber) {\n        LOG(ERROR) << \"Header Magic corrupted. Magic: \" << header->prefix.magic\n                   << \"Expected: \" << kCowMagicNumber;\n        return false;\n    }\n    if (header->prefix.header_size > sizeof(CowHeaderV3)) {\n        LOG(ERROR) << \"Unknown CowHeader size (got \" << header->prefix.header_size\n                   << \" bytes, expected at most \" << sizeof(CowHeaderV3) << \" bytes)\";\n        return false;\n    }\n\n    if (lseek(fd.get(), 0, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"lseek header failed\";\n        return false;\n    }\n    return android::base::ReadFully(fd, header, header->prefix.header_size);\n}\n\nCowReader::CowReader(ReaderFlags reader_flag, bool is_merge)\n    : fd_(-1),\n      header_(),\n      fd_size_(0),\n      block_pos_index_(std::make_shared<std::vector<int>>()),\n      reader_flag_(reader_flag),\n      is_merge_(is_merge) {}\n\nstd::unique_ptr<CowReader> CowReader::CloneCowReader() {\n    auto cow = std::make_unique<CowReader>();\n    cow->owned_fd_.reset();\n    cow->header_ = header_;\n    cow->footer_ = footer_;\n    cow->fd_size_ = fd_size_;\n    cow->last_label_ = last_label_;\n    cow->ops_ = ops_;\n    cow->merge_op_start_ = merge_op_start_;\n    cow->num_total_data_ops_ = num_total_data_ops_;\n    cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_;\n    cow->xor_data_loc_ = xor_data_loc_;\n    cow->block_pos_index_ = block_pos_index_;\n    cow->is_merge_ = is_merge_;\n    return cow;\n}\n\nbool CowReader::InitForMerge(android::base::unique_fd&& fd) {\n    owned_fd_ = std::move(fd);\n    fd_ = owned_fd_.get();\n\n    auto pos = lseek(fd_.get(), 0, SEEK_END);\n    if (pos < 0) {\n        PLOG(ERROR) << \"lseek end failed\";\n        return false;\n    }\n    fd_size_ = pos;\n\n    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"lseek header failed\";\n        return false;\n    }\n\n    CHECK_GE(header_.prefix.header_size, sizeof(CowHeader));\n    CHECK_LE(header_.prefix.header_size, sizeof(header_));\n\n    if (!android::base::ReadFully(fd_, &header_, header_.prefix.header_size)) {\n        PLOG(ERROR) << \"read header failed\";\n        return false;\n    }\n    return true;\n}\n\nbool CowReader::Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label) {\n    owned_fd_ = std::move(fd);\n    return Parse(android::base::borrowed_fd{owned_fd_}, label);\n}\n\nbool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {\n    fd_ = fd;\n\n    if (!ReadCowHeader(fd, &header_)) {\n        return false;\n    }\n\n    std::unique_ptr<CowParserBase> parser;\n    switch (header_.prefix.major_version) {\n        case 1:\n        case 2:\n            parser = std::make_unique<CowParserV2>();\n            break;\n        case 3:\n            parser = std::make_unique<CowParserV3>();\n            break;\n        default:\n            LOG(ERROR) << \"Unknown version: \" << header_.prefix.major_version;\n            return false;\n    }\n    if (!parser->Parse(fd, header_, label)) {\n        return false;\n    }\n\n    TranslatedCowOps ops_info;\n    if (!parser->Translate(&ops_info)) {\n        return false;\n    }\n\n    header_ = ops_info.header;\n    ops_ = std::move(ops_info.ops);\n    footer_ = parser->footer();\n    fd_size_ = parser->fd_size();\n    last_label_ = parser->last_label();\n    xor_data_loc_ = parser->xor_data_loc();\n\n    // If we're resuming a write, we're not ready to merge\n    if (label.has_value()) return true;\n    return PrepMergeOps();\n}\n\nuint32_t CowReader::GetMaxCompressionSize() {\n    switch (header_.prefix.major_version) {\n        case 1:\n        case 2:\n            // Old versions supports only 4KB compression.\n            return header_.block_size;\n            ;\n        case 3:\n            return header_.max_compression_size;\n        default:\n            LOG(ERROR) << \"Unknown version: \" << header_.prefix.major_version;\n            return 0;\n    }\n}\n\n//\n// This sets up the data needed for MergeOpIter. MergeOpIter presents\n// data in the order we intend to merge in.\n//\n// We merge all order sensitive ops up front, and sort the rest to allow for\n// batch merging. Order sensitive ops can either be presented in their proper\n// order in the cow, or be ordered by sequence ops (kCowSequenceOp), in which\n// case we want to merge those ops first, followed by any ops not specified by\n// new_block value by the sequence op, in sorted order.\n// We will re-arrange the vector in such a way that\n// kernel can batch merge. Ex:\n//\n// Existing COW format; All the copy operations\n// are at the beginning.\n// =======================================\n// Copy-op-1    - cow_op->new_block = 1\n// Copy-op-2    - cow_op->new_block = 2\n// Copy-op-3    - cow_op->new_block = 3\n// Replace-op-4 - cow_op->new_block = 6\n// Replace-op-5 - cow_op->new_block = 4\n// Replace-op-6 - cow_op->new_block = 8\n// Replace-op-7 - cow_op->new_block = 9\n// Zero-op-8    - cow_op->new_block = 7\n// Zero-op-9    - cow_op->new_block = 5\n// =======================================\n//\n// First find the operation which isn't a copy-op\n// and then sort all the operations in descending order\n// with the key being cow_op->new_block (source block)\n//\n// The data-structure will look like:\n//\n// =======================================\n// Copy-op-1    - cow_op->new_block = 1\n// Copy-op-2    - cow_op->new_block = 2\n// Copy-op-3    - cow_op->new_block = 3\n// Replace-op-7 - cow_op->new_block = 9\n// Replace-op-6 - cow_op->new_block = 8\n// Zero-op-8    - cow_op->new_block = 7\n// Replace-op-4 - cow_op->new_block = 6\n// Zero-op-9    - cow_op->new_block = 5\n// Replace-op-5 - cow_op->new_block = 4\n// =======================================\n//\n// Daemon will read the above data-structure in reverse-order\n// when reading metadata. Thus, kernel will get the metadata\n// in the following order:\n//\n// ========================================\n// Replace-op-5 - cow_op->new_block = 4\n// Zero-op-9    - cow_op->new_block = 5\n// Replace-op-4 - cow_op->new_block = 6\n// Zero-op-8    - cow_op->new_block = 7\n// Replace-op-6 - cow_op->new_block = 8\n// Replace-op-7 - cow_op->new_block = 9\n// Copy-op-3    - cow_op->new_block = 3\n// Copy-op-2    - cow_op->new_block = 2\n// Copy-op-1    - cow_op->new_block = 1\n// ===========================================\n//\n// When merging begins, kernel will start from the last\n// metadata which was read: In the above format, Copy-op-1\n// will be the first merge operation.\n//\n// Now, batching of the merge operations happens only when\n// 1: origin block numbers in the base device are contiguous\n// (cow_op->new_block) and,\n// 2: cow block numbers which are assigned by daemon in ReadMetadata()\n// are contiguous. These are monotonically increasing numbers.\n//\n// When both (1) and (2) are true, kernel will batch merge the operations.\n// In the above case, we have to ensure that the copy operations\n// are merged first before replace operations are done. Hence,\n// we will not change the order of copy operations. Since,\n// cow_op->new_block numbers are contiguous, we will ensure that the\n// cow block numbers assigned in ReadMetadata() for these respective copy\n// operations are not contiguous forcing kernel to issue merge for each\n// copy operations without batch merging.\n//\n// For all the other operations viz. Replace and Zero op, the cow block\n// numbers assigned by daemon will be contiguous allowing kernel to batch\n// merge.\n//\n// The final format after assiging COW block numbers by the daemon will\n// look something like:\n//\n// =========================================================\n// Replace-op-5 - cow_op->new_block = 4  cow-block-num = 2\n// Zero-op-9    - cow_op->new_block = 5  cow-block-num = 3\n// Replace-op-4 - cow_op->new_block = 6  cow-block-num = 4\n// Zero-op-8    - cow_op->new_block = 7  cow-block-num = 5\n// Replace-op-6 - cow_op->new_block = 8  cow-block-num = 6\n// Replace-op-7 - cow_op->new_block = 9  cow-block-num = 7\n// Copy-op-3    - cow_op->new_block = 3  cow-block-num = 9\n// Copy-op-2    - cow_op->new_block = 2  cow-block-num = 11\n// Copy-op-1    - cow_op->new_block = 1  cow-block-num = 13\n// ==========================================================\n//\n// Merge sequence will look like:\n//\n// Merge-1 - Batch-merge { Copy-op-1, Copy-op-2, Copy-op-3 }\n// Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,\n//                        Replace-op-4, Zero-op-9, Replace-op-5 }\n//==============================================================\nbool CowReader::PrepMergeOps() {\n    std::vector<int> other_ops;\n    std::vector<uint32_t> merge_op_blocks;\n    std::unordered_map<uint32_t, int> block_map;\n\n    switch (header_.prefix.major_version) {\n        case 1:\n        case 2:\n            GetSequenceDataV2(&merge_op_blocks, &other_ops, &block_map);\n            break;\n        case 3:\n            GetSequenceData(&merge_op_blocks, &other_ops, &block_map);\n            break;\n        default:\n            break;\n    }\n\n    for (auto block : merge_op_blocks) {\n        if (block_map.count(block) == 0) {\n            LOG(ERROR) << \"Invalid Sequence Ops. Could not find Cow Op for new block \" << block;\n            return false;\n        }\n    }\n\n    if (merge_op_blocks.size() > header_.num_merge_ops) {\n        num_ordered_ops_to_merge_ = merge_op_blocks.size() - header_.num_merge_ops;\n    } else {\n        num_ordered_ops_to_merge_ = 0;\n    }\n\n    // Sort the vector in increasing order if merging in user-space as\n    // we can batch merge them when iterating from forward.\n    //\n    // dm-snapshot-merge requires decreasing order as we iterate the blocks\n    // in reverse order.\n    if (reader_flag_ == ReaderFlags::USERSPACE_MERGE) {\n        std::sort(other_ops.begin(), other_ops.end());\n    } else {\n        std::sort(other_ops.begin(), other_ops.end(), std::greater<int>());\n    }\n\n    merge_op_blocks.insert(merge_op_blocks.end(), other_ops.begin(), other_ops.end());\n\n    num_total_data_ops_ = merge_op_blocks.size();\n    if (header_.num_merge_ops > 0) {\n        merge_op_start_ = header_.num_merge_ops;\n    }\n\n    if (is_merge_) {\n        // Metadata ops are not required for merge. Thus, just re-arrange\n        // the ops vector as required for merge operations.\n        auto merge_ops_buffer = std::make_shared<std::vector<CowOperation>>();\n        merge_ops_buffer->reserve(num_total_data_ops_);\n        for (auto block : merge_op_blocks) {\n            merge_ops_buffer->emplace_back(ops_->data()[block_map.at(block)]);\n        }\n        ops_->clear();\n        ops_ = merge_ops_buffer;\n        ops_->shrink_to_fit();\n    } else {\n        for (auto block : merge_op_blocks) {\n            block_pos_index_->push_back(block_map.at(block));\n        }\n    }\n\n    block_map.clear();\n    merge_op_blocks.clear();\n\n    return true;\n}\n\nbool CowReader::GetSequenceDataV2(std::vector<uint32_t>* merge_op_blocks,\n                                  std::vector<int>* other_ops,\n                                  std::unordered_map<uint32_t, int>* block_map) {\n    auto seq_ops_set = std::unordered_set<uint32_t>();\n    size_t num_seqs = 0;\n    size_t read;\n    for (size_t i = 0; i < ops_->size(); i++) {\n        auto& current_op = ops_->data()[i];\n\n        if (current_op.type() == kCowSequenceOp) {\n            size_t seq_len = current_op.data_length / sizeof(uint32_t);\n\n            merge_op_blocks->resize(merge_op_blocks->size() + seq_len);\n            if (!GetRawBytes(&current_op, &merge_op_blocks->data()[num_seqs],\n                             current_op.data_length, &read)) {\n                PLOG(ERROR) << \"Failed to read sequence op!\";\n                return false;\n            }\n            for (size_t j = num_seqs; j < num_seqs + seq_len; j++) {\n                seq_ops_set.insert(merge_op_blocks->at(j));\n            }\n            num_seqs += seq_len;\n        }\n\n        if (IsMetadataOp(current_op)) {\n            continue;\n        }\n\n        // Sequence ops must be the first ops in the stream.\n        if (seq_ops_set.empty() && IsOrderedOp(current_op)) {\n            merge_op_blocks->emplace_back(current_op.new_block);\n        } else if (seq_ops_set.count(current_op.new_block) == 0) {\n            other_ops->push_back(current_op.new_block);\n        }\n        block_map->insert({current_op.new_block, i});\n    }\n    return false;\n}\n\nbool CowReader::GetSequenceData(std::vector<uint32_t>* merge_op_blocks, std::vector<int>* other_ops,\n                                std::unordered_map<uint32_t, int>* block_map) {\n    std::unordered_set<uint32_t> seq_ops_set;\n    // read sequence ops data\n    merge_op_blocks->resize(header_.sequence_data_count);\n    if (!android::base::ReadFullyAtOffset(\n                fd_, merge_op_blocks->data(),\n                header_.sequence_data_count * sizeof(merge_op_blocks->at(0)),\n                GetSequenceOffset(header_))) {\n        PLOG(ERROR) << \"failed to read sequence buffer. seq_data_count: \"\n                    << header_.sequence_data_count << \" at offset: \" << GetSequenceOffset(header_);\n        return false;\n    }\n    seq_ops_set.reserve(merge_op_blocks->size());\n    for (auto& i : *merge_op_blocks) {\n        seq_ops_set.insert(i);\n    }\n    // read ordered op data\n    for (size_t i = 0; i < ops_->size(); i++) {\n        auto& current_op = ops_->data()[i];\n        // Sequence ops must be the first ops in the stream.\n        if (seq_ops_set.empty()) {\n            merge_op_blocks->emplace_back(current_op.new_block);\n        } else if (seq_ops_set.count(current_op.new_block) == 0) {\n            other_ops->push_back(current_op.new_block);\n        }\n        block_map->insert({current_op.new_block, i});\n    }\n    return true;\n}\n\nbool CowReader::VerifyMergeOps() {\n    auto itr = GetMergeOpIter(true);\n    std::unordered_map<uint64_t, const CowOperation*> overwritten_blocks;\n    bool non_ordered_op_found = false;\n\n    while (!itr->AtEnd()) {\n        const auto& op = itr->Get();\n        uint64_t offset;\n\n        // Op should not be a metadata\n        if (IsMetadataOp(*op)) {\n            LOG(ERROR) << \"Metadata op: \" << op << \" found during merge sequence\";\n            return false;\n        }\n\n        // Sequence ops should contain all the ordered ops followed\n        // by Replace and Zero ops. If we find the first op which\n        // is not ordered, that means all ordered ops processing\n        // has been completed.\n        if (!IsOrderedOp(*op)) {\n            non_ordered_op_found = true;\n        }\n\n        // Since, all ordered ops processing has been completed,\n        // check that the subsequent ops are not ordered.\n        if (non_ordered_op_found && IsOrderedOp(*op)) {\n            LOG(ERROR) << \"Invalid sequence - non-ordered and ordered ops\"\n                       << \" cannot be mixed during sequence generation\";\n            return false;\n        }\n\n        if (!GetSourceOffset(op, &offset)) {\n            itr->Next();\n            continue;\n        }\n\n        uint64_t block = GetBlockFromOffset(header_, offset);\n        bool misaligned = (GetBlockRelativeOffset(header_, offset) != 0);\n\n        const CowOperation* overwrite = nullptr;\n        if (overwritten_blocks.count(block)) {\n            overwrite = overwritten_blocks[block];\n            LOG(ERROR) << \"Invalid Sequence! Block needed for op:\\n\"\n                       << *op << \"\\noverwritten by previously merged op:\\n\"\n                       << *overwrite;\n        }\n        if (misaligned && overwritten_blocks.count(block + 1)) {\n            overwrite = overwritten_blocks[block + 1];\n            LOG(ERROR) << \"Invalid Sequence! Block needed for op:\\n\"\n                       << op << \"\\noverwritten by previously merged op:\\n\"\n                       << *overwrite;\n        }\n        if (overwrite != nullptr) return false;\n        overwritten_blocks[op->new_block] = op;\n        itr->Next();\n    }\n    return true;\n}\n\nbool CowReader::GetFooter(CowFooter* footer) {\n    if (!footer_) return false;\n    *footer = footer_.value();\n    return true;\n}\n\nbool CowReader::GetLastLabel(uint64_t* label) {\n    if (!last_label_) return false;\n    *label = last_label_.value();\n    return true;\n}\n\nclass CowOpIter final : public ICowOpIter {\n  public:\n    CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start);\n\n    bool AtEnd() override;\n    const CowOperation* Get() override;\n    void Next() override;\n\n    void Prev() override;\n    bool AtBegin() override;\n\n  private:\n    std::shared_ptr<std::vector<CowOperation>> ops_;\n    std::vector<CowOperation>::iterator op_iter_;\n};\n\nCowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops, uint64_t start) {\n    ops_ = ops;\n    op_iter_ = ops_->begin() + start;\n}\n\nbool CowOpIter::AtBegin() {\n    return op_iter_ == ops_->begin();\n}\n\nvoid CowOpIter::Prev() {\n    CHECK(!AtBegin());\n    op_iter_--;\n}\n\nbool CowOpIter::AtEnd() {\n    return op_iter_ == ops_->end();\n}\n\nvoid CowOpIter::Next() {\n    CHECK(!AtEnd());\n    op_iter_++;\n}\n\nconst CowOperation* CowOpIter::Get() {\n    CHECK(!AtEnd());\n    return &(*op_iter_);\n}\n\nclass CowRevMergeOpIter final : public ICowOpIter {\n  public:\n    explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,\n                               std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);\n\n    bool AtEnd() override;\n    const CowOperation* Get() override;\n    void Next() override;\n\n    void Prev() override;\n    bool AtBegin() override;\n\n  private:\n    std::shared_ptr<std::vector<CowOperation>> ops_;\n    std::vector<int>::reverse_iterator block_riter_;\n    std::shared_ptr<std::vector<int>> cow_op_index_vec_;\n    uint64_t start_;\n};\n\nclass CowMergeOpIter final : public ICowOpIter {\n  public:\n    explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,\n                            std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start);\n\n    bool AtEnd() override;\n    const CowOperation* Get() override;\n    void Next() override;\n\n    void Prev() override;\n    bool AtBegin() override;\n\n  private:\n    std::shared_ptr<std::vector<CowOperation>> ops_;\n    std::vector<int>::iterator block_iter_;\n    std::shared_ptr<std::vector<int>> cow_op_index_vec_;\n    uint64_t start_;\n};\n\nCowMergeOpIter::CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,\n                               std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start) {\n    ops_ = ops;\n    start_ = start;\n    cow_op_index_vec_ = block_pos_index;\n    block_iter_ = cow_op_index_vec_->begin() + start;\n}\n\nbool CowMergeOpIter::AtBegin() {\n    return block_iter_ == cow_op_index_vec_->begin();\n}\n\nvoid CowMergeOpIter::Prev() {\n    CHECK(!AtBegin());\n    block_iter_--;\n}\n\nbool CowMergeOpIter::AtEnd() {\n    return block_iter_ == cow_op_index_vec_->end();\n}\n\nvoid CowMergeOpIter::Next() {\n    CHECK(!AtEnd());\n    block_iter_++;\n}\n\nconst CowOperation* CowMergeOpIter::Get() {\n    CHECK(!AtEnd());\n    return &ops_->data()[*block_iter_];\n}\n\nCowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,\n                                     std::shared_ptr<std::vector<int>> block_pos_index,\n                                     uint64_t start) {\n    ops_ = ops;\n    start_ = start;\n    cow_op_index_vec_ = block_pos_index;\n    block_riter_ = cow_op_index_vec_->rbegin();\n}\n\nbool CowRevMergeOpIter::AtBegin() {\n    return block_riter_ == cow_op_index_vec_->rbegin();\n}\n\nvoid CowRevMergeOpIter::Prev() {\n    CHECK(!AtBegin());\n    block_riter_--;\n}\n\nbool CowRevMergeOpIter::AtEnd() {\n    return block_riter_ == cow_op_index_vec_->rend() - start_;\n}\n\nvoid CowRevMergeOpIter::Next() {\n    CHECK(!AtEnd());\n    block_riter_++;\n}\n\nconst CowOperation* CowRevMergeOpIter::Get() {\n    CHECK(!AtEnd());\n    return &ops_->data()[*block_riter_];\n}\n\nstd::unique_ptr<ICowOpIter> CowReader::GetOpIter(bool merge_progress) {\n    return std::make_unique<CowOpIter>(ops_, merge_progress ? merge_op_start_ : 0);\n}\n\nstd::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter(bool ignore_progress) {\n    return std::make_unique<CowRevMergeOpIter>(ops_, block_pos_index_,\n                                               ignore_progress ? 0 : merge_op_start_);\n}\n\nstd::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) {\n    return std::make_unique<CowMergeOpIter>(ops_, block_pos_index_,\n                                            ignore_progress ? 0 : merge_op_start_);\n}\n\nbool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, size_t* read) {\n    switch (op->type()) {\n        case kCowSequenceOp:\n        case kCowReplaceOp:\n        case kCowXorOp:\n            return GetRawBytes(op->source(), buffer, len, read);\n        default:\n            LOG(ERROR) << \"Cannot get raw bytes of non-data op: \" << *op;\n            return false;\n    }\n}\n\nbool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {\n    // Validate the offset, taking care to acknowledge possible overflow of offset+len.\n    if (offset < header_.prefix.header_size || offset >= fd_size_ || offset + len > fd_size_ ||\n        len >= fd_size_) {\n        LOG(ERROR) << \"invalid data offset: \" << offset << \", \" << len << \" bytes\";\n        return false;\n    }\n    if (lseek(fd_.get(), offset, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"lseek to read raw bytes failed\";\n        return false;\n    }\n    ssize_t rv = TEMP_FAILURE_RETRY(::read(fd_.get(), buffer, len));\n    if (rv < 0) {\n        PLOG(ERROR) << \"read failed\";\n        return false;\n    }\n    *read = rv;\n    return true;\n}\n\nclass CowDataStream final : public IByteStream {\n  public:\n    CowDataStream(CowReader* reader, uint64_t offset, size_t data_length)\n        : reader_(reader), offset_(offset), data_length_(data_length) {\n        remaining_ = data_length_;\n    }\n\n    ssize_t Read(void* buffer, size_t length) override {\n        size_t to_read = std::min(length, remaining_);\n        if (!to_read) {\n            return 0;\n        }\n        size_t read;\n        if (!reader_->GetRawBytes(offset_, buffer, to_read, &read)) {\n            return -1;\n        }\n        offset_ += read;\n        remaining_ -= read;\n        return read;\n    }\n\n    size_t Size() const override { return data_length_; }\n\n  private:\n    CowReader* reader_;\n    uint64_t offset_;\n    size_t data_length_;\n    size_t remaining_;\n};\n\nuint8_t CowReader::GetCompressionType() {\n    return header_.compression_algorithm;\n}\n\nssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size,\n                            size_t ignore_bytes) {\n    std::unique_ptr<IDecompressor> decompressor;\n    const size_t op_buf_size = CowOpCompressionSize(op, header_.block_size);\n    if (!op_buf_size) {\n        LOG(ERROR) << \"Compression size is zero. op: \" << *op;\n        return -1;\n    }\n    switch (GetCompressionType()) {\n        case kCowCompressNone:\n            break;\n        case kCowCompressGz:\n            decompressor = IDecompressor::Gz();\n            break;\n        case kCowCompressBrotli:\n            decompressor = IDecompressor::Brotli();\n            break;\n        case kCowCompressZstd:\n            if (op_buf_size != op->data_length) {\n                decompressor = IDecompressor::Zstd();\n            }\n            break;\n        case kCowCompressLz4:\n            if (op_buf_size != op->data_length) {\n                decompressor = IDecompressor::Lz4();\n            }\n            break;\n        default:\n            LOG(ERROR) << \"Unknown compression type: \" << GetCompressionType();\n            return -1;\n    }\n\n    uint64_t offset;\n    if (op->type() == kCowXorOp) {\n        offset = xor_data_loc_->at(op->new_block);\n    } else {\n        offset = op->source();\n    }\n    if (!decompressor ||\n        ((op->data_length == op_buf_size) && (header_.prefix.major_version == 3))) {\n        CowDataStream stream(this, offset + ignore_bytes, op->data_length - ignore_bytes);\n        return stream.ReadFully(buffer, buffer_size);\n    }\n\n    CowDataStream stream(this, offset, op->data_length);\n    decompressor->set_stream(&stream);\n    return decompressor->Decompress(buffer, buffer_size, op_buf_size, ignore_bytes);\n}\n\nbool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) {\n    switch (op->type()) {\n        case kCowCopyOp:\n            *source_offset = op->source() * header_.block_size;\n            return true;\n        case kCowXorOp:\n            *source_offset = op->source();\n            return true;\n        default:\n            return false;\n    }\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/create_cow.cpp",
    "content": "#include <linux/types.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <sys/resource.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <condition_variable>\n#include <cstring>\n#include <fstream>\n#include <future>\n#include <iostream>\n#include <limits>\n#include <mutex>\n#include <string>\n#include <thread>\n#include <unordered_map>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/hex.h>\n#include <android-base/logging.h>\n#include <android-base/scopeguard.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <android/snapshot/snapshot.pb.h>\n#include <ext4_utils/ext4_utils.h>\n#include <fs_avb/fs_avb_util.h>\n#include <gflags/gflags.h>\n#include <libsnapshot/cow_writer.h>\n#include <openssl/sha.h>\n#include <storage_literals/storage_literals.h>\n\nDEFINE_string(source, \"\", \"Source partition image\");\nDEFINE_string(target, \"\", \"Target partition image\");\nDEFINE_string(\n        output_dir, \"\",\n        \"Output directory to write the patch file to. Defaults to current working directory if \"\n        \"not set.\");\nDEFINE_string(compression, \"lz4\",\n              \"Compression algorithm. Default is set to lz4. Available options: lz4, zstd, gz\");\nDEFINE_bool(merkel_tree, false, \"If true, source image hash is obtained from verity merkel tree\");\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android::storage_literals;\nusing namespace android;\nusing android::base::unique_fd;\n\nusing android::snapshot::CreateCowWriter;\nusing android::snapshot::ICowWriter;\n\nclass CreateSnapshot {\n  public:\n    CreateSnapshot(const std::string& src_file, const std::string& target_file,\n                   const std::string& patch_file, const std::string& compression,\n                   const bool& merkel_tree);\n    bool CreateSnapshotPatch();\n\n  private:\n    /* source.img */\n    std::string src_file_;\n    /* target.img */\n    std::string target_file_;\n    /* snapshot-patch generated */\n    std::string patch_file_;\n\n    /*\n     * Active file which is being parsed by this instance.\n     * It will either be source.img or target.img.\n     */\n    std::string parsing_file_;\n    bool create_snapshot_patch_ = false;\n\n    const int kNumThreads = 6;\n    const size_t kBlockSizeToRead = 1_MiB;\n    const size_t compression_factor_ = 64_KiB;\n    size_t replace_ops_ = 0, copy_ops_ = 0, zero_ops_ = 0, in_place_ops_ = 0;\n\n    std::unordered_map<std::string, int> source_block_hash_;\n    std::mutex source_block_hash_lock_;\n\n    std::unique_ptr<ICowWriter> writer_;\n    std::mutex write_lock_;\n\n    std::unique_ptr<uint8_t[]> zblock_;\n\n    std::string compression_ = \"lz4\";\n    unique_fd cow_fd_;\n    unique_fd target_fd_;\n\n    std::vector<uint64_t> zero_blocks_;\n    std::vector<uint64_t> replace_blocks_;\n    std::unordered_map<uint64_t, uint64_t> copy_blocks_;\n\n    const int BLOCK_SZ = 4_KiB;\n    void SHA256(const void* data, size_t length, uint8_t out[32]);\n    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }\n    bool ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz);\n    std::string ToHexString(const uint8_t* buf, size_t len);\n\n    bool CreateSnapshotFile();\n    bool FindSourceBlockHash();\n    bool PrepareParse(std::string& parsing_file, const bool createSnapshot);\n    bool ParsePartition();\n    void PrepareMergeBlock(const void* buffer, uint64_t block, std::string& block_hash);\n    bool WriteV3Snapshots();\n    size_t PrepareWrite(size_t* pending_ops, size_t start_index);\n\n    bool CreateSnapshotWriter();\n    bool WriteOrderedSnapshots();\n    bool WriteNonOrderedSnapshots();\n    bool VerifyMergeOrder();\n\n    bool CalculateDigest(const void* buffer, size_t size, const void* salt, uint32_t salt_length,\n                         uint8_t* digest);\n    bool ParseSourceMerkelTree();\n\n    bool use_merkel_tree_ = false;\n    std::vector<uint8_t> target_salt_;\n    std::vector<uint8_t> source_salt_;\n};\n\nvoid CreateSnapshotLogger(android::base::LogId, android::base::LogSeverity severity, const char*,\n                          const char*, unsigned int, const char* message) {\n    if (severity == android::base::ERROR) {\n        fprintf(stderr, \"%s\\n\", message);\n    } else {\n        fprintf(stdout, \"%s\\n\", message);\n    }\n}\n\nCreateSnapshot::CreateSnapshot(const std::string& src_file, const std::string& target_file,\n                               const std::string& patch_file, const std::string& compression,\n                               const bool& merkel_tree)\n    : src_file_(src_file),\n      target_file_(target_file),\n      patch_file_(patch_file),\n      use_merkel_tree_(merkel_tree) {\n    if (!compression.empty()) {\n        compression_ = compression;\n    }\n}\n\nbool CreateSnapshot::PrepareParse(std::string& parsing_file, const bool createSnapshot) {\n    parsing_file_ = parsing_file;\n    create_snapshot_patch_ = createSnapshot;\n\n    if (createSnapshot) {\n        cow_fd_.reset(open(patch_file_.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0666));\n        if (cow_fd_ < 0) {\n            PLOG(ERROR) << \"Failed to open the snapshot-patch file: \" << patch_file_;\n            return false;\n        }\n\n        target_fd_.reset((open(parsing_file_.c_str(), O_RDONLY)));\n        if (target_fd_ < 0) {\n            LOG(ERROR) << \"open failed: \" << parsing_file_;\n            return false;\n        }\n        zblock_ = std::make_unique<uint8_t[]>(BLOCK_SZ);\n        std::memset(zblock_.get(), 0, BLOCK_SZ);\n    }\n    return true;\n}\n\n/*\n * Create per-block sha256 hash of source partition\n */\nbool CreateSnapshot::FindSourceBlockHash() {\n    if (!PrepareParse(src_file_, false)) {\n        return false;\n    }\n\n    if (use_merkel_tree_) {\n        return ParseSourceMerkelTree();\n    } else {\n        return ParsePartition();\n    }\n}\n\nbool CreateSnapshot::CalculateDigest(const void* buffer, size_t size, const void* salt,\n                                     uint32_t salt_length, uint8_t* digest) {\n    SHA256_CTX ctx;\n    if (SHA256_Init(&ctx) != 1) {\n        return false;\n    }\n    if (SHA256_Update(&ctx, salt, salt_length) != 1) {\n        return false;\n    }\n    if (SHA256_Update(&ctx, buffer, size) != 1) {\n        return false;\n    }\n    if (SHA256_Final(digest, &ctx) != 1) {\n        return false;\n    }\n    return true;\n}\n\nbool CreateSnapshot::ParseSourceMerkelTree() {\n    std::string fname = android::base::Basename(target_file_.c_str());\n    std::string partitionName = fname.substr(0, fname.find(\".img\"));\n\n    auto vbmeta = android::fs_mgr::LoadAndVerifyVbmetaByPath(\n            target_file_, partitionName, \"\", true, false, false, nullptr, nullptr, nullptr);\n    if (vbmeta == nullptr) {\n        LOG(ERROR) << \"LoadAndVerifyVbmetaByPath failed for partition: \" << partitionName;\n        return false;\n    }\n    auto descriptor = android::fs_mgr::GetHashtreeDescriptor(partitionName, std::move(*vbmeta));\n    if (descriptor == nullptr) {\n        LOG(ERROR) << \"GetHashtreeDescriptor failed for partition: \" << partitionName;\n        return false;\n    }\n\n    std::fstream input(src_file_, std::ios::in | std::ios::binary);\n    VerityHash hash;\n    if (!hash.ParseFromIstream(&input)) {\n        LOG(ERROR) << \"Failed to parse message.\";\n        return false;\n    }\n\n    std::string source_salt = hash.salt();\n    source_salt.erase(std::remove(source_salt.begin(), source_salt.end(), '\\0'), source_salt.end());\n    if (!android::base::HexToBytes(source_salt, &source_salt_)) {\n        LOG(ERROR) << \"HexToBytes conversion failed for source salt: \" << source_salt;\n        return false;\n    }\n\n    std::string target_salt = descriptor->salt;\n    if (!android::base::HexToBytes(target_salt, &target_salt_)) {\n        LOG(ERROR) << \"HexToBytes conversion failed for target salt: \" << target_salt;\n        return false;\n    }\n\n    std::vector<uint8_t> digest(32, 0);\n    for (int i = 0; i < hash.block_hash_size(); i++) {\n        CalculateDigest(hash.block_hash(i).data(), hash.block_hash(i).size(), target_salt_.data(),\n                        target_salt_.size(), digest.data());\n        source_block_hash_[ToHexString(digest.data(), 32)] = i;\n    }\n\n    return true;\n}\n\n/*\n * Create snapshot file by comparing sha256 per block\n * of target.img with the constructed per-block sha256 hash\n * of source partition.\n */\nbool CreateSnapshot::CreateSnapshotFile() {\n    if (!PrepareParse(target_file_, true)) {\n        return false;\n    }\n    return ParsePartition();\n}\n\n/*\n * Creates snapshot patch file by comparing source.img and target.img\n */\nbool CreateSnapshot::CreateSnapshotPatch() {\n    if (!FindSourceBlockHash()) {\n        return false;\n    }\n    return CreateSnapshotFile();\n}\n\nvoid CreateSnapshot::SHA256(const void* data, size_t length, uint8_t out[32]) {\n    SHA256_CTX c;\n    SHA256_Init(&c);\n    SHA256_Update(&c, data, length);\n    SHA256_Final(out, &c);\n}\n\nstd::string CreateSnapshot::ToHexString(const uint8_t* buf, size_t len) {\n    char lookup[] = \"0123456789abcdef\";\n    std::string out(len * 2 + 1, '\\0');\n    char* outp = out.data();\n    for (; len > 0; len--, buf++) {\n        *outp++ = (char)lookup[*buf >> 4];\n        *outp++ = (char)lookup[*buf & 0xf];\n    }\n    return out;\n}\n\nvoid CreateSnapshot::PrepareMergeBlock(const void* buffer, uint64_t block,\n                                       std::string& block_hash) {\n    if (std::memcmp(zblock_.get(), buffer, BLOCK_SZ) == 0) {\n        std::lock_guard<std::mutex> lock(write_lock_);\n        zero_blocks_.push_back(block);\n        return;\n    }\n\n    auto iter = source_block_hash_.find(block_hash);\n    if (iter != source_block_hash_.end()) {\n        std::lock_guard<std::mutex> lock(write_lock_);\n        // In-place copy is skipped\n        if (block != iter->second) {\n            copy_blocks_[block] = iter->second;\n        } else {\n            in_place_ops_ += 1;\n        }\n        return;\n    }\n    std::lock_guard<std::mutex> lock(write_lock_);\n    replace_blocks_.push_back(block);\n}\n\nsize_t CreateSnapshot::PrepareWrite(size_t* pending_ops, size_t start_index) {\n    size_t num_ops = *pending_ops;\n    uint64_t start_block = replace_blocks_[start_index];\n    size_t nr_consecutive = 1;\n    num_ops -= 1;\n    while (num_ops) {\n        uint64_t next_block = replace_blocks_[start_index + nr_consecutive];\n        if (next_block != start_block + nr_consecutive) {\n            break;\n        }\n        nr_consecutive += 1;\n        num_ops -= 1;\n    }\n    return nr_consecutive;\n}\n\nbool CreateSnapshot::CreateSnapshotWriter() {\n    uint64_t dev_sz = lseek(target_fd_.get(), 0, SEEK_END);\n    CowOptions options;\n    options.compression = compression_;\n    options.num_compress_threads = 2;\n    options.batch_write = true;\n    options.cluster_ops = 600;\n    options.compression_factor = compression_factor_;\n    options.max_blocks = {dev_sz / options.block_size};\n    writer_ = CreateCowWriter(3, options, std::move(cow_fd_));\n    return true;\n}\n\nbool CreateSnapshot::WriteNonOrderedSnapshots() {\n    zero_ops_ = zero_blocks_.size();\n    for (auto it = zero_blocks_.begin(); it != zero_blocks_.end(); it++) {\n        if (!writer_->AddZeroBlocks(*it, 1)) {\n            return false;\n        }\n    }\n    std::string buffer(compression_factor_, '\\0');\n\n    replace_ops_ = replace_blocks_.size();\n    size_t blocks_to_compress = replace_blocks_.size();\n    size_t num_ops = 0;\n    size_t block_index = 0;\n    while (blocks_to_compress) {\n        num_ops = std::min((compression_factor_ / BLOCK_SZ), blocks_to_compress);\n        auto linear_blocks = PrepareWrite(&num_ops, block_index);\n        if (!android::base::ReadFullyAtOffset(target_fd_.get(), buffer.data(),\n                                              (linear_blocks * BLOCK_SZ),\n                                              replace_blocks_[block_index] * BLOCK_SZ)) {\n            LOG(ERROR) << \"Failed to read at offset: \" << replace_blocks_[block_index] * BLOCK_SZ\n                       << \" size: \" << linear_blocks * BLOCK_SZ;\n            return false;\n        }\n        if (!writer_->AddRawBlocks(replace_blocks_[block_index], buffer.data(),\n                                   linear_blocks * BLOCK_SZ)) {\n            LOG(ERROR) << \"AddRawBlocks failed\";\n            return false;\n        }\n\n        block_index += linear_blocks;\n        blocks_to_compress -= linear_blocks;\n    }\n    if (!writer_->Finalize()) {\n        return false;\n    }\n    return true;\n}\nbool CreateSnapshot::WriteOrderedSnapshots() {\n    // Sort copy_blocks_ by target block index so consecutive\n    // target blocks can be together\n    std::vector<std::pair<uint64_t, uint64_t>> sorted_copy_blocks_(copy_blocks_.begin(),\n                                                                   copy_blocks_.end());\n    std::sort(sorted_copy_blocks_.begin(), sorted_copy_blocks_.end());\n    std::unordered_map<uint64_t, std::vector<uint64_t>> dependency_graph;\n    std::unordered_map<uint64_t, int> in_degree;\n\n    // Initialize in-degree and build the dependency graph\n    for (const auto& [target, source] : sorted_copy_blocks_) {\n        in_degree[target] = 0;\n        if (copy_blocks_.count(source)) {\n            // this source block itself gets modified\n            dependency_graph[source].push_back(target);\n            in_degree[target]++;\n        }\n    }\n\n    std::vector<uint64_t> ordered_copy_ops_;\n    std::deque<uint64_t> queue;\n\n    // Add nodes with in-degree 0 (no dependency) to the queue\n    for (const auto& [target, degree] : in_degree) {\n        if (degree == 0) {\n            queue.push_back(target);\n        }\n    }\n\n    while (!queue.empty()) {\n        uint64_t current_target = queue.front();\n        queue.pop_front();\n        ordered_copy_ops_.push_back(current_target);\n\n        if (dependency_graph.count(current_target)) {\n            for (uint64_t neighbor : dependency_graph[current_target]) {\n                in_degree[neighbor]--;\n                if (in_degree[neighbor] == 0) {\n                    queue.push_back(neighbor);\n                }\n            }\n        }\n    }\n\n    // Detect cycles and change those blocks to replace blocks\n    if (ordered_copy_ops_.size() != copy_blocks_.size()) {\n        LOG(INFO) << \"Cycle detected in copy operations! Converting some to replace.\";\n        std::unordered_set<uint64_t> safe_targets_(ordered_copy_ops_.begin(),\n                                                   ordered_copy_ops_.end());\n        for (const auto& [target, source] : copy_blocks_) {\n            if (safe_targets_.find(target) == safe_targets_.end()) {\n                replace_blocks_.push_back(target);\n                copy_blocks_.erase(target);\n            }\n        }\n    }\n\n    std::reverse(ordered_copy_ops_.begin(), ordered_copy_ops_.end());\n    // Add the copy blocks\n    copy_ops_ = 0;\n    for (uint64_t target : ordered_copy_ops_) {\n        LOG(DEBUG) << \"copy target: \" << target << \" source: \" << copy_blocks_[target];\n        if (!writer_->AddCopy(target, copy_blocks_[target], 1)) {\n            return false;\n        }\n        copy_ops_++;\n    }\n    // Sort the blocks so that if the blocks are contiguous, it would help\n    // compress multiple blocks in one shot based on the compression factor.\n    std::sort(replace_blocks_.begin(), replace_blocks_.end());\n    LOG(DEBUG) << \"Total copy ops: \" << copy_ops_;\n    return true;\n}\n\nbool CreateSnapshot::VerifyMergeOrder() {\n    unique_fd read_fd;\n    read_fd.reset(open(patch_file_.c_str(), O_RDONLY));\n    if (read_fd < 0) {\n        PLOG(ERROR) << \"Failed to open the snapshot-patch file: \" << patch_file_;\n        return false;\n    }\n    CowReader reader;\n    if (!reader.Parse(read_fd)) {\n        LOG(ERROR) << \"Parse failed\";\n        return false;\n    }\n\n    if (!reader.VerifyMergeOps()) {\n        LOG(ERROR) << \"MergeOps Order is wrong\";\n        return false;\n    }\n    return true;\n}\n\nbool CreateSnapshot::WriteV3Snapshots() {\n    if (!CreateSnapshotWriter()) {\n        return false;\n    }\n    if (!WriteOrderedSnapshots()) {\n        return false;\n    }\n    if (!WriteNonOrderedSnapshots()) {\n        return false;\n    }\n    if (!VerifyMergeOrder()) {\n        return false;\n    }\n\n    LOG(INFO) << \"In-place: \" << in_place_ops_ << \" Zero: \" << zero_ops_\n              << \" Replace: \" << replace_ops_ << \" copy: \" << copy_ops_;\n    return true;\n}\n\nbool CreateSnapshot::ReadBlocks(off_t offset, const int skip_blocks, const uint64_t dev_sz) {\n    unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));\n    if (fd < 0) {\n        LOG(ERROR) << \"open failed: \" << parsing_file_;\n        return false;\n    }\n\n    loff_t file_offset = offset;\n    const uint64_t read_sz = kBlockSizeToRead;\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);\n\n    while (true) {\n        size_t to_read = std::min((dev_sz - file_offset), read_sz);\n\n        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {\n            LOG(ERROR) << \"Failed to read block from block device: \" << parsing_file_\n                       << \" at offset: \" << file_offset << \" read-size: \" << to_read\n                       << \" block-size: \" << dev_sz;\n            return false;\n        }\n\n        if (!IsBlockAligned(to_read)) {\n            LOG(ERROR) << \"unable to parse the un-aligned request: \" << to_read;\n            return false;\n        }\n\n        size_t num_blocks = to_read / BLOCK_SZ;\n        uint64_t buffer_offset = 0;\n        off_t foffset = file_offset;\n\n        while (num_blocks) {\n            const void* bufptr = (char*)buffer.get() + buffer_offset;\n            uint64_t blkindex = foffset / BLOCK_SZ;\n            std::string hash;\n\n            if (create_snapshot_patch_ && use_merkel_tree_) {\n                std::vector<uint8_t> digest(32, 0);\n                CalculateDigest(bufptr, BLOCK_SZ, source_salt_.data(), source_salt_.size(),\n                                digest.data());\n                std::vector<uint8_t> final_digest(32, 0);\n                CalculateDigest(digest.data(), digest.size(), target_salt_.data(),\n                                target_salt_.size(), final_digest.data());\n\n                hash = ToHexString(final_digest.data(), final_digest.size());\n            } else {\n                uint8_t checksum[32];\n                SHA256(bufptr, BLOCK_SZ, checksum);\n                hash = ToHexString(checksum, sizeof(checksum));\n            }\n\n            if (create_snapshot_patch_) {\n                PrepareMergeBlock(bufptr, blkindex, hash);\n            } else {\n                std::lock_guard<std::mutex> lock(source_block_hash_lock_);\n                {\n                    if (source_block_hash_.count(hash) == 0) {\n                        source_block_hash_[hash] = blkindex;\n                    }\n                }\n            }\n            buffer_offset += BLOCK_SZ;\n            foffset += BLOCK_SZ;\n            num_blocks -= 1;\n        }\n\n        file_offset += (skip_blocks * to_read);\n        if (file_offset >= dev_sz) {\n            break;\n        }\n    }\n\n    return true;\n}\n\nbool CreateSnapshot::ParsePartition() {\n    unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file_.c_str(), O_RDONLY)));\n    if (fd < 0) {\n        LOG(ERROR) << \"open failed: \" << parsing_file_;\n        return false;\n    }\n\n    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);\n    if (!dev_sz) {\n        LOG(ERROR) << \"Could not determine block device size: \" << parsing_file_;\n        return false;\n    }\n\n    if (!IsBlockAligned(dev_sz)) {\n        LOG(ERROR) << \"dev_sz: \" << dev_sz << \" is not block aligned\";\n        return false;\n    }\n\n    int num_threads = kNumThreads;\n\n    std::vector<std::future<bool>> threads;\n    off_t start_offset = 0;\n    const int skip_blocks = num_threads;\n\n    while (num_threads) {\n        threads.emplace_back(std::async(std::launch::async, &CreateSnapshot::ReadBlocks, this,\n                                        start_offset, skip_blocks, dev_sz));\n        start_offset += kBlockSizeToRead;\n        num_threads -= 1;\n        if (start_offset >= dev_sz) {\n            break;\n        }\n    }\n\n    bool ret = true;\n    for (auto& t : threads) {\n        ret = t.get() && ret;\n    }\n\n    if (ret && create_snapshot_patch_ && !WriteV3Snapshots()) {\n        LOG(ERROR) << \"Snapshot Write failed\";\n        return false;\n    }\n\n    return ret;\n}\n\n}  // namespace snapshot\n}  // namespace android\n\nconstexpr char kUsage[] = R\"(\nNAME\n    create_snapshot - Create snapshot patches by comparing two partition images\n\nSYNOPSIS\n    create_snapshot --source=<source.img> --target=<target.img> --compression=\"<compression-algorithm\"\n\n    source.img -> Source partition image\n    target.img -> Target partition image\n    compression -> compression algorithm. Default set to lz4. Supported types are gz, lz4, zstd.\n    merkel_tree -> If true, source image hash is obtained from verity merkel tree.\n    output_dir -> Output directory to write the patch file to. Defaults to current working directory if not set.\n\nEXAMPLES\n\n   $ create_snapshot $SOURCE_BUILD/system.img $TARGET_BUILD/system.img\n   $ create_snapshot $SOURCE_BUILD/product.img $TARGET_BUILD/product.img --compression=\"zstd\"\n   $ create_snapshot $SOURCE_BUILD/product.img $TARGET_BUILD/product.img --merkel_tree --output_dir=/tmp/create_snapshot_output\n\n)\";\n\nint main(int argc, char* argv[]) {\n    android::base::InitLogging(argv, &android::snapshot::CreateSnapshotLogger);\n    ::gflags::SetUsageMessage(kUsage);\n    ::gflags::ParseCommandLineFlags(&argc, &argv, true);\n\n    if (FLAGS_source.empty() || FLAGS_target.empty()) {\n        LOG(INFO) << kUsage;\n        return 0;\n    }\n\n    std::string fname = android::base::Basename(FLAGS_target.c_str());\n    auto parts = android::base::Split(fname, \".\");\n    std::string snapshotfile = parts[0] + \".patch\";\n    if (!FLAGS_output_dir.empty()) {\n        snapshotfile = FLAGS_output_dir + \"/\" + snapshotfile;\n    }\n    android::snapshot::CreateSnapshot snapshot(FLAGS_source, FLAGS_target, snapshotfile,\n                                               FLAGS_compression, FLAGS_merkel_tree);\n\n    if (!snapshot.CreateSnapshotPatch()) {\n        LOG(ERROR) << \"Snapshot creation failed\";\n        return -1;\n    }\n\n    LOG(INFO) << \"Snapshot patch: \" << snapshotfile << \" created successfully\";\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/inspect_cow.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#include <stdio.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <iomanip>\n#include <iostream>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <gflags/gflags.h>\n#include <libsnapshot/cow_reader.h>\n#include \"parser_v2.h\"\n\nDEFINE_bool(silent, false, \"Run silently\");\nDEFINE_bool(decompress, false, \"Attempt to decompress data ops\");\nDEFINE_bool(show_bad_data, false, \"If an op fails to decompress, show its daw data\");\nDEFINE_bool(show_ops, false, \"Print all opcode information\");\nDEFINE_string(order, \"\", \"If show_ops is true, change the order (either merge or reverse-merge)\");\nDEFINE_bool(show_merged, false,\n            \"If show_ops is true, and order is merge or reverse-merge, include merged ops\");\nDEFINE_bool(verify_merge_sequence, false, \"Verify merge order sequencing\");\nDEFINE_bool(show_merge_sequence, false, \"Show merge order sequence\");\nDEFINE_bool(show_raw_ops, false, \"Show raw ops directly from the underlying parser\");\nDEFINE_string(extract_to, \"\", \"Extract the COW contents to the given file\");\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::borrowed_fd;\nusing android::base::unique_fd;\n\nvoid MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,\n              unsigned int, const char* message) {\n    if (severity == android::base::ERROR) {\n        fprintf(stderr, \"%s\\n\", message);\n    } else {\n        fprintf(stdout, \"%s\\n\", message);\n    }\n}\n\nstatic void ShowBad(CowReader& reader, const CowOperation* op) {\n    size_t count;\n    auto buffer = std::make_unique<uint8_t[]>(op->data_length);\n\n    if (!reader.GetRawBytes(op, buffer.get(), op->data_length, &count)) {\n        std::cerr << \"Failed to read at all!\\n\";\n    } else {\n        std::cout << \"The Block data is:\\n\";\n        for (int i = 0; i < op->data_length; i++) {\n            std::cout << std::hex << (int)buffer[i];\n        }\n        std::cout << std::dec << \"\\n\\n\";\n        if (op->data_length >= sizeof(CowOperation)) {\n            std::cout << \"The start, as an op, would be \" << *(CowOperation*)buffer.get() << \"\\n\";\n        }\n    }\n}\n\nstatic bool ShowRawOpStreamV2(borrowed_fd fd, const CowHeaderV3& header) {\n    CowParserV2 parser;\n    if (!parser.Parse(fd, header)) {\n        LOG(ERROR) << \"v2 parser failed\";\n        return false;\n    }\n    for (const auto& op : *parser.get_v2ops()) {\n        std::cout << op << \"\\n\";\n        if (auto iter = parser.xor_data_loc()->find(op.new_block);\n            iter != parser.xor_data_loc()->end()) {\n            std::cout << \"    data loc: \" << iter->second << \"\\n\";\n        }\n    }\n    return true;\n}\n\nstatic bool ShowRawOpStream(borrowed_fd fd) {\n    CowHeaderV3 header;\n    if (!ReadCowHeader(fd, &header)) {\n        LOG(ERROR) << \"parse header failed\";\n        return false;\n    }\n\n    switch (header.prefix.major_version) {\n        case 1:\n        case 2:\n            return ShowRawOpStreamV2(fd, header);\n        default:\n            LOG(ERROR) << \"unknown COW version: \" << header.prefix.major_version;\n            return false;\n    }\n}\n\nstatic bool Inspect(const std::string& path) {\n    unique_fd fd(open(path.c_str(), O_RDONLY));\n    if (fd < 0) {\n        PLOG(ERROR) << \"open failed: \" << path;\n        return false;\n    }\n\n    unique_fd extract_to;\n    if (!FLAGS_extract_to.empty()) {\n        extract_to.reset(open(FLAGS_extract_to.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));\n        if (extract_to < 0) {\n            PLOG(ERROR) << \"could not open \" << FLAGS_extract_to << \" for writing\";\n            return false;\n        }\n    }\n\n    CowReader reader;\n\n    auto start_time = std::chrono::steady_clock::now();\n    if (!reader.Parse(fd)) {\n        LOG(ERROR) << \"parse failed: \" << path;\n        return false;\n    }\n    std::chrono::duration<double> parse_time = std::chrono::steady_clock::now() - start_time;\n\n    const CowHeader& header = reader.GetHeader();\n    CowFooter footer;\n    bool has_footer = false;\n    if (reader.GetFooter(&footer)) has_footer = true;\n\n    if (!FLAGS_silent) {\n        std::cout << \"Version: \" << header.prefix.major_version << \".\"\n                  << header.prefix.minor_version << \"\\n\";\n        std::cout << \"Header size: \" << header.prefix.header_size << \"\\n\";\n        std::cout << \"Footer size: \" << header.footer_size << \"\\n\";\n        std::cout << \"Block size: \" << header.block_size << \"\\n\";\n        std::cout << \"Merge ops: \" << header.num_merge_ops << \"\\n\";\n        std::cout << \"Readahead buffer: \" << header.buffer_size << \" bytes\\n\";\n        if (has_footer) {\n            std::cout << \"Footer: ops usage: \" << footer.op.ops_size << \" bytes\\n\";\n            std::cout << \"Footer: op count: \" << footer.op.num_ops << \"\\n\";\n        } else {\n            std::cout << \"Footer: none\\n\";\n        }\n    }\n\n    if (!FLAGS_silent) {\n        std::cout << \"Parse time: \" << (parse_time.count() * 1000) << \"ms\\n\";\n    }\n\n    if (FLAGS_verify_merge_sequence) {\n        std::cout << \"\\n\";\n        if (reader.VerifyMergeOps()) {\n            std::cout << \"\\nMerge sequence is consistent.\\n\";\n        } else {\n            std::cout << \"\\nMerge sequence is inconsistent!\\n\";\n        }\n    }\n\n    std::unique_ptr<ICowOpIter> iter;\n    if (FLAGS_order.empty()) {\n        iter = reader.GetOpIter();\n    } else if (FLAGS_order == \"reverse-merge\") {\n        iter = reader.GetRevMergeOpIter(FLAGS_show_merged);\n    } else if (FLAGS_order == \"merge\") {\n        iter = reader.GetMergeOpIter(FLAGS_show_merged);\n    }\n\n    std::string buffer(header.block_size, '\\0');\n\n    if (!FLAGS_silent && FLAGS_show_raw_ops) {\n        std::cout << \"\\n\";\n        std::cout << \"Listing raw op stream:\\n\";\n        std::cout << \"----------------------\\n\";\n        if (!ShowRawOpStream(fd)) {\n            return false;\n        }\n    }\n\n    if (!FLAGS_silent && FLAGS_show_ops) {\n        std::cout << \"\\n\";\n        std::cout << \"Listing op stream:\\n\";\n        std::cout << \"------------------\\n\";\n    }\n\n    bool success = true;\n    uint64_t xor_ops = 0, copy_ops = 0, replace_ops = 0, zero_ops = 0;\n    while (!iter->AtEnd()) {\n        const CowOperation* op = iter->Get();\n\n        if (!FLAGS_silent && FLAGS_show_ops) std::cout << *op << \"\\n\";\n\n        if ((FLAGS_decompress || extract_to >= 0) && op->type() == kCowReplaceOp) {\n            if (reader.ReadData(op, buffer.data(), buffer.size()) < 0) {\n                std::cerr << \"Failed to decompress for :\" << *op << \"\\n\";\n                success = false;\n                if (FLAGS_show_bad_data) ShowBad(reader, op);\n            }\n            if (extract_to >= 0) {\n                off_t offset = uint64_t(op->new_block) * header.block_size;\n                if (!android::base::WriteFullyAtOffset(extract_to, buffer.data(), buffer.size(),\n                                                       offset)) {\n                    PLOG(ERROR) << \"failed to write block \" << op->new_block;\n                    return false;\n                }\n            }\n        } else if (extract_to >= 0 && !IsMetadataOp(*op) && op->type() != kCowZeroOp) {\n            PLOG(ERROR) << \"Cannot extract op yet: \" << *op;\n            return false;\n        }\n\n        if (op->type() == kCowSequenceOp && FLAGS_show_merge_sequence) {\n            size_t read;\n            std::vector<uint32_t> merge_op_blocks;\n            size_t seq_len = op->data_length / sizeof(uint32_t);\n            merge_op_blocks.resize(seq_len);\n            if (!reader.GetRawBytes(op, merge_op_blocks.data(), op->data_length, &read)) {\n                PLOG(ERROR) << \"Failed to read sequence op!\";\n                return false;\n            }\n            if (!FLAGS_silent) {\n                std::cout << \"Sequence for \" << *op << \" is :\\n\";\n                for (size_t i = 0; i < seq_len; i++) {\n                    std::cout << std::setfill('0') << std::setw(6) << merge_op_blocks[i] << \", \";\n                    if ((i + 1) % 10 == 0 || i + 1 == seq_len) std::cout << \"\\n\";\n                }\n            }\n        }\n\n        if (op->type() == kCowCopyOp) {\n            copy_ops++;\n        } else if (op->type() == kCowReplaceOp) {\n            replace_ops++;\n        } else if (op->type() == kCowZeroOp) {\n            zero_ops++;\n        } else if (op->type() == kCowXorOp) {\n            xor_ops++;\n        }\n\n        iter->Next();\n    }\n\n    if (!FLAGS_silent) {\n        auto total_ops = replace_ops + zero_ops + copy_ops + xor_ops;\n        std::cout << \"Data ops: \" << total_ops << \"\\n\";\n        std::cout << \"Replace ops: \" << replace_ops << \"\\n\";\n        std::cout << \"Zero ops: \" << zero_ops << \"\\n\";\n        std::cout << \"Copy ops: \" << copy_ops << \"\\n\";\n        std::cout << \"Xor ops: \" << xor_ops << \"\\n\";\n    }\n\n    return success;\n}\n\n}  // namespace snapshot\n}  // namespace android\n\nint main(int argc, char** argv) {\n    gflags::ParseCommandLineFlags(&argc, &argv, true);\n\n    if (argc < 2) {\n        gflags::ShowUsageWithFlags(argv[0]);\n        return 1;\n    }\n    if (FLAGS_order != \"\" && FLAGS_order != \"merge\" && FLAGS_order != \"reverse-merge\") {\n        std::cerr << \"Order must either be \\\"merge\\\" or \\\"reverse-merge\\\".\\n\";\n        return 1;\n    }\n\n    android::base::InitLogging(argv, android::snapshot::MyLogger);\n\n    if (!android::snapshot::Inspect(argv[1])) {\n        return 1;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/parser_base.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <optional>\n#include <unordered_map>\n\n#include <android-base/unique_fd.h>\n#include <libsnapshot/cow_format.h>\n\nnamespace android {\nnamespace snapshot {\n\nstruct TranslatedCowOps {\n    CowHeaderV3 header;\n    std::shared_ptr<std::vector<CowOperationV3>> ops;\n};\n\nclass CowParserBase {\n  public:\n    virtual ~CowParserBase() = default;\n\n    virtual bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,\n                       std::optional<uint64_t> label = {}) = 0;\n    virtual bool Translate(TranslatedCowOps* out) = 0;\n    virtual std::optional<CowFooter> footer() const { return std::nullopt; }\n    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc() {\n        return xor_data_loc_;\n    };\n\n    uint64_t fd_size() const { return fd_size_; }\n    const std::optional<uint64_t>& last_label() const { return last_label_; }\n\n  protected:\n    CowHeaderV3 header_ = {};\n    uint64_t fd_size_;\n    std::optional<uint64_t> last_label_;\n    std::shared_ptr<std::unordered_map<uint64_t, uint64_t>> xor_data_loc_ = {};\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n#include \"parser_v2.h\"\n\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n\n#include <libsnapshot/cow_format.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::borrowed_fd;\n\nbool CowParserV2::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {\n    auto pos = lseek(fd.get(), 0, SEEK_END);\n    if (pos < 0) {\n        PLOG(ERROR) << \"lseek end failed\";\n        return false;\n    }\n    fd_size_ = pos;\n    header_ = header;\n\n    if (header_.footer_size != sizeof(CowFooter)) {\n        LOG(ERROR) << \"Footer size unknown, read \" << header_.footer_size << \", expected \"\n                   << sizeof(CowFooter);\n        return false;\n    }\n    if (header_.op_size != sizeof(CowOperationV2)) {\n        LOG(ERROR) << \"Operation size unknown, read \" << header_.op_size << \", expected \"\n                   << sizeof(CowOperationV2);\n        return false;\n    }\n    if (header_.cluster_ops == 1) {\n        LOG(ERROR) << \"Clusters must contain at least two operations to function.\";\n        return false;\n    }\n\n    if (header_.prefix.major_version > 2 || header_.prefix.minor_version != 0) {\n        LOG(ERROR) << \"Header version mismatch, \"\n                   << \"major version: \" << header_.prefix.major_version\n                   << \", expected: \" << kCowVersionMajor\n                   << \", minor version: \" << header_.prefix.minor_version\n                   << \", expected: \" << kCowVersionMinor;\n        return false;\n    }\n\n    return ParseOps(fd, label);\n}\n\nbool CowParserV2::ParseOps(borrowed_fd fd, std::optional<uint64_t> label) {\n    uint64_t pos;\n    auto xor_data_loc = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();\n\n    // Skip the scratch space\n    if (header_.prefix.major_version >= 2 && (header_.buffer_size > 0)) {\n        LOG(DEBUG) << \" Scratch space found of size: \" << header_.buffer_size;\n        size_t init_offset = header_.prefix.header_size + header_.buffer_size;\n        pos = lseek(fd.get(), init_offset, SEEK_SET);\n        if (pos != init_offset) {\n            PLOG(ERROR) << \"lseek ops failed\";\n            return false;\n        }\n    } else {\n        pos = lseek(fd.get(), header_.prefix.header_size, SEEK_SET);\n        if (pos != header_.prefix.header_size) {\n            PLOG(ERROR) << \"lseek ops failed\";\n            return false;\n        }\n        // Reading a v1 version of COW which doesn't have buffer_size.\n        header_.buffer_size = 0;\n    }\n    uint64_t data_pos = 0;\n\n    if (header_.cluster_ops) {\n        data_pos = pos + header_.cluster_ops * sizeof(CowOperationV2);\n    } else {\n        data_pos = pos + sizeof(CowOperationV2);\n    }\n\n    auto ops_buffer = std::make_shared<std::vector<CowOperationV2>>();\n    uint64_t current_op_num = 0;\n    uint64_t cluster_ops = header_.cluster_ops ?: 1;\n    bool done = false;\n\n    // Alternating op clusters and data\n    while (!done) {\n        uint64_t to_add = std::min(cluster_ops, (fd_size_ - pos) / sizeof(CowOperationV2));\n        if (to_add == 0) break;\n        ops_buffer->resize(current_op_num + to_add);\n        if (!android::base::ReadFully(fd, &ops_buffer->data()[current_op_num],\n                                      to_add * sizeof(CowOperationV2))) {\n            PLOG(ERROR) << \"read op failed\";\n            return false;\n        }\n        // Parse current cluster to find start of next cluster\n        while (current_op_num < ops_buffer->size()) {\n            auto& current_op = ops_buffer->data()[current_op_num];\n            current_op_num++;\n            if (current_op.type == kCowXorOp) {\n                xor_data_loc->insert({current_op.new_block, data_pos});\n            }\n            pos += sizeof(CowOperationV2) + GetNextOpOffset(current_op, header_.cluster_ops);\n            data_pos += current_op.data_length + GetNextDataOffset(current_op, header_.cluster_ops);\n\n            if (current_op.type == kCowClusterOp) {\n                break;\n            } else if (current_op.type == kCowLabelOp) {\n                last_label_ = {current_op.source};\n\n                // If we reach the requested label, stop reading.\n                if (label && label.value() == current_op.source) {\n                    done = true;\n                    break;\n                }\n            } else if (current_op.type == kCowFooterOp) {\n                footer_.emplace();\n                CowFooter* footer = &footer_.value();\n                memcpy(&footer_->op, &current_op, sizeof(footer->op));\n                off_t offs = lseek(fd.get(), pos, SEEK_SET);\n                if (offs < 0 || pos != static_cast<uint64_t>(offs)) {\n                    PLOG(ERROR) << \"lseek next op failed \" << offs;\n                    return false;\n                }\n                if (!android::base::ReadFully(fd, &footer->unused, sizeof(footer->unused))) {\n                    LOG(ERROR) << \"Could not read COW footer\";\n                    return false;\n                }\n\n                // Drop the footer from the op stream.\n                current_op_num--;\n                done = true;\n                break;\n            }\n        }\n\n        // Position for next cluster read\n        off_t offs = lseek(fd.get(), pos, SEEK_SET);\n        if (offs < 0 || pos != static_cast<uint64_t>(offs)) {\n            PLOG(ERROR) << \"lseek next op failed \" << offs;\n            return false;\n        }\n        ops_buffer->resize(current_op_num);\n    }\n\n    LOG(DEBUG) << \"COW file read complete. Total ops: \" << ops_buffer->size();\n    // To successfully parse a COW file, we need either:\n    //  (1) a label to read up to, and for that label to be found, or\n    //  (2) a valid footer.\n    if (label) {\n        if (!last_label_) {\n            LOG(ERROR) << \"Did not find label \" << label.value()\n                       << \" while reading COW (no labels found)\";\n            return false;\n        }\n        if (last_label_.value() != label.value()) {\n            LOG(ERROR) << \"Did not find label \" << label.value()\n                       << \", last label=\" << last_label_.value();\n            return false;\n        }\n    } else if (!footer_) {\n        LOG(ERROR) << \"No COW footer found\";\n        return false;\n    }\n\n    uint8_t csum[32];\n    memset(csum, 0, sizeof(uint8_t) * 32);\n\n    if (footer_) {\n        if (ops_buffer->size() != footer_->op.num_ops) {\n            LOG(ERROR) << \"num ops does not match, expected \" << footer_->op.num_ops << \", found \"\n                       << ops_buffer->size();\n            return false;\n        }\n        if (ops_buffer->size() * sizeof(CowOperationV2) != footer_->op.ops_size) {\n            LOG(ERROR) << \"ops size does not match \";\n            return false;\n        }\n    }\n\n    v2_ops_ = ops_buffer;\n    v2_ops_->shrink_to_fit();\n    xor_data_loc_ = xor_data_loc;\n    return true;\n}\n\nbool CowParserV2::Translate(TranslatedCowOps* out) {\n    out->ops = std::make_shared<std::vector<CowOperationV3>>(v2_ops_->size());\n\n    // Translate the operation buffer from on disk to in memory\n    for (size_t i = 0; i < out->ops->size(); i++) {\n        const auto& v2_op = v2_ops_->at(i);\n\n        auto& new_op = out->ops->at(i);\n        new_op.set_type(v2_op.type);\n        // v2 ops always have 4k compression\n        new_op.set_compression_bits(0);\n        new_op.data_length = v2_op.data_length;\n\n        if (v2_op.new_block > std::numeric_limits<uint32_t>::max()) {\n            LOG(ERROR) << \"Out-of-range new block in COW op: \" << v2_op;\n            return false;\n        }\n        new_op.new_block = v2_op.new_block;\n\n        uint64_t source_info = v2_op.source;\n        if (new_op.type() != kCowLabelOp) {\n            source_info &= kCowOpSourceInfoDataMask;\n            if (source_info != v2_op.source) {\n                LOG(ERROR) << \"Out-of-range source value in COW op: \" << v2_op;\n                return false;\n            }\n        }\n        if (v2_op.compression != kCowCompressNone) {\n            if (header_.compression_algorithm == kCowCompressNone) {\n                header_.compression_algorithm = v2_op.compression;\n            } else if (header_.compression_algorithm != v2_op.compression) {\n                LOG(ERROR) << \"COW has mixed compression types which is not supported;\"\n                           << \" previously saw \" << header_.compression_algorithm << \", got \"\n                           << v2_op.compression << \", op: \" << v2_op;\n                return false;\n            }\n        }\n        new_op.set_source(source_info);\n    }\n\n    out->header = header_;\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/parser_v2.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n#pragma once\n\n#include <stdint.h>\n\n#include <memory>\n#include <optional>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot_cow/parser_base.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass CowParserV2 final : public CowParserBase {\n  public:\n    bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,\n               std::optional<uint64_t> label = {}) override;\n    bool Translate(TranslatedCowOps* out) override;\n    std::optional<CowFooter> footer() const override { return footer_; }\n\n    const CowHeader& header() const { return header_; }\n    std::shared_ptr<std::vector<CowOperationV2>> get_v2ops() { return v2_ops_; }\n\n  private:\n    bool ParseOps(android::base::borrowed_fd fd, std::optional<uint64_t> label);\n    std::shared_ptr<std::vector<CowOperationV2>> v2_ops_;\n    std::optional<CowFooter> footer_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n#include \"parser_v3.h\"\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_reader.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::borrowed_fd;\n\nbool CowParserV3::Parse(borrowed_fd fd, const CowHeaderV3& header, std::optional<uint64_t> label) {\n    auto pos = lseek(fd.get(), 0, SEEK_END);\n    if (pos < 0) {\n        PLOG(ERROR) << \"lseek end failed\";\n        return false;\n    }\n    fd_size_ = pos;\n    header_ = header;\n\n    if (header_.footer_size != 0) {\n        LOG(ERROR) << \"Footer size isn't 0, read \" << header_.footer_size;\n        return false;\n    }\n\n    if (header_.op_size != sizeof(CowOperationV3)) {\n        LOG(ERROR) << \"Operation size unknown, read \" << header_.op_size << \", expected \"\n                   << sizeof(CowOperationV3);\n        return false;\n    }\n    if (header_.cluster_ops != 0) {\n        LOG(ERROR) << \"Cluster ops not supported in v3\";\n        return false;\n    }\n\n    if (header_.prefix.major_version != 3 || header_.prefix.minor_version != 0) {\n        LOG(ERROR) << \"Header version mismatch, \"\n                   << \"major version: \" << header_.prefix.major_version\n                   << \", expected: \" << kCowVersionMajor\n                   << \", minor version: \" << header_.prefix.minor_version\n                   << \", expected: \" << kCowVersionMinor;\n        return false;\n    }\n\n    std::optional<uint32_t> op_index = header_.op_count;\n    if (label) {\n        if (!ReadResumeBuffer(fd)) {\n            PLOG(ERROR) << \"Failed to read resume buffer\";\n            return false;\n        }\n        op_index = FindResumeOp(label.value());\n        if (op_index == std::nullopt) {\n            LOG(ERROR) << \"failed to get op index from given label: \" << label.value();\n            return false;\n        }\n    }\n\n    return ParseOps(fd, op_index.value());\n}\n\nbool CowParserV3::ReadResumeBuffer(borrowed_fd fd) {\n    resume_points_ = std::make_shared<std::vector<ResumePoint>>(header_.resume_point_count);\n\n    return android::base::ReadFullyAtOffset(fd, resume_points_->data(),\n                                            header_.resume_point_count * sizeof(ResumePoint),\n                                            GetResumeOffset(header_));\n}\n\nstd::optional<uint32_t> CowParserV3::FindResumeOp(const uint64_t label) {\n    for (auto& resume_point : *resume_points_) {\n        if (resume_point.label == label) {\n            return resume_point.op_index;\n        }\n    }\n    LOG(ERROR) << \"failed to find label: \" << label << \"from following labels\";\n    LOG(ERROR) << android::base::Join(*resume_points_, \" \");\n\n    return std::nullopt;\n}\n\nbool CowParserV3::ParseOps(borrowed_fd fd, const uint32_t op_index) {\n    ops_ = std::make_shared<std::vector<CowOperationV3>>();\n    ops_->resize(op_index);\n\n    // read beginning of operation buffer -> so op_index = 0\n    const off_t offset = GetOpOffset(0, header_);\n    if (!android::base::ReadFullyAtOffset(fd, ops_->data(), ops_->size() * sizeof(CowOperationV3),\n                                          offset)) {\n        PLOG(ERROR) << \"read ops failed\";\n        return false;\n    }\n\n    // fill out mapping of XOR op data location\n    uint64_t data_pos = GetDataOffset(header_);\n\n    xor_data_loc_ = std::make_shared<std::unordered_map<uint64_t, uint64_t>>();\n\n    for (auto op : *ops_) {\n        if (op.type() == kCowXorOp) {\n            xor_data_loc_->insert({op.new_block, data_pos});\n        } else if (op.type() == kCowReplaceOp) {\n            if (data_pos != op.source()) {\n                LOG(ERROR) << \"Invalid data location for operation \" << op\n                           << \", expected: \" << data_pos;\n                return false;\n            }\n        }\n        data_pos += op.data_length;\n    }\n    // :TODO: sequence buffer & resume buffer follow\n    // Once we implement labels, we'll have to discard unused ops and adjust\n    // the header as needed.\n\n    ops_->shrink_to_fit();\n\n    return true;\n}\n\nbool CowParserV3::Translate(TranslatedCowOps* out) {\n    out->ops = ops_;\n    out->header = header_;\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/parser_v3.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n#pragma once\n\n#include <stdint.h>\n\n#include <memory>\n#include <optional>\n#include <unordered_map>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot_cow/parser_base.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass CowParserV3 final : public CowParserBase {\n  public:\n    bool Parse(android::base::borrowed_fd fd, const CowHeaderV3& header,\n               std::optional<uint64_t> label = {}) override;\n    bool Translate(TranslatedCowOps* out) override;\n    std::shared_ptr<std::vector<ResumePoint>> resume_points() const { return resume_points_; }\n\n  private:\n    bool ParseOps(android::base::borrowed_fd fd, const uint32_t op_index);\n    std::optional<uint32_t> FindResumeOp(const uint64_t label);\n    CowHeaderV3 header_ = {};\n    std::shared_ptr<std::vector<CowOperationV3>> ops_;\n    bool ReadResumeBuffer(android::base::borrowed_fd fd);\n    std::shared_ptr<std::vector<ResumePoint>> resume_points_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"snapshot_reader.h\"\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::borrowed_fd;\n\nCompressedSnapshotReader::CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,\n                                                   const std::optional<std::string>& source_device,\n                                                   std::optional<uint64_t> block_dev_size)\n    : cow_(std::move(cow)),\n      block_size_(cow_->GetHeader().block_size),\n      source_device_(source_device),\n      block_device_size_(block_dev_size.value_or(0)) {\n    const auto& header = cow_->GetHeader();\n    block_size_ = header.block_size;\n\n    // Populate the operation map.\n    op_iter_ = cow_->GetOpIter(false);\n    while (!op_iter_->AtEnd()) {\n        const CowOperation* op = op_iter_->Get();\n        if (IsMetadataOp(*op)) {\n            op_iter_->Next();\n            continue;\n        }\n\n        size_t num_blocks = 1;\n        if (op->type() == kCowReplaceOp) {\n            num_blocks = (CowOpCompressionSize(op, block_size_) / block_size_);\n        }\n        if (op->new_block >= ops_.size()) {\n            ops_.resize(op->new_block + num_blocks, nullptr);\n        }\n\n        size_t vec_index = op->new_block;\n        while (num_blocks) {\n            ops_[vec_index] = op;\n            num_blocks -= 1;\n            vec_index += 1;\n        }\n        op_iter_->Next();\n    }\n}\n\n// Not supported.\nbool CompressedSnapshotReader::Open(const char*, int, mode_t) {\n    errno = EINVAL;\n    return false;\n}\n\nbool CompressedSnapshotReader::Open(const char*, int) {\n    errno = EINVAL;\n    return false;\n}\n\nssize_t CompressedSnapshotReader::Write(const void*, size_t) {\n    errno = EINVAL;\n    return false;\n}\n\nbool CompressedSnapshotReader::BlkIoctl(int, uint64_t, uint64_t, int*) {\n    errno = EINVAL;\n    return false;\n}\n\nborrowed_fd CompressedSnapshotReader::GetSourceFd() {\n    if (source_fd_ < 0) {\n        if (!source_device_) {\n            LOG(ERROR) << \"CompressedSnapshotReader needs source device, but none was set\";\n            errno = EINVAL;\n            return {-1};\n        }\n        source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));\n        if (source_fd_ < 0) {\n            PLOG(ERROR) << \"open \" << *source_device_;\n            return {-1};\n        }\n    }\n    return source_fd_;\n}\n\nssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {\n    // Find the start and end chunks, inclusive.\n    uint64_t start_chunk = offset_ / block_size_;\n    uint64_t end_chunk = (offset_ + count - 1) / block_size_;\n\n    // Chop off the first N bytes if the position is not block-aligned.\n    size_t start_offset = offset_ % block_size_;\n\n    uint8_t* buf_pos = reinterpret_cast<uint8_t*>(buf);\n    size_t buf_remaining = count;\n\n    size_t initial_bytes = std::min(block_size_ - start_offset, buf_remaining);\n    ssize_t rv = ReadBlock(start_chunk, start_offset, buf_pos, initial_bytes);\n    if (rv < 0) {\n        return -1;\n    }\n    offset_ += rv;\n    buf_pos += rv;\n    buf_remaining -= rv;\n\n    for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {\n        ssize_t rv = ReadBlock(chunk, 0, buf_pos, buf_remaining);\n        if (rv < 0) {\n            return -1;\n        }\n        offset_ += rv;\n        buf_pos += rv;\n        buf_remaining -= rv;\n    }\n\n    if (buf_remaining) {\n        ssize_t rv = ReadBlock(end_chunk, 0, buf_pos, buf_remaining);\n        if (rv < 0) {\n            return -1;\n        }\n        offset_ += rv;\n        buf_pos += rv;\n        buf_remaining -= rv;\n    }\n\n    CHECK_EQ(buf_pos - reinterpret_cast<uint8_t*>(buf), count);\n    CHECK_EQ(buf_remaining, 0);\n\n    errno = 0;\n    return count;\n}\n\nssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, size_t start_offset, void* buffer,\n                                            size_t buffer_size) {\n    size_t bytes_to_read = std::min(static_cast<size_t>(block_size_), buffer_size);\n\n    // The offset is relative to the chunk; we should be reading no more than\n    // one chunk.\n    CHECK(start_offset + bytes_to_read <= block_size_);\n\n    const CowOperation* op = nullptr;\n    if (chunk < ops_.size()) {\n        op = ops_[chunk];\n    }\n\n    if (!op || op->type() == kCowCopyOp) {\n        borrowed_fd fd = GetSourceFd();\n        if (fd < 0) {\n            // GetSourceFd sets errno.\n            return -1;\n        }\n\n        if (op) {\n            uint64_t source_offset;\n            if (!cow_->GetSourceOffset(op, &source_offset)) {\n                LOG(ERROR) << \"GetSourceOffset failed in CompressedSnapshotReader for op: \" << *op;\n                return false;\n            }\n            chunk = GetBlockFromOffset(cow_->GetHeader(), source_offset);\n        }\n\n        off64_t offset = (chunk * block_size_) + start_offset;\n        if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {\n            PLOG(ERROR) << \"read \" << *source_device_;\n            // ReadFullyAtOffset sets errno.\n            return -1;\n        }\n    } else if (op->type() == kCowZeroOp) {\n        memset(buffer, 0, bytes_to_read);\n    } else if (op->type() == kCowReplaceOp) {\n        size_t buffer_size = CowOpCompressionSize(op, block_size_);\n        uint8_t temp_buffer[buffer_size];\n        if (cow_->ReadData(op, temp_buffer, buffer_size, 0) < buffer_size) {\n            LOG(ERROR) << \"CompressedSnapshotReader failed to read replace op: buffer_size: \"\n                       << buffer_size << \"start_offset: \" << start_offset;\n            errno = EIO;\n            return -1;\n        }\n        off_t block_offset{};\n        if (!GetBlockOffset(op, chunk, block_size_, &block_offset)) {\n            LOG(ERROR) << \"GetBlockOffset failed\";\n            return -1;\n        }\n        std::memcpy(buffer, (char*)temp_buffer + block_offset + start_offset, bytes_to_read);\n    } else if (op->type() == kCowXorOp) {\n        borrowed_fd fd = GetSourceFd();\n        if (fd < 0) {\n            // GetSourceFd sets errno.\n            return -1;\n        }\n\n        uint64_t source_offset;\n        if (!cow_->GetSourceOffset(op, &source_offset)) {\n            LOG(ERROR) << \"GetSourceOffset failed in CompressedSnapshotReader for op: \" << *op;\n            return false;\n        }\n        off64_t offset = source_offset + start_offset;\n\n        std::string data(bytes_to_read, '\\0');\n        if (!android::base::ReadFullyAtOffset(fd, data.data(), data.size(), offset)) {\n            PLOG(ERROR) << \"read \" << *source_device_;\n            // ReadFullyAtOffset sets errno.\n            return -1;\n        }\n\n        if (cow_->ReadData(op, buffer, bytes_to_read, start_offset) < bytes_to_read) {\n            LOG(ERROR) << \"CompressedSnapshotReader failed to read xor op\";\n            errno = EIO;\n            return -1;\n        }\n\n        for (size_t i = 0; i < bytes_to_read; i++) {\n            ((char*)buffer)[i] ^= data[i];\n        }\n    } else {\n        LOG(ERROR) << \"CompressedSnapshotReader unknown op type: \" << uint32_t(op->type());\n        errno = EINVAL;\n        return -1;\n    }\n\n    // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.\n    return bytes_to_read;\n}\n\noff64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {\n    switch (whence) {\n        case SEEK_SET:\n            offset_ = offset;\n            break;\n        case SEEK_END:\n            offset_ = static_cast<off64_t>(block_device_size_) + offset;\n            break;\n        case SEEK_CUR:\n            offset_ += offset;\n            break;\n        default:\n            LOG(ERROR) << \"Unrecognized seek whence: \" << whence;\n            errno = EINVAL;\n            return -1;\n    }\n    return offset_;\n}\n\nuint64_t CompressedSnapshotReader::BlockDevSize() {\n    return block_device_size_;\n}\n\nbool CompressedSnapshotReader::Close() {\n    cow_ = nullptr;\n    source_fd_ = {};\n    return true;\n}\n\nbool CompressedSnapshotReader::IsSettingErrno() {\n    return true;\n}\n\nbool CompressedSnapshotReader::IsOpen() {\n    return cow_ != nullptr;\n}\n\nbool CompressedSnapshotReader::Flush() {\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.h",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <optional>\n#include <vector>\n\n#include <android-base/file.h>\n#include <libsnapshot/cow_reader.h>\n#include <payload_consumer/file_descriptor.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass CompressedSnapshotReader : public chromeos_update_engine::FileDescriptor {\n  public:\n    CompressedSnapshotReader(std::unique_ptr<ICowReader>&& cow,\n                             const std::optional<std::string>& source_device,\n                             std::optional<uint64_t> block_dev_size);\n\n    bool Open(const char* path, int flags, mode_t mode) override;\n    bool Open(const char* path, int flags) override;\n    ssize_t Write(const void* buf, size_t count) override;\n    bool BlkIoctl(int request, uint64_t start, uint64_t length, int* result) override;\n    ssize_t Read(void* buf, size_t count) override;\n    off64_t Seek(off64_t offset, int whence) override;\n    uint64_t BlockDevSize() override;\n    bool Close() override;\n    bool IsSettingErrno() override;\n    bool IsOpen() override;\n    bool Flush() override;\n\n  private:\n    ssize_t ReadBlock(uint64_t chunk, size_t start_offset, void* buffer, size_t size);\n    android::base::borrowed_fd GetSourceFd();\n\n    std::unique_ptr<ICowReader> cow_;\n    std::unique_ptr<ICowOpIter> op_iter_;\n    uint32_t block_size_ = 0;\n\n    std::optional<std::string> source_device_;\n    android::base::unique_fd source_fd_;\n    uint64_t block_device_size_ = 0;\n    off64_t offset_ = 0;\n\n    std::vector<const CowOperation*> ops_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader_test.cpp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <unordered_set>\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n#include <libsnapshot/cow_writer.h>\n#include <payload_consumer/file_descriptor.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\nusing chromeos_update_engine::FileDescriptor;\n\nstatic constexpr uint32_t kBlockSize = 4096;\nstatic constexpr size_t kBlockCount = 10;\n\nclass OfflineSnapshotTest : public ::testing::Test {\n  protected:\n    virtual void SetUp() override {\n        base_ = std::make_unique<TemporaryFile>();\n        ASSERT_GE(base_->fd, 0) << strerror(errno);\n\n        cow_ = std::make_unique<TemporaryFile>();\n        ASSERT_GE(cow_->fd, 0) << strerror(errno);\n\n        WriteBaseDevice();\n    }\n\n    virtual void TearDown() override {\n        base_ = nullptr;\n        cow_ = nullptr;\n        base_blocks_ = {};\n    }\n\n    void WriteBaseDevice() {\n        unique_fd random(open(\"/dev/urandom\", O_RDONLY));\n        ASSERT_GE(random, 0);\n\n        for (size_t i = 0; i < kBlockCount; i++) {\n            std::string block(kBlockSize, 0);\n            ASSERT_TRUE(android::base::ReadFully(random, block.data(), block.size()));\n            ASSERT_TRUE(android::base::WriteFully(base_->fd, block.data(), block.size()));\n            base_blocks_.emplace_back(std::move(block));\n        }\n        ASSERT_EQ(fsync(base_->fd), 0);\n    }\n\n    void WriteCow(ICowWriter* writer) {\n        std::string new_block = MakeNewBlockString();\n        std::string xor_block = MakeXorBlockString();\n\n        ASSERT_TRUE(writer->AddXorBlocks(1, xor_block.data(), xor_block.size(), 0, kBlockSize / 2));\n        ASSERT_TRUE(writer->AddCopy(3, 0));\n        ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));\n        ASSERT_TRUE(writer->AddZeroBlocks(7, 2));\n        ASSERT_TRUE(writer->Finalize());\n    }\n\n    void TestBlockReads(ICowWriter* writer) {\n        auto reader = writer->OpenFileDescriptor(base_->path);\n        ASSERT_NE(reader, nullptr);\n\n        // Test that unchanged blocks are not modified.\n        std::unordered_set<size_t> changed_blocks = {1, 3, 5, 7, 8};\n        for (size_t i = 0; i < kBlockCount; i++) {\n            if (changed_blocks.count(i)) {\n                continue;\n            }\n\n            std::string block(kBlockSize, 0);\n            ASSERT_EQ(reader->Seek(i * kBlockSize, SEEK_SET), i * kBlockSize);\n            ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);\n            ASSERT_EQ(block, base_blocks_[i]);\n        }\n\n        // Test that we can read back our modified blocks.\n        std::string data(kBlockSize, 0);\n        std::string offsetblock = base_blocks_[0].substr(kBlockSize / 2, kBlockSize / 2) +\n                                  base_blocks_[1].substr(0, kBlockSize / 2);\n        ASSERT_EQ(offsetblock.size(), kBlockSize);\n        ASSERT_EQ(reader->Seek(1 * kBlockSize, SEEK_SET), 1 * kBlockSize);\n        ASSERT_EQ(reader->Read(data.data(), data.size()), kBlockSize);\n        for (int i = 0; i < 100; i++) {\n            data[i] = (char)~(data[i]);\n        }\n        ASSERT_EQ(data, offsetblock);\n\n        std::string block(kBlockSize, 0);\n        ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);\n        ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);\n        ASSERT_EQ(block, base_blocks_[0]);\n\n        ASSERT_EQ(reader->Seek(5 * kBlockSize, SEEK_SET), 5 * kBlockSize);\n        ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);\n        ASSERT_EQ(block, MakeNewBlockString());\n\n        std::string two_blocks(kBlockSize * 2, 0x7f);\n        std::string zeroes(kBlockSize * 2, 0);\n        ASSERT_EQ(reader->Seek(7 * kBlockSize, SEEK_SET), 7 * kBlockSize);\n        ASSERT_EQ(reader->Read(two_blocks.data(), two_blocks.size()), two_blocks.size());\n        ASSERT_EQ(two_blocks, zeroes);\n    }\n\n    void TestByteReads(ICowWriter* writer) {\n        auto reader = writer->OpenFileDescriptor(base_->path);\n        ASSERT_NE(reader, nullptr);\n\n        std::string blob(kBlockSize * 3, 'x');\n\n        // Test that we can read in the middle of a block.\n        static constexpr size_t kOffset = 970;\n        off64_t offset = 3 * kBlockSize + kOffset;\n        ASSERT_EQ(reader->Seek(0, SEEK_SET), 0);\n        ASSERT_EQ(reader->Seek(offset, SEEK_CUR), offset);\n        ASSERT_EQ(reader->Read(blob.data(), blob.size()), blob.size());\n        ASSERT_EQ(blob.substr(0, 100), base_blocks_[0].substr(kOffset, 100));\n        ASSERT_EQ(blob.substr(kBlockSize - kOffset, kBlockSize), base_blocks_[4]);\n        ASSERT_EQ(blob.substr(kBlockSize * 2 - kOffset, 100), MakeNewBlockString().substr(0, 100));\n        ASSERT_EQ(blob.substr(blob.size() - kOffset), base_blocks_[6].substr(0, kOffset));\n\n        // Pull a random byte from the compressed block.\n        char value;\n        offset = 5 * kBlockSize + 1000;\n        ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);\n        ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));\n        ASSERT_EQ(value, MakeNewBlockString()[1000]);\n\n        // Test a sequence of one byte reads.\n        offset = 5 * kBlockSize + 10;\n        std::string expected = MakeNewBlockString().substr(10, 20);\n        ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);\n\n        std::string got;\n        while (got.size() < expected.size()) {\n            ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));\n            got.push_back(value);\n        }\n        ASSERT_EQ(got, expected);\n    }\n\n    void TestReads(ICowWriter* writer) {\n        ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));\n        ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));\n    }\n\n    std::string MakeNewBlockString() {\n        std::string new_block = \"This is a new block\";\n        new_block.resize(kBlockSize / 2, '*');\n        new_block.resize(kBlockSize, '!');\n        return new_block;\n    }\n\n    std::string MakeXorBlockString() {\n        std::string data(kBlockSize, 0);\n        memset(data.data(), 0xff, 100);\n        return data;\n    }\n\n    std::unique_ptr<TemporaryFile> base_;\n    std::unique_ptr<TemporaryFile> cow_;\n    std::vector<std::string> base_blocks_;\n};\n\nTEST_F(OfflineSnapshotTest, CompressedSnapshot) {\n    CowOptions options;\n    options.compression = \"gz\";\n    options.max_blocks = {kBlockCount};\n    options.scratch_space = false;\n\n    unique_fd cow_fd(dup(cow_->fd));\n    ASSERT_GE(cow_fd, 0);\n\n    auto writer = CreateCowWriter(2, options, std::move(cow_fd));\n    ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));\n    ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <sys/stat.h>\n\n#include <cstdio>\n#include <iostream>\n#include <memory>\n#include <string_view>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <gtest/gtest.h>\n#include <libsnapshot/cow_reader.h>\n#include <libsnapshot/cow_writer.h>\n#include \"cow_decompress.h\"\n#include \"writer_v2.h\"\n\nusing android::base::unique_fd;\nusing testing::AssertionFailure;\nusing testing::AssertionResult;\nusing testing::AssertionSuccess;\n\nnamespace android {\nnamespace snapshot {\n\nclass CowTest : public ::testing::Test {\n  protected:\n    virtual void SetUp() override {\n        cow_ = std::make_unique<TemporaryFile>();\n        ASSERT_GE(cow_->fd, 0) << strerror(errno);\n    }\n\n    virtual void TearDown() override { cow_ = nullptr; }\n\n    unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }\n\n    std::unique_ptr<TemporaryFile> cow_;\n};\n\n// Helper to check read sizes.\nstatic inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {\n    return reader.ReadData(op, buffer, size) == size;\n}\n\nTEST_F(CowTest, CopyContiguous) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    ASSERT_TRUE(writer.AddCopy(10, 1000, 100));\n    ASSERT_TRUE(writer.Finalize());\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.GetHeader();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n    ASSERT_EQ(header.block_size, options.block_size);\n\n    CowFooter footer;\n    ASSERT_TRUE(reader.GetFooter(&footer));\n    ASSERT_EQ(footer.op.num_ops, 100);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    size_t i = 0;\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n        ASSERT_EQ(op->type(), kCowCopyOp);\n        ASSERT_EQ(op->data_length, 0);\n        ASSERT_EQ(op->new_block, 10 + i);\n        ASSERT_EQ(op->source(), 1000 + i);\n        iter->Next();\n        i += 1;\n    }\n\n    ASSERT_EQ(i, 100);\n}\n\nTEST_F(CowTest, ReadWrite) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_TRUE(writer.AddCopy(10, 20));\n    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.GetHeader();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n    ASSERT_EQ(header.block_size, options.block_size);\n\n    CowFooter footer;\n    ASSERT_TRUE(reader.GetFooter(&footer));\n    ASSERT_EQ(footer.op.num_ops, 4);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowCopyOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 10);\n    ASSERT_EQ(op->source(), 20);\n\n    std::string sink(data.size(), '\\0');\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_EQ(op->data_length, 4096);\n    ASSERT_EQ(op->new_block, 50);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    // Note: the zero operation gets split into two blocks.\n    ASSERT_EQ(op->type(), kCowZeroOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 51);\n    ASSERT_EQ(op->source(), 0);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowZeroOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 52);\n    ASSERT_EQ(op->source(), 0);\n\n    iter->Next();\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, ReadWriteXor) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_TRUE(writer.AddCopy(10, 20));\n    ASSERT_TRUE(writer.AddXorBlocks(50, data.data(), data.size(), 24, 10));\n    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.GetHeader();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n    ASSERT_EQ(header.block_size, options.block_size);\n\n    CowFooter footer;\n    ASSERT_TRUE(reader.GetFooter(&footer));\n    ASSERT_EQ(footer.op.num_ops, 4);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowCopyOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 10);\n    ASSERT_EQ(op->source(), 20);\n\n    std::string sink(data.size(), '\\0');\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowXorOp);\n    ASSERT_EQ(op->data_length, 4096);\n    ASSERT_EQ(op->new_block, 50);\n    ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    // Note: the zero operation gets split into two blocks.\n    ASSERT_EQ(op->type(), kCowZeroOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 51);\n    ASSERT_EQ(op->source(), 0);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowZeroOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 52);\n    ASSERT_EQ(op->source(), 0);\n\n    iter->Next();\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, CompressGz) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    options.compression = \"gz\";\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n\n    std::string sink(data.size(), '\\0');\n\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_EQ(op->data_length, 56);  // compressed!\n    ASSERT_EQ(op->new_block, 50);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n\n    iter->Next();\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nclass CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};\n\nTEST_P(CompressionTest, ThreadedBatchWrites) {\n    CowOptions options;\n    options.compression = GetParam();\n    options.num_compress_threads = 2;\n\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string xor_data = \"This is test data-1. Testing xor\";\n    xor_data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer.AddXorBlocks(50, xor_data.data(), xor_data.size(), 24, 10));\n\n    std::string data = \"This is test data-2. Testing replace ops\";\n    data.resize(options.block_size * 2048, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(100, data.data(), data.size()));\n\n    std::string data2 = \"This is test data-3. Testing replace ops\";\n    data2.resize(options.block_size * 259, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(6000, data2.data(), data2.size()));\n\n    std::string data3 = \"This is test data-4. Testing replace ops\";\n    data3.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(9000, data3.data(), data3.size()));\n\n    ASSERT_TRUE(writer.Finalize());\n\n    int expected_blocks = (1 + 2048 + 259 + 1);\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    int total_blocks = 0;\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n\n        if (op->type() == kCowXorOp) {\n            total_blocks += 1;\n            std::string sink(xor_data.size(), '\\0');\n            ASSERT_EQ(op->new_block, 50);\n            ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10\n            ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n            ASSERT_EQ(sink, xor_data);\n        }\n\n        if (op->type() == kCowReplaceOp) {\n            total_blocks += 1;\n            if (op->new_block == 100) {\n                data.resize(options.block_size);\n                std::string sink(data.size(), '\\0');\n                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n                ASSERT_EQ(sink.size(), data.size());\n                ASSERT_EQ(sink, data);\n            }\n            if (op->new_block == 6000) {\n                data2.resize(options.block_size);\n                std::string sink(data2.size(), '\\0');\n                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n                ASSERT_EQ(sink, data2);\n            }\n            if (op->new_block == 9000) {\n                std::string sink(data3.size(), '\\0');\n                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n                ASSERT_EQ(sink, data3);\n            }\n        }\n\n        iter->Next();\n    }\n\n    ASSERT_EQ(total_blocks, expected_blocks);\n}\n\nTEST_P(CompressionTest, NoBatchWrites) {\n    CowOptions options;\n    options.compression = GetParam();\n    options.num_compress_threads = 1;\n    options.cluster_ops = 0;\n\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string data = \"Testing replace ops without batch writes\";\n    data.resize(options.block_size * 1024, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));\n\n    std::string data2 = \"Testing odd blocks without batch writes\";\n    data2.resize(options.block_size * 111, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(3000, data2.data(), data2.size()));\n\n    std::string data3 = \"Testing single 4k block\";\n    data3.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(5000, data3.data(), data3.size()));\n\n    ASSERT_TRUE(writer.Finalize());\n\n    int expected_blocks = (1024 + 111 + 1);\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    int total_blocks = 0;\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n\n        if (op->type() == kCowReplaceOp) {\n            total_blocks += 1;\n            if (op->new_block == 50) {\n                data.resize(options.block_size);\n                std::string sink(data.size(), '\\0');\n                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n                ASSERT_EQ(sink, data);\n            }\n            if (op->new_block == 3000) {\n                data2.resize(options.block_size);\n                std::string sink(data2.size(), '\\0');\n                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n                ASSERT_EQ(sink, data2);\n            }\n            if (op->new_block == 5000) {\n                std::string sink(data3.size(), '\\0');\n                ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n                ASSERT_EQ(sink, data3);\n            }\n        }\n\n        iter->Next();\n    }\n\n    ASSERT_EQ(total_blocks, expected_blocks);\n}\n\ntemplate <typename T>\nclass HorribleStream : public IByteStream {\n  public:\n    HorribleStream(const std::vector<T>& input) : input_(input) {}\n\n    ssize_t Read(void* buffer, size_t length) override {\n        if (pos_ >= input_.size()) {\n            return 0;\n        }\n        if (length) {\n            *reinterpret_cast<char*>(buffer) = input_[pos_];\n        }\n        pos_++;\n        return 1;\n    }\n    size_t Size() const override { return input_.size(); }\n\n  private:\n    std::vector<T> input_;\n    size_t pos_ = 0;\n};\n\nTEST(HorribleStream, ReadFully) {\n    std::string expected_str = \"this is some data\";\n    std::vector<char> expected{expected_str.begin(), expected_str.end()};\n\n    HorribleStream<char> stream(expected);\n\n    std::vector<char> buffer(expected.size(), '\\0');\n    ASSERT_TRUE(stream.ReadFully(buffer.data(), buffer.size()));\n    ASSERT_EQ(buffer, expected);\n}\n\nTEST_P(CompressionTest, HorribleStream) {\n    if (strcmp(GetParam(), \"none\") == 0) {\n        GTEST_SKIP();\n    }\n    CowCompression compression;\n    auto algorithm = CompressionAlgorithmFromString(GetParam());\n    ASSERT_TRUE(algorithm.has_value());\n    compression.algorithm = algorithm.value();\n\n    std::string expected = \"The quick brown fox jumps over the lazy dog.\";\n    expected.resize(4096, '\\0');\n\n    std::unique_ptr<ICompressor> compressor = ICompressor::Create(compression, 4096);\n    auto result = compressor->Compress(expected.data(), expected.size());\n    ASSERT_FALSE(result.empty());\n\n    HorribleStream<uint8_t> stream(result);\n    auto decomp = IDecompressor::FromString(GetParam());\n    ASSERT_NE(decomp, nullptr);\n    decomp->set_stream(&stream);\n\n    expected = expected.substr(10, 500);\n\n    std::string buffer(expected.size(), '\\0');\n    ASSERT_EQ(decomp->Decompress(buffer.data(), 500, 4096, 10), 500);\n    ASSERT_EQ(buffer, expected);\n}\n\nINSTANTIATE_TEST_SUITE_P(AllCompressors, CompressionTest,\n                         testing::Values(\"none\", \"gz\", \"brotli\", \"lz4\"));\n\nTEST_F(CowTest, ClusterCompressGz) {\n    CowOptions options;\n    options.compression = \"gz\";\n    options.cluster_ops = 2;\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));\n\n    std::string data2 = \"More data!\";\n    data2.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));\n\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n\n    std::string sink(data.size(), '\\0');\n\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_EQ(op->data_length, 56);  // compressed!\n    ASSERT_EQ(op->new_block, 50);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowClusterOp);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    sink = {};\n    sink.resize(data2.size(), '\\0');\n    ASSERT_EQ(op->data_length, 41);  // compressed!\n    ASSERT_EQ(op->new_block, 51);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data2);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowClusterOp);\n\n    iter->Next();\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, CompressTwoBlocks) {\n    CowOptions options;\n    options.compression = \"gz\";\n    options.cluster_ops = 0;\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size * 2, '\\0');\n\n    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n\n    std::string sink(options.block_size, '\\0');\n\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_EQ(op->new_block, 51);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n}\n\nTEST_F(CowTest, GetSize) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    CowWriterV2 writer(options, GetCowFd());\n    if (ftruncate(cow_->fd, 0) < 0) {\n        perror(\"Fails to set temp file size\");\n        FAIL();\n    }\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_TRUE(writer.AddCopy(10, 20));\n    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));\n    auto size_before = writer.GetCowSizeInfo().cow_size;\n    ASSERT_TRUE(writer.Finalize());\n    auto size_after = writer.GetCowSizeInfo().cow_size;\n    ASSERT_EQ(size_before, size_after);\n    struct stat buf;\n\n    ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);\n    ASSERT_EQ(buf.st_size, writer.GetCowSizeInfo().cow_size);\n}\n\nTEST_F(CowTest, AppendLabelSmall) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer->AddLabel(3));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({3}));\n\n    std::string data2 = \"More data!\";\n    data2.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    struct stat buf;\n    ASSERT_EQ(fstat(cow_->fd, &buf), 0);\n    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);\n\n    // Read back both operations, and label.\n    CowReader reader;\n    uint64_t label;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    ASSERT_TRUE(reader.GetLastLabel(&label));\n    ASSERT_EQ(label, 3);\n\n    std::string sink(data.size(), '\\0');\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n\n    iter->Next();\n    sink = {};\n    sink.resize(data2.size(), '\\0');\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 3);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data2);\n\n    iter->Next();\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, AppendLabelMissing) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(writer->AddLabel(0));\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer->AddLabel(1));\n    // Drop the tail end of the last op header, corrupting it.\n    ftruncate(cow_->fd, writer->GetCowSizeInfo().cow_size - sizeof(CowFooter) - 3);\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_FALSE(writer->Initialize({1}));\n    ASSERT_TRUE(writer->Initialize({0}));\n\n    ASSERT_TRUE(writer->AddZeroBlocks(51, 1));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    struct stat buf;\n    ASSERT_EQ(fstat(cow_->fd, &buf), 0);\n    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);\n\n    // Read back both operations.\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 0);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowZeroOp);\n\n    iter->Next();\n\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, AppendExtendedCorrupted) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(writer->AddLabel(5));\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size * 2, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer->AddLabel(6));\n\n    // fail to write the footer. Cow Format does not know if Label 6 is valid\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    // Get the last known good label\n    CowReader label_reader;\n    uint64_t label;\n    ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));\n    ASSERT_TRUE(label_reader.GetLastLabel(&label));\n    ASSERT_EQ(label, 5);\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({5}));\n\n    ASSERT_TRUE(writer->Finalize());\n\n    struct stat buf;\n    ASSERT_EQ(fstat(cow_->fd, &buf), 0);\n    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);\n\n    // Read back all valid operations\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 5);\n\n    iter->Next();\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, AppendbyLabel) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size * 2, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));\n\n    ASSERT_TRUE(writer->AddLabel(4));\n\n    ASSERT_TRUE(writer->AddZeroBlocks(50, 2));\n\n    ASSERT_TRUE(writer->AddLabel(5));\n\n    ASSERT_TRUE(writer->AddCopy(5, 6));\n\n    ASSERT_TRUE(writer->AddLabel(6));\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_FALSE(writer->Initialize({12}));\n    ASSERT_TRUE(writer->Initialize({5}));\n\n    // This should drop label 6\n    ASSERT_TRUE(writer->Finalize());\n\n    struct stat buf;\n    ASSERT_EQ(fstat(cow_->fd, &buf), 0);\n    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);\n\n    // Read back all ops\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    std::string sink(options.block_size, '\\0');\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data.substr(0, options.block_size));\n\n    iter->Next();\n    sink = {};\n    sink.resize(options.block_size, '\\0');\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data.substr(options.block_size, 2 * options.block_size));\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 4);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowZeroOp);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowZeroOp);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 5);\n\n    iter->Next();\n\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, ClusterTest) {\n    CowOptions options;\n    options.cluster_ops = 4;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));\n\n    ASSERT_TRUE(writer->AddLabel(4));\n\n    ASSERT_TRUE(writer->AddZeroBlocks(50, 2));  // Cluster split in middle\n\n    ASSERT_TRUE(writer->AddLabel(5));\n\n    ASSERT_TRUE(writer->AddCopy(5, 6));\n\n    // Cluster split\n\n    ASSERT_TRUE(writer->AddLabel(6));\n\n    ASSERT_TRUE(writer->Finalize());  // No data for cluster, so no cluster split needed\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    // Read back all ops\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    std::string sink(data.size(), '\\0');\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data.substr(0, options.block_size));\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 4);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowZeroOp);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowClusterOp);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowZeroOp);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 5);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowCopyOp);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowClusterOp);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 6);\n\n    iter->Next();\n\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, ClusterAppendTest) {\n    CowOptions options;\n    options.cluster_ops = 3;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(writer->AddLabel(50));\n    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op, should be dropped on append\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({50}));\n\n    std::string data2 = \"More data!\";\n    data2.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));\n    ASSERT_TRUE(writer->Finalize());  // Adds a cluster op\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    struct stat buf;\n    ASSERT_EQ(fstat(cow_->fd, &buf), 0);\n    ASSERT_EQ(buf.st_size, writer->GetCowSizeInfo().cow_size);\n\n    // Read back both operations, plus cluster op at end\n    CowReader reader;\n    uint64_t label;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    ASSERT_TRUE(reader.GetLastLabel(&label));\n    ASSERT_EQ(label, 50);\n\n    std::string sink(data2.size(), '\\0');\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowLabelOp);\n    ASSERT_EQ(op->source(), 50);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data2);\n\n    iter->Next();\n\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n    ASSERT_EQ(op->type(), kCowClusterOp);\n\n    iter->Next();\n\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, AppendAfterFinalize) {\n    CowOptions options;\n    options.cluster_ops = 0;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer->AddLabel(3));\n    ASSERT_TRUE(writer->Finalize());\n\n    std::string data2 = \"More data!\";\n    data2.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    // COW should be valid.\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n}\n\nAssertionResult WriteDataBlock(ICowWriter* writer, uint64_t new_block, std::string data) {\n    data.resize(writer->GetBlockSize(), '\\0');\n    if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {\n        return AssertionFailure() << \"Failed to add raw block\";\n    }\n    return AssertionSuccess();\n}\n\nAssertionResult CompareDataBlock(CowReader* reader, const CowOperation* op,\n                                 const std::string& data) {\n    const auto& header = reader->GetHeader();\n\n    std::string cmp = data;\n    cmp.resize(header.block_size, '\\0');\n\n    std::string sink(cmp.size(), '\\0');\n    if (!reader->ReadData(op, sink.data(), sink.size())) {\n        return AssertionFailure() << \"Failed to read data block\";\n    }\n    if (cmp != sink) {\n        return AssertionFailure() << \"Data blocks did not match, expected \" << cmp << \", got \"\n                                  << sink;\n    }\n\n    return AssertionSuccess();\n}\n\nTEST_F(CowTest, ResumeMidCluster) {\n    CowOptions options;\n    options.cluster_ops = 7;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, \"Block 1\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, \"Block 2\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, \"Block 3\"));\n    ASSERT_TRUE(writer->AddLabel(1));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, \"Block 4\"));\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({1}));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, \"Block 4\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, \"Block 5\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, \"Block 6\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, \"Block 7\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, \"Block 8\"));\n    ASSERT_TRUE(writer->AddLabel(2));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    size_t num_replace = 0;\n    size_t max_in_cluster = 0;\n    size_t num_in_cluster = 0;\n    size_t num_clusters = 0;\n    while (!iter->AtEnd()) {\n        const auto& op = iter->Get();\n\n        num_in_cluster++;\n        max_in_cluster = std::max(max_in_cluster, num_in_cluster);\n\n        if (op->type() == kCowReplaceOp) {\n            num_replace++;\n\n            ASSERT_EQ(op->new_block, num_replace);\n            ASSERT_TRUE(CompareDataBlock(&reader, op, \"Block \" + std::to_string(num_replace)));\n        } else if (op->type() == kCowClusterOp) {\n            num_in_cluster = 0;\n            num_clusters++;\n        }\n\n        iter->Next();\n    }\n    ASSERT_EQ(num_replace, 8);\n    ASSERT_EQ(max_in_cluster, 7);\n    ASSERT_EQ(num_clusters, 2);\n}\n\nTEST_F(CowTest, ResumeEndCluster) {\n    CowOptions options;\n    int cluster_ops = 5;\n    options.cluster_ops = cluster_ops;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, \"Block 1\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, \"Block 2\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, \"Block 3\"));\n    ASSERT_TRUE(writer->AddLabel(1));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, \"Block 4\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, \"Block 5\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, \"Block 6\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, \"Block 7\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, \"Block 8\"));\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({1}));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, \"Block 4\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, \"Block 5\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, \"Block 6\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 7, \"Block 7\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 8, \"Block 8\"));\n    ASSERT_TRUE(writer->AddLabel(2));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    size_t num_replace = 0;\n    size_t max_in_cluster = 0;\n    size_t num_in_cluster = 0;\n    size_t num_clusters = 0;\n    while (!iter->AtEnd()) {\n        const auto& op = iter->Get();\n\n        num_in_cluster++;\n        max_in_cluster = std::max(max_in_cluster, num_in_cluster);\n\n        if (op->type() == kCowReplaceOp) {\n            num_replace++;\n\n            ASSERT_EQ(op->new_block, num_replace);\n            ASSERT_TRUE(CompareDataBlock(&reader, op, \"Block \" + std::to_string(num_replace)));\n        } else if (op->type() == kCowClusterOp) {\n            num_in_cluster = 0;\n            num_clusters++;\n        }\n\n        iter->Next();\n    }\n    ASSERT_EQ(num_replace, 8);\n    ASSERT_EQ(max_in_cluster, cluster_ops);\n    ASSERT_EQ(num_clusters, 3);\n}\n\nTEST_F(CowTest, DeleteMidCluster) {\n    CowOptions options;\n    options.cluster_ops = 7;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 1, \"Block 1\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 2, \"Block 2\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 3, \"Block 3\"));\n    ASSERT_TRUE(writer->AddLabel(1));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 4, \"Block 4\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 5, \"Block 5\"));\n    ASSERT_TRUE(WriteDataBlock(writer.get(), 6, \"Block 6\"));\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({1}));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    size_t num_replace = 0;\n    size_t max_in_cluster = 0;\n    size_t num_in_cluster = 0;\n    size_t num_clusters = 0;\n    while (!iter->AtEnd()) {\n        const auto& op = iter->Get();\n\n        num_in_cluster++;\n        max_in_cluster = std::max(max_in_cluster, num_in_cluster);\n        if (op->type() == kCowReplaceOp) {\n            num_replace++;\n\n            ASSERT_EQ(op->new_block, num_replace);\n            ASSERT_TRUE(CompareDataBlock(&reader, op, \"Block \" + std::to_string(num_replace)));\n        } else if (op->type() == kCowClusterOp) {\n            num_in_cluster = 0;\n            num_clusters++;\n        }\n\n        iter->Next();\n    }\n    ASSERT_EQ(num_replace, 3);\n    ASSERT_EQ(max_in_cluster, 5);  // 3 data, 1 label, 1 cluster op\n    ASSERT_EQ(num_clusters, 1);\n}\n\nTEST_F(CowTest, BigSeqOp) {\n    CowOptions options;\n    CowWriterV2 writer(options, GetCowFd());\n    const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;\n    uint32_t sequence[seq_len];\n    for (int i = 0; i < seq_len; i++) {\n        sequence[i] = i + 1;\n    }\n\n    ASSERT_TRUE(writer.Initialize());\n\n    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));\n    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    auto iter = reader.GetRevMergeOpIter();\n\n    for (int i = 0; i < seq_len; i++) {\n        ASSERT_TRUE(!iter->AtEnd());\n        const auto& op = iter->Get();\n\n        ASSERT_EQ(op->new_block, seq_len - i);\n\n        iter->Next();\n    }\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, MissingSeqOp) {\n    CowOptions options;\n    CowWriterV2 writer(options, GetCowFd());\n    const int seq_len = 10;\n    uint32_t sequence[seq_len];\n    for (int i = 0; i < seq_len; i++) {\n        sequence[i] = i + 1;\n    }\n\n    ASSERT_TRUE(writer.Initialize());\n\n    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));\n    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len - 1));\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_FALSE(reader.Parse(cow_->fd));\n}\n\nTEST_F(CowTest, ResumeSeqOp) {\n    CowOptions options;\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    const int seq_len = 10;\n    uint32_t sequence[seq_len];\n    for (int i = 0; i < seq_len; i++) {\n        sequence[i] = i + 1;\n    }\n\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));\n    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));\n    ASSERT_TRUE(writer->AddLabel(1));\n    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n    auto reader = std::make_unique<CowReader>();\n    ASSERT_TRUE(reader->Parse(cow_->fd, 1));\n    auto itr = reader->GetRevMergeOpIter();\n    ASSERT_TRUE(itr->AtEnd());\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({1}));\n    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    reader = std::make_unique<CowReader>();\n    ASSERT_TRUE(reader->Parse(cow_->fd));\n\n    auto iter = reader->GetRevMergeOpIter();\n\n    uint64_t expected_block = 10;\n    while (!iter->AtEnd() && expected_block > 0) {\n        ASSERT_FALSE(iter->AtEnd());\n        const auto& op = iter->Get();\n\n        ASSERT_EQ(op->new_block, expected_block);\n\n        iter->Next();\n        expected_block--;\n    }\n    ASSERT_EQ(expected_block, 0);\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, RevMergeOpItrTest) {\n    CowOptions options;\n    options.cluster_ops = 5;\n    options.num_merge_ops = 1;\n    CowWriterV2 writer(options, GetCowFd());\n    uint32_t sequence[] = {2, 10, 6, 7, 3, 5};\n\n    ASSERT_TRUE(writer.Initialize());\n\n    ASSERT_TRUE(writer.AddSequenceData(6, sequence));\n    ASSERT_TRUE(writer.AddCopy(6, 13));\n    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));\n    ASSERT_TRUE(writer.AddCopy(3, 15));\n    ASSERT_TRUE(writer.AddCopy(2, 11));\n    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));\n    ASSERT_TRUE(writer.AddCopy(5, 16));\n    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));\n    ASSERT_TRUE(writer.AddCopy(10, 12));\n    ASSERT_TRUE(writer.AddCopy(7, 14));\n    ASSERT_TRUE(writer.Finalize());\n\n    // New block in cow order is 6, 12, 8, 11, 3, 2, 4, 9, 5, 1, 10, 7\n    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1\n    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2\n    // new block 2 is \"already merged\", so will be left out.\n\n    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    auto iter = reader.GetRevMergeOpIter();\n    auto expected_new_block = revMergeOpSequence.begin();\n\n    while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {\n        const auto& op = iter->Get();\n\n        ASSERT_EQ(op->new_block, *expected_new_block);\n\n        iter->Next();\n        expected_new_block++;\n    }\n    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, ParseOptionsTest) {\n    CowOptions options;\n    std::vector<std::pair<std::string, bool>> testcases = {\n            {\"gz,4\", true},   {\"gz,4,4\", false}, {\"lz4,4\", true}, {\"brotli,4\", true},\n            {\"zstd,4\", true}, {\"zstd,x\", false}, {\"zs,4\", false}, {\"zstd.4\", false}};\n    for (size_t i = 0; i < testcases.size(); i++) {\n        options.compression = testcases[i].first;\n        CowWriterV2 writer(options, GetCowFd());\n        ASSERT_EQ(writer.Initialize(), testcases[i].second);\n    }\n}\n\nTEST_F(CowTest, LegacyRevMergeOpItrTest) {\n    CowOptions options;\n    options.cluster_ops = 5;\n    options.num_merge_ops = 1;\n    CowWriterV2 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    ASSERT_TRUE(writer.AddCopy(2, 11));\n    ASSERT_TRUE(writer.AddCopy(10, 12));\n    ASSERT_TRUE(writer.AddCopy(6, 13));\n    ASSERT_TRUE(writer.AddCopy(7, 14));\n    ASSERT_TRUE(writer.AddCopy(3, 15));\n    ASSERT_TRUE(writer.AddCopy(5, 16));\n    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));\n    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));\n\n    ASSERT_TRUE(writer.Finalize());\n\n    // New block in cow order is 2, 10, 6, 7, 3, 5, 12, 8, 11, 4, 9, 1\n    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1\n    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2\n    // new block 2 is \"already merged\", so will be left out.\n\n    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    auto iter = reader.GetRevMergeOpIter();\n    auto expected_new_block = revMergeOpSequence.begin();\n\n    while (!iter->AtEnd() && expected_new_block != revMergeOpSequence.end()) {\n        const auto& op = iter->Get();\n\n        ASSERT_EQ(op->new_block, *expected_new_block);\n\n        iter->Next();\n        expected_new_block++;\n    }\n    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTest, InvalidMergeOrderTest) {\n    CowOptions options;\n    options.cluster_ops = 5;\n    options.num_merge_ops = 1;\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n    auto writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    CowReader reader;\n\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(writer->AddCopy(3, 2));\n    ASSERT_TRUE(writer->AddCopy(2, 1));\n    ASSERT_TRUE(writer->AddLabel(1));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    ASSERT_TRUE(reader.VerifyMergeOps());\n\n    ASSERT_TRUE(writer->Initialize({1}));\n    ASSERT_TRUE(writer->AddCopy(4, 2));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    ASSERT_FALSE(reader.VerifyMergeOps());\n\n    writer = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize());\n    ASSERT_TRUE(writer->AddCopy(2, 1));\n    ASSERT_TRUE(writer->AddXorBlocks(3, data.data(), data.size(), 1, 1));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    ASSERT_FALSE(reader.VerifyMergeOps());\n}\n\nunique_fd OpenTestFile(const std::string& file, int flags) {\n    std::string path = \"tools/testdata/\" + file;\n\n    unique_fd fd(open(path.c_str(), flags));\n    if (fd >= 0) {\n        return fd;\n    }\n\n    path = android::base::GetExecutableDirectory() + \"/\" + path;\n    return unique_fd{open(path.c_str(), flags)};\n}\n\nTEST_F(CowTest, CompatibilityTest) {\n    std::string filename = \"cow_v2\";\n    auto fd = OpenTestFile(filename, O_RDONLY);\n    if (fd.get() == -1) {\n        LOG(ERROR) << filename << \" not found\";\n        GTEST_SKIP();\n    }\n    CowReader reader;\n    reader.Parse(fd);\n\n    const auto& header = reader.GetHeader();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, kCowVersionMajor);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n\n    CowFooter footer;\n    ASSERT_TRUE(reader.GetFooter(&footer));\n}\n\nTEST_F(CowTest, DecompressIncompressibleBlock) {\n    auto fd = OpenTestFile(\"incompressible_block\", O_RDONLY);\n    ASSERT_GE(fd, 0);\n\n    std::string original;\n    ASSERT_TRUE(android::base::ReadFdToString(fd, &original)) << strerror(errno);\n    ASSERT_EQ(original.size(), 4096);\n\n    CowOptions options;\n    options.compression = \"gz\";\n    auto writer = CreateCowWriter(2, options, GetCowFd());\n    ASSERT_NE(writer, nullptr);\n    ASSERT_TRUE(writer->AddRawBlocks(0, original.data(), original.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    std::string block(original.size(), '\\0');\n    ASSERT_EQ(iter->Get()->data_length, 4096);\n    ASSERT_TRUE(ReadData(reader, iter->Get(), block.data(), block.size()));\n\n    for (size_t i = 0; i < block.size(); i++) {\n        ASSERT_EQ(block[i], original[i]) << \"mismatch at byte \" << i;\n    }\n}\n\n}  // namespace snapshot\n}  // namespace android\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/test_v3.cpp",
    "content": "//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <sys/stat.h>\n\n#include <cstdio>\n#include <limits>\n#include <memory>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_reader.h>\n#include <libsnapshot/cow_writer.h>\n#include <storage_literals/storage_literals.h>\n#include \"writer_v2.h\"\n#include \"writer_v3.h\"\n\nusing android::base::unique_fd;\nusing testing::AssertionFailure;\nusing testing::AssertionResult;\nusing testing::AssertionSuccess;\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android::storage_literals;\nusing ::testing::TestWithParam;\n\nclass CowTestV3 : public ::testing::Test {\n  protected:\n    virtual void SetUp() override {\n        cow_ = std::make_unique<TemporaryFile>();\n        ASSERT_GE(cow_->fd, 0) << strerror(errno);\n    }\n\n    virtual void TearDown() override { cow_ = nullptr; }\n\n    unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }\n\n    std::unique_ptr<TemporaryFile> cow_;\n};\n\n// Helper to check read sizes.\nstatic inline bool ReadData(CowReader& reader, const CowOperation* op, void* buffer, size_t size) {\n    return reader.ReadData(op, buffer, size) == size;\n}\n\nTEST_F(CowTestV3, CowHeaderV2Test) {\n    CowOptions options;\n    options.cluster_ops = 5;\n    options.num_merge_ops = 1;\n    options.block_size = 4096;\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n    auto writer_v2 = std::make_unique<CowWriterV2>(options, GetCowFd());\n    ASSERT_TRUE(writer_v2->Initialize());\n    ASSERT_TRUE(writer_v2->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.GetHeader();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, 2);\n    ASSERT_EQ(header.prefix.minor_version, 0);\n    ASSERT_EQ(header.block_size, options.block_size);\n    ASSERT_EQ(header.cluster_ops, options.cluster_ops);\n}\n\nTEST_F(CowTestV3, Header) {\n    CowOptions options;\n    options.op_count_max = 15;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.GetHeader();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, 3);\n    ASSERT_EQ(header.prefix.minor_version, 0);\n    ASSERT_EQ(header.block_size, options.block_size);\n    ASSERT_EQ(header.cluster_ops, 0);\n}\n\nTEST_F(CowTestV3, MaxOp) {\n    CowOptions options;\n    options.op_count_max = 20;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    ASSERT_FALSE(writer->AddZeroBlocks(1, 21));\n    ASSERT_TRUE(writer->AddZeroBlocks(1, 20));\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_FALSE(writer->AddRawBlocks(5, data.data(), data.size()));\n\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    ASSERT_EQ(reader.header_v3().op_count, 20);\n}\n\nTEST_F(CowTestV3, MaxOpSingleThreadCompression) {\n    CowOptions options;\n    options.op_count_max = 20;\n    options.num_compress_threads = 1;\n    options.compression_factor = 4096 * 8;\n    options.compression = \"lz4\";\n\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    ASSERT_TRUE(writer->AddZeroBlocks(1, 20));\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_FALSE(writer->AddRawBlocks(5, data.data(), data.size()));\n\n    ASSERT_TRUE(writer->Finalize());\n}\n\nTEST_F(CowTestV3, MaxOpMultiThreadCompression) {\n    CowOptions options;\n    options.op_count_max = 20;\n    options.num_compress_threads = 2;\n    options.compression_factor = 4096 * 8;\n    options.compression = \"lz4\";\n\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    ASSERT_TRUE(writer->AddZeroBlocks(1, 20));\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_FALSE(writer->AddRawBlocks(5, data.data(), data.size()));\n\n    ASSERT_TRUE(writer->Finalize());\n}\n\nTEST_F(CowTestV3, ZeroOp) {\n    CowOptions options;\n    options.op_count_max = 20;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    ASSERT_TRUE(writer->AddZeroBlocks(1, 2));\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    ASSERT_EQ(reader.header_v3().op_count, 2);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    auto op = iter->Get();\n    ASSERT_EQ(op->type(), kCowZeroOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 1);\n    ASSERT_EQ(op->source(), 0);\n\n    iter->Next();\n    ASSERT_FALSE(iter->AtEnd());\n    op = iter->Get();\n\n    ASSERT_EQ(op->type(), kCowZeroOp);\n    ASSERT_EQ(op->data_length, 0);\n    ASSERT_EQ(op->new_block, 2);\n    ASSERT_EQ(op->source(), 0);\n}\n\nTEST_F(CowTestV3, ReplaceOp) {\n    CowOptions options;\n    options.op_count_max = 20;\n    options.scratch_space = false;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.header_v3();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, 3);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n    ASSERT_EQ(header.block_size, options.block_size);\n    ASSERT_EQ(header.op_count, 1);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    auto op = iter->Get();\n    std::string sink(data.size(), '\\0');\n\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_EQ(op->data_length, 4096);\n    ASSERT_EQ(op->new_block, 5);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n}\n\nTEST_F(CowTestV3, BigReplaceOp) {\n    CowOptions options;\n    options.op_count_max = 10000;\n    options.batch_write = true;\n    options.cluster_ops = 2048;\n\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size * 4096, '\\0');\n    for (int i = 0; i < data.size(); i++) {\n        data[i] = static_cast<char>('A' + i / options.block_size);\n    }\n    ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.header_v3();\n    ASSERT_EQ(header.op_count, 4096);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    size_t i = 0;\n\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n        std::string sink(options.block_size, '\\0');\n        ASSERT_EQ(op->type(), kCowReplaceOp);\n        ASSERT_EQ(op->data_length, options.block_size);\n        ASSERT_EQ(op->new_block, 5 + i);\n        ASSERT_TRUE(ReadData(reader, op, sink.data(), options.block_size));\n        ASSERT_EQ(std::string_view(sink),\n                  std::string_view(data).substr(i * options.block_size, options.block_size))\n                << \" readback data for \" << i << \"th block does not match\";\n        iter->Next();\n        i++;\n    }\n}\n\nTEST_F(CowTestV3, ConsecutiveReplaceOp) {\n    CowOptions options;\n    options.op_count_max = 20;\n    options.scratch_space = false;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    std::string data;\n    data.resize(options.block_size * 5);\n    for (int i = 0; i < data.size(); i++) {\n        data[i] = static_cast<char>('A' + i / options.block_size);\n    }\n\n    ASSERT_TRUE(writer->AddRawBlocks(5, data.data(), data.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.header_v3();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, 3);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n    ASSERT_EQ(header.block_size, options.block_size);\n    ASSERT_EQ(header.op_count, 5);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    size_t i = 0;\n\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n        std::string sink(options.block_size, '\\0');\n        ASSERT_EQ(op->type(), kCowReplaceOp);\n        ASSERT_EQ(op->data_length, options.block_size);\n        ASSERT_EQ(op->new_block, 5 + i);\n        ASSERT_TRUE(ReadData(reader, op, sink.data(), options.block_size));\n        ASSERT_EQ(std::string_view(sink),\n                  std::string_view(data).substr(i * options.block_size, options.block_size))\n                << \" readback data for \" << i << \"th block does not match\";\n        iter->Next();\n        i++;\n    }\n\n    ASSERT_EQ(i, 5);\n}\n\nTEST_F(CowTestV3, CopyOp) {\n    CowOptions options;\n    options.op_count_max = 100;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n\n    ASSERT_TRUE(writer->AddCopy(10, 1000, 100));\n    ASSERT_TRUE(writer->Finalize());\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.header_v3();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, 3);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n    ASSERT_EQ(header.block_size, options.block_size);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    size_t i = 0;\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n        ASSERT_EQ(op->type(), kCowCopyOp);\n        ASSERT_EQ(op->data_length, 0);\n        ASSERT_EQ(op->new_block, 10 + i);\n        ASSERT_EQ(op->source(), 1000 + i);\n        iter->Next();\n        i += 1;\n    }\n\n    ASSERT_EQ(i, 100);\n}\n\nTEST_F(CowTestV3, XorOp) {\n    CowOptions options;\n    options.op_count_max = 100;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n\n    std::string data = \"This is test data-1. Testing xor\";\n    data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.header_v3();\n    ASSERT_EQ(header.op_count, 1);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n    std::string sink(data.size(), '\\0');\n\n    ASSERT_EQ(op->type(), kCowXorOp);\n    ASSERT_EQ(op->data_length, 4096);\n    ASSERT_EQ(op->new_block, 50);\n    ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n}\n\nTEST_F(CowTestV3, ConsecutiveXorOp) {\n    CowOptions options;\n    options.op_count_max = 100;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n\n    std::string data;\n    data.resize(options.block_size * 5);\n    for (int i = 0; i < data.size(); i++) {\n        data[i] = char(rand() % 256);\n    }\n\n    ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.header_v3();\n    ASSERT_EQ(header.op_count, 5);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    std::string sink(data.size(), '\\0');\n    size_t i = 0;\n\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n        ASSERT_EQ(op->type(), kCowXorOp);\n        ASSERT_EQ(op->data_length, 4096);\n        ASSERT_EQ(op->new_block, 50 + i);\n        ASSERT_EQ(op->source(), 98314 + (i * options.block_size));  // 4096 * 24 + 10\n        ASSERT_TRUE(\n                ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));\n        iter->Next();\n        i++;\n    }\n    ASSERT_EQ(sink, data);\n\n    ASSERT_EQ(i, 5);\n}\n\nTEST_F(CowTestV3, AllOpsWithCompression) {\n    CowOptions options;\n    options.compression = \"gz\";\n    options.op_count_max = 100;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n\n    std::string data;\n    data.resize(options.block_size * 5);\n    for (int i = 0; i < data.size(); i++) {\n        data[i] = char(rand() % 4);\n    }\n\n    ASSERT_TRUE(writer->AddZeroBlocks(10, 5));\n    ASSERT_TRUE(writer->AddCopy(15, 3, 5));\n    ASSERT_TRUE(writer->AddRawBlocks(18, data.data(), data.size()));\n    ASSERT_TRUE(writer->AddXorBlocks(50, data.data(), data.size(), 24, 10));\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    const auto& header = reader.header_v3();\n    ASSERT_EQ(header.prefix.magic, kCowMagicNumber);\n    ASSERT_EQ(header.prefix.major_version, 3);\n    ASSERT_EQ(header.prefix.minor_version, kCowVersionMinor);\n    ASSERT_EQ(header.block_size, options.block_size);\n    ASSERT_EQ(header.buffer_size, BUFFER_REGION_DEFAULT_SIZE);\n    ASSERT_EQ(header.op_count, 20);\n    ASSERT_EQ(header.op_count_max, 100);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n\n    for (size_t i = 0; i < 5; i++) {\n        auto op = iter->Get();\n        ASSERT_EQ(op->type(), kCowZeroOp);\n        ASSERT_EQ(op->new_block, 10 + i);\n        iter->Next();\n    }\n    for (size_t i = 0; i < 5; i++) {\n        auto op = iter->Get();\n        ASSERT_EQ(op->type(), kCowCopyOp);\n        ASSERT_EQ(op->new_block, 15 + i);\n        ASSERT_EQ(op->source(), 3 + i);\n        iter->Next();\n    }\n    std::string sink(data.size(), '\\0');\n\n    for (size_t i = 0; i < 5; i++) {\n        auto op = iter->Get();\n        ASSERT_EQ(op->type(), kCowReplaceOp);\n        ASSERT_EQ(op->new_block, 18 + i);\n        ASSERT_EQ(reader.ReadData(op, sink.data() + (i * options.block_size), options.block_size),\n                  options.block_size);\n        iter->Next();\n    }\n    ASSERT_EQ(sink, data);\n\n    std::fill(sink.begin(), sink.end(), '\\0');\n    for (size_t i = 0; i < 5; i++) {\n        auto op = iter->Get();\n        ASSERT_EQ(op->type(), kCowXorOp);\n        ASSERT_EQ(op->new_block, 50 + i);\n        ASSERT_EQ(op->source(), 98314 + (i * options.block_size));  // 4096 * 24 + 10\n        ASSERT_TRUE(\n                ReadData(reader, op, sink.data() + (i * options.block_size), options.block_size));\n        iter->Next();\n    }\n    ASSERT_EQ(sink, data);\n}\n\nTEST_F(CowTestV3, GzCompression) {\n    CowOptions options;\n    options.op_count_max = 100;\n    options.compression = \"gz\";\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto header = reader.header_v3();\n    ASSERT_EQ(header.compression_algorithm, kCowCompressGz);\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n    ASSERT_FALSE(iter->AtEnd());\n    auto op = iter->Get();\n\n    std::string sink(data.size(), '\\0');\n\n    ASSERT_EQ(op->type(), kCowReplaceOp);\n    ASSERT_EQ(op->data_length, 56);  // compressed!\n    ASSERT_EQ(op->new_block, 50);\n    ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size()));\n    ASSERT_EQ(sink, data);\n\n    iter->Next();\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTestV3, ResumePointTest) {\n    CowOptions options;\n    options.op_count_max = 100;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n\n    ASSERT_TRUE(writer->AddZeroBlocks(0, 15));\n    ASSERT_TRUE(writer->AddLabel(0));\n    ASSERT_TRUE(writer->AddZeroBlocks(15, 15));\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto header = reader.header_v3();\n    ASSERT_EQ(header.op_count, 30);\n\n    CowWriterV3 second_writer(options, GetCowFd());\n    ASSERT_TRUE(second_writer.Initialize(0));\n    ASSERT_TRUE(second_writer.Finalize());\n\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    header = reader.header_v3();\n    ASSERT_EQ(header.op_count, 15);\n}\n\nTEST_F(CowTestV3, BufferMetadataSyncTest) {\n    CowOptions options;\n    options.op_count_max = 100;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    /*\n    Header metadafields\n    sequence_data_count = 0;\n    resume_point_count = 0;\n    resume_point_max = 4;\n    */\n    ASSERT_TRUE(writer->Finalize());\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto header = reader.header_v3();\n    ASSERT_EQ(header.sequence_data_count, static_cast<uint64_t>(0));\n    ASSERT_EQ(header.resume_point_count, 0);\n    ASSERT_EQ(header.resume_point_max, 4);\n\n    writer->AddLabel(0);\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    header = reader.header_v3();\n    ASSERT_EQ(header.sequence_data_count, static_cast<uint64_t>(0));\n    ASSERT_EQ(header.resume_point_count, 1);\n    ASSERT_EQ(header.resume_point_max, 4);\n\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    header = reader.header_v3();\n\n    /*\n    Header metadafields\n    sequence_data_count = 1;\n    resume_point_count = 0;\n    resume_point_max = 4;\n    */\n}\n\nTEST_F(CowTestV3, SequenceTest) {\n    CowOptions options;\n    constexpr int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;\n    options.op_count_max = seq_len;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    // sequence data. This just an arbitrary set of integers that specify the merge order. The\n    // actual calculation is done by update_engine and passed to writer. All we care about here is\n    // writing that data correctly\n    uint32_t sequence[seq_len];\n    for (int i = 0; i < seq_len; i++) {\n        sequence[i] = i + 1;\n    }\n\n    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));\n    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len - 1));\n    std::vector<uint8_t> data(writer->GetBlockSize());\n    for (size_t i = 0; i < data.size(); i++) {\n        data[i] = static_cast<uint8_t>(i & 0xFF);\n    }\n    ASSERT_TRUE(writer->AddRawBlocks(seq_len, data.data(), data.size()));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n    auto iter = reader.GetRevMergeOpIter();\n\n    for (int i = 0; i < seq_len; i++) {\n        ASSERT_TRUE(!iter->AtEnd());\n        const auto& op = iter->Get();\n\n        ASSERT_EQ(op->new_block, seq_len - i);\n        if (op->new_block == seq_len) {\n            std::vector<uint8_t> read_back(writer->GetBlockSize());\n            ASSERT_EQ(reader.ReadData(op, read_back.data(), read_back.size()),\n                      static_cast<ssize_t>(read_back.size()));\n            ASSERT_EQ(read_back, data);\n        }\n\n        iter->Next();\n    }\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTestV3, MissingSeqOp) {\n    CowOptions options;\n    options.op_count_max = std::numeric_limits<uint32_t>::max();\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    const int seq_len = 10;\n    uint32_t sequence[seq_len];\n    for (int i = 0; i < seq_len; i++) {\n        sequence[i] = i + 1;\n    }\n    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));\n    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len - 1));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_FALSE(reader.Parse(cow_->fd));\n}\n\nTEST_F(CowTestV3, ResumeSeqOp) {\n    CowOptions options;\n    options.op_count_max = std::numeric_limits<uint32_t>::max();\n    auto writer = std::make_unique<CowWriterV3>(options, GetCowFd());\n    const int seq_len = 10;\n    uint32_t sequence[seq_len];\n    for (int i = 0; i < seq_len; i++) {\n        sequence[i] = i + 1;\n    }\n    ASSERT_TRUE(writer->Initialize());\n\n    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));\n    ASSERT_TRUE(writer->AddZeroBlocks(1, seq_len / 2));\n    ASSERT_TRUE(writer->AddLabel(1));\n    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, 1));\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n    auto reader = std::make_unique<CowReader>();\n    ASSERT_TRUE(reader->Parse(cow_->fd, 1));\n    auto itr = reader->GetRevMergeOpIter();\n    ASSERT_TRUE(itr->AtEnd());\n\n    writer = std::make_unique<CowWriterV3>(options, GetCowFd());\n    ASSERT_TRUE(writer->Initialize({1}));\n    ASSERT_TRUE(writer->AddZeroBlocks(1 + seq_len / 2, seq_len / 2));\n    ASSERT_TRUE(writer->Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    reader = std::make_unique<CowReader>();\n    ASSERT_TRUE(reader->Parse(cow_->fd));\n\n    auto iter = reader->GetRevMergeOpIter();\n\n    uint64_t expected_block = 10;\n    while (!iter->AtEnd() && expected_block > 0) {\n        ASSERT_FALSE(iter->AtEnd());\n        const auto& op = iter->Get();\n\n        ASSERT_EQ(op->new_block, expected_block);\n\n        iter->Next();\n        expected_block--;\n    }\n    ASSERT_EQ(expected_block, 0);\n    ASSERT_TRUE(iter->AtEnd());\n}\n\nTEST_F(CowTestV3, SetSourceManyTimes) {\n    CowOperationV3 op{};\n    op.set_source(1);\n    ASSERT_EQ(op.source(), 1);\n    op.set_source(2);\n    ASSERT_EQ(op.source(), 2);\n    op.set_source(4);\n    ASSERT_EQ(op.source(), 4);\n    op.set_source(8);\n    ASSERT_EQ(op.source(), 8);\n}\n\nTEST_F(CowTestV3, SetTypeManyTimes) {\n    CowOperationV3 op{};\n    op.set_type(kCowCopyOp);\n    ASSERT_EQ(op.type(), kCowCopyOp);\n    op.set_type(kCowReplaceOp);\n    ASSERT_EQ(op.type(), kCowReplaceOp);\n    op.set_type(kCowZeroOp);\n    ASSERT_EQ(op.type(), kCowZeroOp);\n    op.set_type(kCowXorOp);\n    ASSERT_EQ(op.type(), kCowXorOp);\n}\n\nTEST_F(CowTestV3, SetTypeSourceInverleave) {\n    CowOperationV3 op{};\n    op.set_type(kCowCopyOp);\n    ASSERT_EQ(op.type(), kCowCopyOp);\n    op.set_source(0x010203040506);\n    ASSERT_EQ(op.source(), 0x010203040506);\n    ASSERT_EQ(op.type(), kCowCopyOp);\n    op.set_type(kCowReplaceOp);\n    ASSERT_EQ(op.source(), 0x010203040506);\n    ASSERT_EQ(op.type(), kCowReplaceOp);\n}\n\nTEST_F(CowTestV3, CowSizeEstimate) {\n    CowOptions options{};\n    options.compression = \"none\";\n    auto estimator = android::snapshot::CreateCowEstimator(3, options);\n    ASSERT_TRUE(estimator->AddZeroBlocks(0, 1024 * 1024));\n    const auto cow_size = estimator->GetCowSizeInfo().cow_size;\n    options.op_count_max = 1024 * 1024;\n    options.max_blocks = 1024 * 1024;\n    CowWriterV3 writer(options, GetCowFd());\n    ASSERT_TRUE(writer.Initialize());\n    ASSERT_TRUE(writer.AddZeroBlocks(0, 1024 * 1024));\n\n    ASSERT_LE(writer.GetCowSizeInfo().cow_size, cow_size);\n}\n\nTEST_F(CowTestV3, CopyOpMany) {\n    CowOptions options;\n    options.op_count_max = 100;\n    CowWriterV3 writer(options, GetCowFd());\n    writer.Initialize();\n    ASSERT_TRUE(writer.AddCopy(100, 50, 50));\n    ASSERT_TRUE(writer.AddCopy(150, 100, 50));\n    ASSERT_TRUE(writer.Finalize());\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(GetCowFd()));\n    auto it = reader.GetOpIter();\n    for (size_t i = 0; i < 100; i++) {\n        ASSERT_FALSE(it->AtEnd()) << \" op iterator ended at \" << i;\n        const auto op = *it->Get();\n        ASSERT_EQ(op.type(), kCowCopyOp);\n        ASSERT_EQ(op.new_block, 100 + i);\n        it->Next();\n    }\n}\n\nTEST_F(CowTestV3, CheckOpCount) {\n    CowOptions options;\n    options.op_count_max = 20;\n    options.batch_write = true;\n    options.cluster_ops = 200;\n    auto writer = CreateCowWriter(3, options, GetCowFd());\n    ASSERT_TRUE(writer->AddZeroBlocks(0, 19));\n    ASSERT_FALSE(writer->AddZeroBlocks(0, 19));\n}\n\nstruct TestParam {\n    std::string compression;\n    int block_size;\n    int num_threads;\n    size_t cluster_ops;\n};\n\nclass VariableBlockTest : public ::testing::TestWithParam<TestParam> {\n  protected:\n    virtual void SetUp() override {\n        cow_ = std::make_unique<TemporaryFile>();\n        ASSERT_GE(cow_->fd, 0) << strerror(errno);\n    }\n\n    virtual void TearDown() override { cow_ = nullptr; }\n\n    unique_fd GetCowFd() { return unique_fd{dup(cow_->fd)}; }\n\n    std::unique_ptr<TemporaryFile> cow_;\n};\n\n// Helper to check read sizes.\nstatic inline void ReadBlockData(CowReader& reader, const CowOperation* op, void* buffer,\n                                 size_t size) {\n    size_t block_size = CowOpCompressionSize(op, 4096);\n    std::string data(block_size, '\\0');\n    size_t value = reader.ReadData(op, data.data(), block_size);\n    ASSERT_TRUE(value == block_size);\n    std::memcpy(buffer, data.data(), size);\n}\n\nTEST_P(VariableBlockTest, VariableBlockCompressionTest) {\n    const TestParam params = GetParam();\n\n    CowOptions options;\n    options.op_count_max = 100000;\n    options.compression = params.compression;\n    options.num_compress_threads = params.num_threads;\n    options.batch_write = true;\n    options.compression_factor = params.block_size;\n    options.cluster_ops = params.cluster_ops;\n\n    CowWriterV3 writer(options, GetCowFd());\n\n    ASSERT_TRUE(writer.Initialize());\n\n    std::string xor_data = \"This is test data-1. Testing xor\";\n    xor_data.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer.AddXorBlocks(50, xor_data.data(), xor_data.size(), 24, 10));\n\n    // Large number of blocks\n    std::string data = \"This is test data-2. Testing replace ops\";\n    data.resize(options.block_size * 2048, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(100, data.data(), data.size()));\n\n    std::string data2 = \"This is test data-3. Testing replace ops\";\n    data2.resize(options.block_size * 259, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(6000, data2.data(), data2.size()));\n\n    // Test data size is smaller than block size\n\n    // 4k block\n    std::string data3 = \"This is test data-4. Testing replace ops\";\n    data3.resize(options.block_size, '\\0');\n    ASSERT_TRUE(writer.AddRawBlocks(9000, data3.data(), data3.size()));\n\n    // 8k block\n    std::string data4;\n    data4.resize(options.block_size * 2, '\\0');\n    for (size_t i = 0; i < data4.size(); i++) {\n        data4[i] = static_cast<char>('A' + i / options.block_size);\n    }\n    ASSERT_TRUE(writer.AddRawBlocks(10000, data4.data(), data4.size()));\n\n    // 16k block\n    std::string data5;\n    data.resize(options.block_size * 4, '\\0');\n    for (int i = 0; i < data5.size(); i++) {\n        data5[i] = static_cast<char>('C' + i / options.block_size);\n    }\n    ASSERT_TRUE(writer.AddRawBlocks(11000, data5.data(), data5.size()));\n\n    // 64k Random buffer which cannot be compressed\n    unique_fd rnd_fd(open(\"/dev/random\", O_RDONLY));\n    ASSERT_GE(rnd_fd, 0);\n    std::string random_buffer;\n    random_buffer.resize(65536, '\\0');\n    ASSERT_EQ(android::base::ReadFullyAtOffset(rnd_fd, random_buffer.data(), 65536, 0), true);\n    ASSERT_TRUE(writer.AddRawBlocks(12000, random_buffer.data(), 65536));\n\n    ASSERT_TRUE(writer.Finalize());\n\n    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(cow_->fd));\n\n    auto iter = reader.GetOpIter();\n    ASSERT_NE(iter, nullptr);\n\n    while (!iter->AtEnd()) {\n        auto op = iter->Get();\n\n        if (op->type() == kCowXorOp) {\n            std::string sink(xor_data.size(), '\\0');\n            ASSERT_EQ(op->new_block, 50);\n            ASSERT_EQ(op->source(), 98314);  // 4096 * 24 + 10\n            ReadBlockData(reader, op, sink.data(), sink.size());\n            ASSERT_EQ(sink, xor_data);\n        }\n        if (op->type() == kCowReplaceOp) {\n            if (op->new_block == 100) {\n                data.resize(options.block_size);\n                std::string sink(data.size(), '\\0');\n                ReadBlockData(reader, op, sink.data(), sink.size());\n                ASSERT_EQ(sink.size(), data.size());\n                ASSERT_EQ(sink, data);\n            }\n            if (op->new_block == 6000) {\n                data2.resize(options.block_size);\n                std::string sink(data2.size(), '\\0');\n                ReadBlockData(reader, op, sink.data(), sink.size());\n                ASSERT_EQ(sink, data2);\n            }\n            if (op->new_block == 9000) {\n                std::string sink(data3.size(), '\\0');\n                ReadBlockData(reader, op, sink.data(), sink.size());\n                ASSERT_EQ(sink, data3);\n            }\n            if (op->new_block == 10000) {\n                data4.resize(options.block_size);\n                std::string sink(options.block_size, '\\0');\n                ReadBlockData(reader, op, sink.data(), sink.size());\n                ASSERT_EQ(sink, data4);\n            }\n            if (op->new_block == 11000) {\n                data5.resize(options.block_size);\n                std::string sink(options.block_size, '\\0');\n                ReadBlockData(reader, op, sink.data(), sink.size());\n                ASSERT_EQ(sink, data5);\n            }\n            if (op->new_block == 12000) {\n                random_buffer.resize(options.block_size);\n                std::string sink(options.block_size, '\\0');\n                ReadBlockData(reader, op, sink.data(), sink.size());\n                ASSERT_EQ(sink, random_buffer);\n            }\n        }\n\n        iter->Next();\n    }\n}\n\nstd::vector<TestParam> GetTestConfigs() {\n    std::vector<TestParam> testParams;\n\n    std::vector<int> block_sizes = {4_KiB, 8_KiB, 16_KiB, 32_KiB, 64_KiB, 128_KiB, 256_KiB};\n    std::vector<std::string> compression_algo = {\"none\", \"lz4\", \"zstd\", \"gz\"};\n    std::vector<int> threads = {1, 2};\n    // This will also test batch size\n    std::vector<size_t> cluster_ops = {1, 256};\n\n    // This should test 112 combination\n    for (auto block : block_sizes) {\n        for (auto compression : compression_algo) {\n            for (auto thread : threads) {\n                for (auto cluster : cluster_ops) {\n                    TestParam param;\n                    param.block_size = block;\n                    param.compression = compression;\n                    param.num_threads = thread;\n                    param.cluster_ops = cluster;\n                    testParams.push_back(std::move(param));\n                }\n            }\n        }\n    }\n\n    return testParams;\n}\n\nINSTANTIATE_TEST_SUITE_P(CompressorsWithVariableBlocks, VariableBlockTest,\n                         ::testing::ValuesIn(GetTestConfigs()));\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/writer_base.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"writer_base.h\"\n\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include \"snapshot_reader.h\"\n\n// The info messages here are spammy, but as useful for update_engine. Disable\n// them when running on the host.\n#ifdef __ANDROID__\n#define LOG_INFO LOG(INFO)\n#else\n#define LOG_INFO LOG(VERBOSE)\n#endif\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::borrowed_fd;\nusing android::base::unique_fd;\n\nnamespace {\nstd::string GetFdPath(borrowed_fd fd) {\n    const auto fd_path = \"/proc/self/fd/\" + std::to_string(fd.get());\n    std::string file_path(512, '\\0');\n    const auto err = readlink(fd_path.c_str(), file_path.data(), file_path.size());\n    if (err <= 0) {\n        PLOG(ERROR) << \"Failed to determine path for fd \" << fd.get();\n        file_path.clear();\n    } else {\n        file_path.resize(err);\n    }\n    return file_path;\n}\n}  // namespace\n\nCowWriterBase::CowWriterBase(const CowOptions& options, unique_fd&& fd)\n    : options_(options), fd_(std::move(fd)) {}\n\nbool CowWriterBase::InitFd() {\n    if (fd_.get() < 0) {\n        fd_.reset(open(\"/dev/null\", O_RDWR | O_CLOEXEC));\n        if (fd_ < 0) {\n            PLOG(ERROR) << \"open /dev/null failed\";\n            return false;\n        }\n        is_dev_null_ = true;\n        return true;\n    }\n\n    struct stat stat {};\n    if (fstat(fd_.get(), &stat) < 0) {\n        PLOG(ERROR) << \"fstat failed\";\n        return false;\n    }\n    const auto file_path = GetFdPath(fd_);\n    is_block_device_ = S_ISBLK(stat.st_mode);\n    if (is_block_device_) {\n        uint64_t size_in_bytes = 0;\n        if (ioctl(fd_.get(), BLKGETSIZE64, &size_in_bytes)) {\n            PLOG(ERROR) << \"Failed to get total size for: \" << fd_.get();\n            return false;\n        }\n        cow_image_size_ = size_in_bytes;\n        LOG_INFO << \"COW image \" << file_path << \" has size \" << size_in_bytes;\n    } else {\n        LOG_INFO << \"COW image \" << file_path\n                 << \" is not a block device, assuming unlimited space.\";\n    }\n    return true;\n}\n\nbool CowWriterBase::AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {\n    CHECK(num_blocks != 0);\n\n    for (size_t i = 0; i < num_blocks; i++) {\n        if (!ValidateNewBlock(new_block + i)) {\n            return false;\n        }\n    }\n\n    return EmitCopy(new_block, old_block, num_blocks);\n}\n\nbool CowWriterBase::AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) {\n    if (size % options_.block_size != 0) {\n        LOG(ERROR) << \"AddRawBlocks: size \" << size << \" is not a multiple of \"\n                   << options_.block_size;\n        return false;\n    }\n\n    uint64_t num_blocks = size / options_.block_size;\n    uint64_t last_block = new_block_start + num_blocks - 1;\n    if (!ValidateNewBlock(last_block)) {\n        return false;\n    }\n    return EmitRawBlocks(new_block_start, data, size);\n}\n\nbool CowWriterBase::AddXorBlocks(uint32_t new_block_start, const void* data, size_t size,\n                                 uint32_t old_block, uint16_t offset) {\n    if (size % options_.block_size != 0) {\n        LOG(ERROR) << \"AddRawBlocks: size \" << size << \" is not a multiple of \"\n                   << options_.block_size;\n        return false;\n    }\n\n    uint64_t num_blocks = size / options_.block_size;\n    uint64_t last_block = new_block_start + num_blocks - 1;\n    if (!ValidateNewBlock(last_block)) {\n        return false;\n    }\n    if (offset >= options_.block_size) {\n        LOG(ERROR) << \"AddXorBlocks: offset \" << offset << \" is not less than \"\n                   << options_.block_size;\n    }\n    return EmitXorBlocks(new_block_start, data, size, old_block, offset);\n}\n\nbool CowWriterBase::AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {\n    uint64_t last_block = new_block_start + num_blocks - 1;\n    if (!ValidateNewBlock(last_block)) {\n        return false;\n    }\n    return EmitZeroBlocks(new_block_start, num_blocks);\n}\n\nbool CowWriterBase::AddLabel(uint64_t label) {\n    return EmitLabel(label);\n}\n\nbool CowWriterBase::AddSequenceData(size_t num_ops, const uint32_t* data) {\n    return EmitSequenceData(num_ops, data);\n}\n\nbool CowWriterBase::ValidateNewBlock(uint64_t new_block) {\n    if (options_.max_blocks && new_block >= options_.max_blocks.value()) {\n        LOG(ERROR) << \"New block \" << new_block << \" exceeds maximum block count \"\n                   << options_.max_blocks.value();\n        return false;\n    }\n    return true;\n}\n\nstd::unique_ptr<ICowReader> CowWriterBase::OpenReader() {\n    unique_fd cow_fd(fcntl(fd_.get(), F_DUPFD | F_DUPFD_CLOEXEC, 0));\n    if (cow_fd < 0) {\n        PLOG(ERROR) << \"CowWriterV2::OpenReander: dup COW device\";\n        return nullptr;\n    }\n\n    auto cow = std::make_unique<CowReader>();\n    if (!cow->Parse(std::move(cow_fd))) {\n        LOG(ERROR) << \"CowWriterV2::OpenReader: unable to read COW\";\n        return nullptr;\n    }\n    return cow;\n}\n\nstd::unique_ptr<chromeos_update_engine::FileDescriptor> CowWriterBase::OpenFileDescriptor(\n        const std::optional<std::string>& source_device) {\n    auto reader = OpenReader();\n    if (!reader) {\n        return nullptr;\n    }\n\n    std::optional<uint64_t> block_dev_size;\n    if (options_.max_blocks) {\n        block_dev_size = {*options_.max_blocks * options_.block_size};\n    }\n\n    return std::make_unique<CompressedSnapshotReader>(std::move(reader), source_device,\n                                                      block_dev_size);\n}\n\nbool CowWriterBase::Sync() {\n    if (is_dev_null_) {\n        return true;\n    }\n    if (fsync(fd_.get()) < 0) {\n        PLOG(ERROR) << \"fsync failed\";\n        return false;\n    }\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/writer_base.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <libsnapshot/cow_writer.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass CowWriterBase : public ICowWriter {\n  public:\n    CowWriterBase(const CowOptions& options, android::base::unique_fd&& fd);\n    virtual ~CowWriterBase() {}\n\n    // Set up the writer.\n    // The file starts from the beginning.\n    //\n    // If fd is < 0, the CowWriter will be opened against /dev/null. This is for\n    // computing COW sizes without using storage space.\n    //\n    // If a label is given, any operations after the given label will be dropped.\n    // If the given label is not found, Initialize will fail.\n    virtual bool Initialize(std::optional<uint64_t> label = {}) = 0;\n\n    bool Sync();\n    bool AddCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;\n    bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;\n    bool AddXorBlocks(uint32_t new_block_start, const void* data, size_t size, uint32_t old_block,\n                      uint16_t offset) override;\n    bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;\n    bool AddLabel(uint64_t label) override;\n    bool AddSequenceData(size_t num_ops, const uint32_t* data) override;\n    uint32_t GetBlockSize() const override { return options_.block_size; }\n    std::optional<uint32_t> GetMaxBlocks() const override { return options_.max_blocks; }\n    std::unique_ptr<ICowReader> OpenReader() override;\n    std::unique_ptr<FileDescriptor> OpenFileDescriptor(\n            const std::optional<std::string>& source_device) override;\n\n    const CowOptions& options() const { return options_; }\n\n  protected:\n    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) = 0;\n    virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) = 0;\n    virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,\n                               uint32_t old_block, uint16_t offset) = 0;\n    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;\n    virtual bool EmitLabel(uint64_t label) = 0;\n    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) = 0;\n\n    bool InitFd();\n    bool ValidateNewBlock(uint64_t new_block);\n\n    bool IsEstimating() const { return is_dev_null_; }\n\n    CowOptions options_;\n\n    android::base::unique_fd fd_;\n    bool is_dev_null_ = false;\n    bool is_block_device_ = false;\n    uint64_t cow_image_size_ = INT64_MAX;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"writer_v2.h\"\n\n#include <sys/types.h>\n#include <sys/uio.h>\n#include <unistd.h>\n\n#include <future>\n#include <limits>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n#include <brotli/encode.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_reader.h>\n#include <libsnapshot/cow_writer.h>\n#include <lz4.h>\n#include <zlib.h>\n\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n\n#include \"android-base/parseint.h\"\n#include \"android-base/strings.h\"\n#include \"parser_v2.h\"\n\n// The info messages here are spammy, but as useful for update_engine. Disable\n// them when running on the host.\n#ifdef __ANDROID__\n#define LOG_INFO LOG(INFO)\n#else\n#define LOG_INFO LOG(VERBOSE)\n#endif\n\nnamespace android {\nnamespace snapshot {\n\nstatic_assert(sizeof(off_t) == sizeof(uint64_t));\n\nusing android::base::unique_fd;\n\nCowWriterV2::CowWriterV2(const CowOptions& options, unique_fd&& fd)\n    : CowWriterBase(options, std::move(fd)) {\n    SetupHeaders();\n    SetupWriteOptions();\n}\n\nCowWriterV2::~CowWriterV2() {\n    for (size_t i = 0; i < compress_threads_.size(); i++) {\n        CompressWorker* worker = compress_threads_[i].get();\n        if (worker) {\n            worker->Finalize();\n        }\n    }\n\n    bool ret = true;\n    for (auto& t : threads_) {\n        ret = t.get() && ret;\n    }\n\n    if (!ret) {\n        LOG(ERROR) << \"Compression failed\";\n    }\n    compress_threads_.clear();\n}\n\nvoid CowWriterV2::SetupWriteOptions() {\n    num_compress_threads_ = options_.num_compress_threads;\n\n    if (!num_compress_threads_) {\n        num_compress_threads_ = 1;\n        // We prefer not to have more than two threads as the overhead of additional\n        // threads is far greater than cutting down compression time.\n        if (header_.cluster_ops &&\n            android::base::GetBoolProperty(\"ro.virtual_ab.compression.threads\", false)) {\n            num_compress_threads_ = 2;\n        }\n    }\n\n    if (header_.cluster_ops &&\n        (android::base::GetBoolProperty(\"ro.virtual_ab.batch_writes\", false) ||\n         options_.batch_write)) {\n        batch_write_ = true;\n    }\n}\n\nvoid CowWriterV2::SetupHeaders() {\n    header_ = {};\n    header_.prefix.magic = kCowMagicNumber;\n    header_.prefix.major_version = kCowVersionMajor;\n    header_.prefix.minor_version = kCowVersionMinor;\n    header_.prefix.header_size = sizeof(CowHeader);\n    header_.footer_size = sizeof(CowFooter);\n    header_.op_size = sizeof(CowOperationV2);\n    header_.block_size = options_.block_size;\n    header_.num_merge_ops = options_.num_merge_ops;\n    header_.cluster_ops = options_.cluster_ops;\n    header_.buffer_size = 0;\n    footer_ = {};\n    footer_.op.data_length = 64;\n    footer_.op.type = kCowFooterOp;\n}\n\nbool CowWriterV2::ParseOptions() {\n    auto parts = android::base::Split(options_.compression, \",\");\n\n    if (parts.size() > 2) {\n        LOG(ERROR) << \"failed to parse compression parameters: invalid argument count: \"\n                   << parts.size() << \" \" << options_.compression;\n        return false;\n    }\n    auto algorithm = CompressionAlgorithmFromString(parts[0]);\n    if (!algorithm) {\n        LOG(ERROR) << \"unrecognized compression: \" << options_.compression;\n        return false;\n    }\n    if (parts.size() > 1) {\n        if (!android::base::ParseInt(parts[1], &compression_.compression_level)) {\n            LOG(ERROR) << \"failed to parse compression level invalid type: \" << parts[1];\n            return false;\n        }\n    } else {\n        compression_.compression_level =\n                CompressWorker::GetDefaultCompressionLevel(algorithm.value());\n    }\n\n    compression_.algorithm = *algorithm;\n\n    if (options_.cluster_ops == 1) {\n        LOG(ERROR) << \"Clusters must contain at least two operations to function.\";\n        return false;\n    }\n    return true;\n}\n\nvoid CowWriterV2::InitBatchWrites() {\n    if (batch_write_) {\n        cowop_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);\n        data_vec_ = std::make_unique<struct iovec[]>(header_.cluster_ops);\n        struct iovec* cowop_ptr = cowop_vec_.get();\n        struct iovec* data_ptr = data_vec_.get();\n        for (size_t i = 0; i < header_.cluster_ops; i++) {\n            std::unique_ptr<CowOperationV2> op = std::make_unique<CowOperationV2>();\n            cowop_ptr[i].iov_base = op.get();\n            cowop_ptr[i].iov_len = sizeof(CowOperationV2);\n            opbuffer_vec_.push_back(std::move(op));\n\n            std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(header_.block_size * 2);\n            data_ptr[i].iov_base = buffer.get();\n            data_ptr[i].iov_len = header_.block_size * 2;\n            databuffer_vec_.push_back(std::move(buffer));\n        }\n\n        current_op_pos_ = next_op_pos_;\n        current_data_pos_ = next_data_pos_;\n    }\n\n    LOG_INFO << \"Batch writes: \" << (batch_write_ ? \"enabled\" : \"disabled\");\n}\n\nvoid CowWriterV2::InitWorkers() {\n    if (num_compress_threads_ <= 1) {\n        LOG_INFO << \"Not creating new threads for compression.\";\n        return;\n    }\n    for (int i = 0; i < num_compress_threads_; i++) {\n        std::unique_ptr<ICompressor> compressor =\n                ICompressor::Create(compression_, header_.block_size);\n        auto wt = std::make_unique<CompressWorker>(std::move(compressor));\n        threads_.emplace_back(std::async(std::launch::async, &CompressWorker::RunThread, wt.get()));\n        compress_threads_.push_back(std::move(wt));\n    }\n\n    LOG_INFO << num_compress_threads_ << \" thread used for compression\";\n}\n\nbool CowWriterV2::Initialize(std::optional<uint64_t> label) {\n    if (!InitFd() || !ParseOptions()) {\n        return false;\n    }\n    if (!label) {\n        if (!OpenForWrite()) {\n            return false;\n        }\n    } else {\n        if (!OpenForAppend(*label)) {\n            return false;\n        }\n    }\n\n    if (!compress_threads_.size()) {\n        InitWorkers();\n    }\n    return true;\n}\n\nvoid CowWriterV2::InitPos() {\n    next_op_pos_ = sizeof(CowHeader) + header_.buffer_size;\n    cluster_size_ = header_.cluster_ops * sizeof(CowOperationV2);\n    if (header_.cluster_ops) {\n        next_data_pos_ = next_op_pos_ + cluster_size_;\n    } else {\n        next_data_pos_ = next_op_pos_ + sizeof(CowOperationV2);\n    }\n    current_cluster_size_ = 0;\n    current_data_size_ = 0;\n}\n\nbool CowWriterV2::OpenForWrite() {\n    // This limitation is tied to the data field size in CowOperationV2.\n    if (header_.block_size > std::numeric_limits<uint16_t>::max()) {\n        LOG(ERROR) << \"Block size is too large\";\n        return false;\n    }\n\n    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"lseek failed\";\n        return false;\n    }\n\n    if (options_.scratch_space) {\n        header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;\n    }\n\n    // Headers are not complete, but this ensures the file is at the right\n    // position.\n    if (!android::base::WriteFully(fd_, &header_, sizeof(CowHeader))) {\n        PLOG(ERROR) << \"write failed\";\n        return false;\n    }\n\n    if (options_.scratch_space) {\n        // Initialize the scratch space\n        std::string data(header_.buffer_size, 0);\n        if (!android::base::WriteFully(fd_, data.data(), header_.buffer_size)) {\n            PLOG(ERROR) << \"writing scratch space failed\";\n            return false;\n        }\n    }\n\n    if (!Sync()) {\n        LOG(ERROR) << \"Header sync failed\";\n        return false;\n    }\n\n    InitPos();\n    InitBatchWrites();\n\n    return true;\n}\n\nbool CowWriterV2::OpenForAppend(uint64_t label) {\n    CowHeaderV3 header_v3;\n    if (!ReadCowHeader(fd_, &header_v3)) {\n        return false;\n    }\n\n    header_ = header_v3;\n\n    CowParserV2 parser;\n    if (!parser.Parse(fd_, header_v3, {label})) {\n        return false;\n    }\n    if (header_.prefix.major_version > 2) {\n        LOG(ERROR) << \"CowWriterV2 tried to open incompatible version \"\n                   << header_.prefix.major_version;\n        return false;\n    }\n\n    options_.block_size = header_.block_size;\n    options_.cluster_ops = header_.cluster_ops;\n\n    // Reset this, since we're going to reimport all operations.\n    footer_.op.num_ops = 0;\n    InitPos();\n\n    for (const auto& op : *parser.get_v2ops()) {\n        AddOperation(op);\n    }\n\n    if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"lseek failed\";\n        return false;\n    }\n\n    InitBatchWrites();\n\n    return EmitClusterIfNeeded();\n}\n\nbool CowWriterV2::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {\n    CHECK(!merge_in_progress_);\n\n    for (size_t i = 0; i < num_blocks; i++) {\n        CowOperationV2 op = {};\n        op.type = kCowCopyOp;\n        op.new_block = new_block + i;\n        op.source = old_block + i;\n        if (!WriteOperation(op)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nbool CowWriterV2::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {\n    return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);\n}\n\nbool CowWriterV2::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,\n                                uint32_t old_block, uint16_t offset) {\n    return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);\n}\n\nbool CowWriterV2::CompressBlocks(size_t num_blocks, const void* data) {\n    size_t num_threads = (num_blocks == 1) ? 1 : num_compress_threads_;\n    size_t num_blocks_per_thread = num_blocks / num_threads;\n    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);\n    compressed_buf_.clear();\n    if (num_threads <= 1) {\n        if (!compressor_) {\n            compressor_ = ICompressor::Create(compression_, header_.block_size);\n        }\n        return CompressWorker::CompressBlocks(compressor_.get(), options_.block_size, data,\n                                              num_blocks, &compressed_buf_);\n    }\n    // Submit the blocks per thread. The retrieval of\n    // compressed buffers has to be done in the same order.\n    // We should not poll for completed buffers in a different order as the\n    // buffers are tightly coupled with block ordering.\n    for (size_t i = 0; i < num_threads; i++) {\n        CompressWorker* worker = compress_threads_[i].get();\n        if (i == num_threads - 1) {\n            num_blocks_per_thread = num_blocks;\n        }\n        worker->EnqueueCompressBlocks(iter, header_.block_size, num_blocks_per_thread);\n        iter += (num_blocks_per_thread * header_.block_size);\n        num_blocks -= num_blocks_per_thread;\n    }\n\n    for (size_t i = 0; i < num_threads; i++) {\n        CompressWorker* worker = compress_threads_[i].get();\n        if (!worker->GetCompressedBuffers(&compressed_buf_)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n\nbool CowWriterV2::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,\n                             uint64_t old_block, uint16_t offset, CowOperationType type) {\n    CHECK(!merge_in_progress_);\n    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);\n\n    // Update engine can potentially send 100MB of blocks at a time. We\n    // don't want to process all those blocks in one shot as it can\n    // stress the memory. Hence, process the blocks in chunks.\n    //\n    // 1024 blocks is reasonable given we will end up using max\n    // memory of ~4MB.\n    const size_t kProcessingBlocks = 1024;\n    size_t num_blocks = (size / header_.block_size);\n    size_t i = 0;\n\n    while (num_blocks) {\n        size_t pending_blocks = (std::min(kProcessingBlocks, num_blocks));\n\n        if (compression_.algorithm && num_compress_threads_ > 1) {\n            if (!CompressBlocks(pending_blocks, iter)) {\n                return false;\n            }\n            buf_iter_ = compressed_buf_.begin();\n            CHECK(pending_blocks == compressed_buf_.size());\n        }\n\n        num_blocks -= pending_blocks;\n\n        while (i < size / header_.block_size && pending_blocks) {\n            CowOperationV2 op = {};\n            op.new_block = new_block_start + i;\n            op.type = type;\n            if (type == kCowXorOp) {\n                op.source = (old_block + i) * header_.block_size + offset;\n            } else {\n                op.source = next_data_pos_;\n            }\n\n            if (compression_.algorithm) {\n                auto data = [&, this]() {\n                    if (num_compress_threads_ > 1) {\n                        auto data = std::move(*buf_iter_);\n                        buf_iter_++;\n                        return data;\n                    } else {\n                        if (!compressor_) {\n                            compressor_ = ICompressor::Create(compression_, header_.block_size);\n                        }\n\n                        auto data = compressor_->Compress(iter, header_.block_size);\n                        return data;\n                    }\n                }();\n                op.compression = compression_.algorithm;\n                op.data_length = static_cast<uint16_t>(data.size());\n\n                if (!WriteOperation(op, data.data(), data.size())) {\n                    PLOG(ERROR) << \"AddRawBlocks: write failed\";\n                    return false;\n                }\n            } else {\n                op.data_length = static_cast<uint16_t>(header_.block_size);\n                if (!WriteOperation(op, iter, header_.block_size)) {\n                    PLOG(ERROR) << \"AddRawBlocks: write failed\";\n                    return false;\n                }\n            }\n            iter += header_.block_size;\n\n            i += 1;\n            pending_blocks -= 1;\n        }\n\n        CHECK(pending_blocks == 0);\n    }\n    return true;\n}\n\nbool CowWriterV2::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {\n    CHECK(!merge_in_progress_);\n    for (uint64_t i = 0; i < num_blocks; i++) {\n        CowOperationV2 op = {};\n        op.type = kCowZeroOp;\n        op.new_block = new_block_start + i;\n        op.source = 0;\n        WriteOperation(op);\n    }\n    return true;\n}\n\nbool CowWriterV2::EmitLabel(uint64_t label) {\n    CHECK(!merge_in_progress_);\n    CowOperationV2 op = {};\n    op.type = kCowLabelOp;\n    op.source = label;\n    return WriteOperation(op) && Sync();\n}\n\nbool CowWriterV2::EmitSequenceData(size_t num_ops, const uint32_t* data) {\n    CHECK(!merge_in_progress_);\n    size_t to_add = 0;\n    size_t max_ops = (header_.block_size * 2) / sizeof(uint32_t);\n    while (num_ops > 0) {\n        CowOperationV2 op = {};\n        op.type = kCowSequenceOp;\n        op.source = next_data_pos_;\n        to_add = std::min(num_ops, max_ops);\n        op.data_length = static_cast<uint16_t>(to_add * sizeof(uint32_t));\n        if (!WriteOperation(op, data, op.data_length)) {\n            PLOG(ERROR) << \"AddSequenceData: write failed\";\n            return false;\n        }\n        num_ops -= to_add;\n        data += to_add;\n    }\n    return true;\n}\n\nbool CowWriterV2::EmitCluster() {\n    CowOperationV2 op = {};\n    op.type = kCowClusterOp;\n    // Next cluster starts after remainder of current cluster and the next data block.\n    op.source = current_data_size_ + cluster_size_ - current_cluster_size_ - sizeof(CowOperationV2);\n    return WriteOperation(op);\n}\n\nbool CowWriterV2::EmitClusterIfNeeded() {\n    // If there isn't room for another op and the cluster end op, end the current cluster\n    if (cluster_size_ && cluster_size_ < current_cluster_size_ + 2 * sizeof(CowOperationV2)) {\n        if (!EmitCluster()) return false;\n    }\n    return true;\n}\n\nbool CowWriterV2::Finalize() {\n    if (!FlushCluster()) {\n        LOG(ERROR) << \"Finalize: FlushCluster() failed\";\n        return false;\n    }\n\n    auto continue_cluster_size = current_cluster_size_;\n    auto continue_data_size = current_data_size_;\n    auto continue_data_pos = next_data_pos_;\n    auto continue_op_pos = next_op_pos_;\n    auto continue_num_ops = footer_.op.num_ops;\n    bool extra_cluster = false;\n\n    // Blank out extra ops, in case we're in append mode and dropped ops.\n    if (cluster_size_) {\n        auto unused_cluster_space = cluster_size_ - current_cluster_size_;\n        std::string clr;\n        clr.resize(unused_cluster_space, '\\0');\n        if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {\n            PLOG(ERROR) << \"Failed to seek to footer position.\";\n            return false;\n        }\n        if (!android::base::WriteFully(fd_, clr.data(), clr.size())) {\n            PLOG(ERROR) << \"clearing unused cluster area failed\";\n            return false;\n        }\n    }\n\n    // Footer should be at the end of a file, so if there is data after the current block, end\n    // it and start a new cluster.\n    if (cluster_size_ && current_data_size_ > 0) {\n        EmitCluster();\n        extra_cluster = true;\n    }\n\n    footer_.op.ops_size = footer_.op.num_ops * sizeof(CowOperationV2);\n    if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"Failed to seek to footer position.\";\n        return false;\n    }\n    memset(&footer_.unused, 0, sizeof(footer_.unused));\n\n    // Write out footer at end of file\n    if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&footer_),\n                                   sizeof(footer_))) {\n        PLOG(ERROR) << \"write footer failed\";\n        return false;\n    }\n\n    // Remove excess data, if we're in append mode and threw away more data\n    // than we wrote before.\n    off_t offs = lseek(fd_.get(), 0, SEEK_CUR);\n    if (offs < 0) {\n        PLOG(ERROR) << \"Failed to lseek to find current position\";\n        return false;\n    }\n    if (!Truncate(offs)) {\n        return false;\n    }\n\n    // Reposition for additional Writing\n    if (extra_cluster) {\n        current_cluster_size_ = continue_cluster_size;\n        current_data_size_ = continue_data_size;\n        next_data_pos_ = continue_data_pos;\n        next_op_pos_ = continue_op_pos;\n        footer_.op.num_ops = continue_num_ops;\n    }\n\n    FlushCluster();\n\n    return Sync();\n}\n\nCowSizeInfo CowWriterV2::GetCowSizeInfo() const {\n    CowSizeInfo info;\n    if (current_data_size_ > 0) {\n        info.cow_size = next_data_pos_ + sizeof(footer_);\n    } else {\n        info.cow_size = next_op_pos_ + sizeof(footer_);\n    }\n    return info;\n}\n\nbool CowWriterV2::GetDataPos(uint64_t* pos) {\n    off_t offs = lseek(fd_.get(), 0, SEEK_CUR);\n    if (offs < 0) {\n        PLOG(ERROR) << \"lseek failed\";\n        return false;\n    }\n    *pos = offs;\n    return true;\n}\n\nbool CowWriterV2::EnsureSpaceAvailable(const uint64_t bytes_needed) const {\n    if (bytes_needed > cow_image_size_) {\n        LOG(ERROR) << \"No space left on COW device. Required: \" << bytes_needed\n                   << \", available: \" << cow_image_size_;\n        errno = ENOSPC;\n        return false;\n    }\n    return true;\n}\n\nbool CowWriterV2::FlushCluster() {\n    ssize_t ret;\n\n    if (op_vec_index_) {\n        ret = pwritev(fd_.get(), cowop_vec_.get(), op_vec_index_, current_op_pos_);\n        if (ret != (op_vec_index_ * sizeof(CowOperationV2))) {\n            PLOG(ERROR) << \"pwritev failed for CowOperationV2. Expected: \"\n                        << (op_vec_index_ * sizeof(CowOperationV2));\n            return false;\n        }\n    }\n\n    if (data_vec_index_) {\n        ret = pwritev(fd_.get(), data_vec_.get(), data_vec_index_, current_data_pos_);\n        if (ret != total_data_written_) {\n            PLOG(ERROR) << \"pwritev failed for data. Expected: \" << total_data_written_;\n            return false;\n        }\n    }\n\n    total_data_written_ = 0;\n    op_vec_index_ = 0;\n    data_vec_index_ = 0;\n    current_op_pos_ = next_op_pos_;\n    current_data_pos_ = next_data_pos_;\n\n    return true;\n}\n\nbool CowWriterV2::WriteOperation(const CowOperationV2& op, const void* data, size_t size) {\n    if (!EnsureSpaceAvailable(next_op_pos_ + sizeof(op)) ||\n        !EnsureSpaceAvailable(next_data_pos_ + size)) {\n        return false;\n    }\n\n    if (batch_write_) {\n        CowOperationV2* cow_op =\n                reinterpret_cast<CowOperationV2*>(cowop_vec_[op_vec_index_].iov_base);\n        std::memcpy(cow_op, &op, sizeof(CowOperationV2));\n        op_vec_index_ += 1;\n\n        if (data != nullptr && size > 0) {\n            struct iovec* data_ptr = data_vec_.get();\n            std::memcpy(data_ptr[data_vec_index_].iov_base, data, size);\n            data_ptr[data_vec_index_].iov_len = size;\n            data_vec_index_ += 1;\n            total_data_written_ += size;\n        }\n    } else {\n        if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {\n            PLOG(ERROR) << \"lseek failed for writing operation.\";\n            return false;\n        }\n        if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&op), sizeof(op))) {\n            return false;\n        }\n        if (data != nullptr && size > 0) {\n            if (!WriteRawData(data, size)) return false;\n        }\n    }\n\n    AddOperation(op);\n\n    if (batch_write_) {\n        if (op_vec_index_ == header_.cluster_ops || data_vec_index_ == header_.cluster_ops ||\n            op.type == kCowLabelOp || op.type == kCowClusterOp) {\n            if (!FlushCluster()) {\n                LOG(ERROR) << \"Failed to flush cluster data\";\n                return false;\n            }\n        }\n    }\n\n    return EmitClusterIfNeeded();\n}\n\nvoid CowWriterV2::AddOperation(const CowOperationV2& op) {\n    footer_.op.num_ops++;\n\n    if (op.type == kCowClusterOp) {\n        current_cluster_size_ = 0;\n        current_data_size_ = 0;\n    } else if (header_.cluster_ops) {\n        current_cluster_size_ += sizeof(op);\n        current_data_size_ += op.data_length;\n    }\n\n    next_data_pos_ += op.data_length + GetNextDataOffset(op, header_.cluster_ops);\n    next_op_pos_ += sizeof(CowOperationV2) + GetNextOpOffset(op, header_.cluster_ops);\n}\n\nbool CowWriterV2::WriteRawData(const void* data, const size_t size) {\n    if (!android::base::WriteFullyAtOffset(fd_, data, size, next_data_pos_)) {\n        return false;\n    }\n    return true;\n}\n\nbool CowWriterV2::Truncate(off_t length) {\n    if (is_dev_null_ || is_block_device_) {\n        return true;\n    }\n    if (ftruncate(fd_.get(), length) < 0) {\n        PLOG(ERROR) << \"Failed to truncate.\";\n        return false;\n    }\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/writer_v2.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <future>\n#include \"writer_base.h\"\n\nnamespace android {\nnamespace snapshot {\n\nclass CowWriterV2 : public CowWriterBase {\n  public:\n    explicit CowWriterV2(const CowOptions& options, android::base::unique_fd&& fd);\n    ~CowWriterV2() override;\n\n    bool Initialize(std::optional<uint64_t> label = {}) override;\n    bool Finalize() override;\n    CowSizeInfo GetCowSizeInfo() const override;\n\n  protected:\n    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;\n    virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;\n    virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,\n                               uint32_t old_block, uint16_t offset) override;\n    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;\n    virtual bool EmitLabel(uint64_t label) override;\n    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;\n\n  private:\n    bool EmitCluster();\n    bool EmitClusterIfNeeded();\n    bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,\n                    uint16_t offset, CowOperationType type);\n    void SetupHeaders();\n    void SetupWriteOptions();\n    bool ParseOptions();\n    bool OpenForWrite();\n    bool OpenForAppend(uint64_t label);\n    bool GetDataPos(uint64_t* pos);\n    bool WriteRawData(const void* data, size_t size);\n    bool WriteOperation(const CowOperationV2& op, const void* data = nullptr, size_t size = 0);\n    void AddOperation(const CowOperationV2& op);\n    void InitPos();\n    void InitBatchWrites();\n    void InitWorkers();\n    bool FlushCluster();\n\n    bool CompressBlocks(size_t num_blocks, const void* data);\n    bool Truncate(off_t length);\n    bool EnsureSpaceAvailable(const uint64_t bytes_needed) const;\n\n  private:\n    CowFooter footer_{};\n    CowHeader header_{};\n    CowCompression compression_;\n    // in the case that we are using one thread for compression, we can store and re-use the same\n    // compressor\n    std::unique_ptr<ICompressor> compressor_;\n    uint64_t current_op_pos_ = 0;\n    uint64_t next_op_pos_ = 0;\n    uint64_t next_data_pos_ = 0;\n    uint64_t current_data_pos_ = 0;\n    ssize_t total_data_written_ = 0;\n    uint32_t cluster_size_ = 0;\n    uint32_t current_cluster_size_ = 0;\n    uint64_t current_data_size_ = 0;\n    bool merge_in_progress_ = false;\n\n    int num_compress_threads_ = 1;\n    std::vector<std::unique_ptr<CompressWorker>> compress_threads_;\n    std::vector<std::future<bool>> threads_;\n    std::vector<std::vector<uint8_t>> compressed_buf_;\n    std::vector<std::vector<uint8_t>>::iterator buf_iter_;\n\n    std::vector<std::unique_ptr<CowOperationV2>> opbuffer_vec_;\n    std::vector<std::unique_ptr<uint8_t[]>> databuffer_vec_;\n    std::unique_ptr<struct iovec[]> cowop_vec_;\n    int op_vec_index_ = 0;\n\n    std::unique_ptr<struct iovec[]> data_vec_;\n    int data_vec_index_ = 0;\n    bool batch_write_ = false;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.cpp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source_info Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"writer_v3.h\"\n\n#include <sys/types.h>\n#include <sys/uio.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <brotli/encode.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_reader.h>\n#include <libsnapshot/cow_writer.h>\n#include <lz4.h>\n#include <zlib.h>\n\n#include <fcntl.h>\n#include <libsnapshot/cow_compress.h>\n#include <libsnapshot_cow/parser_v3.h>\n#include <linux/fs.h>\n#include <storage_literals/storage_literals.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n#include <numeric>\n\n// The info messages here are spammy, but as useful for update_engine. Disable\n// them when running on the host.\n#ifdef __ANDROID__\n#define LOG_INFO LOG(INFO)\n#else\n#define LOG_INFO LOG(VERBOSE)\n#endif\n\nnamespace android {\nnamespace snapshot {\n\nstatic_assert(sizeof(off_t) == sizeof(uint64_t));\n\nusing namespace android::storage_literals;\nusing android::base::unique_fd;\n\n// Divide |x| by |y| and round up to the nearest integer.\nconstexpr uint64_t DivRoundUp(uint64_t x, uint64_t y) {\n    return (x + y - 1) / y;\n}\n\nCowWriterV3::CowWriterV3(const CowOptions& options, unique_fd&& fd)\n    : CowWriterBase(options, std::move(fd)), batch_size_(std::max<size_t>(options.cluster_ops, 1)) {\n    SetupHeaders();\n}\n\nvoid CowWriterV3::InitWorkers() {\n    if (num_compress_threads_ <= 1) {\n        LOG_INFO << \"Not creating new threads for compression.\";\n        return;\n    }\n    compress_threads_.reserve(num_compress_threads_);\n    compress_threads_.clear();\n    threads_.reserve(num_compress_threads_);\n    threads_.clear();\n    for (size_t i = 0; i < num_compress_threads_; i++) {\n        std::unique_ptr<ICompressor> compressor =\n                ICompressor::Create(compression_, header_.max_compression_size);\n        auto&& wt = compress_threads_.emplace_back(\n                std::make_unique<CompressWorker>(std::move(compressor)));\n        threads_.emplace_back(std::thread([wt = wt.get()]() { wt->RunThread(); }));\n    }\n    LOG(INFO) << num_compress_threads_ << \" thread used for compression\";\n}\n\nvoid CowWriterV3::SetupHeaders() {\n    header_ = {};\n    header_.prefix.magic = kCowMagicNumber;\n    header_.prefix.major_version = 3;\n    header_.prefix.minor_version = 0;\n    header_.prefix.header_size = sizeof(CowHeaderV3);\n    header_.footer_size = 0;\n    header_.op_size = sizeof(CowOperationV3);\n    header_.block_size = options_.block_size;\n    header_.num_merge_ops = options_.num_merge_ops;\n    header_.cluster_ops = 0;\n    if (options_.scratch_space) {\n        header_.buffer_size = BUFFER_REGION_DEFAULT_SIZE;\n    }\n\n    // v3 specific fields\n    // WIP: not quite sure how some of these are calculated yet, assuming buffer_size is determined\n    // during COW size estimation\n    header_.sequence_data_count = 0;\n\n    header_.resume_point_count = 0;\n    header_.resume_point_max = kNumResumePoints;\n    header_.op_count = 0;\n    header_.op_count_max = 0;\n    header_.compression_algorithm = kCowCompressNone;\n    header_.max_compression_size = options_.compression_factor;\n}\n\nbool CowWriterV3::ParseOptions() {\n    if (!header_.max_compression_size || !IsBlockAligned(header_.max_compression_size)) {\n        LOG(ERROR) << \"Invalid compression factor: \" << header_.max_compression_size;\n        return false;\n    }\n\n    num_compress_threads_ = std::max(int(options_.num_compress_threads), 1);\n    auto parts = android::base::Split(options_.compression, \",\");\n    if (parts.size() > 2) {\n        LOG(ERROR) << \"failed to parse compression parameters: invalid argument count: \"\n                   << parts.size() << \" \" << options_.compression;\n        return false;\n    }\n    auto algorithm = CompressionAlgorithmFromString(parts[0]);\n    if (!algorithm) {\n        LOG(ERROR) << \"unrecognized compression: \" << options_.compression;\n        return false;\n    }\n    header_.compression_algorithm = *algorithm;\n    header_.op_count_max = options_.op_count_max;\n\n    if (!IsEstimating() && header_.op_count_max == 0) {\n        if (!options_.max_blocks.has_value()) {\n            LOG(ERROR) << \"can't size op buffer size since op_count_max is 0 and max_blocks is not \"\n                          \"set.\";\n            return false;\n        }\n        LOG(INFO) << \"op count max is read in as 0. Setting to \"\n                     \"num blocks in partition \"\n                  << options_.max_blocks.value();\n        header_.op_count_max = options_.max_blocks.value();\n    }\n\n    if (parts.size() > 1) {\n        if (!android::base::ParseInt(parts[1], &compression_.compression_level)) {\n            LOG(ERROR) << \"failed to parse compression level invalid type: \" << parts[1];\n            return false;\n        }\n    } else {\n        compression_.compression_level =\n                CompressWorker::GetDefaultCompressionLevel(algorithm.value());\n    }\n\n    compression_.algorithm = *algorithm;\n    if (compression_.algorithm != kCowCompressNone) {\n        compressor_ = ICompressor::Create(compression_, header_.max_compression_size);\n        if (compressor_ == nullptr) {\n            LOG(ERROR) << \"Failed to create compressor for \" << compression_.algorithm;\n            return false;\n        }\n    }\n\n    if (options_.cluster_ops &&\n        (android::base::GetBoolProperty(\"ro.virtual_ab.batch_writes\", false) ||\n         options_.batch_write)) {\n        batch_size_ = std::max<size_t>(options_.cluster_ops, 1);\n        data_vec_.reserve(batch_size_);\n        cached_data_.reserve(batch_size_);\n        cached_ops_.reserve(batch_size_ * kNonDataOpBufferSize);\n    }\n\n    if (batch_size_ > 1) {\n        LOG(INFO) << \"Batch writes: enabled with batch size \" << batch_size_;\n    } else {\n        LOG(INFO) << \"Batch writes: disabled\";\n    }\n    if (android::base::GetBoolProperty(\"ro.virtual_ab.compression.threads\", false) &&\n        options_.num_compress_threads) {\n        num_compress_threads_ = options_.num_compress_threads;\n    }\n    InitWorkers();\n\n    return true;\n}\n\nCowWriterV3::~CowWriterV3() {\n    for (const auto& t : compress_threads_) {\n        t->Finalize();\n    }\n    for (auto& t : threads_) {\n        if (t.joinable()) {\n            t.join();\n        }\n    }\n}\n\nbool CowWriterV3::Initialize(std::optional<uint64_t> label) {\n    if (!InitFd() || !ParseOptions()) {\n        return false;\n    }\n    if (!label) {\n        if (!OpenForWrite()) {\n            return false;\n        }\n    } else {\n        if (!OpenForAppend(*label)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool CowWriterV3::OpenForWrite() {\n    // This limitation is tied to the data field size in CowOperationV2.\n    // Keeping this for V3 writer <- although we\n    if (header_.block_size > std::numeric_limits<uint16_t>::max()) {\n        LOG(ERROR) << \"Block size is too large\";\n        return false;\n    }\n\n    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {\n        PLOG(ERROR) << \"lseek failed\";\n        return false;\n    }\n\n    // Headers are not complete, but this ensures the file is at the right\n    // position.\n    if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {\n        PLOG(ERROR) << \"write failed\";\n        return false;\n    }\n\n    if (options_.scratch_space) {\n        // Initialize the scratch space\n        std::string data(header_.buffer_size, 0);\n        if (!android::base::WriteFully(fd_, data.data(), header_.buffer_size)) {\n            PLOG(ERROR) << \"writing scratch space failed\";\n            return false;\n        }\n    }\n\n    resume_points_ = std::make_shared<std::vector<ResumePoint>>();\n\n    if (!Sync()) {\n        LOG(ERROR) << \"Header sync failed\";\n        return false;\n    }\n    next_data_pos_ = GetDataOffset(header_);\n    return true;\n}\n\nbool CowWriterV3::OpenForAppend(uint64_t label) {\n    CowHeaderV3 header_v3{};\n    if (!ReadCowHeader(fd_, &header_v3)) {\n        LOG(ERROR) << \"Couldn't read Cow Header\";\n        return false;\n    }\n\n    header_ = header_v3;\n\n    CHECK(label >= 0);\n    CowParserV3 parser;\n    if (!parser.Parse(fd_, header_, label)) {\n        PLOG(ERROR) << \"unable to parse with given label: \" << label;\n        return false;\n    }\n\n    resume_points_ = parser.resume_points();\n    options_.block_size = header_.block_size;\n    next_data_pos_ = GetDataOffset(header_);\n\n    TranslatedCowOps ops;\n    parser.Translate(&ops);\n    header_.op_count = ops.ops->size();\n\n    for (const auto& op : *ops.ops) {\n        next_data_pos_ += op.data_length;\n    }\n\n    return true;\n}\n\nbool CowWriterV3::CheckOpCount(size_t op_count) {\n    if (IsEstimating()) {\n        return true;\n    }\n    if (header_.op_count + cached_ops_.size() + op_count > header_.op_count_max) {\n        LOG(ERROR) << \"Current number of ops on disk: \" << header_.op_count\n                   << \", number of ops cached in memory: \" << cached_ops_.size()\n                   << \", number of ops attempting to write: \" << op_count\n                   << \", this will exceed max op count \" << header_.op_count_max;\n        return false;\n    }\n    return true;\n}\n\nsize_t CowWriterV3::CachedDataSize() const {\n    size_t size = 0;\n    for (const auto& i : cached_data_) {\n        size += i.size();\n    }\n    return size;\n}\n\nbool CowWriterV3::EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks) {\n    if (!CheckOpCount(num_blocks)) {\n        return false;\n    }\n    for (size_t i = 0; i < num_blocks; i++) {\n        CowOperationV3& op = cached_ops_.emplace_back();\n        op.set_type(kCowCopyOp);\n        op.new_block = new_block + i;\n        op.set_source(old_block + i);\n    }\n\n    if (NeedsFlush()) {\n        if (!FlushCacheOps()) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool CowWriterV3::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {\n    return EmitBlocks(new_block_start, data, size, 0, 0, kCowReplaceOp);\n}\n\nbool CowWriterV3::EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,\n                                uint32_t old_block, uint16_t offset) {\n    return EmitBlocks(new_block_start, data, size, old_block, offset, kCowXorOp);\n}\n\nbool CowWriterV3::NeedsFlush() const {\n    // Allow bigger batch sizes for ops without data. A single CowOperationV3\n    // struct uses 14 bytes of memory, even if we cache 200 * 16 ops in memory,\n    // it's only ~44K.\n    return CachedDataSize() >= batch_size_ * header_.block_size ||\n           cached_ops_.size() >= batch_size_ * kNonDataOpBufferSize;\n}\n\nbool CowWriterV3::ConstructCowOpCompressedBuffers(uint64_t new_block_start, const void* data,\n                                                  uint64_t old_block, uint16_t offset,\n                                                  CowOperationType type, size_t blocks_to_write) {\n    size_t compressed_bytes = 0;\n    auto&& blocks = CompressBlocks(blocks_to_write, data, type);\n    if (blocks.empty()) {\n        LOG(ERROR) << \"Failed to compress blocks \" << new_block_start << \", \" << blocks_to_write\n                   << \", actual number of blocks received from compressor \" << blocks.size();\n        return false;\n    }\n    if (!CheckOpCount(blocks.size())) {\n        return false;\n    }\n    size_t blocks_written = 0;\n    for (size_t blk_index = 0; blk_index < blocks.size(); blk_index++) {\n        CowOperation& op = cached_ops_.emplace_back();\n        auto& vec = data_vec_.emplace_back();\n        CompressedBuffer buffer = std::move(blocks[blk_index]);\n        auto& compressed_data = cached_data_.emplace_back(std::move(buffer.compressed_data));\n        op.new_block = new_block_start + blocks_written;\n\n        op.set_type(type);\n        op.set_compression_bits(std::log2(buffer.compression_factor / header_.block_size));\n\n        if (type == kCowXorOp) {\n            op.set_source((old_block + blocks_written) * header_.block_size + offset);\n        } else {\n            op.set_source(next_data_pos_ + compressed_bytes);\n        }\n\n        vec = {.iov_base = compressed_data.data(), .iov_len = compressed_data.size()};\n        op.data_length = vec.iov_len;\n        compressed_bytes += op.data_length;\n        blocks_written += (buffer.compression_factor / header_.block_size);\n    }\n    if (blocks_written != blocks_to_write) {\n        LOG(ERROR) << \"Total compressed blocks: \" << blocks_written\n                   << \" Expected: \" << blocks_to_write;\n        return false;\n    }\n    return true;\n}\n\nbool CowWriterV3::EmitBlocks(uint64_t new_block_start, const void* data, size_t size,\n                             uint64_t old_block, uint16_t offset, CowOperationType type) {\n    if (compression_.algorithm != kCowCompressNone && compressor_ == nullptr) {\n        LOG(ERROR) << \"Compression algorithm is \" << compression_.algorithm\n                   << \" but compressor is uninitialized.\";\n        return false;\n    }\n    const auto bytes = reinterpret_cast<const uint8_t*>(data);\n    size_t num_blocks = (size / header_.block_size);\n    size_t total_written = 0;\n    while (total_written < num_blocks) {\n        size_t chunk = std::min(num_blocks - total_written, batch_size_);\n        if (!ConstructCowOpCompressedBuffers(new_block_start + total_written,\n                                             bytes + header_.block_size * total_written,\n                                             old_block + total_written, offset, type, chunk)) {\n            return false;\n        }\n\n        if (NeedsFlush() && !FlushCacheOps()) {\n            LOG(ERROR) << \"EmitBlocks with compression: write failed. new block: \"\n                       << new_block_start << \" compression: \" << compression_.algorithm\n                       << \", op type: \" << type;\n            return false;\n        }\n        total_written += chunk;\n    }\n\n    return true;\n}\n\nbool CowWriterV3::EmitZeroBlocks(uint64_t new_block_start, const uint64_t num_blocks) {\n    if (!CheckOpCount(num_blocks)) {\n        return false;\n    }\n    for (uint64_t i = 0; i < num_blocks; i++) {\n        auto& op = cached_ops_.emplace_back();\n        op.set_type(kCowZeroOp);\n        op.new_block = new_block_start + i;\n    }\n    if (NeedsFlush()) {\n        if (!FlushCacheOps()) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool CowWriterV3::EmitLabel(uint64_t label) {\n    // remove all labels greater than this current one. we want to avoid the situation of adding\n    // in\n    // duplicate labels with differing op values\n    if (!FlushCacheOps()) {\n        LOG(ERROR) << \"Failed to flush cached ops before emitting label \" << label;\n        return false;\n    }\n    auto remove_if_callback = [&](const auto& resume_point) -> bool {\n        if (resume_point.label >= label) return true;\n        return false;\n    };\n    resume_points_->erase(\n            std::remove_if(resume_points_->begin(), resume_points_->end(), remove_if_callback),\n            resume_points_->end());\n\n    resume_points_->push_back({label, header_.op_count});\n    header_.resume_point_count++;\n    // remove the oldest resume point if resume_buffer is full\n    while (resume_points_->size() > header_.resume_point_max) {\n        resume_points_->erase(resume_points_->begin());\n    }\n\n    CHECK_LE(resume_points_->size(), header_.resume_point_max);\n\n    if (!android::base::WriteFullyAtOffset(fd_, resume_points_->data(),\n                                           resume_points_->size() * sizeof(ResumePoint),\n                                           GetResumeOffset(header_))) {\n        PLOG(ERROR) << \"writing resume buffer failed\";\n        return false;\n    }\n    return Finalize();\n}\n\nbool CowWriterV3::EmitSequenceData(size_t num_ops, const uint32_t* data) {\n    if (header_.op_count > 0 || !cached_ops_.empty()) {\n        LOG(ERROR) << \"There's \" << header_.op_count << \" operations written to disk and \"\n                   << cached_ops_.size()\n                   << \" ops cached in memory. Writing sequence data is only allowed before all \"\n                      \"operation writes.\";\n        return false;\n    }\n\n    header_.sequence_data_count = num_ops;\n\n    // Ensure next_data_pos_ is updated as previously initialized + the newly added sequence\n    // buffer.\n    CHECK_EQ(next_data_pos_ + header_.sequence_data_count * sizeof(uint32_t),\n             GetDataOffset(header_));\n    next_data_pos_ = GetDataOffset(header_);\n\n    if (!android::base::WriteFullyAtOffset(fd_, data, sizeof(data[0]) * num_ops,\n                                           GetSequenceOffset(header_))) {\n        PLOG(ERROR) << \"writing sequence buffer failed\";\n        return false;\n    }\n    return true;\n}\n\nbool CowWriterV3::FlushCacheOps() {\n    if (cached_ops_.empty()) {\n        if (!data_vec_.empty()) {\n            LOG(ERROR) << \"Cached ops is empty, but data iovec has size: \" << data_vec_.size()\n                       << \" this is definitely a bug.\";\n            return false;\n        }\n        return true;\n    }\n    size_t bytes_written = 0;\n\n    for (auto& op : cached_ops_) {\n        if (op.type() == kCowReplaceOp) {\n            op.set_source(next_data_pos_ + bytes_written);\n        }\n        bytes_written += op.data_length;\n    }\n    if (!WriteOperation(cached_ops_, data_vec_)) {\n        LOG(ERROR) << \"Failed to flush \" << cached_ops_.size() << \" ops to disk\";\n        return false;\n    }\n    cached_ops_.clear();\n    cached_data_.clear();\n    data_vec_.clear();\n    return true;\n}\n\nsize_t CowWriterV3::GetCompressionFactor(const size_t blocks_to_compress,\n                                         CowOperationType type) const {\n    // For XOR ops, we don't support bigger block size compression yet.\n    // For bigger block size support, snapshot-merge also has to changed. We\n    // aren't there yet; hence, just stick to 4k for now until\n    // snapshot-merge is ready for XOR operation.\n    if (type == kCowXorOp) {\n        return header_.block_size;\n    }\n\n    size_t compression_factor = header_.max_compression_size;\n    while (compression_factor > header_.block_size) {\n        size_t num_blocks = compression_factor / header_.block_size;\n        if (blocks_to_compress >= num_blocks) {\n            return compression_factor;\n        }\n        compression_factor >>= 1;\n    }\n    return header_.block_size;\n}\n\nstd::vector<CowWriterV3::CompressedBuffer> CowWriterV3::ProcessBlocksWithNoCompression(\n        const size_t num_blocks, const void* data, CowOperationType type) {\n    size_t blocks_to_compress = num_blocks;\n    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);\n    std::vector<CompressedBuffer> compressed_vec;\n\n    while (blocks_to_compress) {\n        CompressedBuffer buffer;\n\n        const size_t compression_factor = GetCompressionFactor(blocks_to_compress, type);\n        size_t num_blocks = compression_factor / header_.block_size;\n\n        buffer.compression_factor = compression_factor;\n        buffer.compressed_data.resize(compression_factor);\n\n        // No compression. Just copy the data as-is.\n        std::memcpy(buffer.compressed_data.data(), iter, compression_factor);\n\n        compressed_vec.push_back(std::move(buffer));\n        blocks_to_compress -= num_blocks;\n        iter += compression_factor;\n    }\n    return compressed_vec;\n}\n\nstd::vector<CowWriterV3::CompressedBuffer> CowWriterV3::ProcessBlocksWithCompression(\n        const size_t num_blocks, const void* data, CowOperationType type) {\n    size_t blocks_to_compress = num_blocks;\n    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);\n    std::vector<CompressedBuffer> compressed_vec;\n\n    while (blocks_to_compress) {\n        CompressedBuffer buffer;\n\n        const size_t compression_factor = GetCompressionFactor(blocks_to_compress, type);\n        size_t num_blocks = compression_factor / header_.block_size;\n\n        buffer.compression_factor = compression_factor;\n        // Compress the blocks\n        buffer.compressed_data = compressor_->Compress(iter, compression_factor);\n        if (buffer.compressed_data.empty()) {\n            PLOG(ERROR) << \"Compression failed\";\n            return {};\n        }\n\n        // Check if the buffer was indeed compressed\n        if (buffer.compressed_data.size() >= compression_factor) {\n            buffer.compressed_data.resize(compression_factor);\n            std::memcpy(buffer.compressed_data.data(), iter, compression_factor);\n        }\n\n        compressed_vec.push_back(std::move(buffer));\n        blocks_to_compress -= num_blocks;\n        iter += compression_factor;\n    }\n    return compressed_vec;\n}\n\nstd::vector<CowWriterV3::CompressedBuffer> CowWriterV3::ProcessBlocksWithThreadedCompression(\n        const size_t num_blocks, const void* data, CowOperationType type) {\n    const size_t num_threads = num_compress_threads_;\n    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);\n\n    // We will alternate which thread to send compress work to. E.g. alternate between T1 and T2\n    // until all blocks are processed\n    std::vector<CompressedBuffer> compressed_vec;\n    int iteration = 0;\n    int blocks_to_compress = static_cast<int>(num_blocks);\n    while (blocks_to_compress) {\n        CompressedBuffer buffer;\n        CompressWorker* worker = compress_threads_[iteration % num_threads].get();\n\n        const size_t compression_factor = GetCompressionFactor(blocks_to_compress, type);\n        size_t num_blocks = compression_factor / header_.block_size;\n\n        worker->EnqueueCompressBlocks(iter, compression_factor, 1);\n        buffer.compression_factor = compression_factor;\n        compressed_vec.push_back(std::move(buffer));\n\n        iteration++;\n        iter += compression_factor;\n        blocks_to_compress -= num_blocks;\n    }\n\n    std::vector<std::vector<uint8_t>> compressed_buf;\n    std::vector<std::vector<std::vector<uint8_t>>> worker_buffers(num_threads);\n    compressed_buf.clear();\n    for (size_t i = 0; i < num_threads; i++) {\n        CompressWorker* worker = compress_threads_[i].get();\n        if (!worker->GetCompressedBuffers(&worker_buffers[i])) {\n            return {};\n        }\n    }\n    // compressed_vec | CB 1 | CB 2 | CB 3 | CB 4 | <-compressed buffers\n    //                   t1     t2     t1     t2    <- processed by these threads\n    // Ordering is important here. We need to retrieve the compressed data in the same order we\n    // processed it and assume that that we submit data beginning with the first thread and then\n    // round robin the consecutive data calls. We need to Fetch compressed buffers from the\n    // threads via the same ordering\n    for (size_t i = 0; i < compressed_vec.size(); i++) {\n        compressed_buf.emplace_back(worker_buffers[i % num_threads][i / num_threads]);\n    }\n\n    if (compressed_vec.size() != compressed_buf.size()) {\n        LOG(ERROR) << \"Compressed buffer size: \" << compressed_buf.size()\n                   << \" - Expected: \" << compressed_vec.size();\n        return {};\n    }\n\n    iter = reinterpret_cast<const uint8_t*>(data);\n    // Walk through all the compressed buffers\n    for (size_t i = 0; i < compressed_buf.size(); i++) {\n        auto& buffer = compressed_vec[i];\n        auto& block = compressed_buf[i];\n        size_t block_size = buffer.compression_factor;\n        // Check if the blocks was indeed compressed\n        if (block.size() >= block_size) {\n            buffer.compressed_data.resize(block_size);\n            std::memcpy(buffer.compressed_data.data(), iter, block_size);\n        } else {\n            // Compressed block\n            buffer.compressed_data.resize(block.size());\n            std::memcpy(buffer.compressed_data.data(), block.data(), block.size());\n        }\n        iter += block_size;\n    }\n    return compressed_vec;\n}\n\nstd::vector<CowWriterV3::CompressedBuffer> CowWriterV3::CompressBlocks(const size_t num_blocks,\n                                                                       const void* data,\n                                                                       CowOperationType type) {\n    if (compression_.algorithm == kCowCompressNone) {\n        return ProcessBlocksWithNoCompression(num_blocks, data, type);\n    }\n\n    const size_t num_threads = (num_blocks == 1) ? 1 : num_compress_threads_;\n\n    // If no threads are required, just compress the blocks inline.\n    if (num_threads <= 1) {\n        return ProcessBlocksWithCompression(num_blocks, data, type);\n    }\n\n    return ProcessBlocksWithThreadedCompression(num_blocks, data, type);\n}\n\nbool CowWriterV3::WriteOperation(std::span<const CowOperationV3> ops,\n                                 std::span<const struct iovec> data) {\n    const auto total_data_size =\n            std::transform_reduce(data.begin(), data.end(), 0, std::plus<size_t>{},\n                                  [](const struct iovec& a) { return a.iov_len; });\n    if (IsEstimating()) {\n        header_.op_count += ops.size();\n        if (header_.op_count > header_.op_count_max) {\n            // If we increment op_count_max, the offset of data section would\n            // change. So need to update |next_data_pos_|\n            next_data_pos_ += (header_.op_count - header_.op_count_max) * sizeof(CowOperationV3);\n            header_.op_count_max = header_.op_count;\n        }\n        next_data_pos_ += total_data_size;\n        return true;\n    }\n\n    if (header_.op_count + ops.size() > header_.op_count_max) {\n        LOG(ERROR) << \"Current op count \" << header_.op_count << \", attempting to write \"\n                   << ops.size() << \" ops will exceed the max of \" << header_.op_count_max;\n        return false;\n    }\n    const off_t offset = GetOpOffset(header_.op_count, header_);\n    if (!android::base::WriteFullyAtOffset(fd_, ops.data(), ops.size() * sizeof(ops[0]), offset)) {\n        PLOG(ERROR) << \"Write failed for \" << ops.size() << \" ops at \" << offset;\n        return false;\n    }\n    if (!data.empty()) {\n        int total_written = 0;\n        int i = 0;\n        while (i < data.size()) {\n            int chunk = std::min(static_cast<int>(data.size() - i), IOV_MAX);\n\n            const auto ret = pwritev(fd_, data.data() + i, chunk, next_data_pos_ + total_written);\n            if (ret < 0) {\n                PLOG(ERROR) << \"write failed chunk size of: \" << chunk\n                            << \" at offset: \" << next_data_pos_ + total_written << \" \" << errno;\n                return false;\n            }\n            total_written += ret;\n            i += chunk;\n        }\n        if (total_written != total_data_size) {\n            PLOG(ERROR) << \"write failed for data vector of size: \" << data.size()\n                        << \" and total data length: \" << total_data_size\n                        << \" at offset: \" << next_data_pos_ << \" \" << errno\n                        << \", only wrote: \" << total_written;\n            return false;\n        }\n    }\n\n    header_.op_count += ops.size();\n    next_data_pos_ += total_data_size;\n\n    return true;\n}\n\nbool CowWriterV3::Finalize() {\n    CHECK_GE(header_.prefix.header_size, sizeof(CowHeaderV3));\n    CHECK_LE(header_.prefix.header_size, sizeof(header_));\n    if (!FlushCacheOps()) {\n        return false;\n    }\n    if (!android::base::WriteFullyAtOffset(fd_, &header_, header_.prefix.header_size, 0)) {\n        return false;\n    }\n    return Sync();\n}\n\nCowSizeInfo CowWriterV3::GetCowSizeInfo() const {\n    CowSizeInfo info;\n    info.cow_size = next_data_pos_;\n    info.op_count_max = header_.op_count_max;\n    return info;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/libsnapshot_cow/writer_v3.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <android-base/logging.h>\n#include <span>\n#include <string_view>\n#include <thread>\n#include <vector>\n\n#include <libsnapshot/cow_format.h>\n#include <storage_literals/storage_literals.h>\n#include \"writer_base.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android::storage_literals;\n// This is a multiple on top of the number of data ops that can be stored in our cache at once. This\n// is added so that we can cache more non-data ops as it takes up less space.\nstatic constexpr uint32_t kNonDataOpBufferSize = 16;\n\nclass CowWriterV3 : public CowWriterBase {\n  public:\n    explicit CowWriterV3(const CowOptions& options, android::base::unique_fd&& fd);\n    ~CowWriterV3() override;\n\n    bool Initialize(std::optional<uint64_t> label = {}) override;\n    bool Finalize() override;\n    CowSizeInfo GetCowSizeInfo() const override;\n\n  protected:\n    virtual bool EmitCopy(uint64_t new_block, uint64_t old_block, uint64_t num_blocks = 1) override;\n    virtual bool EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;\n    virtual bool EmitXorBlocks(uint32_t new_block_start, const void* data, size_t size,\n                               uint32_t old_block, uint16_t offset) override;\n    virtual bool EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;\n    virtual bool EmitLabel(uint64_t label) override;\n    virtual bool EmitSequenceData(size_t num_ops, const uint32_t* data) override;\n\n  private:\n    struct CompressedBuffer {\n        size_t compression_factor;\n        std::vector<uint8_t> compressed_data;\n    };\n    void SetupHeaders();\n    bool NeedsFlush() const;\n    bool ParseOptions();\n    bool OpenForWrite();\n    bool OpenForAppend(uint64_t label);\n    bool WriteOperation(std::span<const CowOperationV3> op, std::span<const struct iovec> data);\n    bool EmitBlocks(uint64_t new_block_start, const void* data, size_t size, uint64_t old_block,\n                    uint16_t offset, CowOperationType type);\n    bool ConstructCowOpCompressedBuffers(uint64_t new_block_start, const void* data,\n                                         uint64_t old_block, uint16_t offset, CowOperationType type,\n                                         size_t blocks_to_write);\n    bool CheckOpCount(size_t op_count);\n\n  private:\n    std::vector<CompressedBuffer> ProcessBlocksWithNoCompression(const size_t num_blocks,\n                                                                 const void* data,\n                                                                 CowOperationType type);\n    std::vector<CompressedBuffer> ProcessBlocksWithCompression(const size_t num_blocks,\n                                                               const void* data,\n                                                               CowOperationType type);\n    std::vector<CompressedBuffer> ProcessBlocksWithThreadedCompression(const size_t num_blocks,\n                                                                       const void* data,\n                                                                       CowOperationType type);\n    std::vector<CompressedBuffer> CompressBlocks(const size_t num_blocks, const void* data,\n                                                 CowOperationType type);\n    size_t GetCompressionFactor(const size_t blocks_to_compress, CowOperationType type) const;\n\n    constexpr bool IsBlockAligned(const uint64_t size) {\n        // These are the only block size supported. Block size beyond 256k\n        // may impact random read performance post OTA boot.\n        const size_t values[] = {4_KiB, 8_KiB, 16_KiB, 32_KiB, 64_KiB, 128_KiB, 256_KiB};\n\n        auto it = std::lower_bound(std::begin(values), std::end(values), size);\n\n        if (it != std::end(values) && *it == size) {\n            return true;\n        }\n        return false;\n    }\n    size_t CachedDataSize() const;\n    bool ReadBackVerification();\n    bool FlushCacheOps();\n    void InitWorkers();\n    CowHeaderV3 header_{};\n    CowCompression compression_;\n    // in the case that we are using one thread for compression, we can store and re-use the same\n    // compressor\n    std::unique_ptr<ICompressor> compressor_;\n    std::vector<std::unique_ptr<CompressWorker>> compress_threads_;\n    // Resume points contain a laebl + cow_op_index.\n    std::shared_ptr<std::vector<ResumePoint>> resume_points_;\n\n    uint64_t next_data_pos_ = 0;\n\n    // in the case that we are using one thread for compression, we can store and re-use the same\n    // compressor\n    int num_compress_threads_ = 1;\n    size_t batch_size_ = 1;\n    std::vector<CowOperationV3> cached_ops_;\n    std::vector<std::vector<uint8_t>> cached_data_;\n    std::vector<struct iovec> data_vec_;\n\n    std::vector<std::thread> threads_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/partition_cow_creator.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"partition_cow_creator.h\"\n\n#include <math.h>\n\n#include <android-base/logging.h>\n#include <android/snapshot/snapshot.pb.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"dm_snapshot_internals.h\"\n#include \"utility.h\"\n\nusing android::dm::kSectorSize;\nusing android::fs_mgr::Extent;\nusing android::fs_mgr::Interval;\nusing android::fs_mgr::kDefaultBlockSize;\nusing android::fs_mgr::Partition;\nusing chromeos_update_engine::InstallOperation;\ntemplate <typename T>\nusing RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;\n\nnamespace android {\nnamespace snapshot {\n\nstatic constexpr uint64_t kBlockSize = 4096;\n\nusing namespace android::storage_literals;\n\n// Intersect two linear extents. If no intersection, return an extent with length 0.\nstatic std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {\n    // Convert target_extent and existing_extent to linear extents. Zero extents\n    // doesn't matter and doesn't result in any intersection.\n    auto existing_linear_extent = existing_extent->AsLinearExtent();\n    if (!existing_linear_extent) return nullptr;\n\n    auto target_linear_extent = target_extent->AsLinearExtent();\n    if (!target_linear_extent) return nullptr;\n\n    return Interval::Intersect(target_linear_extent->AsInterval(),\n                               existing_linear_extent->AsInterval())\n            .AsExtent();\n}\n\n// Check that partition |p| contains |e| fully. Both of them should\n// be from |target_metadata|.\n// Returns true as long as |e| is a subrange of any extent of |p|.\nbool PartitionCowCreator::HasExtent(Partition* p, Extent* e) {\n    for (auto& partition_extent : p->extents()) {\n        auto intersection = Intersect(partition_extent.get(), e);\n        if (intersection != nullptr && intersection->num_sectors() == e->num_sectors()) {\n            return true;\n        }\n    }\n    return false;\n}\n\nbool OptimizeSourceCopyOperation(const InstallOperation& operation, InstallOperation* optimized) {\n    if (operation.type() != InstallOperation::SOURCE_COPY) {\n        return false;\n    }\n\n    optimized->Clear();\n    optimized->set_type(InstallOperation::SOURCE_COPY);\n\n    const auto& src_extents = operation.src_extents();\n    const auto& dst_extents = operation.dst_extents();\n\n    // If input is empty, skip by returning an empty result.\n    if (src_extents.empty() && dst_extents.empty()) {\n        return true;\n    }\n\n    auto s_it = src_extents.begin();\n    auto d_it = dst_extents.begin();\n    uint64_t s_offset = 0;  // offset within *s_it\n    uint64_t d_offset = 0;  // offset within *d_it\n    bool is_optimized = false;\n\n    while (s_it != src_extents.end() || d_it != dst_extents.end()) {\n        if (s_it == src_extents.end() || d_it == dst_extents.end()) {\n            LOG(ERROR) << \"number of blocks do not equal in src_extents and dst_extents\";\n            return false;\n        }\n        if (s_it->num_blocks() <= s_offset || d_it->num_blocks() <= d_offset) {\n            LOG(ERROR) << \"Offset goes out of bounds.\";\n            return false;\n        }\n\n        // Check the next |step| blocks, where |step| is the min of remaining blocks in the current\n        // source extent and current destination extent.\n        auto s_step = s_it->num_blocks() - s_offset;\n        auto d_step = d_it->num_blocks() - d_offset;\n        auto step = std::min(s_step, d_step);\n\n        bool moved = s_it->start_block() + s_offset != d_it->start_block() + d_offset;\n        if (moved) {\n            // If the next |step| blocks are not copied to the same location, add them to result.\n            AppendExtent(optimized->mutable_src_extents(), s_it->start_block() + s_offset, step);\n            AppendExtent(optimized->mutable_dst_extents(), d_it->start_block() + d_offset, step);\n        } else {\n            // The next |step| blocks are optimized out.\n            is_optimized = true;\n        }\n\n        // Advance offsets by |step|, and go to the next non-empty extent if the current extent is\n        // depleted.\n        s_offset += step;\n        d_offset += step;\n        while (s_it != src_extents.end() && s_offset >= s_it->num_blocks()) {\n            ++s_it;\n            s_offset = 0;\n        }\n        while (d_it != dst_extents.end() && d_offset >= d_it->num_blocks()) {\n            ++d_it;\n            d_offset = 0;\n        }\n    }\n    return is_optimized;\n}\n\nbool WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Extent& de,\n                 unsigned int sectors_per_block) {\n    const auto block_boundary = de.start_block() + de.num_blocks();\n    for (auto b = de.start_block(); b < block_boundary; ++b) {\n        for (unsigned int s = 0; s < sectors_per_block; ++s) {\n            // sector_id = b * sectors_per_block + s;\n            uint64_t block_start_sector_id;\n            if (__builtin_mul_overflow(b, sectors_per_block, &block_start_sector_id)) {\n                LOG(ERROR) << \"Integer overflow when calculating sector id (\" << b << \" * \"\n                           << sectors_per_block << \")\";\n                return false;\n            }\n            uint64_t sector_id;\n            if (__builtin_add_overflow(block_start_sector_id, s, &sector_id)) {\n                LOG(ERROR) << \"Integer overflow when calculating sector id (\"\n                           << block_start_sector_id << \" + \" << s << \")\";\n                return false;\n            }\n            sc->WriteSector(sector_id);\n        }\n    }\n    return true;\n}\n\nstd::optional<uint64_t> PartitionCowCreator::GetCowSize() {\n    if (using_snapuserd) {\n        if (update == nullptr || !update->has_estimate_cow_size()) {\n            LOG(ERROR) << \"Update manifest does not include a COW size\";\n            return std::nullopt;\n        }\n\n        // Add an extra 2MB of wiggle room for any minor differences in labels/metadata\n        // that might come up.\n        auto size = update->estimate_cow_size() + 2_MiB;\n\n        // Align to nearest block.\n        size += kBlockSize - 1;\n        size &= ~(kBlockSize - 1);\n        return size;\n    }\n\n    // WARNING: The origin partition should be READ-ONLY\n    const uint64_t logical_block_size = current_metadata->logical_block_size();\n    const unsigned int sectors_per_block = logical_block_size / kSectorSize;\n    DmSnapCowSizeCalculator sc(kSectorSize, kSnapshotChunkSize);\n\n    // Allocate space for extra extents (if any). These extents are those that can be\n    // used for error corrections or to store verity hash trees.\n    for (const auto& de : extra_extents) {\n        if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;\n    }\n\n    if (update == nullptr) return sc.cow_size_bytes();\n\n    for (const auto& iop : update->operations()) {\n        const InstallOperation* written_op = &iop;\n        InstallOperation buf;\n        // Do not allocate space for extents that are going to be skipped\n        // during OTA application.\n        if (iop.type() == InstallOperation::SOURCE_COPY && OptimizeSourceCopyOperation(iop, &buf)) {\n            written_op = &buf;\n        }\n\n        for (const auto& de : written_op->dst_extents()) {\n            if (!WriteExtent(&sc, de, sectors_per_block)) return std::nullopt;\n        }\n    }\n\n    return sc.cow_size_bytes();\n}\n\nstd::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {\n    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&\n          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);\n\n    const uint64_t logical_block_size = current_metadata->logical_block_size();\n    CHECK(logical_block_size != 0 && !(logical_block_size & (logical_block_size - 1)))\n            << \"logical_block_size is not power of 2\";\n\n    Return ret;\n    ret.snapshot_status.set_name(target_partition->name());\n    ret.snapshot_status.set_device_size(target_partition->size());\n    ret.snapshot_status.set_snapshot_size(target_partition->size());\n\n    if (update && update->has_estimate_cow_size()) {\n        ret.snapshot_status.set_estimated_cow_size(update->estimate_cow_size());\n        ret.snapshot_status.set_estimated_ops_buffer_size(update->estimate_op_count_max());\n    }\n\n    if (ret.snapshot_status.snapshot_size() == 0) {\n        LOG(INFO) << \"Not creating snapshot for partition \" << ret.snapshot_status.name();\n        ret.snapshot_status.set_cow_partition_size(0);\n        ret.snapshot_status.set_cow_file_size(0);\n        return ret;\n    }\n\n    // Being the COW partition virtual, its size doesn't affect the storage\n    // memory that will be occupied by the target.\n    // The actual storage space is affected by the COW file, whose size depends\n    // on the chunks that diverged between |current| and |target|.\n    // If the |target| partition is bigger than |current|, the data that is\n    // modified outside of |current| can be written directly to |current|.\n    // This because the data that will be written outside of |current| would\n    // not invalidate any useful information of |current|, thus:\n    // - if the snapshot is accepted for merge, this data would be already at\n    // the right place and should not be copied;\n    // - in the unfortunate case of the snapshot to be discarded, the regions\n    // modified by this data can be set as free regions and reused.\n    // Compute regions that are free in both current and target metadata. These are the regions\n    // we can use for COW partition.\n    auto target_free_regions = target_metadata->GetFreeRegions();\n    auto current_free_regions = current_metadata->GetFreeRegions();\n    auto free_regions = Interval::Intersect(target_free_regions, current_free_regions);\n    uint64_t free_region_length = 0;\n    for (const auto& interval : free_regions) {\n        free_region_length += interval.length();\n    }\n    free_region_length *= kSectorSize;\n\n    LOG(INFO) << \"Remaining free space for COW: \" << free_region_length << \" bytes\";\n    auto cow_size = GetCowSize();\n    if (!cow_size) {\n        return {};\n    }\n\n    // Compute the COW partition size.\n    uint64_t cow_partition_size = std::min(cow_size.value(), free_region_length);\n    // Round it down to the nearest logical block. Logical partitions must be a multiple\n    // of logical blocks.\n    cow_partition_size &= ~(logical_block_size - 1);\n    ret.snapshot_status.set_cow_partition_size(cow_partition_size);\n    // Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.\n    ret.cow_partition_usable_regions = std::move(free_regions);\n\n    auto cow_file_size = cow_size.value() - cow_partition_size;\n    // Round it up to the nearest sector.\n    cow_file_size += kSectorSize - 1;\n    cow_file_size &= ~(kSectorSize - 1);\n    ret.snapshot_status.set_cow_file_size(cow_file_size);\n\n    return ret;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/partition_cow_creator.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include <liblp/builder.h>\n#include <update_engine/update_metadata.pb.h>\n\n#include <android/snapshot/snapshot.pb.h>\n\nnamespace android {\nnamespace snapshot {\n\n// Helper class that creates COW for a partition.\nstruct PartitionCowCreator {\n    using Extent = android::fs_mgr::Extent;\n    using ChromeOSExtent = chromeos_update_engine::Extent;\n    using Interval = android::fs_mgr::Interval;\n    using MetadataBuilder = android::fs_mgr::MetadataBuilder;\n    using Partition = android::fs_mgr::Partition;\n    using InstallOperation = chromeos_update_engine::InstallOperation;\n    using PartitionUpdate = chromeos_update_engine::PartitionUpdate;\n    template <typename T>\n    using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;\n\n    // The metadata that will be written to target metadata slot.\n    MetadataBuilder* target_metadata = nullptr;\n    // The suffix of the target slot.\n    std::string target_suffix;\n    // The partition in target_metadata that needs to be snapshotted.\n    Partition* target_partition = nullptr;\n    // The metadata at the current slot (that would be used if the device boots\n    // normally). This is used to determine which extents are being used.\n    MetadataBuilder* current_metadata = nullptr;\n    // The suffix of the current slot.\n    std::string current_suffix;\n    // Partition information from the OTA manifest.\n    const PartitionUpdate* update = nullptr;\n    // Extra extents that are going to be invalidated during the update\n    // process.\n    std::vector<ChromeOSExtent> extra_extents = {};\n    // True if snapuserd COWs are enabled.\n    bool using_snapuserd = false;\n    std::string compression_algorithm;\n    uint64_t compression_factor;\n    uint32_t read_ahead_size;\n\n    // Enable direct reads on source device\n    bool o_direct;\n\n    // True if multi-threaded compression should be enabled\n    bool enable_threading;\n\n    // True if COW writes should be batched in memory\n    bool batched_writes;\n\n    struct Return {\n        SnapshotStatus snapshot_status;\n        std::vector<Interval> cow_partition_usable_regions;\n    };\n\n    std::optional<Return> Run();\n\n  private:\n    bool HasExtent(Partition* p, Extent* e);\n    std::optional<uint64_t> GetCowSize();\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/partition_cow_creator_test.cpp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <optional>\n#include <tuple>\n\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <libdm/dm.h>\n#include <liblp/builder.h>\n#include <liblp/property_fetcher.h>\n\n#include <libsnapshot/test_helpers.h>\n\n#include \"dm_snapshot_internals.h\"\n#include \"partition_cow_creator.h\"\n#include \"utility.h\"\n\nusing namespace android::fs_mgr;\n\nusing chromeos_update_engine::InstallOperation;\nusing UeExtent = chromeos_update_engine::Extent;\nusing google::protobuf::RepeatedPtrField;\nusing ::testing::Matches;\nusing ::testing::Pointwise;\nusing ::testing::Truly;\n\nnamespace android {\nnamespace snapshot {\n\n// @VsrTest = 3.7.6\nclass PartitionCowCreatorTest : public ::testing::Test {\n  public:\n    void SetUp() override {\n        SKIP_IF_NON_VIRTUAL_AB();\n        SnapshotTestPropertyFetcher::SetUp();\n    }\n    void TearDown() override {\n        RETURN_IF_NON_VIRTUAL_AB();\n        SnapshotTestPropertyFetcher::TearDown();\n    }\n};\n\nTEST_F(PartitionCowCreatorTest, IntersectSelf) {\n    constexpr uint64_t super_size = 1_MiB;\n    constexpr uint64_t partition_size = 40_KiB;\n\n    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_a, nullptr);\n    auto system_a = builder_a->AddPartition(\"system_a\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_a, nullptr);\n    ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));\n\n    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_b, nullptr);\n    auto system_b = builder_b->AddPartition(\"system_b\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_b, nullptr);\n    ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));\n\n    PartitionCowCreator creator{.target_metadata = builder_b.get(),\n                                .target_suffix = \"_b\",\n                                .target_partition = system_b,\n                                .current_metadata = builder_a.get(),\n                                .current_suffix = \"_a\"};\n    auto ret = creator.Run();\n    ASSERT_TRUE(ret.has_value());\n    ASSERT_EQ(partition_size, ret->snapshot_status.device_size());\n    ASSERT_EQ(partition_size, ret->snapshot_status.snapshot_size());\n}\n\nTEST_F(PartitionCowCreatorTest, Holes) {\n    const auto& opener = test_device->GetPartitionOpener();\n\n    constexpr auto slack_space = 1_MiB;\n    constexpr auto big_size = (kSuperSize - slack_space) / 2;\n    constexpr auto small_size = big_size / 2;\n\n    BlockDeviceInfo super_device(\"super\", kSuperSize, 0, 0, 4_KiB);\n    std::vector<BlockDeviceInfo> devices = {super_device};\n    auto source = MetadataBuilder::New(devices, \"super\", 1_KiB, 2);\n    auto system = source->AddPartition(\"system_a\", 0);\n    ASSERT_NE(nullptr, system);\n    ASSERT_TRUE(source->ResizePartition(system, big_size));\n    auto vendor = source->AddPartition(\"vendor_a\", 0);\n    ASSERT_NE(nullptr, vendor);\n    ASSERT_TRUE(source->ResizePartition(vendor, big_size));\n    // Create a hole between system and vendor\n    ASSERT_TRUE(source->ResizePartition(system, small_size));\n    auto source_metadata = source->Export();\n    ASSERT_NE(nullptr, source_metadata);\n    ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *source_metadata.get()));\n\n    auto target = MetadataBuilder::NewForUpdate(opener, \"super\", 0, 1);\n    // Shrink vendor\n    vendor = target->FindPartition(\"vendor_b\");\n    ASSERT_NE(nullptr, vendor);\n    ASSERT_TRUE(target->ResizePartition(vendor, small_size));\n    // Grow system to take hole & saved space from vendor\n    system = target->FindPartition(\"system_b\");\n    ASSERT_NE(nullptr, system);\n    ASSERT_TRUE(target->ResizePartition(system, big_size * 2 - small_size));\n\n    PartitionCowCreator creator{.target_metadata = target.get(),\n                                .target_suffix = \"_b\",\n                                .target_partition = system,\n                                .current_metadata = source.get(),\n                                .current_suffix = \"_a\"};\n    auto ret = creator.Run();\n    ASSERT_TRUE(ret.has_value());\n}\n\nTEST_F(PartitionCowCreatorTest, CowSize) {\n    using InstallOperation = chromeos_update_engine::InstallOperation;\n    using RepeatedInstallOperationPtr = google::protobuf::RepeatedPtrField<InstallOperation>;\n    using Extent = chromeos_update_engine::Extent;\n\n    constexpr uint64_t super_size = 50_MiB;\n    constexpr uint64_t partition_size = 40_MiB;\n\n    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_a, nullptr);\n    auto system_a = builder_a->AddPartition(\"system_a\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_a, nullptr);\n    ASSERT_TRUE(builder_a->ResizePartition(system_a, partition_size));\n\n    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_b, nullptr);\n    auto system_b = builder_b->AddPartition(\"system_b\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_b, nullptr);\n    ASSERT_TRUE(builder_b->ResizePartition(system_b, partition_size));\n\n    const uint64_t block_size = builder_b->logical_block_size();\n    const uint64_t chunk_size = kSnapshotChunkSize * dm::kSectorSize;\n    ASSERT_EQ(chunk_size, block_size);\n\n    auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a,\n                              MetadataBuilder* builder_b, Partition* system_b) {\n        PartitionUpdate update;\n        *update.mutable_operations() = RepeatedInstallOperationPtr(iopv.begin(), iopv.end());\n\n        PartitionCowCreator creator{.target_metadata = builder_b,\n                                    .target_suffix = \"_b\",\n                                    .target_partition = system_b,\n                                    .current_metadata = builder_a,\n                                    .current_suffix = \"_a\",\n                                    .update = &update};\n\n        auto ret = creator.Run();\n\n        if (ret.has_value()) {\n            return ret->snapshot_status.cow_file_size() + ret->snapshot_status.cow_partition_size();\n        }\n        return std::numeric_limits<uint64_t>::max();\n    };\n\n    std::vector<InstallOperation> iopv;\n    InstallOperation iop;\n    Extent* e;\n\n    // No data written, no operations performed\n    ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));\n\n    // No data written\n    e = iop.add_dst_extents();\n    e->set_start_block(0);\n    e->set_num_blocks(0);\n    iopv.push_back(iop);\n    ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));\n\n    e = iop.add_dst_extents();\n    e->set_start_block(1);\n    e->set_num_blocks(0);\n    iopv.push_back(iop);\n    ASSERT_EQ(2 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));\n\n    // Fill the first block\n    e = iop.add_dst_extents();\n    e->set_start_block(0);\n    e->set_num_blocks(1);\n    iopv.push_back(iop);\n    ASSERT_EQ(3 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));\n\n    // Fill the second block\n    e = iop.add_dst_extents();\n    e->set_start_block(1);\n    e->set_num_blocks(1);\n    iopv.push_back(iop);\n    ASSERT_EQ(4 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));\n\n    // Jump to 5th block and write 2\n    e = iop.add_dst_extents();\n    e->set_start_block(5);\n    e->set_num_blocks(2);\n    iopv.push_back(iop);\n    ASSERT_EQ(6 * chunk_size, cow_device_size(iopv, builder_a.get(), builder_b.get(), system_b));\n}\n\nTEST_F(PartitionCowCreatorTest, Zero) {\n    constexpr uint64_t super_size = 1_MiB;\n    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_a, nullptr);\n\n    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_b, nullptr);\n    auto system_b = builder_b->AddPartition(\"system_b\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_b, nullptr);\n\n    PartitionCowCreator creator{.target_metadata = builder_b.get(),\n                                .target_suffix = \"_b\",\n                                .target_partition = system_b,\n                                .current_metadata = builder_a.get(),\n                                .current_suffix = \"_a\",\n                                .update = nullptr};\n\n    auto ret = creator.Run();\n\n    ASSERT_EQ(0u, ret->snapshot_status.device_size());\n    ASSERT_EQ(0u, ret->snapshot_status.snapshot_size());\n    ASSERT_EQ(0u, ret->snapshot_status.cow_file_size());\n    ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());\n}\n\nTEST_F(PartitionCowCreatorTest, CompressionEnabled) {\n    constexpr uint64_t super_size = 1_MiB;\n    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_a, nullptr);\n\n    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_b, nullptr);\n    auto system_b = builder_b->AddPartition(\"system_b\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_b, nullptr);\n    ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));\n\n    PartitionUpdate update;\n    update.set_estimate_cow_size(256_KiB);\n\n    PartitionCowCreator creator{.target_metadata = builder_b.get(),\n                                .target_suffix = \"_b\",\n                                .target_partition = system_b,\n                                .current_metadata = builder_a.get(),\n                                .current_suffix = \"_a\",\n                                .update = &update,\n                                .using_snapuserd = true};\n\n    auto ret = creator.Run();\n    ASSERT_TRUE(ret.has_value());\n    ASSERT_EQ(ret->snapshot_status.cow_file_size(), 1458176);\n}\n\nTEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) {\n    constexpr uint64_t super_size = 1_MiB;\n    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_a, nullptr);\n\n    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);\n    ASSERT_NE(builder_b, nullptr);\n    auto system_b = builder_b->AddPartition(\"system_b\", LP_PARTITION_ATTR_READONLY);\n    ASSERT_NE(system_b, nullptr);\n    ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));\n\n    PartitionUpdate update;\n\n    PartitionCowCreator creator{.target_metadata = builder_b.get(),\n                                .target_suffix = \"_b\",\n                                .target_partition = system_b,\n                                .current_metadata = builder_a.get(),\n                                .current_suffix = \"_a\",\n                                .update = nullptr,\n                                .using_snapuserd = true};\n\n    auto ret = creator.Run();\n    ASSERT_FALSE(ret.has_value());\n}\n\nTEST(DmSnapshotInternals, CowSizeCalculator) {\n    SKIP_IF_NON_VIRTUAL_AB();\n\n    DmSnapCowSizeCalculator cc(512, 8);\n    unsigned long int b;\n\n    // Empty COW\n    ASSERT_EQ(cc.cow_size_sectors(), 16);\n\n    // First chunk written\n    for (b = 0; b < 4_KiB; ++b) {\n        cc.WriteByte(b);\n        ASSERT_EQ(cc.cow_size_sectors(), 24);\n    }\n\n    // Second chunk written\n    for (b = 4_KiB; b < 8_KiB; ++b) {\n        cc.WriteByte(b);\n        ASSERT_EQ(cc.cow_size_sectors(), 32);\n    }\n\n    // Leave a hole and write 5th chunk\n    for (b = 16_KiB; b < 20_KiB; ++b) {\n        cc.WriteByte(b);\n        ASSERT_EQ(cc.cow_size_sectors(), 40);\n    }\n\n    // Write a byte that would surely overflow the counter\n    cc.WriteChunk(std::numeric_limits<uint64_t>::max());\n    ASSERT_FALSE(cc.cow_size_sectors().has_value());\n}\n\nvoid BlocksToExtents(const std::vector<uint64_t>& blocks,\n                     google::protobuf::RepeatedPtrField<UeExtent>* extents) {\n    for (uint64_t block : blocks) {\n        AppendExtent(extents, block, 1);\n    }\n}\n\ntemplate <typename T>\nstd::vector<uint64_t> ExtentsToBlocks(const T& extents) {\n    std::vector<uint64_t> blocks;\n    for (const auto& extent : extents) {\n        for (uint64_t offset = 0; offset < extent.num_blocks(); ++offset) {\n            blocks.push_back(extent.start_block() + offset);\n        }\n    }\n    return blocks;\n}\n\nInstallOperation CreateCopyOp(const std::vector<uint64_t>& src_blocks,\n                              const std::vector<uint64_t>& dst_blocks) {\n    InstallOperation op;\n    op.set_type(InstallOperation::SOURCE_COPY);\n    BlocksToExtents(src_blocks, op.mutable_src_extents());\n    BlocksToExtents(dst_blocks, op.mutable_dst_extents());\n    return op;\n}\n\n// ExtentEqual(tuple<UeExtent, UeExtent>)\nMATCHER(ExtentEqual, \"\") {\n    auto&& [a, b] = arg;\n    return a.start_block() == b.start_block() && a.num_blocks() == b.num_blocks();\n}\n\nstruct OptimizeOperationTestParam {\n    InstallOperation input;\n    std::optional<InstallOperation> expected_output;\n};\n\nclass OptimizeOperationTest : public ::testing::TestWithParam<OptimizeOperationTestParam> {\n    void SetUp() override { SKIP_IF_NON_VIRTUAL_AB(); }\n};\nTEST_P(OptimizeOperationTest, Test) {\n    InstallOperation actual_output;\n    EXPECT_EQ(GetParam().expected_output.has_value(),\n              OptimizeSourceCopyOperation(GetParam().input, &actual_output))\n            << \"OptimizeSourceCopyOperation should \"\n            << (GetParam().expected_output.has_value() ? \"succeed\" : \"fail\");\n    if (!GetParam().expected_output.has_value()) return;\n    EXPECT_THAT(actual_output.src_extents(),\n                Pointwise(ExtentEqual(), GetParam().expected_output->src_extents()));\n    EXPECT_THAT(actual_output.dst_extents(),\n                Pointwise(ExtentEqual(), GetParam().expected_output->dst_extents()));\n}\n\nstd::vector<OptimizeOperationTestParam> GetOptimizeOperationTestParams() {\n    return {\n            {CreateCopyOp({}, {}), CreateCopyOp({}, {})},\n            {CreateCopyOp({1, 2, 4}, {1, 2, 4}), CreateCopyOp({}, {})},\n            {CreateCopyOp({1, 2, 3}, {4, 5, 6}), std::nullopt},\n            {CreateCopyOp({3, 2}, {1, 2}), CreateCopyOp({3}, {1})},\n            {CreateCopyOp({5, 6, 3, 4, 1, 2}, {1, 2, 3, 4, 5, 6}),\n             CreateCopyOp({5, 6, 1, 2}, {1, 2, 5, 6})},\n            {CreateCopyOp({1, 2, 3, 5, 5, 6}, {5, 6, 3, 4, 1, 2}),\n             CreateCopyOp({1, 2, 5, 5, 6}, {5, 6, 4, 1, 2})},\n            {CreateCopyOp({1, 2, 5, 6, 9, 10}, {1, 4, 5, 6, 7, 8}),\n             CreateCopyOp({2, 9, 10}, {4, 7, 8})},\n            {CreateCopyOp({2, 3, 3, 4, 4}, {1, 2, 3, 4, 5}), CreateCopyOp({2, 3, 4}, {1, 2, 5})},\n    };\n}\n\nINSTANTIATE_TEST_CASE_P(Snapshot, OptimizeOperationTest,\n                        ::testing::ValuesIn(GetOptimizeOperationTestParams()));\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/return.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <libsnapshot/return.h>\n\n#include <string.h>\n\nusing android::fiemap::FiemapStatus;\n\nnamespace android::snapshot {\n\nstd::string Return::string() const {\n    switch (error_code()) {\n        case ErrorCode::ERROR:\n            return \"Error\";\n        case ErrorCode::SUCCESS:\n            [[fallthrough]];\n        case ErrorCode::NO_SPACE:\n            return strerror(-static_cast<int>(error_code()));\n    }\n}\n\nReturn::ErrorCode Return::FromFiemapStatusErrorCode(FiemapStatus::ErrorCode error_code) {\n    switch (error_code) {\n        case FiemapStatus::ErrorCode::SUCCESS:\n        case FiemapStatus::ErrorCode::ERROR:\n        case FiemapStatus::ErrorCode::NO_SPACE:\n            return static_cast<ErrorCode>(error_code);\n        default:\n            return ErrorCode::ERROR;\n    }\n}\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/scratch_super.cpp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <selinux/selinux.h>\n#include <stdlib.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <sys/types.h>\n#include <sys/vfs.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <filesystem>\n#include <memory>\n#include <optional>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <ext4_utils/ext4_utils.h>\n\n#include <libsnapshot/snapshot.h>\n\n#include <fs_mgr.h>\n#include <fs_mgr_dm_linear.h>\n#include <fstab/fstab.h>\n#include <liblp/builder.h>\n#include <storage_literals/storage_literals.h>\n\n#include \"device_info.h\"\n#include \"scratch_super.h\"\n\nusing namespace std::literals;\nusing namespace android::dm;\nusing namespace android::fs_mgr;\nusing namespace android::storage_literals;\n\nnamespace android {\nnamespace snapshot {\n\nstatic bool UmountScratch() {\n    Fstab fstab;\n    if (!ReadFstabFromProcMounts(&fstab)) {\n        LOG(ERROR) << \"Cannot read /proc/mounts\";\n        return false;\n    }\n    if (GetEntryForMountPoint(&fstab, kOtaMetadataMount) == nullptr) {\n        return true;\n    }\n\n    auto ota_dir = std::string(kOtaMetadataMount) + \"/\" + \"ota\";\n\n    std::error_code ec;\n    if (std::filesystem::remove_all(ota_dir, ec) == static_cast<std::uintmax_t>(-1)) {\n        LOG(ERROR) << \"Failed to remove OTA directory: \" << ec.message();\n        return false;\n    }\n\n    if (umount(kOtaMetadataMount) != 0) {\n        PLOG(ERROR) << \"UmountScratch failed\";\n        return false;\n    }\n\n    LOG(INFO) << \"umount scratch_super success\";\n    return true;\n}\n\nbool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info) {\n    if (!UmountScratch()) {\n        return false;\n    }\n\n    std::unique_ptr<MetadataBuilder> builder;\n    const auto partition_name = android::base::Basename(kOtaMetadataMount);\n    const std::vector<int> slots = {0, 1};\n\n    if (info == nullptr) {\n        info = new android::snapshot::DeviceInfo();\n    }\n\n    std::string super_device;\n    if (info->IsTestDevice()) {\n        super_device = \"super\";\n    } else {\n        super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n    }\n    const auto& opener = info->GetPartitionOpener();\n    std::string slot_suffix = info->GetSlotSuffix();\n    // Walk both the slots and clean up metadata related to scratch space from\n    // both the slots.\n    for (auto slot : slots) {\n        std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);\n        if (!builder) {\n            return false;\n        }\n\n        if (builder->FindPartition(partition_name) != nullptr) {\n            builder->RemovePartition(partition_name);\n            auto metadata = builder->Export();\n            if (!metadata) {\n                return false;\n            }\n            if (!UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(),\n                                      slot)) {\n                LOG(ERROR) << \"UpdatePartitionTable failed for slot: \" << slot;\n                return false;\n            }\n            if (DestroyLogicalPartition(partition_name)) {\n                LOG(INFO) << \"CleanupScratchOtaMetadata success for slot: \" << slot;\n            }\n        }\n    }\n\n    return true;\n}\n\nstatic bool SetupOTADirs() {\n    if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {\n        PLOG(ERROR) << \"setfscreatecon failed: \" << android::snapshot::kOtaMetadataFileContext;\n        return false;\n    }\n    const auto ota_dir = std::string(kOtaMetadataMount) + \"/\" + \"ota\";\n    if (mkdir(ota_dir.c_str(), 0755) != 0 && errno != EEXIST) {\n        PLOG(ERROR) << \"mkdir \" << ota_dir;\n        return false;\n    }\n\n    const auto snapshot_dir = ota_dir + \"/\" + \"snapshots\";\n    if (mkdir(snapshot_dir.c_str(), 0755) != 0 && errno != EEXIST) {\n        PLOG(ERROR) << \"mkdir \" << snapshot_dir;\n        return false;\n    }\n    if (setfscreatecon(nullptr)) {\n        PLOG(ERROR) << \"setfscreatecon null\";\n        return false;\n    }\n    return true;\n}\n\nstatic bool MountScratch(const std::string& device_path) {\n    if (access(device_path.c_str(), R_OK | W_OK)) {\n        LOG(ERROR) << \"Path does not exist or is not readwrite: \" << device_path;\n        return false;\n    }\n\n    std::string filesystem_candidate;\n    if (fs_mgr_is_ext4(device_path)) {\n        filesystem_candidate = \"ext4\";\n    } else {\n        LOG(ERROR) << \"Scratch partition is not ext4\";\n        return false;\n    }\n    if (setfscreatecon(android::snapshot::kOtaMetadataFileContext)) {\n        PLOG(ERROR) << \"setfscreatecon failed: \" << android::snapshot::kOtaMetadataFileContext;\n        return false;\n    }\n    if (mkdir(kOtaMetadataMount, 0755) && (errno != EEXIST)) {\n        PLOG(ERROR) << \"create \" << kOtaMetadataMount;\n        return false;\n    }\n\n    android::fs_mgr::FstabEntry entry;\n    entry.blk_device = device_path;\n    entry.mount_point = kOtaMetadataMount;\n    entry.flags = MS_NOATIME;\n    entry.flags |= MS_SYNCHRONOUS;\n    entry.fs_options = \"nodiscard\";\n    fs_mgr_set_blk_ro(device_path, false);\n    entry.fs_mgr_flags.check = true;\n\n    bool mounted = false;\n    entry.fs_type = filesystem_candidate.c_str();\n    if (fs_mgr_do_mount_one(entry) == 0) {\n        mounted = true;\n    }\n\n    if (setfscreatecon(nullptr)) {\n        PLOG(ERROR) << \"setfscreatecon null\";\n        return false;\n    }\n    if (!mounted) {\n        rmdir(kOtaMetadataMount);\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool MakeScratchFilesystem(const std::string& scratch_device) {\n    std::string fs_type;\n    std::string command;\n    if (!access(kMkExt4, X_OK)) {\n        fs_type = \"ext4\";\n        command = kMkExt4 + \" -F -b 4096 -t ext4 -m 0 -O has_journal -M \"s + kOtaMetadataMount;\n    } else {\n        LOG(ERROR) << \"No supported mkfs command or filesystem driver available, supported \"\n                      \"filesystems \"\n                      \"are: f2fs, ext4\";\n        return false;\n    }\n    command += \" \" + scratch_device + \" >/dev/null 2>/dev/null </dev/null\";\n    fs_mgr_set_blk_ro(scratch_device, false);\n    auto ret = system(command.c_str());\n    if (ret) {\n        LOG(ERROR) << \"make \" << fs_type << \" filesystem on \" << scratch_device\n                   << \" return=\" << ret;\n        return false;\n    }\n    return true;\n}\n\nstatic bool CreateDynamicScratch(const ISnapshotManager::IDeviceInfo* info,\n                                 std::string* scratch_device) {\n    const auto partition_name = android::base::Basename(kOtaMetadataMount);\n    auto& dm = DeviceMapper::Instance();\n    if (info == nullptr) {\n        info = new android::snapshot::DeviceInfo();\n    }\n\n    std::string super_device;\n    if (info->IsTestDevice()) {\n        super_device = \"super\";\n    } else {\n        super_device = kPhysicalDevice + fs_mgr_get_super_partition_name();\n    }\n\n    bool partition_exists = dm.GetState(partition_name) != DmDeviceState::INVALID;\n    if (partition_exists) {\n        LOG(ERROR) << \"Partition already exists: \" << partition_name;\n        return false;\n    }\n\n    const auto& opener = info->GetPartitionOpener();\n    std::string slot_suffix = info->GetSlotSuffix();\n    int slot = SlotNumberForSlotSuffix(slot_suffix);\n    std::unique_ptr<MetadataBuilder> builder = MetadataBuilder::New(opener, super_device, slot);\n\n    if (!builder) {\n        LOG(ERROR) << \"open \" << super_device << \" failed\";\n        return false;\n    }\n\n    auto partition = builder->FindPartition(partition_name);\n    partition_exists = partition != nullptr;\n    if (partition_exists) {\n        LOG(ERROR) << \"Partition exists in super metadata\";\n        return false;\n    }\n\n    partition = builder->AddPartition(partition_name, LP_PARTITION_ATTR_NONE);\n    if (!partition) {\n        LOG(ERROR) << \"AddPartition failed \" << partition_name;\n        return false;\n    }\n\n    auto free_space = builder->AllocatableSpace() - builder->UsedSpace();\n    if (free_space < kOtaMetadataPartitionSize) {\n        LOG(ERROR) << \"No space in super partition. Free space: \" << free_space\n                   << \" Requested space: \" << kOtaMetadataPartitionSize;\n        return false;\n    }\n\n    LOG(INFO) << \"CreateDynamicScratch: free_space: \" << free_space\n              << \" scratch_size: \" << kOtaMetadataPartitionSize << \" slot_number: \" << slot;\n\n    if (!builder->ResizePartition(partition, kOtaMetadataPartitionSize)) {\n        LOG(ERROR) << \"ResizePartition failed: \" << partition_name << \" free_space: \" << free_space\n                   << \" scratch_size: \" << kOtaMetadataPartitionSize;\n        return false;\n    }\n\n    auto metadata = builder->Export();\n    CreateLogicalPartitionParams params;\n\n    if (!metadata ||\n        !UpdatePartitionTable(info->GetPartitionOpener(), super_device, *metadata.get(), slot)) {\n        LOG(ERROR) << \"UpdatePartitionTable failed: \" << partition_name;\n        return false;\n    }\n    params = {\n            .block_device = super_device,\n            .metadata_slot = slot,\n            .partition_name = partition_name,\n            .force_writable = true,\n            .timeout_ms = 10s,\n            .partition_opener = &info->GetPartitionOpener(),\n    };\n\n    if (!CreateLogicalPartition(params, scratch_device)) {\n        LOG(ERROR) << \"CreateLogicalPartition failed\";\n        return false;\n    }\n\n    LOG(INFO) << \"Scratch device created successfully: \" << *scratch_device << \" slot: \" << slot;\n    return true;\n}\n\nbool IsScratchOtaMetadataOnSuper() {\n    auto partition_name = android::base::Basename(kOtaMetadataMount);\n    auto source_slot = fs_mgr_get_slot_suffix();\n    auto source_slot_number = SlotNumberForSlotSuffix(source_slot);\n\n    const auto super_device =\n            kPhysicalDevice + fs_mgr_get_super_partition_name(!source_slot_number);\n\n    auto metadata = android::fs_mgr::ReadMetadata(super_device, !source_slot_number);\n    if (!metadata) {\n        return false;\n    }\n    auto partition = android::fs_mgr::FindPartition(*metadata.get(), partition_name);\n    if (!partition) {\n        return false;\n    }\n\n    auto& dm = DeviceMapper::Instance();\n    if (dm.GetState(partition_name) == DmDeviceState::ACTIVE) {\n        LOG(INFO) << \"Partition: \" << partition_name << \" is active\";\n        return true;\n    }\n\n    CreateLogicalPartitionParams params = {\n            .block_device = super_device,\n            .metadata = metadata.get(),\n            .partition = partition,\n    };\n\n    std::string scratch_path;\n    if (!CreateLogicalPartition(params, &scratch_path)) {\n        LOG(ERROR) << \"Could not create logical partition: \" << partition_name;\n        return false;\n    }\n    LOG(INFO) << \"Scratch device: \" << scratch_path << \" created successfully\";\n\n    return true;\n}\n\nstd::string GetScratchOtaMetadataPartition() {\n    std::string device;\n    auto& dm = DeviceMapper::Instance();\n    auto partition_name = android::base::Basename(kOtaMetadataMount);\n\n    bool invalid_partition = (dm.GetState(partition_name) == DmDeviceState::INVALID);\n    if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &device)) {\n        return device;\n    }\n    return \"\";\n}\n\nstatic bool ScratchAlreadyMounted(const std::string& mount_point) {\n    android::fs_mgr::Fstab fstab;\n    if (!ReadFstabFromProcMounts(&fstab)) {\n        return false;\n    }\n    for (const auto& entry : GetEntriesForMountPoint(&fstab, mount_point)) {\n        if (entry->fs_type == \"ext4\") {\n            return true;\n        }\n    }\n    return false;\n}\n\nstd::string MapScratchOtaMetadataPartition(const std::string& scratch_device) {\n    if (!ScratchAlreadyMounted(kOtaMetadataMount)) {\n        if (!MountScratch(scratch_device)) {\n            return \"\";\n        }\n    }\n\n    auto ota_dir = std::string(kOtaMetadataMount) + \"/\" + \"ota\";\n    if (access(ota_dir.c_str(), F_OK) != 0) {\n        return \"\";\n    }\n    return ota_dir;\n}\n\n// Entry point to create a scratch device on super partition\n// This will create a 2MB space in super. The space will be\n// from the current active slot. Ext4 filesystem will be created\n// on this scratch device and all the OTA related directories\n// will be created.\nbool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info) {\n    std::string scratch_device;\n\n    if (!CreateDynamicScratch(info, &scratch_device)) {\n        LOG(ERROR) << \"CreateDynamicScratch failed\";\n        return false;\n    }\n    if (!MakeScratchFilesystem(scratch_device)) {\n        LOG(ERROR) << \"MakeScratchFilesystem failed\";\n        return false;\n    }\n    if (!MountScratch(scratch_device)) {\n        LOG(ERROR) << \"MountScratch failed\";\n        return false;\n    }\n    if (!SetupOTADirs()) {\n        LOG(ERROR) << \"SetupOTADirs failed\";\n        return false;\n    }\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/scratch_super.h",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\nnamespace android {\nnamespace snapshot {\n\nconstexpr char kMkExt4[] = \"/system/bin/mke2fs\";\nconstexpr char kOtaMetadataFileContext[] = \"u:object_r:ota_metadata_file:s0\";\nconstexpr char kOtaMetadataMount[] = \"/mnt/scratch_ota_metadata_super\";\nconst size_t kOtaMetadataPartitionSize = uint64_t(2 * 1024 * 1024);\nconstexpr char kPhysicalDevice[] = \"/dev/block/by-name/\";\n\nbool IsScratchOtaMetadataOnSuper();\nstd::string GetScratchOtaMetadataPartition();\nstd::string MapScratchOtaMetadataPartition(const std::string& device);\nbool CreateScratchOtaMetadataOnSuper(const ISnapshotManager::IDeviceInfo* info = nullptr);\nbool CleanupScratchOtaMetadataIfPresent(const ISnapshotManager::IDeviceInfo* info = nullptr);\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/scripts/Android.bp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_binary_host {\n    name: \"dump_snapshot_proto\",\n    main: \"dump_snapshot_proto.py\",\n    srcs: [\n        \"dump_snapshot_proto.py\",\n    ],\n    libs: [\n        \"snapshot_proto_python\",\n    ],\n}\n\nsh_binary_host {\n    name: \"apply_update\",\n    src: \"apply-update.sh\",\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/scripts/apply-update.sh",
    "content": "#!/bin/bash\n\n# Copyright 2024 Google Inc. All rights reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# apply_update.sh: Script to update the device in incremental way\n\n# Ensure OUT directory exists\nif [ -z \"$OUT\" ]; then\n  echo \"Error: OUT environment variable not set.\" >&2\n  exit 1\nfi\n\nDEVICE_PATH=\"/data/verity-hash\"\nHOST_PATH=\"$OUT/verity-hash\"\n\n# Create the log file path\nlog_file=\"$HOST_PATH/snapshot.log\"\n\n# Function to log messages to both console and log file\nlog_message() {\n    message=\"$1\"\n    echo \"$message\"  # Print to stdout\n    echo \"$(date '+%Y-%m-%d %H:%M:%S') - $message\" >> \"$log_file\"  # Append to log file with timestamp\n}\n\n# Function to check for create_snapshot and build if needed\nensure_create_snapshot() {\n  if ! command -v create_snapshot &> /dev/null; then\n    log_message \"create_snapshot not found. Building...\"\n    m create_snapshot\n    if [[ $? -ne 0 ]]; then\n      log_message \"Error: Failed to build create_snapshot.\"\n      exit 1\n    fi\n  fi\n}\n\nensure_create_snapshot\n\n# Function to flash static partitions\nflash_static_partitions() {\n  local wipe_flag=\"$1\"\n  local flash_bootloader=\"$2\"\n\n  if (( flash_bootloader )); then\n    fastboot flash bootloader \"$OUT\"/bootloader.img\n    fastboot reboot bootloader\n    sleep 1\n    fastboot flash radio \"$OUT\"/radio.img\n    fastboot reboot bootloader\n    sleep 1\n  fi\n  fastboot flashall --exclude-dynamic-partitions --disable-super-optimization --skip-reboot\n\n  if (( wipe_flag )); then\n      log_message \"Wiping device...\"\n      fastboot -w\n  fi\n  fastboot reboot\n}\n\n# Function to display the help message\nshow_help() {\n  cat << EOF\nUsage: $0 [OPTIONS]\n\nThis script updates an Android device with incremental flashing, optionally wiping data and flashing static partitions.\n\nOptions:\n  --skip-static-partitions  Skip flashing static partitions (bootloader, radio, boot, vbmeta, dtbo and other static A/B partitions).\n                           * Requires manual update of static partitions on both A/B slots\n                             *before* using this flag.\n                           * Speeds up the update process and development iteration.\n                           * Ideal for development focused on the Android platform (AOSP,\n                             git_main).\n                           * Safe usage: First update static partitions on both slots, then\n                             use this flag for faster development iterations.\n                             Ex:\n                                1: Run this on both the slots - This will update the kernel and other static partitions:\n                                   $fastboot flashall --exclude-dynamic-partitions --disable-super-optimization --skip-reboot\n\n                                2: Update bootloader on both the slots:\n                                    $fastboot flash bootloader $OUT/bootloader.img --slot=all\n\n                                3: Update radio on both the slots:\n                                    $fastboot flash radio $OUT/radio.img --slot=all\n                            Now, the script can safely use this flag for update purpose.\n\n  --wipe                   Wipe user data during the update.\n  --boot_snapshot          Boot the device off snapshots - No data wipe is supported\n                              To revert back to original state - `adb shell snapshotctl revert-snapshots`\n  --help                   Display this help message.\n\nEnvironment Variables:\n  OUT                      Path to the directory containing build output.\n                           This is required for the script to function correctly.\n\nExamples:\n  <Development workflow for any project in the platform and build with 'm' to create the images>\n\n  Update the device:\n  $0\n\n  Update the device, but skip flashing static partitions (see above for the usage):\n  $0 --skip-static-partitions\n\n  Update the device and wipe user data:\n  $0 --wipe\n\n  Display this help message:\n  $0 --help\nEOF\n}\n\nskip_static_partitions=0\nboot_snapshot=0\nflash_bootloader=1\nwipe_flag=0\nhelp_flag=0\n\n# Parse arguments\nfor arg in \"$@\"; do\n  case \"$arg\" in\n    --skip-static-partitions)\n      skip_static_partitions=1\n      ;;\n    --wipe)\n      wipe_flag=1\n      ;;\n    --skip_bootloader)\n      flash_bootloader=0\n      ;;\n    --boot_snapshot)\n      boot_snapshot=1\n      ;;\n    --help)\n      help_flag=1\n      ;;\n    *)\n      echo \"Unknown argument: $arg\" >&2\n      help_flag=1\n      ;;\n  esac\ndone\n\n# Check if help flag is set\nif (( help_flag )); then\n  show_help\n  exit 0\nfi\n\nrm -rf $HOST_PATH\n\nadb root\nadb wait-for-device\n\nadb shell rm -rf $DEVICE_PATH\nadb shell mkdir -p $DEVICE_PATH\n\necho \"Extracting device source hash from dynamic partitions\"\nadb shell snapshotctl dump-verity-hash $DEVICE_PATH\nadb pull -q $DEVICE_PATH $OUT/\n\nlog_message \"Entering directory:\"\n\n# Navigate to the verity-hash directory\ncd \"$HOST_PATH\" || { log_message \"Error: Could not navigate to $HOST_PATH\"; exit 1; }\n\npwd\n\n# Iterate over all .pb files using a for loop\nfor pb_file in *.pb; do\n  # Extract the base filename without the .pb extension\n  base_filename=\"${pb_file%.*}\"\n\n  # Construct the source and target file names\n  source_file=\"$pb_file\"\n  target_file=\"$OUT/$base_filename.img\"\n\n  # Construct the create_snapshot command using an array\n  snapshot_args=(\n    \"create_snapshot\"\n    \"--source\" \"$source_file\"\n    \"--target\" \"$target_file\"\n    \"--merkel_tree\"\n  )\n\n  # Log the command about to be executed\n  log_message \"Running: ${snapshot_args[*]}\"\n\n  \"${snapshot_args[@]}\" >> \"$log_file\" 2>&1 &\ndone\n\nlog_message \"Waiting for snapshot patch creation\"\n\n# Wait for all background processes to complete\nwait $(jobs -p)\n\nlog_message \"Snapshot patches created successfully\"\n\nadb push -q $HOST_PATH/*.patch $DEVICE_PATH\n\nlog_message \"Applying update\"\n\nif (( boot_snapshot)); then\n  adb shell snapshotctl map-snapshots $DEVICE_PATH\nelif (( wipe_flag )); then\n  adb shell snapshotctl apply-update $DEVICE_PATH -w\nelse\n  adb shell snapshotctl apply-update $DEVICE_PATH\nfi\n\nif (( skip_static_partitions )); then\n    log_message \"Rebooting device - Skipping flashing static partitions\"\n    adb reboot\nelse\n    log_message \"Rebooting device to bootloader\"\n    adb reboot bootloader\n    log_message \"Waiting to enter fastboot bootloader\"\n    flash_static_partitions \"$wipe_flag\" \"$flash_bootloader\"\nfi\n\nlog_message \"Update completed\"\n"
  },
  {
    "path": "fs_mgr/libsnapshot/scripts/dump_snapshot_proto.py",
    "content": "# Copyright (C) 2021 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\n\nfrom android.snapshot import snapshot_pb2\n\ndef main():\n    parser = argparse.ArgumentParser()\n    parser.add_argument('type', type = str, help = 'Type (snapshot or update)')\n    parser.add_argument('file', type = str, help = 'Input file')\n    args = parser.parse_args()\n\n    with open(args.file, 'rb') as fp:\n        data = fp.read()\n\n    if args.type == 'snapshot':\n        msg = snapshot_pb2.SnapshotStatus()\n    elif args.type == 'update':\n        msg = snapshot_pb2.SnapshotUpdateStatus()\n    else:\n        raise Exception('Unknown proto type')\n\n    msg.ParseFromString(data)\n    print(msg)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshot.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <libsnapshot/snapshot.h>\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <math.h>\n#include <sys/file.h>\n#include <sys/types.h>\n#include <sys/unistd.h>\n#include <sys/xattr.h>\n\n#include <chrono>\n#include <filesystem>\n#include <optional>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <cutils/sockets.h>\n#include <ext4_utils/ext4_utils.h>\n#include <fs_mgr.h>\n#include <fs_mgr/file_wait.h>\n#include <fs_mgr_dm_linear.h>\n#include <fstab/fstab.h>\n#include <libdm/dm.h>\n#include <libfiemap/image_manager.h>\n#include <liblp/liblp.h>\n\n#include <android/snapshot/snapshot.pb.h>\n#include <libsnapshot/snapshot_stats.h>\n#include \"device_info.h\"\n#include \"partition_cow_creator.h\"\n#include \"scratch_super.h\"\n#include \"snapshot_metadata_updater.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing aidl::android::hardware::boot::MergeStatus;\nusing android::base::unique_fd;\nusing android::dm::DeviceMapper;\nusing android::dm::DmDeviceState;\nusing android::dm::DmTable;\nusing android::dm::DmTargetLinear;\nusing android::dm::DmTargetSnapshot;\nusing android::dm::DmTargetUser;\nusing android::dm::kSectorSize;\nusing android::dm::SnapshotStorageMode;\nusing android::fiemap::FiemapStatus;\nusing android::fiemap::IImageManager;\nusing android::fs_mgr::CreateDmTable;\nusing android::fs_mgr::CreateLogicalPartition;\nusing android::fs_mgr::CreateLogicalPartitionParams;\nusing android::fs_mgr::GetPartitionGroupName;\nusing android::fs_mgr::GetPartitionName;\nusing android::fs_mgr::LpMetadata;\nusing android::fs_mgr::MetadataBuilder;\nusing android::fs_mgr::SlotNumberForSlotSuffix;\nusing chromeos_update_engine::DeltaArchiveManifest;\nusing chromeos_update_engine::Extent;\nusing chromeos_update_engine::FileDescriptor;\nusing chromeos_update_engine::PartitionUpdate;\ntemplate <typename T>\nusing RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;\nusing std::chrono::duration_cast;\nusing namespace std::chrono_literals;\nusing namespace std::string_literals;\nusing android::base::Realpath;\nusing android::base::StringPrintf;\n\nstatic constexpr char kBootSnapshotsWithoutSlotSwitch[] =\n        \"/metadata/ota/snapshot-boot-without-slot-switch\";\nstatic constexpr char kBootIndicatorPath[] = \"/metadata/ota/snapshot-boot\";\nstatic constexpr char kRollbackIndicatorPath[] = \"/metadata/ota/rollback-indicator\";\nstatic constexpr char kSnapuserdFromSystem[] = \"/metadata/ota/snapuserd-from-system\";\nstatic constexpr auto kUpdateStateCheckInterval = 2s;\nstatic constexpr char kOtaFileContext[] = \"u:object_r:ota_metadata_file:s0\";\n\n/*\n * The readahead size is set to 32kb so that\n * there is no significant memory pressure (/proc/pressure/memory) during boot.\n * After OTA, during boot, partitions are scanned before marking slot as successful.\n * This scan will trigger readahead both on source and COW block device thereby\n * leading to Inactive(file) pages to be very high.\n *\n * A lower value may help reduce memory pressure further, however, that will\n * increase the boot time. Thus, for device which don't care about OTA boot\n * time, they could use O_DIRECT functionality wherein the I/O to the source\n * block device will be O_DIRECT.\n */\nstatic constexpr auto kReadAheadSizeKb = 32;\n\n// Note: IImageManager is an incomplete type in the header, so the default\n// destructor doesn't work.\nSnapshotManager::~SnapshotManager() {}\n\nstd::unique_ptr<SnapshotManager> SnapshotManager::New(IDeviceInfo* info) {\n    if (!info) {\n        info = new DeviceInfo();\n    }\n\n    auto sm = std::unique_ptr<SnapshotManager>(new SnapshotManager(info));\n    if (info->IsTempMetadata()) {\n        LOG(INFO) << \"Using temp metadata from super\";\n    }\n    return sm;\n}\n\nstd::unique_ptr<SnapshotManager> SnapshotManager::NewForFirstStageMount(IDeviceInfo* info) {\n    if (!info) {\n        DeviceInfo* impl = new DeviceInfo();\n        impl->set_first_stage_init(true);\n        info = impl;\n    }\n    auto sm = New(info);\n\n    // The first-stage version of snapuserd is explicitly started by init. Do\n    // not attempt to using it during tests (which run in normal AOSP).\n    if (!sm->device()->IsTestDevice()) {\n        sm->use_first_stage_snapuserd_ = true;\n    }\n    return sm;\n}\n\nSnapshotManager::SnapshotManager(IDeviceInfo* device)\n    : dm_(device->GetDeviceMapper()), device_(device), metadata_dir_(device_->GetMetadataDir()) {}\n\nstatic std::string GetCowName(const std::string& snapshot_name) {\n    return snapshot_name + \"-cow\";\n}\n\nSnapshotManager::SnapshotDriver SnapshotManager::GetSnapshotDriver(LockedFile* lock) {\n    if (UpdateUsesUserSnapshots(lock)) {\n        return SnapshotManager::SnapshotDriver::DM_USER;\n    } else {\n        return SnapshotManager::SnapshotDriver::DM_SNAPSHOT;\n    }\n}\n\nstatic std::string GetDmUserCowName(const std::string& snapshot_name,\n                                    SnapshotManager::SnapshotDriver driver) {\n    // dm-user block device will act as a snapshot device. We identify it with\n    // the same partition name so that when partitions can be mounted off\n    // dm-user.\n\n    switch (driver) {\n        case SnapshotManager::SnapshotDriver::DM_USER: {\n            return snapshot_name;\n        }\n\n        case SnapshotManager::SnapshotDriver::DM_SNAPSHOT: {\n            return snapshot_name + \"-user-cow\";\n        }\n\n        default: {\n            LOG(ERROR) << \"Invalid snapshot driver\";\n            return \"\";\n        }\n    }\n}\n\nstatic std::string GetCowImageDeviceName(const std::string& snapshot_name) {\n    return snapshot_name + \"-cow-img\";\n}\n\nstatic std::string GetBaseDeviceName(const std::string& partition_name) {\n    return partition_name + \"-base\";\n}\n\nstatic std::string GetSourceDeviceName(const std::string& partition_name) {\n    return partition_name + \"-src\";\n}\n\nbool SnapshotManager::BeginUpdate() {\n    switch (TryCancelUpdate()) {\n        case CancelResult::OK:\n            break;\n        case CancelResult::NEEDS_MERGE: {\n            LOG(INFO) << \"Wait for merge (if any) before beginning a new update.\";\n            auto state = ProcessUpdateState();\n            LOG(INFO) << \"Merged with end state: \" << state;\n            break;\n        }\n        default:\n            LOG(ERROR) << \"Cannot begin update, existing update cannot be cancelled.\";\n            return false;\n    }\n\n    auto file = LockExclusive();\n    if (!file) return false;\n\n    // Purge the ImageManager just in case there is a corrupt lp_metadata file\n    // lying around. (NB: no need to return false on an error, we can let the\n    // update try to progress.)\n    if (EnsureImageManager()) {\n        images_->RemoveAllImages();\n    }\n\n    // Clear any cached metadata (this allows re-using one manager across tests).\n    old_partition_metadata_ = nullptr;\n\n    auto state = ReadUpdateState(file.get());\n    if (state != UpdateState::None) {\n        LOG(ERROR) << \"An update is already in progress, cannot begin a new update\";\n        return false;\n    }\n    return WriteUpdateState(file.get(), UpdateState::Initiated);\n}\n\nbool SnapshotManager::CancelUpdate() {\n    return TryCancelUpdate() == CancelResult::OK;\n}\n\nCancelResult SnapshotManager::TryCancelUpdate() {\n    auto lock = LockExclusive();\n    if (!lock) return CancelResult::ERROR;\n\n    UpdateState state = ReadUpdateState(lock.get());\n    CancelResult result = IsCancelUpdateSafe(state);\n\n    if (result != CancelResult::OK && device_->IsRecovery()) {\n        LOG(ERROR) << \"Cancel result \" << result << \" will be overridden in recovery.\";\n        result = CancelResult::OK;\n    }\n\n    switch (result) {\n        case CancelResult::OK:\n            LOG(INFO) << \"Cancelling update from state: \" << state;\n            RemoveAllUpdateState(lock.get());\n            RemoveInvalidSnapshots(lock.get());\n            break;\n        case CancelResult::NEEDS_MERGE:\n            LOG(ERROR) << \"Cannot cancel an update while a merge is in progress.\";\n            break;\n        case CancelResult::LIVE_SNAPSHOTS:\n            LOG(ERROR) << \"Cannot cancel an update while snapshots are live.\";\n            break;\n        case CancelResult::ERROR:\n            // Error was already reported.\n            break;\n    }\n    return result;\n}\n\nbool SnapshotManager::IsCancelUpdateSafe() {\n    // This may be called in recovery, so ensure we have /metadata.\n    auto mount = EnsureMetadataMounted();\n    if (!mount || !mount->HasDevice()) {\n        return true;\n    }\n\n    auto lock = LockExclusive();\n    if (!lock) {\n        return false;\n    }\n\n    UpdateState state = ReadUpdateState(lock.get());\n    return IsCancelUpdateSafe(state) == CancelResult::OK;\n}\n\nCancelResult SnapshotManager::IsCancelUpdateSafe(UpdateState state) {\n    if (IsSnapshotWithoutSlotSwitch()) {\n        return CancelResult::LIVE_SNAPSHOTS;\n    }\n\n    switch (state) {\n        case UpdateState::Merging:\n        case UpdateState::MergeNeedsReboot:\n        case UpdateState::MergeFailed:\n            return CancelResult::NEEDS_MERGE;\n        case UpdateState::Unverified: {\n            // We completed an update, but it can still be canceled if we haven't booted into it.\n            auto slot = GetCurrentSlot();\n            if (slot == Slot::Target) {\n                return CancelResult::LIVE_SNAPSHOTS;\n            }\n            return CancelResult::OK;\n        }\n        case UpdateState::None:\n        case UpdateState::Initiated:\n        case UpdateState::Cancelled:\n            return CancelResult::OK;\n        default:\n            LOG(ERROR) << \"Unknown state: \" << state;\n            return CancelResult::ERROR;\n    }\n}\n\nstd::string SnapshotManager::ReadUpdateSourceSlotSuffix() {\n    auto boot_file = GetSnapshotBootIndicatorPath();\n    std::string contents;\n    if (!android::base::ReadFileToString(boot_file, &contents)) {\n        return {};\n    }\n    return contents;\n}\n\nSnapshotManager::Slot SnapshotManager::GetCurrentSlot() {\n    auto contents = ReadUpdateSourceSlotSuffix();\n    if (contents.empty()) {\n        return Slot::Unknown;\n    }\n    if (device_->GetSlotSuffix() == contents) {\n        return Slot::Source;\n    }\n    return Slot::Target;\n}\n\nstd::string SnapshotManager::GetSnapshotSlotSuffix() {\n    switch (GetCurrentSlot()) {\n        case Slot::Target:\n            return device_->GetSlotSuffix();\n        default:\n            return device_->GetOtherSlotSuffix();\n    }\n}\n\nstatic bool RemoveFileIfExists(const std::string& path) {\n    std::string message;\n    if (!android::base::RemoveFileIfExists(path, &message)) {\n        LOG(ERROR) << \"Remove failed: \" << path << \": \" << message;\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::RemoveAllUpdateState(LockedFile* lock, const std::function<bool()>& prolog) {\n    if (prolog && !prolog()) {\n        LOG(WARNING) << \"Can't RemoveAllUpdateState: prolog failed.\";\n        return false;\n    }\n\n    LOG(INFO) << \"Removing all update state.\";\n\n    if (ReadUpdateState(lock) != UpdateState::None) {\n        // Only call this if we're actually cancelling an update. It's not\n        // expected to yield anything otherwise, and firing up gsid on normal\n        // boot is expensive.\n        if (!RemoveAllSnapshots(lock)) {\n            LOG(ERROR) << \"Could not remove all snapshots\";\n            return false;\n        }\n    }\n\n    // It's okay if these fail:\n    // - For SnapshotBoot and Rollback, first-stage init performs a deeper check after\n    // reading the indicator file, so it's not a problem if it still exists\n    // after the update completes.\n    // - For ForwardMerge, FinishedSnapshotWrites asserts that the existence of the indicator\n    // matches the incoming update.\n    std::vector<std::string> files = {\n            GetSnapshotBootIndicatorPath(),          GetRollbackIndicatorPath(),\n            GetForwardMergeIndicatorPath(),          GetOldPartitionMetadataPath(),\n            GetBootSnapshotsWithoutSlotSwitchPath(), GetSnapuserdFromSystemPath(),\n    };\n    for (const auto& file : files) {\n        RemoveFileIfExists(file);\n    }\n\n    // If this fails, we'll keep trying to remove the update state (as the\n    // device reboots or starts a new update) until it finally succeeds.\n    return WriteUpdateState(lock, UpdateState::None);\n}\n\nbool SnapshotManager::FinishedSnapshotWrites(bool wipe) {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    auto update_state = ReadUpdateState(lock.get());\n    if (update_state == UpdateState::Unverified) {\n        LOG(INFO) << \"FinishedSnapshotWrites already called before. Ignored.\";\n        return true;\n    }\n\n    if (update_state != UpdateState::Initiated) {\n        LOG(ERROR) << \"Can only transition to the Unverified state from the Initiated state.\";\n        return false;\n    }\n\n    if (!EnsureNoOverflowSnapshot(lock.get())) {\n        LOG(ERROR) << \"Cannot ensure there are no overflow snapshots.\";\n        return false;\n    }\n\n    if (!UpdateForwardMergeIndicator(wipe)) {\n        return false;\n    }\n\n    // This file is written on boot to detect whether a rollback occurred. It\n    // MUST NOT exist before rebooting, otherwise, we're at risk of deleting\n    // snapshots too early.\n    if (!RemoveFileIfExists(GetRollbackIndicatorPath())) {\n        return false;\n    }\n\n    // This file acts as both a quick indicator for init (it can use access(2)\n    // to decide how to do first-stage mounts), and it stores the old slot, so\n    // we can tell whether or not we performed a rollback.\n    auto contents = device_->GetSlotSuffix();\n    auto boot_file = GetSnapshotBootIndicatorPath();\n    if (!WriteStringToFileAtomic(contents, boot_file)) {\n        PLOG(ERROR) << \"write failed: \" << boot_file;\n        return false;\n    }\n    return WriteUpdateState(lock.get(), UpdateState::Unverified);\n}\n\nbool SnapshotManager::CreateSnapshot(LockedFile* lock, PartitionCowCreator* cow_creator,\n                                     SnapshotStatus* status) {\n    CHECK(lock);\n    CHECK(lock->lock_mode() == LOCK_EX);\n    CHECK(status);\n\n    if (status->name().empty()) {\n        LOG(ERROR) << \"SnapshotStatus has no name.\";\n        return false;\n    }\n    // Check these sizes. Like liblp, we guarantee the partition size is\n    // respected, which means it has to be sector-aligned. (This guarantee is\n    // useful for locating avb footers correctly). The COW file size, however,\n    // can be arbitrarily larger than specified, so we can safely round it up.\n    if (status->device_size() % kSectorSize != 0) {\n        LOG(ERROR) << \"Snapshot \" << status->name()\n                   << \" device size is not a multiple of the sector size: \"\n                   << status->device_size();\n        return false;\n    }\n    if (status->snapshot_size() % kSectorSize != 0) {\n        LOG(ERROR) << \"Snapshot \" << status->name()\n                   << \" snapshot size is not a multiple of the sector size: \"\n                   << status->snapshot_size();\n        return false;\n    }\n    if (status->cow_partition_size() % kSectorSize != 0) {\n        LOG(ERROR) << \"Snapshot \" << status->name()\n                   << \" cow partition size is not a multiple of the sector size: \"\n                   << status->cow_partition_size();\n        return false;\n    }\n    if (status->cow_file_size() % kSectorSize != 0) {\n        LOG(ERROR) << \"Snapshot \" << status->name()\n                   << \" cow file size is not a multiple of the sector size: \"\n                   << status->cow_file_size();\n        return false;\n    }\n\n    status->set_state(SnapshotState::CREATED);\n    status->set_sectors_allocated(0);\n    status->set_metadata_sectors(0);\n    status->set_using_snapuserd(cow_creator->using_snapuserd);\n    status->set_compression_algorithm(cow_creator->compression_algorithm);\n    status->set_compression_factor(cow_creator->compression_factor);\n    status->set_read_ahead_size(cow_creator->read_ahead_size);\n    if (cow_creator->enable_threading) {\n        status->set_enable_threading(cow_creator->enable_threading);\n    }\n    if (cow_creator->batched_writes) {\n        status->set_batched_writes(cow_creator->batched_writes);\n    }\n\n    if (!WriteSnapshotStatus(lock, *status)) {\n        PLOG(ERROR) << \"Could not write snapshot status: \" << status->name();\n        return false;\n    }\n    return true;\n}\n\nReturn SnapshotManager::CreateCowImage(LockedFile* lock, const std::string& name) {\n    CHECK(lock);\n    CHECK(lock->lock_mode() == LOCK_EX);\n    if (!EnsureImageManager()) return Return::Error();\n\n    SnapshotStatus status;\n    if (!ReadSnapshotStatus(lock, name, &status)) {\n        return Return::Error();\n    }\n\n    // The COW file size should have been rounded up to the nearest sector in CreateSnapshot.\n    if (status.cow_file_size() % kSectorSize != 0) {\n        LOG(ERROR) << \"Snapshot \" << name << \" COW file size is not a multiple of the sector size: \"\n                   << status.cow_file_size();\n        return Return::Error();\n    }\n\n    std::string cow_image_name = GetCowImageDeviceName(name);\n    int cow_flags = IImageManager::CREATE_IMAGE_DEFAULT;\n    return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));\n}\n\nbool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,\n                                   const std::string& cow_file, const std::string& base_device,\n                                   const std::string& base_path_merge,\n                                   const std::chrono::milliseconds& timeout_ms, std::string* path) {\n    CHECK(lock);\n\n    if (UpdateUsesUserSnapshots(lock)) {\n        SnapshotStatus status;\n        if (!ReadSnapshotStatus(lock, name, &status)) {\n            LOG(ERROR) << \"MapDmUserCow: ReadSnapshotStatus failed...\";\n            return false;\n        }\n\n        if (status.state() == SnapshotState::NONE ||\n            status.state() == SnapshotState::MERGE_COMPLETED) {\n            LOG(ERROR) << \"Should not create a snapshot device for \" << name\n                       << \" after merging has completed.\";\n            return false;\n        }\n\n        SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n        if (update_status.state() == UpdateState::MergeCompleted ||\n            update_status.state() == UpdateState::MergeNeedsReboot) {\n            LOG(ERROR) << \"Should not create a snapshot device for \" << name\n                       << \" after global merging has completed.\";\n            return false;\n        }\n    }\n\n    // Use an extra decoration for first-stage init, so we can transition\n    // to a new table entry in second-stage.\n    std::string misc_name = name;\n    if (use_first_stage_snapuserd_) {\n        misc_name += \"-init\";\n    }\n\n    if (!EnsureSnapuserdConnected()) {\n        return false;\n    }\n\n    uint64_t base_sectors = 0;\n    if (!UpdateUsesUserSnapshots(lock)) {\n        base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device);\n        if (base_sectors == 0) {\n            LOG(ERROR) << \"Failed to retrieve base_sectors from Snapuserd\";\n            return false;\n        }\n    } else if (IsSnapshotWithoutSlotSwitch()) {\n        // When snapshots are on current slot, we determine the size\n        // of block device based on the number of COW operations. We cannot\n        // use base device as it will be from older image.\n        unique_fd fd(open(cow_file.c_str(), O_RDONLY | O_CLOEXEC));\n        if (fd < 0) {\n            PLOG(ERROR) << \"Failed to open \" << cow_file;\n            return false;\n        }\n\n        CowReader reader;\n        if (!reader.Parse(std::move(fd))) {\n            LOG(ERROR) << \"Failed to parse cow \" << cow_file;\n            return false;\n        }\n\n        uint64_t dev_sz = 0;\n        const auto& header = reader.GetHeader();\n        if (header.prefix.major_version == 2) {\n            const size_t num_ops = reader.get_num_total_data_ops();\n            dev_sz = (num_ops * header.block_size);\n        } else {\n            // create_snapshot will skip in-place copy ops. Hence, fetch this\n            // information directly from v3 header.\n            const auto& v3_header = reader.header_v3();\n            dev_sz = v3_header.op_count_max * v3_header.block_size;\n        }\n\n        base_sectors = dev_sz >> 9;\n    } else {\n        // For userspace snapshots, the size of the base device is taken as the\n        // size of the dm-user block device. Since there is no pseudo mapping\n        // created in the daemon, we no longer need to rely on the daemon for\n        // sizing the dm-user block device.\n        unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge.c_str(), O_RDONLY | O_CLOEXEC)));\n        if (fd < 0) {\n            LOG(ERROR) << \"Cannot open block device: \" << base_path_merge;\n            return false;\n        }\n\n        uint64_t dev_sz = get_block_device_size(fd.get());\n        if (!dev_sz) {\n            LOG(ERROR) << \"Failed to find block device size: \" << base_path_merge;\n            return false;\n        }\n\n        base_sectors = dev_sz >> 9;\n    }\n\n    DmTable table;\n    table.Emplace<DmTargetUser>(0, base_sectors, misc_name);\n    if (!dm_.CreateDevice(name, table, path, timeout_ms)) {\n        LOG(ERROR) << \" dm-user: CreateDevice failed... \";\n        return false;\n    }\n    if (!WaitForDevice(*path, timeout_ms)) {\n        LOG(ERROR) << \" dm-user: timeout: Failed to create block device for: \" << name;\n        return false;\n    }\n\n    auto control_device = \"/dev/dm-user/\" + misc_name;\n    if (!WaitForDevice(control_device, timeout_ms)) {\n        return false;\n    }\n\n    if (UpdateUsesUserSnapshots(lock)) {\n        // Now that the dm-user device is created, initialize the daemon and\n        // spin up the worker threads.\n        if (!snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device, base_path_merge)) {\n            LOG(ERROR) << \"InitDmUserCow failed\";\n            return false;\n        }\n    }\n\n    return snapuserd_client_->AttachDmUser(misc_name);\n}\n\nbool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,\n                                  const std::string& base_device, const std::string& cow_device,\n                                  const std::chrono::milliseconds& timeout_ms,\n                                  std::string* dev_path) {\n    CHECK(lock);\n\n    SnapshotStatus status;\n    if (!ReadSnapshotStatus(lock, name, &status)) {\n        return false;\n    }\n    if (status.state() == SnapshotState::NONE || status.state() == SnapshotState::MERGE_COMPLETED) {\n        LOG(ERROR) << \"Should not create a snapshot device for \" << name\n                   << \" after merging has completed.\";\n        return false;\n    }\n\n    // Validate the block device size, as well as the requested snapshot size.\n    // Note that during first-stage init, we don't have the device paths.\n    if (android::base::StartsWith(base_device, \"/\")) {\n        unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));\n        if (fd < 0) {\n            PLOG(ERROR) << \"open failed: \" << base_device;\n            return false;\n        }\n        auto dev_size = get_block_device_size(fd);\n        if (!dev_size) {\n            PLOG(ERROR) << \"Could not determine block device size: \" << base_device;\n            return false;\n        }\n        if (status.device_size() != dev_size) {\n            LOG(ERROR) << \"Block device size for \" << base_device << \" does not match\"\n                       << \"(expected \" << status.device_size() << \", got \" << dev_size << \")\";\n            return false;\n        }\n    }\n    if (status.device_size() % kSectorSize != 0) {\n        LOG(ERROR) << \"invalid blockdev size for \" << base_device << \": \" << status.device_size();\n        return false;\n    }\n    if (status.snapshot_size() % kSectorSize != 0 ||\n        status.snapshot_size() > status.device_size()) {\n        LOG(ERROR) << \"Invalid snapshot size for \" << base_device << \": \" << status.snapshot_size();\n        return false;\n    }\n    if (status.device_size() != status.snapshot_size()) {\n        LOG(ERROR) << \"Device size and snapshot size must be the same (device size = \"\n                   << status.device_size() << \", snapshot size = \" << status.snapshot_size();\n        return false;\n    }\n\n    uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;\n\n    // Note that merging is a global state. We do track whether individual devices\n    // have completed merging, but the start of the merge process is considered\n    // atomic.\n    SnapshotStorageMode mode;\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    switch (update_status.state()) {\n        case UpdateState::MergeCompleted:\n        case UpdateState::MergeNeedsReboot:\n            LOG(ERROR) << \"Should not create a snapshot device for \" << name\n                       << \" after global merging has completed.\";\n            return false;\n        case UpdateState::Merging:\n        case UpdateState::MergeFailed:\n            // Note: MergeFailed indicates that a merge is in progress, but\n            // is possibly stalled. We still have to honor the merge.\n            if (DecideMergePhase(status) == update_status.merge_phase()) {\n                mode = SnapshotStorageMode::Merge;\n            } else {\n                mode = SnapshotStorageMode::Persistent;\n            }\n            break;\n        default:\n            mode = SnapshotStorageMode::Persistent;\n            break;\n    }\n\n    if (mode == SnapshotStorageMode::Persistent && status.state() == SnapshotState::MERGING) {\n        LOG(ERROR) << \"Snapshot: \" << name\n                   << \" has snapshot status Merging but mode set to Persistent.\"\n                   << \" Changing mode to Snapshot-Merge.\";\n        mode = SnapshotStorageMode::Merge;\n    }\n\n    DmTable table;\n    table.Emplace<DmTargetSnapshot>(0, snapshot_sectors, base_device, cow_device, mode,\n                                    kSnapshotChunkSize);\n    if (!dm_.CreateDevice(name, table, dev_path, timeout_ms)) {\n        LOG(ERROR) << \"Could not create snapshot device: \" << name;\n        return false;\n    }\n    return true;\n}\n\nstd::optional<std::string> SnapshotManager::MapCowImage(\n        const std::string& name, const std::chrono::milliseconds& timeout_ms) {\n    if (!EnsureImageManager()) return std::nullopt;\n    auto cow_image_name = GetCowImageDeviceName(name);\n\n    bool ok;\n    std::string cow_dev;\n    if (device_->IsRecovery() || device_->IsFirstStageInit()) {\n        const auto& opener = device_->GetPartitionOpener();\n        ok = images_->MapImageWithDeviceMapper(opener, cow_image_name, &cow_dev);\n    } else {\n        ok = images_->MapImageDevice(cow_image_name, timeout_ms, &cow_dev);\n    }\n\n    if (ok) {\n        LOG(INFO) << \"Mapped \" << cow_image_name << \" to \" << cow_dev;\n        return cow_dev;\n    }\n    LOG(ERROR) << \"Could not map image device: \" << cow_image_name;\n    return std::nullopt;\n}\n\nbool SnapshotManager::MapSourceDevice(LockedFile* lock, const std::string& name,\n                                      const std::chrono::milliseconds& timeout_ms,\n                                      std::string* path) {\n    CHECK(lock);\n\n    auto metadata = ReadOldPartitionMetadata(lock);\n    if (!metadata) {\n        LOG(ERROR) << \"Could not map source device due to missing or corrupt metadata\";\n        return false;\n    }\n\n    auto old_name = GetOtherPartitionName(name);\n    auto slot_suffix = device_->GetSlotSuffix();\n    auto slot = SlotNumberForSlotSuffix(slot_suffix);\n\n    CreateLogicalPartitionParams params = {\n            .block_device = device_->GetSuperDevice(slot),\n            .metadata = metadata,\n            .partition_name = old_name,\n            .timeout_ms = timeout_ms,\n            .device_name = GetSourceDeviceName(name),\n            .partition_opener = &device_->GetPartitionOpener(),\n    };\n    if (!CreateLogicalPartition(std::move(params), path)) {\n        LOG(ERROR) << \"Could not create source device for snapshot \" << name;\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::UnmapSnapshot(LockedFile* lock, const std::string& name) {\n    CHECK(lock);\n\n    if (UpdateUsesUserSnapshots(lock)) {\n        if (!UnmapUserspaceSnapshotDevice(lock, name)) {\n            return false;\n        }\n    } else {\n        if (!DeleteDeviceIfExists(name)) {\n            LOG(ERROR) << \"Could not delete snapshot device: \" << name;\n            return false;\n        }\n    }\n    return true;\n}\n\nbool SnapshotManager::UnmapCowImage(const std::string& name) {\n    if (!EnsureImageManager()) return false;\n    return images_->UnmapImageIfExists(GetCowImageDeviceName(name));\n}\n\nbool SnapshotManager::DeleteSnapshot(LockedFile* lock, const std::string& name) {\n    CHECK(lock);\n    CHECK(lock->lock_mode() == LOCK_EX);\n    if (!EnsureImageManager()) return false;\n\n    if (!UnmapCowDevices(lock, name)) {\n        return false;\n    }\n\n    // We can't delete snapshots in recovery. The only way we'd try is it we're\n    // completing or canceling a merge in preparation for a data wipe, in which\n    // case, we don't care if the file sticks around.\n    if (device_->IsRecovery()) {\n        LOG(INFO) << \"Skipping delete of snapshot \" << name << \" in recovery.\";\n        return true;\n    }\n\n    auto cow_image_name = GetCowImageDeviceName(name);\n    if (images_->BackingImageExists(cow_image_name)) {\n        if (!images_->DeleteBackingImage(cow_image_name)) {\n            return false;\n        }\n    }\n\n    std::string error;\n    auto file_path = GetSnapshotStatusFilePath(name);\n    if (!android::base::RemoveFileIfExists(file_path, &error)) {\n        LOG(ERROR) << \"Failed to remove status file \" << file_path << \": \" << error;\n        return false;\n    }\n\n    // This path may never exist. If it is present, then it's a stale\n    // snapshot status file. Just remove the file and log the message.\n    const std::string tmp_path = file_path + \".tmp\";\n    if (!android::base::RemoveFileIfExists(tmp_path, &error)) {\n        LOG(ERROR) << \"Failed to remove stale snapshot file \" << tmp_path;\n    }\n\n    return true;\n}\n\nbool SnapshotManager::InitiateMerge() {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    UpdateState state = ReadUpdateState(lock.get());\n    if (state != UpdateState::Unverified) {\n        LOG(ERROR) << \"Cannot begin a merge if an update has not been verified\";\n        return false;\n    }\n\n    auto slot = GetCurrentSlot();\n    if (slot != Slot::Target) {\n        LOG(ERROR) << \"Device cannot merge while not booting from new slot\";\n        return false;\n    }\n\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock.get(), &snapshots)) {\n        LOG(ERROR) << \"Could not list snapshots\";\n        return false;\n    }\n\n    auto current_slot_suffix = device_->GetSlotSuffix();\n\n    for (const auto& snapshot : snapshots) {\n        if (!android::base::EndsWith(snapshot, current_slot_suffix)) {\n            // Allow the merge to continue, but log this unexpected case.\n            LOG(ERROR) << \"Unexpected snapshot found during merge: \" << snapshot;\n            continue;\n        }\n\n        // The device has to be mapped, since everything should be merged at\n        // the same time. This is a fairly serious error. We could forcefully\n        // map everything here, but it should have been mapped during first-\n        // stage init.\n        if (dm_.GetState(snapshot) == DmDeviceState::INVALID) {\n            LOG(ERROR) << \"Cannot begin merge; device \" << snapshot << \" is not mapped.\";\n            return false;\n        }\n    }\n\n    auto metadata = ReadCurrentMetadata();\n    for (auto it = snapshots.begin(); it != snapshots.end();) {\n        switch (GetMetadataPartitionState(*metadata, *it)) {\n            case MetadataPartitionState::Flashed:\n                LOG(WARNING) << \"Detected re-flashing for partition \" << *it\n                             << \". Skip merging it.\";\n                [[fallthrough]];\n            case MetadataPartitionState::None: {\n                LOG(WARNING) << \"Deleting snapshot for partition \" << *it;\n                if (!DeleteSnapshot(lock.get(), *it)) {\n                    LOG(WARNING) << \"Cannot delete snapshot for partition \" << *it\n                                 << \". Skip merging it anyways.\";\n                }\n                it = snapshots.erase(it);\n            } break;\n            case MetadataPartitionState::Updated: {\n                ++it;\n            } break;\n        }\n    }\n\n    bool using_snapuserd = false;\n\n    std::vector<std::string> first_merge_group;\n\n    DmTargetSnapshot::Status initial_target_values = {};\n    for (const auto& snapshot : snapshots) {\n        if (!UpdateUsesUserSnapshots(lock.get())) {\n            DmTargetSnapshot::Status current_status;\n            if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) {\n                return false;\n            }\n            initial_target_values.sectors_allocated += current_status.sectors_allocated;\n            initial_target_values.total_sectors += current_status.total_sectors;\n            initial_target_values.metadata_sectors += current_status.metadata_sectors;\n        }\n\n        SnapshotStatus snapshot_status;\n        if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {\n            return false;\n        }\n\n        using_snapuserd |= snapshot_status.using_snapuserd();\n        if (DecideMergePhase(snapshot_status) == MergePhase::FIRST_PHASE) {\n            first_merge_group.emplace_back(snapshot);\n        }\n    }\n\n    SnapshotUpdateStatus initial_status = ReadSnapshotUpdateStatus(lock.get());\n    initial_status.set_state(UpdateState::Merging);\n    initial_status.set_using_snapuserd(using_snapuserd);\n\n    if (!UpdateUsesUserSnapshots(lock.get())) {\n        initial_status.set_sectors_allocated(initial_target_values.sectors_allocated);\n        initial_status.set_total_sectors(initial_target_values.total_sectors);\n        initial_status.set_metadata_sectors(initial_target_values.metadata_sectors);\n    }\n\n    // If any partitions shrunk, we need to merge them before we merge any other\n    // partitions (see b/177935716). Otherwise, a merge from another partition\n    // may overwrite the source block of a copy operation.\n    const std::vector<std::string>* merge_group;\n    if (first_merge_group.empty()) {\n        merge_group = &snapshots;\n        initial_status.set_merge_phase(MergePhase::SECOND_PHASE);\n    } else {\n        merge_group = &first_merge_group;\n        initial_status.set_merge_phase(MergePhase::FIRST_PHASE);\n    }\n\n    // Point of no return - mark that we're starting a merge. From now on every\n    // eligible snapshot must be a merge target.\n    if (!WriteSnapshotUpdateStatus(lock.get(), initial_status)) {\n        return false;\n    }\n\n    auto reported_code = MergeFailureCode::Ok;\n    for (const auto& snapshot : *merge_group) {\n        // If this fails, we have no choice but to continue. Everything must\n        // be merged. This is not an ideal state to be in, but it is safe,\n        // because we the next boot will try again.\n        auto code = SwitchSnapshotToMerge(lock.get(), snapshot);\n        if (code != MergeFailureCode::Ok) {\n            LOG(ERROR) << \"Failed to switch snapshot to a merge target: \" << snapshot;\n            if (reported_code == MergeFailureCode::Ok) {\n                reported_code = code;\n            }\n        }\n    }\n\n    // If we couldn't switch everything to a merge target, pre-emptively mark\n    // this merge as failed. It will get acknowledged when WaitForMerge() is\n    // called.\n    if (reported_code != MergeFailureCode::Ok) {\n        WriteUpdateState(lock.get(), UpdateState::MergeFailed, reported_code);\n    }\n\n    // Return true no matter what, because a merge was initiated.\n    return true;\n}\n\nMergeFailureCode SnapshotManager::SwitchSnapshotToMerge(LockedFile* lock, const std::string& name) {\n    SnapshotStatus status;\n    if (!ReadSnapshotStatus(lock, name, &status)) {\n        return MergeFailureCode::ReadStatus;\n    }\n    if (status.state() != SnapshotState::CREATED) {\n        LOG(WARNING) << \"Snapshot \" << name\n                     << \" has unexpected state: \" << SnapshotState_Name(status.state());\n    }\n\n    if (UpdateUsesUserSnapshots(lock)) {\n        if (EnsureSnapuserdConnected()) {\n            // This is the point where we inform the daemon to initiate/resume\n            // the merge\n            if (!snapuserd_client_->InitiateMerge(name)) {\n                return MergeFailureCode::UnknownTable;\n            }\n        } else {\n            LOG(ERROR) << \"Failed to connect to snapuserd daemon to initiate merge\";\n            return MergeFailureCode::UnknownTable;\n        }\n    } else {\n        // After this, we return true because we technically did switch to a merge\n        // target. Everything else we do here is just informational.\n        if (auto code = RewriteSnapshotDeviceTable(name); code != MergeFailureCode::Ok) {\n            return code;\n        }\n    }\n\n    status.set_state(SnapshotState::MERGING);\n\n    if (!UpdateUsesUserSnapshots(lock)) {\n        DmTargetSnapshot::Status dm_status;\n        if (!QuerySnapshotStatus(name, nullptr, &dm_status)) {\n            LOG(ERROR) << \"Could not query merge status for snapshot: \" << name;\n        }\n        status.set_sectors_allocated(dm_status.sectors_allocated);\n        status.set_metadata_sectors(dm_status.metadata_sectors);\n    }\n\n    if (!WriteSnapshotStatus(lock, status)) {\n        LOG(ERROR) << \"Could not update status file for snapshot: \" << name;\n    }\n    return MergeFailureCode::Ok;\n}\n\nMergeFailureCode SnapshotManager::RewriteSnapshotDeviceTable(const std::string& name) {\n    std::vector<DeviceMapper::TargetInfo> old_targets;\n    if (!dm_.GetTableInfo(name, &old_targets)) {\n        LOG(ERROR) << \"Could not read snapshot device table: \" << name;\n        return MergeFailureCode::GetTableInfo;\n    }\n    if (old_targets.size() != 1 || DeviceMapper::GetTargetType(old_targets[0].spec) != \"snapshot\") {\n        LOG(ERROR) << \"Unexpected device-mapper table for snapshot: \" << name;\n        return MergeFailureCode::UnknownTable;\n    }\n\n    std::string base_device, cow_device;\n    if (!DmTargetSnapshot::GetDevicesFromParams(old_targets[0].data, &base_device, &cow_device)) {\n        LOG(ERROR) << \"Could not derive underlying devices for snapshot: \" << name;\n        return MergeFailureCode::GetTableParams;\n    }\n\n    DmTable table;\n    table.Emplace<DmTargetSnapshot>(0, old_targets[0].spec.length, base_device, cow_device,\n                                    SnapshotStorageMode::Merge, kSnapshotChunkSize);\n    if (!dm_.LoadTableAndActivate(name, table)) {\n        LOG(ERROR) << \"Could not swap device-mapper tables on snapshot device \" << name;\n        return MergeFailureCode::ActivateNewTable;\n    }\n    LOG(INFO) << \"Successfully switched snapshot device to a merge target: \" << name;\n    return MergeFailureCode::Ok;\n}\n\nbool SnapshotManager::GetSingleTarget(const std::string& dm_name, TableQuery query,\n                                      DeviceMapper::TargetInfo* target) {\n    if (dm_.GetState(dm_name) == DmDeviceState::INVALID) {\n        return false;\n    }\n\n    std::vector<DeviceMapper::TargetInfo> targets;\n    bool result;\n    if (query == TableQuery::Status) {\n        result = dm_.GetTableStatus(dm_name, &targets);\n    } else {\n        result = dm_.GetTableInfo(dm_name, &targets);\n    }\n    if (!result) {\n        LOG(ERROR) << \"Could not query device: \" << dm_name;\n        return false;\n    }\n    if (targets.size() != 1) {\n        return false;\n    }\n\n    *target = std::move(targets[0]);\n    return true;\n}\n\nbool SnapshotManager::IsSnapshotDevice(const std::string& dm_name, TargetInfo* target) {\n    DeviceMapper::TargetInfo snap_target;\n    if (!GetSingleTarget(dm_name, TableQuery::Status, &snap_target)) {\n        return false;\n    }\n    auto type = DeviceMapper::GetTargetType(snap_target.spec);\n\n    // If this is not a user-snapshot device then it should either\n    // be a dm-snapshot or dm-snapshot-merge target\n    if (type != \"user\") {\n        if (type != \"snapshot\" && type != \"snapshot-merge\") {\n            return false;\n        }\n    }\n\n    if (target) {\n        *target = std::move(snap_target);\n    }\n    return true;\n}\n\nauto SnapshotManager::UpdateStateToStr(const enum UpdateState state) {\n    switch (state) {\n        case None:\n            return \"None\";\n        case Initiated:\n            return \"Initiated\";\n        case Unverified:\n            return \"Unverified\";\n        case Merging:\n            return \"Merging\";\n        case MergeNeedsReboot:\n            return \"MergeNeedsReboot\";\n        case MergeCompleted:\n            return \"MergeCompleted\";\n        case MergeFailed:\n            return \"MergeFailed\";\n        case Cancelled:\n            return \"Cancelled\";\n        default:\n            return \"Unknown\";\n    }\n}\n\nbool SnapshotManager::QuerySnapshotStatus(const std::string& dm_name, std::string* target_type,\n                                          DmTargetSnapshot::Status* status) {\n    DeviceMapper::TargetInfo target;\n    if (!IsSnapshotDevice(dm_name, &target)) {\n        LOG(ERROR) << \"Device \" << dm_name << \" is not a snapshot or snapshot-merge device\";\n        return false;\n    }\n    if (!DmTargetSnapshot::ParseStatusText(target.data, status)) {\n        LOG(ERROR) << \"Could not parse snapshot status text: \" << dm_name;\n        return false;\n    }\n    if (target_type) {\n        *target_type = DeviceMapper::GetTargetType(target.spec);\n    }\n    if (!status->error.empty()) {\n        LOG(ERROR) << \"Snapshot: \" << dm_name << \" returned error code: \" << status->error;\n        return false;\n    }\n    return true;\n}\n\n// Note that when a merge fails, we will *always* try again to complete the\n// merge each time the device boots. There is no harm in doing so, and if\n// the problem was transient, we might manage to get a new outcome.\nUpdateState SnapshotManager::ProcessUpdateState(const std::function<bool()>& callback,\n                                                const std::function<bool()>& before_cancel) {\n    while (true) {\n        auto result = CheckMergeState(before_cancel);\n        LOG(INFO) << \"ProcessUpdateState handling state: \" << UpdateStateToStr(result.state);\n\n        if (result.state == UpdateState::MergeFailed) {\n            AcknowledgeMergeFailure(result.failure_code);\n        }\n\n        if (result.state == UpdateState::MergeCompleted) {\n            if (device_->IsTempMetadata()) {\n                CleanupScratchOtaMetadataIfPresent();\n            }\n        }\n\n        if (result.state != UpdateState::Merging) {\n            // Either there is no merge, or the merge was finished, so no need\n            // to keep waiting.\n            return result.state;\n        }\n\n        if (callback && !callback()) {\n            return result.state;\n        }\n\n        // This wait is not super time sensitive, so we have a relatively\n        // low polling frequency.\n        std::this_thread::sleep_for(kUpdateStateCheckInterval);\n    }\n}\n\nauto SnapshotManager::CheckMergeState(const std::function<bool()>& before_cancel) -> MergeResult {\n    auto lock = LockExclusive();\n    if (!lock) {\n        return MergeResult(UpdateState::MergeFailed, MergeFailureCode::AcquireLock);\n    }\n\n    auto result = CheckMergeState(lock.get(), before_cancel);\n    LOG(INFO) << \"CheckMergeState for snapshots returned: \" << UpdateStateToStr(result.state);\n\n    if (result.state == UpdateState::MergeCompleted) {\n        // Do this inside the same lock. Failures get acknowledged without the\n        // lock, because flock() might have failed.\n        AcknowledgeMergeSuccess(lock.get());\n    } else if (result.state == UpdateState::Cancelled) {\n        if (!device_->IsRecovery() && !RemoveAllUpdateState(lock.get(), before_cancel)) {\n            LOG(ERROR) << \"Failed to remove all update state after acknowleding cancelled update.\";\n        }\n    }\n    return result;\n}\n\nauto SnapshotManager::CheckMergeState(LockedFile* lock,\n                                      const std::function<bool()>& before_cancel) -> MergeResult {\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    switch (update_status.state()) {\n        case UpdateState::None:\n        case UpdateState::MergeCompleted:\n            // Harmless races are allowed between two callers of WaitForMerge,\n            // so in both of these cases we just propagate the state.\n            return MergeResult(update_status.state());\n\n        case UpdateState::Merging:\n        case UpdateState::MergeNeedsReboot:\n        case UpdateState::MergeFailed:\n            // We'll poll each snapshot below. Note that for the NeedsReboot\n            // case, we always poll once to give cleanup another opportunity to\n            // run.\n            break;\n\n        case UpdateState::Unverified:\n            // This is an edge case. Normally cancelled updates are detected\n            // via the merge poll below, but if we never started a merge, we\n            // need to also check here.\n            if (HandleCancelledUpdate(lock, before_cancel)) {\n                return MergeResult(UpdateState::Cancelled);\n            }\n            return MergeResult(update_status.state());\n\n        default:\n            return MergeResult(update_status.state());\n    }\n\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock, &snapshots)) {\n        return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ListSnapshots);\n    }\n\n    auto current_slot_suffix = device_->GetSlotSuffix();\n\n    bool cancelled = false;\n    bool merging = false;\n    bool needs_reboot = false;\n    bool wrong_phase = false;\n    MergeFailureCode failure_code = MergeFailureCode::Ok;\n    for (const auto& snapshot : snapshots) {\n        if (!android::base::EndsWith(snapshot, current_slot_suffix)) {\n            // This will have triggered an error message in InitiateMerge already.\n            LOG(ERROR) << \"Skipping merge validation of unexpected snapshot: \" << snapshot;\n            continue;\n        }\n\n        auto result = CheckTargetMergeState(lock, snapshot, update_status);\n        LOG(INFO) << \"CheckTargetMergeState for \" << snapshot\n                  << \" returned: \" << UpdateStateToStr(result.state);\n\n        switch (result.state) {\n            case UpdateState::MergeFailed:\n                // Take the first failure code in case other failures compound.\n                if (failure_code == MergeFailureCode::Ok) {\n                    failure_code = result.failure_code;\n                }\n                break;\n            case UpdateState::Merging:\n                merging = true;\n                break;\n            case UpdateState::MergeNeedsReboot:\n                needs_reboot = true;\n                break;\n            case UpdateState::MergeCompleted:\n                break;\n            case UpdateState::Cancelled:\n                cancelled = true;\n                break;\n            case UpdateState::None:\n                wrong_phase = true;\n                break;\n            default:\n                LOG(ERROR) << \"Unknown merge status for \\\"\" << snapshot << \"\\\": \" << \"\\\"\"\n                           << result.state << \"\\\"\";\n                if (failure_code == MergeFailureCode::Ok) {\n                    failure_code = MergeFailureCode::UnexpectedMergeState;\n                }\n                break;\n        }\n    }\n\n    if (merging) {\n        // Note that we handle \"Merging\" before we handle anything else. We\n        // want to poll until *nothing* is merging if we can, so everything has\n        // a chance to get marked as completed or failed.\n        return MergeResult(UpdateState::Merging);\n    }\n    if (failure_code != MergeFailureCode::Ok) {\n        // Note: since there are many drop-out cases for failure, we acknowledge\n        // it in WaitForMerge rather than here and elsewhere.\n        return MergeResult(UpdateState::MergeFailed, failure_code);\n    }\n    if (wrong_phase) {\n        // If we got here, no other partitions are being merged, and nothing\n        // failed to merge. It's safe to move to the next merge phase.\n        auto code = MergeSecondPhaseSnapshots(lock);\n        if (code != MergeFailureCode::Ok) {\n            return MergeResult(UpdateState::MergeFailed, code);\n        }\n        return MergeResult(UpdateState::Merging);\n    }\n    if (needs_reboot) {\n        WriteUpdateState(lock, UpdateState::MergeNeedsReboot);\n        return MergeResult(UpdateState::MergeNeedsReboot);\n    }\n    if (cancelled) {\n        // This is an edge case, that we handle as correctly as we sensibly can.\n        // The underlying partition has changed behind update_engine, and we've\n        // removed the snapshot as a result. The exact state of the update is\n        // undefined now, but this can only happen on an unlocked device where\n        // partitions can be flashed without wiping userdata.\n        return MergeResult(UpdateState::Cancelled);\n    }\n    return MergeResult(UpdateState::MergeCompleted);\n}\n\nauto SnapshotManager::CheckTargetMergeState(LockedFile* lock, const std::string& name,\n                                            const SnapshotUpdateStatus& update_status)\n        -> MergeResult {\n    SnapshotStatus snapshot_status;\n    if (!ReadSnapshotStatus(lock, name, &snapshot_status)) {\n        return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ReadStatus);\n    }\n\n    std::unique_ptr<LpMetadata> current_metadata;\n\n    if (!IsSnapshotDevice(name)) {\n        if (!current_metadata) {\n            current_metadata = ReadCurrentMetadata();\n        }\n\n        if (!current_metadata ||\n            GetMetadataPartitionState(*current_metadata, name) != MetadataPartitionState::Updated) {\n            DeleteSnapshot(lock, name);\n            return MergeResult(UpdateState::Cancelled);\n        }\n\n        // During a check, we decided the merge was complete, but we were unable to\n        // collapse the device-mapper stack and perform COW cleanup. If we haven't\n        // rebooted after this check, the device will still be a snapshot-merge\n        // target. If we have rebooted, the device will now be a linear target,\n        // and we can try cleanup again.\n        if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {\n            // NB: It's okay if this fails now, we gave cleanup our best effort.\n            OnSnapshotMergeComplete(lock, name, snapshot_status);\n            return MergeResult(UpdateState::MergeCompleted);\n        }\n\n        LOG(ERROR) << \"Expected snapshot or snapshot-merge for device: \" << name;\n        return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);\n    }\n\n    // This check is expensive so it is only enabled for debugging.\n    DCHECK((current_metadata = ReadCurrentMetadata()) &&\n           GetMetadataPartitionState(*current_metadata, name) == MetadataPartitionState::Updated);\n\n    if (UpdateUsesUserSnapshots(lock)) {\n        if (!EnsureSnapuserdConnected()) {\n            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);\n        }\n\n        // Query the snapshot status from the daemon\n        const auto merge_status = snapuserd_client_->QuerySnapshotStatus(name);\n        if (merge_status == \"snapshot-merge-failed\") {\n            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);\n        }\n\n        // This is the case when device reboots during merge. Once the device boots,\n        // snapuserd daemon will not resume merge immediately in first stage init.\n        // This is slightly different as compared to dm-snapshot-merge; In this\n        // case, metadata file will have \"MERGING\" state whereas the daemon will be\n        // waiting to resume the merge. Thus, we resume the merge at this point.\n        if (merge_status == \"snapshot\" && snapshot_status.state() == SnapshotState::MERGING) {\n            if (!snapuserd_client_->InitiateMerge(name)) {\n                return MergeResult(UpdateState::MergeFailed, MergeFailureCode::UnknownTargetType);\n            }\n            return MergeResult(UpdateState::Merging);\n        }\n\n        if (merge_status == \"snapshot\" &&\n            DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE) {\n            if (update_status.merge_phase() == MergePhase::FIRST_PHASE) {\n                // The snapshot is not being merged because it's in the wrong phase.\n                return MergeResult(UpdateState::None);\n            } else {\n                // update_status is already in second phase but the\n                // snapshot_status is still not set to SnapshotState::MERGING.\n                //\n                // Resume the merge at this point. see b/374225913\n                LOG(INFO) << \"SwitchSnapshotToMerge: \" << name << \" after resuming merge\";\n                auto code = SwitchSnapshotToMerge(lock, name);\n                if (code != MergeFailureCode::Ok) {\n                    LOG(ERROR) << \"Failed to switch snapshot: \" << name\n                               << \" to merge during second phase\";\n                    return MergeResult(UpdateState::MergeFailed,\n                                       MergeFailureCode::UnknownTargetType);\n                }\n                return MergeResult(UpdateState::Merging);\n            }\n        }\n\n        if (merge_status == \"snapshot-merge\") {\n            if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {\n                LOG(ERROR) << \"Snapshot \" << name\n                           << \" is merging after being marked merge-complete.\";\n                return MergeResult(UpdateState::MergeFailed,\n                                   MergeFailureCode::UnmergedSectorsAfterCompletion);\n            }\n            return MergeResult(UpdateState::Merging);\n        }\n\n        if (merge_status != \"snapshot-merge-complete\") {\n            LOG(ERROR) << \"Snapshot \" << name << \" has incorrect status: \" << merge_status;\n            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ExpectedMergeTarget);\n        }\n    } else {\n        // dm-snapshot in the kernel\n        std::string target_type;\n        DmTargetSnapshot::Status status;\n        if (!QuerySnapshotStatus(name, &target_type, &status)) {\n            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::QuerySnapshotStatus);\n        }\n        if (target_type == \"snapshot\" &&\n            DecideMergePhase(snapshot_status) == MergePhase::SECOND_PHASE &&\n            update_status.merge_phase() == MergePhase::FIRST_PHASE) {\n            // The snapshot is not being merged because it's in the wrong phase.\n            return MergeResult(UpdateState::None);\n        }\n        if (target_type != \"snapshot-merge\") {\n            // We can get here if we failed to rewrite the target type in\n            // InitiateMerge(). If we failed to create the target in first-stage\n            // init, boot would not succeed.\n            LOG(ERROR) << \"Snapshot \" << name << \" has incorrect target type: \" << target_type;\n            return MergeResult(UpdateState::MergeFailed, MergeFailureCode::ExpectedMergeTarget);\n        }\n\n        // These two values are equal when merging is complete.\n        if (status.sectors_allocated != status.metadata_sectors) {\n            if (snapshot_status.state() == SnapshotState::MERGE_COMPLETED) {\n                LOG(ERROR) << \"Snapshot \" << name\n                           << \" is merging after being marked merge-complete.\";\n                return MergeResult(UpdateState::MergeFailed,\n                                   MergeFailureCode::UnmergedSectorsAfterCompletion);\n            }\n            return MergeResult(UpdateState::Merging);\n        }\n    }\n\n    // Merging is done. First, update the status file to indicate the merge\n    // is complete. We do this before calling OnSnapshotMergeComplete, even\n    // though this means the write is potentially wasted work (since in the\n    // ideal case we'll immediately delete the file).\n    //\n    // This makes it simpler to reason about the next reboot: no matter what\n    // part of cleanup failed, first-stage init won't try to create another\n    // snapshot device for this partition.\n    snapshot_status.set_state(SnapshotState::MERGE_COMPLETED);\n    if (!WriteSnapshotStatus(lock, snapshot_status)) {\n        return MergeResult(UpdateState::MergeFailed, MergeFailureCode::WriteStatus);\n    }\n    if (!OnSnapshotMergeComplete(lock, name, snapshot_status)) {\n        return MergeResult(UpdateState::MergeNeedsReboot);\n    }\n    return MergeResult(UpdateState::MergeCompleted, MergeFailureCode::Ok);\n}\n\n// This returns the backing device, not the dm-user layer.\nstatic std::string GetMappedCowDeviceName(const std::string& snapshot,\n                                          const SnapshotStatus& status) {\n    // If no partition was created (the COW exists entirely on /data), the\n    // device-mapper layering is different than if we had a partition.\n    if (status.cow_partition_size() == 0) {\n        return GetCowImageDeviceName(snapshot);\n    }\n    return GetCowName(snapshot);\n}\n\nMergeFailureCode SnapshotManager::MergeSecondPhaseSnapshots(LockedFile* lock) {\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock, &snapshots)) {\n        return MergeFailureCode::ListSnapshots;\n    }\n\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    CHECK(update_status.state() == UpdateState::Merging ||\n          update_status.state() == UpdateState::MergeFailed);\n    CHECK(update_status.merge_phase() == MergePhase::FIRST_PHASE);\n\n    update_status.set_state(UpdateState::Merging);\n    update_status.set_merge_phase(MergePhase::SECOND_PHASE);\n    if (!WriteSnapshotUpdateStatus(lock, update_status)) {\n        return MergeFailureCode::WriteStatus;\n    }\n\n    auto current_slot_suffix = device_->GetSlotSuffix();\n    MergeFailureCode result = MergeFailureCode::Ok;\n    for (const auto& snapshot : snapshots) {\n        if (!android::base::EndsWith(snapshot, current_slot_suffix)) {\n            LOG(ERROR) << \"Skipping invalid snapshot: \" << snapshot\n                       << \" during MergeSecondPhaseSnapshots\";\n            continue;\n        }\n        SnapshotStatus snapshot_status;\n        if (!ReadSnapshotStatus(lock, snapshot, &snapshot_status)) {\n            return MergeFailureCode::ReadStatus;\n        }\n        if (DecideMergePhase(snapshot_status) != MergePhase::SECOND_PHASE) {\n            continue;\n        }\n        auto code = SwitchSnapshotToMerge(lock, snapshot);\n        if (code != MergeFailureCode::Ok) {\n            LOG(ERROR) << \"Failed to switch snapshot to a second-phase merge target: \" << snapshot;\n            if (result == MergeFailureCode::Ok) {\n                result = code;\n            }\n        }\n    }\n    return result;\n}\n\nstd::string SnapshotManager::GetBootSnapshotsWithoutSlotSwitchPath() {\n    return metadata_dir_ + \"/\" + android::base::Basename(kBootSnapshotsWithoutSlotSwitch);\n}\n\nstd::string SnapshotManager::GetSnapshotBootIndicatorPath() {\n    return metadata_dir_ + \"/\" + android::base::Basename(kBootIndicatorPath);\n}\n\nstd::string SnapshotManager::GetRollbackIndicatorPath() {\n    return metadata_dir_ + \"/\" + android::base::Basename(kRollbackIndicatorPath);\n}\n\nstd::string SnapshotManager::GetSnapuserdFromSystemPath() {\n    return metadata_dir_ + \"/\" + android::base::Basename(kSnapuserdFromSystem);\n}\n\nstd::string SnapshotManager::GetForwardMergeIndicatorPath() {\n    return metadata_dir_ + \"/allow-forward-merge\";\n}\n\nstd::string SnapshotManager::GetOldPartitionMetadataPath() {\n    return metadata_dir_ + \"/old-partition-metadata\";\n}\n\nvoid SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {\n    // It's not possible to remove update state in recovery, so write an\n    // indicator that cleanup is needed on reboot. If a factory data reset\n    // was requested, it doesn't matter, everything will get wiped anyway.\n    // To make testing easier we consider a /data wipe as cleaned up.\n    if (device_->IsRecovery()) {\n        WriteUpdateState(lock, UpdateState::MergeCompleted);\n        return;\n    }\n\n    RemoveAllUpdateState(lock);\n\n    if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {\n        if (snapuserd_client_) {\n            snapuserd_client_->DetachSnapuserd();\n            snapuserd_client_->RemoveTransitionedDaemonIndicator();\n            snapuserd_client_ = nullptr;\n        }\n    }\n}\n\nvoid SnapshotManager::AcknowledgeMergeFailure(MergeFailureCode failure_code) {\n    // Log first, so worst case, we always have a record of why the calls below\n    // were being made.\n    LOG(ERROR) << \"Merge could not be completed and will be marked as failed.\";\n\n    auto lock = LockExclusive();\n    if (!lock) return;\n\n    // Since we released the lock in between WaitForMerge and here, it's\n    // possible (1) the merge successfully completed or (2) was already\n    // marked as a failure. So make sure to check the state again, and\n    // only mark as a failure if appropriate.\n    UpdateState state = ReadUpdateState(lock.get());\n    if (state != UpdateState::Merging && state != UpdateState::MergeNeedsReboot) {\n        return;\n    }\n\n    WriteUpdateState(lock.get(), UpdateState::MergeFailed, failure_code);\n}\n\nbool SnapshotManager::OnSnapshotMergeComplete(LockedFile* lock, const std::string& name,\n                                              const SnapshotStatus& status) {\n    if (!UpdateUsesUserSnapshots(lock)) {\n        if (IsSnapshotDevice(name)) {\n            // We are extra-cautious here, to avoid deleting the wrong table.\n            std::string target_type;\n            DmTargetSnapshot::Status dm_status;\n            if (!QuerySnapshotStatus(name, &target_type, &dm_status)) {\n                return false;\n            }\n            if (target_type != \"snapshot-merge\") {\n                LOG(ERROR) << \"Unexpected target type \" << target_type\n                           << \" for snapshot device: \" << name;\n                return false;\n            }\n            if (dm_status.sectors_allocated != dm_status.metadata_sectors) {\n                LOG(ERROR) << \"Merge is unexpectedly incomplete for device \" << name;\n                return false;\n            }\n            if (!CollapseSnapshotDevice(lock, name, status)) {\n                LOG(ERROR) << \"Unable to collapse snapshot: \" << name;\n                return false;\n            }\n        }\n    } else {\n        // Just collapse the device - no need to query again as we just did\n        // prior to calling this function\n        if (!CollapseSnapshotDevice(lock, name, status)) {\n            LOG(ERROR) << \"Unable to collapse snapshot: \" << name;\n            return false;\n        }\n    }\n\n    // Note that collapsing is implicitly an Unmap, so we don't need to\n    // unmap the snapshot.\n\n    if (!DeleteSnapshot(lock, name)) {\n        LOG(ERROR) << \"Could not delete snapshot: \" << name;\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::CollapseSnapshotDevice(LockedFile* lock, const std::string& name,\n                                             const SnapshotStatus& status) {\n    if (!UpdateUsesUserSnapshots(lock)) {\n        // Verify we have a snapshot-merge device.\n        DeviceMapper::TargetInfo target;\n        if (!GetSingleTarget(name, TableQuery::Table, &target)) {\n            return false;\n        }\n        if (DeviceMapper::GetTargetType(target.spec) != \"snapshot-merge\") {\n            // This should be impossible, it was checked earlier.\n            LOG(ERROR) << \"Snapshot device has invalid target type: \" << name;\n            return false;\n        }\n\n        std::string base_device, cow_device;\n        if (!DmTargetSnapshot::GetDevicesFromParams(target.data, &base_device, &cow_device)) {\n            LOG(ERROR) << \"Could not parse snapshot device \" << name\n                       << \" parameters: \" << target.data;\n            return false;\n        }\n    }\n\n    uint64_t snapshot_sectors = status.snapshot_size() / kSectorSize;\n    if (snapshot_sectors * kSectorSize != status.snapshot_size()) {\n        LOG(ERROR) << \"Snapshot \" << name\n                   << \" size is not sector aligned: \" << status.snapshot_size();\n        return false;\n    }\n\n    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());\n    // Create a DmTable that is identical to the base device.\n    CreateLogicalPartitionParams base_device_params{\n            .block_device = device_->GetSuperDevice(slot),\n            .metadata_slot = slot,\n            .partition_name = name,\n            .partition_opener = &device_->GetPartitionOpener(),\n    };\n    DmTable table;\n    if (!CreateDmTable(base_device_params, &table)) {\n        LOG(ERROR) << \"Could not create a DmTable for partition: \" << name;\n        return false;\n    }\n\n    if (!dm_.LoadTableAndActivate(name, table)) {\n        return false;\n    }\n\n    if (!UpdateUsesUserSnapshots(lock)) {\n        // Attempt to delete the snapshot device if one still exists. Nothing\n        // should be depending on the device, and device-mapper should have\n        // flushed remaining I/O. We could in theory replace with dm-zero (or\n        // re-use the table above), but for now it's better to know why this\n        // would fail.\n        //\n        // Furthermore, we should not be trying to unmap for userspace snapshot\n        // as unmap will fail since dm-user itself was a snapshot device prior\n        // to switching of tables. Unmap will fail as the device will be mounted\n        // by system partitions\n        if (status.using_snapuserd()) {\n            auto dm_user_name = GetDmUserCowName(name, GetSnapshotDriver(lock));\n            UnmapDmUserDevice(dm_user_name);\n        }\n    }\n\n    // We can't delete base device immediately as daemon holds a reference.\n    // Make sure we wait for all the worker threads to terminate and release\n    // the reference\n    if (UpdateUsesUserSnapshots(lock) && EnsureSnapuserdConnected()) {\n        if (!snapuserd_client_->WaitForDeviceDelete(name)) {\n            LOG(ERROR) << \"Failed to wait for \" << name << \" control device to delete\";\n        }\n    }\n\n    auto base_name = GetBaseDeviceName(name);\n    if (!DeleteDeviceIfExists(base_name)) {\n        LOG(ERROR) << \"Unable to delete base device for snapshot: \" << base_name;\n    }\n\n    if (!DeleteDeviceIfExists(GetSourceDeviceName(name), 4000ms)) {\n        LOG(ERROR) << \"Unable to delete source device for snapshot: \" << GetSourceDeviceName(name);\n    }\n\n    return true;\n}\n\nbool SnapshotManager::HandleCancelledUpdate(LockedFile* lock,\n                                            const std::function<bool()>& before_cancel) {\n    auto slot = GetCurrentSlot();\n    if (slot == Slot::Unknown) {\n        return false;\n    }\n\n    // If all snapshots were reflashed, then cancel the entire update.\n    if (AreAllSnapshotsCancelled(lock)) {\n        LOG(WARNING) << \"Detected re-flashing, cancelling unverified update.\";\n        return RemoveAllUpdateState(lock, before_cancel);\n    }\n\n    // If update has been rolled back, then cancel the entire update.\n    // Client (update_engine) is responsible for doing additional cleanup work on its own states\n    // when ProcessUpdateState() returns UpdateState::Cancelled.\n    auto current_slot = GetCurrentSlot();\n    if (current_slot != Slot::Source) {\n        LOG(INFO) << \"Update state is being processed while booting at \" << current_slot\n                  << \" slot, taking no action.\";\n        return false;\n    }\n\n    // current_slot == Source. Attempt to detect rollbacks.\n    if (access(GetRollbackIndicatorPath().c_str(), F_OK) != 0) {\n        // This unverified update is not attempted. Take no action.\n        PLOG(INFO) << \"Rollback indicator not detected. \"\n                   << \"Update state is being processed before reboot, taking no action.\";\n        return false;\n    }\n\n    LOG(WARNING) << \"Detected rollback, cancelling unverified update.\";\n    return RemoveAllUpdateState(lock, before_cancel);\n}\n\nbool SnapshotManager::PerformInitTransition(InitTransition transition,\n                                            std::vector<std::string>* snapuserd_argv) {\n    LOG(INFO) << \"Performing transition for snapuserd.\";\n\n    // Don't use EnsureSnapuserdConnected() because this is called from init,\n    // and attempting to do so will deadlock.\n    if (!snapuserd_client_ && transition != InitTransition::SELINUX_DETACH) {\n        snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);\n        if (!snapuserd_client_) {\n            LOG(ERROR) << \"Unable to connect to snapuserd\";\n            return false;\n        }\n    }\n\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock.get(), &snapshots)) {\n        LOG(ERROR) << \"Failed to list snapshots.\";\n        return false;\n    }\n\n    if (UpdateUsesUserSnapshots(lock.get()) && transition == InitTransition::SELINUX_DETACH) {\n        snapuserd_argv->emplace_back(\"-user_snapshot\");\n        if (UpdateUsesIouring(lock.get())) {\n            snapuserd_argv->emplace_back(\"-io_uring\");\n        }\n        if (UpdateUsesODirect(lock.get())) {\n            snapuserd_argv->emplace_back(\"-o_direct\");\n        }\n        uint cow_op_merge_size = GetUpdateCowOpMergeSize(lock.get());\n        if (cow_op_merge_size != 0) {\n            snapuserd_argv->emplace_back(\"-cow_op_merge_size=\" + std::to_string(cow_op_merge_size));\n        }\n        uint32_t worker_count = GetUpdateWorkerCount(lock.get());\n        if (worker_count != 0) {\n            snapuserd_argv->emplace_back(\"-worker_count=\" + std::to_string(worker_count));\n        }\n    }\n\n    size_t num_cows = 0;\n    size_t ok_cows = 0;\n    for (const auto& snapshot : snapshots) {\n        std::string user_cow_name = GetDmUserCowName(snapshot, GetSnapshotDriver(lock.get()));\n\n        if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {\n            continue;\n        }\n\n        DeviceMapper::TargetInfo target;\n        if (!GetSingleTarget(user_cow_name, TableQuery::Table, &target)) {\n            continue;\n        }\n\n        auto target_type = DeviceMapper::GetTargetType(target.spec);\n        if (target_type != \"user\") {\n            LOG(ERROR) << \"Unexpected target type for \" << user_cow_name << \": \" << target_type;\n            continue;\n        }\n\n        num_cows++;\n\n        SnapshotStatus snapshot_status;\n        if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {\n            LOG(ERROR) << \"Unable to read snapshot status: \" << snapshot;\n            continue;\n        }\n\n        auto misc_name = user_cow_name;\n\n        std::string source_device_name;\n        if (snapshot_status.old_partition_size() > 0) {\n            source_device_name = GetSourceDeviceName(snapshot);\n        } else {\n            source_device_name = GetBaseDeviceName(snapshot);\n        }\n\n        std::string source_device;\n        if (!dm_.GetDmDevicePathByName(source_device_name, &source_device)) {\n            LOG(ERROR) << \"Could not get device path for \" << GetSourceDeviceName(snapshot);\n            continue;\n        }\n\n        std::string base_path_merge;\n        if (!dm_.GetDmDevicePathByName(GetBaseDeviceName(snapshot), &base_path_merge)) {\n            LOG(ERROR) << \"Could not get device path for \" << GetSourceDeviceName(snapshot);\n            continue;\n        }\n\n        std::string cow_image_name = GetMappedCowDeviceName(snapshot, snapshot_status);\n\n        std::string cow_image_device;\n        if (!dm_.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {\n            LOG(ERROR) << \"Could not get device path for \" << cow_image_name;\n            continue;\n        }\n\n        if (transition == InitTransition::SELINUX_DETACH) {\n            if (!UpdateUsesUserSnapshots(lock.get())) {\n                auto message = misc_name + \",\" + cow_image_device + \",\" + source_device;\n                snapuserd_argv->emplace_back(std::move(message));\n            } else {\n                auto message = misc_name + \",\" + cow_image_device + \",\" + source_device + \",\" +\n                               base_path_merge;\n                snapuserd_argv->emplace_back(std::move(message));\n            }\n            SetReadAheadSize(cow_image_device, snapshot_status.read_ahead_size());\n            SetReadAheadSize(source_device, snapshot_status.read_ahead_size());\n\n            // Do not attempt to connect to the new snapuserd yet, it hasn't\n            // been started. We do however want to wait for the misc device\n            // to have been created.\n            ok_cows++;\n            continue;\n        }\n\n        DmTable table;\n        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);\n        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {\n            LOG(ERROR) << \"Unable to swap tables for \" << misc_name;\n            continue;\n        }\n\n        // Wait for ueventd to acknowledge and create the control device node.\n        std::string control_device = \"/dev/dm-user/\" + misc_name;\n        if (!WaitForDevice(control_device, 10s)) {\n            LOG(ERROR) << \"dm-user control device no found:  \" << misc_name;\n            continue;\n        }\n\n        uint64_t base_sectors;\n        if (!UpdateUsesUserSnapshots(lock.get())) {\n            base_sectors =\n                    snapuserd_client_->InitDmUserCow(misc_name, cow_image_device, source_device);\n        } else {\n            base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_image_device,\n                                                            source_device, base_path_merge);\n        }\n\n        if (base_sectors == 0) {\n            // Unrecoverable as metadata reads from cow device failed\n            LOG(FATAL) << \"Failed to retrieve base_sectors from Snapuserd\";\n            return false;\n        }\n\n        CHECK(base_sectors <= target.spec.length);\n\n        if (!snapuserd_client_->AttachDmUser(misc_name)) {\n            // This error is unrecoverable. We cannot proceed because reads to\n            // the underlying device will fail.\n            LOG(FATAL) << \"Could not initialize snapuserd for \" << user_cow_name;\n            return false;\n        }\n\n        ok_cows++;\n    }\n\n    if (ok_cows != num_cows) {\n        LOG(ERROR) << \"Could not transition all snapuserd consumers.\";\n        return false;\n    }\n    return true;\n}\n\nstd::unique_ptr<LpMetadata> SnapshotManager::ReadCurrentMetadata() {\n    const auto& opener = device_->GetPartitionOpener();\n    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());\n    auto super_device = device_->GetSuperDevice(slot);\n    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);\n    if (!metadata) {\n        LOG(ERROR) << \"Could not read dynamic partition metadata for device: \" << super_device;\n        return nullptr;\n    }\n    return metadata;\n}\n\nSnapshotManager::MetadataPartitionState SnapshotManager::GetMetadataPartitionState(\n        const LpMetadata& metadata, const std::string& name) {\n    auto partition = android::fs_mgr::FindPartition(metadata, name);\n    if (!partition) return MetadataPartitionState::None;\n    if (partition->attributes & LP_PARTITION_ATTR_UPDATED) {\n        return MetadataPartitionState::Updated;\n    }\n    return MetadataPartitionState::Flashed;\n}\n\nbool SnapshotManager::AreAllSnapshotsCancelled(LockedFile* lock) {\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock, &snapshots)) {\n        LOG(WARNING) << \"Failed to list snapshots to determine whether device has been flashed \"\n                     << \"after applying an update. Assuming no snapshots.\";\n        // Let HandleCancelledUpdate resets UpdateState.\n        return true;\n    }\n\n    std::map<std::string, bool> flashing_status;\n\n    if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {\n        LOG(WARNING) << \"Failed to determine whether partitions have been flashed. Not\"\n                     << \"removing update states.\";\n        return false;\n    }\n\n    bool all_snapshots_cancelled = std::all_of(flashing_status.begin(), flashing_status.end(),\n                                               [](const auto& pair) { return pair.second; });\n\n    if (all_snapshots_cancelled) {\n        LOG(WARNING) << \"All partitions are re-flashed after update, removing all update states.\";\n    }\n    return all_snapshots_cancelled;\n}\n\nbool SnapshotManager::GetSnapshotFlashingStatus(LockedFile* lock,\n                                                const std::vector<std::string>& snapshots,\n                                                std::map<std::string, bool>* out) {\n    CHECK(lock);\n\n    auto source_slot_suffix = ReadUpdateSourceSlotSuffix();\n    if (source_slot_suffix.empty()) {\n        return false;\n    }\n    uint32_t source_slot = SlotNumberForSlotSuffix(source_slot_suffix);\n    uint32_t target_slot = (source_slot == 0) ? 1 : 0;\n\n    // Attempt to detect re-flashing on each partition.\n    // - If all partitions are re-flashed, we can proceed to cancel the whole update.\n    // - If only some of the partitions are re-flashed, snapshots for re-flashed partitions are\n    //   deleted. Caller is responsible for merging the rest of the snapshots.\n    // - If none of the partitions are re-flashed, caller is responsible for merging the snapshots.\n    //\n    // Note that we use target slot metadata, since if an OTA has been applied\n    // to the target slot, we can detect the UPDATED flag. Any kind of flash\n    // operation against dynamic partitions ensures that all copies of the\n    // metadata are in sync, so flashing all partitions on the source slot will\n    // remove the UPDATED flag on the target slot as well.\n    const auto& opener = device_->GetPartitionOpener();\n    auto super_device = device_->GetSuperDevice(target_slot);\n    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, target_slot);\n    if (!metadata) {\n        return false;\n    }\n\n    for (const auto& snapshot_name : snapshots) {\n        if (GetMetadataPartitionState(*metadata, snapshot_name) ==\n            MetadataPartitionState::Updated) {\n            out->emplace(snapshot_name, false);\n        } else {\n            // Delete snapshots for partitions that are re-flashed after the update.\n            LOG(WARNING) << \"Detected re-flashing of partition \" << snapshot_name << \".\";\n            out->emplace(snapshot_name, true);\n        }\n    }\n    return true;\n}\n\nvoid SnapshotManager::RemoveInvalidSnapshots(LockedFile* lock) {\n    std::vector<std::string> snapshots;\n\n    // Remove the stale snapshot metadata\n    //\n    // We make sure that all the three cases\n    // are valid before removing the snapshot metadata:\n    //\n    // 1: dm state is active\n    // 2: Root fs is not mounted off as a snapshot device\n    // 3: Snapshot slot suffix should match current device slot\n    if (!ListSnapshots(lock, &snapshots, device_->GetSlotSuffix()) || snapshots.empty()) {\n        return;\n    }\n\n    // We indeed have some invalid snapshots\n    for (const auto& name : snapshots) {\n        if (dm_.GetState(name) == DmDeviceState::ACTIVE && !IsSnapshotDevice(name)) {\n            if (!DeleteSnapshot(lock, name)) {\n                LOG(ERROR) << \"Failed to delete invalid snapshot: \" << name;\n            } else {\n                LOG(INFO) << \"Invalid snapshot: \" << name << \" deleted\";\n            }\n        }\n    }\n}\n\nbool SnapshotManager::RemoveAllSnapshots(LockedFile* lock) {\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock, &snapshots)) {\n        LOG(ERROR) << \"Could not list snapshots\";\n        return false;\n    }\n\n    std::map<std::string, bool> flashing_status;\n    if (!GetSnapshotFlashingStatus(lock, snapshots, &flashing_status)) {\n        LOG(WARNING) << \"Failed to get flashing status\";\n    }\n\n    auto current_slot = GetCurrentSlot();\n    bool ok = true;\n    bool has_mapped_cow_images = false;\n    for (const auto& name : snapshots) {\n        // If booting off source slot, it is okay to unmap and delete all the snapshots.\n        // If boot indicator is missing, update state is None or Initiated, so\n        //   it is also okay to unmap and delete all the snapshots.\n        // If booting off target slot,\n        //  - should not unmap because:\n        //    - In Android mode, snapshots are not mapped, but\n        //      filesystems are mounting off dm-linear targets directly.\n        //    - In recovery mode, assume nothing is mapped, so it is optional to unmap.\n        //  - If partition is flashed or unknown, it is okay to delete snapshots.\n        //    Otherwise (UPDATED flag), only delete snapshots if they are not mapped\n        //    as dm-snapshot (for example, after merge completes).\n        bool should_unmap = current_slot != Slot::Target;\n        bool should_delete = ShouldDeleteSnapshot(flashing_status, current_slot, name);\n        if (should_unmap && android::base::EndsWith(name, device_->GetSlotSuffix())) {\n            // Something very unexpected has happened - we want to unmap this\n            // snapshot, but it's on the wrong slot. We can't unmap an active\n            // partition. If this is not really a snapshot, skip the unmap\n            // step.\n            if (dm_.GetState(name) == DmDeviceState::INVALID || !IsSnapshotDevice(name)) {\n                LOG(ERROR) << \"Detected snapshot \" << name << \" on \" << current_slot << \" slot\"\n                           << \" for source partition; removing without unmap.\";\n                should_unmap = false;\n            }\n        }\n\n        bool partition_ok = true;\n        if (should_unmap && !UnmapPartitionWithSnapshot(lock, name)) {\n            partition_ok = false;\n        }\n        if (partition_ok && should_delete && !DeleteSnapshot(lock, name)) {\n            partition_ok = false;\n        }\n\n        if (!partition_ok) {\n            // Remember whether or not we were able to unmap the cow image.\n            auto cow_image_device = GetCowImageDeviceName(name);\n            has_mapped_cow_images |=\n                    (EnsureImageManager() && images_->IsImageMapped(cow_image_device));\n\n            ok = false;\n        }\n    }\n\n    if (ok || !has_mapped_cow_images) {\n        if (!EnsureImageManager()) {\n            return false;\n        }\n\n        if (device_->IsRecovery()) {\n            // If a device is in recovery, we need to mark the snapshots for cleanup\n            // upon next reboot, since we cannot delete them here.\n            if (!images_->DisableAllImages()) {\n                LOG(ERROR) << \"Could not remove all snapshot artifacts in recovery\";\n                return false;\n            }\n        } else if (!images_->RemoveAllImages()) {\n            // Delete any image artifacts as a precaution, in case an update is\n            // being cancelled due to some corrupted state in an lp_metadata file.\n            // Note that we do not do this if some cow images are still mapped,\n            // since we must not remove backing storage if it's in use.\n            LOG(ERROR) << \"Could not remove all snapshot artifacts\";\n            return false;\n        }\n    }\n    return ok;\n}\n\n// See comments in RemoveAllSnapshots().\nbool SnapshotManager::ShouldDeleteSnapshot(const std::map<std::string, bool>& flashing_status,\n                                           Slot current_slot, const std::string& name) {\n    if (current_slot != Slot::Target) {\n        return true;\n    }\n    auto it = flashing_status.find(name);\n    if (it == flashing_status.end()) {\n        LOG(WARNING) << \"Can't determine flashing status for \" << name;\n        return true;\n    }\n    if (it->second) {\n        // partition flashed, okay to delete obsolete snapshots\n        return true;\n    }\n    return !IsSnapshotDevice(name);\n}\n\nUpdateState SnapshotManager::GetUpdateState(double* progress) {\n    // If we've never started an update, the state file won't exist.\n    auto state_file = GetStateFilePath();\n    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {\n        return UpdateState::None;\n    }\n\n    auto lock = LockShared();\n    if (!lock) {\n        return UpdateState::None;\n    }\n\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());\n    auto state = update_status.state();\n    if (progress == nullptr) {\n        return state;\n    }\n\n    if (state == UpdateState::MergeCompleted) {\n        *progress = 100.0;\n        return state;\n    }\n\n    *progress = 0.0;\n    if (state != UpdateState::Merging) {\n        return state;\n    }\n\n    if (!UpdateUsesUserSnapshots(lock.get())) {\n        // Sum all the snapshot states as if the system consists of a single huge\n        // snapshots device, then compute the merge completion percentage of that\n        // device.\n        std::vector<std::string> snapshots;\n        if (!ListSnapshots(lock.get(), &snapshots)) {\n            LOG(ERROR) << \"Could not list snapshots\";\n            return state;\n        }\n\n        DmTargetSnapshot::Status fake_snapshots_status = {};\n        for (const auto& snapshot : snapshots) {\n            DmTargetSnapshot::Status current_status;\n\n            if (!IsSnapshotDevice(snapshot)) continue;\n            if (!QuerySnapshotStatus(snapshot, nullptr, &current_status)) continue;\n\n            fake_snapshots_status.sectors_allocated += current_status.sectors_allocated;\n            fake_snapshots_status.total_sectors += current_status.total_sectors;\n            fake_snapshots_status.metadata_sectors += current_status.metadata_sectors;\n        }\n\n        *progress = DmTargetSnapshot::MergePercent(fake_snapshots_status,\n                                                   update_status.sectors_allocated());\n    } else {\n        if (EnsureSnapuserdConnected()) {\n            *progress = snapuserd_client_->GetMergePercent();\n        }\n    }\n\n    return state;\n}\n\nbool SnapshotManager::IsSnapshotWithoutSlotSwitch() {\n    return (access(GetBootSnapshotsWithoutSlotSwitchPath().c_str(), F_OK) == 0);\n}\n\nbool SnapshotManager::UpdateUsesCompression() {\n    auto lock = LockShared();\n    if (!lock) return false;\n    return UpdateUsesCompression(lock.get());\n}\n\nbool SnapshotManager::UpdateUsesCompression(LockedFile* lock) {\n    // This returns true even if compression is \"none\", since update_engine is\n    // really just trying to see if snapuserd is in use.\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    return update_status.using_snapuserd();\n}\n\nbool SnapshotManager::UpdateUsesIouring(LockedFile* lock) {\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    return update_status.io_uring_enabled();\n}\n\nbool SnapshotManager::UpdateUsesODirect(LockedFile* lock) {\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    return update_status.o_direct();\n}\n\nuint32_t SnapshotManager::GetUpdateCowOpMergeSize(LockedFile* lock) {\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    return update_status.cow_op_merge_size();\n}\n\nuint32_t SnapshotManager::GetUpdateWorkerCount(LockedFile* lock) {\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n    return update_status.num_worker_threads();\n}\n\nbool SnapshotManager::MarkSnapuserdFromSystem() {\n    auto path = GetSnapuserdFromSystemPath();\n\n    if (!android::base::WriteStringToFile(\"1\", path)) {\n        PLOG(ERROR) << \"Unable to write to vendor update path: \" << path;\n        return false;\n    }\n\n    unique_fd fd(open(path.c_str(), O_PATH));\n    if (fd < 0) {\n        PLOG(ERROR) << \"Failed to open file: \" << path;\n        return false;\n    }\n\n    /*\n     * This function is invoked by first stage init and hence we need to\n     * explicitly set the correct selinux label for this file as update_engine\n     * will try to remove this file later on once the snapshot merge is\n     * complete.\n     */\n    if (fsetxattr(fd.get(), XATTR_NAME_SELINUX, kOtaFileContext, strlen(kOtaFileContext) + 1, 0) <\n        0) {\n        PLOG(ERROR) << \"fsetxattr for the path: \" << path << \" failed\";\n    }\n\n    return true;\n}\n\n/*\n * Please see b/304829384 for more details.\n *\n * In Android S, we use dm-snapshot for mounting snapshots and snapshot-merge\n * process. If the vendor partition continues to be on Android S, then\n * \"snapuserd\" binary in first stage ramdisk will be from vendor partition.\n * Thus, we need to maintain backward compatibility.\n *\n * Now, We take a two step approach to maintain the backward compatibility:\n *\n * 1: During OTA installation, we will continue to use \"user-space\" snapshots\n * for OTA installation as both update-engine and snapuserd binary will be from system partition.\n * However, during installation, we mark \"legacy_snapuserd\" in\n * SnapshotUpdateStatus file to mark that this is a path to support backward compatibility.\n * Thus, this function will return \"false\" during OTA installation.\n *\n * 2: Post OTA reboot, there are two key steps:\n *    a: During first stage init, \"init\" and \"snapuserd\" could be from vendor\n *    partition. This could be from Android S. Thus, the snapshot mount path\n *    will be based off dm-snapshot.\n *\n *    b: Post selinux transition, \"init\" and \"update-engine\" will be \"system\"\n *    partition. Now, since the snapshots are mounted off dm-snapshot,\n *    update-engine interaction with \"snapuserd\" should work based off\n *    dm-snapshots.\n *\n *    TL;DR: update-engine will use the \"system\" snapuserd for installing new\n *    updates (this is safe as there is no \"vendor\" snapuserd running during\n *    installation). Post reboot, update-engine will use the legacy path when\n *    communicating with \"vendor\" snapuserd that was started in first-stage\n *    init. Hence, this function checks:\n *         i: Are we in post OTA reboot\n *         ii: Is the Vendor from Android 12\n *         iii: If both (i) and (ii) are true, then use the dm-snapshot based\n *         approach.\n *\n * 3: Post OTA reboot, if the vendor partition was updated from Android 12 to\n * any other release post Android 12, then snapuserd binary will be \"system\"\n * partition as post Android 12, init_boot will contain a copy of snapuserd\n * binary. Thus, during first stage init, if init is able to communicate to\n * daemon, that gives us a signal that the binary is from \"system\" copy. Hence,\n * there is no need to fallback to legacy dm-snapshot. Thus, init will use a\n * marker in /metadata to signal that the snapuserd binary from first stage init\n * can handle userspace snapshots.\n *\n */\nbool SnapshotManager::IsLegacySnapuserdPostReboot() {\n    auto slot = GetCurrentSlot();\n    if (slot == Slot::Target) {\n        /*\n            If this marker is present, the daemon can handle userspace snapshots.\n            During post-OTA reboot, this implies that the vendor partition is\n            Android 13 or higher. If the snapshots were created on an\n            Android 12 vendor, this means the vendor partition has been updated.\n        */\n        if (access(GetSnapuserdFromSystemPath().c_str(), F_OK) == 0) {\n            is_snapshot_userspace_ = true;\n            return false;\n        }\n        // If the marker isn't present and if the vendor is still in Android 12\n        if (is_legacy_snapuserd_.has_value() && is_legacy_snapuserd_.value() == true) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nbool SnapshotManager::UpdateUsesUserSnapshots() {\n    // This and the following function is constantly\n    // invoked during snapshot merge. We want to avoid\n    // constantly reading from disk. Hence, store this\n    // value in memory.\n    //\n    // Furthermore, this value in the disk is set\n    // only when OTA is applied and doesn't change\n    // during merge phase. Hence, once we know that\n    // the value is read from disk the very first time,\n    // it is safe to read successive checks from memory.\n\n    if (is_snapshot_userspace_.has_value()) {\n        // Check if legacy snapuserd is running post OTA reboot\n        if (IsLegacySnapuserdPostReboot()) {\n            return false;\n        }\n        return is_snapshot_userspace_.value();\n    }\n\n    auto lock = LockShared();\n    if (!lock) return false;\n\n    return UpdateUsesUserSnapshots(lock.get());\n}\n\nbool SnapshotManager::UpdateUsesUserSnapshots(LockedFile* lock) {\n    if (!is_snapshot_userspace_.has_value()) {\n        SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock);\n        is_snapshot_userspace_ = update_status.userspace_snapshots();\n        is_legacy_snapuserd_ = update_status.legacy_snapuserd();\n    }\n\n    if (IsLegacySnapuserdPostReboot()) {\n        return false;\n    }\n\n    return is_snapshot_userspace_.value();\n}\n\nbool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* snapshots,\n                                    const std::string& suffix) {\n    CHECK(lock);\n\n    auto dir_path = metadata_dir_ + \"/snapshots\"s;\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dir_path.c_str()), closedir);\n    if (!dir) {\n        PLOG(ERROR) << \"opendir failed: \" << dir_path;\n        return false;\n    }\n\n    struct dirent* dp;\n    while ((dp = readdir(dir.get())) != nullptr) {\n        if (dp->d_type != DT_REG) continue;\n\n        std::string name(dp->d_name);\n        if (!suffix.empty() && !android::base::EndsWith(name, suffix)) {\n            continue;\n        }\n\n        // Insert system and product partition at the beginning so that\n        // during snapshot-merge, these partitions are merged first.\n        if (name == \"system_a\" || name == \"system_b\" || name == \"product_a\" ||\n            name == \"product_b\") {\n            snapshots->insert(snapshots->begin(), std::move(name));\n        } else {\n            snapshots->emplace_back(std::move(name));\n        }\n    }\n\n    return true;\n}\n\nbool SnapshotManager::IsSnapshotManagerNeeded() {\n    if (access(kBootIndicatorPath, F_OK) == 0) {\n        return true;\n    }\n\n    if (IsScratchOtaMetadataOnSuper()) {\n        return true;\n    }\n\n    return false;\n}\n\nbool SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(\n        const std::function<bool(const std::string&)>& init) {\n    auto device = android::snapshot::GetScratchOtaMetadataPartition();\n    if (!device.empty()) {\n        init(device);\n        if (android::snapshot::MapScratchOtaMetadataPartition(device).empty()) {\n            return false;\n        }\n    }\n    return true;\n}\n\nstd::string SnapshotManager::GetGlobalRollbackIndicatorPath() {\n    return kRollbackIndicatorPath;\n}\n\nbool SnapshotManager::NeedSnapshotsInFirstStageMount() {\n    if (IsSnapshotWithoutSlotSwitch()) {\n        if (GetCurrentSlot() != Slot::Source) {\n            LOG(ERROR) << \"Snapshots marked to boot without slot switch; but slot is wrong\";\n            return false;\n        }\n        return true;\n    }\n    // If we fail to read, we'll wind up using CreateLogicalPartitions, which\n    // will create devices that look like the old slot, except with extra\n    // content at the end of each device. This will confuse dm-verity, and\n    // ultimately we'll fail to boot. Why not make it a fatal error and have\n    // the reason be clearer? Because the indicator file still exists, and\n    // if this was FATAL, reverting to the old slot would be broken.\n    auto slot = GetCurrentSlot();\n\n    if (slot != Slot::Target) {\n        if (slot == Slot::Source) {\n            // Device is rebooting into the original slot, so mark this as a\n            // rollback.\n            auto path = GetRollbackIndicatorPath();\n            if (!android::base::WriteStringToFile(\"1\", path)) {\n                PLOG(ERROR) << \"Unable to write rollback indicator: \" << path;\n            } else {\n                LOG(INFO) << \"Rollback detected, writing rollback indicator to \" << path;\n                if (device_->IsTempMetadata()) {\n                    CleanupScratchOtaMetadataIfPresent();\n                }\n            }\n        }\n        LOG(INFO) << \"Not booting from new slot. Will not mount snapshots.\";\n        return false;\n    }\n\n    // If we can't read the update state, it's unlikely anything else will\n    // succeed, so this is a fatal error. We'll eventually exhaust boot\n    // attempts and revert to the old slot.\n    auto lock = LockShared();\n    if (!lock) {\n        LOG(FATAL) << \"Could not read update state to determine snapshot status\";\n        return false;\n    }\n    switch (ReadUpdateState(lock.get())) {\n        case UpdateState::Unverified:\n        case UpdateState::Merging:\n        case UpdateState::MergeFailed:\n            return true;\n        default:\n            return false;\n    }\n}\n\nbool SnapshotManager::CreateLogicalAndSnapshotPartitions(\n        const std::string& super_device, const std::chrono::milliseconds& timeout_ms) {\n    LOG(INFO) << \"Creating logical partitions with snapshots as needed\";\n\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());\n    return MapAllPartitions(lock.get(), super_device, slot, timeout_ms);\n}\n\nbool SnapshotManager::MapAllPartitions(LockedFile* lock, const std::string& super_device,\n                                       uint32_t slot, const std::chrono::milliseconds& timeout_ms) {\n    const auto& opener = device_->GetPartitionOpener();\n    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);\n    if (!metadata) {\n        LOG(ERROR) << \"Could not read dynamic partition metadata for device: \" << super_device;\n        return false;\n    }\n\n    if (!EnsureImageManager()) {\n        return false;\n    }\n\n    for (const auto& partition : metadata->partitions) {\n        if (GetPartitionGroupName(metadata->groups[partition.group_index]) == kCowGroupName) {\n            LOG(INFO) << \"Skip mapping partition \" << GetPartitionName(partition) << \" in group \"\n                      << kCowGroupName;\n            continue;\n        }\n\n        if (GetPartitionName(partition) ==\n            android::base::Basename(android::snapshot::kOtaMetadataMount)) {\n            LOG(INFO) << \"Partition: \" << GetPartitionName(partition) << \" skipping\";\n            continue;\n        }\n\n        CreateLogicalPartitionParams params = {\n                .block_device = super_device,\n                .metadata = metadata.get(),\n                .partition = &partition,\n                .timeout_ms = timeout_ms,\n                .partition_opener = &opener,\n        };\n        if (!MapPartitionWithSnapshot(lock, std::move(params), SnapshotContext::Mount, nullptr)) {\n            return false;\n        }\n    }\n\n    LOG(INFO) << \"Created logical partitions with snapshot.\";\n    return true;\n}\n\nstatic std::chrono::milliseconds GetRemainingTime(\n        const std::chrono::milliseconds& timeout,\n        const std::chrono::time_point<std::chrono::steady_clock>& begin) {\n    // If no timeout is specified, execute all commands without specifying any timeout.\n    if (timeout.count() == 0) return std::chrono::milliseconds(0);\n    auto passed_time = std::chrono::steady_clock::now() - begin;\n    auto remaining_time = timeout - duration_cast<std::chrono::milliseconds>(passed_time);\n    if (remaining_time.count() <= 0) {\n        LOG(ERROR) << \"MapPartitionWithSnapshot has reached timeout \" << timeout.count() << \"ms (\"\n                   << remaining_time.count() << \"ms remaining)\";\n        // Return min() instead of remaining_time here because 0 is treated as a special value for\n        // no timeout, where the rest of the commands will still be executed.\n        return std::chrono::milliseconds::min();\n    }\n    return remaining_time;\n}\n\nbool SnapshotManager::MapPartitionWithSnapshot(LockedFile* lock,\n                                               CreateLogicalPartitionParams params,\n                                               SnapshotContext context, SnapshotPaths* paths) {\n    auto begin = std::chrono::steady_clock::now();\n\n    CHECK(lock);\n\n    if (params.GetPartitionName() != params.GetDeviceName()) {\n        LOG(ERROR) << \"Mapping snapshot with a different name is unsupported: partition_name = \"\n                   << params.GetPartitionName() << \", device_name = \" << params.GetDeviceName();\n        return false;\n    }\n\n    // Fill out fields in CreateLogicalPartitionParams so that we have more information (e.g. by\n    // reading super partition metadata).\n    CreateLogicalPartitionParams::OwnedData params_owned_data;\n    if (!params.InitDefaults(&params_owned_data)) {\n        return false;\n    }\n\n    if (!params.partition->num_extents) {\n        LOG(INFO) << \"Skipping zero-length logical partition: \" << params.GetPartitionName();\n        return true;  // leave path empty to indicate that nothing is mapped.\n    }\n\n    // Determine if there is a live snapshot for the SnapshotStatus of the partition; i.e. if the\n    // partition still has a snapshot that needs to be mapped.  If no live snapshot or merge\n    // completed, live_snapshot_status is set to nullopt.\n    std::optional<SnapshotStatus> live_snapshot_status;\n    do {\n        if (!IsSnapshotWithoutSlotSwitch() &&\n            !(params.partition->attributes & LP_PARTITION_ATTR_UPDATED)) {\n            LOG(INFO) << \"Detected re-flashing of partition, will skip snapshot: \"\n                      << params.GetPartitionName();\n            break;\n        }\n        auto file_path = GetSnapshotStatusFilePath(params.GetPartitionName());\n        if (access(file_path.c_str(), F_OK) != 0) {\n            if (errno != ENOENT) {\n                PLOG(INFO) << \"Can't map snapshot for \" << params.GetPartitionName()\n                           << \": Can't access \" << file_path;\n                return false;\n            }\n            break;\n        }\n        live_snapshot_status = std::make_optional<SnapshotStatus>();\n        if (!ReadSnapshotStatus(lock, params.GetPartitionName(), &*live_snapshot_status)) {\n            return false;\n        }\n        // No live snapshot if merge is completed.\n        if (live_snapshot_status->state() == SnapshotState::MERGE_COMPLETED) {\n            live_snapshot_status.reset();\n        }\n\n        if (live_snapshot_status->state() == SnapshotState::NONE ||\n            live_snapshot_status->cow_partition_size() + live_snapshot_status->cow_file_size() ==\n                    0) {\n            LOG(WARNING) << \"Snapshot status for \" << params.GetPartitionName()\n                         << \" is invalid, ignoring: state = \"\n                         << SnapshotState_Name(live_snapshot_status->state())\n                         << \", cow_partition_size = \" << live_snapshot_status->cow_partition_size()\n                         << \", cow_file_size = \" << live_snapshot_status->cow_file_size();\n            live_snapshot_status.reset();\n        }\n    } while (0);\n\n    if (live_snapshot_status.has_value()) {\n        // dm-snapshot requires the base device to be writable.\n        params.force_writable = true;\n        // Map the base device with a different name to avoid collision.\n        params.device_name = GetBaseDeviceName(params.GetPartitionName());\n    }\n\n    AutoDeviceList created_devices;\n\n    // Create the base device for the snapshot, or if there is no snapshot, the\n    // device itself. This device consists of the real blocks in the super\n    // partition that this logical partition occupies.\n    std::string base_path;\n    if (!CreateLogicalPartition(params, &base_path)) {\n        LOG(ERROR) << \"Could not create logical partition \" << params.GetPartitionName()\n                   << \" as device \" << params.GetDeviceName();\n        return false;\n    }\n    created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, params.GetDeviceName());\n\n    if (paths) {\n        paths->target_device = base_path;\n    }\n\n    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);\n    if (remaining_time.count() < 0) {\n        return false;\n    }\n\n    // Wait for the base device to appear\n    if (!WaitForDevice(base_path, remaining_time)) {\n        return false;\n    }\n\n    if (!live_snapshot_status.has_value()) {\n        created_devices.Release();\n        return true;\n    }\n\n    // We don't have ueventd in first-stage init, so use device major:minor\n    // strings instead.\n    std::string base_device;\n    if (!dm_.GetDeviceString(params.GetDeviceName(), &base_device)) {\n        LOG(ERROR) << \"Could not determine major/minor for: \" << params.GetDeviceName();\n        return false;\n    }\n\n    remaining_time = GetRemainingTime(params.timeout_ms, begin);\n    if (remaining_time.count() < 0) return false;\n\n    std::string cow_name;\n    CreateLogicalPartitionParams cow_params = params;\n    cow_params.timeout_ms = remaining_time;\n    if (!MapCowDevices(lock, cow_params, *live_snapshot_status, &created_devices, &cow_name)) {\n        return false;\n    }\n    std::string cow_device;\n    if (!GetMappedImageDeviceStringOrPath(cow_name, &cow_device)) {\n        LOG(ERROR) << \"Could not determine major/minor for: \" << cow_name;\n        return false;\n    }\n    if (paths) {\n        paths->cow_device_name = cow_name;\n    }\n\n    remaining_time = GetRemainingTime(params.timeout_ms, begin);\n    if (remaining_time.count() < 0) return false;\n\n    if (context == SnapshotContext::Update && live_snapshot_status->using_snapuserd()) {\n        // Stop here, we can't run dm-user yet, the COW isn't built.\n        created_devices.Release();\n        return true;\n    }\n\n    if (live_snapshot_status->using_snapuserd()) {\n        // Get the source device (eg the view of the partition from before it was resized).\n        std::string source_device_path;\n        if (live_snapshot_status->old_partition_size() > 0) {\n            if (!MapSourceDevice(lock, params.GetPartitionName(), remaining_time,\n                                 &source_device_path)) {\n                LOG(ERROR) << \"Could not map source device for: \" << cow_name;\n                return false;\n            }\n\n            auto source_device = GetSourceDeviceName(params.GetPartitionName());\n            created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, source_device);\n        } else {\n            source_device_path = base_path;\n        }\n\n        if (!WaitForDevice(source_device_path, remaining_time)) {\n            return false;\n        }\n\n        std::string cow_path;\n        if (!GetMappedImageDevicePath(cow_name, &cow_path)) {\n            LOG(ERROR) << \"Could not determine path for: \" << cow_name;\n            return false;\n        }\n        if (!WaitForDevice(cow_path, remaining_time)) {\n            return false;\n        }\n\n        auto name = GetDmUserCowName(params.GetPartitionName(), GetSnapshotDriver(lock));\n\n        std::string new_cow_device;\n        if (!MapDmUserCow(lock, name, cow_path, source_device_path, base_path, remaining_time,\n                          &new_cow_device)) {\n            LOG(ERROR) << \"Could not map dm-user device for partition \"\n                       << params.GetPartitionName();\n            return false;\n        }\n        created_devices.EmplaceBack<AutoUnmapDevice>(&dm_, name);\n\n        cow_device = new_cow_device;\n    }\n\n    // For userspace snapshots, dm-user block device itself will act as a\n    // snapshot device. There is one subtle difference - MapSnapshot will create\n    // either snapshot target or snapshot-merge target based on the underlying\n    // state of the snapshot device. If snapshot-merge target is created, merge\n    // will immediately start in the kernel.\n    //\n    // This is no longer true with respect to userspace snapshots. When dm-user\n    // block device is created, we just have the snapshots ready but daemon in\n    // the user-space will not start the merge. We have to explicitly inform the\n    // daemon to resume the merge. Check ProcessUpdateState() call stack.\n    if (!UpdateUsesUserSnapshots(lock)) {\n        remaining_time = GetRemainingTime(params.timeout_ms, begin);\n        if (remaining_time.count() < 0) return false;\n\n        std::string path;\n        if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,\n                         &path)) {\n            LOG(ERROR) << \"Could not map snapshot for partition: \" << params.GetPartitionName();\n            return false;\n        }\n        // No need to add params.GetPartitionName() to created_devices since it is immediately\n        // released.\n\n        if (paths) {\n            paths->snapshot_device = path;\n        }\n        LOG(INFO) << \"Mapped \" << params.GetPartitionName() << \" as snapshot device at \" << path;\n    } else {\n        LOG(INFO) << \"Mapped \" << params.GetPartitionName() << \" as snapshot device at \"\n                  << cow_device;\n    }\n\n    created_devices.Release();\n\n    return true;\n}\n\nbool SnapshotManager::UnmapPartitionWithSnapshot(LockedFile* lock,\n                                                 const std::string& target_partition_name) {\n    CHECK(lock);\n\n    if (!UnmapSnapshot(lock, target_partition_name)) {\n        return false;\n    }\n\n    if (!UnmapCowDevices(lock, target_partition_name)) {\n        return false;\n    }\n\n    auto base_name = GetBaseDeviceName(target_partition_name);\n    if (!DeleteDeviceIfExists(base_name)) {\n        LOG(ERROR) << \"Cannot delete base device: \" << base_name;\n        return false;\n    }\n\n    auto source_name = GetSourceDeviceName(target_partition_name);\n    if (!DeleteDeviceIfExists(source_name)) {\n        LOG(ERROR) << \"Cannot delete source device: \" << source_name;\n        return false;\n    }\n\n    LOG(INFO) << \"Successfully unmapped snapshot \" << target_partition_name;\n\n    return true;\n}\n\nbool SnapshotManager::MapCowDevices(LockedFile* lock, const CreateLogicalPartitionParams& params,\n                                    const SnapshotStatus& snapshot_status,\n                                    AutoDeviceList* created_devices, std::string* cow_name) {\n    CHECK(lock);\n    CHECK(snapshot_status.cow_partition_size() + snapshot_status.cow_file_size() > 0);\n    auto begin = std::chrono::steady_clock::now();\n\n    std::string partition_name = params.GetPartitionName();\n    std::string cow_image_name = GetCowImageDeviceName(partition_name);\n    *cow_name = GetCowName(partition_name);\n\n    // Map COW image if necessary.\n    if (snapshot_status.cow_file_size() > 0) {\n        if (!EnsureImageManager()) return false;\n        auto remaining_time = GetRemainingTime(params.timeout_ms, begin);\n        if (remaining_time.count() < 0) return false;\n\n        if (!MapCowImage(partition_name, remaining_time).has_value()) {\n            LOG(ERROR) << \"Could not map cow image for partition: \" << partition_name;\n            return false;\n        }\n        created_devices->EmplaceBack<AutoUnmapImage>(images_.get(), cow_image_name);\n\n        // If no COW partition exists, just return the image alone.\n        if (snapshot_status.cow_partition_size() == 0) {\n            *cow_name = std::move(cow_image_name);\n            LOG(INFO) << \"Mapped COW image for \" << partition_name << \" at \" << *cow_name;\n            return true;\n        }\n    }\n\n    auto remaining_time = GetRemainingTime(params.timeout_ms, begin);\n    if (remaining_time.count() < 0) return false;\n\n    CHECK(snapshot_status.cow_partition_size() > 0);\n\n    // Create the DmTable for the COW device. It is the DmTable of the COW partition plus\n    // COW image device as the last extent.\n    CreateLogicalPartitionParams cow_partition_params = params;\n    cow_partition_params.partition = nullptr;\n    cow_partition_params.partition_name = *cow_name;\n    cow_partition_params.device_name.clear();\n    DmTable table;\n    if (!CreateDmTable(cow_partition_params, &table)) {\n        return false;\n    }\n    // If the COW image exists, append it as the last extent.\n    if (snapshot_status.cow_file_size() > 0) {\n        std::string cow_image_device;\n        if (!GetMappedImageDeviceStringOrPath(cow_image_name, &cow_image_device)) {\n            LOG(ERROR) << \"Cannot determine major/minor for: \" << cow_image_name;\n            return false;\n        }\n        auto cow_partition_sectors = snapshot_status.cow_partition_size() / kSectorSize;\n        auto cow_image_sectors = snapshot_status.cow_file_size() / kSectorSize;\n        table.Emplace<DmTargetLinear>(cow_partition_sectors, cow_image_sectors, cow_image_device,\n                                      0);\n    }\n\n    // We have created the DmTable now. Map it.\n    std::string cow_path;\n    if (!dm_.CreateDevice(*cow_name, table, &cow_path, remaining_time)) {\n        LOG(ERROR) << \"Could not create COW device: \" << *cow_name;\n        return false;\n    }\n    created_devices->EmplaceBack<AutoUnmapDevice>(&dm_, *cow_name);\n    LOG(INFO) << \"Mapped COW device for \" << params.GetPartitionName() << \" at \" << cow_path;\n    return true;\n}\n\nbool SnapshotManager::UnmapCowDevices(LockedFile* lock, const std::string& name) {\n    CHECK(lock);\n    if (!EnsureImageManager()) return false;\n\n    if (UpdateUsesCompression(lock) && !UpdateUsesUserSnapshots(lock)) {\n        auto dm_user_name = GetDmUserCowName(name, GetSnapshotDriver(lock));\n        if (!UnmapDmUserDevice(dm_user_name)) {\n            return false;\n        }\n    }\n\n    if (!DeleteDeviceIfExists(GetCowName(name), 4000ms)) {\n        LOG(ERROR) << \"Cannot unmap: \" << GetCowName(name);\n        return false;\n    }\n\n    std::string cow_image_name = GetCowImageDeviceName(name);\n    if (!images_->UnmapImageIfExists(cow_image_name)) {\n        LOG(ERROR) << \"Cannot unmap image \" << cow_image_name;\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::UnmapDmUserDevice(const std::string& dm_user_name) {\n    if (dm_.GetState(dm_user_name) == DmDeviceState::INVALID) {\n        return true;\n    }\n\n    if (!DeleteDeviceIfExists(dm_user_name)) {\n        LOG(ERROR) << \"Cannot unmap \" << dm_user_name;\n        return false;\n    }\n\n    if (EnsureSnapuserdConnected()) {\n        if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {\n            LOG(ERROR) << \"Failed to wait for \" << dm_user_name << \" control device to delete\";\n            return false;\n        }\n    }\n\n    // Ensure the control device is gone so we don't run into ABA problems.\n    auto control_device = \"/dev/dm-user/\" + dm_user_name;\n    if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {\n        LOG(ERROR) << \"Timed out waiting for \" << control_device << \" to unlink\";\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::UnmapUserspaceSnapshotDevice(LockedFile* lock,\n                                                   const std::string& snapshot_name) {\n    auto dm_user_name = GetDmUserCowName(snapshot_name, GetSnapshotDriver(lock));\n    if (dm_.GetState(dm_user_name) == DmDeviceState::INVALID) {\n        return true;\n    }\n\n    CHECK(lock);\n\n    SnapshotStatus snapshot_status;\n\n    if (!ReadSnapshotStatus(lock, snapshot_name, &snapshot_status)) {\n        return false;\n    }\n    // If the merge is complete, then we switch dm tables which is equivalent\n    // to unmap; hence, we can't be deleting the device\n    // as the table would be mounted off partitions and will fail.\n    if (snapshot_status.state() != SnapshotState::MERGE_COMPLETED) {\n        if (!DeleteDeviceIfExists(dm_user_name, 4000ms)) {\n            LOG(ERROR) << \"Cannot unmap \" << dm_user_name;\n            return false;\n        }\n    }\n\n    if (EnsureSnapuserdConnected()) {\n        if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {\n            LOG(ERROR) << \"Failed to wait for \" << dm_user_name << \" control device to delete\";\n            return false;\n        }\n    }\n\n    // Ensure the control device is gone so we don't run into ABA problems.\n    auto control_device = \"/dev/dm-user/\" + dm_user_name;\n    if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {\n        LOG(ERROR) << \"Timed out waiting for \" << control_device << \" to unlink\";\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    auto state = ReadUpdateState(lock.get());\n    if (state == UpdateState::Unverified) {\n        if (GetCurrentSlot() == Slot::Target) {\n            LOG(ERROR) << \"Cannot call MapAllSnapshots when booting from the target slot.\";\n            return false;\n        }\n    } else if (state != UpdateState::Initiated) {\n        LOG(ERROR) << \"Cannot call MapAllSnapshots from update state: \" << state;\n        return false;\n    }\n\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock.get(), &snapshots)) {\n        return false;\n    }\n\n    const auto& opener = device_->GetPartitionOpener();\n    auto slot_suffix = device_->GetOtherSlotSuffix();\n    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);\n    auto super_device = device_->GetSuperDevice(slot_number);\n    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot_number);\n    if (!metadata) {\n        LOG(ERROR) << \"MapAllSnapshots could not read dynamic partition metadata for device: \"\n                   << super_device;\n        return false;\n    }\n\n    for (const auto& snapshot : snapshots) {\n        if (!UnmapPartitionWithSnapshot(lock.get(), snapshot)) {\n            LOG(ERROR) << \"MapAllSnapshots could not unmap snapshot: \" << snapshot;\n            return false;\n        }\n\n        CreateLogicalPartitionParams params = {\n                .block_device = super_device,\n                .metadata = metadata.get(),\n                .partition_name = snapshot,\n                .timeout_ms = timeout_ms,\n                .partition_opener = &opener,\n        };\n        if (!MapPartitionWithSnapshot(lock.get(), std::move(params), SnapshotContext::Mount,\n                                      nullptr)) {\n            LOG(ERROR) << \"MapAllSnapshots failed to map: \" << snapshot;\n            return false;\n        }\n    }\n\n    LOG(INFO) << \"MapAllSnapshots succeeded.\";\n    return true;\n}\n\nbool SnapshotManager::UnmapAllSnapshots() {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    return UnmapAllSnapshots(lock.get());\n}\n\nbool SnapshotManager::UnmapAllSnapshots(LockedFile* lock) {\n    LOG(INFO) << \"Lock acquired for \" << __FUNCTION__;\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock, &snapshots)) {\n        return false;\n    }\n    LOG(INFO) << \"Found \" << snapshots.size() << \" partitions with snapshots\";\n\n    for (const auto& snapshot : snapshots) {\n        if (!UnmapPartitionWithSnapshot(lock, snapshot)) {\n            LOG(ERROR) << \"Failed to unmap snapshot: \" << snapshot;\n            return false;\n        }\n    }\n    LOG(INFO) << \"Unmapped \" << snapshots.size() << \" partitions with snapshots\";\n\n    // Terminate the daemon and release the snapuserd_client_ object.\n    // If we need to re-connect with the daemon, EnsureSnapuserdConnected()\n    // will re-create the object and establish the socket connection.\n    if (snapuserd_client_) {\n        LOG(INFO) << \"Shutdown snapuserd daemon\";\n        snapuserd_client_->DetachSnapuserd();\n        snapuserd_client_ = nullptr;\n    }\n\n    return true;\n}\n\nauto SnapshotManager::OpenFile(const std::string& file,\n                               int lock_flags) -> std::unique_ptr<LockedFile> {\n    const auto start = std::chrono::system_clock::now();\n    unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));\n    if (fd < 0) {\n        PLOG(ERROR) << \"Open failed: \" << file;\n        return nullptr;\n    }\n    if (lock_flags != 0 && TEMP_FAILURE_RETRY(flock(fd, lock_flags)) < 0) {\n        PLOG(ERROR) << \"Acquire flock failed: \" << file;\n        return nullptr;\n    }\n    // For simplicity, we want to CHECK that lock_mode == LOCK_EX, in some\n    // calls, so strip extra flags.\n    int lock_mode = lock_flags & (LOCK_EX | LOCK_SH);\n    const auto end = std::chrono::system_clock::now();\n    const auto duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);\n    if (duration_ms >= 1000ms) {\n        LOG(INFO) << \"Taking lock on \" << file << \" took \" << duration_ms.count() << \"ms\";\n    }\n    return std::make_unique<LockedFile>(file, std::move(fd), lock_mode);\n}\n\nSnapshotManager::LockedFile::~LockedFile() {\n    if (TEMP_FAILURE_RETRY(flock(fd_, LOCK_UN)) < 0) {\n        PLOG(ERROR) << \"Failed to unlock file: \" << path_;\n    }\n}\n\nstd::string SnapshotManager::GetStateFilePath() const {\n    return metadata_dir_ + \"/state\"s;\n}\n\nstd::string SnapshotManager::GetMergeStateFilePath() const {\n    return metadata_dir_ + \"/merge_state\"s;\n}\n\nstd::string SnapshotManager::GetLockPath() const {\n    return metadata_dir_;\n}\n\nstd::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::OpenLock(int lock_flags) {\n    auto lock_file = GetLockPath();\n    return OpenFile(lock_file, lock_flags);\n}\n\nstd::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockShared() {\n    return OpenLock(LOCK_SH);\n}\n\nstd::unique_ptr<SnapshotManager::LockedFile> SnapshotManager::LockExclusive() {\n    return OpenLock(LOCK_EX);\n}\n\nstatic UpdateState UpdateStateFromString(const std::string& contents) {\n    if (contents.empty() || contents == \"none\") {\n        return UpdateState::None;\n    } else if (contents == \"initiated\") {\n        return UpdateState::Initiated;\n    } else if (contents == \"unverified\") {\n        return UpdateState::Unverified;\n    } else if (contents == \"merging\") {\n        return UpdateState::Merging;\n    } else if (contents == \"merge-completed\") {\n        return UpdateState::MergeCompleted;\n    } else if (contents == \"merge-needs-reboot\") {\n        return UpdateState::MergeNeedsReboot;\n    } else if (contents == \"merge-failed\") {\n        return UpdateState::MergeFailed;\n    } else if (contents == \"cancelled\") {\n        return UpdateState::Cancelled;\n    } else {\n        LOG(ERROR) << \"Unknown merge state in update state file: \\\"\" << contents << \"\\\"\";\n        return UpdateState::None;\n    }\n}\n\nstd::ostream& operator<<(std::ostream& os, UpdateState state) {\n    switch (state) {\n        case UpdateState::None:\n            return os << \"none\";\n        case UpdateState::Initiated:\n            return os << \"initiated\";\n        case UpdateState::Unverified:\n            return os << \"unverified\";\n        case UpdateState::Merging:\n            return os << \"merging\";\n        case UpdateState::MergeCompleted:\n            return os << \"merge-completed\";\n        case UpdateState::MergeNeedsReboot:\n            return os << \"merge-needs-reboot\";\n        case UpdateState::MergeFailed:\n            return os << \"merge-failed\";\n        case UpdateState::Cancelled:\n            return os << \"cancelled\";\n        default:\n            LOG(ERROR) << \"Unknown update state: \" << static_cast<uint32_t>(state);\n            return os;\n    }\n}\n\nstd::ostream& operator<<(std::ostream& os, MergePhase phase) {\n    switch (phase) {\n        case MergePhase::NO_MERGE:\n            return os << \"none\";\n        case MergePhase::FIRST_PHASE:\n            return os << \"first\";\n        case MergePhase::SECOND_PHASE:\n            return os << \"second\";\n        default:\n            LOG(ERROR) << \"Unknown merge phase: \" << static_cast<uint32_t>(phase);\n            return os << \"unknown(\" << static_cast<uint32_t>(phase) << \")\";\n    }\n}\n\nUpdateState SnapshotManager::ReadUpdateState(LockedFile* lock) {\n    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock);\n    return status.state();\n}\n\nSnapshotUpdateStatus SnapshotManager::ReadSnapshotUpdateStatus(LockedFile* lock) {\n    CHECK(lock);\n\n    SnapshotUpdateStatus status = {};\n    std::string contents;\n    if (!android::base::ReadFileToString(GetStateFilePath(), &contents)) {\n        PLOG(ERROR) << \"Read state file failed\";\n        status.set_state(UpdateState::None);\n        return status;\n    }\n\n    if (!status.ParseFromString(contents)) {\n        LOG(WARNING) << \"Unable to parse state file as SnapshotUpdateStatus, using the old format\";\n\n        // Try to rollback to legacy file to support devices that are\n        // currently using the old file format.\n        // TODO(b/147409432)\n        status.set_state(UpdateStateFromString(contents));\n    }\n\n    return status;\n}\n\nbool SnapshotManager::WriteUpdateState(LockedFile* lock, UpdateState state,\n                                       MergeFailureCode failure_code) {\n    SnapshotUpdateStatus status;\n    status.set_state(state);\n\n    switch (state) {\n        case UpdateState::MergeFailed:\n            status.set_merge_failure_code(failure_code);\n            break;\n        case UpdateState::Initiated:\n            status.set_source_build_fingerprint(\n                    android::base::GetProperty(\"ro.build.fingerprint\", \"\"));\n            break;\n        default:\n            break;\n    }\n\n    // If we're transitioning between two valid states (eg, we're not beginning\n    // or ending an OTA), then make sure to propagate the compression bit and\n    // build fingerprint.\n    if (!(state == UpdateState::Initiated || state == UpdateState::None)) {\n        SnapshotUpdateStatus old_status = ReadSnapshotUpdateStatus(lock);\n        status.set_using_snapuserd(old_status.using_snapuserd());\n        status.set_source_build_fingerprint(old_status.source_build_fingerprint());\n        status.set_merge_phase(old_status.merge_phase());\n        status.set_userspace_snapshots(old_status.userspace_snapshots());\n        status.set_io_uring_enabled(old_status.io_uring_enabled());\n        status.set_legacy_snapuserd(old_status.legacy_snapuserd());\n        status.set_o_direct(old_status.o_direct());\n        status.set_cow_op_merge_size(old_status.cow_op_merge_size());\n        status.set_num_worker_threads(old_status.num_worker_threads());\n    }\n    return WriteSnapshotUpdateStatus(lock, status);\n}\n\nbool SnapshotManager::WriteSnapshotUpdateStatus(LockedFile* lock,\n                                                const SnapshotUpdateStatus& status) {\n    CHECK(lock);\n    CHECK(lock->lock_mode() == LOCK_EX);\n\n    std::string contents;\n    if (!status.SerializeToString(&contents)) {\n        LOG(ERROR) << \"Unable to serialize SnapshotUpdateStatus.\";\n        return false;\n    }\n\n#ifdef LIBSNAPSHOT_USE_HAL\n    auto merge_status = MergeStatus::UNKNOWN;\n    switch (status.state()) {\n        // The needs-reboot and completed cases imply that /data and /metadata\n        // can be safely wiped, so we don't report a merge status.\n        case UpdateState::None:\n        case UpdateState::MergeNeedsReboot:\n        case UpdateState::MergeCompleted:\n        case UpdateState::Initiated:\n            merge_status = MergeStatus::NONE;\n            break;\n        case UpdateState::Unverified:\n            merge_status = MergeStatus::SNAPSHOTTED;\n            break;\n        case UpdateState::Merging:\n        case UpdateState::MergeFailed:\n            merge_status = MergeStatus::MERGING;\n            break;\n        default:\n            // Note that Cancelled flows to here - it is never written, since\n            // it only communicates a transient state to the caller.\n            LOG(ERROR) << \"Unexpected update status: \" << status.state();\n            break;\n    }\n\n    bool set_before_write =\n            merge_status == MergeStatus::SNAPSHOTTED || merge_status == MergeStatus::MERGING;\n    if (set_before_write && !device_->SetBootControlMergeStatus(merge_status)) {\n        return false;\n    }\n#endif\n\n    if (!WriteStringToFileAtomic(contents, GetStateFilePath())) {\n        PLOG(ERROR) << \"Could not write to state file\";\n        return false;\n    }\n\n#ifdef LIBSNAPSHOT_USE_HAL\n    if (!set_before_write && !device_->SetBootControlMergeStatus(merge_status)) {\n        return false;\n    }\n#endif\n    return true;\n}\n\nstd::string SnapshotManager::GetSnapshotStatusFilePath(const std::string& name) {\n    auto file = metadata_dir_ + \"/snapshots/\"s + name;\n    return file;\n}\n\nbool SnapshotManager::ReadSnapshotStatus(LockedFile* lock, const std::string& name,\n                                         SnapshotStatus* status) {\n    CHECK(lock);\n    auto path = GetSnapshotStatusFilePath(name);\n\n    unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));\n    if (fd < 0) {\n        PLOG(ERROR) << \"Open failed: \" << path;\n        return false;\n    }\n\n    if (!status->ParseFromFileDescriptor(fd.get())) {\n        PLOG(ERROR) << \"Unable to parse \" << path << \" as SnapshotStatus\";\n        return false;\n    }\n\n    if (status->name() != name) {\n        LOG(WARNING) << \"Found snapshot status named \" << status->name() << \" in \" << path;\n        status->set_name(name);\n    }\n\n    return true;\n}\n\nbool SnapshotManager::WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status) {\n    // The caller must take an exclusive lock to modify snapshots.\n    CHECK(lock);\n    CHECK(lock->lock_mode() == LOCK_EX);\n    CHECK(!status.name().empty());\n\n    auto path = GetSnapshotStatusFilePath(status.name());\n\n    std::string content;\n    if (!status.SerializeToString(&content)) {\n        LOG(ERROR) << \"Unable to serialize SnapshotStatus for \" << status.name();\n        return false;\n    }\n\n    if (!WriteStringToFileAtomic(content, path)) {\n        PLOG(ERROR) << \"Unable to write SnapshotStatus to \" << path;\n        return false;\n    }\n\n    return true;\n}\n\nbool SnapshotManager::EnsureImageManager() {\n    if (images_) return true;\n\n    images_ = device_->OpenImageManager();\n    if (!images_) {\n        LOG(ERROR) << \"Could not open ImageManager\";\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::EnsureSnapuserdConnected(std::chrono::milliseconds timeout_ms) {\n    if (snapuserd_client_) {\n        return true;\n    }\n\n    if (!use_first_stage_snapuserd_ && !EnsureSnapuserdStarted()) {\n        return false;\n    }\n\n    snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, timeout_ms);\n    if (!snapuserd_client_) {\n        LOG(ERROR) << \"Unable to connect to snapuserd\";\n        return false;\n    }\n    return true;\n}\n\nvoid SnapshotManager::UnmapAndDeleteCowPartition(MetadataBuilder* current_metadata) {\n    std::vector<std::string> to_delete;\n    for (auto* existing_cow_partition : current_metadata->ListPartitionsInGroup(kCowGroupName)) {\n        if (!DeleteDeviceIfExists(existing_cow_partition->name())) {\n            LOG(WARNING) << existing_cow_partition->name()\n                         << \" cannot be unmapped and its space cannot be reclaimed\";\n            continue;\n        }\n        to_delete.push_back(existing_cow_partition->name());\n    }\n    for (const auto& name : to_delete) {\n        current_metadata->RemovePartition(name);\n    }\n}\n\nstatic Return AddRequiredSpace(Return orig,\n                               const std::map<std::string, SnapshotStatus>& all_snapshot_status) {\n    if (orig.error_code() != Return::ErrorCode::NO_SPACE) {\n        return orig;\n    }\n    uint64_t sum = 0;\n    for (auto&& [name, status] : all_snapshot_status) {\n        sum += status.cow_file_size();\n    }\n    LOG(INFO) << \"Calculated needed COW space: \" << sum << \" bytes\";\n    return Return::NoSpace(sum);\n}\n\nReturn SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manifest) {\n    auto lock = LockExclusive();\n    if (!lock) return Return::Error();\n\n    auto update_state = ReadUpdateState(lock.get());\n    if (update_state != UpdateState::Initiated) {\n        LOG(ERROR) << \"Cannot create update snapshots in state \" << update_state;\n        return Return::Error();\n    }\n\n    // TODO(b/134949511): remove this check. Right now, with overlayfs mounted, the scratch\n    // partition takes up a big chunk of space in super, causing COW images to be created on\n    // retrofit Virtual A/B devices.\n    if (device_->IsOverlayfsSetup()) {\n        LOG(ERROR) << \"Cannot create update snapshots with overlayfs setup. Run `adb enable-verity`\"\n                   << \", reboot, then try again.\";\n        return Return::Error();\n    }\n\n    const auto& opener = device_->GetPartitionOpener();\n    auto current_suffix = device_->GetSlotSuffix();\n    uint32_t current_slot = SlotNumberForSlotSuffix(current_suffix);\n    auto target_suffix = device_->GetOtherSlotSuffix();\n    uint32_t target_slot = SlotNumberForSlotSuffix(target_suffix);\n    auto current_super = device_->GetSuperDevice(current_slot);\n\n    auto current_metadata = MetadataBuilder::New(opener, current_super, current_slot);\n    if (current_metadata == nullptr) {\n        LOG(ERROR) << \"Cannot create metadata builder.\";\n        return Return::Error();\n    }\n\n    auto target_metadata =\n            MetadataBuilder::NewForUpdate(opener, current_super, current_slot, target_slot);\n    if (target_metadata == nullptr) {\n        LOG(ERROR) << \"Cannot create target metadata builder.\";\n        return Return::Error();\n    }\n\n    // Delete partitions with target suffix in |current_metadata|. Otherwise,\n    // partition_cow_creator recognizes these left-over partitions as used space.\n    for (const auto& group_name : current_metadata->ListGroups()) {\n        if (android::base::EndsWith(group_name, target_suffix)) {\n            current_metadata->RemoveGroupAndPartitions(group_name);\n        }\n    }\n\n    SnapshotMetadataUpdater metadata_updater(target_metadata.get(), target_slot, manifest);\n    if (!metadata_updater.Update()) {\n        LOG(ERROR) << \"Cannot calculate new metadata.\";\n        return Return::Error();\n    }\n\n    // Delete previous COW partitions in current_metadata so that PartitionCowCreator marks those as\n    // free regions.\n    UnmapAndDeleteCowPartition(current_metadata.get());\n\n    // Check that all these metadata is not retrofit dynamic partitions. Snapshots on\n    // devices with retrofit dynamic partitions does not make sense.\n    // This ensures that current_metadata->GetFreeRegions() uses the same device\n    // indices as target_metadata (i.e. 0 -> \"super\").\n    // This is also assumed in MapCowDevices() call below.\n    CHECK(current_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME &&\n          target_metadata->GetBlockDevicePartitionName(0) == LP_METADATA_DEFAULT_PARTITION_NAME);\n\n    const auto& dap_metadata = manifest.dynamic_partition_metadata();\n\n    std::string vabc_disable_reason;\n    if (!dap_metadata.vabc_enabled()) {\n        vabc_disable_reason = \"not enabled metadata\";\n    } else if (device_->IsRecovery()) {\n        vabc_disable_reason = \"recovery\";\n    } else if (!KernelSupportsCompressedSnapshots()) {\n        vabc_disable_reason = \"kernel missing userspace block device support\";\n    }\n\n    // Deduce supported features.\n    bool userspace_snapshots = CanUseUserspaceSnapshots();\n    bool legacy_compression = GetLegacyCompressionEnabledProperty();\n    bool is_legacy_snapuserd = IsVendorFromAndroid12();\n\n    if (!vabc_disable_reason.empty()) {\n        if (userspace_snapshots) {\n            LOG(INFO) << \"Userspace snapshots disabled: \" << vabc_disable_reason;\n        }\n        if (legacy_compression) {\n            LOG(INFO) << \"Compression disabled: \" << vabc_disable_reason;\n        }\n        userspace_snapshots = false;\n        legacy_compression = false;\n        is_legacy_snapuserd = false;\n    }\n\n    if (legacy_compression || userspace_snapshots) {\n        if (dap_metadata.cow_version() < kMinCowVersion ||\n            dap_metadata.cow_version() > kMaxCowVersion) {\n            LOG(ERROR) << \"Manifest cow version is out of bounds (got: \"\n                       << dap_metadata.cow_version() << \", min: \" << kMinCowVersion\n                       << \", max: \" << kMaxCowVersion << \")\";\n            return Return::Error();\n        }\n    }\n\n    if (!userspace_snapshots && is_legacy_snapuserd && legacy_compression) {\n        userspace_snapshots = true;\n        LOG(INFO) << \"Vendor from Android 12. Enabling userspace snapshot for OTA install\";\n    }\n\n    const bool using_snapuserd = userspace_snapshots || legacy_compression;\n    if (!using_snapuserd) {\n        LOG(INFO) << \"Using legacy Virtual A/B (dm-snapshot)\";\n    }\n\n    std::string compression_algorithm;\n    uint64_t compression_factor{};\n    if (using_snapuserd) {\n        compression_algorithm = dap_metadata.vabc_compression_param();\n        compression_factor = dap_metadata.compression_factor();\n        if (compression_algorithm.empty()) {\n            // Older OTAs don't set an explicit compression type, so default to gz.\n            compression_algorithm = \"gz\";\n        }\n        LOG(INFO) << \"using compression algorithm: \" << compression_algorithm\n                  << \", max compressible block size: \" << compression_factor;\n    }\n    auto read_ahead_size =\n            android::base::GetUintProperty<uint>(\"ro.virtual_ab.read_ahead_size\", kReadAheadSizeKb);\n    PartitionCowCreator cow_creator{\n            .target_metadata = target_metadata.get(),\n            .target_suffix = target_suffix,\n            .target_partition = nullptr,\n            .current_metadata = current_metadata.get(),\n            .current_suffix = current_suffix,\n            .update = nullptr,\n            .extra_extents = {},\n            .using_snapuserd = using_snapuserd,\n            .compression_algorithm = compression_algorithm,\n            .compression_factor = compression_factor,\n            .read_ahead_size = read_ahead_size,\n    };\n\n    if (dap_metadata.vabc_feature_set().has_threaded()) {\n        cow_creator.enable_threading = dap_metadata.vabc_feature_set().threaded();\n    }\n    if (dap_metadata.vabc_feature_set().has_batch_writes()) {\n        cow_creator.batched_writes = dap_metadata.vabc_feature_set().batch_writes();\n    }\n\n    // In case of error, automatically delete devices that are created along the way.\n    // Note that \"lock\" is destroyed after \"created_devices\", so it is safe to use |lock| for\n    // these devices.\n    AutoDeviceList created_devices;\n    std::map<std::string, SnapshotStatus> all_snapshot_status;\n    auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,\n                                             &all_snapshot_status);\n    if (!ret.is_ok()) {\n        LOG(ERROR) << \"CreateUpdateSnapshotsInternal failed: \" << ret.string();\n        return ret;\n    }\n\n    auto exported_target_metadata = target_metadata->Export();\n    if (exported_target_metadata == nullptr) {\n        LOG(ERROR) << \"Cannot export target metadata\";\n        return Return::Error();\n    }\n\n    ret = InitializeUpdateSnapshots(lock.get(), dap_metadata.cow_version(), target_metadata.get(),\n                                    exported_target_metadata.get(), target_suffix,\n                                    all_snapshot_status);\n    if (!ret.is_ok()) return ret;\n\n    if (!UpdatePartitionTable(opener, device_->GetSuperDevice(target_slot),\n                              *exported_target_metadata, target_slot)) {\n        LOG(ERROR) << \"Cannot write target metadata\";\n        return Return::Error();\n    }\n\n    // If snapuserd is enabled, we need to retain a copy of the old metadata\n    // so we can access original blocks in case they are moved around. We do\n    // not want to rely on the old super metadata slot because we don't\n    // guarantee its validity after the slot switch is successful.\n    if (using_snapuserd) {\n        auto metadata = current_metadata->Export();\n        if (!metadata) {\n            LOG(ERROR) << \"Could not export current metadata\";\n            return Return::Error();\n        }\n\n        auto path = GetOldPartitionMetadataPath();\n        if (!android::fs_mgr::WriteToImageFile(path, *metadata.get())) {\n            LOG(ERROR) << \"Cannot write old metadata to \" << path;\n            return Return::Error();\n        }\n    }\n\n    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());\n    status.set_state(update_state);\n    status.set_using_snapuserd(using_snapuserd);\n\n    if (userspace_snapshots) {\n        status.set_userspace_snapshots(true);\n        LOG(INFO) << \"Virtual A/B using userspace snapshots\";\n\n        if (GetIouringEnabledProperty()) {\n            status.set_io_uring_enabled(true);\n            LOG(INFO) << \"io_uring for snapshots enabled\";\n        }\n        if (GetODirectEnabledProperty()) {\n            status.set_o_direct(true);\n            LOG(INFO) << \"o_direct for source image enabled\";\n        }\n        if (is_legacy_snapuserd) {\n            status.set_legacy_snapuserd(true);\n            LOG(INFO) << \"Setting legacy_snapuserd to true\";\n        }\n        status.set_cow_op_merge_size(\n                android::base::GetUintProperty<uint32_t>(\"ro.virtual_ab.cow_op_merge_size\", 0));\n        status.set_num_worker_threads(\n                android::base::GetUintProperty<uint32_t>(\"ro.virtual_ab.num_worker_threads\", 0));\n\n    } else if (legacy_compression) {\n        LOG(INFO) << \"Virtual A/B using legacy snapuserd\";\n    } else {\n        LOG(INFO) << \"Virtual A/B using dm-snapshot\";\n    }\n\n    is_snapshot_userspace_.emplace(userspace_snapshots);\n    is_legacy_snapuserd_.emplace(is_legacy_snapuserd);\n\n    if (!device()->IsTestDevice() && using_snapuserd) {\n        // Terminate stale daemon if any\n        std::unique_ptr<SnapuserdClient> snapuserd_client = std::move(snapuserd_client_);\n        if (!snapuserd_client) {\n            snapuserd_client = SnapuserdClient::TryConnect(kSnapuserdSocket, 5s);\n        }\n        if (snapuserd_client) {\n            snapuserd_client->DetachSnapuserd();\n            snapuserd_client = nullptr;\n        }\n    }\n\n    if (!WriteSnapshotUpdateStatus(lock.get(), status)) {\n        LOG(ERROR) << \"Unable to write new update state\";\n        return Return::Error();\n    }\n\n    created_devices.Release();\n    LOG(INFO) << \"Successfully created all snapshots for target slot \" << target_suffix;\n\n    return Return::Ok();\n}\n\nReturn SnapshotManager::CreateUpdateSnapshotsInternal(\n        LockedFile* lock, const DeltaArchiveManifest& manifest, PartitionCowCreator* cow_creator,\n        AutoDeviceList* created_devices,\n        std::map<std::string, SnapshotStatus>* all_snapshot_status) {\n    CHECK(lock);\n\n    auto* target_metadata = cow_creator->target_metadata;\n    const auto& target_suffix = cow_creator->target_suffix;\n\n    if (!target_metadata->AddGroup(kCowGroupName, 0)) {\n        LOG(ERROR) << \"Cannot add group \" << kCowGroupName;\n        return Return::Error();\n    }\n\n    std::map<std::string, const PartitionUpdate*> partition_map;\n    std::map<std::string, std::vector<Extent>> extra_extents_map;\n    for (const auto& partition_update : manifest.partitions()) {\n        auto suffixed_name = partition_update.partition_name() + target_suffix;\n        auto&& [it, inserted] = partition_map.emplace(suffixed_name, &partition_update);\n        if (!inserted) {\n            LOG(ERROR) << \"Duplicated partition \" << partition_update.partition_name()\n                       << \" in update manifest.\";\n            return Return::Error();\n        }\n\n        auto& extra_extents = extra_extents_map[suffixed_name];\n        if (partition_update.has_hash_tree_extent()) {\n            extra_extents.push_back(partition_update.hash_tree_extent());\n        }\n        if (partition_update.has_fec_extent()) {\n            extra_extents.push_back(partition_update.fec_extent());\n        }\n    }\n\n    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {\n        cow_creator->target_partition = target_partition;\n        cow_creator->update = nullptr;\n        auto iter = partition_map.find(target_partition->name());\n        if (iter != partition_map.end()) {\n            cow_creator->update = iter->second;\n        } else {\n            LOG(INFO) << target_partition->name()\n                      << \" isn't included in the payload, skipping the cow creation.\";\n            continue;\n        }\n\n        cow_creator->extra_extents.clear();\n        auto extra_extents_it = extra_extents_map.find(target_partition->name());\n        if (extra_extents_it != extra_extents_map.end()) {\n            cow_creator->extra_extents = std::move(extra_extents_it->second);\n        }\n\n        // Compute the device sizes for the partition.\n        auto cow_creator_ret = cow_creator->Run();\n        if (!cow_creator_ret.has_value()) {\n            LOG(ERROR) << \"PartitionCowCreator returned no value for \" << target_partition->name();\n            return Return::Error();\n        }\n\n        LOG(INFO) << \"For partition \" << target_partition->name()\n                  << \", device size = \" << cow_creator_ret->snapshot_status.device_size()\n                  << \", snapshot size = \" << cow_creator_ret->snapshot_status.snapshot_size()\n                  << \", cow partition size = \"\n                  << cow_creator_ret->snapshot_status.cow_partition_size()\n                  << \", cow file size = \" << cow_creator_ret->snapshot_status.cow_file_size();\n\n        // Delete any existing snapshot before re-creating one.\n        if (!DeleteSnapshot(lock, target_partition->name())) {\n            LOG(ERROR) << \"Cannot delete existing snapshot before creating a new one for partition \"\n                       << target_partition->name();\n            return Return::Error();\n        }\n\n        // It is possible that the whole partition uses free space in super, and snapshot / COW\n        // would not be needed. In this case, skip the partition.\n        bool needs_snapshot = cow_creator_ret->snapshot_status.snapshot_size() > 0;\n        bool needs_cow = (cow_creator_ret->snapshot_status.cow_partition_size() +\n                          cow_creator_ret->snapshot_status.cow_file_size()) > 0;\n        CHECK(needs_snapshot == needs_cow);\n\n        if (!needs_snapshot) {\n            LOG(INFO) << \"Skip creating snapshot for partition \" << target_partition->name()\n                      << \"because nothing needs to be snapshotted.\";\n            continue;\n        }\n\n        // Find the original partition size.\n        auto name = target_partition->name();\n        auto old_partition_name =\n                name.substr(0, name.size() - target_suffix.size()) + cow_creator->current_suffix;\n        auto old_partition = cow_creator->current_metadata->FindPartition(old_partition_name);\n        if (old_partition) {\n            cow_creator_ret->snapshot_status.set_old_partition_size(old_partition->size());\n        }\n\n        // Store these device sizes to snapshot status file.\n        if (!CreateSnapshot(lock, cow_creator, &cow_creator_ret->snapshot_status)) {\n            return Return::Error();\n        }\n        created_devices->EmplaceBack<AutoDeleteSnapshot>(this, lock, target_partition->name());\n\n        // Create the COW partition. That is, use any remaining free space in super partition before\n        // creating the COW images.\n        if (cow_creator_ret->snapshot_status.cow_partition_size() > 0) {\n            CHECK(cow_creator_ret->snapshot_status.cow_partition_size() % kSectorSize == 0)\n                    << \"cow_partition_size == \"\n                    << cow_creator_ret->snapshot_status.cow_partition_size()\n                    << \" is not a multiple of sector size \" << kSectorSize;\n            auto cow_partition = target_metadata->AddPartition(GetCowName(target_partition->name()),\n                                                               kCowGroupName, 0 /* flags */);\n            if (cow_partition == nullptr) {\n                return Return::Error();\n            }\n\n            if (!target_metadata->ResizePartition(\n                        cow_partition, cow_creator_ret->snapshot_status.cow_partition_size(),\n                        cow_creator_ret->cow_partition_usable_regions)) {\n                LOG(ERROR) << \"Cannot create COW partition on metadata with size \"\n                           << cow_creator_ret->snapshot_status.cow_partition_size();\n                return Return::Error();\n            }\n            // Only the in-memory target_metadata is modified; nothing to clean up if there is an\n            // error in the future.\n        }\n\n        all_snapshot_status->emplace(target_partition->name(),\n                                     std::move(cow_creator_ret->snapshot_status));\n\n        LOG(INFO) << \"Successfully created snapshot partition for \" << target_partition->name();\n    }\n\n    LOG(INFO) << \"Allocating CoW images.\";\n\n    for (auto&& [name, snapshot_status] : *all_snapshot_status) {\n        // Create the backing COW image if necessary.\n        if (snapshot_status.cow_file_size() > 0) {\n            auto ret = CreateCowImage(lock, name);\n            if (!ret.is_ok()) {\n                LOG(ERROR) << \"CreateCowImage failed: \" << ret.string();\n                return AddRequiredSpace(ret, *all_snapshot_status);\n            }\n        }\n\n        LOG(INFO) << \"Successfully created snapshot for \" << name;\n    }\n\n    return Return::Ok();\n}\n\nReturn SnapshotManager::InitializeUpdateSnapshots(\n        LockedFile* lock, uint32_t cow_version, MetadataBuilder* target_metadata,\n        const LpMetadata* exported_target_metadata, const std::string& target_suffix,\n        const std::map<std::string, SnapshotStatus>& all_snapshot_status) {\n    CHECK(lock);\n\n    CreateLogicalPartitionParams cow_params{\n            .block_device = LP_METADATA_DEFAULT_PARTITION_NAME,\n            .metadata = exported_target_metadata,\n            .timeout_ms = std::chrono::milliseconds::max(),\n            .partition_opener = &device_->GetPartitionOpener(),\n    };\n    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {\n        AutoDeviceList created_devices_for_cow;\n\n        if (!UnmapPartitionWithSnapshot(lock, target_partition->name())) {\n            LOG(ERROR) << \"Cannot unmap existing COW devices before re-mapping them for zero-fill: \"\n                       << target_partition->name();\n            return Return::Error();\n        }\n\n        auto it = all_snapshot_status.find(target_partition->name());\n        if (it == all_snapshot_status.end()) continue;\n        cow_params.partition_name = target_partition->name();\n        std::string cow_name;\n        if (!MapCowDevices(lock, cow_params, it->second, &created_devices_for_cow, &cow_name)) {\n            return Return::Error();\n        }\n\n        std::string cow_path;\n        if (!images_->GetMappedImageDevice(cow_name, &cow_path)) {\n            LOG(ERROR) << \"Cannot determine path for \" << cow_name;\n            return Return::Error();\n        }\n\n        if (!android::fs_mgr::WaitForFile(cow_path, 6s)) {\n            LOG(ERROR) << \"Timed out waiting for device to appear: \" << cow_path;\n            return Return::Error();\n        }\n\n        if (it->second.using_snapuserd()) {\n            unique_fd fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));\n            if (fd < 0) {\n                PLOG(ERROR) << \"open \" << cow_path << \" failed for snapshot \"\n                            << cow_params.partition_name;\n                return Return::Error();\n            }\n\n            CowOptions options;\n            if (device()->IsTestDevice()) {\n                options.scratch_space = false;\n            }\n            options.compression = it->second.compression_algorithm();\n            if (cow_version >= 3) {\n                options.op_count_max = it->second.estimated_ops_buffer_size();\n                options.max_blocks = {it->second.device_size() / options.block_size};\n            }\n\n            auto writer = CreateCowWriter(cow_version, options, std::move(fd));\n            if (!writer->Finalize()) {\n                LOG(ERROR) << \"Could not initialize COW device for \" << target_partition->name();\n                return Return::Error();\n            }\n        } else {\n            auto ret = InitializeKernelCow(cow_path);\n            if (!ret.is_ok()) {\n                LOG(ERROR) << \"Can't zero-fill COW device for \" << target_partition->name() << \": \"\n                           << cow_path;\n                return AddRequiredSpace(ret, all_snapshot_status);\n            }\n        }\n        // Let destructor of created_devices_for_cow to unmap the COW devices.\n    };\n    return Return::Ok();\n}\n\nbool SnapshotManager::MapUpdateSnapshot(const CreateLogicalPartitionParams& params,\n                                        std::string* snapshot_path) {\n    auto lock = LockShared();\n    if (!lock) return false;\n    if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {\n        LOG(ERROR) << \"Cannot unmap existing snapshot before re-mapping it: \"\n                   << params.GetPartitionName();\n        return false;\n    }\n\n    SnapshotStatus status;\n    if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {\n        return false;\n    }\n    if (status.using_snapuserd()) {\n        LOG(ERROR) << \"Cannot use MapUpdateSnapshot with snapuserd\";\n        return false;\n    }\n\n    SnapshotPaths paths;\n    if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {\n        return false;\n    }\n\n    if (!paths.snapshot_device.empty()) {\n        *snapshot_path = paths.snapshot_device;\n    } else {\n        *snapshot_path = paths.target_device;\n    }\n    DCHECK(!snapshot_path->empty());\n    return true;\n}\n\nstd::unique_ptr<ICowWriter> SnapshotManager::OpenSnapshotWriter(\n        const android::fs_mgr::CreateLogicalPartitionParams& params,\n        std::optional<uint64_t> label) {\n#if defined(LIBSNAPSHOT_NO_COW_WRITE)\n    (void)params;\n    (void)label;\n\n    LOG(ERROR) << \"Snapshots cannot be written in first-stage init or recovery\";\n    return nullptr;\n#else\n    // First unmap any existing mapping.\n    auto lock = LockShared();\n    if (!lock) return nullptr;\n    if (!UnmapPartitionWithSnapshot(lock.get(), params.GetPartitionName())) {\n        LOG(ERROR) << \"Cannot unmap existing snapshot before re-mapping it: \"\n                   << params.GetPartitionName();\n        return nullptr;\n    }\n\n    SnapshotPaths paths;\n    if (!MapPartitionWithSnapshot(lock.get(), params, SnapshotContext::Update, &paths)) {\n        return nullptr;\n    }\n\n    SnapshotStatus status;\n    if (!paths.cow_device_name.empty()) {\n        if (!ReadSnapshotStatus(lock.get(), params.GetPartitionName(), &status)) {\n            return nullptr;\n        }\n    } else {\n        // Currently, partition_cow_creator always creates snapshots. The\n        // reason is that if partition X shrinks while partition Y grows, we\n        // cannot bindly write to the newly freed extents in X. This would\n        // make the old slot unusable. So, the entire size of the target\n        // partition is currently considered snapshottable.\n        LOG(ERROR) << \"No snapshot available for partition \" << params.GetPartitionName();\n        return nullptr;\n    }\n\n    if (!status.using_snapuserd()) {\n        LOG(ERROR) << \"Can only create snapshot writers with userspace or compressed snapshots\";\n        return nullptr;\n    }\n\n    return OpenCompressedSnapshotWriter(lock.get(), status, paths, label);\n#endif\n}\n\n#if !defined(LIBSNAPSHOT_NO_COW_WRITE)\nstd::unique_ptr<ICowWriter> SnapshotManager::OpenCompressedSnapshotWriter(\n        LockedFile* lock, const SnapshotStatus& status, const SnapshotPaths& paths,\n        std::optional<uint64_t> label) {\n    CHECK(lock);\n\n    CowOptions cow_options;\n    cow_options.compression = status.compression_algorithm();\n    cow_options.max_blocks = {status.device_size() / cow_options.block_size};\n    cow_options.batch_write = status.batched_writes();\n    cow_options.num_compress_threads = status.enable_threading() ? 2 : 1;\n    cow_options.op_count_max = status.estimated_ops_buffer_size();\n    cow_options.compression_factor = status.compression_factor();\n    // Disable scratch space for vts tests\n    if (device()->IsTestDevice()) {\n        cow_options.scratch_space = false;\n    }\n\n    // Currently we don't support partial snapshots, since partition_cow_creator\n    // never creates this scenario.\n    CHECK(status.snapshot_size() == status.device_size());\n\n    std::string cow_path;\n    if (!GetMappedImageDevicePath(paths.cow_device_name, &cow_path)) {\n        LOG(ERROR) << \"Could not determine path for \" << paths.cow_device_name;\n        return nullptr;\n    }\n\n    unique_fd cow_fd(open(cow_path.c_str(), O_RDWR | O_CLOEXEC));\n    if (cow_fd < 0) {\n        PLOG(ERROR) << \"OpenCompressedSnapshotWriter: open \" << cow_path;\n        return nullptr;\n    }\n\n    CowHeaderV3 header;\n    if (!ReadCowHeader(cow_fd, &header)) {\n        LOG(ERROR) << \"OpenCompressedSnapshotWriter: read header failed\";\n        return nullptr;\n    }\n\n    return CreateCowWriter(header.prefix.major_version, cow_options, std::move(cow_fd), label);\n}\n#endif  // !defined(LIBSNAPSHOT_NO_COW_WRITE)\n\nbool SnapshotManager::UnmapUpdateSnapshot(const std::string& target_partition_name) {\n    auto lock = LockShared();\n    if (!lock) return false;\n    return UnmapPartitionWithSnapshot(lock.get(), target_partition_name);\n}\n\nbool SnapshotManager::UnmapAllPartitionsInRecovery() {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    const auto& opener = device_->GetPartitionOpener();\n    uint32_t slot = SlotNumberForSlotSuffix(device_->GetSlotSuffix());\n    auto super_device = device_->GetSuperDevice(slot);\n    auto metadata = android::fs_mgr::ReadMetadata(opener, super_device, slot);\n    if (!metadata) {\n        LOG(ERROR) << \"Could not read dynamic partition metadata for device: \" << super_device;\n        return false;\n    }\n\n    bool ok = true;\n    for (const auto& partition : metadata->partitions) {\n        auto partition_name = GetPartitionName(partition);\n        ok &= UnmapPartitionWithSnapshot(lock.get(), partition_name);\n    }\n    return ok;\n}\n\nstd::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot) {\n    switch (slot) {\n        case SnapshotManager::Slot::Unknown:\n            return os << \"unknown\";\n        case SnapshotManager::Slot::Source:\n            return os << \"source\";\n        case SnapshotManager::Slot::Target:\n            return os << \"target\";\n    }\n}\n\nbool SnapshotManager::Dump(std::ostream& os) {\n    // Don't actually lock. Dump() is for debugging purposes only, so it is okay\n    // if it is racy.\n    auto file = OpenLock(0 /* lock flag */);\n    if (!file) return false;\n\n    std::stringstream ss;\n\n    auto update_status = ReadSnapshotUpdateStatus(file.get());\n\n    ss << \"Update state: \" << update_status.state() << std::endl;\n    ss << \"Using snapuserd: \" << update_status.using_snapuserd() << std::endl;\n    ss << \"Using userspace snapshots: \" << update_status.userspace_snapshots() << std::endl;\n    ss << \"Using io_uring: \" << update_status.io_uring_enabled() << std::endl;\n    ss << \"Using o_direct: \" << update_status.o_direct() << std::endl;\n    ss << \"Cow op merge size (0 for uncapped): \" << update_status.cow_op_merge_size() << std::endl;\n    ss << \"Worker thread count: \" << update_status.num_worker_threads() << std::endl;\n    ss << \"Using XOR compression: \" << GetXorCompressionEnabledProperty() << std::endl;\n    ss << \"Current slot: \" << device_->GetSlotSuffix() << std::endl;\n    ss << \"Boot indicator: booting from \" << GetCurrentSlot() << \" slot\" << std::endl;\n    ss << \"Rollback indicator: \"\n       << (access(GetRollbackIndicatorPath().c_str(), F_OK) == 0 ? \"exists\" : strerror(errno))\n       << std::endl;\n    ss << \"Forward merge indicator: \"\n       << (access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0 ? \"exists\" : strerror(errno))\n       << std::endl;\n    ss << \"Source build fingerprint: \" << update_status.source_build_fingerprint() << std::endl;\n\n    if (update_status.state() == UpdateState::Merging) {\n        ss << \"Merge completion: \";\n        if (!EnsureSnapuserdConnected()) {\n            ss << \"N/A\";\n        } else {\n            ss << snapuserd_client_->GetMergePercent() << \"%\";\n        }\n        ss << std::endl;\n        ss << \"Merge phase: \" << update_status.merge_phase() << std::endl;\n    }\n\n    bool ok = true;\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(file.get(), &snapshots)) {\n        LOG(ERROR) << \"Could not list snapshots\";\n        snapshots.clear();\n        ok = false;\n    }\n    for (const auto& name : snapshots) {\n        ss << \"Snapshot: \" << name << std::endl;\n        SnapshotStatus status;\n        if (!ReadSnapshotStatus(file.get(), name, &status)) {\n            ok = false;\n            continue;\n        }\n        ss << \"    state: \" << SnapshotState_Name(status.state()) << std::endl;\n        ss << \"    device size (bytes): \" << status.device_size() << std::endl;\n        ss << \"    snapshot size (bytes): \" << status.snapshot_size() << std::endl;\n        ss << \"    cow partition size (bytes): \" << status.cow_partition_size() << std::endl;\n        ss << \"    cow file size (bytes): \" << status.cow_file_size() << std::endl;\n        ss << \"    allocated sectors: \" << status.sectors_allocated() << std::endl;\n        ss << \"    metadata sectors: \" << status.metadata_sectors() << std::endl;\n        ss << \"    compression: \" << status.compression_algorithm() << std::endl;\n        ss << \"    compression factor: \" << status.compression_factor() << std::endl;\n        ss << \"    merge phase: \" << DecideMergePhase(status) << std::endl;\n    }\n    os << ss.rdbuf();\n    return ok;\n}\n\nstd::unique_ptr<AutoDevice> SnapshotManager::EnsureMetadataMounted() {\n    if (!device_->IsRecovery()) {\n        // No need to mount anything in recovery.\n        LOG(INFO) << \"EnsureMetadataMounted does nothing in Android mode.\";\n        return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice());\n    }\n    auto ret = AutoUnmountDevice::New(device_->GetMetadataDir());\n    if (ret == nullptr) return nullptr;\n\n    // In rescue mode, it is possible to erase and format metadata, but /metadata/ota is not\n    // created to execute snapshot updates. Hence, subsequent calls is likely to fail because\n    // Lock*() fails. By failing early and returning nullptr here, update_engine_sideload can\n    // treat this case as if /metadata is not mounted.\n    if (!LockShared()) {\n        LOG(WARNING) << \"/metadata is mounted, but errors occur when acquiring a shared lock. \"\n                        \"Subsequent calls to SnapshotManager will fail. Unmounting /metadata now.\";\n        return nullptr;\n    }\n    return ret;\n}\n\nbool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {\n    if (!device_->IsRecovery()) {\n        LOG(ERROR) << \"Data wipes are only allowed in recovery.\";\n        return false;\n    }\n\n    auto mount = EnsureMetadataMounted();\n    if (!mount || !mount->HasDevice()) {\n        // We allow the wipe to continue, because if we can't mount /metadata,\n        // it is unlikely the device would have booted anyway. If there is no\n        // metadata partition, then the device predates Virtual A/B.\n        LOG(INFO) << \"/metadata not found; allowing wipe.\";\n        return true;\n    }\n\n    // This could happen if /metadata mounted but there is no filesystem\n    // structure. Weird, but we have to assume there's no OTA pending, and\n    // thus we let the wipe proceed.\n    UpdateState state;\n    {\n        auto lock = LockExclusive();\n        if (!lock) {\n            LOG(ERROR) << \"Unable to determine update state; allowing wipe.\";\n            return true;\n        }\n\n        state = ReadUpdateState(lock.get());\n        LOG(INFO) << \"Update state before wipe: \" << state << \"; slot: \" << GetCurrentSlot()\n                  << \"; suffix: \" << device_->GetSlotSuffix();\n    }\n\n    bool try_merge = false;\n    switch (state) {\n        case UpdateState::None:\n        case UpdateState::Initiated:\n            LOG(INFO) << \"Wipe is not impacted by update state; allowing wipe.\";\n            break;\n        case UpdateState::Unverified:\n            if (GetCurrentSlot() != Slot::Target) {\n                LOG(INFO) << \"Wipe is not impacted by rolled back update; allowing wipe\";\n                break;\n            }\n            if (!HasForwardMergeIndicator()) {\n                auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());\n                auto other_slot_number = SlotNumberForSlotSuffix(device_->GetOtherSlotSuffix());\n\n                // We're not allowed to forward merge, so forcefully rollback the\n                // slot switch.\n                LOG(INFO) << \"Allowing wipe due to lack of forward merge indicator; reverting to \"\n                             \"old slot since update will be deleted.\";\n                device_->SetSlotAsUnbootable(slot_number);\n                device_->SetActiveBootSlot(other_slot_number);\n                break;\n            }\n\n            // Forward merge indicator means we have to mount snapshots and try to merge.\n            LOG(INFO) << \"Forward merge indicator is present.\";\n            try_merge = true;\n            break;\n        case UpdateState::Merging:\n        case UpdateState::MergeFailed:\n            try_merge = true;\n            break;\n        case UpdateState::MergeNeedsReboot:\n        case UpdateState::Cancelled:\n            LOG(INFO) << \"Unexpected update state in recovery; allowing wipe.\";\n            break;\n        default:\n            break;\n    }\n\n    if (try_merge) {\n        auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());\n        auto super_path = device_->GetSuperDevice(slot_number);\n        if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {\n            LOG(ERROR) << \"Unable to map partitions to complete merge.\";\n            return false;\n        }\n\n        auto process_callback = [&]() -> bool {\n            if (callback) {\n                callback();\n            }\n            return true;\n        };\n\n        state = ProcessUpdateStateOnDataWipe(process_callback);\n        if (state == UpdateState::MergeFailed) {\n            return false;\n        }\n\n        // Nothing should be depending on partitions now, so unmap them all.\n        if (!UnmapAllPartitionsInRecovery()) {\n            LOG(ERROR) << \"Unable to unmap all partitions; fastboot may fail to flash.\";\n        }\n    }\n\n    if (state != UpdateState::None) {\n        auto lock = LockExclusive();\n        if (!lock) return false;\n\n        // Zap the update state so the bootloader doesn't think we're still\n        // merging. It's okay if this fails, it's informative only at this\n        // point.\n        WriteUpdateState(lock.get(), UpdateState::None);\n    }\n    return true;\n}\n\nbool SnapshotManager::FinishMergeInRecovery() {\n    if (!device_->IsRecovery()) {\n        LOG(ERROR) << \"Data wipes are only allowed in recovery.\";\n        return false;\n    }\n\n    auto mount = EnsureMetadataMounted();\n    if (!mount || !mount->HasDevice()) {\n        return false;\n    }\n\n    auto slot_number = SlotNumberForSlotSuffix(device_->GetSlotSuffix());\n    auto super_path = device_->GetSuperDevice(slot_number);\n    if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {\n        LOG(ERROR) << \"Unable to map partitions to complete merge.\";\n        return false;\n    }\n\n    UpdateState state = ProcessUpdateState();\n    if (state != UpdateState::MergeCompleted) {\n        LOG(ERROR) << \"Merge returned unexpected status: \" << state;\n        return false;\n    }\n\n    // Nothing should be depending on partitions now, so unmap them all.\n    if (!UnmapAllPartitionsInRecovery()) {\n        LOG(ERROR) << \"Unable to unmap all partitions; fastboot may fail to flash.\";\n    }\n    return true;\n}\n\nUpdateState SnapshotManager::ProcessUpdateStateOnDataWipe(const std::function<bool()>& callback) {\n    while (true) {\n        UpdateState state = ProcessUpdateState(callback);\n        LOG(INFO) << \"Processed updated state in recovery: \" << state;\n        switch (state) {\n            case UpdateState::MergeFailed:\n                LOG(ERROR) << \"Unrecoverable merge failure detected.\";\n                return state;\n            case UpdateState::Unverified: {\n                // Unverified was already handled earlier, in HandleImminentDataWipe,\n                // but it will fall through here if a forward merge is required.\n                //\n                // If InitiateMerge fails, we early return. If it succeeds, then we\n                // are guaranteed that the next call to ProcessUpdateState will not\n                // return Unverified.\n                if (!InitiateMerge()) {\n                    LOG(ERROR) << \"Failed to initiate merge on data wipe.\";\n                    return UpdateState::MergeFailed;\n                }\n                continue;\n            }\n            case UpdateState::MergeNeedsReboot:\n                // We shouldn't get here, because nothing is depending on\n                // logical partitions.\n                LOG(ERROR) << \"Unexpected merge-needs-reboot state in recovery.\";\n                return state;\n            default:\n                return state;\n        }\n    }\n}\n\nbool SnapshotManager::HasForwardMergeIndicator() {\n    return access(GetForwardMergeIndicatorPath().c_str(), F_OK) == 0;\n}\n\nbool SnapshotManager::EnsureNoOverflowSnapshot(LockedFile* lock) {\n    CHECK(lock);\n\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock, &snapshots)) {\n        LOG(ERROR) << \"Could not list snapshots.\";\n        return false;\n    }\n\n    for (const auto& snapshot : snapshots) {\n        SnapshotStatus status;\n        if (!ReadSnapshotStatus(lock, snapshot, &status)) {\n            return false;\n        }\n        if (status.using_snapuserd()) {\n            continue;\n        }\n\n        std::vector<DeviceMapper::TargetInfo> targets;\n        if (!dm_.GetTableStatus(snapshot, &targets)) {\n            LOG(ERROR) << \"Could not read snapshot device table: \" << snapshot;\n            return false;\n        }\n        if (targets.size() != 1) {\n            LOG(ERROR) << \"Unexpected device-mapper table for snapshot: \" << snapshot\n                       << \", size = \" << targets.size();\n            return false;\n        }\n        if (targets[0].IsOverflowSnapshot()) {\n            LOG(ERROR) << \"Detected overflow in snapshot \" << snapshot\n                       << \", CoW device size computation is wrong!\";\n            return false;\n        }\n    }\n\n    return true;\n}\n\nCreateResult SnapshotManager::RecoveryCreateSnapshotDevices() {\n    if (!device_->IsRecovery()) {\n        LOG(ERROR) << __func__ << \" is only allowed in recovery.\";\n        return CreateResult::NOT_CREATED;\n    }\n\n    auto mount = EnsureMetadataMounted();\n    if (!mount || !mount->HasDevice()) {\n        LOG(ERROR) << \"Couldn't mount Metadata.\";\n        return CreateResult::NOT_CREATED;\n    }\n    return RecoveryCreateSnapshotDevices(mount);\n}\n\nCreateResult SnapshotManager::RecoveryCreateSnapshotDevices(\n        const std::unique_ptr<AutoDevice>& metadata_device) {\n    if (!device_->IsRecovery()) {\n        LOG(ERROR) << __func__ << \" is only allowed in recovery.\";\n        return CreateResult::NOT_CREATED;\n    }\n\n    if (metadata_device == nullptr || !metadata_device->HasDevice()) {\n        LOG(ERROR) << \"Metadata not mounted.\";\n        return CreateResult::NOT_CREATED;\n    }\n\n    auto state_file = GetStateFilePath();\n    if (access(state_file.c_str(), F_OK) != 0 && errno == ENOENT) {\n        LOG(ERROR) << \"Couldn't access state file.\";\n        return CreateResult::NOT_CREATED;\n    }\n\n    if (!NeedSnapshotsInFirstStageMount()) {\n        return CreateResult::NOT_CREATED;\n    }\n\n    auto slot_suffix = device_->GetOtherSlotSuffix();\n    auto slot_number = SlotNumberForSlotSuffix(slot_suffix);\n    auto super_path = device_->GetSuperDevice(slot_number);\n    if (!CreateLogicalAndSnapshotPartitions(super_path, 20s)) {\n        LOG(ERROR) << \"Unable to map partitions.\";\n        return CreateResult::ERROR;\n    }\n    return CreateResult::CREATED;\n}\n\nbool SnapshotManager::UpdateForwardMergeIndicator(bool wipe) {\n    auto path = GetForwardMergeIndicatorPath();\n\n    if (!wipe) {\n        LOG(INFO) << \"Wipe is not scheduled. Deleting forward merge indicator.\";\n        return RemoveFileIfExists(path);\n    }\n\n    // TODO(b/152094219): Don't forward merge if no CoW file is allocated.\n\n    LOG(INFO) << \"Wipe will be scheduled. Allowing forward merge of snapshots.\";\n    if (!android::base::WriteStringToFile(\"1\", path)) {\n        PLOG(ERROR) << \"Unable to write forward merge indicator: \" << path;\n        return false;\n    }\n\n    return true;\n}\n\nISnapshotMergeStats* SnapshotManager::GetSnapshotMergeStatsInstance() {\n    return SnapshotMergeStats::GetInstance(*this);\n}\n\n// This is only to be used in recovery or normal Android (not first-stage init).\n// We don't guarantee dm paths are available in first-stage init, because ueventd\n// isn't running yet.\nbool SnapshotManager::GetMappedImageDevicePath(const std::string& device_name,\n                                               std::string* device_path) {\n    // Try getting the device string if it is a device mapper device.\n    if (dm_.GetState(device_name) != DmDeviceState::INVALID) {\n        return dm_.GetDmDevicePathByName(device_name, device_path);\n    }\n\n    // Otherwise, get path from IImageManager.\n    return images_->GetMappedImageDevice(device_name, device_path);\n}\n\nbool SnapshotManager::GetMappedImageDeviceStringOrPath(const std::string& device_name,\n                                                       std::string* device_string_or_mapped_path) {\n    // Try getting the device string if it is a device mapper device.\n    if (dm_.GetState(device_name) != DmDeviceState::INVALID) {\n        return dm_.GetDeviceString(device_name, device_string_or_mapped_path);\n    }\n\n    // Otherwise, get path from IImageManager.\n    if (!images_->GetMappedImageDevice(device_name, device_string_or_mapped_path)) {\n        return false;\n    }\n\n    LOG(WARNING) << \"Calling GetMappedImageDevice with local image manager; device \"\n                 << (device_string_or_mapped_path ? *device_string_or_mapped_path : \"(nullptr)\")\n                 << \"may not be available in first stage init! \";\n    return true;\n}\n\nbool SnapshotManager::WaitForDevice(const std::string& device,\n                                    std::chrono::milliseconds timeout_ms) {\n    if (!android::base::StartsWith(device, \"/\")) {\n        return true;\n    }\n\n    // In first-stage init, we rely on init setting a callback which can\n    // regenerate uevents and populate /dev for us.\n    if (uevent_regen_callback_) {\n        if (!uevent_regen_callback_(device)) {\n            LOG(ERROR) << \"Failed to find device after regenerating uevents: \" << device;\n            return false;\n        }\n        return true;\n    }\n\n    // Otherwise, the only kind of device we need to wait for is a dm-user\n    // misc device. Normal calls to DeviceMapper::CreateDevice() guarantee\n    // the path has been created.\n    if (!android::base::StartsWith(device, \"/dev/dm-user/\")) {\n        return true;\n    }\n\n    if (timeout_ms.count() == 0) {\n        LOG(ERROR) << \"No timeout was specified to wait for device: \" << device;\n        return false;\n    }\n    if (!android::fs_mgr::WaitForFile(device, timeout_ms)) {\n        LOG(ERROR) << \"Timed out waiting for device to appear: \" << device;\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::IsSnapuserdRequired() {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    auto status = ReadSnapshotUpdateStatus(lock.get());\n    return status.state() != UpdateState::None && status.using_snapuserd();\n}\n\nbool SnapshotManager::PrepareSnapuserdArgsForSelinux(std::vector<std::string>* snapuserd_argv) {\n    return PerformInitTransition(InitTransition::SELINUX_DETACH, snapuserd_argv);\n}\n\nbool SnapshotManager::DetachFirstStageSnapuserdForSelinux() {\n    LOG(INFO) << \"Detaching first stage snapuserd\";\n\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock.get(), &snapshots)) {\n        LOG(ERROR) << \"Failed to list snapshots.\";\n        return false;\n    }\n\n    size_t num_cows = 0;\n    size_t ok_cows = 0;\n    for (const auto& snapshot : snapshots) {\n        std::string user_cow_name = GetDmUserCowName(snapshot, GetSnapshotDriver(lock.get()));\n\n        if (dm_.GetState(user_cow_name) == DmDeviceState::INVALID) {\n            continue;\n        }\n\n        DeviceMapper::TargetInfo target;\n        if (!GetSingleTarget(user_cow_name, TableQuery::Table, &target)) {\n            continue;\n        }\n\n        auto target_type = DeviceMapper::GetTargetType(target.spec);\n        if (target_type != \"user\") {\n            LOG(ERROR) << \"Unexpected target type for \" << user_cow_name << \": \" << target_type;\n            continue;\n        }\n\n        num_cows++;\n        auto misc_name = user_cow_name;\n\n        DmTable table;\n        table.Emplace<DmTargetUser>(0, target.spec.length, misc_name);\n        if (!dm_.LoadTableAndActivate(user_cow_name, table)) {\n            LOG(ERROR) << \"Unable to swap tables for \" << misc_name;\n            continue;\n        }\n\n        // Wait for ueventd to acknowledge and create the control device node.\n        std::string control_device = \"/dev/dm-user/\" + misc_name;\n        if (!WaitForDevice(control_device, 10s)) {\n            LOG(ERROR) << \"dm-user control device no found:  \" << misc_name;\n            continue;\n        }\n\n        ok_cows++;\n        LOG(INFO) << \"control device is ready: \" << control_device;\n    }\n\n    if (ok_cows != num_cows) {\n        LOG(ERROR) << \"Could not transition all snapuserd consumers.\";\n        return false;\n    }\n\n    return true;\n}\n\nbool SnapshotManager::PerformSecondStageInitTransition() {\n    return PerformInitTransition(InitTransition::SECOND_STAGE);\n}\n\nconst LpMetadata* SnapshotManager::ReadOldPartitionMetadata(LockedFile* lock) {\n    CHECK(lock);\n\n    if (!old_partition_metadata_) {\n        auto path = GetOldPartitionMetadataPath();\n        old_partition_metadata_ = android::fs_mgr::ReadFromImageFile(path);\n        if (!old_partition_metadata_) {\n            LOG(ERROR) << \"Could not read old partition metadata from \" << path;\n            return nullptr;\n        }\n    }\n    return old_partition_metadata_.get();\n}\n\nMergePhase SnapshotManager::DecideMergePhase(const SnapshotStatus& status) {\n    if (status.using_snapuserd() && status.device_size() < status.old_partition_size()) {\n        return MergePhase::FIRST_PHASE;\n    }\n    return MergePhase::SECOND_PHASE;\n}\n\nvoid SnapshotManager::UpdateCowStats(ISnapshotMergeStats* stats) {\n    auto lock = LockExclusive();\n    if (!lock) return;\n\n    std::vector<std::string> snapshots;\n    if (!ListSnapshots(lock.get(), &snapshots, GetSnapshotSlotSuffix())) {\n        LOG(ERROR) << \"Could not list snapshots\";\n        return;\n    }\n\n    uint64_t cow_file_size = 0;\n    uint64_t total_cow_size = 0;\n    uint64_t estimated_cow_size = 0;\n    for (const auto& snapshot : snapshots) {\n        SnapshotStatus status;\n        if (!ReadSnapshotStatus(lock.get(), snapshot, &status)) {\n            return;\n        }\n\n        cow_file_size += status.cow_file_size();\n        total_cow_size += status.cow_file_size() + status.cow_partition_size();\n        estimated_cow_size += status.estimated_cow_size();\n    }\n\n    stats->report()->set_cow_file_size(cow_file_size);\n    stats->report()->set_total_cow_size_bytes(total_cow_size);\n    stats->report()->set_estimated_cow_size_bytes(estimated_cow_size);\n}\n\nvoid SnapshotManager::SetMergeStatsFeatures(ISnapshotMergeStats* stats) {\n    auto lock = LockExclusive();\n    if (!lock) return;\n\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());\n    stats->report()->set_iouring_used(update_status.io_uring_enabled());\n    stats->report()->set_userspace_snapshots_used(update_status.userspace_snapshots());\n    stats->report()->set_xor_compression_used(GetXorCompressionEnabledProperty());\n}\n\nbool SnapshotManager::DeleteDeviceIfExists(const std::string& name,\n                                           const std::chrono::milliseconds& timeout_ms) {\n    auto start = std::chrono::steady_clock::now();\n    while (true) {\n        if (dm_.DeleteDeviceIfExists(name)) {\n            return true;\n        }\n        auto now = std::chrono::steady_clock::now();\n        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);\n        if (elapsed >= timeout_ms) {\n            break;\n        }\n        std::this_thread::sleep_for(400ms);\n    }\n\n    // Try to diagnose why this failed. First get the actual device path.\n    std::string full_path;\n    if (!dm_.GetDmDevicePathByName(name, &full_path)) {\n        LOG(ERROR) << \"Unable to diagnose DM_DEV_REMOVE failure.\";\n        return false;\n    }\n\n    // Check for child dm-devices.\n    std::string block_name = android::base::Basename(full_path);\n    std::string sysfs_holders = \"/sys/class/block/\" + block_name + \"/holders\";\n\n    std::error_code ec;\n    std::filesystem::directory_iterator dir_iter(sysfs_holders, ec);\n    if (auto begin = std::filesystem::begin(dir_iter); begin != std::filesystem::end(dir_iter)) {\n        LOG(ERROR) << \"Child device-mapper device still mapped: \" << begin->path();\n        return false;\n    }\n\n    // Check for mounted partitions.\n    android::fs_mgr::Fstab fstab;\n    android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &fstab);\n    for (const auto& entry : fstab) {\n        if (android::base::Basename(entry.blk_device) == block_name) {\n            LOG(ERROR) << \"Partition still mounted: \" << entry.mount_point;\n            return false;\n        }\n    }\n\n    // Check for detached mounted partitions.\n    for (const auto& fs : std::filesystem::directory_iterator(\"/sys/fs\", ec)) {\n        std::string fs_type = android::base::Basename(fs.path().c_str());\n        if (!(fs_type == \"ext4\" || fs_type == \"f2fs\")) {\n            continue;\n        }\n\n        std::string path = fs.path().c_str() + \"/\"s + block_name;\n        if (access(path.c_str(), F_OK) == 0) {\n            LOG(ERROR) << \"Block device was lazily unmounted and is still in-use: \" << full_path\n                       << \"; possibly open file descriptor or attached loop device.\";\n            return false;\n        }\n    }\n\n    LOG(ERROR) << \"Device-mapper device \" << name << \"(\" << full_path << \")\" << \" still in use.\"\n               << \"  Probably a file descriptor was leaked or held open, or a loop device is\"\n               << \" attached.\";\n    return false;\n}\n\nMergeFailureCode SnapshotManager::ReadMergeFailureCode() {\n    auto lock = LockExclusive();\n    if (!lock) return MergeFailureCode::AcquireLock;\n\n    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());\n    if (status.state() != UpdateState::MergeFailed) {\n        return MergeFailureCode::Ok;\n    }\n    return status.merge_failure_code();\n}\n\nstd::string SnapshotManager::ReadSourceBuildFingerprint() {\n    auto lock = LockExclusive();\n    if (!lock) return {};\n\n    SnapshotUpdateStatus status = ReadSnapshotUpdateStatus(lock.get());\n    return status.source_build_fingerprint();\n}\n\nbool SnapshotManager::PauseSnapshotMerge() {\n    auto snapuserd_client = SnapuserdClient::TryConnect(kSnapuserdSocket, 5s);\n    if (snapuserd_client) {\n        // Pause the snapshot-merge\n        return snapuserd_client->PauseMerge();\n    }\n    return false;\n}\n\nbool SnapshotManager::ResumeSnapshotMerge() {\n    auto snapuserd_client = SnapuserdClient::TryConnect(kSnapuserdSocket, 5s);\n    if (snapuserd_client) {\n        // Resume the snapshot-merge\n        return snapuserd_client->ResumeMerge();\n    }\n    return false;\n}\n\nbool SnapshotManager::IsUserspaceSnapshotUpdateInProgress(\n        std::vector<std::string>& dynamic_partitions) {\n    // We cannot grab /metadata/ota lock here as this\n    // is in reboot path. See b/308900853\n    //\n    // Check if any of the partitions are mounted\n    // off dm-user block device. If so, then we are certain\n    // that OTA update in progress.\n    auto current_suffix = device_->GetSlotSuffix();\n    auto& dm = DeviceMapper::Instance();\n    auto dm_block_devices = dm.FindDmPartitions();\n    if (dm_block_devices.empty()) {\n        LOG(ERROR) << \"No dm-enabled block device is found.\";\n        return false;\n    }\n\n    bool is_ota_in_progress = false;\n    for (auto& partition : dm_block_devices) {\n        std::string partition_name = partition.first + current_suffix;\n        DeviceMapper::TargetInfo snap_target;\n        if (!GetSingleTarget(partition_name, TableQuery::Status, &snap_target)) {\n            continue;\n        }\n        auto type = DeviceMapper::GetTargetType(snap_target.spec);\n        // Partition is mounted off snapshots\n        if (type == \"user\") {\n            dynamic_partitions.emplace_back(\"/\" + partition.first);\n            is_ota_in_progress = true;\n        }\n    }\n    return is_ota_in_progress;\n}\n\nbool SnapshotManager::BootFromSnapshotsWithoutSlotSwitch() {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    auto contents = device_->GetSlotSuffix();\n    // This is the indicator which tells first-stage init\n    // to boot from snapshots even though there was no slot-switch\n    auto boot_file = GetBootSnapshotsWithoutSlotSwitchPath();\n    if (!WriteStringToFileAtomic(contents, boot_file)) {\n        PLOG(ERROR) << \"write failed: \" << boot_file;\n        return false;\n    }\n\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());\n    update_status.set_state(UpdateState::Initiated);\n    update_status.set_userspace_snapshots(true);\n    update_status.set_using_snapuserd(true);\n    if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotManager::PrepareDeviceToBootWithoutSnapshot() {\n    auto lock = LockExclusive();\n    if (!lock) return false;\n\n    android::base::RemoveFileIfExists(GetSnapshotBootIndicatorPath());\n    android::base::RemoveFileIfExists(GetBootSnapshotsWithoutSlotSwitchPath());\n\n    SnapshotUpdateStatus update_status = ReadSnapshotUpdateStatus(lock.get());\n    update_status.set_state(UpdateState::Cancelled);\n    if (!WriteSnapshotUpdateStatus(lock.get(), update_status)) {\n        return false;\n    }\n    return true;\n}\n\nvoid SnapshotManager::SetReadAheadSize(const std::string& entry_block_device, off64_t size_kb) {\n    std::string block_device;\n    if (!Realpath(entry_block_device, &block_device)) {\n        PLOG(ERROR) << \"Failed to realpath \" << entry_block_device;\n        return;\n    }\n\n    static constexpr std::string_view kDevBlockPrefix(\"/dev/block/\");\n    if (!android::base::StartsWith(block_device, kDevBlockPrefix)) {\n        LOG(ERROR) << block_device << \" is not a block device\";\n        return;\n    }\n\n    std::string block_name = block_device.substr(kDevBlockPrefix.length());\n    std::string sys_partition =\n            android::base::StringPrintf(\"/sys/class/block/%s/partition\", block_name.c_str());\n    struct stat info;\n    if (lstat(sys_partition.c_str(), &info) == 0) {\n        block_name += \"/..\";\n    }\n    std::string sys_ra = android::base::StringPrintf(\"/sys/class/block/%s/queue/read_ahead_kb\",\n                                                     block_name.c_str());\n    std::string size = std::to_string(size_kb);\n    android::base::WriteStringToFile(size, sys_ra.c_str());\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshot_metadata_updater.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"snapshot_metadata_updater.h\"\n\n#include <algorithm>\n#include <map>\n#include <optional>\n#include <set>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n#include <libsnapshot/snapshot.h>\n\nusing android::fs_mgr::MetadataBuilder;\nusing android::fs_mgr::Partition;\nusing android::fs_mgr::SlotSuffixForSlotNumber;\nusing chromeos_update_engine::DeltaArchiveManifest;\n\nnamespace android {\nnamespace snapshot {\nSnapshotMetadataUpdater::SnapshotMetadataUpdater(MetadataBuilder* builder, uint32_t target_slot,\n                                                 const DeltaArchiveManifest& manifest)\n    : builder_(builder), target_suffix_(SlotSuffixForSlotNumber(target_slot)) {\n    partial_update_ = manifest.partial_update();\n\n    if (!manifest.has_dynamic_partition_metadata()) {\n        return;\n    }\n\n    // Key: partition name (\"system\"). Value: group name (\"group\").\n    // No suffix.\n    std::map<std::string_view, std::string_view> partition_group_map;\n    const auto& metadata_groups = manifest.dynamic_partition_metadata().groups();\n    groups_.reserve(metadata_groups.size());\n    for (const auto& group : metadata_groups) {\n        groups_.emplace_back(Group{group.name() + target_suffix_, &group});\n        for (const auto& partition_name : group.partition_names()) {\n            partition_group_map[partition_name] = group.name();\n        }\n    }\n\n    for (const auto& p : manifest.partitions()) {\n        auto it = partition_group_map.find(p.partition_name());\n        if (it != partition_group_map.end()) {\n            partitions_.emplace_back(Partition{p.partition_name() + target_suffix_,\n                                               std::string(it->second) + target_suffix_, &p});\n        }\n    }\n\n}\n\nbool SnapshotMetadataUpdater::ShrinkPartitions() const {\n    for (const auto& partition_update : partitions_) {\n        auto* existing_partition = builder_->FindPartition(partition_update.name);\n        if (existing_partition == nullptr) {\n            continue;\n        }\n        auto new_size = partition_update->new_partition_info().size();\n        if (existing_partition->size() <= new_size) {\n            continue;\n        }\n        if (!builder_->ResizePartition(existing_partition, new_size)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::DeletePartitions() const {\n    // For partial update, not all dynamic partitions are included in the payload.\n    // TODO(xunchang) delete the untouched partitions whose group is in the payload.\n    // e.g. Delete vendor in the following scenario\n    // On device:\n    //   Group A: system, vendor\n    // In payload:\n    //   Group A: system\n    if (partial_update_) {\n        LOG(INFO) << \"Skip deleting partitions for partial update\";\n        return true;\n    }\n\n    std::vector<std::string> partitions_to_delete;\n    // Don't delete partitions in groups where the group name doesn't have target_suffix,\n    // e.g. default.\n    for (auto* existing_partition : ListPartitionsWithSuffix(builder_, target_suffix_)) {\n        auto iter = std::find_if(partitions_.begin(), partitions_.end(),\n                                 [existing_partition](auto&& partition_update) {\n                                     return partition_update.name == existing_partition->name();\n                                 });\n        // Update package metadata doesn't have this partition. Prepare to delete it.\n        // Not deleting from builder_ yet because it may break ListPartitionsWithSuffix if it were\n        // to return an iterable view of builder_.\n        if (iter == partitions_.end()) {\n            partitions_to_delete.push_back(existing_partition->name());\n        }\n    }\n\n    for (const auto& partition_name : partitions_to_delete) {\n        builder_->RemovePartition(partition_name);\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::MovePartitionsToDefault() const {\n    for (const auto& partition_update : partitions_) {\n        auto* existing_partition = builder_->FindPartition(partition_update.name);\n        if (existing_partition == nullptr) {\n            continue;\n        }\n        if (existing_partition->group_name() == partition_update.group_name) {\n            continue;\n        }\n        // Move to \"default\" group (which doesn't have maximum size constraint)\n        // temporarily.\n        if (!builder_->ChangePartitionGroup(existing_partition, android::fs_mgr::kDefaultGroup)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::ShrinkGroups() const {\n    for (const auto& group_update : groups_) {\n        auto* existing_group = builder_->FindGroup(group_update.name);\n        if (existing_group == nullptr) {\n            continue;\n        }\n        if (existing_group->maximum_size() <= group_update->size()) {\n            continue;\n        }\n        if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::DeleteGroups() const {\n    if (partial_update_) {\n        LOG(INFO) << \"Skip deleting groups for partial update\";\n        return true;\n    }\n\n    std::vector<std::string> existing_groups = builder_->ListGroups();\n    for (const auto& existing_group_name : existing_groups) {\n        // Don't delete groups without target suffix, e.g. default.\n        if (!android::base::EndsWith(existing_group_name, target_suffix_)) {\n            continue;\n        }\n\n        auto iter = std::find_if(groups_.begin(), groups_.end(),\n                                 [&existing_group_name](auto&& group_update) {\n                                     return group_update.name == existing_group_name;\n                                 });\n        // Update package metadata has this group as well, so not deleting it.\n        if (iter != groups_.end()) {\n            continue;\n        }\n        // Update package metadata doesn't have this group. Before deleting it, check that it\n        // doesn't have any partitions left. Update metadata shouldn't assign any partitions to\n        // this group, so all partitions that originally belong to this group should be moved by\n        // MovePartitionsToDefault at this point.\n        auto existing_partitions_in_group = builder_->ListPartitionsInGroup(existing_group_name);\n        if (!existing_partitions_in_group.empty()) {\n            std::vector<std::string> partition_names_in_group;\n            std::transform(existing_partitions_in_group.begin(), existing_partitions_in_group.end(),\n                           std::back_inserter(partition_names_in_group),\n                           [](auto* p) { return p->name(); });\n            LOG(ERROR)\n                    << \"Group \" << existing_group_name\n                    << \" cannot be deleted because the following partitions are left unassigned: [\"\n                    << android::base::Join(partition_names_in_group, \",\") << \"]\";\n            return false;\n        }\n        builder_->RemoveGroupAndPartitions(existing_group_name);\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::AddGroups() const {\n    for (const auto& group_update : groups_) {\n        if (builder_->FindGroup(group_update.name) == nullptr) {\n            if (!builder_->AddGroup(group_update.name, group_update->size())) {\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::GrowGroups() const {\n    for (const auto& group_update : groups_) {\n        auto* existing_group = builder_->FindGroup(group_update.name);\n        if (existing_group == nullptr) {\n            continue;\n        }\n        if (existing_group->maximum_size() >= group_update->size()) {\n            continue;\n        }\n        if (!builder_->ChangeGroupSize(existing_group->name(), group_update->size())) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::AddPartitions() const {\n    for (const auto& partition_update : partitions_) {\n        if (builder_->FindPartition(partition_update.name) == nullptr) {\n            auto* p =\n                    builder_->AddPartition(partition_update.name, partition_update.group_name,\n                                           LP_PARTITION_ATTR_READONLY | LP_PARTITION_ATTR_UPDATED);\n            if (p == nullptr) {\n                return false;\n            }\n        }\n    }\n    // Will be resized in GrowPartitions.\n    return true;\n}\n\nbool SnapshotMetadataUpdater::GrowPartitions() const {\n    for (const auto& partition_update : partitions_) {\n        auto* existing_partition = builder_->FindPartition(partition_update.name);\n        if (existing_partition == nullptr) {\n            continue;\n        }\n        auto new_size = partition_update->new_partition_info().size();\n        if (existing_partition->size() >= new_size) {\n            continue;\n        }\n        if (!builder_->ResizePartition(existing_partition, new_size)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::MovePartitionsToCorrectGroup() const {\n    for (const auto& partition_update : partitions_) {\n        auto* existing_partition = builder_->FindPartition(partition_update.name);\n        if (existing_partition == nullptr) {\n            continue;\n        }\n        if (existing_partition->group_name() == partition_update.group_name) {\n            continue;\n        }\n        if (!builder_->ChangePartitionGroup(existing_partition, partition_update.group_name)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool SnapshotMetadataUpdater::Update() const {\n    // Remove extents used by COW devices by removing the COW group completely.\n    builder_->RemoveGroupAndPartitions(android::snapshot::kCowGroupName);\n\n    // The order of these operations are important so that we\n    // always have enough space to grow or add new partitions / groups.\n    // clang-format off\n    return ShrinkPartitions() &&\n           DeletePartitions() &&\n           MovePartitionsToDefault() &&\n           ShrinkGroups() &&\n           DeleteGroups() &&\n           AddGroups() &&\n           GrowGroups() &&\n           AddPartitions() &&\n           GrowPartitions() &&\n           MovePartitionsToCorrectGroup();\n    // clang-format on\n}\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshot_metadata_updater.h",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <stdint.h>\n\n#include <string>\n#include <vector>\n\n#include <liblp/builder.h>\n#include <update_engine/update_metadata.pb.h>\n\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\n// Helper class that modifies a super partition metadata for an update for\n// Virtual A/B devices.\nclass SnapshotMetadataUpdater {\n    using DeltaArchiveManifest = chromeos_update_engine::DeltaArchiveManifest;\n    using DynamicPartitionMetadata = chromeos_update_engine::DynamicPartitionMetadata;\n    using DynamicPartitionGroup = chromeos_update_engine::DynamicPartitionGroup;\n    using PartitionUpdate = chromeos_update_engine::PartitionUpdate;\n\n  public:\n    // Caller is responsible for ensuring the lifetime of manifest to be longer\n    // than SnapshotMetadataUpdater.\n    SnapshotMetadataUpdater(android::fs_mgr::MetadataBuilder* builder, uint32_t target_slot,\n                            const DeltaArchiveManifest& manifest);\n    bool Update() const;\n\n  private:\n    bool RenameGroupSuffix() const;\n    bool ShrinkPartitions() const;\n    bool DeletePartitions() const;\n    bool MovePartitionsToDefault() const;\n    bool ShrinkGroups() const;\n    bool DeleteGroups() const;\n    bool AddGroups() const;\n    bool GrowGroups() const;\n    bool AddPartitions() const;\n    bool GrowPartitions() const;\n    bool MovePartitionsToCorrectGroup() const;\n\n    // Wraps a DynamicPartitionGroup with a slot-suffixed name. Always use\n    // .name instead of ->name() because .name has the slot suffix (e.g.\n    // .name is \"group_b\" and ->name() is \"group\".)\n    struct Group {\n        std::string name;\n        const DynamicPartitionGroup* group;\n        const DynamicPartitionGroup* operator->() const { return group; }\n    };\n    // Wraps a PartitionUpdate with a slot-suffixed name / group name. Always use\n    // .name instead of ->partition_name() because .name has the slot suffix (e.g.\n    // .name is \"system_b\" and ->partition_name() is \"system\".)\n    struct Partition {\n        std::string name;\n        std::string group_name;\n        const PartitionUpdate* partition;\n        const PartitionUpdate* operator->() const { return partition; }\n    };\n\n    android::fs_mgr::MetadataBuilder* const builder_;\n    const std::string target_suffix_;\n    std::vector<Group> groups_;\n    std::vector<Partition> partitions_;\n    bool partial_update_{false};\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshot_metadata_updater_test.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"snapshot_metadata_updater.h\"\n\n#include <memory>\n#include <string>\n\n#include <android-base/properties.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <liblp/builder.h>\n#include <storage_literals/storage_literals.h>\n\n#include <libsnapshot/test_helpers.h>\n\nusing namespace android::storage_literals;\nusing android::fs_mgr::LpMetadata;\nusing android::fs_mgr::MetadataBuilder;\nusing android::fs_mgr::SlotSuffixForSlotNumber;\nusing chromeos_update_engine::DeltaArchiveManifest;\nusing chromeos_update_engine::DynamicPartitionGroup;\nusing chromeos_update_engine::PartitionUpdate;\nusing testing::AssertionFailure;\nusing testing::AssertionResult;\nusing testing::AssertionSuccess;\n\nnamespace android {\nnamespace snapshot {\n\nclass SnapshotMetadataUpdaterTest : public ::testing::TestWithParam<uint32_t> {\n  public:\n    SnapshotMetadataUpdaterTest() = default;\n\n    void SetUp() override {\n        SKIP_IF_NON_VIRTUAL_AB();\n\n        target_slot_ = GetParam();\n        target_suffix_ = SlotSuffixForSlotNumber(target_slot_);\n        SnapshotTestPropertyFetcher::SetUp(SlotSuffixForSlotNumber(1 - target_slot_));\n        builder_ = MetadataBuilder::New(4_GiB + 1_MiB, 4_KiB, 2);\n\n        group_ = manifest_.mutable_dynamic_partition_metadata()->add_groups();\n        group_->set_name(\"group\");\n        group_->set_size(4_GiB);\n        group_->add_partition_names(\"system\");\n        group_->add_partition_names(\"vendor\");\n        system_ = manifest_.add_partitions();\n        system_->set_partition_name(\"system\");\n        SetSize(system_, 2_GiB);\n        vendor_ = manifest_.add_partitions();\n        vendor_->set_partition_name(\"vendor\");\n        SetSize(vendor_, 1_GiB);\n\n        ASSERT_TRUE(FillFakeMetadata(builder_.get(), manifest_, target_suffix_));\n    }\n\n    void TearDown() override {\n        RETURN_IF_NON_VIRTUAL_AB();\n\n        SnapshotTestPropertyFetcher::TearDown();\n    }\n\n    // Append suffix to name.\n    std::string T(std::string_view name) { return std::string(name) + target_suffix_; }\n\n    AssertionResult UpdateAndExport() {\n        SnapshotMetadataUpdater updater(builder_.get(), target_slot_, manifest_);\n        if (!updater.Update()) {\n            return AssertionFailure() << \"Update failed.\";\n        }\n\n        exported_ = builder_->Export();\n        if (exported_ == nullptr) {\n            return AssertionFailure() << \"Export failed.\";\n        }\n        return AssertionSuccess();\n    }\n\n    // Check that in |builder_|, partition |name| + |target_suffix_| has the given |size|.\n    AssertionResult CheckSize(std::string_view name, uint64_t size) {\n        auto p = builder_->FindPartition(T(name));\n        if (p == nullptr) {\n            return AssertionFailure() << \"Cannot find partition \" << T(name);\n        }\n        if (p->size() != size) {\n            return AssertionFailure() << \"Partition \" << T(name) << \" should be \" << size\n                                      << \" bytes, but is \" << p->size() << \" bytes.\";\n        }\n        return AssertionSuccess() << \"Partition\" << T(name) << \" is \" << size << \" bytes.\";\n    }\n\n    // Check that in |builder_|, group |name| + |target_suffix_| has the given |size|.\n    AssertionResult CheckGroupSize(std::string_view name, uint64_t size) {\n        auto g = builder_->FindGroup(T(name));\n        if (g == nullptr) {\n            return AssertionFailure() << \"Cannot find group \" << T(name);\n        }\n        if (g->maximum_size() != size) {\n            return AssertionFailure() << \"Group \" << T(name) << \" should be \" << size\n                                      << \" bytes, but is \" << g->maximum_size() << \" bytes.\";\n        }\n        return AssertionSuccess() << \"Group\" << T(name) << \" is \" << size << \" bytes.\";\n    }\n\n    // Check that in |builder_|, partition |partition_name| + |target_suffix_| is in group\n    // |group_name| + |target_suffix_|;\n    AssertionResult CheckGroupName(std::string_view partition_name, std::string_view group_name) {\n        auto p = builder_->FindPartition(T(partition_name));\n        if (p == nullptr) {\n            return AssertionFailure() << \"Cannot find partition \" << T(partition_name);\n        }\n        if (p->group_name() != T(group_name)) {\n            return AssertionFailure() << \"Partition \" << T(partition_name) << \" should be in \"\n                                      << T(group_name) << \", but is in \" << p->group_name() << \".\";\n        }\n        return AssertionSuccess() << \"Partition\" << T(partition_name) << \" is in \" << T(group_name)\n                                  << \".\";\n    }\n\n    std::unique_ptr<MetadataBuilder> builder_;\n    uint32_t target_slot_;\n    std::string target_suffix_;\n    DeltaArchiveManifest manifest_;\n    std::unique_ptr<LpMetadata> exported_;\n    DynamicPartitionGroup* group_ = nullptr;\n    PartitionUpdate* system_ = nullptr;\n    PartitionUpdate* vendor_ = nullptr;\n};\n\nTEST_P(SnapshotMetadataUpdaterTest, NoChange) {\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckGroupSize(\"group\", 4_GiB));\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB));\n    EXPECT_TRUE(CheckGroupName(\"system\", \"group\"));\n    EXPECT_TRUE(CheckSize(\"vendor\", 1_GiB));\n    EXPECT_TRUE(CheckGroupName(\"vendor\", \"group\"));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, GrowWithinBounds) {\n    SetSize(system_, 2_GiB + 512_MiB);\n    SetSize(vendor_, 1_GiB + 512_MiB);\n\n    ASSERT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB + 512_MiB));\n    EXPECT_TRUE(CheckSize(\"vendor\", 1_GiB + 512_MiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, GrowOverSuper) {\n    SetSize(system_, 3_GiB);\n    SetSize(vendor_, 1_GiB + 512_MiB);\n\n    EXPECT_FALSE(UpdateAndExport());\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, GrowOverGroup) {\n    SetSize(system_, 3_GiB);\n    SetSize(vendor_, 1_GiB + 4_KiB);\n\n    EXPECT_FALSE(UpdateAndExport());\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, Add) {\n    group_->add_partition_names(\"product\");\n    auto product = manifest_.add_partitions();\n    product->set_partition_name(\"product\");\n    SetSize(product, 1_GiB);\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB));\n    EXPECT_TRUE(CheckSize(\"vendor\", 1_GiB));\n    EXPECT_TRUE(CheckSize(\"product\", 1_GiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, AddTooBig) {\n    group_->add_partition_names(\"product\");\n    auto product = manifest_.add_partitions();\n    product->set_partition_name(\"product\");\n    SetSize(product, 1_GiB + 4_KiB);\n\n    EXPECT_FALSE(UpdateAndExport());\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, ShrinkAll) {\n    SetSize(system_, 1_GiB);\n    SetSize(vendor_, 512_MiB);\n\n    ASSERT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 1_GiB));\n    EXPECT_TRUE(CheckSize(\"vendor\", 512_MiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, ShrinkAndGrow) {\n    SetSize(system_, 3_GiB + 512_MiB);\n    SetSize(vendor_, 512_MiB);\n\n    ASSERT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 3_GiB + 512_MiB));\n    EXPECT_TRUE(CheckSize(\"vendor\", 512_MiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, ShrinkAndAdd) {\n    SetSize(system_, 2_GiB);\n    SetSize(vendor_, 512_MiB);\n    group_->add_partition_names(\"product\");\n    auto product = manifest_.add_partitions();\n    product->set_partition_name(\"product\");\n    SetSize(product, 1_GiB + 512_MiB);\n\n    ASSERT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB));\n    EXPECT_TRUE(CheckSize(\"vendor\", 512_MiB));\n    EXPECT_TRUE(CheckSize(\"product\", 1_GiB + 512_MiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, Delete) {\n    group_->mutable_partition_names()->RemoveLast();\n    // No need to delete it from manifest.partitions as SnapshotMetadataUpdater\n    // should ignore them (treat them as static partitions).\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB));\n    EXPECT_EQ(nullptr, builder_->FindPartition(T(\"vendor\")));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, DeleteAndGrow) {\n    group_->mutable_partition_names()->RemoveLast();\n    SetSize(system_, 4_GiB);\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 4_GiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, DeleteAndAdd) {\n    group_->mutable_partition_names()->RemoveLast();\n    group_->add_partition_names(\"product\");\n    auto product = manifest_.add_partitions();\n    product->set_partition_name(\"product\");\n    SetSize(product, 2_GiB);\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB));\n    EXPECT_EQ(nullptr, builder_->FindPartition(T(\"vendor\")));\n    EXPECT_TRUE(CheckSize(\"product\", 2_GiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, GrowGroup) {\n    group_->set_size(4_GiB + 512_KiB);\n    SetSize(system_, 2_GiB + 256_KiB);\n    SetSize(vendor_, 2_GiB + 256_KiB);\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB + 256_KiB));\n    EXPECT_TRUE(CheckSize(\"vendor\", 2_GiB + 256_KiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, ShrinkGroup) {\n    group_->set_size(1_GiB);\n    SetSize(system_, 512_MiB);\n    SetSize(vendor_, 512_MiB);\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckSize(\"system\", 512_MiB));\n    EXPECT_TRUE(CheckSize(\"vendor\", 512_MiB));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, MoveToNewGroup) {\n    group_->mutable_partition_names()->RemoveLast();\n    group_->set_size(2_GiB);\n\n    auto another_group = manifest_.mutable_dynamic_partition_metadata()->add_groups();\n    another_group->set_name(\"another_group\");\n    another_group->set_size(2_GiB);\n    another_group->add_partition_names(\"vendor\");\n    SetSize(vendor_, 2_GiB);\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_TRUE(CheckGroupSize(\"group\", 2_GiB));\n    EXPECT_TRUE(CheckGroupSize(\"another_group\", 2_GiB));\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB));\n    EXPECT_TRUE(CheckGroupName(\"system\", \"group\"));\n    EXPECT_TRUE(CheckSize(\"vendor\", 2_GiB));\n    EXPECT_TRUE(CheckGroupName(\"vendor\", \"another_group\"));\n}\n\nTEST_P(SnapshotMetadataUpdaterTest, DeleteAndAddGroup) {\n    manifest_.mutable_dynamic_partition_metadata()->mutable_groups()->RemoveLast();\n    group_ = nullptr;\n\n    auto another_group = manifest_.mutable_dynamic_partition_metadata()->add_groups();\n    another_group->set_name(\"another_group\");\n    another_group->set_size(4_GiB);\n    another_group->add_partition_names(\"system\");\n    another_group->add_partition_names(\"vendor\");\n    another_group->add_partition_names(\"product\");\n    auto product = manifest_.add_partitions();\n    product->set_partition_name(\"product\");\n    SetSize(product, 1_GiB);\n\n    EXPECT_TRUE(UpdateAndExport());\n\n    EXPECT_EQ(nullptr, builder_->FindGroup(T(\"group\")));\n    EXPECT_TRUE(CheckGroupSize(\"another_group\", 4_GiB));\n    EXPECT_TRUE(CheckSize(\"system\", 2_GiB));\n    EXPECT_TRUE(CheckGroupName(\"system\", \"another_group\"));\n    EXPECT_TRUE(CheckSize(\"vendor\", 1_GiB));\n    EXPECT_TRUE(CheckGroupName(\"vendor\", \"another_group\"));\n    EXPECT_TRUE(CheckSize(\"product\", 1_GiB));\n    EXPECT_TRUE(CheckGroupName(\"product\", \"another_group\"));\n}\n\nINSTANTIATE_TEST_SUITE_P(Snapshot, SnapshotMetadataUpdaterTest, testing::Values(0, 1));\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshot_stats.cpp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <libsnapshot/snapshot_stats.h>\n\n#include <sstream>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nSnapshotMergeStats* SnapshotMergeStats::GetInstance(SnapshotManager& parent) {\n    static std::unique_ptr<SnapshotMergeStats> g_instance;\n\n    if (!g_instance || g_instance->path_ != parent.GetMergeStateFilePath()) {\n        g_instance = std::make_unique<SnapshotMergeStats>(parent.GetMergeStateFilePath());\n    }\n    return g_instance.get();\n}\n\nSnapshotMergeStats::SnapshotMergeStats(const std::string& path) : path_(path), running_(false) {}\n\nbool SnapshotMergeStats::ReadState() {\n    std::string contents;\n    if (!android::base::ReadFileToString(path_, &contents)) {\n        PLOG(INFO) << \"Read merge statistics file failed\";\n        return false;\n    }\n    if (!report_.ParseFromString(contents)) {\n        LOG(ERROR) << \"Unable to parse merge statistics file as SnapshotMergeReport\";\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotMergeStats::WriteState() {\n    std::string contents;\n    if (!report_.SerializeToString(&contents)) {\n        LOG(ERROR) << \"Unable to serialize SnapshotMergeStats.\";\n        return false;\n    }\n    if (!WriteStringToFileAtomic(contents, path_)) {\n        PLOG(ERROR) << \"Could not write to merge statistics file\";\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotMergeStats::DeleteState() {\n    std::string error;\n    if (!android::base::RemoveFileIfExists(path_, &error)) {\n        LOG(ERROR) << \"Failed to remove merge statistics file \" << path_ << \": \" << error;\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotMergeStats::Start() {\n    if (running_) {\n        LOG(ERROR) << \"SnapshotMergeStats running_ == \" << running_;\n        return false;\n    }\n    running_ = true;\n\n    start_time_ = std::chrono::steady_clock::now();\n    if (ReadState()) {\n        report_.set_resume_count(report_.resume_count() + 1);\n    } else {\n        report_.set_resume_count(0);\n        report_.set_state(UpdateState::None);\n    }\n\n    return WriteState();\n}\n\nvoid SnapshotMergeStats::set_state(android::snapshot::UpdateState state) {\n    report_.set_state(state);\n}\n\nuint64_t SnapshotMergeStats::cow_file_size() {\n    return report_.cow_file_size();\n}\n\nuint64_t SnapshotMergeStats::total_cow_size_bytes() {\n    return report_.total_cow_size_bytes();\n}\n\nuint64_t SnapshotMergeStats::estimated_cow_size_bytes() {\n    return report_.estimated_cow_size_bytes();\n}\n\nvoid SnapshotMergeStats::set_boot_complete_time_ms(uint32_t ms) {\n    report_.set_boot_complete_time_ms(ms);\n}\n\nuint32_t SnapshotMergeStats::boot_complete_time_ms() {\n    return report_.boot_complete_time_ms();\n}\n\nvoid SnapshotMergeStats::set_boot_complete_to_merge_start_time_ms(uint32_t ms) {\n    report_.set_boot_complete_to_merge_start_time_ms(ms);\n}\n\nuint32_t SnapshotMergeStats::boot_complete_to_merge_start_time_ms() {\n    return report_.boot_complete_to_merge_start_time_ms();\n}\n\nvoid SnapshotMergeStats::set_merge_failure_code(MergeFailureCode code) {\n    report_.set_merge_failure_code(code);\n}\n\nMergeFailureCode SnapshotMergeStats::merge_failure_code() {\n    return report_.merge_failure_code();\n}\n\nvoid SnapshotMergeStats::set_source_build_fingerprint(const std::string& fingerprint) {\n    report_.set_source_build_fingerprint(fingerprint);\n}\n\nstd::string SnapshotMergeStats::source_build_fingerprint() {\n    return report_.source_build_fingerprint();\n}\n\nclass SnapshotMergeStatsResultImpl : public SnapshotMergeStats::Result {\n  public:\n    SnapshotMergeStatsResultImpl(const SnapshotMergeReport& report,\n                                 std::chrono::steady_clock::duration merge_time)\n        : report_(report), merge_time_(merge_time) {}\n    const SnapshotMergeReport& report() const override { return report_; }\n    std::chrono::steady_clock::duration merge_time() const override { return merge_time_; }\n\n  private:\n    SnapshotMergeReport report_;\n    std::chrono::steady_clock::duration merge_time_;\n};\n\nstd::unique_ptr<SnapshotMergeStats::Result> SnapshotMergeStats::Finish() {\n    if (!running_) {\n        LOG(ERROR) << \"SnapshotMergeStats running_ == \" << running_;\n        return nullptr;\n    }\n    running_ = false;\n\n    auto result = std::make_unique<SnapshotMergeStatsResultImpl>(\n            report_, std::chrono::steady_clock::now() - start_time_);\n\n    // We still want to report result if state is not deleted. Just leave\n    // it there and move on. A side effect is that it may be reported over and\n    // over again in the future, but there is nothing we can do.\n    (void)DeleteState();\n\n    return result;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshot_stub.cpp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <libsnapshot/snapshot_stub.h>\n\n#include <android-base/logging.h>\n\n#include <libsnapshot/snapshot_stats.h>\n\nusing android::fs_mgr::CreateLogicalPartitionParams;\nusing chromeos_update_engine::DeltaArchiveManifest;\nusing chromeos_update_engine::FileDescriptor;\n\nnamespace android::snapshot {\n\nstd::unique_ptr<ISnapshotManager> SnapshotManagerStub::New() {\n    return std::make_unique<SnapshotManagerStub>();\n}\n\nbool SnapshotManagerStub::BeginUpdate() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::CancelUpdate() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::FinishedSnapshotWrites(bool) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::InitiateMerge() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nUpdateState SnapshotManagerStub::ProcessUpdateState(const std::function<bool()>&,\n                                                    const std::function<bool()>&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return UpdateState::None;\n}\n\nUpdateState SnapshotManagerStub::GetUpdateState(double*) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return UpdateState::None;\n}\n\nReturn SnapshotManagerStub::CreateUpdateSnapshots(const DeltaArchiveManifest&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return Return::Error();\n}\n\nbool SnapshotManagerStub::MapUpdateSnapshot(const CreateLogicalPartitionParams&, std::string*) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::UnmapUpdateSnapshot(const std::string&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::NeedSnapshotsInFirstStageMount() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::CreateLogicalAndSnapshotPartitions(const std::string&,\n                                                             const std::chrono::milliseconds&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::HandleImminentDataWipe(const std::function<void()>&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::FinishMergeInRecovery() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nCreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return CreateResult::ERROR;\n}\n\nCreateResult SnapshotManagerStub::RecoveryCreateSnapshotDevices(\n        const std::unique_ptr<AutoDevice>&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return CreateResult::ERROR;\n}\n\nbool SnapshotManagerStub::Dump(std::ostream&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nstd::unique_ptr<AutoDevice> SnapshotManagerStub::EnsureMetadataMounted() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return nullptr;\n}\n\nbool SnapshotManagerStub::UpdateUsesCompression() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::UpdateUsesUserSnapshots() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nclass SnapshotMergeStatsStub : public ISnapshotMergeStats {\n    bool Start() override { return false; }\n    void set_state(android::snapshot::UpdateState) override {}\n    uint64_t cow_file_size() override { return 0; }\n    std::unique_ptr<Result> Finish() override { return nullptr; }\n    uint64_t total_cow_size_bytes() override { return 0; }\n    uint64_t estimated_cow_size_bytes() override { return 0; }\n    void set_boot_complete_time_ms(uint32_t) override {}\n    uint32_t boot_complete_time_ms() override { return 0; }\n    void set_boot_complete_to_merge_start_time_ms(uint32_t) override {}\n    uint32_t boot_complete_to_merge_start_time_ms() override { return 0; }\n    void set_merge_failure_code(MergeFailureCode) override {}\n    MergeFailureCode merge_failure_code() override { return MergeFailureCode::Ok; }\n    void set_source_build_fingerprint(const std::string&) override {}\n    std::string source_build_fingerprint() override { return {}; }\n    bool WriteState() override { return false; }\n    SnapshotMergeReport* report() override { return &report_; }\n\n  private:\n    SnapshotMergeReport report_;\n};\n\nISnapshotMergeStats* SnapshotManagerStub::GetSnapshotMergeStatsInstance() {\n    static SnapshotMergeStatsStub snapshot_merge_stats;\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return &snapshot_merge_stats;\n}\n\nstd::unique_ptr<ICowWriter> SnapshotManagerStub::OpenSnapshotWriter(\n        const CreateLogicalPartitionParams&, std::optional<uint64_t>) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return nullptr;\n}\n\nbool SnapshotManagerStub::MapAllSnapshots(const std::chrono::milliseconds&) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nbool SnapshotManagerStub::UnmapAllSnapshots() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\nvoid SnapshotManagerStub::UpdateCowStats(ISnapshotMergeStats*) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n}\n\nauto SnapshotManagerStub::ReadMergeFailureCode() -> MergeFailureCode {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return MergeFailureCode::Ok;\n}\n\nstd::string SnapshotManagerStub::ReadSourceBuildFingerprint() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return {};\n}\n\nvoid SnapshotManagerStub::SetMergeStatsFeatures(ISnapshotMergeStats*) {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n}\n\nbool SnapshotManagerStub::IsCancelUpdateSafe() {\n    LOG(ERROR) << __FUNCTION__ << \" should never be called.\";\n    return false;\n}\n\n}  // namespace android::snapshot\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshot_test.cpp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/snapshot.h>\n\n#include <fcntl.h>\n#include <signal.h>\n#include <sys/file.h>\n#include <sys/stat.h>\n#include <sys/statvfs.h>\n#include <sys/types.h>\n\n#include <chrono>\n#include <deque>\n#include <future>\n#include <iostream>\n\n#include <aidl/android/hardware/boot/MergeStatus.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <fs_mgr/file_wait.h>\n#include <fs_mgr/roots.h>\n#include <fs_mgr_dm_linear.h>\n#include <gflags/gflags.h>\n#include <gtest/gtest.h>\n#include <libdm/dm.h>\n#include <libfiemap/image_manager.h>\n#include <liblp/builder.h>\n#include <openssl/sha.h>\n#include <storage_literals/storage_literals.h>\n\n#include <android/snapshot/snapshot.pb.h>\n#include <libsnapshot/test_helpers.h>\n#include \"partition_cow_creator.h\"\n#include \"scratch_super.h\"\n#include \"utility.h\"\n\n// Mock classes are not used. Header included to ensure mocked class definition aligns with the\n// class itself.\n#include <libsnapshot/mock_device_info.h>\n#include <libsnapshot/mock_snapshot.h>\n\n#if defined(LIBSNAPSHOT_TEST_VAB_LEGACY)\n#define DEFAULT_MODE \"vab-legacy\"\n#else\n#define DEFAULT_MODE \"\"\n#endif\n\nDEFINE_string(force_mode, DEFAULT_MODE,\n              \"Force testing older modes (vab-legacy) ignoring device config.\");\nDEFINE_string(force_iouring_disable, \"\",\n              \"Force testing mode (iouring_disabled) - disable io_uring\");\nDEFINE_string(compression_method, \"gz\", \"Default compression algorithm.\");\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\nusing android::dm::DeviceMapper;\nusing android::dm::DmDeviceState;\nusing android::dm::IDeviceMapper;\nusing android::fiemap::FiemapStatus;\nusing android::fiemap::IImageManager;\nusing android::fs_mgr::BlockDeviceInfo;\nusing android::fs_mgr::CreateLogicalPartitionParams;\nusing android::fs_mgr::DestroyLogicalPartition;\nusing android::fs_mgr::EnsurePathMounted;\nusing android::fs_mgr::EnsurePathUnmounted;\nusing android::fs_mgr::Extent;\nusing android::fs_mgr::Fstab;\nusing android::fs_mgr::GetPartitionGroupName;\nusing android::fs_mgr::GetPartitionName;\nusing android::fs_mgr::Interval;\nusing android::fs_mgr::MetadataBuilder;\nusing android::fs_mgr::SlotSuffixForSlotNumber;\nusing chromeos_update_engine::DeltaArchiveManifest;\nusing chromeos_update_engine::DynamicPartitionGroup;\nusing chromeos_update_engine::PartitionUpdate;\nusing namespace ::testing;\nusing namespace android::storage_literals;\nusing namespace std::chrono_literals;\nusing namespace std::string_literals;\n\n// Global states. See test_helpers.h.\nstd::unique_ptr<SnapshotManager> sm;\nTestDeviceInfo* test_device = nullptr;\nstd::string fake_super;\n\nvoid MountMetadata();\n\n// @VsrTest = 3.7.6\nclass SnapshotTest : public ::testing::Test {\n  public:\n    SnapshotTest() : dm_(DeviceMapper::Instance()) {}\n\n    // This is exposed for main.\n    void Cleanup() {\n        InitializeState();\n        CleanupTestArtifacts();\n    }\n\n  protected:\n    void SetUp() override {\n        const testing::TestInfo* const test_info =\n                testing::UnitTest::GetInstance()->current_test_info();\n        test_name_ = test_info->test_suite_name() + \"/\"s + test_info->name();\n\n        LOG(INFO) << \"Starting test: \" << test_name_;\n\n        SKIP_IF_NON_VIRTUAL_AB();\n        SKIP_IF_VENDOR_ON_ANDROID_S();\n\n        SetupProperties();\n        if (!DeviceSupportsMode()) {\n            GTEST_SKIP() << \"Mode not supported on this device\";\n        }\n\n        InitializeState();\n        CleanupTestArtifacts();\n        FormatFakeSuper();\n        MountMetadata();\n        ASSERT_TRUE(sm->BeginUpdate());\n    }\n\n    void SetupProperties() {\n        std::unordered_map<std::string, std::string> properties;\n\n        ASSERT_TRUE(android::base::SetProperty(\"snapuserd.test.io_uring.force_disable\", \"0\"))\n                << \"Failed to set property: snapuserd.test.io_uring.disabled\";\n\n        if (FLAGS_force_mode == \"vab-legacy\") {\n            properties[\"ro.virtual_ab.compression.enabled\"] = \"false\";\n            properties[\"ro.virtual_ab.userspace.snapshots.enabled\"] = \"false\";\n        }\n\n        if (FLAGS_force_iouring_disable == \"iouring_disabled\") {\n            ASSERT_TRUE(android::base::SetProperty(\"snapuserd.test.io_uring.force_disable\", \"1\"))\n                    << \"Failed to set property: snapuserd.test.io_uring.disabled\";\n            properties[\"ro.virtual_ab.io_uring.enabled\"] = \"false\";\n        }\n\n        auto fetcher = std::make_unique<SnapshotTestPropertyFetcher>(\"_a\", std::move(properties));\n        IPropertyFetcher::OverrideForTesting(std::move(fetcher));\n\n        if (GetLegacyCompressionEnabledProperty() || CanUseUserspaceSnapshots()) {\n            // If we're asked to test the device's actual configuration, then it\n            // may be misconfigured, so check for kernel support as libsnapshot does.\n            if (FLAGS_force_mode.empty()) {\n                snapuserd_required_ = KernelSupportsCompressedSnapshots();\n            } else {\n                snapuserd_required_ = true;\n            }\n        }\n    }\n\n    void TearDown() override {\n        RETURN_IF_NON_VIRTUAL_AB();\n        RETURN_IF_VENDOR_ON_ANDROID_S();\n\n        LOG(INFO) << \"Tearing down SnapshotTest test: \" << test_name_;\n\n        lock_ = nullptr;\n\n        CleanupTestArtifacts();\n        SnapshotTestPropertyFetcher::TearDown();\n\n        LOG(INFO) << \"Teardown complete for test: \" << test_name_;\n    }\n\n    bool DeviceSupportsMode() {\n        if (FLAGS_force_mode.empty()) {\n            return true;\n        }\n        if (snapuserd_required_ && !KernelSupportsCompressedSnapshots()) {\n            return false;\n        }\n        return true;\n    }\n\n    bool ShouldSkipLegacyMerging() {\n        if (!GetLegacyCompressionEnabledProperty() || !snapuserd_required_) {\n            return false;\n        }\n        int api_level = android::base::GetIntProperty(\"ro.board.api_level\", -1);\n        if (api_level == -1) {\n            api_level = android::base::GetIntProperty(\"ro.product.first_api_level\", -1);\n        }\n        return api_level != __ANDROID_API_S__;\n    }\n\n    void InitializeState() {\n        ASSERT_TRUE(sm->EnsureImageManager());\n        image_manager_ = sm->image_manager();\n\n        test_device->set_slot_suffix(\"_a\");\n\n        sm->set_use_first_stage_snapuserd(false);\n    }\n\n    void CleanupTestArtifacts() {\n        // Normally cancelling inside a merge is not allowed. Since these\n        // are tests, we don't care, destroy everything that might exist.\n        // Note we hardcode this list because of an annoying quirk: when\n        // completing a merge, the snapshot stops existing, so we can't\n        // get an accurate list to remove.\n        lock_ = nullptr;\n\n        // If there is no image manager, the test was skipped.\n        if (!image_manager_) {\n            return;\n        }\n\n        std::vector<std::string> snapshots = {\"test-snapshot\", \"test_partition_a\",\n                                              \"test_partition_b\"};\n        for (const auto& snapshot : snapshots) {\n            CleanupSnapshotArtifacts(snapshot);\n        }\n\n        // Remove stale partitions in fake super.\n        std::vector<std::string> partitions = {\n                \"base-device\",\n                \"test_partition_b\",\n                \"test_partition_b-base\",\n                \"test_partition_b-cow\",\n        };\n        for (const auto& partition : partitions) {\n            DeleteDevice(partition);\n        }\n\n        if (sm->GetUpdateState() != UpdateState::None) {\n            auto state_file = sm->GetStateFilePath();\n            unlink(state_file.c_str());\n        }\n    }\n\n    void CleanupSnapshotArtifacts(const std::string& snapshot) {\n        // The device-mapper stack may have been collapsed to dm-linear, so it's\n        // necessary to check what state it's in before attempting a cleanup.\n        // SnapshotManager has no path like this because we'd never remove a\n        // merged snapshot (a live partition).\n        bool is_dm_user = false;\n        DeviceMapper::TargetInfo target;\n        if (sm->IsSnapshotDevice(snapshot, &target)) {\n            is_dm_user = (DeviceMapper::GetTargetType(target.spec) == \"user\");\n        }\n\n        if (is_dm_user) {\n            ASSERT_TRUE(sm->EnsureSnapuserdConnected());\n            ASSERT_TRUE(AcquireLock());\n\n            auto local_lock = std::move(lock_);\n            ASSERT_TRUE(sm->UnmapUserspaceSnapshotDevice(local_lock.get(), snapshot));\n        }\n\n        ASSERT_TRUE(DeleteSnapshotDevice(snapshot));\n        DeleteBackingImage(image_manager_, snapshot + \"-cow-img\");\n\n        auto status_file = sm->GetSnapshotStatusFilePath(snapshot);\n        android::base::RemoveFileIfExists(status_file);\n    }\n\n    bool AcquireLock() {\n        lock_ = sm->LockExclusive();\n        return !!lock_;\n    }\n\n    // This is so main() can instantiate this to invoke Cleanup.\n    virtual void TestBody() override {}\n\n    void FormatFakeSuper() {\n        BlockDeviceInfo super_device(\"super\", kSuperSize, 0, 0, 4096);\n        std::vector<BlockDeviceInfo> devices = {super_device};\n\n        auto builder = MetadataBuilder::New(devices, \"super\", 65536, 2);\n        ASSERT_NE(builder, nullptr);\n\n        auto metadata = builder->Export();\n        ASSERT_NE(metadata, nullptr);\n\n        TestPartitionOpener opener(fake_super);\n        ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *metadata.get()));\n    }\n\n    // If |path| is non-null, the partition will be mapped after creation.\n    bool CreatePartition(const std::string& name, uint64_t size, std::string* path = nullptr,\n                         const std::optional<std::string> group = {}) {\n        TestPartitionOpener opener(fake_super);\n        auto builder = MetadataBuilder::New(opener, \"super\", 0);\n        if (!builder) return false;\n\n        std::string partition_group = std::string(android::fs_mgr::kDefaultGroup);\n        if (group) {\n            partition_group = *group;\n        }\n        return CreatePartition(builder.get(), name, size, path, partition_group);\n    }\n\n    bool CreatePartition(MetadataBuilder* builder, const std::string& name, uint64_t size,\n                         std::string* path, const std::string& group) {\n        auto partition = builder->AddPartition(name, group, 0);\n        if (!partition) return false;\n        if (!builder->ResizePartition(partition, size)) {\n            return false;\n        }\n\n        // Update the source slot.\n        auto metadata = builder->Export();\n        if (!metadata) return false;\n\n        TestPartitionOpener opener(fake_super);\n        if (!UpdatePartitionTable(opener, \"super\", *metadata.get(), 0)) {\n            return false;\n        }\n\n        if (!path) return true;\n\n        CreateLogicalPartitionParams params = {\n                .block_device = fake_super,\n                .metadata = metadata.get(),\n                .partition_name = name,\n                .force_writable = true,\n                .timeout_ms = 10s,\n        };\n        return CreateLogicalPartition(params, path);\n    }\n\n    AssertionResult MapUpdateSnapshot(const std::string& name,\n                                      std::unique_ptr<ICowWriter>* writer) {\n        TestPartitionOpener opener(fake_super);\n        CreateLogicalPartitionParams params{\n                .block_device = fake_super,\n                .metadata_slot = 1,\n                .partition_name = name,\n                .timeout_ms = 10s,\n                .partition_opener = &opener,\n        };\n\n        auto result = sm->OpenSnapshotWriter(params, {});\n        if (!result) {\n            return AssertionFailure() << \"Cannot open snapshot for writing: \" << name;\n        }\n\n        if (writer) {\n            *writer = std::move(result);\n        }\n        return AssertionSuccess();\n    }\n\n    AssertionResult MapUpdateSnapshot(const std::string& name, std::string* path) {\n        TestPartitionOpener opener(fake_super);\n        CreateLogicalPartitionParams params{\n                .block_device = fake_super,\n                .metadata_slot = 1,\n                .partition_name = name,\n                .timeout_ms = 10s,\n                .partition_opener = &opener,\n        };\n\n        auto result = sm->MapUpdateSnapshot(params, path);\n        if (!result) {\n            return AssertionFailure() << \"Cannot open snapshot for writing: \" << name;\n        }\n        return AssertionSuccess();\n    }\n\n    AssertionResult DeleteSnapshotDevice(const std::string& snapshot) {\n        AssertionResult res = AssertionSuccess();\n        if (!(res = DeleteDevice(snapshot))) return res;\n        if (!sm->UnmapDmUserDevice(snapshot + \"-user-cow\")) {\n            return AssertionFailure() << \"Cannot delete dm-user device for \" << snapshot;\n        }\n        if (!(res = DeleteDevice(snapshot + \"-inner\"))) return res;\n        if (!(res = DeleteDevice(snapshot + \"-cow\"))) return res;\n        if (!image_manager_->UnmapImageIfExists(snapshot + \"-cow-img\")) {\n            return AssertionFailure() << \"Cannot unmap image \" << snapshot << \"-cow-img\";\n        }\n        if (!(res = DeleteDevice(snapshot + \"-base\"))) return res;\n        if (!(res = DeleteDevice(snapshot + \"-src\"))) return res;\n        return AssertionSuccess();\n    }\n\n    AssertionResult DeleteDevice(const std::string& device) {\n        if (!sm->DeleteDeviceIfExists(device, 1s)) {\n            return AssertionFailure() << \"Can't delete \" << device;\n        }\n        return AssertionSuccess();\n    }\n\n    AssertionResult CreateCowImage(const std::string& name) {\n        if (!sm->CreateCowImage(lock_.get(), name)) {\n            return AssertionFailure() << \"Cannot create COW image \" << name;\n        }\n        std::string cow_device;\n        auto map_res = MapCowImage(name, 10s, &cow_device);\n        if (!map_res) {\n            return map_res;\n        }\n        if (!InitializeKernelCow(cow_device)) {\n            return AssertionFailure() << \"Cannot zero fill \" << cow_device;\n        }\n        if (!sm->UnmapCowImage(name)) {\n            return AssertionFailure() << \"Cannot unmap \" << name << \" after zero filling it\";\n        }\n        return AssertionSuccess();\n    }\n\n    AssertionResult MapCowImage(const std::string& name,\n                                const std::chrono::milliseconds& timeout_ms, std::string* path) {\n        auto cow_image_path = sm->MapCowImage(name, timeout_ms);\n        if (!cow_image_path.has_value()) {\n            return AssertionFailure() << \"Cannot map cow image \" << name;\n        }\n        *path = *cow_image_path;\n        return AssertionSuccess();\n    }\n\n    // Prepare A/B slot for a partition named \"test_partition\".\n    AssertionResult PrepareOneSnapshot(uint64_t device_size,\n                                       std::unique_ptr<ICowWriter>* writer = nullptr) {\n        lock_ = nullptr;\n\n        DeltaArchiveManifest manifest;\n\n        auto dynamic_partition_metadata = manifest.mutable_dynamic_partition_metadata();\n        dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);\n        dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);\n        if (snapuserd_required_) {\n            dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);\n        }\n\n        auto group = dynamic_partition_metadata->add_groups();\n        group->set_name(\"group\");\n        group->set_size(device_size * 2);\n        group->add_partition_names(\"test_partition\");\n\n        auto pu = manifest.add_partitions();\n        pu->set_partition_name(\"test_partition\");\n        pu->set_estimate_cow_size(device_size);\n        SetSize(pu, device_size);\n\n        auto extent = pu->add_operations()->add_dst_extents();\n        extent->set_start_block(0);\n        if (device_size) {\n            extent->set_num_blocks(device_size / manifest.block_size());\n        }\n\n        TestPartitionOpener opener(fake_super);\n        auto builder = MetadataBuilder::New(opener, \"super\", 0);\n        if (!builder) {\n            return AssertionFailure() << \"Failed to open MetadataBuilder\";\n        }\n        builder->AddGroup(\"group_a\", 16_GiB);\n        builder->AddGroup(\"group_b\", 16_GiB);\n        if (!CreatePartition(builder.get(), \"test_partition_a\", device_size, nullptr, \"group_a\")) {\n            return AssertionFailure() << \"Failed create test_partition_a\";\n        }\n\n        if (!sm->CreateUpdateSnapshots(manifest)) {\n            return AssertionFailure() << \"Failed to create update snapshots\";\n        }\n\n        if (writer) {\n            auto res = MapUpdateSnapshot(\"test_partition_b\", writer);\n            if (!res) {\n                return res;\n            }\n        } else if (!snapuserd_required_) {\n            std::string ignore;\n            if (!MapUpdateSnapshot(\"test_partition_b\", &ignore)) {\n                return AssertionFailure() << \"Failed to map test_partition_b\";\n            }\n        }\n        if (!AcquireLock()) {\n            return AssertionFailure() << \"Failed to acquire lock\";\n        }\n        return AssertionSuccess();\n    }\n\n    // Simulate a reboot into the new slot.\n    AssertionResult SimulateReboot() {\n        lock_ = nullptr;\n        if (!sm->FinishedSnapshotWrites(false)) {\n            return AssertionFailure() << \"Failed to finish snapshot writes\";\n        }\n        if (!sm->UnmapUpdateSnapshot(\"test_partition_b\")) {\n            return AssertionFailure() << \"Failed to unmap COW for test_partition_b\";\n        }\n        if (!dm_.DeleteDeviceIfExists(\"test_partition_b\")) {\n            return AssertionFailure() << \"Failed to delete test_partition_b\";\n        }\n        if (!dm_.DeleteDeviceIfExists(\"test_partition_b-base\")) {\n            return AssertionFailure() << \"Failed to destroy test_partition_b-base\";\n        }\n        return AssertionSuccess();\n    }\n\n    std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(\n            const std::string& slot_suffix = \"_a\") {\n        auto info = new TestDeviceInfo(fake_super, slot_suffix);\n        return NewManagerForFirstStageMount(info);\n    }\n\n    std::unique_ptr<SnapshotManager> NewManagerForFirstStageMount(TestDeviceInfo* info) {\n        info->set_first_stage_init(true);\n        auto init = SnapshotManager::NewForFirstStageMount(info);\n        if (!init) {\n            return nullptr;\n        }\n        init->SetUeventRegenCallback([](const std::string& device) -> bool {\n            return android::fs_mgr::WaitForFile(device, snapshot_timeout_);\n        });\n        return init;\n    }\n\n    static constexpr std::chrono::milliseconds snapshot_timeout_ = 5s;\n    DeviceMapper& dm_;\n    std::unique_ptr<SnapshotManager::LockedFile> lock_;\n    android::fiemap::IImageManager* image_manager_ = nullptr;\n    std::string fake_super_;\n    bool snapuserd_required_ = false;\n    std::string test_name_;\n};\n\nTEST_F(SnapshotTest, CreateSnapshot) {\n    ASSERT_TRUE(AcquireLock());\n\n    PartitionCowCreator cow_creator;\n    cow_creator.using_snapuserd = snapuserd_required_;\n    if (cow_creator.using_snapuserd) {\n        cow_creator.compression_algorithm = FLAGS_compression_method;\n    } else {\n        cow_creator.compression_algorithm = \"none\";\n    }\n\n    static const uint64_t kDeviceSize = 1024 * 1024;\n    SnapshotStatus status;\n    status.set_name(\"test-snapshot\");\n    status.set_device_size(kDeviceSize);\n    status.set_snapshot_size(kDeviceSize);\n    status.set_cow_file_size(kDeviceSize);\n    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));\n    ASSERT_TRUE(CreateCowImage(\"test-snapshot\"));\n\n    std::vector<std::string> snapshots;\n    ASSERT_TRUE(sm->ListSnapshots(lock_.get(), &snapshots));\n    ASSERT_EQ(snapshots.size(), 1);\n    ASSERT_EQ(snapshots[0], \"test-snapshot\");\n\n    // Scope so delete can re-acquire the snapshot file lock.\n    {\n        SnapshotStatus status;\n        ASSERT_TRUE(sm->ReadSnapshotStatus(lock_.get(), \"test-snapshot\", &status));\n        ASSERT_EQ(status.state(), SnapshotState::CREATED);\n        ASSERT_EQ(status.device_size(), kDeviceSize);\n        ASSERT_EQ(status.snapshot_size(), kDeviceSize);\n        ASSERT_EQ(status.using_snapuserd(), cow_creator.using_snapuserd);\n        ASSERT_EQ(status.compression_algorithm(), cow_creator.compression_algorithm);\n    }\n\n    ASSERT_TRUE(sm->UnmapSnapshot(lock_.get(), \"test-snapshot\"));\n    ASSERT_TRUE(sm->UnmapCowImage(\"test-snapshot\"));\n    ASSERT_TRUE(sm->DeleteSnapshot(lock_.get(), \"test-snapshot\"));\n}\n\nTEST_F(SnapshotTest, MapSnapshot) {\n    ASSERT_TRUE(AcquireLock());\n\n    PartitionCowCreator cow_creator;\n    cow_creator.using_snapuserd = snapuserd_required_;\n\n    static const uint64_t kDeviceSize = 1024 * 1024;\n    SnapshotStatus status;\n    status.set_name(\"test-snapshot\");\n    status.set_device_size(kDeviceSize);\n    status.set_snapshot_size(kDeviceSize);\n    status.set_cow_file_size(kDeviceSize);\n    ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));\n    ASSERT_TRUE(CreateCowImage(\"test-snapshot\"));\n\n    std::string base_device;\n    ASSERT_TRUE(CreatePartition(\"base-device\", kDeviceSize, &base_device));\n\n    std::string cow_device;\n    ASSERT_TRUE(MapCowImage(\"test-snapshot\", 10s, &cow_device));\n\n    std::string snap_device;\n    ASSERT_TRUE(sm->MapSnapshot(lock_.get(), \"test-snapshot\", base_device, cow_device, 10s,\n                                &snap_device));\n    ASSERT_TRUE(android::base::StartsWith(snap_device, \"/dev/block/dm-\"));\n}\n\nTEST_F(SnapshotTest, NoMergeBeforeReboot) {\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Merge should fail, since the slot hasn't changed.\n    ASSERT_FALSE(sm->InitiateMerge());\n}\n\nTEST_F(SnapshotTest, CleanFirstStageMount) {\n    // If there's no update in progress, there should be no first-stage mount\n    // needed.\n    auto sm = NewManagerForFirstStageMount();\n    ASSERT_NE(sm, nullptr);\n    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());\n}\n\nTEST_F(SnapshotTest, FirstStageMountAfterRollback) {\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // We didn't change the slot, so we shouldn't need snapshots.\n    auto sm = NewManagerForFirstStageMount();\n    ASSERT_NE(sm, nullptr);\n    ASSERT_FALSE(sm->NeedSnapshotsInFirstStageMount());\n\n    auto indicator = sm->GetRollbackIndicatorPath();\n    ASSERT_EQ(access(indicator.c_str(), R_OK), 0);\n}\n\nTEST_F(SnapshotTest, Merge) {\n    ASSERT_TRUE(AcquireLock());\n\n    static constexpr uint64_t kDeviceSize = 1024 * 1024;\n    static constexpr uint32_t kBlockSize = 4096;\n\n    std::string test_string = \"This is a test string.\";\n    test_string.resize(kBlockSize);\n\n    bool userspace_snapshots = false;\n    if (snapuserd_required_) {\n        std::unique_ptr<ICowWriter> writer;\n        ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize, &writer));\n\n        userspace_snapshots = sm->UpdateUsesUserSnapshots(lock_.get());\n\n        // Release the lock.\n        lock_ = nullptr;\n\n        ASSERT_TRUE(writer->AddRawBlocks(0, test_string.data(), test_string.size()));\n        ASSERT_TRUE(writer->Finalize());\n        writer = nullptr;\n    } else {\n        ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));\n\n        // Release the lock.\n        lock_ = nullptr;\n\n        std::string path;\n        ASSERT_TRUE(dm_.GetDmDevicePathByName(\"test_partition_b\", &path));\n\n        unique_fd fd(open(path.c_str(), O_WRONLY));\n        ASSERT_GE(fd, 0);\n        ASSERT_TRUE(android::base::WriteFully(fd, test_string.data(), test_string.size()));\n    }\n\n    // Done updating.\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    ASSERT_TRUE(sm->UnmapUpdateSnapshot(\"test_partition_b\"));\n\n    test_device->set_slot_suffix(\"_b\");\n    ASSERT_TRUE(sm->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(sm->InitiateMerge());\n\n    // Create stale files in snapshot directory. Merge should skip these files\n    // as the suffix doesn't match the current slot.\n    auto tmp_path = test_device->GetMetadataDir() + \"/snapshots/test_partition_b.tmp\";\n    auto other_slot = test_device->GetMetadataDir() + \"/snapshots/test_partition_a\";\n\n    unique_fd fd(open(tmp_path.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));\n    ASSERT_GE(fd, 0);\n\n    fd.reset(open(other_slot.c_str(), O_RDWR | O_CLOEXEC | O_CREAT, 0644));\n    ASSERT_GE(fd, 0);\n\n    // The device should have been switched to a snapshot-merge target.\n    DeviceMapper::TargetInfo target;\n    ASSERT_TRUE(sm->IsSnapshotDevice(\"test_partition_b\", &target));\n    if (userspace_snapshots) {\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n    } else {\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"snapshot-merge\");\n    }\n\n    // We should not be able to cancel an update now.\n    ASSERT_EQ(sm->TryCancelUpdate(), CancelResult::NEEDS_MERGE);\n    ASSERT_FALSE(sm->CancelUpdate());\n\n    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::MergeCompleted);\n    ASSERT_EQ(sm->GetUpdateState(), UpdateState::None);\n\n    // Make sure that snapshot states are cleared and all stale files\n    // are deleted\n    {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        std::vector<std::string> snapshots;\n        ASSERT_TRUE(sm->ListSnapshots(local_lock.get(), &snapshots));\n        ASSERT_TRUE(snapshots.empty());\n    }\n\n    // The device should no longer be a snapshot or snapshot-merge.\n    ASSERT_FALSE(sm->IsSnapshotDevice(\"test_partition_b\"));\n\n    // Test that we can read back the string we wrote to the snapshot. Note\n    // that the base device is gone now. |snap_device| contains the correct\n    // partition.\n    fd.reset(open(\"/dev/block/mapper/test_partition_b\", O_RDONLY | O_CLOEXEC));\n    ASSERT_GE(fd, 0);\n\n    std::string buffer(test_string.size(), '\\0');\n    ASSERT_TRUE(android::base::ReadFully(fd, buffer.data(), buffer.size()));\n    ASSERT_EQ(test_string, buffer);\n}\n\nTEST_F(SnapshotTest, FirstStageMountAndMerge) {\n    ASSERT_TRUE(AcquireLock());\n\n    static const uint64_t kDeviceSize = 1024 * 1024;\n    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));\n    ASSERT_TRUE(SimulateReboot());\n\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    ASSERT_TRUE(AcquireLock());\n\n    bool userspace_snapshots = init->UpdateUsesUserSnapshots(lock_.get());\n\n    // Validate that we have a snapshot device.\n    SnapshotStatus status;\n    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), \"test_partition_b\", &status));\n    ASSERT_EQ(status.state(), SnapshotState::CREATED);\n    if (snapuserd_required_) {\n        ASSERT_EQ(status.compression_algorithm(), FLAGS_compression_method);\n    } else {\n        ASSERT_EQ(status.compression_algorithm(), \"\");\n    }\n\n    DeviceMapper::TargetInfo target;\n    ASSERT_TRUE(init->IsSnapshotDevice(\"test_partition_b\", &target));\n    if (userspace_snapshots) {\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n    } else {\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"snapshot\");\n    }\n}\n\nTEST_F(SnapshotTest, FlashSuperDuringUpdate) {\n    ASSERT_TRUE(AcquireLock());\n\n    static const uint64_t kDeviceSize = 1024 * 1024;\n    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));\n    ASSERT_TRUE(SimulateReboot());\n\n    // Reflash the super partition.\n    FormatFakeSuper();\n    ASSERT_TRUE(CreatePartition(\"test_partition_b\", kDeviceSize));\n\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    ASSERT_TRUE(AcquireLock());\n\n    SnapshotStatus status;\n    ASSERT_TRUE(init->ReadSnapshotStatus(lock_.get(), \"test_partition_b\", &status));\n\n    // We should not get a snapshot device now.\n    DeviceMapper::TargetInfo target;\n    ASSERT_FALSE(init->IsSnapshotDevice(\"test_partition_b\", &target));\n\n    // We should see a cancelled update as well.\n    lock_ = nullptr;\n    ASSERT_EQ(sm->ProcessUpdateState(), UpdateState::Cancelled);\n}\n\nTEST_F(SnapshotTest, FlashSuperDuringMerge) {\n    ASSERT_TRUE(AcquireLock());\n\n    static const uint64_t kDeviceSize = 1024 * 1024;\n    ASSERT_TRUE(PrepareOneSnapshot(kDeviceSize));\n    ASSERT_TRUE(SimulateReboot());\n\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(init->InitiateMerge());\n\n    // Now, reflash super. Note that we haven't called ProcessUpdateState, so the\n    // status is still Merging.\n    ASSERT_TRUE(DeleteSnapshotDevice(\"test_partition_b\"));\n    ASSERT_TRUE(init->image_manager()->UnmapImageIfExists(\"test_partition_b-cow-img\"));\n    FormatFakeSuper();\n    ASSERT_TRUE(CreatePartition(\"test_partition_b\", kDeviceSize));\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Because the status is Merging, we must call ProcessUpdateState, which should\n    // detect a cancelled update.\n    ASSERT_EQ(init->ProcessUpdateState(), UpdateState::Cancelled);\n    ASSERT_EQ(init->GetUpdateState(), UpdateState::None);\n}\n\nTEST_F(SnapshotTest, UpdateBootControlHal) {\n    ASSERT_TRUE(AcquireLock());\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Initiated));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Unverified));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::SNAPSHOTTED);\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::Merging));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeNeedsReboot));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeCompleted));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::NONE);\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeFailed));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);\n}\n\nTEST_F(SnapshotTest, MergeFailureCode) {\n    ASSERT_TRUE(AcquireLock());\n\n    ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::MergeFailed,\n                                     MergeFailureCode::ListSnapshots));\n    ASSERT_EQ(test_device->merge_status(), MergeStatus::MERGING);\n\n    SnapshotUpdateStatus status = sm->ReadSnapshotUpdateStatus(lock_.get());\n    ASSERT_EQ(status.state(), UpdateState::MergeFailed);\n    ASSERT_EQ(status.merge_failure_code(), MergeFailureCode::ListSnapshots);\n}\n\nenum class Request { UNKNOWN, LOCK_SHARED, LOCK_EXCLUSIVE, UNLOCK, EXIT };\nstd::ostream& operator<<(std::ostream& os, Request request) {\n    switch (request) {\n        case Request::LOCK_SHARED:\n            return os << \"Shared\";\n        case Request::LOCK_EXCLUSIVE:\n            return os << \"Exclusive\";\n        case Request::UNLOCK:\n            return os << \"Unlock\";\n        case Request::EXIT:\n            return os << \"Exit\";\n        case Request::UNKNOWN:\n            [[fallthrough]];\n        default:\n            return os << \"Unknown\";\n    }\n}\n\nclass LockTestConsumer {\n  public:\n    AssertionResult MakeRequest(Request new_request) {\n        {\n            std::unique_lock<std::mutex> ulock(mutex_);\n            requests_.push_back(new_request);\n        }\n        cv_.notify_all();\n        return AssertionSuccess() << \"Request \" << new_request << \" successful\";\n    }\n\n    template <typename R, typename P>\n    AssertionResult WaitFulfill(std::chrono::duration<R, P> timeout) {\n        std::unique_lock<std::mutex> ulock(mutex_);\n        if (cv_.wait_for(ulock, timeout, [this] { return requests_.empty(); })) {\n            return AssertionSuccess() << \"All requests_ fulfilled.\";\n        }\n        return AssertionFailure() << \"Timeout waiting for fulfilling \" << requests_.size()\n                                  << \" request(s), first one is \"\n                                  << (requests_.empty() ? Request::UNKNOWN : requests_.front());\n    }\n\n    void StartHandleRequestsInBackground() {\n        future_ = std::async(std::launch::async, &LockTestConsumer::HandleRequests, this);\n    }\n\n  private:\n    void HandleRequests() {\n        static constexpr auto consumer_timeout = 3s;\n\n        auto next_request = Request::UNKNOWN;\n        do {\n            // Peek next request.\n            {\n                std::unique_lock<std::mutex> ulock(mutex_);\n                if (cv_.wait_for(ulock, consumer_timeout, [this] { return !requests_.empty(); })) {\n                    next_request = requests_.front();\n                } else {\n                    next_request = Request::EXIT;\n                }\n            }\n\n            // Handle next request.\n            switch (next_request) {\n                case Request::LOCK_SHARED: {\n                    lock_ = sm->LockShared();\n                } break;\n                case Request::LOCK_EXCLUSIVE: {\n                    lock_ = sm->LockExclusive();\n                } break;\n                case Request::EXIT:\n                    [[fallthrough]];\n                case Request::UNLOCK: {\n                    lock_.reset();\n                } break;\n                case Request::UNKNOWN:\n                    [[fallthrough]];\n                default:\n                    break;\n            }\n\n            // Pop next request. This thread is the only thread that\n            // pops from the front of the requests_ deque.\n            {\n                std::unique_lock<std::mutex> ulock(mutex_);\n                if (next_request == Request::EXIT) {\n                    requests_.clear();\n                } else {\n                    requests_.pop_front();\n                }\n            }\n            cv_.notify_all();\n        } while (next_request != Request::EXIT);\n    }\n\n    std::mutex mutex_;\n    std::condition_variable cv_;\n    std::deque<Request> requests_;\n    std::unique_ptr<SnapshotManager::LockedFile> lock_;\n    std::future<void> future_;\n};\n\nclass LockTest : public ::testing::Test {\n  public:\n    void SetUp() {\n        SKIP_IF_NON_VIRTUAL_AB();\n        first_consumer.StartHandleRequestsInBackground();\n        second_consumer.StartHandleRequestsInBackground();\n    }\n\n    void TearDown() {\n        RETURN_IF_NON_VIRTUAL_AB();\n        EXPECT_TRUE(first_consumer.MakeRequest(Request::EXIT));\n        EXPECT_TRUE(second_consumer.MakeRequest(Request::EXIT));\n    }\n\n    static constexpr auto request_timeout = 500ms;\n    LockTestConsumer first_consumer;\n    LockTestConsumer second_consumer;\n};\n\nTEST_F(LockTest, SharedShared) {\n    ASSERT_TRUE(first_consumer.MakeRequest(Request::LOCK_SHARED));\n    ASSERT_TRUE(first_consumer.WaitFulfill(request_timeout));\n    ASSERT_TRUE(second_consumer.MakeRequest(Request::LOCK_SHARED));\n    ASSERT_TRUE(second_consumer.WaitFulfill(request_timeout));\n}\n\nusing LockTestParam = std::pair<Request, Request>;\nclass LockTestP : public LockTest, public ::testing::WithParamInterface<LockTestParam> {};\nTEST_P(LockTestP, Test) {\n    ASSERT_TRUE(first_consumer.MakeRequest(GetParam().first));\n    ASSERT_TRUE(first_consumer.WaitFulfill(request_timeout));\n    ASSERT_TRUE(second_consumer.MakeRequest(GetParam().second));\n    ASSERT_FALSE(second_consumer.WaitFulfill(request_timeout))\n            << \"Should not be able to \" << GetParam().second << \" while separate thread \"\n            << GetParam().first;\n    ASSERT_TRUE(first_consumer.MakeRequest(Request::UNLOCK));\n    ASSERT_TRUE(second_consumer.WaitFulfill(request_timeout))\n            << \"Should be able to hold lock that is released by separate thread\";\n}\nINSTANTIATE_TEST_SUITE_P(\n        LockTest, LockTestP,\n        testing::Values(LockTestParam{Request::LOCK_EXCLUSIVE, Request::LOCK_EXCLUSIVE},\n                        LockTestParam{Request::LOCK_EXCLUSIVE, Request::LOCK_SHARED},\n                        LockTestParam{Request::LOCK_SHARED, Request::LOCK_EXCLUSIVE}),\n        [](const testing::TestParamInfo<LockTestP::ParamType>& info) {\n            std::stringstream ss;\n            ss << info.param.first << info.param.second;\n            return ss.str();\n        });\n\nclass SnapshotUpdateTest : public SnapshotTest {\n  public:\n    void SetUp() override {\n        SKIP_IF_NON_VIRTUAL_AB();\n        SKIP_IF_VENDOR_ON_ANDROID_S();\n\n        SnapshotTest::SetUp();\n        if (!image_manager_) {\n            // Test was skipped.\n            return;\n        }\n\n        Cleanup();\n\n        // Cleanup() changes slot suffix, so initialize it again.\n        test_device->set_slot_suffix(\"_a\");\n\n        opener_ = std::make_unique<TestPartitionOpener>(fake_super);\n\n        auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();\n        dynamic_partition_metadata->set_vabc_enabled(snapuserd_required_);\n        dynamic_partition_metadata->set_cow_version(android::snapshot::kCowVersionMajor);\n        if (snapuserd_required_) {\n            dynamic_partition_metadata->set_vabc_compression_param(FLAGS_compression_method);\n        }\n\n        // Create a fake update package metadata.\n        // Not using full name \"system\", \"vendor\", \"product\" because these names collide with the\n        // mapped partitions on the running device.\n        // Each test modifies manifest_ slightly to indicate changes to the partition layout.\n        group_ = dynamic_partition_metadata->add_groups();\n        group_->set_name(\"group\");\n        group_->set_size(kGroupSize);\n        group_->add_partition_names(\"sys\");\n        group_->add_partition_names(\"vnd\");\n        group_->add_partition_names(\"prd\");\n        sys_ = manifest_.add_partitions();\n        sys_->set_partition_name(\"sys\");\n        sys_->set_estimate_cow_size(2_MiB);\n        SetSize(sys_, 3_MiB);\n        vnd_ = manifest_.add_partitions();\n        vnd_->set_partition_name(\"vnd\");\n        vnd_->set_estimate_cow_size(2_MiB);\n        SetSize(vnd_, 3_MiB);\n        prd_ = manifest_.add_partitions();\n        prd_->set_partition_name(\"prd\");\n        prd_->set_estimate_cow_size(2_MiB);\n        SetSize(prd_, 3_MiB);\n\n        // Initialize source partition metadata using |manifest_|.\n        src_ = MetadataBuilder::New(*opener_, \"super\", 0);\n        ASSERT_NE(src_, nullptr);\n        ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, \"_a\"));\n        // Add sys_b which is like system_other.\n        ASSERT_TRUE(src_->AddGroup(\"group_b\", kGroupSize));\n        auto partition = src_->AddPartition(\"sys_b\", \"group_b\", 0);\n        ASSERT_NE(nullptr, partition);\n        ASSERT_TRUE(src_->ResizePartition(partition, 1_MiB));\n        auto metadata = src_->Export();\n        ASSERT_NE(nullptr, metadata);\n        ASSERT_TRUE(UpdatePartitionTable(*opener_, \"super\", *metadata.get(), 0));\n\n        // Map source partitions.\n        std::string path;\n        for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n            ASSERT_TRUE(CreateLogicalPartition(\n                    CreateLogicalPartitionParams{\n                            .block_device = fake_super,\n                            .metadata_slot = 0,\n                            .partition_name = name,\n                            .timeout_ms = 1s,\n                            .partition_opener = opener_.get(),\n                    },\n                    &path));\n            ASSERT_TRUE(WriteRandomData(path));\n            auto hash = GetHash(path);\n            ASSERT_TRUE(hash.has_value());\n            hashes_[name] = *hash;\n        }\n\n        // OTA client blindly unmaps all partitions that are possibly mapped.\n        for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n            ASSERT_TRUE(sm->UnmapUpdateSnapshot(name));\n        }\n    }\n    void TearDown() override {\n        RETURN_IF_NON_VIRTUAL_AB();\n        RETURN_IF_VENDOR_ON_ANDROID_S();\n\n        LOG(INFO) << \"Tearing down SnapshotUpdateTest test: \" << test_name_;\n\n        Cleanup();\n        SnapshotTest::TearDown();\n    }\n    void Cleanup() {\n        if (!image_manager_) {\n            InitializeState();\n        }\n        MountMetadata();\n        for (const auto& suffix : {\"_a\", \"_b\"}) {\n            test_device->set_slot_suffix(suffix);\n\n            // Cheat our way out of merge failed states.\n            if (sm->ProcessUpdateState() == UpdateState::MergeFailed) {\n                ASSERT_TRUE(AcquireLock());\n                ASSERT_TRUE(sm->WriteUpdateState(lock_.get(), UpdateState::None));\n                lock_ = {};\n            }\n\n            EXPECT_TRUE(sm->CancelUpdate()) << suffix;\n        }\n        EXPECT_TRUE(UnmapAll());\n    }\n\n    AssertionResult IsPartitionUnchanged(const std::string& name) {\n        std::string path;\n        if (!dm_.GetDmDevicePathByName(name, &path)) {\n            return AssertionFailure() << \"Path of \" << name << \" cannot be determined\";\n        }\n        auto hash = GetHash(path);\n        if (!hash.has_value()) {\n            return AssertionFailure() << \"Cannot read partition \" << name << \": \" << path;\n        }\n        auto it = hashes_.find(name);\n        if (it == hashes_.end()) {\n            return AssertionFailure() << \"No existing hash for \" << name << \". Bad test code?\";\n        }\n        if (it->second != *hash) {\n            return AssertionFailure() << \"Content of \" << name << \" has changed\";\n        }\n        return AssertionSuccess();\n    }\n\n    std::optional<uint64_t> GetSnapshotSize(const std::string& name) {\n        if (!AcquireLock()) {\n            return std::nullopt;\n        }\n        auto local_lock = std::move(lock_);\n\n        SnapshotStatus status;\n        if (!sm->ReadSnapshotStatus(local_lock.get(), name, &status)) {\n            return std::nullopt;\n        }\n        return status.snapshot_size();\n    }\n\n    AssertionResult UnmapAll() {\n        for (const auto& name : {\"sys\", \"vnd\", \"prd\", \"dlkm\"}) {\n            if (!dm_.DeleteDeviceIfExists(name + \"_a\"s)) {\n                return AssertionFailure() << \"Cannot unmap \" << name << \"_a\";\n            }\n            if (!DeleteSnapshotDevice(name + \"_b\"s)) {\n                return AssertionFailure() << \"Cannot delete snapshot \" << name << \"_b\";\n            }\n        }\n        return AssertionSuccess();\n    }\n\n    AssertionResult MapOneUpdateSnapshot(const std::string& name) {\n        if (snapuserd_required_) {\n            std::unique_ptr<ICowWriter> writer;\n            return MapUpdateSnapshot(name, &writer);\n        } else {\n            std::string path;\n            return MapUpdateSnapshot(name, &path);\n        }\n    }\n\n    AssertionResult WriteSnapshots() {\n        for (const auto& partition : {sys_, vnd_, prd_}) {\n            auto res = WriteSnapshotAndHash(partition);\n            if (!res) {\n                return res;\n            }\n        }\n        return AssertionSuccess();\n    }\n\n    AssertionResult WriteSnapshotAndHash(PartitionUpdate* partition) {\n        std::string name = partition->partition_name() + \"_b\";\n        if (snapuserd_required_) {\n            std::unique_ptr<ICowWriter> writer;\n            auto res = MapUpdateSnapshot(name, &writer);\n            if (!res) {\n                return res;\n            }\n            if (!WriteRandomSnapshotData(writer.get(), &hashes_[name])) {\n                return AssertionFailure() << \"Unable to write random data to snapshot \" << name;\n            }\n            if (!writer->Finalize()) {\n                return AssertionFailure() << \"Unable to finalize COW for \" << name;\n            }\n        } else {\n            std::string path;\n            auto res = MapUpdateSnapshot(name, &path);\n            if (!res) {\n                return res;\n            }\n            if (!WriteRandomData(path, std::nullopt, &hashes_[name])) {\n                return AssertionFailure() << \"Unable to write random data to snapshot \" << name;\n            }\n        }\n\n        // Make sure updates to one device are seen by all devices.\n        sync();\n\n        return AssertionSuccess() << \"Written random data to snapshot \" << name\n                                  << \", hash: \" << hashes_[name];\n    }\n\n    bool WriteRandomSnapshotData(ICowWriter* writer, std::string* hash) {\n        unique_fd rand(open(\"/dev/urandom\", O_RDONLY));\n        if (rand < 0) {\n            PLOG(ERROR) << \"open /dev/urandom\";\n            return false;\n        }\n\n        SHA256_CTX ctx;\n        SHA256_Init(&ctx);\n\n        if (!writer->GetMaxBlocks()) {\n            LOG(ERROR) << \"CowWriter must specify maximum number of blocks\";\n            return false;\n        }\n        const auto num_blocks = writer->GetMaxBlocks().value();\n\n        const auto block_size = writer->GetBlockSize();\n        std::string block(block_size, '\\0');\n        for (uint64_t i = 0; i < num_blocks; i++) {\n            if (!ReadFully(rand, block.data(), block.size())) {\n                PLOG(ERROR) << \"read /dev/urandom\";\n                return false;\n            }\n            if (!writer->AddRawBlocks(i, block.data(), block.size())) {\n                LOG(ERROR) << \"Failed to add raw block \" << i;\n                return false;\n            }\n            SHA256_Update(&ctx, block.data(), block.size());\n        }\n\n        uint8_t out[32];\n        SHA256_Final(out, &ctx);\n        *hash = ToHexString(out, sizeof(out));\n        return true;\n    }\n\n    // Generate a snapshot that moves all the upper blocks down to the start.\n    // It doesn't really matter the order, we just want copies that reference\n    // blocks that won't exist if the partition shrinks.\n    AssertionResult ShiftAllSnapshotBlocks(const std::string& name, uint64_t old_size) {\n        std::unique_ptr<ICowWriter> writer;\n        if (auto res = MapUpdateSnapshot(name, &writer); !res) {\n            return res;\n        }\n        if (!writer->GetMaxBlocks() || !*writer->GetMaxBlocks()) {\n            return AssertionFailure() << \"No max blocks set for \" << name << \" writer\";\n        }\n\n        uint64_t src_block = (old_size / writer->GetBlockSize()) - 1;\n        uint64_t dst_block = 0;\n        uint64_t max_blocks = *writer->GetMaxBlocks();\n        while (dst_block < max_blocks && dst_block < src_block) {\n            if (!writer->AddCopy(dst_block, src_block)) {\n                return AssertionFailure() << \"Unable to add copy for \" << name << \" for blocks \"\n                                          << src_block << \", \" << dst_block;\n            }\n            dst_block++;\n            src_block--;\n        }\n        if (!writer->Finalize()) {\n            return AssertionFailure() << \"Unable to finalize writer for \" << name;\n        }\n\n        auto old_partition = \"/dev/block/mapper/\" + GetOtherPartitionName(name);\n        auto reader = writer->OpenFileDescriptor(old_partition);\n        if (!reader) {\n            return AssertionFailure() << \"Could not open file descriptor for \" << name;\n        }\n\n        auto hash = HashSnapshot(reader.get());\n        if (hash.empty()) {\n            return AssertionFailure() << \"Unable to hash snapshot writer for \" << name;\n        }\n        hashes_[name] = hash;\n\n        return AssertionSuccess();\n    }\n\n    AssertionResult MapUpdateSnapshots(const std::vector<std::string>& names = {\"sys_b\", \"vnd_b\",\n                                                                                \"prd_b\"}) {\n        for (const auto& name : names) {\n            auto res = MapOneUpdateSnapshot(name);\n            if (!res) {\n                return res;\n            }\n        }\n        return AssertionSuccess();\n    }\n\n    // Create fake install operations to grow the COW device size.\n    void AddOperation(PartitionUpdate* partition_update, uint64_t size_bytes = 0) {\n        auto e = partition_update->add_operations()->add_dst_extents();\n        e->set_start_block(0);\n        if (size_bytes == 0) {\n            size_bytes = GetSize(partition_update);\n        }\n        e->set_num_blocks(size_bytes / manifest_.block_size());\n    }\n\n    void AddOperationForPartitions(std::vector<PartitionUpdate*> partitions = {}) {\n        if (partitions.empty()) {\n            partitions = {sys_, vnd_, prd_};\n        }\n        for (auto* partition : partitions) {\n            AddOperation(partition);\n        }\n    }\n\n    std::unique_ptr<TestPartitionOpener> opener_;\n    DeltaArchiveManifest manifest_;\n    std::unique_ptr<MetadataBuilder> src_;\n    std::map<std::string, std::string> hashes_;\n\n    PartitionUpdate* sys_ = nullptr;\n    PartitionUpdate* vnd_ = nullptr;\n    PartitionUpdate* prd_ = nullptr;\n    DynamicPartitionGroup* group_ = nullptr;\n};\n\nTEST_F(SnapshotUpdateTest, SuperOtaMetadataTest) {\n    auto info = new TestDeviceInfo(fake_super);\n    ASSERT_TRUE(CleanupScratchOtaMetadataIfPresent(info));\n    ASSERT_TRUE(CreateScratchOtaMetadataOnSuper(info));\n    std::string scratch_device = GetScratchOtaMetadataPartition();\n    ASSERT_NE(scratch_device, \"\");\n    ASSERT_NE(MapScratchOtaMetadataPartition(scratch_device), \"\");\n    ASSERT_TRUE(CleanupScratchOtaMetadataIfPresent(info));\n}\n\n// Test full update flow executed by update_engine. Some partitions uses super empty space,\n// some uses images, and some uses both.\n// Also test UnmapUpdateSnapshot unmaps everything.\n// Also test first stage mount and merge after this.\nTEST_F(SnapshotUpdateTest, FullUpdateFlow) {\n    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs\n    // fit in super, but not |prd|.\n    constexpr uint64_t partition_size = 3788_KiB;\n    SetSize(sys_, partition_size);\n    SetSize(vnd_, partition_size);\n    SetSize(prd_, 18_MiB);\n\n    // Make sure |prd| does not fit in super at all. On VABC, this means we\n    // fake an extra large COW for |vnd| to fill up super.\n    vnd_->set_estimate_cow_size(30_MiB);\n    prd_->set_estimate_cow_size(30_MiB);\n\n    AddOperationForPartitions();\n\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Test that partitions prioritize using space in super.\n    auto tgt = MetadataBuilder::New(*opener_, \"super\", 1);\n    ASSERT_NE(tgt, nullptr);\n    ASSERT_NE(nullptr, tgt->FindPartition(\"sys_b-cow\"));\n    ASSERT_NE(nullptr, tgt->FindPartition(\"vnd_b-cow\"));\n    ASSERT_EQ(nullptr, tgt->FindPartition(\"prd_b-cow\"));\n\n    // Write some data to target partitions.\n    ASSERT_TRUE(WriteSnapshots());\n\n    // Assert that source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    auto indicator = sm->GetRollbackIndicatorPath();\n    ASSERT_NE(access(indicator.c_str(), R_OK), 0);\n\n    // Check that the target partitions have the same content.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    // Initiate the merge and wait for it to be completed.\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(init->InitiateMerge());\n    ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);\n    {\n        // We should have started in SECOND_PHASE since nothing shrinks.\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());\n        ASSERT_EQ(status.merge_phase(), MergePhase::SECOND_PHASE);\n    }\n    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());\n\n    // Make sure the second phase ran and deleted snapshots.\n    {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        std::vector<std::string> snapshots;\n        ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));\n        ASSERT_TRUE(snapshots.empty());\n    }\n\n    // Check that the target partitions have the same content after the merge.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name))\n                << \"Content of \" << name << \" changes after the merge\";\n    }\n}\n\nTEST_F(SnapshotUpdateTest, DuplicateOps) {\n    if (!snapuserd_required_) {\n        GTEST_SKIP() << \"snapuserd-only test\";\n    }\n\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Write some data to target partitions.\n    ASSERT_TRUE(WriteSnapshots());\n\n    std::vector<PartitionUpdate*> partitions = {sys_, vnd_, prd_};\n    for (auto* partition : partitions) {\n        AddOperation(partition);\n\n        std::unique_ptr<ICowWriter> writer;\n        auto res = MapUpdateSnapshot(partition->partition_name() + \"_b\", &writer);\n        ASSERT_TRUE(res);\n        ASSERT_TRUE(writer->AddZeroBlocks(0, 1));\n        ASSERT_TRUE(writer->AddZeroBlocks(0, 1));\n        ASSERT_TRUE(writer->Finalize());\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Initiate the merge and wait for it to be completed.\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(init->InitiateMerge());\n    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());\n}\n\n// Test that shrinking and growing partitions at the same time is handled\n// correctly in VABC.\nTEST_F(SnapshotUpdateTest, SpaceSwapUpdate) {\n    if (!snapuserd_required_) {\n        // b/179111359\n        GTEST_SKIP() << \"Skipping snapuserd test\";\n    }\n\n    auto old_sys_size = GetSize(sys_);\n    auto old_prd_size = GetSize(prd_);\n\n    // Grow |sys| but shrink |prd|.\n    SetSize(sys_, old_sys_size * 2);\n    sys_->set_estimate_cow_size(8_MiB);\n    SetSize(prd_, old_prd_size / 2);\n    prd_->set_estimate_cow_size(1_MiB);\n\n    AddOperationForPartitions();\n\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Check that the old partition sizes were saved correctly.\n    {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n\n        SnapshotStatus status;\n        ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), \"prd_b\", &status));\n        ASSERT_EQ(status.old_partition_size(), 3145728);\n        ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), \"sys_b\", &status));\n        ASSERT_EQ(status.old_partition_size(), 3145728);\n    }\n\n    ASSERT_TRUE(WriteSnapshotAndHash(sys_));\n    ASSERT_TRUE(WriteSnapshotAndHash(vnd_));\n    ASSERT_TRUE(ShiftAllSnapshotBlocks(\"prd_b\", old_prd_size));\n\n    sync();\n\n    // Assert that source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    auto indicator = sm->GetRollbackIndicatorPath();\n    ASSERT_NE(access(indicator.c_str(), R_OK), 0);\n\n    // Check that the target partitions have the same content.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    // Initiate the merge and wait for it to be completed.\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(init->InitiateMerge());\n    ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);\n    {\n        // Check that the merge phase is FIRST_PHASE until at least one call\n        // to ProcessUpdateState() occurs.\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());\n        ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);\n    }\n\n    // Simulate shutting down the device and creating partitions again.\n    ASSERT_TRUE(UnmapAll());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Check that we used the correct types after rebooting mid-merge.\n    DeviceMapper::TargetInfo target;\n    ASSERT_TRUE(init->IsSnapshotDevice(\"prd_b\", &target));\n\n    bool userspace_snapshots = init->UpdateUsesUserSnapshots();\n    if (userspace_snapshots) {\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n        ASSERT_TRUE(init->IsSnapshotDevice(\"sys_b\", &target));\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n        ASSERT_TRUE(init->IsSnapshotDevice(\"vnd_b\", &target));\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n    } else {\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"snapshot-merge\");\n        ASSERT_TRUE(init->IsSnapshotDevice(\"sys_b\", &target));\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"snapshot\");\n        ASSERT_TRUE(init->IsSnapshotDevice(\"vnd_b\", &target));\n        ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"snapshot\");\n    }\n\n    // Complete the merge.\n    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());\n\n    // Make sure the second phase ran and deleted snapshots.\n    {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        std::vector<std::string> snapshots;\n        ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));\n        ASSERT_TRUE(snapshots.empty());\n    }\n\n    // Check that the target partitions have the same content after the merge.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name))\n                << \"Content of \" << name << \" changes after the merge\";\n    }\n}\n\n// Test that shrinking and growing partitions at the same time is handled\n// correctly in VABC.\nTEST_F(SnapshotUpdateTest, InterruptMergeDuringPhaseUpdate) {\n    if (!snapuserd_required_) {\n        // b/179111359\n        GTEST_SKIP() << \"Skipping snapuserd test\";\n    }\n\n    auto old_sys_size = GetSize(sys_);\n    auto old_prd_size = GetSize(prd_);\n\n    // Grow |sys| but shrink |prd|.\n    SetSize(sys_, old_sys_size * 2);\n    sys_->set_estimate_cow_size(8_MiB);\n    SetSize(prd_, old_prd_size / 2);\n    prd_->set_estimate_cow_size(1_MiB);\n\n    AddOperationForPartitions();\n\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Check that the old partition sizes were saved correctly.\n    {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n\n        SnapshotStatus status;\n        ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), \"prd_b\", &status));\n        ASSERT_EQ(status.old_partition_size(), 3145728);\n        ASSERT_TRUE(sm->ReadSnapshotStatus(local_lock.get(), \"sys_b\", &status));\n        ASSERT_EQ(status.old_partition_size(), 3145728);\n    }\n\n    ASSERT_TRUE(WriteSnapshotAndHash(sys_));\n    ASSERT_TRUE(WriteSnapshotAndHash(vnd_));\n    ASSERT_TRUE(ShiftAllSnapshotBlocks(\"prd_b\", old_prd_size));\n\n    sync();\n\n    // Assert that source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Check that the target partitions have the same content.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    // Initiate the merge and wait for it to be completed.\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(init->InitiateMerge());\n    ASSERT_EQ(init->IsSnapuserdRequired(), snapuserd_required_);\n    {\n        // Check that the merge phase is FIRST_PHASE until at least one call\n        // to ProcessUpdateState() occurs.\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());\n        ASSERT_EQ(status.merge_phase(), MergePhase::FIRST_PHASE);\n    }\n\n    // Wait until prd_b merge is completed which is part of first phase\n    std::chrono::milliseconds timeout(6000);\n    auto start = std::chrono::steady_clock::now();\n    // Keep polling until the merge is complete or timeout is reached\n    while (true) {\n        // Query the merge status\n        const auto merge_status = init->snapuserd_client()->QuerySnapshotStatus(\"prd_b\");\n        if (merge_status == \"snapshot-merge-complete\") {\n            break;\n        }\n\n        auto now = std::chrono::steady_clock::now();\n        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);\n\n        ASSERT_TRUE(elapsed < timeout);\n        // sleep for a second and allow merge to complete\n        std::this_thread::sleep_for(std::chrono::milliseconds(1000));\n    }\n\n    // Now, forcefully update the snapshot-update status to SECOND PHASE\n    // This will not update the snapshot status of sys_b to MERGING\n    if (init->UpdateUsesUserSnapshots()) {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        auto status = init->ReadSnapshotUpdateStatus(local_lock.get());\n        status.set_merge_phase(MergePhase::SECOND_PHASE);\n        ASSERT_TRUE(init->WriteSnapshotUpdateStatus(local_lock.get(), status));\n    }\n\n    // Simulate shutting down the device and creating partitions again.\n    ASSERT_TRUE(UnmapAll());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    DeviceMapper::TargetInfo target;\n    ASSERT_TRUE(init->IsSnapshotDevice(\"prd_b\", &target));\n\n    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n    ASSERT_TRUE(init->IsSnapshotDevice(\"sys_b\", &target));\n    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n    ASSERT_TRUE(init->IsSnapshotDevice(\"vnd_b\", &target));\n    ASSERT_EQ(DeviceMapper::GetTargetType(target.spec), \"user\");\n\n    // Complete the merge; \"sys\" and \"vnd\" should resume the merge\n    // even though merge was interrupted after update_status was updated to\n    // SECOND_PHASE\n    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());\n\n    // Make sure the second phase ran and deleted snapshots.\n    {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        std::vector<std::string> snapshots;\n        ASSERT_TRUE(init->ListSnapshots(local_lock.get(), &snapshots));\n        ASSERT_TRUE(snapshots.empty());\n    }\n\n    // Check that the target partitions have the same content after the merge.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name))\n                << \"Content of \" << name << \" changes after the merge\";\n    }\n}\n\n// Test that if new system partitions uses empty space in super, that region is not snapshotted.\nTEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {\n    GTEST_SKIP() << \"b/141889746\";\n    SetSize(sys_, 4_MiB);\n    // vnd_b and prd_b are unchanged.\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_EQ(3_MiB, GetSnapshotSize(\"sys_b\").value_or(0));\n}\n\n// Test that if new system partitions uses space of old vendor partition, that region is\n// snapshotted.\nTEST_F(SnapshotUpdateTest, SnapshotOldPartitions) {\n    SetSize(sys_, 4_MiB);  // grows\n    SetSize(vnd_, 2_MiB);  // shrinks\n    // prd_b is unchanged\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_EQ(4_MiB, GetSnapshotSize(\"sys_b\").value_or(0));\n}\n\n// Test that even if there seem to be empty space in target metadata, COW partition won't take\n// it because they are used by old partitions.\nTEST_F(SnapshotUpdateTest, CowPartitionDoNotTakeOldPartitions) {\n    SetSize(sys_, 2_MiB);  // shrinks\n    // vnd_b and prd_b are unchanged.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    auto tgt = MetadataBuilder::New(*opener_, \"super\", 1);\n    ASSERT_NE(nullptr, tgt);\n    auto metadata = tgt->Export();\n    ASSERT_NE(nullptr, metadata);\n    std::vector<std::string> written;\n    // Write random data to all COW partitions in super\n    for (auto p : metadata->partitions) {\n        if (GetPartitionGroupName(metadata->groups[p.group_index]) != kCowGroupName) {\n            continue;\n        }\n        std::string path;\n        ASSERT_TRUE(CreateLogicalPartition(\n                CreateLogicalPartitionParams{\n                        .block_device = fake_super,\n                        .metadata = metadata.get(),\n                        .partition = &p,\n                        .timeout_ms = 1s,\n                        .partition_opener = opener_.get(),\n                },\n                &path));\n        ASSERT_TRUE(WriteRandomData(path));\n        written.push_back(GetPartitionName(p));\n    }\n    ASSERT_FALSE(written.empty())\n            << \"No COW partitions are created even if there are empty space in super partition\";\n\n    // Make sure source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n}\n\n// Test that it crashes after creating snapshot status file but before creating COW image, then\n// calling CreateUpdateSnapshots again works.\nTEST_F(SnapshotUpdateTest, SnapshotStatusFileWithoutCow) {\n    // Write some trash snapshot files to simulate leftovers from previous runs.\n    {\n        ASSERT_TRUE(AcquireLock());\n        auto local_lock = std::move(lock_);\n        SnapshotStatus status;\n        status.set_name(\"sys_b\");\n        ASSERT_TRUE(sm->WriteSnapshotStatus(local_lock.get(), status));\n        ASSERT_TRUE(image_manager_->CreateBackingImage(\"sys_b-cow-img\", 1_MiB,\n                                                       IImageManager::CREATE_IMAGE_DEFAULT));\n    }\n\n    // Redo the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->UnmapUpdateSnapshot(\"sys_b\"));\n\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Check that target partitions can be mapped.\n    EXPECT_TRUE(MapUpdateSnapshots());\n}\n\n// Test that the old partitions are not modified.\nTEST_F(SnapshotUpdateTest, TestRollback) {\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->UnmapUpdateSnapshot(\"sys_b\"));\n\n    AddOperationForPartitions();\n\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Write some data to target partitions.\n    ASSERT_TRUE(WriteSnapshots());\n\n    // Assert that source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Check that the target partitions have the same content.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    // Simulate shutting down the device again.\n    ASSERT_TRUE(UnmapAll());\n    init = NewManagerForFirstStageMount(\"_a\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_FALSE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Assert that the source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n}\n\n// Test that if an update is applied but not booted into, it can be canceled.\nTEST_F(SnapshotUpdateTest, CancelAfterApply) {\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n    ASSERT_TRUE(sm->CancelUpdate());\n}\n\nstatic std::vector<Interval> ToIntervals(const std::vector<std::unique_ptr<Extent>>& extents) {\n    std::vector<Interval> ret;\n    std::transform(extents.begin(), extents.end(), std::back_inserter(ret),\n                   [](const auto& extent) { return extent->AsLinearExtent()->AsInterval(); });\n    return ret;\n}\n\n// Test that at the second update, old COW partition spaces are reclaimed.\nTEST_F(SnapshotUpdateTest, ReclaimCow) {\n    // Make sure VABC cows are small enough that they fit in fake_super.\n    sys_->set_estimate_cow_size(64_KiB);\n    vnd_->set_estimate_cow_size(64_KiB);\n    prd_->set_estimate_cow_size(64_KiB);\n\n    // Execute the first update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    init = nullptr;\n\n    // Initiate the merge and wait for it to be completed.\n    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, \"_b\"));\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(new_sm->InitiateMerge());\n    ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());\n\n    // Execute the second update.\n    ASSERT_TRUE(new_sm->BeginUpdate());\n    ASSERT_TRUE(new_sm->CreateUpdateSnapshots(manifest_));\n\n    // Check that the old COW space is reclaimed and does not occupy space of mapped partitions.\n    auto src = MetadataBuilder::New(*opener_, \"super\", 1);\n    ASSERT_NE(src, nullptr);\n    auto tgt = MetadataBuilder::New(*opener_, \"super\", 0);\n    ASSERT_NE(tgt, nullptr);\n    for (const auto& cow_part_name : {\"sys_a-cow\", \"vnd_a-cow\", \"prd_a-cow\"}) {\n        auto* cow_part = tgt->FindPartition(cow_part_name);\n        ASSERT_NE(nullptr, cow_part) << cow_part_name << \" does not exist in target metadata\";\n        auto cow_intervals = ToIntervals(cow_part->extents());\n        for (const auto& old_part_name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n            auto* old_part = src->FindPartition(old_part_name);\n            ASSERT_NE(nullptr, old_part) << old_part_name << \" does not exist in source metadata\";\n            auto old_intervals = ToIntervals(old_part->extents());\n\n            auto intersect = Interval::Intersect(cow_intervals, old_intervals);\n            ASSERT_TRUE(intersect.empty()) << \"COW uses space of source partitions\";\n        }\n    }\n}\n\nTEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {\n    constexpr auto kRetrofitGroupSize = kGroupSize / 2;\n\n    // Initialize device-mapper / disk\n    ASSERT_TRUE(UnmapAll());\n    FormatFakeSuper();\n\n    // Setup source partition metadata to have both _a and _b partitions.\n    src_ = MetadataBuilder::New(*opener_, \"super\", 0);\n    ASSERT_NE(nullptr, src_);\n    for (const auto& suffix : {\"_a\"s, \"_b\"s}) {\n        ASSERT_TRUE(src_->AddGroup(group_->name() + suffix, kRetrofitGroupSize));\n        for (const auto& name : {\"sys\"s, \"vnd\"s, \"prd\"s}) {\n            auto partition = src_->AddPartition(name + suffix, group_->name() + suffix, 0);\n            ASSERT_NE(nullptr, partition);\n            ASSERT_TRUE(src_->ResizePartition(partition, 2_MiB));\n        }\n    }\n    auto metadata = src_->Export();\n    ASSERT_NE(nullptr, metadata);\n    ASSERT_TRUE(UpdatePartitionTable(*opener_, \"super\", *metadata.get(), 0));\n\n    // Flash source partitions\n    std::string path;\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(CreateLogicalPartition(\n                CreateLogicalPartitionParams{\n                        .block_device = fake_super,\n                        .metadata_slot = 0,\n                        .partition_name = name,\n                        .timeout_ms = 1s,\n                        .partition_opener = opener_.get(),\n                },\n                &path));\n        ASSERT_TRUE(WriteRandomData(path));\n        auto hash = GetHash(path);\n        ASSERT_TRUE(hash.has_value());\n        hashes_[name] = *hash;\n    }\n\n    // Setup manifest.\n    group_->set_size(kRetrofitGroupSize);\n    for (auto* partition : {sys_, vnd_, prd_}) {\n        SetSize(partition, 2_MiB);\n    }\n    AddOperationForPartitions();\n\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Test that COW image should not be created for retrofit devices; super\n    // should be big enough.\n    ASSERT_FALSE(image_manager_->BackingImageExists(\"sys_b-cow-img\"));\n    ASSERT_FALSE(image_manager_->BackingImageExists(\"vnd_b-cow-img\"));\n    ASSERT_FALSE(image_manager_->BackingImageExists(\"prd_b-cow-img\"));\n\n    // Write some data to target partitions.\n    ASSERT_TRUE(WriteSnapshots());\n\n    // Assert that source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n}\n\nTEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {\n    // Make source partitions as big as possible to force COW image to be created.\n    SetSize(sys_, 10_MiB);\n    SetSize(vnd_, 10_MiB);\n    SetSize(prd_, 10_MiB);\n    sys_->set_estimate_cow_size(12_MiB);\n    vnd_->set_estimate_cow_size(12_MiB);\n    prd_->set_estimate_cow_size(12_MiB);\n\n    src_ = MetadataBuilder::New(*opener_, \"super\", 0);\n    ASSERT_NE(src_, nullptr);\n    src_->RemoveGroupAndPartitions(group_->name() + \"_a\");\n    src_->RemoveGroupAndPartitions(group_->name() + \"_b\");\n    ASSERT_TRUE(FillFakeMetadata(src_.get(), manifest_, \"_a\"));\n    auto metadata = src_->Export();\n    ASSERT_NE(nullptr, metadata);\n    ASSERT_TRUE(UpdatePartitionTable(*opener_, \"super\", *metadata.get(), 0));\n\n    // Add operations for sys. The whole device is written.\n    AddOperation(sys_);\n\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    // Normally we should use NewManagerForFirstStageMount, but if so,\n    // \"gsid.mapped_image.sys_b-cow-img\" won't be set.\n    auto init = SnapshotManager::New(new TestDeviceInfo(fake_super, \"_b\"));\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Keep an open handle to the cow device. This should cause the merge to\n    // be incomplete.\n    auto cow_path = android::base::GetProperty(\"gsid.mapped_image.sys_b-cow-img\", \"\");\n    unique_fd fd(open(cow_path.c_str(), O_RDONLY | O_CLOEXEC));\n    ASSERT_GE(fd, 0);\n\n    // COW cannot be removed due to open fd, so expect a soft failure.\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(init->InitiateMerge());\n    ASSERT_EQ(UpdateState::MergeNeedsReboot, init->ProcessUpdateState());\n\n    // Simulate shutting down the device.\n    fd.reset();\n    ASSERT_TRUE(UnmapAll());\n\n    // init does first stage mount again.\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // sys_b should be mapped as a dm-linear device directly.\n    ASSERT_FALSE(sm->IsSnapshotDevice(\"sys_b\", nullptr));\n\n    // Merge should be able to complete now.\n    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());\n}\n\nclass MetadataMountedTest : public ::testing::Test {\n  public:\n    // This is so main() can instantiate this to invoke Cleanup.\n    virtual void TestBody() override {}\n    void SetUp() override {\n        SKIP_IF_NON_VIRTUAL_AB();\n        metadata_dir_ = test_device->GetMetadataDir();\n        ASSERT_TRUE(ReadDefaultFstab(&fstab_));\n    }\n    void TearDown() override {\n        RETURN_IF_NON_VIRTUAL_AB();\n        SetUp();\n        // Remount /metadata\n        test_device->set_recovery(false);\n        EXPECT_TRUE(android::fs_mgr::EnsurePathMounted(&fstab_, metadata_dir_));\n    }\n    AssertionResult IsMetadataMounted() {\n        Fstab mounted_fstab;\n        if (!ReadFstabFromFile(\"/proc/mounts\", &mounted_fstab)) {\n            ADD_FAILURE() << \"Failed to scan mounted volumes\";\n            return AssertionFailure() << \"Failed to scan mounted volumes\";\n        }\n\n        auto entry = GetEntryForPath(&fstab_, metadata_dir_);\n        if (entry == nullptr) {\n            return AssertionFailure() << \"No mount point found in fstab for path \" << metadata_dir_;\n        }\n\n        auto mv = GetEntryForMountPoint(&mounted_fstab, entry->mount_point);\n        if (mv == nullptr) {\n            return AssertionFailure() << metadata_dir_ << \" is not mounted\";\n        }\n        return AssertionSuccess() << metadata_dir_ << \" is mounted\";\n    }\n    std::string metadata_dir_;\n    Fstab fstab_;\n};\n\nvoid MountMetadata() {\n    MetadataMountedTest().TearDown();\n}\n\nTEST_F(MetadataMountedTest, Android) {\n    auto device = sm->EnsureMetadataMounted();\n    EXPECT_NE(nullptr, device);\n    device.reset();\n\n    EXPECT_TRUE(IsMetadataMounted());\n    EXPECT_TRUE(sm->CancelUpdate()) << \"Metadata dir should never be unmounted in Android mode\";\n}\n\nTEST_F(MetadataMountedTest, Recovery) {\n    GTEST_SKIP() << \"b/350715463\";\n\n    test_device->set_recovery(true);\n    metadata_dir_ = test_device->GetMetadataDir();\n\n    EXPECT_TRUE(android::fs_mgr::EnsurePathUnmounted(&fstab_, metadata_dir_));\n    EXPECT_FALSE(IsMetadataMounted());\n\n    auto device = sm->EnsureMetadataMounted();\n    EXPECT_NE(nullptr, device);\n    EXPECT_TRUE(IsMetadataMounted());\n\n    device.reset();\n    EXPECT_FALSE(IsMetadataMounted());\n}\n\n// Test that during a merge, we can wipe data in recovery.\nTEST_F(SnapshotUpdateTest, MergeInRecovery) {\n    // Execute the first update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    init = nullptr;\n\n    // Initiate the merge and then immediately stop it to simulate a reboot.\n    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, \"_b\"));\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(new_sm->InitiateMerge());\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate a reboot into recovery.\n    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, \"_b\");\n    test_device->set_recovery(true);\n    new_sm = NewManagerForFirstStageMount(test_device.release());\n\n    ASSERT_TRUE(new_sm->HandleImminentDataWipe());\n    ASSERT_EQ(new_sm->GetUpdateState(), UpdateState::None);\n}\n\n// Test that a merge does not clear the snapshot state in fastboot.\nTEST_F(SnapshotUpdateTest, MergeInFastboot) {\n    // Execute the first update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    init = nullptr;\n\n    // Initiate the merge and then immediately stop it to simulate a reboot.\n    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, \"_b\"));\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(new_sm->InitiateMerge());\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate a reboot into recovery.\n    auto test_device = std::make_unique<TestDeviceInfo>(fake_super, \"_b\");\n    test_device->set_recovery(true);\n    new_sm = NewManagerForFirstStageMount(test_device.release());\n\n    ASSERT_TRUE(new_sm->FinishMergeInRecovery());\n\n    ASSERT_TRUE(UnmapAll());\n\n    auto mount = new_sm->EnsureMetadataMounted();\n    ASSERT_TRUE(mount && mount->HasDevice());\n    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);\n\n    // Finish the merge in a normal boot.\n    test_device = std::make_unique<TestDeviceInfo>(fake_super, \"_b\");\n    init = NewManagerForFirstStageMount(test_device.release());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    init = nullptr;\n\n    test_device = std::make_unique<TestDeviceInfo>(fake_super, \"_b\");\n    new_sm = NewManagerForFirstStageMount(test_device.release());\n    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::MergeCompleted);\n    ASSERT_EQ(new_sm->ProcessUpdateState(), UpdateState::None);\n}\n\n// Test that after an OTA, before a merge, we can wipe data in recovery.\nTEST_F(SnapshotUpdateTest, DataWipeRollbackInRecovery) {\n    // Execute the first update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate a reboot into recovery.\n    auto test_device = new TestDeviceInfo(fake_super, \"_b\");\n    test_device->set_recovery(true);\n    auto new_sm = NewManagerForFirstStageMount(test_device);\n\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);\n    ASSERT_TRUE(new_sm->HandleImminentDataWipe());\n    // Manually mount metadata so that we can call GetUpdateState() below.\n    MountMetadata();\n    EXPECT_TRUE(test_device->IsSlotUnbootable(1));\n    EXPECT_FALSE(test_device->IsSlotUnbootable(0));\n}\n\n// Test that after an OTA and a bootloader rollback with no merge, we can wipe\n// data in recovery.\nTEST_F(SnapshotUpdateTest, DataWipeAfterRollback) {\n    // Execute the first update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate a rollback, with reboot into recovery.\n    auto test_device = new TestDeviceInfo(fake_super, \"_a\");\n    test_device->set_recovery(true);\n    auto new_sm = NewManagerForFirstStageMount(test_device);\n\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);\n    ASSERT_TRUE(new_sm->HandleImminentDataWipe());\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);\n    EXPECT_FALSE(test_device->IsSlotUnbootable(0));\n    EXPECT_FALSE(test_device->IsSlotUnbootable(1));\n}\n\n// Test update package that requests data wipe.\nTEST_F(SnapshotUpdateTest, DataWipeRequiredInPackage) {\n    AddOperationForPartitions();\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Write some data to target partitions.\n    ASSERT_TRUE(WriteSnapshots());\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate a reboot into recovery.\n    auto test_device = new TestDeviceInfo(fake_super, \"_b\");\n    test_device->set_recovery(true);\n    auto new_sm = NewManagerForFirstStageMount(test_device);\n\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);\n    ASSERT_TRUE(new_sm->HandleImminentDataWipe());\n    // Manually mount metadata so that we can call GetUpdateState() below.\n    MountMetadata();\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);\n    ASSERT_FALSE(test_device->IsSlotUnbootable(1));\n    ASSERT_FALSE(test_device->IsSlotUnbootable(0));\n\n    ASSERT_TRUE(UnmapAll());\n\n    // Now reboot into new slot.\n    test_device = new TestDeviceInfo(fake_super, \"_b\");\n    auto init = NewManagerForFirstStageMount(test_device);\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    // Verify that we are on the downgraded build.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name)) << name;\n    }\n}\n\n// Cancel an OTA in recovery.\nTEST_F(SnapshotUpdateTest, CancelInRecovery) {\n    AddOperationForPartitions();\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Write some data to target partitions.\n    ASSERT_TRUE(WriteSnapshots());\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate a reboot into recovery.\n    auto test_device = new TestDeviceInfo(fake_super, \"_b\");\n    test_device->set_recovery(true);\n    auto new_sm = NewManagerForFirstStageMount(test_device);\n\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);\n    ASSERT_FALSE(new_sm->IsCancelUpdateSafe());\n    ASSERT_TRUE(new_sm->CancelUpdate());\n\n    ASSERT_TRUE(new_sm->EnsureImageManager());\n    auto im = new_sm->image_manager();\n    ASSERT_NE(im, nullptr);\n    ASSERT_TRUE(im->IsImageDisabled(\"sys_b\"));\n    ASSERT_TRUE(im->IsImageDisabled(\"vnd_b\"));\n    ASSERT_TRUE(im->IsImageDisabled(\"prd_b\"));\n}\n\n// Test update package that requests data wipe.\nTEST_F(SnapshotUpdateTest, DataWipeWithStaleSnapshots) {\n    AddOperationForPartitions();\n\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Write some data to target partitions.\n    ASSERT_TRUE(WriteSnapshots());\n\n    // Create a stale snapshot that should not exist.\n    {\n        ASSERT_TRUE(AcquireLock());\n\n        PartitionCowCreator cow_creator = {\n                .using_snapuserd = snapuserd_required_,\n                .compression_algorithm = snapuserd_required_ ? FLAGS_compression_method : \"\",\n        };\n        SnapshotStatus status;\n        status.set_name(\"sys_a\");\n        status.set_device_size(1_MiB);\n        status.set_snapshot_size(2_MiB);\n        status.set_cow_partition_size(2_MiB);\n\n        ASSERT_TRUE(sm->CreateSnapshot(lock_.get(), &cow_creator, &status));\n        lock_ = nullptr;\n\n        ASSERT_TRUE(sm->EnsureImageManager());\n        ASSERT_TRUE(sm->image_manager()->CreateBackingImage(\"sys_a\", 1_MiB, 0));\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(true /* wipe */));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate a reboot into recovery.\n    auto test_device = new TestDeviceInfo(fake_super, \"_b\");\n    test_device->set_recovery(true);\n    auto new_sm = NewManagerForFirstStageMount(test_device);\n\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::Unverified);\n    ASSERT_TRUE(new_sm->HandleImminentDataWipe());\n    // Manually mount metadata so that we can call GetUpdateState() below.\n    MountMetadata();\n    EXPECT_EQ(new_sm->GetUpdateState(), UpdateState::None);\n    ASSERT_FALSE(test_device->IsSlotUnbootable(1));\n    ASSERT_FALSE(test_device->IsSlotUnbootable(0));\n\n    ASSERT_TRUE(UnmapAll());\n\n    // Now reboot into new slot.\n    test_device = new TestDeviceInfo(fake_super, \"_b\");\n    auto init = NewManagerForFirstStageMount(test_device);\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    // Verify that we are on the downgraded build.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name)) << name;\n    }\n}\n\nTEST_F(SnapshotUpdateTest, Hashtree) {\n    constexpr auto partition_size = 4_MiB;\n    constexpr auto data_size = 3_MiB;\n    constexpr auto hashtree_size = 512_KiB;\n    constexpr auto fec_size = partition_size - data_size - hashtree_size;\n\n    const auto block_size = manifest_.block_size();\n    SetSize(sys_, partition_size);\n    AddOperation(sys_, data_size);\n\n    sys_->set_estimate_cow_size(partition_size + data_size);\n\n    // Set hastree extents.\n    sys_->mutable_hash_tree_data_extent()->set_start_block(0);\n    sys_->mutable_hash_tree_data_extent()->set_num_blocks(data_size / block_size);\n\n    sys_->mutable_hash_tree_extent()->set_start_block(data_size / block_size);\n    sys_->mutable_hash_tree_extent()->set_num_blocks(hashtree_size / block_size);\n\n    // Set FEC extents.\n    sys_->mutable_fec_data_extent()->set_start_block(0);\n    sys_->mutable_fec_data_extent()->set_num_blocks((data_size + hashtree_size) / block_size);\n\n    sys_->mutable_fec_extent()->set_start_block((data_size + hashtree_size) / block_size);\n    sys_->mutable_fec_extent()->set_num_blocks(fec_size / block_size);\n\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Map and write some data to target partition.\n    ASSERT_TRUE(MapUpdateSnapshots({\"vnd_b\", \"prd_b\"}));\n    ASSERT_TRUE(WriteSnapshotAndHash(sys_));\n\n    // Finish update.\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Check that the target partition have the same content. Hashtree and FEC extents\n    // should be accounted for.\n    ASSERT_TRUE(IsPartitionUnchanged(\"sys_b\"));\n}\n\n// Test for overflow bit after update\nTEST_F(SnapshotUpdateTest, Overflow) {\n    if (snapuserd_required_) {\n        GTEST_SKIP() << \"No overflow bit set for snapuserd COWs\";\n    }\n\n    const auto actual_write_size = GetSize(sys_);\n    const auto declared_write_size = actual_write_size - 1_MiB;\n\n    AddOperation(sys_, declared_write_size);\n\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Map and write some data to target partitions.\n    ASSERT_TRUE(MapUpdateSnapshots({\"vnd_b\", \"prd_b\"}));\n    ASSERT_TRUE(WriteSnapshotAndHash(sys_));\n\n    std::vector<android::dm::DeviceMapper::TargetInfo> table;\n    ASSERT_TRUE(DeviceMapper::Instance().GetTableStatus(\"sys_b\", &table));\n    ASSERT_EQ(1u, table.size());\n    EXPECT_TRUE(table[0].IsOverflowSnapshot());\n\n    ASSERT_FALSE(sm->FinishedSnapshotWrites(false))\n            << \"FinishedSnapshotWrites should detect overflow of CoW device.\";\n}\n\nTEST_F(SnapshotUpdateTest, AddPartition) {\n    group_->add_partition_names(\"dlkm\");\n\n    auto dlkm = manifest_.add_partitions();\n    dlkm->set_partition_name(\"dlkm\");\n    dlkm->set_estimate_cow_size(2_MiB);\n    SetSize(dlkm, 3_MiB);\n\n    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs\n    // fit in super, but not |prd|.\n    constexpr uint64_t partition_size = 3788_KiB;\n    SetSize(sys_, partition_size);\n    SetSize(vnd_, partition_size);\n    SetSize(prd_, partition_size);\n    SetSize(dlkm, partition_size);\n\n    AddOperationForPartitions({sys_, vnd_, prd_, dlkm});\n\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    // Write some data to target partitions.\n    for (const auto& partition : {sys_, vnd_, prd_, dlkm}) {\n        ASSERT_TRUE(WriteSnapshotAndHash(partition));\n    }\n\n    // Assert that source partitions aren't affected.\n    for (const auto& name : {\"sys_a\", \"vnd_a\", \"prd_a\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    // After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n\n    if (snapuserd_required_) {\n        ASSERT_TRUE(init->EnsureSnapuserdConnected());\n        init->set_use_first_stage_snapuserd(true);\n    }\n\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Check that the target partitions have the same content.\n    std::vector<std::string> partitions = {\"sys_b\", \"vnd_b\", \"prd_b\", \"dlkm_b\"};\n    for (const auto& name : partitions) {\n        ASSERT_TRUE(IsPartitionUnchanged(name));\n    }\n\n    if (snapuserd_required_) {\n        ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));\n        for (const auto& name : partitions) {\n            ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(name + \"-user-cow-init\"));\n        }\n    }\n\n    // Initiate the merge and wait for it to be completed.\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n    ASSERT_TRUE(init->InitiateMerge());\n    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());\n\n    // Check that the target partitions have the same content after the merge.\n    for (const auto& name : {\"sys_b\", \"vnd_b\", \"prd_b\", \"dlkm_b\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name))\n                << \"Content of \" << name << \" changes after the merge\";\n    }\n}\n\nclass AutoKill final {\n  public:\n    explicit AutoKill(pid_t pid) : pid_(pid) {}\n    ~AutoKill() {\n        if (pid_ > 0) kill(pid_, SIGKILL);\n    }\n\n    bool valid() const { return pid_ > 0; }\n\n  private:\n    pid_t pid_;\n};\n\nTEST_F(SnapshotUpdateTest, DaemonTransition) {\n    if (!snapuserd_required_) {\n        GTEST_SKIP() << \"Skipping snapuserd test\";\n    }\n\n    // Ensure a connection to the second-stage daemon, but use the first-stage\n    // code paths thereafter.\n    ASSERT_TRUE(sm->EnsureSnapuserdConnected());\n    sm->set_use_first_stage_snapuserd(true);\n\n    AddOperationForPartitions();\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n    ASSERT_TRUE(UnmapAll());\n\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n\n    ASSERT_TRUE(init->EnsureSnapuserdConnected());\n    init->set_use_first_stage_snapuserd(true);\n\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    bool userspace_snapshots = init->UpdateUsesUserSnapshots();\n\n    if (userspace_snapshots) {\n        ASSERT_EQ(access(\"/dev/dm-user/sys_b-init\", F_OK), 0);\n        ASSERT_EQ(access(\"/dev/dm-user/sys_b\", F_OK), -1);\n    } else {\n        ASSERT_EQ(access(\"/dev/dm-user/sys_b-user-cow-init\", F_OK), 0);\n        ASSERT_EQ(access(\"/dev/dm-user/sys_b-user-cow\", F_OK), -1);\n    }\n\n    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SECOND_STAGE));\n\n    // :TODO: this is a workaround to ensure the handler list stays empty. We\n    // should make this test more like actual init, and spawn two copies of\n    // snapuserd, given how many other tests we now have for normal snapuserd.\n    if (userspace_snapshots) {\n        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(\"sys_b-init\"));\n        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(\"vnd_b-init\"));\n        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(\"prd_b-init\"));\n\n        // The control device should have been renamed.\n        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(\"/dev/dm-user/sys_b-init\", 10s));\n        ASSERT_EQ(access(\"/dev/dm-user/sys_b\", F_OK), 0);\n    } else {\n        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(\"sys_b-user-cow-init\"));\n        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(\"vnd_b-user-cow-init\"));\n        ASSERT_TRUE(init->snapuserd_client()->WaitForDeviceDelete(\"prd_b-user-cow-init\"));\n\n        // The control device should have been renamed.\n        ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(\"/dev/dm-user/sys_b-user-cow-init\", 10s));\n        ASSERT_EQ(access(\"/dev/dm-user/sys_b-user-cow\", F_OK), 0);\n    }\n}\n\nTEST_F(SnapshotUpdateTest, MapAllSnapshotsWithoutSlotSwitch) {\n    MountMetadata();\n    AddOperationForPartitions();\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    if (!sm->UpdateUsesUserSnapshots()) {\n        GTEST_SKIP() << \"Test does not apply as UserSnapshots aren't enabled.\";\n    }\n\n    ASSERT_TRUE(WriteSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    if (ShouldSkipLegacyMerging()) {\n        GTEST_SKIP() << \"Skipping legacy merge test\";\n    }\n    // Mark the indicator\n    ASSERT_TRUE(sm->BootFromSnapshotsWithoutSlotSwitch());\n\n    ASSERT_TRUE(sm->EnsureSnapuserdConnected());\n    sm->set_use_first_stage_snapuserd(true);\n\n    ASSERT_TRUE(sm->NeedSnapshotsInFirstStageMount());\n\n    // Map snapshots\n    ASSERT_TRUE(sm->MapAllSnapshots(10s));\n\n    // New updates should fail\n    ASSERT_FALSE(sm->BeginUpdate());\n\n    // Snapshots cannot be cancelled\n    ASSERT_FALSE(sm->CancelUpdate());\n\n    // Merge cannot start\n    ASSERT_FALSE(sm->InitiateMerge());\n\n    // Read bytes back and verify they match the cache.\n    ASSERT_TRUE(IsPartitionUnchanged(\"sys_b\"));\n\n    // Remove the indicators\n    ASSERT_TRUE(sm->PrepareDeviceToBootWithoutSnapshot());\n\n    // Cleanup snapshots\n    ASSERT_TRUE(sm->UnmapAllSnapshots());\n}\n\nTEST_F(SnapshotUpdateTest, MapAllSnapshots) {\n    AddOperationForPartitions();\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(WriteSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n    ASSERT_TRUE(sm->MapAllSnapshots(10s));\n\n    // Read bytes back and verify they match the cache.\n    ASSERT_TRUE(IsPartitionUnchanged(\"sys_b\"));\n\n    ASSERT_TRUE(sm->UnmapAllSnapshots());\n}\n\nTEST_F(SnapshotUpdateTest, CancelOnTargetSlot) {\n    AddOperationForPartitions();\n\n    ASSERT_TRUE(UnmapAll());\n\n    // Execute the update from B->A.\n    test_device->set_slot_suffix(\"_b\");\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    std::string path;\n    ASSERT_TRUE(CreateLogicalPartition(\n            CreateLogicalPartitionParams{\n                    .block_device = fake_super,\n                    .metadata_slot = 0,\n                    .partition_name = \"sys_a\",\n                    .timeout_ms = 1s,\n                    .partition_opener = opener_.get(),\n            },\n            &path));\n\n    bool userspace_snapshots = sm->UpdateUsesUserSnapshots();\n\n    unique_fd fd;\n    if (!userspace_snapshots) {\n        // Hold sys_a open so it can't be unmapped.\n        fd.reset(open(path.c_str(), O_RDONLY));\n    }\n\n    // Switch back to \"A\", make sure we can cancel. Instead of unmapping sys_a\n    // we should simply delete the old snapshots.\n    test_device->set_slot_suffix(\"_a\");\n    ASSERT_TRUE(sm->BeginUpdate());\n}\n\nTEST_F(SnapshotUpdateTest, QueryStatusError) {\n    // Grow all partitions. Set |prd| large enough that |sys| and |vnd|'s COWs\n    // fit in super, but not |prd|.\n    constexpr uint64_t partition_size = 3788_KiB;\n    SetSize(sys_, partition_size);\n\n    AddOperationForPartitions();\n\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n\n    if (sm->UpdateUsesUserSnapshots()) {\n        GTEST_SKIP() << \"Test does not apply to userspace snapshots\";\n    }\n\n    ASSERT_TRUE(WriteSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    ASSERT_TRUE(UnmapAll());\n\n    class DmStatusFailure final : public DeviceMapperWrapper {\n      public:\n        bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) override {\n            if (!DeviceMapperWrapper::GetTableStatus(name, table)) {\n                return false;\n            }\n            if (name == \"sys_b\" && !table->empty()) {\n                auto& info = table->at(0);\n                if (DeviceMapper::GetTargetType(info.spec) == \"snapshot-merge\") {\n                    info.data = \"Merge failed\";\n                }\n            }\n            return true;\n        }\n    };\n    DmStatusFailure wrapper;\n\n    // After reboot, init does first stage mount.\n    auto info = new TestDeviceInfo(fake_super, \"_b\");\n    info->set_dm(&wrapper);\n\n    auto init = NewManagerForFirstStageMount(info);\n    ASSERT_NE(init, nullptr);\n\n    ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Initiate the merge and wait for it to be completed.\n    ASSERT_TRUE(init->InitiateMerge());\n    ASSERT_EQ(UpdateState::MergeFailed, init->ProcessUpdateState());\n\n    if (ShouldSkipLegacyMerging()) {\n        LOG(INFO) << \"Skipping legacy merge in test\";\n        return;\n    }\n\n    // Simulate a reboot that tries the merge again, with the non-failing dm.\n    ASSERT_TRUE(UnmapAll());\n    init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n    ASSERT_EQ(UpdateState::MergeCompleted, init->ProcessUpdateState());\n}\n\nTEST_F(SnapshotUpdateTest, BadCowVersion) {\n    if (!snapuserd_required_) {\n        GTEST_SKIP() << \"VABC only\";\n    }\n\n    ASSERT_TRUE(sm->BeginUpdate());\n\n    auto dynamic_partition_metadata = manifest_.mutable_dynamic_partition_metadata();\n    dynamic_partition_metadata->set_cow_version(kMinCowVersion - 1);\n    ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));\n\n    dynamic_partition_metadata->set_cow_version(kMaxCowVersion + 1);\n    ASSERT_FALSE(sm->CreateUpdateSnapshots(manifest_));\n\n    dynamic_partition_metadata->set_cow_version(kMaxCowVersion);\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n}\n\nTEST_F(SnapshotTest, FlagCheck) {\n    if (!snapuserd_required_) {\n        GTEST_SKIP() << \"Skipping snapuserd test\";\n    }\n    ASSERT_TRUE(AcquireLock());\n\n    SnapshotUpdateStatus status = sm->ReadSnapshotUpdateStatus(lock_.get());\n\n    // Set flags in proto\n    status.set_o_direct(true);\n    status.set_io_uring_enabled(true);\n    status.set_userspace_snapshots(true);\n    status.set_cow_op_merge_size(16);\n\n    sm->WriteSnapshotUpdateStatus(lock_.get(), status);\n    // Ensure a connection to the second-stage daemon, but use the first-stage\n    // code paths thereafter.\n    ASSERT_TRUE(sm->EnsureSnapuserdConnected());\n    sm->set_use_first_stage_snapuserd(true);\n\n    auto init = NewManagerForFirstStageMount(\"_b\");\n    ASSERT_NE(init, nullptr);\n\n    lock_ = nullptr;\n\n    std::vector<std::string> snapuserd_argv;\n    ASSERT_TRUE(init->PerformInitTransition(SnapshotManager::InitTransition::SELINUX_DETACH,\n                                            &snapuserd_argv));\n    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), \"-o_direct\") !=\n                snapuserd_argv.end());\n    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), \"-io_uring\") !=\n                snapuserd_argv.end());\n    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), \"-user_snapshot\") !=\n                snapuserd_argv.end());\n    ASSERT_TRUE(std::find(snapuserd_argv.begin(), snapuserd_argv.end(), \"-cow_op_merge_size=16\") !=\n                snapuserd_argv.end());\n}\n\nclass FlashAfterUpdateTest : public SnapshotUpdateTest,\n                             public WithParamInterface<std::tuple<uint32_t, bool>> {\n  public:\n    AssertionResult InitiateMerge(const std::string& slot_suffix) {\n        auto sm = SnapshotManager::New(new TestDeviceInfo(fake_super, slot_suffix));\n        if (!sm->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_)) {\n            return AssertionFailure() << \"Cannot CreateLogicalAndSnapshotPartitions\";\n        }\n        if (!sm->InitiateMerge()) {\n            return AssertionFailure() << \"Cannot initiate merge\";\n        }\n        return AssertionSuccess();\n    }\n};\n\nTEST_P(FlashAfterUpdateTest, FlashSlotAfterUpdate) {\n    // Execute the update.\n    ASSERT_TRUE(sm->BeginUpdate());\n    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));\n    ASSERT_TRUE(MapUpdateSnapshots());\n    ASSERT_TRUE(sm->FinishedSnapshotWrites(false));\n\n    // Simulate shutting down the device.\n    ASSERT_TRUE(UnmapAll());\n\n    bool after_merge = std::get<1>(GetParam());\n    if (after_merge) {\n        ASSERT_TRUE(InitiateMerge(\"_b\"));\n        // Simulate shutting down the device after merge has initiated.\n        ASSERT_TRUE(UnmapAll());\n    }\n\n    auto flashed_slot = std::get<0>(GetParam());\n    auto flashed_slot_suffix = SlotSuffixForSlotNumber(flashed_slot);\n\n    // Simulate flashing |flashed_slot|. This clears the UPDATED flag.\n    auto flashed_builder = MetadataBuilder::New(*opener_, \"super\", flashed_slot);\n    ASSERT_NE(flashed_builder, nullptr);\n    flashed_builder->RemoveGroupAndPartitions(group_->name() + flashed_slot_suffix);\n    flashed_builder->RemoveGroupAndPartitions(kCowGroupName);\n    ASSERT_TRUE(FillFakeMetadata(flashed_builder.get(), manifest_, flashed_slot_suffix));\n\n    // Deliberately remove a partition from this build so that\n    // InitiateMerge do not switch state to \"merging\". This is possible in\n    // practice because the list of dynamic partitions may change.\n    ASSERT_NE(nullptr, flashed_builder->FindPartition(\"prd\" + flashed_slot_suffix));\n    flashed_builder->RemovePartition(\"prd\" + flashed_slot_suffix);\n\n    // Note that fastbootd always updates the partition table of both slots.\n    auto flashed_metadata = flashed_builder->Export();\n    ASSERT_NE(nullptr, flashed_metadata);\n    ASSERT_TRUE(UpdatePartitionTable(*opener_, \"super\", *flashed_metadata, 0));\n    ASSERT_TRUE(UpdatePartitionTable(*opener_, \"super\", *flashed_metadata, 1));\n\n    std::string path;\n    for (const auto& name : {\"sys\", \"vnd\"}) {\n        ASSERT_TRUE(CreateLogicalPartition(\n                CreateLogicalPartitionParams{\n                        .block_device = fake_super,\n                        .metadata_slot = flashed_slot,\n                        .partition_name = name + flashed_slot_suffix,\n                        .timeout_ms = 1s,\n                        .partition_opener = opener_.get(),\n                },\n                &path));\n        ASSERT_TRUE(WriteRandomData(path));\n        auto hash = GetHash(path);\n        ASSERT_TRUE(hash.has_value());\n        hashes_[name + flashed_slot_suffix] = *hash;\n    }\n\n    // Simulate shutting down the device after flash.\n    ASSERT_TRUE(UnmapAll());\n\n    // Simulate reboot. After reboot, init does first stage mount.\n    auto init = NewManagerForFirstStageMount(flashed_slot_suffix);\n    ASSERT_NE(init, nullptr);\n\n    if (flashed_slot && after_merge) {\n        ASSERT_TRUE(init->NeedSnapshotsInFirstStageMount());\n    }\n    ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions(\"super\", snapshot_timeout_));\n\n    // Check that the target partitions have the same content.\n    for (const auto& name : {\"sys\", \"vnd\"}) {\n        ASSERT_TRUE(IsPartitionUnchanged(name + flashed_slot_suffix));\n    }\n\n    // There should be no snapshot to merge.\n    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, flashed_slot_suffix));\n    if (flashed_slot == 0 && after_merge) {\n        ASSERT_EQ(UpdateState::MergeCompleted, new_sm->ProcessUpdateState());\n    } else {\n        // update_engine calls ProcessUpdateState first -- should see Cancelled.\n        ASSERT_EQ(UpdateState::Cancelled, new_sm->ProcessUpdateState());\n    }\n\n    // Next OTA calls CancelUpdate no matter what.\n    ASSERT_TRUE(new_sm->CancelUpdate());\n}\n\nINSTANTIATE_TEST_SUITE_P(Snapshot, FlashAfterUpdateTest, Combine(Values(0, 1), Bool()),\n                         [](const TestParamInfo<FlashAfterUpdateTest::ParamType>& info) {\n                             return \"Flash\"s + (std::get<0>(info.param) ? \"New\"s : \"Old\"s) +\n                                    \"Slot\"s + (std::get<1>(info.param) ? \"After\"s : \"Before\"s) +\n                                    \"Merge\"s;\n                         });\n\nbool Mkdir(const std::string& path) {\n    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {\n        std::cerr << \"Could not mkdir \" << path << \": \" << strerror(errno) << std::endl;\n        return false;\n    }\n    return true;\n}\n\nclass SnapshotTestEnvironment : public ::testing::Environment {\n  public:\n    ~SnapshotTestEnvironment() override {}\n    void SetUp() override;\n    void TearDown() override;\n\n  private:\n    bool CreateFakeSuper();\n\n    std::unique_ptr<IImageManager> super_images_;\n};\n\nbool SnapshotTestEnvironment::CreateFakeSuper() {\n    // Create and map the fake super partition.\n    static constexpr int kImageFlags =\n            IImageManager::CREATE_IMAGE_DEFAULT | IImageManager::CREATE_IMAGE_ZERO_FILL;\n    if (!super_images_->CreateBackingImage(\"fake-super\", kSuperSize, kImageFlags)) {\n        LOG(ERROR) << \"Could not create fake super partition\";\n        return false;\n    }\n    if (!super_images_->MapImageDevice(\"fake-super\", 10s, &fake_super)) {\n        LOG(ERROR) << \"Could not map fake super partition\";\n        return false;\n    }\n    test_device->set_fake_super(fake_super);\n    return true;\n}\n\nvoid SnapshotTestEnvironment::SetUp() {\n    // b/163082876: GTEST_SKIP in Environment will make atest report incorrect results. Until\n    // that is fixed, don't call GTEST_SKIP here, but instead call GTEST_SKIP in individual test\n    // suites.\n    RETURN_IF_NON_VIRTUAL_AB_MSG(\"Virtual A/B is not enabled, skipping global setup.\\n\");\n\n    std::vector<std::string> paths = {\n            // clang-format off\n            \"/data/gsi/ota/test\",\n            \"/data/gsi/ota/test/super\",\n            \"/metadata/gsi/ota/test\",\n            \"/metadata/gsi/ota/test/super\",\n            \"/metadata/ota/test\",\n            \"/metadata/ota/test/snapshots\",\n            // clang-format on\n    };\n    for (const auto& path : paths) {\n        ASSERT_TRUE(Mkdir(path));\n    }\n\n    // Create this once, otherwise, gsid will start/stop between each test.\n    test_device = new TestDeviceInfo();\n    sm = SnapshotManager::New(test_device);\n    ASSERT_NE(nullptr, sm) << \"Could not create snapshot manager\";\n\n    // Use a separate image manager for our fake super partition.\n    super_images_ = IImageManager::Open(\"ota/test/super\", 10s);\n    ASSERT_NE(nullptr, super_images_) << \"Could not create image manager\";\n\n    // Map the old image if one exists so we can safely unmap everything that\n    // depends on it.\n    bool recreate_fake_super;\n    if (super_images_->BackingImageExists(\"fake-super\")) {\n        if (super_images_->IsImageMapped(\"fake-super\")) {\n            ASSERT_TRUE(super_images_->GetMappedImageDevice(\"fake-super\", &fake_super));\n        } else {\n            ASSERT_TRUE(super_images_->MapImageDevice(\"fake-super\", 10s, &fake_super));\n        }\n        test_device->set_fake_super(fake_super);\n        recreate_fake_super = true;\n    } else {\n        ASSERT_TRUE(CreateFakeSuper());\n        recreate_fake_super = false;\n    }\n\n    // Clean up previous run.\n    MetadataMountedTest().TearDown();\n    SnapshotUpdateTest().Cleanup();\n    SnapshotTest().Cleanup();\n\n    if (recreate_fake_super) {\n        // Clean up any old copy.\n        DeleteBackingImage(super_images_.get(), \"fake-super\");\n        ASSERT_TRUE(CreateFakeSuper());\n    }\n}\n\nvoid SnapshotTestEnvironment::TearDown() {\n    RETURN_IF_NON_VIRTUAL_AB();\n    RETURN_IF_VENDOR_ON_ANDROID_S();\n\n    if (super_images_ != nullptr) {\n        DeleteBackingImage(super_images_.get(), \"fake-super\");\n    }\n}\n\nvoid KillSnapuserd() {\n    // Detach the daemon if it's alive\n    auto snapuserd_client = SnapuserdClient::TryConnect(kSnapuserdSocket, 5s);\n    if (snapuserd_client) {\n        snapuserd_client->DetachSnapuserd();\n    }\n\n    // Now stop the service - Init will send a SIGKILL to the daemon. However,\n    // process state will move from \"running\" to \"stopping\". Only after the\n    // process is reaped by init, the service state is moved to \"stopped\".\n    //\n    // Since the tests involve starting the daemon immediately, wait for the\n    // process to completely stop (aka. wait until init reaps the terminated\n    // process).\n    android::base::SetProperty(\"ctl.stop\", \"snapuserd\");\n    if (!android::base::WaitForProperty(\"init.svc.snapuserd\", \"stopped\", 10s)) {\n        LOG(ERROR) << \"Timed out waiting for snapuserd to stop.\";\n    }\n}\n\n}  // namespace snapshot\n}  // namespace android\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    ::testing::AddGlobalTestEnvironment(new ::android::snapshot::SnapshotTestEnvironment());\n    gflags::ParseCommandLineFlags(&argc, &argv, false);\n\n    // During incremental flashing, snapshot updates are in progress.\n    //\n    // When snapshot update is in-progress, snapuserd daemon\n    // will be up and running. These tests will start and stop the daemon\n    // thereby interfering with the update and snapshot-merge progress.\n    // Hence, wait until the update is complete.\n    auto sm = android::snapshot::SnapshotManager::New();\n    std::vector<std::string> snapshot_partitions;\n    while (sm->IsUserspaceSnapshotUpdateInProgress(snapshot_partitions)) {\n        LOG(INFO) << \"Waiting for: \" << snapshot_partitions.size()\n                  << \" partitions to finish snapshot-merge\";\n        std::this_thread::sleep_for(std::chrono::milliseconds(1000));\n    }\n\n    bool vab_legacy = false;\n    if (FLAGS_force_mode == \"vab-legacy\") {\n        vab_legacy = true;\n    }\n\n    if (!vab_legacy) {\n        // This is necessary if the configuration we're testing doesn't match the device.\n        android::base::SetProperty(\"ctl.stop\", \"snapuserd\");\n        android::snapshot::KillSnapuserd();\n    }\n\n    std::unordered_set<std::string> modes = {\"\", \"vab-legacy\"};\n    if (modes.count(FLAGS_force_mode) == 0) {\n        std::cerr << \"Unexpected force_config argument\\n\";\n        return 1;\n    }\n\n    int ret = RUN_ALL_TESTS();\n\n    android::base::SetProperty(\"snapuserd.test.io_uring.force_disable\", \"0\");\n\n    if (!vab_legacy) {\n        android::snapshot::KillSnapuserd();\n    }\n    return ret;\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapshotctl.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <sysexits.h>\n#include <unistd.h>\n#include <chrono>\n#include <filesystem>\n#include <fstream>\n#include <future>\n#include <iostream>\n#include <map>\n#include <sstream>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/hex.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android/snapshot/snapshot.pb.h>\n\n#include <fs_avb/fs_avb_util.h>\n#include <fs_mgr.h>\n#include <fs_mgr_dm_linear.h>\n#include <fstab/fstab.h>\n#include <liblp/builder.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/snapshot.h>\n#include <storage_literals/storage_literals.h>\n\n#include <openssl/sha.h>\n\n#include \"partition_cow_creator.h\"\n#include \"scratch_super.h\"\n\n#include \"utility.h\"\n\n#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG\n#include <BootControlClient.h>\n#endif\n\nusing namespace std::chrono_literals;\nusing namespace std::string_literals;\nusing namespace android::storage_literals;\nusing android::base::LogdLogger;\nusing android::base::StderrLogger;\nusing android::base::TeeLogger;\nusing namespace android::dm;\nusing namespace android::fs_mgr;\nusing android::fs_mgr::CreateLogicalPartitionParams;\nusing android::fs_mgr::FindPartition;\nusing android::fs_mgr::GetPartitionSize;\nusing android::fs_mgr::PartitionOpener;\nusing android::fs_mgr::ReadMetadata;\nusing android::fs_mgr::SlotNumberForSlotSuffix;\n\nint Usage() {\n    std::cerr << \"snapshotctl: Control snapshots.\\n\"\n                 \"Usage: snapshotctl [action] [flags]\\n\"\n                 \"Actions:\\n\"\n                 \"  dump\\n\"\n                 \"    Print snapshot states.\\n\"\n                 \"  merge\\n\"\n                 \"    Deprecated.\\n\"\n                 \"  map\\n\"\n                 \"    Map all partitions at /dev/block/mapper\\n\"\n                 \"  pause-merge\\n\"\n                 \"    Pause snapshot merge\\n\"\n                 \"  resume-merge\\n\"\n                 \"    Resume snapshot merge\\n\"\n                 \"  map-snapshots <directory where snapshot patches are present>\\n\"\n                 \"    Map all snapshots based on patches present in the directory\\n\"\n                 \"  unmap-snapshots\\n\"\n                 \"    Unmap all pre-created snapshots\\n\"\n                 \"  delete-snapshots\\n\"\n                 \"    Delete all pre-created snapshots\\n\"\n                 \"  revert-snapshots\\n\"\n                 \"    Prepares devices to boot without snapshots on next boot.\\n\"\n                 \"    This does not delete the snapshot. It only removes the indicators\\n\"\n                 \"    so that first stage init will not mount from snapshots.\\n\"\n                 \"  apply-update\\n\"\n                 \"    Apply the incremental OTA update wherein the snapshots are\\n\"\n                 \"    directly written to COW block device. This will bypass update-engine\\n\"\n                 \"    and the device will be ready to boot from the target build.\\n\"\n                 \"  dump-verity-hash <directory where verity merkel tree hashes are stored> \"\n                 \"[-verify]\\n\"\n                 \"    Dump the verity merkel tree hashes at the specified path\\n\"\n                 \"    -verify: Verify the dynamic partition blocks by comparing it with verity \"\n                 \"merkel tree\\n\";\n    return EX_USAGE;\n}\n\nnamespace android {\nnamespace snapshot {\n\n#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG\nclass MapSnapshots {\n  public:\n    MapSnapshots(std::string path = \"\", bool metadata_super = false);\n    bool CreateSnapshotDevice(std::string& partition_name, std::string& patch);\n    bool InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch);\n    bool FinishSnapshotWrites();\n    bool UnmapCowImagePath(std::string& name);\n    bool DeleteSnapshots();\n    bool CleanupSnapshot();\n    bool BeginUpdate();\n    bool ApplyUpdate();\n\n  private:\n    std::optional<std::string> GetCowImagePath(std::string& name);\n    bool PrepareUpdate();\n    bool GetCowDevicePath(std::string partition_name, std::string* cow_path);\n    bool WriteSnapshotPatch(std::string cow_device, std::string patch);\n    std::string GetGroupName(const android::fs_mgr::LpMetadata& pt,\n                             const std::string& partiton_name);\n    std::unique_ptr<SnapshotManager::LockedFile> lock_;\n    std::unique_ptr<SnapshotManager> sm_;\n    std::vector<std::future<bool>> threads_;\n    std::string snapshot_dir_path_;\n    std::unordered_map<std::string, chromeos_update_engine::DynamicPartitionGroup*> group_map_;\n\n    std::vector<std::string> patchfiles_;\n    chromeos_update_engine::DeltaArchiveManifest manifest_;\n    bool metadata_super_ = false;\n};\n\nMapSnapshots::MapSnapshots(std::string path, bool metadata_super) {\n    snapshot_dir_path_ = path + \"/\";\n    metadata_super_ = metadata_super;\n}\n\nstd::string MapSnapshots::GetGroupName(const android::fs_mgr::LpMetadata& pt,\n                                       const std::string& partition_name) {\n    std::string group_name;\n    for (const auto& partition : pt.partitions) {\n        std::string name = android::fs_mgr::GetPartitionName(partition);\n        auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);\n        std::string pname = name.substr(0, name.size() - suffix.size());\n        if (pname == partition_name) {\n            std::string group_name =\n                    android::fs_mgr::GetPartitionGroupName(pt.groups[partition.group_index]);\n            return group_name.substr(0, group_name.size() - suffix.size());\n        }\n    }\n    return \"\";\n}\n\nbool MapSnapshots::PrepareUpdate() {\n    if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {\n        LOG(ERROR) << \"Failed to create OTA metadata on super\";\n        return false;\n    }\n    sm_ = SnapshotManager::New();\n\n    auto source_slot = fs_mgr_get_slot_suffix();\n    auto source_slot_number = SlotNumberForSlotSuffix(source_slot);\n    auto super_source = fs_mgr_get_super_partition_name(source_slot_number);\n\n    // Get current partition information.\n    PartitionOpener opener;\n    auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);\n    if (!source_metadata) {\n        LOG(ERROR) << \"Could not read source partition metadata.\\n\";\n        return false;\n    }\n\n    auto dap = manifest_.mutable_dynamic_partition_metadata();\n    dap->set_snapshot_enabled(true);\n    dap->set_vabc_enabled(true);\n    dap->set_vabc_compression_param(\"lz4\");\n    dap->set_cow_version(3);\n\n    for (const auto& entry : std::filesystem::directory_iterator(snapshot_dir_path_)) {\n        if (android::base::EndsWith(entry.path().generic_string(), \".patch\")) {\n            patchfiles_.push_back(android::base::Basename(entry.path().generic_string()));\n        }\n    }\n\n    for (auto& patchfile : patchfiles_) {\n        std::string parsing_file = snapshot_dir_path_ + patchfile;\n        android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));\n        if (fd < 0) {\n            LOG(ERROR) << \"Failed to open file: \" << parsing_file;\n            return false;\n        }\n        uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);\n        if (!dev_sz) {\n            LOG(ERROR) << \"Could not determine block device size: \" << parsing_file;\n            return false;\n        }\n\n        const int block_sz = 4_KiB;\n        dev_sz += block_sz - 1;\n        dev_sz &= ~(block_sz - 1);\n\n        auto npos = patchfile.rfind(\".patch\");\n        auto partition_name = patchfile.substr(0, npos);\n\n        chromeos_update_engine::DynamicPartitionGroup* group = nullptr;\n        std::string group_name = GetGroupName(*source_metadata.get(), partition_name);\n        if (group_map_.find(group_name) != group_map_.end()) {\n            group = group_map_[group_name];\n        } else {\n            group = dap->add_groups();\n            group->set_name(group_name);\n            group_map_[group_name] = group;\n        }\n        group->add_partition_names(partition_name);\n\n        auto pu = manifest_.mutable_partitions()->Add();\n        pu->set_partition_name(partition_name);\n        pu->set_estimate_cow_size(dev_sz);\n\n        CowReader reader;\n        if (!reader.Parse(fd)) {\n            LOG(ERROR) << \"COW reader parse failed\";\n            return false;\n        }\n\n        uint64_t new_device_size = 0;\n        const auto& header = reader.GetHeader();\n        if (header.prefix.major_version == 2) {\n            size_t num_ops = reader.get_num_total_data_ops();\n            new_device_size = (num_ops * header.block_size);\n        } else {\n            const auto& v3_header = reader.header_v3();\n            new_device_size = v3_header.op_count_max * v3_header.block_size;\n        }\n\n        LOG(INFO) << \"Partition: \" << partition_name << \" Group_name: \" << group_name\n                  << \" size: \" << new_device_size << \" COW-size: \" << dev_sz;\n        pu->mutable_new_partition_info()->set_size(new_device_size);\n    }\n    return true;\n}\n\nbool MapSnapshots::GetCowDevicePath(std::string partition_name, std::string* cow_path) {\n    auto& dm = android::dm::DeviceMapper::Instance();\n\n    std::string cow_device = partition_name + \"-cow-img\";\n    if (metadata_super_) {\n        // If COW device exists on /data, then data wipe cannot be done.\n        if (dm.GetDmDevicePathByName(cow_device, cow_path)) {\n            LOG(ERROR) << \"COW device exists on /data: \" << *cow_path;\n            return false;\n        }\n    }\n\n    cow_device = partition_name + \"-cow\";\n    if (dm.GetDmDevicePathByName(cow_device, cow_path)) {\n        return true;\n    }\n\n    LOG(INFO) << \"Failed to find cow path: \" << cow_device << \" Checking the device for -img path\";\n    // If the COW device exists only on /data\n    cow_device = partition_name + \"-cow-img\";\n    if (!dm.GetDmDevicePathByName(cow_device, cow_path)) {\n        LOG(ERROR) << \"Failed to cow path: \" << cow_device;\n        return false;\n    }\n    return true;\n}\n\nbool MapSnapshots::ApplyUpdate() {\n    if (!PrepareUpdate()) {\n        LOG(ERROR) << \"PrepareUpdate failed\";\n        return false;\n    }\n    if (!sm_->BeginUpdate()) {\n        LOG(ERROR) << \"BeginUpdate failed\";\n        return false;\n    }\n    if (!sm_->CreateUpdateSnapshots(manifest_)) {\n        LOG(ERROR) << \"Could not apply snapshots\";\n        return false;\n    }\n\n    LOG(INFO) << \"CreateUpdateSnapshots success\";\n    if (!sm_->MapAllSnapshots(10s)) {\n        LOG(ERROR) << \"MapAllSnapshots failed\";\n        return false;\n    }\n\n    LOG(INFO) << \"MapAllSnapshots success\";\n\n    auto target_slot = fs_mgr_get_other_slot_suffix();\n    for (auto& patchfile : patchfiles_) {\n        auto npos = patchfile.rfind(\".patch\");\n        auto partition_name = patchfile.substr(0, npos) + target_slot;\n        std::string cow_path;\n        if (!GetCowDevicePath(partition_name, &cow_path)) {\n            LOG(ERROR) << \"Failed to find cow path\";\n            return false;\n        }\n        threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch,\n                                         this, cow_path, patchfile));\n    }\n\n    bool ret = true;\n    for (auto& t : threads_) {\n        ret = t.get() && ret;\n    }\n    if (!ret) {\n        LOG(ERROR) << \"Snapshot writes failed\";\n        return false;\n    }\n    if (!sm_->UnmapAllSnapshots()) {\n        LOG(ERROR) << \"UnmapAllSnapshots failed\";\n        return false;\n    }\n\n    LOG(INFO) << \"Pre-created snapshots successfully copied\";\n    // All snapshots have been written.\n    if (!sm_->FinishedSnapshotWrites(false /* wipe */)) {\n        LOG(ERROR) << \"Could not finalize snapshot writes.\\n\";\n        return false;\n    }\n\n    auto hal = hal::BootControlClient::WaitForService();\n    if (!hal) {\n        LOG(ERROR) << \"Could not find IBootControl HAL.\\n\";\n        return false;\n    }\n    auto target_slot_number = SlotNumberForSlotSuffix(target_slot);\n    auto cr = hal->SetActiveBootSlot(target_slot_number);\n    if (!cr.IsOk()) {\n        LOG(ERROR) << \"Could not set active boot slot: \" << cr.errMsg;\n        return false;\n    }\n\n    LOG(INFO) << \"ApplyUpdate success\";\n    return true;\n}\n\nbool MapSnapshots::BeginUpdate() {\n    if (metadata_super_ && !CreateScratchOtaMetadataOnSuper()) {\n        LOG(ERROR) << \"Failed to create OTA metadata on super\";\n        return false;\n    }\n    sm_ = SnapshotManager::New();\n\n    lock_ = sm_->LockExclusive();\n    std::vector<std::string> snapshots;\n    sm_->ListSnapshots(lock_.get(), &snapshots);\n    if (!snapshots.empty()) {\n        // Snapshots are already present.\n        return true;\n    }\n\n    lock_ = nullptr;\n    if (!sm_->BeginUpdate()) {\n        LOG(ERROR) << \"BeginUpdate failed\";\n        return false;\n    }\n    lock_ = sm_->LockExclusive();\n    return true;\n}\n\nbool MapSnapshots::CreateSnapshotDevice(std::string& partition_name, std::string& patchfile) {\n    std::string parsing_file = snapshot_dir_path_ + patchfile;\n\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(parsing_file.c_str(), O_RDONLY)));\n    if (fd < 0) {\n        LOG(ERROR) << \"Failed to open file: \" << parsing_file;\n        return false;\n    }\n\n    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);\n    if (!dev_sz) {\n        LOG(ERROR) << \"Could not determine block device size: \" << parsing_file;\n        return false;\n    }\n\n    const int block_sz = 4_KiB;\n    dev_sz += block_sz - 1;\n    dev_sz &= ~(block_sz - 1);\n\n    SnapshotStatus status;\n    status.set_state(SnapshotState::CREATED);\n    status.set_using_snapuserd(true);\n    status.set_old_partition_size(0);\n    status.set_name(partition_name);\n    status.set_cow_file_size(dev_sz);\n    status.set_cow_partition_size(0);\n\n    PartitionCowCreator cow_creator;\n    cow_creator.using_snapuserd = true;\n\n    if (!sm_->CreateSnapshot(lock_.get(), &cow_creator, &status)) {\n        LOG(ERROR) << \"CreateSnapshot failed\";\n        return false;\n    }\n\n    if (!sm_->CreateCowImage(lock_.get(), partition_name)) {\n        LOG(ERROR) << \"CreateCowImage failed\";\n        return false;\n    }\n\n    return true;\n}\n\nstd::optional<std::string> MapSnapshots::GetCowImagePath(std::string& name) {\n    auto cow_dev = sm_->MapCowImage(name, 5s);\n    if (!cow_dev.has_value()) {\n        LOG(ERROR) << \"Failed to get COW device path\";\n        return std::nullopt;\n    }\n\n    LOG(INFO) << \"COW Device path: \" << cow_dev.value();\n    return cow_dev;\n}\n\nbool MapSnapshots::WriteSnapshotPatch(std::string cow_device, std::string patch) {\n    std::string patch_file = snapshot_dir_path_ + patch;\n\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(patch_file.c_str(), O_RDONLY)));\n    if (fd < 0) {\n        LOG(ERROR) << \"Failed to open file: \" << patch_file;\n        return false;\n    }\n\n    uint64_t dev_sz = lseek(fd.get(), 0, SEEK_END);\n    if (!dev_sz) {\n        std::cout << \"Could not determine block device size: \" << patch_file;\n        return false;\n    }\n\n    android::base::unique_fd cfd(TEMP_FAILURE_RETRY(open(cow_device.c_str(), O_RDWR)));\n    if (cfd < 0) {\n        LOG(ERROR) << \"Failed to open file: \" << cow_device;\n        return false;\n    }\n\n    const uint64_t read_sz = 1_MiB;\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(read_sz);\n    off_t file_offset = 0;\n\n    while (true) {\n        size_t to_read = std::min((dev_sz - file_offset), read_sz);\n        if (!android::base::ReadFullyAtOffset(fd.get(), buffer.get(), to_read, file_offset)) {\n            PLOG(ERROR) << \"ReadFullyAtOffset failed\";\n            return false;\n        }\n\n        if (!android::base::WriteFullyAtOffset(cfd, buffer.get(), to_read, file_offset)) {\n            PLOG(ERROR) << \"WriteFullyAtOffset failed\";\n            return false;\n        }\n        file_offset += to_read;\n        if (file_offset >= dev_sz) {\n            break;\n        }\n    }\n    if (fsync(cfd.get()) < 0) {\n        PLOG(ERROR) << \"Fsync failed\";\n        return false;\n    }\n    return true;\n}\n\nbool MapSnapshots::InitiateThreadedSnapshotWrite(std::string& pname, std::string& snapshot_patch) {\n    auto path = GetCowImagePath(pname);\n    if (!path.has_value()) {\n        return false;\n    }\n    threads_.emplace_back(std::async(std::launch::async, &MapSnapshots::WriteSnapshotPatch, this,\n                                     path.value(), snapshot_patch));\n    return true;\n}\n\nbool MapSnapshots::FinishSnapshotWrites() {\n    bool ret = true;\n    for (auto& t : threads_) {\n        ret = t.get() && ret;\n    }\n\n    lock_ = nullptr;\n    if (ret) {\n        LOG(INFO) << \"Pre-created snapshots successfully copied\";\n        if (!sm_->FinishedSnapshotWrites(false)) {\n            return false;\n        }\n        return sm_->BootFromSnapshotsWithoutSlotSwitch();\n    }\n\n    LOG(ERROR) << \"Snapshot copy failed\";\n    return false;\n}\n\nbool MapSnapshots::UnmapCowImagePath(std::string& name) {\n    sm_ = SnapshotManager::New();\n    return sm_->UnmapCowImage(name);\n}\n\nbool MapSnapshots::CleanupSnapshot() {\n    sm_ = SnapshotManager::New();\n    return sm_->PrepareDeviceToBootWithoutSnapshot();\n}\n\nbool MapSnapshots::DeleteSnapshots() {\n    sm_ = SnapshotManager::New();\n    lock_ = sm_->LockExclusive();\n    if (!sm_->RemoveAllUpdateState(lock_.get())) {\n        LOG(ERROR) << \"Remove All Update State failed\";\n        return false;\n    }\n    return true;\n}\n#endif\n\nbool DumpCmdHandler(int /*argc*/, char** argv) {\n    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));\n    return SnapshotManager::New()->Dump(std::cout);\n}\n\nbool MapCmdHandler(int, char** argv) {\n    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));\n    using namespace std::chrono_literals;\n    return SnapshotManager::New()->MapAllSnapshots(5000ms);\n}\n\nbool UnmapCmdHandler(int, char** argv) {\n    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));\n    return SnapshotManager::New()->UnmapAllSnapshots();\n}\n\nbool PauseSnapshotMerge(int, char** argv) {\n    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));\n    return SnapshotManager::New()->PauseSnapshotMerge();\n}\n\nbool ResumeSnapshotMerge(int, char** argv) {\n    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));\n    return SnapshotManager::New()->ResumeSnapshotMerge();\n}\n\nbool MergeCmdHandler(int /*argc*/, char** argv) {\n    android::base::InitLogging(argv, TeeLogger(LogdLogger(), &StderrLogger));\n    LOG(WARNING) << \"Deprecated. Call update_engine_client --merge instead.\";\n    return false;\n}\n\n#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG\nbool GetVerityPartitions(std::vector<std::string>& partitions) {\n    auto& dm = android::dm::DeviceMapper::Instance();\n    auto dm_block_devices = dm.FindDmPartitions();\n    if (dm_block_devices.empty()) {\n        LOG(ERROR) << \"No dm-enabled block device is found.\";\n        return false;\n    }\n\n    for (auto& block_device : dm_block_devices) {\n        std::string dm_block_name = block_device.first;\n        std::string slot_suffix = fs_mgr_get_slot_suffix();\n        std::string partition = dm_block_name + slot_suffix;\n        partitions.push_back(partition);\n    }\n    return true;\n}\n\nbool UnMapPrecreatedSnapshots(int, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n    // Make sure we are root.\n    if (::getuid() != 0) {\n        LOG(ERROR) << \"Not running as root. Try \\\"adb root\\\" first.\";\n        return EXIT_FAILURE;\n    }\n\n    std::vector<std::string> partitions;\n    if (!GetVerityPartitions(partitions)) {\n        return false;\n    }\n\n    MapSnapshots snapshot;\n    for (auto partition : partitions) {\n        if (!snapshot.UnmapCowImagePath(partition)) {\n            LOG(ERROR) << \"UnmapCowImagePath failed: \" << partition;\n        }\n    }\n    return true;\n}\n\nbool RemovePrecreatedSnapshots(int, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n    // Make sure we are root.\n    if (::getuid() != 0) {\n        LOG(ERROR) << \"Not running as root. Try \\\"adb root\\\" first.\";\n        return false;\n    }\n\n    MapSnapshots snapshot;\n    if (!snapshot.CleanupSnapshot()) {\n        LOG(ERROR) << \"CleanupSnapshot failed\";\n        return false;\n    }\n    return true;\n}\n\nbool DeletePrecreatedSnapshots(int, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n    // Make sure we are root.\n    if (::getuid() != 0) {\n        LOG(ERROR) << \"Not running as root. Try \\\"adb root\\\" first.\";\n        return EXIT_FAILURE;\n    }\n\n    MapSnapshots snapshot;\n    return snapshot.DeleteSnapshots();\n}\n\nbool ApplyUpdate(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n\n    // Make sure we are root.\n    if (::getuid() != 0) {\n        LOG(ERROR) << \"Not running as root. Try \\\"adb root\\\" first.\";\n        return EXIT_FAILURE;\n    }\n\n    if (argc < 3) {\n        std::cerr << \" apply-update <directory location where snapshot patches are present> {-w}\"\n                     \"    Apply the snapshots to the COW block device\\n\";\n        return false;\n    }\n\n    std::string path = std::string(argv[2]);\n    bool metadata_on_super = false;\n    if (argc == 4) {\n        if (std::string(argv[3]) == \"-w\") {\n            metadata_on_super = true;\n        }\n    }\n\n    if (!std::filesystem::exists(path) || std::filesystem::is_empty(path)) {\n        LOG(ERROR) << path << \" doesn't exist\";\n        return false;\n    }\n\n    MapSnapshots cow(path, metadata_on_super);\n    if (!cow.ApplyUpdate()) {\n        return false;\n    }\n    LOG(INFO) << \"Apply update success. Please reboot the device\";\n    return true;\n}\n\nstatic bool GetBlockHashFromMerkelTree(android::base::borrowed_fd image_fd, uint64_t image_size,\n                                       uint32_t data_block_size, uint32_t hash_block_size,\n                                       uint64_t tree_offset,\n                                       std::vector<std::string>& out_block_hash) {\n    uint32_t padded_digest_size = 32;\n    if (image_size % data_block_size != 0) {\n        LOG(ERROR) << \"Image_size: \" << image_size\n                   << \" not a multiple of data block size: \" << data_block_size;\n        return false;\n    }\n\n    // vector of level-size and offset\n    std::vector<std::pair<uint64_t, uint64_t>> levels;\n    uint64_t data_block_count = image_size / data_block_size;\n    uint32_t digests_per_block = hash_block_size / padded_digest_size;\n    uint32_t level_block_count = data_block_count;\n    while (level_block_count > 1) {\n        uint32_t next_level_block_count =\n                (level_block_count + digests_per_block - 1) / digests_per_block;\n        levels.emplace_back(std::make_pair(next_level_block_count * hash_block_size, 0));\n        level_block_count = next_level_block_count;\n    }\n    // root digest\n    levels.emplace_back(std::make_pair(0, 0));\n    // initialize offset\n    for (auto level = std::prev(levels.end()); level != levels.begin(); level--) {\n        std::prev(level)->second = level->second + level->first;\n    }\n\n    // We just want level 0\n    auto level = levels.begin();\n    std::string hash_block(hash_block_size, '\\0');\n    uint64_t block_offset = tree_offset + level->second;\n    uint64_t t_read_blocks = 0;\n    uint64_t blockidx = 0;\n    uint64_t num_hash_blocks = level->first / hash_block_size;\n    while ((t_read_blocks < num_hash_blocks) && (blockidx < data_block_count)) {\n        if (!android::base::ReadFullyAtOffset(image_fd, hash_block.data(), hash_block.size(),\n                                              block_offset)) {\n            LOG(ERROR) << \"Failed to read tree block at offset: \" << block_offset;\n            return false;\n        }\n\n        for (uint32_t offset = 0; offset < hash_block.size(); offset += padded_digest_size) {\n            std::string single_hash = hash_block.substr(offset, padded_digest_size);\n            out_block_hash.emplace_back(single_hash);\n\n            blockidx += 1;\n            if (blockidx >= data_block_count) {\n                break;\n            }\n        }\n\n        block_offset += hash_block_size;\n        t_read_blocks += 1;\n    }\n    return true;\n}\n\nstatic bool CalculateDigest(const void* buffer, size_t size, const void* salt, uint32_t salt_length,\n                            uint8_t* digest) {\n    SHA256_CTX ctx;\n    if (SHA256_Init(&ctx) != 1) {\n        return false;\n    }\n    if (SHA256_Update(&ctx, salt, salt_length) != 1) {\n        return false;\n    }\n    if (SHA256_Update(&ctx, buffer, size) != 1) {\n        return false;\n    }\n    if (SHA256_Final(digest, &ctx) != 1) {\n        return false;\n    }\n    return true;\n}\n\nbool verify_data_blocks(android::base::borrowed_fd fd, const std::vector<std::string>& block_hash,\n                        std::unique_ptr<android::fs_mgr::FsAvbHashtreeDescriptor>& descriptor,\n                        const std::vector<uint8_t>& salt) {\n    uint64_t data_block_count = descriptor->image_size / descriptor->data_block_size;\n    uint64_t foffset = 0;\n    uint64_t blk = 0;\n\n    std::string hash_block(descriptor->hash_block_size, '\\0');\n    while (blk < data_block_count) {\n        if (!android::base::ReadFullyAtOffset(fd, hash_block.data(), descriptor->hash_block_size,\n                                              foffset)) {\n            LOG(ERROR) << \"Failed to read from offset: \" << foffset;\n            return false;\n        }\n\n        std::string digest(32, '\\0');\n        CalculateDigest(hash_block.data(), descriptor->hash_block_size, salt.data(), salt.size(),\n                        reinterpret_cast<uint8_t*>(digest.data()));\n        if (digest != block_hash[blk]) {\n            LOG(ERROR) << \"Hash mismatch for block: \" << blk << \" Expected: \" << block_hash[blk]\n                       << \" Received: \" << digest;\n            return false;\n        }\n\n        foffset += descriptor->hash_block_size;\n        blk += 1;\n    }\n\n    return true;\n}\n\nbool DumpVerityHash(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n\n    if (::getuid() != 0) {\n        LOG(ERROR) << \"Not running as root. Try \\\"adb root\\\" first.\";\n        return EXIT_FAILURE;\n    }\n\n    if (argc < 3) {\n        std::cerr\n                << \" dump-verity-hash <directory location where verity hash is saved> {-verify}\\n\";\n        return false;\n    }\n\n    bool verification_required = false;\n    std::string hash_file_path = argv[2];\n    bool metadata_on_super = false;\n    if (argc == 4) {\n        if (argv[3] == \"-verify\"s) {\n            verification_required = true;\n        }\n    }\n\n    auto& dm = android::dm::DeviceMapper::Instance();\n    auto dm_block_devices = dm.FindDmPartitions();\n    if (dm_block_devices.empty()) {\n        LOG(ERROR) << \"No dm-enabled block device is found.\";\n        return false;\n    }\n\n    android::fs_mgr::Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        LOG(ERROR) << \"Failed to read fstab\";\n        return false;\n    }\n\n    for (const auto& pair : dm_block_devices) {\n        std::string partition_name = pair.first;\n        android::fs_mgr::FstabEntry* fstab_entry =\n                GetEntryForMountPoint(&fstab, \"/\" + partition_name);\n        auto vbmeta = LoadAndVerifyVbmeta(*fstab_entry, \"\", nullptr, nullptr, nullptr);\n        if (vbmeta == nullptr) {\n            LOG(ERROR) << \"LoadAndVerifyVbmetaByPath failed for partition: \" << partition_name;\n            return false;\n        }\n\n        auto descriptor =\n                android::fs_mgr::GetHashtreeDescriptor(partition_name, std::move(*vbmeta));\n        if (descriptor == nullptr) {\n            LOG(ERROR) << \"GetHashtreeDescriptor failed for partition: \" << partition_name;\n            return false;\n        }\n\n        std::string device_path = fstab_entry->blk_device;\n        if (!dm.GetDmDevicePathByName(fstab_entry->blk_device, &device_path)) {\n            LOG(ERROR) << \"Failed to resolve logical device path for: \" << fstab_entry->blk_device;\n            return false;\n        }\n\n        android::base::unique_fd fd(open(device_path.c_str(), O_RDONLY));\n        if (fd < 0) {\n            LOG(ERROR) << \"Failed to open file: \" << device_path;\n            return false;\n        }\n        std::vector<std::string> block_hash;\n        if (!GetBlockHashFromMerkelTree(fd, descriptor->image_size, descriptor->data_block_size,\n                                        descriptor->hash_block_size, descriptor->tree_offset,\n                                        block_hash)) {\n            LOG(ERROR) << \"GetBlockHashFromMerkelTree failed\";\n            return false;\n        }\n\n        uint64_t dev_sz = lseek(fd, 0, SEEK_END);\n        uint64_t fec_size = dev_sz - descriptor->image_size;\n        if (fec_size % descriptor->data_block_size != 0) {\n            LOG(ERROR) << \"fec_size: \" << fec_size\n                       << \" isn't multiple of: \" << descriptor->data_block_size;\n            return false;\n        }\n\n        std::vector<uint8_t> salt;\n        const std::string& salt_str = descriptor->salt;\n        bool ok = android::base::HexToBytes(salt_str, &salt);\n        if (!ok) {\n            LOG(ERROR) << \"HexToBytes conversion failed\";\n            return false;\n        }\n        uint64_t file_offset = descriptor->image_size;\n        std::vector<uint8_t> hash_block(descriptor->hash_block_size, 0);\n        while (file_offset < dev_sz) {\n            if (!android::base::ReadFullyAtOffset(fd, hash_block.data(),\n                                                  descriptor->hash_block_size, file_offset)) {\n                LOG(ERROR) << \"Failed to read tree block at offset: \" << file_offset;\n                return false;\n            }\n            std::string digest(32, '\\0');\n            CalculateDigest(hash_block.data(), descriptor->hash_block_size, salt.data(),\n                            salt.size(), reinterpret_cast<uint8_t*>(digest.data()));\n            block_hash.push_back(digest);\n            file_offset += descriptor->hash_block_size;\n            fec_size -= descriptor->hash_block_size;\n        }\n\n        if (fec_size != 0) {\n            LOG(ERROR) << \"Checksum calculation pending: \" << fec_size;\n            return false;\n        }\n\n        if (verification_required) {\n            if (!verify_data_blocks(fd, block_hash, descriptor, salt)) {\n                LOG(ERROR) << \"verify_data_blocks failed\";\n                return false;\n            }\n        }\n\n        VerityHash verity_hash;\n        verity_hash.set_partition_name(partition_name);\n        verity_hash.set_salt(salt_str);\n        for (auto hash : block_hash) {\n            verity_hash.add_block_hash(hash.data(), hash.size());\n        }\n        std::string hash_file = hash_file_path + \"/\" + partition_name + \".pb\";\n        std::string content;\n        if (!verity_hash.SerializeToString(&content)) {\n            LOG(ERROR) << \"Unable to serialize verity_hash\";\n            return false;\n        }\n        if (!WriteStringToFileAtomic(content, hash_file)) {\n            PLOG(ERROR) << \"Unable to write VerityHash to \" << hash_file;\n            return false;\n        }\n\n        LOG(INFO) << partition_name\n                  << \": GetBlockHashFromMerkelTree success. Num Blocks: \" << block_hash.size();\n    }\n    return true;\n}\n\nbool MapPrecreatedSnapshots(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n\n    // Make sure we are root.\n    if (::getuid() != 0) {\n        LOG(ERROR) << \"Not running as root. Try \\\"adb root\\\" first.\";\n        return EXIT_FAILURE;\n    }\n\n    if (argc < 3) {\n        std::cerr << \" map-snapshots <directory location where snapshot patches are present> {-w}\"\n                     \"    Map all snapshots based on patches present in the directory\\n\";\n        return false;\n    }\n\n    std::string path = std::string(argv[2]);\n    std::vector<std::string> patchfiles;\n\n    if (!std::filesystem::exists(path) || std::filesystem::is_empty(path)) {\n        LOG(ERROR) << path << \" doesn't exist\";\n        return false;\n    }\n\n    for (const auto& entry : std::filesystem::directory_iterator(path)) {\n        if (android::base::EndsWith(entry.path().generic_string(), \".patch\")) {\n            patchfiles.push_back(android::base::Basename(entry.path().generic_string()));\n        }\n    }\n    auto& dm = android::dm::DeviceMapper::Instance();\n    auto dm_block_devices = dm.FindDmPartitions();\n    if (dm_block_devices.empty()) {\n        LOG(ERROR) << \"No dm-enabled block device is found.\";\n        return false;\n    }\n\n    std::vector<std::pair<std::string, std::string>> partitions;\n    for (auto& patchfile : patchfiles) {\n        auto npos = patchfile.rfind(\".patch\");\n        auto dm_block_name = patchfile.substr(0, npos);\n        if (dm_block_devices.find(dm_block_name) != dm_block_devices.end()) {\n            std::string slot_suffix = fs_mgr_get_slot_suffix();\n            std::string partition = dm_block_name + slot_suffix;\n            partitions.push_back(std::make_pair(partition, patchfile));\n        }\n    }\n\n    bool metadata_on_super = false;\n    if (argc == 4) {\n        if (std::string(argv[3]) == \"-w\") {\n            metadata_on_super = true;\n        }\n    }\n\n    MapSnapshots cow(path, metadata_on_super);\n    if (!cow.BeginUpdate()) {\n        LOG(ERROR) << \"BeginUpdate failed\";\n        return false;\n    }\n\n    for (auto& pair : partitions) {\n        if (!cow.CreateSnapshotDevice(pair.first, pair.second)) {\n            LOG(ERROR) << \"CreateSnapshotDevice failed for: \" << pair.first;\n            return false;\n        }\n        if (!cow.InitiateThreadedSnapshotWrite(pair.first, pair.second)) {\n            LOG(ERROR) << \"InitiateThreadedSnapshotWrite failed for: \" << pair.first;\n            return false;\n        }\n    }\n\n    return cow.FinishSnapshotWrites();\n}\n\nbool CreateTestUpdate(SnapshotManager* sm) {\n    chromeos_update_engine::DeltaArchiveManifest manifest;\n\n    // We only copy system, to simplify things.\n    manifest.set_partial_update(true);\n\n    auto dap = manifest.mutable_dynamic_partition_metadata();\n    dap->set_snapshot_enabled(true);\n    dap->set_vabc_enabled(true);\n    dap->set_vabc_compression_param(\"none\");\n    dap->set_cow_version(kCowVersionMajor);\n\n    auto source_slot = fs_mgr_get_slot_suffix();\n    auto source_slot_number = SlotNumberForSlotSuffix(source_slot);\n    auto target_slot = fs_mgr_get_other_slot_suffix();\n    auto target_slot_number = SlotNumberForSlotSuffix(target_slot);\n    auto super_source = fs_mgr_get_super_partition_name(source_slot_number);\n\n    // Get current partition information.\n    PartitionOpener opener;\n    auto source_metadata = ReadMetadata(opener, super_source, source_slot_number);\n    if (!source_metadata) {\n        std::cerr << \"Could not read source partition metadata.\\n\";\n        return false;\n    }\n\n    auto system_source_name = \"system\" + source_slot;\n    auto system_source = FindPartition(*source_metadata.get(), system_source_name);\n    if (!system_source) {\n        std::cerr << \"Could not find system partition: \" << system_source_name << \".\\n\";\n        return false;\n    }\n    auto system_source_size = GetPartitionSize(*source_metadata.get(), *system_source);\n\n    // Since we only add copy operations, 64MB should be enough.\n    auto system_update = manifest.mutable_partitions()->Add();\n    system_update->set_partition_name(\"system\");\n    system_update->set_estimate_cow_size(64_MiB);\n    system_update->mutable_new_partition_info()->set_size(system_source_size);\n\n    if (!sm->CreateUpdateSnapshots(manifest)) {\n        std::cerr << \"Could not create update snapshots.\\n\";\n        return false;\n    }\n\n    // Write the \"new\" system partition.\n    auto system_target_name = \"system\" + target_slot;\n    CreateLogicalPartitionParams clpp = {\n            .block_device = fs_mgr_get_super_partition_name(target_slot_number),\n            .metadata_slot = {target_slot_number},\n            .partition_name = system_target_name,\n            .timeout_ms = 10s,\n            .partition_opener = &opener,\n    };\n    auto writer = sm->OpenSnapshotWriter(clpp, std::nullopt);\n    if (!writer) {\n        std::cerr << \"Could not open snapshot writer.\\n\";\n        return false;\n    }\n\n    for (uint64_t block = 0; block < system_source_size / 4096; block++) {\n        if (!writer->AddCopy(block, block)) {\n            std::cerr << \"Unable to add copy operation for block \" << block << \".\\n\";\n            return false;\n        }\n    }\n    if (!writer->Finalize()) {\n        std::cerr << \"Could not finalize COW for \" << system_target_name << \".\\n\";\n        return false;\n    }\n    writer = nullptr;\n\n    // Finished writing this partition, unmap.\n    if (!sm->UnmapUpdateSnapshot(system_target_name)) {\n        std::cerr << \"Could not unmap snapshot for \" << system_target_name << \".\\n\";\n        return false;\n    }\n\n    // All snapshots have been written.\n    if (!sm->FinishedSnapshotWrites(false /* wipe */)) {\n        std::cerr << \"Could not finalize snapshot writes.\\n\";\n        return false;\n    }\n\n    auto hal = hal::BootControlClient::WaitForService();\n    if (!hal) {\n        std::cerr << \"Could not find IBootControl HAL.\\n\";\n        return false;\n    }\n    auto cr = hal->SetActiveBootSlot(target_slot_number);\n    if (!cr.IsOk()) {\n        std::cerr << \"Could not set active boot slot: \" << cr.errMsg;\n        return false;\n    }\n\n    std::cerr << \"It is now safe to reboot your device. If using a physical device, make\\n\"\n              << \"sure that all physical partitions are flashed to both A and B slots.\\n\";\n    return true;\n}\n\nbool TestOtaHandler(int /* argc */, char** /* argv */) {\n    auto sm = SnapshotManager::New();\n\n    if (!sm->BeginUpdate()) {\n        std::cerr << \"Error starting update.\\n\";\n        return false;\n    }\n\n    if (!CreateTestUpdate(sm.get())) {\n        sm->CancelUpdate();\n        return false;\n    }\n    return true;\n}\n#endif\n\nstatic std::map<std::string, std::function<bool(int, char**)>> kCmdMap = {\n        // clang-format off\n        {\"dump\", DumpCmdHandler},\n        {\"merge\", MergeCmdHandler},\n        {\"map\", MapCmdHandler},\n#ifdef SNAPSHOTCTL_USERDEBUG_OR_ENG\n        {\"test-blank-ota\", TestOtaHandler},\n        {\"apply-update\", ApplyUpdate},\n        {\"map-snapshots\", MapPrecreatedSnapshots},\n        {\"unmap-snapshots\", UnMapPrecreatedSnapshots},\n        {\"delete-snapshots\", DeletePrecreatedSnapshots},\n        {\"revert-snapshots\", RemovePrecreatedSnapshots},\n        {\"dump-verity-hash\", DumpVerityHash},\n#endif\n        {\"unmap\", UnmapCmdHandler},\n        {\"pause-merge\", PauseSnapshotMerge},\n        {\"resume-merge\", ResumeSnapshotMerge},\n        // clang-format on\n};\n\n}  // namespace snapshot\n}  // namespace android\n\nint main(int argc, char** argv) {\n    using namespace android::snapshot;\n    if (argc < 2) {\n        return Usage();\n    }\n\n    for (const auto& cmd : kCmdMap) {\n        if (cmd.first == argv[1]) {\n            return cmd.second(argc, argv) ? EX_OK : EX_SOFTWARE;\n        }\n    }\n\n    return Usage();\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/Android.bp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libsnapuserd_client_defaults\",\n    defaults: [\n        \"fs_mgr_defaults\",\n    ],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n    ],\n    export_include_dirs: [\"include\"],\n    srcs: [\n        \"snapuserd_client.cpp\",\n    ],\n}\n\ncc_library_static {\n    name: \"libsnapuserd_client\",\n    defaults: [\n        \"fs_mgr_defaults\",\n        \"libsnapuserd_client_defaults\",\n    ],\n    recovery_available: true,\n    static_libs: [\n        \"libcutils_sockets\",\n        \"libfs_mgr_file_wait\",\n        \"libdm\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    export_include_dirs: [\"include\"],\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n}\n\ncc_library_static {\n    name: \"libsnapuserd\",\n    defaults: [\n        \"fs_mgr_defaults\",\n    ],\n    local_include_dirs: [\"include/\"],\n    srcs: [\n        \"dm_user_block_server.cpp\",\n        \"snapuserd_buffer.cpp\",\n        \"user-space-merge/handler_manager.cpp\",\n        \"user-space-merge/merge_worker.cpp\",\n        \"user-space-merge/read_worker.cpp\",\n        \"user-space-merge/snapuserd_core.cpp\",\n        \"user-space-merge/snapuserd_readahead.cpp\",\n        \"user-space-merge/snapuserd_transitions.cpp\",\n        \"user-space-merge/snapuserd_verify.cpp\",\n        \"user-space-merge/worker.cpp\",\n        \"utility.cpp\",\n    ],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libext4_utils\",\n        \"libsnapshot_cow\",\n        \"liburing\",\n        \"libprocessgroup\",\n        \"libprocessgroup_util\",\n        \"libjsoncpp\",\n        \"liburing_cpp\",\n    ],\n    export_include_dirs: [\"include\"],\n    header_libs: [\n        \"libcutils_headers\",\n        \"libstorage_literals_headers\",\n    ],\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    host_supported: true,\n}\n\ncc_defaults {\n    name: \"snapuserd_defaults\",\n    defaults: [\n        \"fs_mgr_defaults\",\n    ],\n    srcs: [\n        \"snapuserd_daemon.cpp\",\n        \"user-space-merge/snapuserd_server.cpp\",\n    ],\n\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    static_libs: [\n        \"libbase\",\n        \"libbrotli\",\n        \"libcutils_sockets\",\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libfs_mgr_file_wait\",\n        \"libgflags\",\n        \"liblog\",\n        \"libsnapshot_cow\",\n        \"libsnapuserd\",\n        \"libprocessgroup\",\n        \"libprocessgroup_util\",\n        \"libjsoncpp\",\n        \"libsnapuserd_client\",\n        \"libz\",\n        \"liblz4\",\n        \"libext4_utils\",\n        \"liburing\",\n        \"libzstd\",\n        \"liburing_cpp\",\n    ],\n\n    header_libs: [\n        \"libcutils_headers\",\n        \"libstorage_literals_headers\",\n    ],\n\n    system_shared_libs: [],\n\n    // snapuserd is started during early boot by first-stage init. At that\n    // point, /system is mounted using the \"dm-user\" device-mapper kernel\n    // module. dm-user routes all I/O to userspace to be handled by\n    // snapuserd, which would lead to deadlock if we had to handle page\n    // faults for its code pages.\n    static_executable: true,\n}\n\ncc_binary {\n    name: \"snapuserd\",\n    defaults: [\"snapuserd_defaults\"],\n    init_rc: [\n        \"snapuserd.rc\",\n    ],\n    static_libs: [\n        \"libsnapuserd_client\",\n    ],\n    ramdisk_available: false,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n}\n\n// This target will install to /system/bin/snapuserd_ramdisk\n// It will also create a symblink on /system/bin/snapuserd that point to\n// /system/bin/snapuserd_ramdisk .\n// This way, init can check if generic ramdisk copy exists.\ncc_binary {\n    name: \"snapuserd_ramdisk\",\n    defaults: [\"snapuserd_defaults\"],\n    init_rc: [\n        \"snapuserd.rc\",\n    ],\n    // This target is specifically for generic ramdisk, therefore we set\n    // vendor_ramdisk_available to false.\n    ramdisk_available: true,\n    vendor_ramdisk_available: false,\n    ramdisk: true,\n    symlinks: [\"snapuserd\"],\n}\n\ncc_defaults {\n    name: \"snapuserd_test_defaults\",\n    defaults: [\n        \"fs_mgr_defaults\",\n        \"libsnapshot_cow_defaults\",\n    ],\n    srcs: [\n        \"testing/dm_user_harness.cpp\",\n        \"testing/harness.cpp\",\n        \"testing/host_harness.cpp\",\n        \"user-space-merge/snapuserd_test.cpp\",\n    ],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libbrotli\",\n        \"libcutils_sockets\",\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libext4_utils\",\n        \"libfs_mgr_file_wait\",\n        \"libgflags\",\n        \"libgtest\",\n        \"libsnapshot_cow\",\n        \"libsnapuserd\",\n        \"libprocessgroup\",\n        \"libprocessgroup_util\",\n        \"libjsoncpp\",\n        \"liburing\",\n        \"libz\",\n        \"liburing_cpp\",\n    ],\n    include_dirs: [\n        \".\",\n    ],\n    header_libs: [\n        \"libstorage_literals_headers\",\n        \"libfiemap_headers\",\n        \"libcutils_headers\",\n    ],\n    test_options: {\n        min_shipping_api_level: 30,\n    },\n\n    compile_multilib: \"both\",\n    multilib: {\n        lib32: {\n            suffix: \"32\",\n        },\n        lib64: {\n            suffix: \"64\",\n        },\n    },\n\n    auto_gen_config: true,\n    require_root: true,\n}\n\ncc_test {\n    name: \"snapuserd_test\",\n    defaults: [\"snapuserd_test_defaults\"],\n    host_supported: true,\n    test_suites: [\n        \"general-tests\",\n    ],\n    test_options: {\n        test_runner_options: [\n            {\n                name: \"force-no-test-error\",\n                value: \"false\",\n            },\n            {\n                name: \"native-test-timeout\",\n                value: \"15m\",\n            },\n        ],\n    },\n}\n\n// vts tests cannot be host_supported.\ncc_test {\n    name: \"vts_snapuserd_test\",\n    defaults: [\"snapuserd_test_defaults\"],\n    test_suites: [\n        \"vts\",\n    ],\n    test_options: {\n        // VABC mandatory in Android T per VSR.\n        min_shipping_api_level: 32,\n    },\n}\n\ncc_binary_host {\n    name: \"snapuserd_extractor\",\n    defaults: [\n        \"fs_mgr_defaults\",\n        \"libsnapshot_cow_defaults\",\n    ],\n    srcs: [\n        \"testing/dm_user_harness.cpp\",\n        \"testing/harness.cpp\",\n        \"testing/host_harness.cpp\",\n        \"user-space-merge/extractor.cpp\",\n        \"snapuserd_extractor.cpp\",\n    ],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libbrotli\",\n        \"libcutils_sockets\",\n        \"libdm\",\n        \"libext2_uuid\",\n        \"libext4_utils\",\n        \"libfs_mgr_file_wait\",\n        \"libgflags\",\n        \"libsnapshot_cow\",\n        \"libsnapuserd\",\n        \"libprocessgroup\",\n        \"libjsoncpp\",\n        \"liburing\",\n        \"libz\",\n        \"liburing_cpp\",\n    ],\n    include_dirs: [\n        \".\",\n    ],\n    header_libs: [\n        \"libstorage_literals_headers\",\n        \"libfiemap_headers\",\n        \"libcutils_headers\",\n    ],\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/OWNERS",
    "content": "akailash@google.com\ndvander@google.com\ndrosen@google.com\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/dm_user_block_server.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <snapuserd/dm_user_block_server.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <snapuserd/snapuserd_kernel.h>\n#include \"snapuserd_logging.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\n\nDmUserBlockServer::DmUserBlockServer(const std::string& misc_name, unique_fd&& ctrl_fd,\n                                     Delegate* delegate, size_t buffer_size)\n    : misc_name_(misc_name), ctrl_fd_(std::move(ctrl_fd)), delegate_(delegate) {\n    buffer_.Initialize(sizeof(struct dm_user_header), buffer_size);\n}\n\nbool DmUserBlockServer::ProcessRequests() {\n    struct dm_user_header* header =\n            reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());\n    if (!android::base::ReadFully(ctrl_fd_, header, sizeof(*header))) {\n        if (errno != ENOTBLK) {\n            SNAP_PLOG(ERROR) << \"Control-read failed\";\n        }\n\n        SNAP_PLOG(DEBUG) << \"ReadDmUserHeader failed....\";\n        return false;\n    }\n\n    SNAP_LOG(DEBUG) << \"Daemon: msg->seq: \" << std::dec << header->seq;\n    SNAP_LOG(DEBUG) << \"Daemon: msg->len: \" << std::dec << header->len;\n    SNAP_LOG(DEBUG) << \"Daemon: msg->sector: \" << std::dec << header->sector;\n    SNAP_LOG(DEBUG) << \"Daemon: msg->type: \" << std::dec << header->type;\n    SNAP_LOG(DEBUG) << \"Daemon: msg->flags: \" << std::dec << header->flags;\n\n    if (!ProcessRequest(header)) {\n        if (header->type != DM_USER_RESP_ERROR) {\n            SendError();\n        }\n        return false;\n    }\n    return true;\n}\n\nbool DmUserBlockServer::ProcessRequest(dm_user_header* header) {\n    // Use the same header buffer as the response header.\n    int request_type = header->type;\n    header->type = DM_USER_RESP_SUCCESS;\n    header_response_ = true;\n\n    // Reset the output buffer.\n    buffer_.ResetBufferOffset();\n\n    switch (request_type) {\n        case DM_USER_REQ_MAP_READ:\n            return delegate_->RequestSectors(header->sector, header->len);\n\n        case DM_USER_REQ_MAP_WRITE:\n            // We should not get any write request to dm-user as we mount all\n            // partitions as read-only.\n            SNAP_LOG(ERROR) << \"Unexpected write request from dm-user\";\n            return false;\n\n        default:\n            SNAP_LOG(ERROR) << \"Unexpected request from dm-user: \" << request_type;\n            return false;\n    }\n}\n\nvoid* DmUserBlockServer::GetResponseBuffer(size_t size, size_t to_write) {\n    return buffer_.AcquireBuffer(size, to_write);\n}\n\nbool DmUserBlockServer::SendBufferedIo() {\n    return WriteDmUserPayload(buffer_.GetPayloadBytesWritten());\n}\n\nvoid DmUserBlockServer::SendError() {\n    struct dm_user_header* header =\n            reinterpret_cast<struct dm_user_header*>(buffer_.GetHeaderPtr());\n    header->type = DM_USER_RESP_ERROR;\n    // This is an issue with the dm-user interface. There\n    // is no way to propagate the I/O error back to dm-user\n    // if we have already communicated the header back. Header\n    // is responded once at the beginning; however I/O can\n    // be processed in chunks. If we encounter an I/O error\n    // somewhere in the middle of the processing, we can't communicate\n    // this back to dm-user.\n    //\n    // TODO: Fix the interface\n    CHECK(header_response_);\n\n    WriteDmUserPayload(0);\n}\n\nbool DmUserBlockServer::WriteDmUserPayload(size_t size) {\n    size_t payload_size = size;\n    void* buf = buffer_.GetPayloadBufPtr();\n    if (header_response_) {\n        payload_size += sizeof(struct dm_user_header);\n        buf = buffer_.GetBufPtr();\n    }\n\n    if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {\n        SNAP_PLOG(ERROR) << \"Write to dm-user failed size: \" << payload_size;\n        return false;\n    }\n\n    // After the first header is sent in response to a request, we cannot\n    // send any additional headers.\n    header_response_ = false;\n\n    // Reset the buffer for use by the next request.\n    buffer_.ResetBufferOffset();\n    return true;\n}\n\nDmUserBlockServerOpener::DmUserBlockServerOpener(const std::string& misc_name,\n                                                 const std::string& dm_user_path)\n    : misc_name_(misc_name), dm_user_path_(dm_user_path) {}\n\nstd::unique_ptr<IBlockServer> DmUserBlockServerOpener::Open(IBlockServer::Delegate* delegate,\n                                                            size_t buffer_size) {\n    unique_fd fd(open(dm_user_path_.c_str(), O_RDWR | O_CLOEXEC));\n    if (fd < 0) {\n        SNAP_PLOG(ERROR) << \"Could not open dm-user path: \" << dm_user_path_;\n        return nullptr;\n    }\n    return std::make_unique<DmUserBlockServer>(misc_name_, std::move(fd), delegate, buffer_size);\n}\n\nstd::shared_ptr<IBlockServerOpener> DmUserBlockServerFactory::CreateOpener(\n        const std::string& misc_name) {\n    auto dm_path = \"/dev/dm-user/\" + misc_name;\n    return std::make_shared<DmUserBlockServerOpener>(misc_name, dm_path);\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/include/snapuserd/block_server.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n\nnamespace android {\nnamespace snapshot {\n\n// These interfaces model the block device driver component of snapuserd (eg,\n// dm-user).\n\n// An open connection to a userspace block device control\nclass IBlockServer {\n  public:\n    class Delegate {\n      public:\n        virtual ~Delegate() {}\n\n        // Respond to a request for reading a contiguous run of sectors. This\n        // call should be followed by calls to GetResponseBuffer/CommitBuffer\n        // until the |size| is fulfilled.\n        //\n        // If false is returned, an error will be automatically reported unless\n        // SendError was called.\n        virtual bool RequestSectors(uint64_t sector, uint64_t size) = 0;\n    };\n\n    virtual ~IBlockServer() {}\n\n    // Process I/O requests. This can block the worker thread until either a\n    // request is available or the underlying connection has been destroyed.\n    //\n    // True indicates that one or more requests was processed. False indicates\n    // an unrecoverable condition and processing should stop.\n    virtual bool ProcessRequests() = 0;\n\n    // Return a buffer for fulfilling a RequestSectors request. This buffer\n    // is valid until calling SendBufferedIo. This cannot be called outside\n    // of RequestSectors().\n    //\n    // \"to_write\" must be <= \"size\". If it is < size, the excess bytes are\n    // available for writing, but will not be send via SendBufferedIo, and\n    // may be reallocated in the next call to GetResponseBuffer.\n    //\n    // All buffers returned are invalidated after SendBufferedIo or returning\n    // control from RequestSectors.\n    virtual void* GetResponseBuffer(size_t size, size_t to_write) = 0;\n\n    // Send all outstanding buffers to the driver, in order. This should\n    // be called at least once in response to RequestSectors. This returns\n    // ownership of any buffers returned by GetResponseBuffer.\n    //\n    // If false is returned, an error is automatically reported to the driver.\n    virtual bool SendBufferedIo() = 0;\n\n    void* GetResponseBuffer(size_t size) { return GetResponseBuffer(size, size); }\n};\n\nclass IBlockServerOpener {\n  public:\n    virtual ~IBlockServerOpener() = default;\n\n    // Open a connection to the service. This is called on the daemon thread.\n    //\n    // buffer_size is the maximum amount of buffered I/O to use.\n    virtual std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,\n                                               size_t buffer_size) = 0;\n};\n\nclass IBlockServerFactory {\n  public:\n    virtual ~IBlockServerFactory() {}\n\n    // Return a new IBlockServerOpener given a unique device name.\n    virtual std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) = 0;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/include/snapuserd/dm_user_block_server.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <android-base/unique_fd.h>\n\n#include <string>\n\n#include <snapuserd/block_server.h>\n#include <snapuserd/snapuserd_buffer.h>\n#include <snapuserd/snapuserd_kernel.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass DmUserBlockServer : public IBlockServer {\n  public:\n    DmUserBlockServer(const std::string& misc_name, android::base::unique_fd&& ctrl_fd,\n                      Delegate* delegate, size_t buffer_size);\n\n    bool ProcessRequests() override;\n    void* GetResponseBuffer(size_t size, size_t to_write) override;\n    bool SendBufferedIo() override;\n    void SendError();\n\n  private:\n    bool ProcessRequest(dm_user_header* header);\n    bool WriteDmUserPayload(size_t size);\n\n    std::string misc_name_;\n    android::base::unique_fd ctrl_fd_;\n    Delegate* delegate_;\n\n    // Per-request state.\n    BufferSink buffer_;\n    bool header_response_ = false;\n};\n\nclass DmUserBlockServerOpener : public IBlockServerOpener {\n  public:\n    DmUserBlockServerOpener(const std::string& misc_name, const std::string& dm_user_path);\n\n    std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,\n                                       size_t buffer_size) override;\n\n  private:\n    std::string misc_name_;\n    std::string dm_user_path_;\n};\n\nclass DmUserBlockServerFactory : public IBlockServerFactory {\n  public:\n    std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_buffer.h",
    "content": "// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <linux/types.h>\n#include <stdint.h>\n#include <stdlib.h>\n\n#include <iostream>\n\n#include <libsnapshot/cow_reader.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass BufferSink final {\n  public:\n    // Do not reserve any space of header by default\n    void Initialize(size_t size) { return Initialize(0, size); };\n    // This allows to set const header_size_ to be used if caller needs it\n    // for example, while working with dm_user\n    void Initialize(size_t header_size, size_t size);\n    void* GetBufPtr() { return buffer_.get(); }\n    void Clear() { memset(GetBufPtr(), 0, buffer_size_); }\n    void* GetPayloadBuffer(size_t size);\n    void* GetBuffer(size_t requested, size_t* actual);\n    void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }\n    void* GetHeaderPtr();\n    void ResetBufferOffset() { buffer_offset_ = 0; }\n    void* GetPayloadBufPtr();\n    loff_t GetPayloadBytesWritten() { return buffer_offset_; }\n\n    // Same as calling GetPayloadBuffer and then UpdateBufferOffset.\n    //\n    // This is preferred over GetPayloadBuffer as it does not require a\n    // separate call to UpdateBufferOffset.\n    void* AcquireBuffer(size_t size) { return AcquireBuffer(size, size); }\n\n    // Same as AcquireBuffer, but separates the requested size from the buffer\n    // offset. This is useful for a situation where a full run of data will be\n    // read, but only a partial amount will be returned.\n    //\n    // If size != to_write, the excess bytes may be reallocated by the next\n    // call to AcquireBuffer.\n    void* AcquireBuffer(size_t size, size_t to_write);\n\n  private:\n    std::unique_ptr<uint8_t[]> buffer_;\n    loff_t buffer_offset_;\n    size_t buffer_size_;\n    size_t header_size_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_client.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <unistd.h>\n\n#include <chrono>\n#include <string>\n\n#include <android-base/unique_fd.h>\n\nnamespace android {\nnamespace snapshot {\n\nstatic constexpr uint32_t PACKET_SIZE = 512;\n\nstatic constexpr char kSnapuserdSocket[] = \"snapuserd\";\nstatic constexpr char kSnapuserdSocketProxy[] = \"snapuserd_proxy\";\nstatic constexpr char kDaemonAliveIndicator[] = \"daemon-alive-indicator\";\n\n// Ensure that the second-stage daemon for snapuserd is running.\nbool EnsureSnapuserdStarted();\n\nclass SnapuserdClient {\n  private:\n    android::base::unique_fd sockfd_;\n\n    bool Sendmsg(const std::string& msg);\n    std::string Receivemsg();\n\n    bool ValidateConnection();\n    std::string GetDaemonAliveIndicatorPath();\n\n    void WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms);\n\n  public:\n    explicit SnapuserdClient(android::base::unique_fd&& sockfd);\n    SnapuserdClient(){};\n\n    // Attempt to connect to snapsuerd, wait for the daemon to start if\n    // connection failed.\n    static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,\n                                                    std::chrono::milliseconds timeout_ms);\n    // Attempt to connect to snapsuerd, but does not wait for the daemon to\n    // start.\n    static std::unique_ptr<SnapuserdClient> TryConnect(const std::string& socket_name,\n                                                       std::chrono::milliseconds timeout_ms);\n    bool StopSnapuserd();\n\n    // Initializing a snapuserd handler is a three-step process:\n    //\n    //  1. Client invokes InitDmUserCow. This creates the snapuserd handler and validates the\n    //     COW. The number of sectors required for the dm-user target is returned.\n    //  2. Client creates the device-mapper device with the dm-user target.\n    //  3. Client calls AttachControlDevice.\n    //\n    // The misc_name must be the \"misc_name\" given to dm-user in step 2.\n    //\n    uint64_t InitDmUserCow(const std::string& misc_name, const std::string& cow_device,\n                           const std::string& backing_device,\n                           const std::string& base_path_merge = \"\");\n    bool AttachDmUser(const std::string& misc_name);\n\n    // Wait for snapuserd to disassociate with a dm-user control device. This\n    // must ONLY be called if the control device has already been deleted.\n    bool WaitForDeviceDelete(const std::string& control_device);\n\n    // Detach snapuserd. This shuts down the listener socket, and will cause\n    // snapuserd to gracefully exit once all handler threads have terminated.\n    // This should only be used on first-stage instances of snapuserd.\n    bool DetachSnapuserd();\n\n    // Returns true if the snapuserd instance supports bridging a socket to second-stage init.\n    bool SupportsSecondStageSocketHandoff();\n\n    // Returns true if the merge is started(or resumed from crash).\n    bool InitiateMerge(const std::string& misc_name);\n\n    // Returns Merge completion percentage\n    double GetMergePercent();\n\n    // Return the status of the snapshot\n    std::string QuerySnapshotStatus(const std::string& misc_name);\n\n    // Check the update verification status - invoked by update_verifier during\n    // boot\n    bool QueryUpdateVerification();\n\n    // Check if Snapuser daemon is ready post selinux transition after OTA boot\n    // This is invoked only by init as there is no sockets setup yet during\n    // selinux transition\n    bool IsTransitionedDaemonReady();\n\n    // Remove the daemon-alive-indicator path post snapshot merge\n    bool RemoveTransitionedDaemonIndicator();\n\n    // Notify init that snapuserd daemon is ready post selinux transition\n    void NotifyTransitionDaemonIsReady();\n\n    // Pause Merge threads\n    bool PauseMerge();\n\n    // Resume Merge threads\n    bool ResumeMerge();\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <linux/types.h>\n\nnamespace android {\nnamespace snapshot {\n\n#define DM_USER_REQ_MAP_READ 0\n#define DM_USER_REQ_MAP_WRITE 1\n\n#define DM_USER_RESP_SUCCESS 0\n#define DM_USER_RESP_ERROR 1\n#define DM_USER_RESP_UNSUPPORTED 2\n\n// Kernel COW header fields\nstatic constexpr uint32_t SNAP_MAGIC = 0x70416e53;\n\nstatic constexpr uint32_t SNAPSHOT_DISK_VERSION = 1;\n\nstatic constexpr uint32_t NUM_SNAPSHOT_HDR_CHUNKS = 1;\n\nstatic constexpr uint32_t SNAPSHOT_VALID = 1;\n\n/*\n * The basic unit of block I/O is a sector. It is used in a number of contexts\n * in Linux (blk, bio, genhd). The size of one sector is 512 = 2**9\n * bytes. Variables of type sector_t represent an offset or size that is a\n * multiple of 512 bytes. Hence these two constants.\n */\nstatic constexpr uint32_t SECTOR_SHIFT = 9;\nstatic constexpr uint64_t SECTOR_SIZE = (1ULL << SECTOR_SHIFT);\n\nstatic constexpr size_t BLOCK_SZ = 4096;\nstatic constexpr size_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SZ) - 1);\n\ntypedef __u64 sector_t;\ntypedef sector_t chunk_t;\n\nstatic constexpr uint32_t CHUNK_SIZE = 8;\nstatic constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);\n\n// This structure represents the kernel COW header.\n// All the below fields should be in Little Endian format.\nstruct disk_header {\n    uint32_t magic;\n\n    /*\n     * Is this snapshot valid.  There is no way of recovering\n     * an invalid snapshot.\n     */\n    uint32_t valid;\n\n    /*\n     * Simple, incrementing version. no backward\n     * compatibility.\n     */\n    uint32_t version;\n\n    /* In sectors */\n    uint32_t chunk_size;\n} __attribute__((packed));\n\n// A disk exception is a mapping of old_chunk to new_chunk\n// old_chunk is the chunk ID of a dm-snapshot device.\n// new_chunk is the chunk ID of the COW device.\nstruct disk_exception {\n    uint64_t old_chunk;\n    uint64_t new_chunk;\n} __attribute__((packed));\n\n// Control structures to communicate with dm-user\n// It comprises of header and a payload\nstruct dm_user_header {\n    __u64 seq;\n    __u64 type;\n    __u64 flags;\n    __u64 sector;\n    __u64 len;\n} __attribute__((packed));\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/snapuserd.rc",
    "content": "service snapuserd /system/bin/snapuserd\n    socket snapuserd stream 0660 system system\n    oneshot\n    disabled\n    user root\n    group root system\n    task_profiles OtaProfiles\n    seclabel u:r:snapuserd:s0\n\nservice snapuserd_proxy /system/bin/snapuserd -socket-handoff\n    socket snapuserd stream 0660 system system\n    socket snapuserd_proxy seqpacket 0660 system root\n    oneshot\n    disabled\n    user root\n    group root system\n    seclabel u:r:snapuserd:s0\n\non property:init.svc.snapuserd=stopped\n    setprop snapuserd.ready false\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/snapuserd_buffer.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <snapuserd/snapuserd_buffer.h>\n\n#include <android-base/logging.h>\n#include <snapuserd/snapuserd_kernel.h>\n\nnamespace android {\nnamespace snapshot {\n\nvoid BufferSink::Initialize(size_t header_size, size_t size) {\n    header_size_ = header_size;\n    buffer_size_ = size + header_size;\n    buffer_offset_ = 0;\n    buffer_ = std::make_unique<uint8_t[]>(buffer_size_);\n}\n\nvoid* BufferSink::AcquireBuffer(size_t size, size_t to_write) {\n    CHECK(to_write <= size);\n\n    void* ptr = GetPayloadBuffer(size);\n    if (!ptr) {\n        return nullptr;\n    }\n    UpdateBufferOffset(to_write);\n    return ptr;\n}\n\nvoid* BufferSink::GetPayloadBuffer(size_t size) {\n    char* buffer = reinterpret_cast<char*>(GetBufPtr());\n\n    if ((buffer_size_ - buffer_offset_ - header_size_) < size) {\n        return nullptr;\n    }\n    return (char*)(&buffer[0] + header_size_ + buffer_offset_);\n}\n\nvoid* BufferSink::GetBuffer(size_t requested, size_t* actual) {\n    void* buf = GetPayloadBuffer(requested);\n    if (!buf) {\n        *actual = 0;\n        return nullptr;\n    }\n    *actual = requested;\n    return buf;\n}\n\nvoid* BufferSink::GetHeaderPtr() {\n    // If no sufficient space or header not reserved\n    if (!(header_size_ <= buffer_size_) || !header_size_) {\n        return nullptr;\n    }\n    char* buf = reinterpret_cast<char*>(GetBufPtr());\n    return (void*)(&(buf[0]));\n}\n\nvoid* BufferSink::GetPayloadBufPtr() {\n    char* buffer = reinterpret_cast<char*>(GetBufPtr());\n    return &buffer[header_size_];\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/snapuserd_client.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <arpa/inet.h>\n#include <cutils/sockets.h>\n#include <errno.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <fs_mgr/file_wait.h>\n#include <libdm/dm.h>\n#include <snapuserd/snapuserd_client.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace std::chrono_literals;\nusing android::base::unique_fd;\n\nbool EnsureSnapuserdStarted() {\n    if (android::base::GetProperty(\"init.svc.snapuserd\", \"\") != \"running\") {\n        android::base::SetProperty(\"ctl.start\", \"snapuserd\");\n        if (!android::base::WaitForProperty(\"init.svc.snapuserd\", \"running\", 10s)) {\n            LOG(ERROR) << \"Timed out waiting for snapuserd to start.\";\n            return false;\n        }\n    }\n\n    if (!android::base::WaitForProperty(\"snapuserd.ready\", \"true\", 10s)) {\n        LOG(ERROR) << \"Timed out waiting for snapuserd to be ready.\";\n        return false;\n    }\n    return true;\n}\n\nSnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}\n\nstatic inline bool IsRetryErrno() {\n    return errno == ECONNREFUSED || errno == EINTR || errno == ENOENT;\n}\n\nstd::unique_ptr<SnapuserdClient> SnapuserdClient::TryConnect(const std::string& socket_name,\n                                                             std::chrono::milliseconds timeout_ms) {\n    unique_fd fd;\n    const auto start = std::chrono::steady_clock::now();\n    while (true) {\n        fd.reset(TEMP_FAILURE_RETRY(socket_local_client(\n                socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM)));\n        if (fd >= 0) {\n            auto client = std::make_unique<SnapuserdClient>(std::move(fd));\n            if (!client->ValidateConnection()) {\n                return nullptr;\n            }\n            return client;\n        }\n        if (errno == ENOENT) {\n            LOG(INFO) << \"Daemon socket \" << socket_name\n                      << \" does not exist, return without waiting.\";\n            return nullptr;\n        }\n        if (errno == ECONNREFUSED) {\n            const auto now = std::chrono::steady_clock::now();\n            const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);\n            if (elapsed >= timeout_ms) {\n                LOG(ERROR) << \"Timed out connecting to snapuserd socket: \" << socket_name;\n                return nullptr;\n            }\n            std::this_thread::sleep_for(10ms);\n        } else {\n            PLOG(ERROR) << \"connect failed: \" << socket_name;\n            return nullptr;\n        }\n    }\n}\n\nstd::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,\n                                                          std::chrono::milliseconds timeout_ms) {\n    unique_fd fd;\n    auto start = std::chrono::steady_clock::now();\n    while (true) {\n        fd.reset(socket_local_client(socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,\n                                     SOCK_STREAM));\n        if (fd >= 0) break;\n        if (fd < 0 && !IsRetryErrno()) {\n            PLOG(ERROR) << \"connect failed: \" << socket_name;\n            return nullptr;\n        }\n\n        auto now = std::chrono::steady_clock::now();\n        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);\n        if (elapsed >= timeout_ms) {\n            LOG(ERROR) << \"Timed out connecting to snapuserd socket: \" << socket_name;\n            return nullptr;\n        }\n\n        std::this_thread::sleep_for(100ms);\n    }\n\n    auto client = std::make_unique<SnapuserdClient>(std::move(fd));\n    if (!client->ValidateConnection()) {\n        return nullptr;\n    }\n    return client;\n}\n\nvoid SnapuserdClient::WaitForServiceToTerminate(std::chrono::milliseconds timeout_ms) {\n    auto start = std::chrono::steady_clock::now();\n    while (android::base::GetProperty(\"init.svc.snapuserd\", \"\") == \"running\") {\n        auto now = std::chrono::steady_clock::now();\n        auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);\n        if (elapsed >= timeout_ms) {\n            LOG(ERROR) << \"Timed out - Snapuserd service did not stop - Forcefully terminating the \"\n                          \"service\";\n            android::base::SetProperty(\"ctl.stop\", \"snapuserd\");\n            return;\n        }\n        std::this_thread::sleep_for(100ms);\n    }\n}\n\nbool SnapuserdClient::ValidateConnection() {\n    if (!Sendmsg(\"query\")) {\n        return false;\n    }\n\n    std::string str = Receivemsg();\n\n    // If the daemon is passive then fallback to secondary active daemon. Daemon\n    // is passive during transition phase.\n    if (str.find(\"passive\") != std::string::npos) {\n        LOG(ERROR) << \"Snapuserd is terminating\";\n        return false;\n    }\n\n    if (str != \"active\") {\n        LOG(ERROR) << \"Received failure querying daemon\";\n        return false;\n    }\n    return true;\n}\n\nbool SnapuserdClient::Sendmsg(const std::string& msg) {\n    LOG(DEBUG) << \"Sendmsg: msg \" << msg << \" sockfd: \" << sockfd_;\n    ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), MSG_NOSIGNAL));\n    if (numBytesSent < 0) {\n        PLOG(ERROR) << \"Send failed\";\n        return false;\n    }\n\n    if ((size_t)numBytesSent < msg.size()) {\n        LOG(ERROR) << \"Partial data sent, expected \" << msg.size() << \" bytes, sent \"\n                   << numBytesSent;\n        return false;\n    }\n    return true;\n}\n\nbool SnapuserdClient::WaitForDeviceDelete(const std::string& control_device) {\n    std::string msg = \"delete,\" + control_device;\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd\";\n        return false;\n    }\n    std::string response = Receivemsg();\n    if (response != \"success\") {\n        LOG(ERROR) << \"Failed waiting to delete device \" << control_device;\n        return false;\n    }\n    return true;\n}\n\nbool SnapuserdClient::SupportsSecondStageSocketHandoff() {\n    std::string msg = \"supports,second_stage_socket_handoff\";\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd\";\n        return false;\n    }\n    std::string response = Receivemsg();\n    return response == \"success\";\n}\n\nstd::string SnapuserdClient::Receivemsg() {\n    char msg[PACKET_SIZE];\n    ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));\n    if (ret < 0) {\n        PLOG(ERROR) << \"Snapuserd:client: recv failed\";\n        return {};\n    }\n    if (ret == 0) {\n        LOG(DEBUG) << \"Snapuserd:client disconnected\";\n        return {};\n    }\n    return std::string(msg, ret);\n}\n\nbool SnapuserdClient::StopSnapuserd() {\n    if (!Sendmsg(\"stop\")) {\n        LOG(ERROR) << \"Failed to send stop message to snapuserd daemon\";\n        return false;\n    }\n\n    sockfd_ = {};\n    return true;\n}\n\nbool SnapuserdClient::AttachDmUser(const std::string& misc_name) {\n    std::string msg = \"start,\" + misc_name;\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd daemon\";\n        return false;\n    }\n\n    std::string str = Receivemsg();\n    if (str != \"success\") {\n        LOG(ERROR) << \"Failed to receive ack for \" << msg << \" from snapuserd daemon\";\n        return false;\n    }\n\n    LOG(DEBUG) << \"Snapuserd daemon initialized with \" << msg;\n    return true;\n}\n\nuint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,\n                                        const std::string& backing_device,\n                                        const std::string& base_path_merge) {\n    std::vector<std::string> parts;\n\n    if (base_path_merge.empty()) {\n        parts = {\"init\", misc_name, cow_device, backing_device};\n    } else {\n        // For userspace snapshots\n        parts = {\"init\", misc_name, cow_device, backing_device, base_path_merge};\n    }\n    std::string msg = android::base::Join(parts, \",\");\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd daemon\";\n        return 0;\n    }\n\n    std::string str = Receivemsg();\n\n    std::vector<std::string> input = android::base::Split(str, \",\");\n\n    if (input.empty() || input[0] != \"success\") {\n        LOG(ERROR) << \"Failed to receive number of sectors for \" << msg << \" from snapuserd daemon\";\n        return 0;\n    }\n\n    LOG(DEBUG) << \"Snapuserd daemon COW device initialized: \" << cow_device\n               << \" Num-sectors: \" << input[1];\n\n    uint64_t num_sectors = 0;\n    if (!android::base::ParseUint(input[1], &num_sectors)) {\n        LOG(ERROR) << \"Failed to parse input string to sectors\";\n        return 0;\n    }\n    return num_sectors;\n}\n\nbool SnapuserdClient::DetachSnapuserd() {\n    if (!Sendmsg(\"detach\")) {\n        LOG(ERROR) << \"Failed to detach snapuserd.\";\n        return false;\n    }\n\n    WaitForServiceToTerminate(3s);\n    return true;\n}\n\nbool SnapuserdClient::InitiateMerge(const std::string& misc_name) {\n    std::string msg = \"initiate_merge,\" + misc_name;\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd\";\n        return false;\n    }\n    std::string response = Receivemsg();\n    return response == \"success\";\n}\n\ndouble SnapuserdClient::GetMergePercent() {\n    std::string msg = \"merge_percent\";\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd\";\n        return false;\n    }\n    std::string response = Receivemsg();\n\n    // If server socket disconnects most likely because of device reboot,\n    // then we just return 0.\n    if (response.empty()) {\n        return 0.0;\n    }\n    return std::stod(response);\n}\n\nstd::string SnapuserdClient::QuerySnapshotStatus(const std::string& misc_name) {\n    std::string msg = \"getstatus,\" + misc_name;\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd\";\n        return \"snapshot-merge-failed\";\n    }\n    return Receivemsg();\n}\n\nbool SnapuserdClient::QueryUpdateVerification() {\n    std::string msg = \"update-verify\";\n    if (!Sendmsg(msg)) {\n        LOG(ERROR) << \"Failed to send message \" << msg << \" to snapuserd\";\n        return false;\n    }\n    std::string response = Receivemsg();\n    return response == \"success\";\n}\n\nstd::string SnapuserdClient::GetDaemonAliveIndicatorPath() {\n    std::string metadata_dir;\n    std::string temp_metadata_mnt = \"/mnt/scratch_ota_metadata_super\";\n\n    auto& dm = ::android::dm::DeviceMapper::Instance();\n    auto partition_name = android::base::Basename(temp_metadata_mnt);\n\n    bool invalid_partition = (dm.GetState(partition_name) == dm::DmDeviceState::INVALID);\n    std::string temp_device;\n    if (!invalid_partition && dm.GetDmDevicePathByName(partition_name, &temp_device)) {\n        metadata_dir = temp_metadata_mnt + \"/\" + \"ota/\";\n    } else {\n        metadata_dir = \"/metadata/ota/\";\n    }\n\n    return metadata_dir + std::string(kDaemonAliveIndicator);\n}\n\nbool SnapuserdClient::IsTransitionedDaemonReady() {\n    if (!android::fs_mgr::WaitForFile(GetDaemonAliveIndicatorPath(), 10s)) {\n        LOG(ERROR) << \"Timed out waiting for daemon indicator path: \"\n                   << GetDaemonAliveIndicatorPath();\n        return false;\n    }\n\n    return true;\n}\n\nbool SnapuserdClient::RemoveTransitionedDaemonIndicator() {\n    std::string error;\n    std::string filePath = GetDaemonAliveIndicatorPath();\n    if (!android::base::RemoveFileIfExists(filePath, &error)) {\n        LOG(ERROR) << \"Failed to remove DaemonAliveIndicatorPath - error: \" << error;\n        return false;\n    }\n\n    if (!android::fs_mgr::WaitForFileDeleted(filePath, 5s)) {\n        LOG(ERROR) << \"Timed out waiting for \" << filePath << \" to unlink\";\n        return false;\n    }\n\n    return true;\n}\n\nvoid SnapuserdClient::NotifyTransitionDaemonIsReady() {\n    if (!android::base::WriteStringToFile(\"1\", GetDaemonAliveIndicatorPath())) {\n        PLOG(ERROR) << \"Unable to write daemon alive indicator path: \"\n                    << GetDaemonAliveIndicatorPath();\n    }\n}\n\nbool SnapuserdClient::PauseMerge() {\n    if (!Sendmsg(\"pause_merge\")) {\n        LOG(ERROR) << \"Failed to pause snapshot merge.\";\n        return false;\n    }\n    std::string response = Receivemsg();\n    return response == \"success\";\n}\n\nbool SnapuserdClient::ResumeMerge() {\n    if (!Sendmsg(\"resume_merge\")) {\n        LOG(ERROR) << \"Failed to resume snapshot merge.\";\n        return false;\n    }\n    std::string response = Receivemsg();\n    return response == \"success\";\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <gflags/gflags.h>\n#include <snapuserd/snapuserd_client.h>\n\n#include \"snapuserd_daemon.h\"\n\nDEFINE_string(socket, android::snapshot::kSnapuserdSocket, \"Named socket or socket path.\");\nDEFINE_bool(no_socket, false,\n            \"If true, no socket is used. Each additional argument is an INIT message.\");\nDEFINE_bool(socket_handoff, false,\n            \"If true, perform a socket hand-off with an existing snapuserd instance, then exit.\");\nDEFINE_bool(user_snapshot, false, \"If true, user-space snapshots are used\");\nDEFINE_bool(io_uring, false, \"If true, io_uring feature is enabled\");\nDEFINE_bool(o_direct, false, \"If true, enable direct reads on source device\");\nDEFINE_int32(cow_op_merge_size, 0, \"number of operations to be processed at once\");\nDEFINE_int32(worker_count, 4, \"number of worker threads used to serve I/O requests to dm-user\");\n\nnamespace android {\nnamespace snapshot {\n\nbool Daemon::IsUserspaceSnapshotsEnabled() {\n    const std::string UNKNOWN = \"unknown\";\n    const std::string vendor_release =\n            android::base::GetProperty(\"ro.vendor.build.version.release_or_codename\", UNKNOWN);\n\n    // If the vendor is on Android S, install process will forcefully take the\n    // userspace snapshots path.\n    //\n    // We will not reach here post OTA reboot as the binary will be from vendor\n    // ramdisk which is on Android S.\n    if (vendor_release.find(\"12\") != std::string::npos) {\n        LOG(INFO) << \"Userspace snapshots enabled as vendor partition is on Android: \"\n                  << vendor_release;\n        return true;\n    }\n\n    return android::base::GetBoolProperty(\"ro.virtual_ab.userspace.snapshots.enabled\", false);\n}\n\nbool Daemon::StartDaemon(int argc, char** argv) {\n    int arg_start = gflags::ParseCommandLineFlags(&argc, &argv, true);\n\n    // Daemon launched from first stage init and during selinux transition\n    // will have the command line \"-user_snapshot\" flag set if the user-space\n    // snapshots are enabled.\n    //\n    // Daemon launched as a init service during \"socket-handoff\" and when OTA\n    // is applied will check for the property. This is ok as the system\n    // properties are valid at this point. We can't do this during first\n    // stage init and hence use the command line flags to get the information.\n    bool user_snapshots = FLAGS_user_snapshot;\n    if (!user_snapshots) {\n        user_snapshots = IsUserspaceSnapshotsEnabled();\n    }\n    if (user_snapshots) {\n        LOG(INFO) << \"Starting daemon for user-space snapshots.....\";\n        return StartServerForUserspaceSnapshots(arg_start, argc, argv);\n    } else {\n        LOG(ERROR) << \"Userspace snapshots not enabled. No support for legacy snapshots\";\n    }\n    return false;\n}\n\nbool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** argv) {\n    sigfillset(&signal_mask_);\n    sigdelset(&signal_mask_, SIGINT);\n    sigdelset(&signal_mask_, SIGTERM);\n    sigdelset(&signal_mask_, SIGUSR1);\n\n    // Masking signals here ensure that after this point, we won't handle INT/TERM\n    // until after we call into ppoll()\n    signal(SIGINT, Daemon::SignalHandler);\n    signal(SIGTERM, Daemon::SignalHandler);\n    signal(SIGPIPE, Daemon::SignalHandler);\n    signal(SIGUSR1, Daemon::SignalHandler);\n\n    MaskAllSignalsExceptIntAndTerm();\n\n    user_server_.SetServerRunning();\n    if (FLAGS_io_uring) {\n        user_server_.SetIouringEnabled();\n    }\n\n    if (FLAGS_socket_handoff) {\n        return user_server_.RunForSocketHandoff();\n    }\n    if (!FLAGS_no_socket) {\n        if (!user_server_.Start(FLAGS_socket)) {\n            return false;\n        }\n        return user_server_.Run();\n    }\n    for (int i = arg_start; i < argc; i++) {\n        auto parts = android::base::Split(argv[i], \",\");\n\n        if (parts.size() != 4) {\n            LOG(ERROR) << \"Malformed message, expected at least four sub-arguments.\";\n            return false;\n        }\n        auto handler =\n                user_server_.AddHandler(parts[0], parts[1], parts[2], parts[3], FLAGS_worker_count,\n                                        FLAGS_o_direct, FLAGS_cow_op_merge_size);\n        if (!handler || !user_server_.StartHandler(parts[0])) {\n            return false;\n        }\n    }\n\n    // We reach this point only during selinux transition during device boot.\n    // At this point, all threads are spin up and are ready to serve the I/O\n    // requests for dm-user. Lets inform init.\n    auto client = std::make_unique<SnapuserdClient>();\n    client->NotifyTransitionDaemonIsReady();\n\n    // Skip the accept() call to avoid spurious log spam. The server will still\n    // run until all handlers have completed.\n    return user_server_.WaitForSocket();\n}\n\nvoid Daemon::MaskAllSignalsExceptIntAndTerm() {\n    sigset_t signal_mask;\n    sigfillset(&signal_mask);\n    sigdelset(&signal_mask, SIGINT);\n    sigdelset(&signal_mask, SIGTERM);\n    sigdelset(&signal_mask, SIGPIPE);\n    sigdelset(&signal_mask, SIGUSR1);\n    if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {\n        PLOG(ERROR) << \"Failed to set sigprocmask\";\n    }\n}\n\nvoid Daemon::MaskAllSignals() {\n    sigset_t signal_mask;\n    sigfillset(&signal_mask);\n    if (sigprocmask(SIG_SETMASK, &signal_mask, NULL) != 0) {\n        PLOG(ERROR) << \"Couldn't mask all signals\";\n    }\n}\n\nvoid Daemon::Interrupt() {\n    // TODO: We cannot access system property during first stage init.\n    // Until we remove the dm-snapshot code, we will have this check\n    // and verify it through a temp variable.\n    if (user_server_.IsServerRunning()) {\n        user_server_.Interrupt();\n    }\n}\n\nvoid Daemon::ReceivedSocketSignal() {\n    if (user_server_.IsServerRunning()) {\n        user_server_.ReceivedSocketSignal();\n    }\n}\n\nvoid Daemon::SignalHandler(int signal) {\n    LOG(DEBUG) << \"Snapuserd received signal: \" << signal;\n    switch (signal) {\n        case SIGINT:\n        case SIGTERM: {\n            Daemon::Instance().Interrupt();\n            break;\n        }\n        case SIGPIPE: {\n            LOG(ERROR) << \"Received SIGPIPE signal\";\n            break;\n        }\n        case SIGUSR1: {\n            LOG(INFO) << \"Received SIGUSR1, attaching to proxy socket\";\n            Daemon::Instance().ReceivedSocketSignal();\n            break;\n        }\n        default:\n            LOG(ERROR) << \"Received unknown signal \" << signal;\n            break;\n    }\n}\n\n}  // namespace snapshot\n}  // namespace android\n\nint main(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n\n    android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();\n\n    if (!daemon.StartDaemon(argc, argv)) {\n        LOG(ERROR) << \"Snapuserd daemon failed to start\";\n        exit(EXIT_FAILURE);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/snapuserd_daemon.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <poll.h>\n\n#include <string>\n#include <vector>\n\n#include \"user-space-merge/snapuserd_server.h\"\n\nnamespace android {\nnamespace snapshot {\n\nclass Daemon {\n    // The Daemon class is a singleton to avoid\n    // instantiating more than once\n  public:\n    Daemon() {}\n\n    static Daemon& Instance() {\n        static Daemon instance;\n        return instance;\n    }\n\n    bool StartServerForUserspaceSnapshots(int arg_start, int argc, char** argv);\n    void Interrupt();\n    void ReceivedSocketSignal();\n    bool IsUserspaceSnapshotsEnabled();\n    bool StartDaemon(int argc, char** argv);\n\n  private:\n    // Signal mask used with ppoll()\n    sigset_t signal_mask_;\n\n    Daemon(Daemon const&) = delete;\n    void operator=(Daemon const&) = delete;\n\n    UserSnapshotServer user_server_;\n    void MaskAllSignalsExceptIntAndTerm();\n    void MaskAllSignals();\n    static void SignalHandler(int signal);\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/snapuserd_extractor.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <iostream>\n#include <memory>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <gflags/gflags.h>\n#include \"user-space-merge/extractor.h\"\n\nusing namespace std::string_literals;\n\nDEFINE_string(base, \"\", \"Base device/image\");\nDEFINE_string(cow, \"\", \"COW device/image\");\nDEFINE_string(out, \"\", \"Output path\");\nDEFINE_int32(num_sectors, 0, \"Number of sectors to read\");\n\nint main([[maybe_unused]] int argc, [[maybe_unused]] char** argv) {\n    android::base::InitLogging(argv);\n    gflags::ParseCommandLineFlags(&argc, &argv, true);\n\n    if (FLAGS_out.empty()) {\n        LOG(ERROR) << \"Missing -out argument.\";\n        return 1;\n    }\n    if (FLAGS_base.empty()) {\n        LOG(ERROR) << \"Missing -base argument.\";\n        return 1;\n    }\n    if (FLAGS_cow.empty()) {\n        LOG(ERROR) << \"missing -out argument.\";\n        return 1;\n    }\n    if (!FLAGS_num_sectors) {\n        LOG(ERROR) << \"missing -num_sectors argument.\";\n        return 1;\n    }\n\n    android::snapshot::Extractor extractor(FLAGS_base, FLAGS_cow);\n    if (!extractor.Init()) {\n        return 1;\n    }\n    if (!extractor.Extract(FLAGS_num_sectors, FLAGS_out)) {\n        return 1;\n    }\n    return 0;\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/snapuserd_logging.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <android-base/logging.h>\n\n#define SNAP_LOG(level) LOG(level) << misc_name_ << \": \"\n#define SNAP_PLOG(level) PLOG(level) << misc_name_ << \": \"\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"dm_user_harness.h\"\n\n#include <fcntl.h>\n\n#include <android-base/file.h>\n#include <fs_mgr/file_wait.h>\n#include <libdm/dm.h>\n#include <snapuserd/dm_user_block_server.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace std::chrono_literals;\nusing android::base::unique_fd;\n\nDmUserDevice::DmUserDevice(std::unique_ptr<Tempdevice>&& dev) : dev_(std::move(dev)) {}\n\nconst std::string& DmUserDevice::GetPath() {\n    return dev_->path();\n}\n\nbool DmUserDevice::Destroy() {\n    return dev_->Destroy();\n}\n\nDmUserTestHarness::DmUserTestHarness() {\n    block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();\n}\n\nstd::unique_ptr<IUserDevice> DmUserTestHarness::CreateUserDevice(const std::string& dev_name,\n                                                                 const std::string& misc_name,\n                                                                 uint64_t num_sectors) {\n    android::dm::DmTable dmuser_table;\n    dmuser_table.Emplace<android::dm::DmTargetUser>(0, num_sectors, misc_name);\n    auto dev = std::make_unique<Tempdevice>(dev_name, dmuser_table);\n    if (!dev->valid()) {\n        return nullptr;\n    }\n\n    auto misc_device = \"/dev/dm-user/\" + misc_name;\n    if (!android::fs_mgr::WaitForFile(misc_device, 10s)) {\n        return nullptr;\n    }\n\n    return std::make_unique<DmUserDevice>(std::move(dev));\n}\n\nIBlockServerFactory* DmUserTestHarness::GetBlockServerFactory() {\n    return block_server_factory_.get();\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/testing/dm_user_harness.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <android-base/unique_fd.h>\n\n#include \"harness.h\"\n#include \"temp_device.h\"\n\n#include <snapuserd/dm_user_block_server.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\n\nclass DmUserDevice final : public IUserDevice {\n  public:\n    explicit DmUserDevice(std::unique_ptr<Tempdevice>&& dev);\n    const std::string& GetPath() override;\n    bool Destroy() override;\n\n  private:\n    std::unique_ptr<Tempdevice> dev_;\n};\n\nclass DmUserTestHarness final : public ITestHarness {\n  public:\n    DmUserTestHarness();\n\n    std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,\n                                                  const std::string& misc_name,\n                                                  uint64_t num_sectors) override;\n    IBlockServerFactory* GetBlockServerFactory() override;\n    bool HasUserDevice() override { return true; }\n\n  private:\n    std::unique_ptr<DmUserBlockServerFactory> block_server_factory_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/testing/harness.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"harness.h\"\n\n#ifdef __ANDROID__\n#include <linux/memfd.h>\n#endif\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <ext4_utils/ext4_utils.h>\n#include <libdm/loop_control.h>\n#include \"snapuserd_logging.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace std::chrono_literals;\nusing android::base::unique_fd;\nusing android::dm::LoopDevice;\n\n#ifdef __ANDROID__\n// Prefer this on device since it is a real block device, which is more similar\n// to how we use snapuserd.\nclass MemoryBackedDevice final : public IBackingDevice {\n  public:\n    bool Init(uint64_t size) {\n        memfd_.reset(memfd_create(\"snapuserd_test\", MFD_ALLOW_SEALING));\n        if (memfd_ < 0) {\n            PLOG(ERROR) << \"memfd_create failed\";\n            return false;\n        }\n        if (ftruncate(memfd_.get(), size) < 0) {\n            PLOG(ERROR) << \"ftruncate failed\";\n            return false;\n        }\n        if (fcntl(memfd_.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) {\n            PLOG(ERROR) << \"fcntl seal failed\";\n            return false;\n        }\n        dev_ = std::make_unique<LoopDevice>(memfd_, 10s);\n        return dev_->valid();\n    }\n    const std::string& GetPath() override { return dev_->device(); }\n    uint64_t GetSize() override {\n        unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC));\n        if (fd < 0) {\n            PLOG(ERROR) << \"open failed: \" << GetPath();\n            return 0;\n        }\n        return get_block_device_size(fd.get());\n    }\n\n  private:\n    unique_fd memfd_;\n    std::unique_ptr<LoopDevice> dev_;\n};\n#endif\n\nclass FileBackedDevice final : public IBackingDevice {\n  public:\n    bool Init(uint64_t size) {\n        if (temp_.fd < 0) {\n            return false;\n        }\n        if (ftruncate(temp_.fd, size) < 0) {\n            PLOG(ERROR) << \"ftruncate failed: \" << temp_.path;\n            return false;\n        }\n        path_ = temp_.path;\n        return true;\n    }\n\n    const std::string& GetPath() override { return path_; }\n    uint64_t GetSize() override {\n        off_t off = lseek(temp_.fd, 0, SEEK_END);\n        if (off < 0) {\n            PLOG(ERROR) << \"lseek failed: \" << temp_.path;\n            return 0;\n        }\n        return off;\n    }\n\n  private:\n    TemporaryFile temp_;\n    std::string path_;\n};\n\nstd::unique_ptr<IBackingDevice> ITestHarness::CreateBackingDevice(uint64_t size) {\n#ifdef __ANDROID__\n    auto dev = std::make_unique<MemoryBackedDevice>();\n#else\n    auto dev = std::make_unique<FileBackedDevice>();\n#endif\n    if (!dev->Init(size)) {\n        return nullptr;\n    }\n    return dev;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/testing/harness.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stddef.h>\n#include <sys/types.h>\n\n#include <memory>\n\n#include <android-base/unique_fd.h>\n#include <snapuserd/block_server.h>\n\nnamespace android {\nnamespace snapshot {\n\n// Interface for a \"block driver in userspace\" device.\nclass IUserDevice {\n  public:\n    virtual ~IUserDevice() {}\n    virtual const std::string& GetPath() = 0;\n    virtual bool Destroy() = 0;\n};\n\n// Interface for an fd/temp file that is a block device when possible.\nclass IBackingDevice {\n  public:\n    virtual ~IBackingDevice() {}\n    virtual const std::string& GetPath() = 0;\n    virtual uint64_t GetSize() = 0;\n};\n\nclass ITestHarness {\n  public:\n    virtual ~ITestHarness() {}\n    virtual std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,\n                                                          const std::string& misc_name,\n                                                          uint64_t num_sectors) = 0;\n    virtual IBlockServerFactory* GetBlockServerFactory() = 0;\n    virtual bool HasUserDevice() = 0;\n    virtual std::unique_ptr<IBackingDevice> CreateBackingDevice(uint64_t size);\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/testing/host_harness.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"host_harness.h\"\n\n#include \"snapuserd_logging.h\"\n\nnamespace android {\nnamespace snapshot {\n\nvoid TestBlockServerQueue::WaitForShutdown() {\n    std::unique_lock lock(m_);\n    if (shutdown_) {\n        return;\n    }\n    cv_.wait(lock, [this]() -> bool { return shutdown_; });\n}\n\nvoid TestBlockServerQueue::Shutdown() {\n    std::unique_lock lock(m_);\n    shutdown_ = true;\n    cv_.notify_all();\n}\n\nTestBlockServer::TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue,\n                                 const std::string& misc_name)\n    : queue_(queue), misc_name_(misc_name) {}\n\nbool TestBlockServer::ProcessRequests() {\n    queue_->WaitForShutdown();\n    return false;\n}\n\nvoid* TestBlockServer::GetResponseBuffer(size_t size, size_t to_write) {\n    std::string buffer(size, '\\0');\n    buffered_.emplace_back(std::move(buffer), to_write);\n    return buffered_.back().first.data();\n}\n\nbool TestBlockServer::SendBufferedIo() {\n    for (const auto& [data, to_write] : buffered_) {\n        sent_io_ += data.substr(0, to_write);\n    }\n    buffered_.clear();\n    return true;\n}\n\nTestBlockServerOpener::TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,\n                                             const std::string& misc_name)\n    : queue_(queue), misc_name_(misc_name) {}\n\nstd::unique_ptr<IBlockServer> TestBlockServerOpener::Open(IBlockServer::Delegate*, size_t) {\n    return std::make_unique<TestBlockServer>(queue_, misc_name_);\n}\n\nstd::shared_ptr<TestBlockServerOpener> TestBlockServerFactory::CreateTestOpener(\n        const std::string& misc_name) {\n    if (queues_.count(misc_name)) {\n        LOG(ERROR) << \"Cannot create opener for \" << misc_name << \", already exists\";\n        return nullptr;\n    }\n    auto queue = std::make_shared<TestBlockServerQueue>();\n    queues_.emplace(misc_name, queue);\n    return std::make_shared<TestBlockServerOpener>(queue, misc_name);\n}\n\nstd::shared_ptr<IBlockServerOpener> TestBlockServerFactory::CreateOpener(\n        const std::string& misc_name) {\n    return CreateTestOpener(misc_name);\n}\n\nbool TestBlockServerFactory::DeleteQueue(const std::string& misc_name) {\n    auto iter = queues_.find(misc_name);\n    if (iter == queues_.end()) {\n        LOG(ERROR) << \"Cannot delete queue \" << misc_name << \", not found\";\n        return false;\n    }\n    iter->second->Shutdown();\n    queues_.erase(iter);\n    return true;\n}\n\nHostUserDevice::HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name)\n    : factory_(factory), misc_name_(misc_name) {}\n\nbool HostUserDevice::Destroy() {\n    return factory_->DeleteQueue(misc_name_);\n}\n\nstd::unique_ptr<IUserDevice> HostTestHarness::CreateUserDevice(const std::string&,\n                                                               const std::string& misc_name,\n                                                               uint64_t) {\n    return std::make_unique<HostUserDevice>(&factory_, misc_name);\n}\n\nIBlockServerFactory* HostTestHarness::GetBlockServerFactory() {\n    return &factory_;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/testing/host_harness.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <condition_variable>\n#include <mutex>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\n#include \"harness.h\"\n\nnamespace android {\nnamespace snapshot {\n\nclass TestBlockServerQueue final {\n  public:\n    void WaitForShutdown();\n    void Shutdown();\n\n  private:\n    std::mutex m_;\n    std::condition_variable cv_;\n    bool shutdown_ = false;\n};\n\nclass TestBlockServer final : public IBlockServer {\n  public:\n    TestBlockServer(std::shared_ptr<TestBlockServerQueue> queue, const std::string& misc_name);\n    bool ProcessRequests() override;\n    void* GetResponseBuffer(size_t size, size_t to_write) override;\n    bool SendBufferedIo() override;\n\n    std::string&& sent_io() { return std::move(sent_io_); }\n\n  private:\n    std::shared_ptr<TestBlockServerQueue> queue_;\n    std::string misc_name_;\n    std::string sent_io_;\n    std::vector<std::pair<std::string, size_t>> buffered_;\n};\n\nclass TestBlockServerOpener final : public IBlockServerOpener {\n  public:\n    TestBlockServerOpener(std::shared_ptr<TestBlockServerQueue> queue,\n                          const std::string& misc_name);\n    std::unique_ptr<IBlockServer> Open(IBlockServer::Delegate* delegate,\n                                       size_t buffer_size) override;\n\n  private:\n    std::shared_ptr<TestBlockServerQueue> queue_;\n    std::string misc_name_;\n};\n\nclass TestBlockServerFactory final : public IBlockServerFactory {\n  public:\n    std::shared_ptr<IBlockServerOpener> CreateOpener(const std::string& misc_name) override;\n    std::shared_ptr<TestBlockServerOpener> CreateTestOpener(const std::string& misc_name);\n    bool DeleteQueue(const std::string& misc_name);\n\n  private:\n    std::unordered_map<std::string, std::shared_ptr<TestBlockServerQueue>> queues_;\n};\n\nclass TestBlockServerFactory;\n\nclass HostUserDevice final : public IUserDevice {\n  public:\n    HostUserDevice(TestBlockServerFactory* factory, const std::string& misc_name);\n    const std::string& GetPath() override { return empty_path_; }\n    bool Destroy();\n\n  private:\n    TestBlockServerFactory* factory_;\n    std::string misc_name_;\n    std::string empty_path_;\n};\n\nclass HostTestHarness final : public ITestHarness {\n  public:\n    std::unique_ptr<IUserDevice> CreateUserDevice(const std::string& dev_name,\n                                                  const std::string& misc_name,\n                                                  uint64_t num_sectors) override;\n    IBlockServerFactory* GetBlockServerFactory() override;\n    bool HasUserDevice() override { return false; }\n\n  private:\n    TestBlockServerFactory factory_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/testing/temp_device.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <chrono>\n#include <string>\n\n#include <libdm/dm.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::dm::DeviceMapper;\nusing android::dm::DmTable;\n\nclass Tempdevice {\n  public:\n    Tempdevice(const std::string& name, const DmTable& table)\n        : dm_(DeviceMapper::Instance()), name_(name), valid_(false) {\n        valid_ = dm_.CreateDevice(name, table, &path_, std::chrono::seconds(5));\n    }\n    Tempdevice(Tempdevice&& other) noexcept\n        : dm_(other.dm_), name_(other.name_), path_(other.path_), valid_(other.valid_) {\n        other.valid_ = false;\n    }\n    ~Tempdevice() {\n        if (valid_) {\n            dm_.DeleteDeviceIfExists(name_);\n        }\n    }\n    bool Destroy() {\n        if (!valid_) {\n            return true;\n        }\n        valid_ = false;\n        return dm_.DeleteDeviceIfExists(name_);\n    }\n    const std::string& path() const { return path_; }\n    const std::string& name() const { return name_; }\n    bool valid() const { return valid_; }\n\n    Tempdevice(const Tempdevice&) = delete;\n    Tempdevice& operator=(const Tempdevice&) = delete;\n\n    Tempdevice& operator=(Tempdevice&& other) noexcept {\n        name_ = other.name_;\n        valid_ = other.valid_;\n        other.valid_ = false;\n        return *this;\n    }\n\n  private:\n    DeviceMapper& dm_;\n    std::string name_;\n    std::string path_;\n    bool valid_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"extractor.h\"\n\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <iostream>\n#include <memory>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n\nusing android::base::unique_fd;\nusing namespace std::string_literals;\n\nnamespace android {\nnamespace snapshot {\n\nExtractor::Extractor(const std::string& base_path, const std::string& cow_path)\n    : base_path_(base_path), cow_path_(cow_path), control_name_(\"test\") {}\n\nbool Extractor::Init() {\n    auto opener = factory_.CreateTestOpener(control_name_);\n    handler_ = std::make_shared<SnapshotHandler>(control_name_, cow_path_, base_path_, base_path_,\n                                                 opener, 1, false, false, false, 0);\n    if (!handler_->InitCowDevice()) {\n        return false;\n    }\n    if (!handler_->InitializeWorkers()) {\n        return false;\n    }\n\n    read_worker_ = std::make_unique<ReadWorker>(cow_path_, base_path_, control_name_, base_path_,\n                                                handler_->GetSharedPtr(), opener, false);\n    if (!read_worker_->Init()) {\n        return false;\n    }\n    block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());\n\n    handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());\n    return true;\n}\n\nExtractor::~Extractor() {\n    factory_.DeleteQueue(control_name_);\n}\n\nbool Extractor::Extract(off_t num_sectors, const std::string& out_path) {\n    unique_fd out_fd(open(out_path.c_str(), O_RDWR | O_CLOEXEC | O_TRUNC | O_CREAT, 0664));\n    if (out_fd < 0) {\n        PLOG(ERROR) << \"Could not open for writing: \" << out_path;\n        return false;\n    }\n\n    for (off_t i = 0; i < num_sectors; i++) {\n        if (!read_worker_->RequestSectors(i, 512)) {\n            LOG(ERROR) << \"Read sector \" << i << \" failed.\";\n            return false;\n        }\n        std::string result = std::move(block_server_->sent_io());\n        off_t offset = i * 512;\n        if (!android::base::WriteFullyAtOffset(out_fd, result.data(), result.size(), offset)) {\n            PLOG(ERROR) << \"write failed\";\n            return false;\n        }\n    }\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/extractor.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <string>\n#include <thread>\n\n#include <android-base/unique_fd.h>\n#include \"merge_worker.h\"\n#include \"read_worker.h\"\n#include \"snapuserd_core.h\"\n#include \"testing/host_harness.h\"\n\nnamespace android {\nnamespace snapshot {\n\nclass Extractor final {\n  public:\n    Extractor(const std::string& base_path, const std::string& cow_path);\n    ~Extractor();\n\n    bool Init();\n    bool Extract(off_t num_sectors, const std::string& out_path);\n\n  private:\n    std::string base_path_;\n    std::string cow_path_;\n\n    TestBlockServerFactory factory_;\n    HostTestHarness harness_;\n    std::string control_name_;\n    std::shared_ptr<SnapshotHandler> handler_;\n    std::unique_ptr<ReadWorker> read_worker_;\n    std::future<bool> handler_thread_;\n    TestBlockServer* block_server_ = nullptr;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"handler_manager.h\"\n\n#include <pthread.h>\n#include <sys/eventfd.h>\n\n#include <android-base/logging.h>\n\n#include \"android-base/properties.h\"\n#include \"merge_worker.h\"\n#include \"read_worker.h\"\n#include \"snapuserd_core.h\"\n\nnamespace android {\nnamespace snapshot {\n\nstatic constexpr uint8_t kMaxMergeThreads = 2;\n\nHandlerThread::HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd)\n    : snapuserd_(snapuserd), misc_name_(snapuserd_->GetMiscName()) {}\n\nvoid HandlerThread::FreeResources() {\n    // Each worker thread holds a reference to snapuserd.\n    // Clear them so that all the resources\n    // held by snapuserd is released\n    if (snapuserd_) {\n        snapuserd_->FreeResources();\n        snapuserd_ = nullptr;\n    }\n}\n\nSnapshotHandlerManager::SnapshotHandlerManager() {\n    monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC));\n    if (monitor_merge_event_fd_ == -1) {\n        PLOG(FATAL) << \"monitor_merge_event_fd_: failed to create eventfd\";\n    }\n}\n\nstd::shared_ptr<HandlerThread> SnapshotHandlerManager::AddHandler(\n        const std::string& misc_name, const std::string& cow_device_path,\n        const std::string& backing_device, const std::string& base_path_merge,\n        std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,\n        bool o_direct, uint32_t cow_op_merge_size) {\n    auto snapuserd = std::make_shared<SnapshotHandler>(\n            misc_name, cow_device_path, backing_device, base_path_merge, opener, num_worker_threads,\n            use_iouring, perform_verification_, o_direct, cow_op_merge_size);\n    if (!snapuserd->InitCowDevice()) {\n        LOG(ERROR) << \"Failed to initialize Snapuserd\";\n        return nullptr;\n    }\n\n    if (!snapuserd->InitializeWorkers()) {\n        LOG(ERROR) << \"Failed to initialize workers\";\n        return nullptr;\n    }\n\n    auto handler = std::make_shared<HandlerThread>(snapuserd);\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        if (FindHandler(&lock, misc_name) != dm_users_.end()) {\n            LOG(ERROR) << \"Handler already exists: \" << misc_name;\n            return nullptr;\n        }\n        dm_users_.push_back(handler);\n    }\n    return handler;\n}\n\nbool SnapshotHandlerManager::StartHandler(const std::string& misc_name) {\n    std::lock_guard<std::mutex> lock(lock_);\n    auto iter = FindHandler(&lock, misc_name);\n    if (iter == dm_users_.end()) {\n        LOG(ERROR) << \"Could not find handler: \" << misc_name;\n        return false;\n    }\n    if (!(*iter)->snapuserd() || (*iter)->snapuserd()->IsAttached()) {\n        LOG(ERROR) << \"Tried to re-attach control device: \" << misc_name;\n        return false;\n    }\n    if (!StartHandler(*iter)) {\n        return false;\n    }\n    return true;\n}\n\nbool SnapshotHandlerManager::StartHandler(const std::shared_ptr<HandlerThread>& handler) {\n    if (handler->snapuserd()->IsAttached()) {\n        LOG(ERROR) << \"Handler already attached\";\n        return false;\n    }\n\n    handler->snapuserd()->AttachControlDevice();\n\n    handler->thread() = std::thread(std::bind(&SnapshotHandlerManager::RunThread, this, handler));\n    return true;\n}\n\nbool SnapshotHandlerManager::DeleteHandler(const std::string& misc_name) {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        auto iter = FindHandler(&lock, misc_name);\n        if (iter == dm_users_.end()) {\n            // After merge is completed, we swap dm-user table with\n            // the underlying dm-linear base device. Hence, worker\n            // threads would have terminted and was removed from\n            // the list.\n            LOG(DEBUG) << \"Could not find handler: \" << misc_name;\n            return true;\n        }\n\n        if (!(*iter)->ThreadTerminated()) {\n            (*iter)->snapuserd()->NotifyIOTerminated();\n        }\n    }\n    if (!RemoveAndJoinHandler(misc_name)) {\n        return false;\n    }\n    return true;\n}\n\nvoid SnapshotHandlerManager::RunThread(std::shared_ptr<HandlerThread> handler) {\n    LOG(INFO) << \"Entering thread for handler: \" << handler->misc_name();\n\n    pthread_setname_np(pthread_self(), \"Handler\");\n\n    if (!handler->snapuserd()->Start()) {\n        LOG(ERROR) << \" Failed to launch all worker threads\";\n    }\n\n    handler->snapuserd()->CloseFds();\n    bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus();\n    handler->snapuserd()->UnmapBufferRegion();\n\n    auto misc_name = handler->misc_name();\n    LOG(INFO) << \"Handler thread about to exit: \" << misc_name;\n\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        if (merge_completed) {\n            num_partitions_merge_complete_ += 1;\n            active_merge_threads_ -= 1;\n            WakeupMonitorMergeThread();\n        }\n        handler->SetThreadTerminated();\n        auto iter = FindHandler(&lock, handler->misc_name());\n        if (iter == dm_users_.end()) {\n            // RemoveAndJoinHandler() already removed us from the list, and is\n            // now waiting on a join(), so just return. Additionally, release\n            // all the resources held by snapuserd object which are shared\n            // by worker threads. This should be done when the last reference\n            // of \"handler\" is released; but we will explicitly release here\n            // to make sure snapuserd object is freed as it is the biggest\n            // consumer of memory in the daemon.\n            handler->FreeResources();\n            LOG(INFO) << \"Exiting handler thread to allow for join: \" << misc_name;\n            return;\n        }\n\n        LOG(INFO) << \"Exiting handler thread and freeing resources: \" << misc_name;\n\n        if (handler->snapuserd()->IsAttached()) {\n            handler->thread().detach();\n        }\n\n        // Important: free resources within the lock. This ensures that if\n        // WaitForDelete() is called, the handler is either in the list, or\n        // it's not and its resources are guaranteed to be freed.\n        handler->FreeResources();\n        dm_users_.erase(iter);\n    }\n}\n\nbool SnapshotHandlerManager::InitiateMerge(const std::string& misc_name) {\n    std::lock_guard<std::mutex> lock(lock_);\n    auto iter = FindHandler(&lock, misc_name);\n    if (iter == dm_users_.end()) {\n        LOG(ERROR) << \"Could not find handler: \" << misc_name;\n        return false;\n    }\n\n    return StartMerge(&lock, *iter);\n}\n\nbool SnapshotHandlerManager::StartMerge(std::lock_guard<std::mutex>* proof_of_lock,\n                                        const std::shared_ptr<HandlerThread>& handler) {\n    CHECK(proof_of_lock);\n\n    if (!handler->snapuserd()->IsAttached()) {\n        LOG(ERROR) << \"Handler not attached to dm-user - Merge thread cannot be started\";\n        return false;\n    }\n\n    handler->snapuserd()->MonitorMerge();\n\n    if (!merge_monitor_.joinable()) {\n        merge_monitor_ = std::thread(&SnapshotHandlerManager::MonitorMerge, this);\n    }\n\n    merge_handlers_.push(handler);\n    WakeupMonitorMergeThread();\n    return true;\n}\n\nvoid SnapshotHandlerManager::WakeupMonitorMergeThread() {\n    uint64_t notify = 1;\n    ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), &notify, sizeof(notify)));\n    if (rc < 0) {\n        PLOG(FATAL) << \"failed to notify monitor merge thread\";\n    }\n}\n\nvoid SnapshotHandlerManager::MonitorMerge() {\n    pthread_setname_np(pthread_self(), \"Merge Monitor\");\n    while (!stop_monitor_merge_thread_) {\n        uint64_t testVal;\n        ssize_t ret =\n                TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal)));\n        if (ret == -1) {\n            PLOG(FATAL) << \"Failed to read from eventfd\";\n        } else if (ret == 0) {\n            LOG(FATAL) << \"Hit EOF on eventfd\";\n        }\n\n        LOG(INFO) << \"MonitorMerge: active-merge-threads: \" << active_merge_threads_;\n        {\n            auto num_merge_threads = android::base::GetUintProperty<uint>(\n                    \"ro.virtual_ab.num_merge_threads\", kMaxMergeThreads);\n            std::lock_guard<std::mutex> lock(lock_);\n            while (active_merge_threads_ < num_merge_threads && merge_handlers_.size() > 0) {\n                auto handler = merge_handlers_.front();\n                merge_handlers_.pop();\n\n                if (!handler->snapuserd()) {\n                    LOG(INFO) << \"MonitorMerge: skipping deleted handler: \" << handler->misc_name();\n                    continue;\n                }\n\n                LOG(INFO) << \"Starting merge for partition: \"\n                          << handler->snapuserd()->GetMiscName();\n                handler->snapuserd()->InitiateMerge();\n                active_merge_threads_ += 1;\n            }\n        }\n    }\n\n    LOG(INFO) << \"Exiting MonitorMerge: size: \" << merge_handlers_.size();\n}\n\nstd::string SnapshotHandlerManager::GetMergeStatus(const std::string& misc_name) {\n    std::lock_guard<std::mutex> lock(lock_);\n    auto iter = FindHandler(&lock, misc_name);\n    if (iter == dm_users_.end()) {\n        LOG(ERROR) << \"Could not find handler: \" << misc_name;\n        return {};\n    }\n\n    return (*iter)->snapuserd()->GetMergeStatus();\n}\n\ndouble SnapshotHandlerManager::GetMergePercentage() {\n    std::lock_guard<std::mutex> lock(lock_);\n\n    double percentage = 0.0;\n    int n = 0;\n\n    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {\n        auto& th = (*iter)->thread();\n        if (th.joinable()) {\n            // Merge percentage by individual partitions wherein merge is still\n            // in-progress\n            percentage += (*iter)->snapuserd()->GetMergePercentage();\n            n += 1;\n        }\n    }\n\n    // Calculate final merge including those partitions where merge was already\n    // completed - num_partitions_merge_complete_ will track them when each\n    // thread exists in RunThread.\n    int total_partitions = n + num_partitions_merge_complete_;\n\n    if (total_partitions) {\n        percentage = ((num_partitions_merge_complete_ * 100.0) + percentage) / total_partitions;\n    }\n\n    LOG(DEBUG) << \"Merge %: \" << percentage\n               << \" num_partitions_merge_complete_: \" << num_partitions_merge_complete_\n               << \" total_partitions: \" << total_partitions << \" n: \" << n;\n    return percentage;\n}\n\nbool SnapshotHandlerManager::GetVerificationStatus() {\n    std::lock_guard<std::mutex> lock(lock_);\n\n    bool status = true;\n    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {\n        auto& th = (*iter)->thread();\n        if (th.joinable() && status) {\n            status = (*iter)->snapuserd()->CheckPartitionVerification() && status;\n        } else {\n            // return immediately if there is a failure\n            return false;\n        }\n    }\n\n    return status;\n}\n\nbool SnapshotHandlerManager::RemoveAndJoinHandler(const std::string& misc_name) {\n    std::shared_ptr<HandlerThread> handler;\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n\n        auto iter = FindHandler(&lock, misc_name);\n        if (iter == dm_users_.end()) {\n            // Client already deleted.\n            return true;\n        }\n        handler = std::move(*iter);\n        dm_users_.erase(iter);\n    }\n\n    auto& th = handler->thread();\n    if (th.joinable()) {\n        th.join();\n    }\n    return true;\n}\n\nvoid SnapshotHandlerManager::TerminateMergeThreads() {\n    std::lock_guard<std::mutex> guard(lock_);\n\n    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {\n        if (!(*iter)->ThreadTerminated()) {\n            (*iter)->snapuserd()->NotifyIOTerminated();\n        }\n    }\n}\n\nvoid SnapshotHandlerManager::JoinAllThreads() {\n    // Acquire the thread list within the lock.\n    std::vector<std::shared_ptr<HandlerThread>> dm_users;\n    {\n        std::lock_guard<std::mutex> guard(lock_);\n        dm_users = std::move(dm_users_);\n    }\n\n    for (auto& client : dm_users) {\n        auto& th = client->thread();\n\n        if (th.joinable()) th.join();\n    }\n\n    if (merge_monitor_.joinable()) {\n        stop_monitor_merge_thread_ = true;\n        WakeupMonitorMergeThread();\n\n        merge_monitor_.join();\n    }\n}\n\nauto SnapshotHandlerManager::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,\n                                         const std::string& misc_name) -> HandlerList::iterator {\n    CHECK(proof_of_lock);\n\n    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {\n        if ((*iter)->misc_name() == misc_name) {\n            return iter;\n        }\n    }\n    return dm_users_.end();\n}\n\nvoid SnapshotHandlerManager::PauseMerge() {\n    std::lock_guard<std::mutex> guard(lock_);\n\n    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {\n        if (!(*iter)->ThreadTerminated()) {\n            (*iter)->snapuserd()->PauseMergeThreads();\n        }\n    }\n}\n\nvoid SnapshotHandlerManager::ResumeMerge() {\n    std::lock_guard<std::mutex> guard(lock_);\n\n    for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {\n        if (!(*iter)->ThreadTerminated()) {\n            (*iter)->snapuserd()->ResumeMergeThreads();\n        }\n    }\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <memory>\n#include <mutex>\n#include <queue>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <snapuserd/block_server.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass SnapshotHandler;\n\nclass HandlerThread {\n  public:\n    explicit HandlerThread(std::shared_ptr<SnapshotHandler> snapuserd);\n\n    void FreeResources();\n    const std::shared_ptr<SnapshotHandler>& snapuserd() const { return snapuserd_; }\n    std::thread& thread() { return thread_; }\n\n    const std::string& misc_name() const { return misc_name_; }\n    bool ThreadTerminated() { return thread_terminated_; }\n    void SetThreadTerminated() { thread_terminated_ = true; }\n\n  private:\n    std::thread thread_;\n    std::shared_ptr<SnapshotHandler> snapuserd_;\n    std::string misc_name_;\n    bool thread_terminated_ = false;\n};\n\nclass ISnapshotHandlerManager {\n  public:\n    virtual ~ISnapshotHandlerManager() {}\n\n    // Add a new snapshot handler but do not start serving requests yet.\n    virtual std::shared_ptr<HandlerThread> AddHandler(\n            const std::string& misc_name, const std::string& cow_device_path,\n            const std::string& backing_device, const std::string& base_path_merge,\n            std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads, bool use_iouring,\n            bool o_direct, uint32_t cow_op_merge_size) = 0;\n\n    // Start serving requests on a snapshot handler.\n    virtual bool StartHandler(const std::string& misc_name) = 0;\n\n    // Stop serving requests on a snapshot handler and remove it.\n    virtual bool DeleteHandler(const std::string& misc_name) = 0;\n\n    // Begin merging blocks on the given snapshot handler.\n    virtual bool InitiateMerge(const std::string& misc_name) = 0;\n\n    // Return a string containing a status code indicating the merge status\n    // on the handler. Returns empty on error.\n    virtual std::string GetMergeStatus(const std::string& misc_name) = 0;\n\n    // Wait until all handlers have terminated.\n    virtual void JoinAllThreads() = 0;\n\n    // Stop any in-progress merge threads.\n    virtual void TerminateMergeThreads() = 0;\n\n    // Returns the merge progress across all merging snapshot handlers.\n    virtual double GetMergePercentage() = 0;\n\n    // Returns whether all snapshots have verified.\n    virtual bool GetVerificationStatus() = 0;\n\n    // Disable partition verification\n    virtual void DisableVerification() = 0;\n\n    // Pause Merge threads\n    virtual void PauseMerge() = 0;\n\n    // Resume Merge threads\n    virtual void ResumeMerge() = 0;\n};\n\nclass SnapshotHandlerManager final : public ISnapshotHandlerManager {\n  public:\n    SnapshotHandlerManager();\n    std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,\n                                              const std::string& cow_device_path,\n                                              const std::string& backing_device,\n                                              const std::string& base_path_merge,\n                                              std::shared_ptr<IBlockServerOpener> opener,\n                                              int num_worker_threads, bool use_iouring,\n                                              bool o_direct, uint32_t cow_op_merge_size) override;\n    bool StartHandler(const std::string& misc_name) override;\n    bool DeleteHandler(const std::string& misc_name) override;\n    bool InitiateMerge(const std::string& misc_name) override;\n    std::string GetMergeStatus(const std::string& misc_name) override;\n    void JoinAllThreads() override;\n    void TerminateMergeThreads() override;\n    double GetMergePercentage() override;\n    bool GetVerificationStatus() override;\n    void DisableVerification() override { perform_verification_ = false; }\n    void PauseMerge() override;\n    void ResumeMerge() override;\n\n  private:\n    bool StartHandler(const std::shared_ptr<HandlerThread>& handler);\n    void RunThread(std::shared_ptr<HandlerThread> handler);\n    bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock,\n                    const std::shared_ptr<HandlerThread>& handler);\n    void MonitorMerge();\n    void WakeupMonitorMergeThread();\n    bool RemoveAndJoinHandler(const std::string& misc_name);\n\n    // Find a HandlerThread within a lock.\n    using HandlerList = std::vector<std::shared_ptr<HandlerThread>>;\n    HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,\n                                      const std::string& misc_name);\n\n    std::mutex lock_;\n    HandlerList dm_users_;\n\n    bool stop_monitor_merge_thread_ = false;\n    int active_merge_threads_ = 0;\n    std::thread merge_monitor_;\n    int num_partitions_merge_complete_ = 0;\n    std::queue<std::shared_ptr<HandlerThread>> merge_handlers_;\n    android::base::unique_fd monitor_merge_event_fd_;\n    bool perform_verification_ = true;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <libsnapshot/cow_format.h>\n#include <pthread.h>\n\n#include <android-base/properties.h>\n\n#include \"merge_worker.h\"\n#include \"snapuserd_core.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android;\nusing namespace android::dm;\nusing android::base::unique_fd;\n\nMergeWorker::MergeWorker(const std::string& cow_device, const std::string& misc_name,\n                         const std::string& base_path_merge,\n                         std::shared_ptr<SnapshotHandler> snapuserd, uint32_t cow_op_merge_size)\n    : Worker(cow_device, misc_name, base_path_merge, snapuserd),\n      cow_op_merge_size_(cow_op_merge_size) {}\n\nint MergeWorker::PrepareMerge(uint64_t* source_offset, int* pending_ops,\n                              std::vector<const CowOperation*>* replace_zero_vec) {\n    int num_ops = *pending_ops;\n    // 0 indicates ro.virtual_ab.cow_op_merge_size was not set in the build\n    if (cow_op_merge_size_ != 0) {\n        num_ops = std::min(cow_op_merge_size_, static_cast<uint32_t>(*pending_ops));\n    }\n\n    int nr_consecutive = 0;\n    bool checkOrderedOp = (replace_zero_vec == nullptr);\n    size_t num_blocks = 1;\n\n    do {\n        if (!cowop_iter_->AtEnd() && num_ops) {\n            const CowOperation* cow_op = cowop_iter_->Get();\n            if (checkOrderedOp && !IsOrderedOp(*cow_op)) {\n                break;\n            }\n\n            *source_offset = static_cast<uint64_t>(cow_op->new_block) * BLOCK_SZ;\n            if (!checkOrderedOp) {\n                replace_zero_vec->push_back(cow_op);\n                if (cow_op->type() == kCowReplaceOp) {\n                    // Get the number of blocks this op has compressed\n                    num_blocks = (CowOpCompressionSize(cow_op, BLOCK_SZ) / BLOCK_SZ);\n                }\n            }\n\n            cowop_iter_->Next();\n            num_ops -= num_blocks;\n            nr_consecutive = num_blocks;\n\n            while (!cowop_iter_->AtEnd() && num_ops) {\n                const CowOperation* op = cowop_iter_->Get();\n                if (checkOrderedOp && !IsOrderedOp(*op)) {\n                    break;\n                }\n\n                uint64_t next_offset = static_cast<uint64_t>(op->new_block) * BLOCK_SZ;\n                if (next_offset != (*source_offset + nr_consecutive * BLOCK_SZ)) {\n                    break;\n                }\n\n                if (!checkOrderedOp) {\n                    if (op->type() == kCowReplaceOp) {\n                        num_blocks = (CowOpCompressionSize(op, BLOCK_SZ) / BLOCK_SZ);\n                        if (num_ops < num_blocks) {\n                            break;\n                        }\n                    } else {\n                        // zero op\n                        num_blocks = 1;\n                    }\n                    replace_zero_vec->push_back(op);\n                }\n\n                nr_consecutive += num_blocks;\n                num_ops -= num_blocks;\n                cowop_iter_->Next();\n            }\n        }\n    } while (0);\n\n    return nr_consecutive;\n}\n\nbool MergeWorker::MergeReplaceZeroOps() {\n    // Flush after merging 1MB. Since all ops are independent and there is no\n    // dependency between COW ops, we will flush the data and the number\n    // of ops merged in COW block device. If there is a crash, we will\n    // end up replaying some of the COW ops which were already merged. That is\n    // ok.\n    //\n    // Although increasing this greater than 1MB may help in improving merge\n    // times; however, on devices with low memory, this can be problematic\n    // when there are multiple merge threads in parallel.\n    int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ);\n    int num_ops_merged = 0;\n\n    SNAP_LOG(INFO) << \"MergeReplaceZeroOps started....\";\n\n    while (!cowop_iter_->AtEnd()) {\n        int num_ops = PAYLOAD_BUFFER_SZ / BLOCK_SZ;\n        std::vector<const CowOperation*> replace_zero_vec;\n        uint64_t source_offset;\n\n        int linear_blocks = PrepareMerge(&source_offset, &num_ops, &replace_zero_vec);\n        if (linear_blocks == 0) {\n            // Merge complete\n            CHECK(cowop_iter_->AtEnd());\n            break;\n        }\n\n        for (size_t i = 0; i < replace_zero_vec.size(); i++) {\n            const CowOperation* cow_op = replace_zero_vec[i];\n            if (cow_op->type() == kCowReplaceOp) {\n                size_t buffer_size = CowOpCompressionSize(cow_op, BLOCK_SZ);\n                void* buffer = bufsink_.AcquireBuffer(buffer_size);\n                if (!buffer) {\n                    SNAP_LOG(ERROR) << \"AcquireBuffer failed in MergeReplaceOps\";\n                    return false;\n                }\n                // Read the entire compressed buffer spanning multiple blocks\n                if (!reader_->ReadData(cow_op, buffer, buffer_size)) {\n                    SNAP_LOG(ERROR) << \"Failed to read COW in merge\";\n                    return false;\n                }\n            } else {\n                void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);\n                if (!buffer) {\n                    SNAP_LOG(ERROR) << \"AcquireBuffer failed in MergeReplaceOps\";\n                    return false;\n                }\n                CHECK(cow_op->type() == kCowZeroOp);\n                memset(buffer, 0, BLOCK_SZ);\n            }\n        }\n\n        size_t io_size = linear_blocks * BLOCK_SZ;\n\n        // Merge - Write the contents back to base device\n        int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(), bufsink_.GetPayloadBufPtr(),\n                                            io_size, source_offset));\n        if (ret < 0 || ret != io_size) {\n            SNAP_LOG(ERROR)\n                    << \"Merge: ReplaceZeroOps: Failed to write to backing device while merging \"\n                    << \" at offset: \" << source_offset << \" io_size: \" << io_size;\n            return false;\n        }\n\n        num_ops_merged += replace_zero_vec.size();\n\n        if (num_ops_merged >= total_ops_merged_per_commit) {\n            // Flush the data\n            if (fsync(base_path_merge_fd_.get()) < 0) {\n                SNAP_LOG(ERROR) << \"Merge: ReplaceZeroOps: Failed to fsync merged data\";\n                return false;\n            }\n\n            // Track the merge completion\n            if (!snapuserd_->CommitMerge(num_ops_merged)) {\n                SNAP_LOG(ERROR) << \" Failed to commit the merged block in the header\";\n                return false;\n            }\n\n            num_ops_merged = 0;\n        }\n\n        bufsink_.ResetBufferOffset();\n\n        if (snapuserd_->IsIOTerminated()) {\n            SNAP_LOG(ERROR) << \"MergeReplaceZeroOps: MergeWorker threads terminated - shutting \"\n                               \"down merge\";\n            return false;\n        }\n\n        // Safe to check if there is a pause request.\n        snapuserd_->PauseMergeIfRequired();\n    }\n\n    // Any left over ops not flushed yet.\n    if (num_ops_merged) {\n        // Flush the data\n        if (fsync(base_path_merge_fd_.get()) < 0) {\n            SNAP_LOG(ERROR) << \"Merge: ReplaceZeroOps: Failed to fsync merged data\";\n            return false;\n        }\n\n        if (!snapuserd_->CommitMerge(num_ops_merged)) {\n            SNAP_LOG(ERROR) << \" Failed to commit the merged block in the header\";\n            return false;\n        }\n\n        num_ops_merged = 0;\n    }\n\n    return true;\n}\n\nbool MergeWorker::MergeOrderedOpsAsync() {\n    void* mapped_addr = snapuserd_->GetMappedAddr();\n    void* read_ahead_buffer =\n            static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());\n\n    SNAP_LOG(INFO) << \"MergeOrderedOpsAsync started....\";\n\n    while (!cowop_iter_->AtEnd()) {\n        const CowOperation* cow_op = cowop_iter_->Get();\n        if (!IsOrderedOp(*cow_op)) {\n            break;\n        }\n\n        SNAP_LOG(DEBUG) << \"Waiting for merge begin...\";\n        // Wait for RA thread to notify that the merge window\n        // is ready for merging.\n        if (!snapuserd_->WaitForMergeBegin()) {\n            SNAP_LOG(ERROR) << \"Failed waiting for merge to begin\";\n            return false;\n        }\n\n        std::optional<std::lock_guard<std::mutex>> buffer_lock;\n        // Acquire the buffer lock at this point so that RA thread\n        // doesn't step into this buffer. See b/377819507\n        buffer_lock.emplace(snapuserd_->GetBufferLock());\n\n        snapuserd_->SetMergeInProgress(ra_block_index_);\n\n        loff_t offset = 0;\n        int num_ops = snapuserd_->GetTotalBlocksToMerge();\n\n        int pending_sqe = queue_depth_;\n        int pending_ios_to_submit = 0;\n        bool flush_required = false;\n        blocks_merged_in_group_ = 0;\n\n        SNAP_LOG(DEBUG) << \"Merging copy-ops of size: \" << num_ops;\n        while (num_ops) {\n            uint64_t source_offset;\n\n            int linear_blocks = PrepareMerge(&source_offset, &num_ops);\n\n            if (linear_blocks != 0) {\n                size_t io_size = (linear_blocks * BLOCK_SZ);\n\n                // Get an SQE entry from the ring and populate the I/O variables\n                struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());\n                if (!sqe) {\n                    SNAP_PLOG(ERROR) << \"io_uring_get_sqe failed during merge-ordered ops\";\n                    return false;\n                }\n\n                io_uring_prep_write(sqe, base_path_merge_fd_.get(),\n                                    (char*)read_ahead_buffer + offset, io_size, source_offset);\n\n                offset += io_size;\n                num_ops -= linear_blocks;\n                blocks_merged_in_group_ += linear_blocks;\n\n                pending_sqe -= 1;\n                pending_ios_to_submit += 1;\n                // These flags are important - We need to make sure that the\n                // blocks are linked and are written in the same order as\n                // populated. This is because of overlapping block writes.\n                //\n                // If there are no dependency, we can optimize this further by\n                // allowing parallel writes; but for now, just link all the SQ\n                // entries.\n                sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);\n            }\n\n            // Ring is full or no more COW ops to be merged in this batch\n            if (pending_sqe == 0 || num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {\n                // If this is a last set of COW ops to be merged in this batch, we need\n                // to sync the merged data. We will try to grab an SQE entry\n                // and set the FSYNC command; additionally, make sure that\n                // the fsync is done after all the I/O operations queued\n                // in the ring is completed by setting IOSQE_IO_DRAIN.\n                //\n                // If there is no space in the ring, we will flush it later\n                // by explicitly calling fsync() system call.\n                if (num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {\n                    if (pending_sqe != 0) {\n                        struct io_uring_sqe* sqe = io_uring_get_sqe(ring_.get());\n                        if (!sqe) {\n                            // very unlikely but let's continue and not fail the\n                            // merge - we will flush it later\n                            SNAP_PLOG(ERROR) << \"io_uring_get_sqe failed during merge-ordered ops\";\n                            flush_required = true;\n                        } else {\n                            io_uring_prep_fsync(sqe, base_path_merge_fd_.get(), 0);\n                            // Drain the queue before fsync\n                            io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);\n                            pending_sqe -= 1;\n                            flush_required = false;\n                            pending_ios_to_submit += 1;\n                            sqe->flags |= (IOSQE_IO_LINK | IOSQE_ASYNC);\n                        }\n                    } else {\n                        flush_required = true;\n                    }\n                }\n\n                // Submit the IO for all the COW ops in a single syscall\n                int ret = io_uring_submit(ring_.get());\n                if (ret != pending_ios_to_submit) {\n                    SNAP_PLOG(ERROR)\n                            << \"io_uring_submit failed for read-ahead: \"\n                            << \" io submit: \" << ret << \" expected: \" << pending_ios_to_submit;\n                    return false;\n                }\n\n                int pending_ios_to_complete = pending_ios_to_submit;\n                pending_ios_to_submit = 0;\n\n                bool status = true;\n\n                // Reap I/O completions\n                while (pending_ios_to_complete) {\n                    struct io_uring_cqe* cqe;\n\n                    // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;\n                    // these error codes are not truly I/O errors; we can retry them\n                    // by re-populating the SQE entries and submitting the I/O\n                    // request back. However, we don't do that now; instead we\n                    // will fallback to synchronous I/O.\n                    ret = io_uring_wait_cqe(ring_.get(), &cqe);\n                    if (ret) {\n                        SNAP_LOG(ERROR) << \"Merge: io_uring_wait_cqe failed: \" << strerror(-ret);\n                        status = false;\n                        break;\n                    }\n\n                    if (cqe->res < 0) {\n                        SNAP_LOG(ERROR) << \"Merge: io_uring_wait_cqe failed with res: \" << cqe->res;\n                        status = false;\n                        break;\n                    }\n\n                    io_uring_cqe_seen(ring_.get(), cqe);\n                    pending_ios_to_complete -= 1;\n                }\n\n                if (!status) {\n                    return false;\n                }\n\n                pending_sqe = queue_depth_;\n            }\n\n            if (linear_blocks == 0) {\n                break;\n            }\n        }\n\n        // Verify all ops are merged\n        CHECK(num_ops == 0);\n\n        // Flush the data\n        if (flush_required && (fsync(base_path_merge_fd_.get()) < 0)) {\n            SNAP_LOG(ERROR) << \" Failed to fsync merged data\";\n            return false;\n        }\n\n        // Merge is done and data is on disk. Update the COW Header about\n        // the merge completion\n        if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {\n            SNAP_LOG(ERROR) << \" Failed to commit the merged block in the header\";\n            return false;\n        }\n\n        SNAP_LOG(DEBUG) << \"Block commit of size: \" << snapuserd_->GetTotalBlocksToMerge();\n\n        // Mark the block as merge complete\n        snapuserd_->SetMergeCompleted(ra_block_index_);\n\n        // Release the buffer lock\n        buffer_lock.reset();\n\n        // Notify RA thread that the merge thread is ready to merge the next\n        // window\n        snapuserd_->NotifyRAForMergeReady();\n\n        // Get the next block\n        ra_block_index_ += 1;\n    }\n\n    return true;\n}\n\nbool MergeWorker::MergeOrderedOps() {\n    void* mapped_addr = snapuserd_->GetMappedAddr();\n    void* read_ahead_buffer =\n            static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());\n\n    SNAP_LOG(INFO) << \"MergeOrderedOps started....\";\n\n    while (!cowop_iter_->AtEnd()) {\n        const CowOperation* cow_op = cowop_iter_->Get();\n        if (!IsOrderedOp(*cow_op)) {\n            break;\n        }\n\n        SNAP_LOG(DEBUG) << \"Waiting for merge begin...\";\n        // Wait for RA thread to notify that the merge window\n        // is ready for merging.\n        if (!snapuserd_->WaitForMergeBegin()) {\n            snapuserd_->SetMergeFailed(ra_block_index_);\n            return false;\n        }\n\n        std::optional<std::lock_guard<std::mutex>> buffer_lock;\n        // Acquire the buffer lock at this point so that RA thread\n        // doesn't step into this buffer. See b/377819507\n        buffer_lock.emplace(snapuserd_->GetBufferLock());\n\n        snapuserd_->SetMergeInProgress(ra_block_index_);\n\n        loff_t offset = 0;\n        int num_ops = snapuserd_->GetTotalBlocksToMerge();\n        SNAP_LOG(DEBUG) << \"Merging copy-ops of size: \" << num_ops;\n        while (num_ops) {\n            uint64_t source_offset;\n\n            int linear_blocks = PrepareMerge(&source_offset, &num_ops);\n            if (linear_blocks == 0) {\n                break;\n            }\n\n            size_t io_size = (linear_blocks * BLOCK_SZ);\n            // Write to the base device. Data is already in the RA buffer. Note\n            // that XOR ops is already handled by the RA thread. We just write\n            // the contents out.\n            int ret = TEMP_FAILURE_RETRY(pwrite(base_path_merge_fd_.get(),\n                                                (char*)read_ahead_buffer + offset, io_size,\n                                                source_offset));\n            if (ret < 0 || ret != io_size) {\n                SNAP_LOG(ERROR) << \"Failed to write to backing device while merging \"\n                                << \" at offset: \" << source_offset << \" io_size: \" << io_size;\n                snapuserd_->SetMergeFailed(ra_block_index_);\n                return false;\n            }\n\n            offset += io_size;\n            num_ops -= linear_blocks;\n        }\n\n        // Verify all ops are merged\n        CHECK(num_ops == 0);\n\n        // Flush the data\n        if (fsync(base_path_merge_fd_.get()) < 0) {\n            SNAP_LOG(ERROR) << \" Failed to fsync merged data\";\n            snapuserd_->SetMergeFailed(ra_block_index_);\n            return false;\n        }\n\n        // Merge is done and data is on disk. Update the COW Header about\n        // the merge completion\n        if (!snapuserd_->CommitMerge(snapuserd_->GetTotalBlocksToMerge())) {\n            SNAP_LOG(ERROR) << \" Failed to commit the merged block in the header\";\n            snapuserd_->SetMergeFailed(ra_block_index_);\n            return false;\n        }\n\n        SNAP_LOG(DEBUG) << \"Block commit of size: \" << snapuserd_->GetTotalBlocksToMerge();\n        // Mark the block as merge complete\n        snapuserd_->SetMergeCompleted(ra_block_index_);\n\n        // Release the buffer lock\n        buffer_lock.reset();\n\n        // Notify RA thread that the merge thread is ready to merge the next\n        // window\n        snapuserd_->NotifyRAForMergeReady();\n\n        // Get the next block\n        ra_block_index_ += 1;\n    }\n\n    return true;\n}\n\nbool MergeWorker::AsyncMerge() {\n    if (!MergeOrderedOpsAsync()) {\n        SNAP_LOG(ERROR) << \"MergeOrderedOpsAsync failed - Falling back to synchronous I/O\";\n        // Reset the iter so that we retry the merge\n        while (blocks_merged_in_group_ && !cowop_iter_->AtBegin()) {\n            cowop_iter_->Prev();\n            blocks_merged_in_group_ -= 1;\n        }\n\n        return false;\n    }\n\n    SNAP_LOG(INFO) << \"MergeOrderedOpsAsync completed\";\n    return true;\n}\n\nbool MergeWorker::SyncMerge() {\n    if (!MergeOrderedOps()) {\n        SNAP_LOG(ERROR) << \"Merge failed for ordered ops\";\n        return false;\n    }\n\n    SNAP_LOG(INFO) << \"MergeOrderedOps completed\";\n    return true;\n}\n\nbool MergeWorker::Merge() {\n    cowop_iter_ = reader_->GetOpIter(true);\n\n    bool retry = false;\n    bool ordered_ops_merge_status;\n\n    // Start Async Merge\n    if (merge_async_) {\n        ordered_ops_merge_status = AsyncMerge();\n        if (!ordered_ops_merge_status) {\n            FinalizeIouring();\n            retry = true;\n            merge_async_ = false;\n        }\n    }\n\n    // Check if we need to fallback and retry the merge\n    //\n    // If the device doesn't support async merge, we\n    // will directly enter here (aka devices with 4.x kernels)\n    const bool sync_merge_required = (retry || !merge_async_);\n\n    if (sync_merge_required) {\n        ordered_ops_merge_status = SyncMerge();\n        if (!ordered_ops_merge_status) {\n            // Merge failed. Device will continue to be mounted\n            // off snapshots; merge will be retried during\n            // next reboot\n            SNAP_LOG(ERROR) << \"Merge failed for ordered ops\";\n            snapuserd_->MergeFailed();\n            return false;\n        }\n    }\n\n    // Replace and Zero ops\n    if (!MergeReplaceZeroOps()) {\n        SNAP_LOG(ERROR) << \"Merge failed for replace/zero ops\";\n        snapuserd_->MergeFailed();\n        return false;\n    }\n\n    snapuserd_->MergeCompleted();\n\n    return true;\n}\n\nbool MergeWorker::InitializeIouring() {\n    if (!snapuserd_->IsIouringSupported()) {\n        return false;\n    }\n\n    ring_ = std::make_unique<struct io_uring>();\n\n    int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);\n    if (ret) {\n        LOG(ERROR) << \"Merge: io_uring_queue_init failed with ret: \" << ret;\n        return false;\n    }\n\n    merge_async_ = true;\n\n    LOG(INFO) << \"Merge: io_uring initialized with queue depth: \" << queue_depth_;\n    return true;\n}\n\nvoid MergeWorker::FinalizeIouring() {\n    if (merge_async_) {\n        io_uring_queue_exit(ring_.get());\n    }\n}\n\nbool MergeWorker::Run() {\n    SNAP_LOG(DEBUG) << \"Waiting for merge begin...\";\n\n    pthread_setname_np(pthread_self(), \"MergeWorker\");\n\n    if (!snapuserd_->WaitForMergeBegin()) {\n        return true;\n    }\n    auto merge_thread_priority = android::base::GetUintProperty<uint32_t>(\n            \"ro.virtual_ab.merge_thread_priority\", ANDROID_PRIORITY_BACKGROUND);\n\n    if (!SetThreadPriority(merge_thread_priority)) {\n        SNAP_PLOG(ERROR) << \"Failed to set thread priority\";\n    }\n\n    if (!SetProfiles({\"CPUSET_SP_BACKGROUND\"})) {\n        SNAP_PLOG(ERROR) << \"Failed to assign task profile to Mergeworker thread\";\n    }\n\n    SNAP_LOG(INFO) << \"Merge starting..\";\n\n    bufsink_.Initialize(PAYLOAD_BUFFER_SZ);\n\n    if (!Init()) {\n        SNAP_LOG(ERROR) << \"Merge thread initialization failed...\";\n        snapuserd_->MergeFailed();\n        return false;\n    }\n\n    InitializeIouring();\n\n    if (!Merge()) {\n        return false;\n    }\n\n    FinalizeIouring();\n    CloseFds();\n    reader_->CloseCowFd();\n\n    SNAP_LOG(INFO) << \"Snapshot-Merge completed\";\n\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n#pragma once\n\n#include \"worker.h\"\n\n#include <liburing.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass MergeWorker : public Worker {\n  public:\n    MergeWorker(const std::string& cow_device, const std::string& misc_name,\n                const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd,\n                uint32_t cow_op_merge_size);\n    bool Run();\n\n  private:\n    int PrepareMerge(uint64_t* source_offset, int* pending_ops,\n                     std::vector<const CowOperation*>* replace_zero_vec = nullptr);\n    bool MergeReplaceZeroOps();\n    bool MergeOrderedOps();\n    bool MergeOrderedOpsAsync();\n    bool Merge();\n    bool AsyncMerge();\n    bool SyncMerge();\n    bool InitializeIouring();\n    void FinalizeIouring();\n\n  private:\n    BufferSink bufsink_;\n    std::unique_ptr<ICowOpIter> cowop_iter_;\n    std::unique_ptr<struct io_uring> ring_;\n    size_t ra_block_index_ = 0;\n    uint64_t blocks_merged_in_group_ = 0;\n    bool merge_async_ = false;\n    // Queue depth of 8 seems optimal. We don't want\n    // to have a huge depth as it may put more memory pressure\n    // on the kernel worker threads given that we use\n    // IOSQE_ASYNC flag - ASYNC flags can potentially\n    // result in EINTR; Since we don't restart\n    // syscalls and fallback to synchronous I/O, we\n    // don't want huge queue depth\n    int queue_depth_ = 8;\n    uint32_t cow_op_merge_size_ = 0;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/properties.h>\n\n#include <libsnapshot/cow_format.h>\n#include <pthread.h>\n\n#include \"read_worker.h\"\n#include \"snapuserd_core.h\"\n#include \"user-space-merge/worker.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android;\nusing namespace android::dm;\nusing android::base::unique_fd;\n\nvoid ReadWorker::CloseFds() {\n    block_server_ = {};\n    backing_store_fd_ = {};\n    backing_store_direct_fd_ = {};\n    Worker::CloseFds();\n}\n\nReadWorker::ReadWorker(const std::string& cow_device, const std::string& backing_device,\n                       const std::string& misc_name, const std::string& base_path_merge,\n                       std::shared_ptr<SnapshotHandler> snapuserd,\n                       std::shared_ptr<IBlockServerOpener> opener, bool direct_read)\n    : Worker(cow_device, misc_name, base_path_merge, snapuserd),\n      backing_store_device_(backing_device),\n      direct_read_(direct_read),\n      block_server_opener_(opener),\n      aligned_buffer_(std::unique_ptr<void, decltype(&::free)>(nullptr, &::free)) {}\n\n// Start the replace operation. This will read the\n// internal COW format and if the block is compressed,\n// it will be de-compressed.\nbool ReadWorker::ProcessReplaceOp(const CowOperation* cow_op, void* buffer, size_t buffer_size) {\n    if (!reader_->ReadData(cow_op, buffer, buffer_size)) {\n        SNAP_LOG(ERROR) << \"ProcessReplaceOp failed for block \" << cow_op->new_block\n                        << \" buffer_size: \" << buffer_size;\n        return false;\n    }\n    return true;\n}\n\nbool ReadWorker::ReadFromSourceDevice(const CowOperation* cow_op, void* buffer) {\n    uint64_t offset;\n    if (!reader_->GetSourceOffset(cow_op, &offset)) {\n        SNAP_LOG(ERROR) << \"ReadFromSourceDevice: Failed to get source offset\";\n        return false;\n    }\n    SNAP_LOG(DEBUG) << \" ReadFromBaseDevice...: new-block: \" << cow_op->new_block\n                    << \" Op: \" << *cow_op;\n\n    if (direct_read_ && IsBlockAligned(offset)) {\n        if (!android::base::ReadFullyAtOffset(backing_store_direct_fd_, aligned_buffer_.get(),\n                                              BLOCK_SZ, offset)) {\n            SNAP_PLOG(ERROR) << \"O_DIRECT Read failed at offset: \" << offset;\n            return false;\n        }\n        std::memcpy(buffer, aligned_buffer_.get(), BLOCK_SZ);\n        return true;\n    }\n\n    if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {\n        std::string op;\n        if (cow_op->type() == kCowCopyOp)\n            op = \"Copy-op\";\n        else {\n            op = \"Xor-op\";\n        }\n        SNAP_PLOG(ERROR) << op << \" failed. Read from backing store: \" << backing_store_device_\n                         << \"at block :\" << offset / BLOCK_SZ << \" offset:\" << offset % BLOCK_SZ;\n        return false;\n    }\n\n    return true;\n}\n\n// Start the copy operation. This will read the backing\n// block device which is represented by cow_op->source.\nbool ReadWorker::ProcessCopyOp(const CowOperation* cow_op, void* buffer) {\n    if (!ReadFromSourceDevice(cow_op, buffer)) {\n        return false;\n    }\n    return true;\n}\n\nbool ReadWorker::ProcessXorOp(const CowOperation* cow_op, void* buffer) {\n    using WordType = std::conditional_t<sizeof(void*) == sizeof(uint64_t), uint64_t, uint32_t>;\n\n    if (!ReadFromSourceDevice(cow_op, buffer)) {\n        return false;\n    }\n\n    if (xor_buffer_.empty()) {\n        xor_buffer_.resize(BLOCK_SZ);\n    }\n    CHECK(xor_buffer_.size() == BLOCK_SZ);\n\n    ssize_t size = reader_->ReadData(cow_op, xor_buffer_.data(), xor_buffer_.size());\n    if (size != BLOCK_SZ) {\n        SNAP_LOG(ERROR) << \"ProcessXorOp failed for block \" << cow_op->new_block\n                        << \", return value: \" << size;\n        return false;\n    }\n\n    auto xor_in = reinterpret_cast<const WordType*>(xor_buffer_.data());\n    auto xor_out = reinterpret_cast<WordType*>(buffer);\n    auto num_words = BLOCK_SZ / sizeof(WordType);\n\n    for (auto i = 0; i < num_words; i++) {\n        xor_out[i] ^= xor_in[i];\n    }\n    return true;\n}\n\nbool ReadWorker::ProcessZeroOp(void* buffer) {\n    memset(buffer, 0, BLOCK_SZ);\n    return true;\n}\n\nbool ReadWorker::ProcessOrderedOp(const CowOperation* cow_op, void* buffer) {\n    MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);\n\n    switch (state) {\n        case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {\n            // Merge is completed for this COW op; just read directly from\n            // the base device\n            SNAP_LOG(DEBUG) << \"Merge-completed: Reading from base device sector: \"\n                            << (cow_op->new_block >> SECTOR_SHIFT)\n                            << \" Block-number: \" << cow_op->new_block;\n            if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), buffer, BLOCK_SZ)) {\n                SNAP_LOG(ERROR) << \"ReadDataFromBaseDevice at sector: \"\n                                << (cow_op->new_block >> SECTOR_SHIFT) << \" after merge-complete.\";\n                return false;\n            }\n            return true;\n        }\n        case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {\n            bool ret;\n            if (cow_op->type() == kCowCopyOp) {\n                ret = ProcessCopyOp(cow_op, buffer);\n            } else {\n                ret = ProcessXorOp(cow_op, buffer);\n            }\n\n            // I/O is complete - decrement the refcount irrespective of the return\n            // status\n            snapuserd_->NotifyIOCompletion(cow_op->new_block);\n            return ret;\n        }\n        // We already have the data in the buffer retrieved from RA thread.\n        // Nothing to process further.\n        case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {\n            [[fallthrough]];\n        }\n        case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {\n            return true;\n        }\n        default: {\n            // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)\n            return false;\n        }\n    }\n\n    return false;\n}\n\nbool ReadWorker::ProcessCowOp(const CowOperation* cow_op, void* buffer) {\n    if (cow_op == nullptr) {\n        SNAP_LOG(ERROR) << \"ProcessCowOp: Invalid cow_op\";\n        return false;\n    }\n\n    switch (cow_op->type()) {\n        case kCowReplaceOp: {\n            size_t buffer_size = CowOpCompressionSize(cow_op, BLOCK_SZ);\n            uint8_t chunk[buffer_size];\n            if (!ProcessReplaceOp(cow_op, chunk, buffer_size)) {\n                return false;\n            }\n            std::memcpy(buffer, chunk, BLOCK_SZ);\n            return true;\n        }\n\n        case kCowZeroOp: {\n            return ProcessZeroOp(buffer);\n        }\n\n        case kCowCopyOp:\n            [[fallthrough]];\n        case kCowXorOp: {\n            return ProcessOrderedOp(cow_op, buffer);\n        }\n\n        default: {\n            SNAP_LOG(ERROR) << \"Unknown operation-type found: \"\n                            << static_cast<uint8_t>(cow_op->type());\n        }\n    }\n    return false;\n}\n\nbool ReadWorker::Init() {\n    if (!Worker::Init()) {\n        return false;\n    }\n\n    const size_t compression_factor = reader_->GetMaxCompressionSize();\n    if (!compression_factor) {\n        SNAP_LOG(ERROR) << \"Compression factor is set to 0 which is invalid.\";\n        return false;\n    }\n    decompressed_buffer_ = std::make_unique<uint8_t[]>(compression_factor);\n\n    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));\n    if (backing_store_fd_ < 0) {\n        SNAP_PLOG(ERROR) << \"Open Failed: \" << backing_store_device_;\n        return false;\n    }\n\n    if (direct_read_) {\n        backing_store_direct_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY | O_DIRECT));\n        if (backing_store_direct_fd_ < 0) {\n            SNAP_PLOG(ERROR) << \"Open Failed with O_DIRECT: \" << backing_store_direct_fd_;\n            direct_read_ = false;\n        } else {\n            void* aligned_addr;\n            ssize_t page_size = getpagesize();\n            if (posix_memalign(&aligned_addr, page_size, page_size) < 0) {\n                direct_read_ = false;\n                SNAP_PLOG(ERROR) << \"posix_memalign failed \"\n                                 << \" page_size: \" << page_size << \" read_sz: \" << page_size;\n            } else {\n                aligned_buffer_.reset(aligned_addr);\n            }\n        }\n    }\n\n    block_server_ = block_server_opener_->Open(this, PAYLOAD_BUFFER_SZ);\n    if (!block_server_) {\n        SNAP_PLOG(ERROR) << \"Unable to open block server\";\n        return false;\n    }\n    return true;\n}\n\nbool ReadWorker::Run() {\n    SNAP_LOG(INFO) << \"Processing snapshot I/O requests....\";\n\n    pthread_setname_np(pthread_self(), \"ReadWorker\");\n    auto worker_thread_priority = android::base::GetUintProperty<uint32_t>(\n            \"ro.virtual_ab.worker_thread_priority\", ANDROID_PRIORITY_NORMAL);\n\n    if (!SetThreadPriority(worker_thread_priority)) {\n        SNAP_PLOG(ERROR) << \"Failed to set thread priority\";\n    }\n\n    // Start serving IO\n    while (true) {\n        if (!block_server_->ProcessRequests()) {\n            break;\n        }\n    }\n\n    CloseFds();\n    reader_->CloseCowFd();\n\n    return true;\n}\n\nbool ReadWorker::ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size) {\n    CHECK(read_size <= BLOCK_SZ);\n\n    loff_t offset = sector << SECTOR_SHIFT;\n    if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {\n        SNAP_PLOG(ERROR) << \"ReadDataFromBaseDevice failed. fd: \" << base_path_merge_fd_\n                         << \"at sector :\" << sector << \" size: \" << read_size;\n        return false;\n    }\n\n    return true;\n}\n\nbool ReadWorker::GetCowOpBlockOffset(const CowOperation* cow_op, uint64_t io_block,\n                                     off_t* block_offset) {\n    // If this is a replace op, get the block offset of this I/O\n    // block. Multi-block compression is supported only for\n    // Replace ops.\n    //\n    // Note: This can be extended when we support COPY and XOR ops down the\n    // line as the blocks are mostly contiguous.\n    if (cow_op && cow_op->type() == kCowReplaceOp) {\n        return GetBlockOffset(cow_op, io_block, BLOCK_SZ, block_offset);\n    }\n    return false;\n}\n\nbool ReadWorker::ReadAlignedSector(sector_t sector, size_t sz) {\n    size_t remaining_size = sz;\n    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();\n    int ret = 0;\n\n    do {\n        // Process 1MB payload at a time\n        size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);\n\n        size_t total_bytes_read = 0;\n        const CowOperation* prev_op = nullptr;\n        while (read_size) {\n            // We need to check every 4k block to verify if it is\n            // present in the mapping.\n            size_t size = std::min(BLOCK_SZ, read_size);\n\n            auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),\n                                       std::make_pair(sector, nullptr), SnapshotHandler::compare);\n            const bool sector_not_found = (it == chunk_vec.end() || it->first != sector);\n\n            void* buffer = block_server_->GetResponseBuffer(BLOCK_SZ, size);\n            if (!buffer) {\n                SNAP_LOG(ERROR) << \"AcquireBuffer failed in ReadAlignedSector\";\n                return false;\n            }\n\n            if (sector_not_found) {\n                // Find the 4k block\n                uint64_t io_block = SectorToChunk(sector);\n                // Get the previous iterator. Since the vector is sorted, the\n                // lookup of this sector can fall in a range of blocks if\n                // CowOperation has compressed multiple blocks.\n                if (it != chunk_vec.begin()) {\n                    std::advance(it, -1);\n                }\n\n                bool is_mapping_present = true;\n\n                // Vector itself is empty. This can happen if the block was not\n                // changed per the OTA or if the merge was already complete but\n                // snapshot table was not yet collapsed.\n                if (it == chunk_vec.end()) {\n                    is_mapping_present = false;\n                }\n\n                const CowOperation* cow_op = nullptr;\n                // Relative offset within the compressed multiple blocks\n                off_t block_offset = 0;\n                if (is_mapping_present) {\n                    // Get the nearest operation found in the vector\n                    cow_op = it->second;\n                    is_mapping_present = GetCowOpBlockOffset(cow_op, io_block, &block_offset);\n                }\n\n                // Thus, we have a case wherein sector was not found in the sorted\n                // vector; however, we indeed have a mapping of this sector\n                // embedded in one of the CowOperation which spans multiple\n                // block size.\n                if (is_mapping_present) {\n                    // block_offset = 0 would mean that the CowOperation should\n                    // already be in the sorted vector. Hence, lookup should\n                    // have already found it. If not, this is a bug.\n                    if (block_offset == 0) {\n                        SNAP_LOG(ERROR)\n                                << \"GetBlockOffset returned offset 0 for io_block: \" << io_block;\n                        return false;\n                    }\n\n                    // Get the CowOperation actual compression size\n                    size_t compression_size = CowOpCompressionSize(cow_op, BLOCK_SZ);\n                    // Offset cannot be greater than the compression size\n                    if (block_offset > compression_size) {\n                        SNAP_LOG(ERROR) << \"Invalid I/O block found. io_block: \" << io_block\n                                        << \" CowOperation-new-block: \" << cow_op->new_block\n                                        << \" compression-size: \" << compression_size;\n                        return false;\n                    }\n\n                    // Cached copy of the previous iteration. Just retrieve the\n                    // data\n                    if (prev_op && prev_op->new_block == cow_op->new_block) {\n                        std::memcpy(buffer, (char*)decompressed_buffer_.get() + block_offset, size);\n                    } else {\n                        // Get the data from the disk based on the compression\n                        // size\n                        if (!ProcessReplaceOp(cow_op, decompressed_buffer_.get(),\n                                              compression_size)) {\n                            return false;\n                        }\n                        // Copy the data from the decompressed buffer relative\n                        // to the i/o block offset.\n                        std::memcpy(buffer, (char*)decompressed_buffer_.get() + block_offset, size);\n                        // Cache this CowOperation pointer for successive I/O\n                        // operation. Since the request is sequential and the\n                        // block is already decompressed, subsequest I/O blocks\n                        // can fetch the data directly from this decompressed\n                        // buffer.\n                        prev_op = cow_op;\n                    }\n                } else {\n                    // Block not found in map - which means this block was not\n                    // changed as per the OTA. Just route the I/O to the base\n                    // device.\n                    if (!ReadDataFromBaseDevice(sector, buffer, size)) {\n                        SNAP_LOG(ERROR) << \"ReadDataFromBaseDevice failed\";\n                        return false;\n                    }\n                }\n                ret = size;\n            } else {\n                // We found the sector in mapping. Check the type of COW OP and\n                // process it.\n                if (!ProcessCowOp(it->second, buffer)) {\n                    SNAP_LOG(ERROR)\n                            << \"ProcessCowOp failed, sector = \" << sector << \", size = \" << sz;\n                    return false;\n                }\n\n                ret = std::min(BLOCK_SZ, read_size);\n            }\n\n            read_size -= ret;\n            total_bytes_read += ret;\n            sector += (ret >> SECTOR_SHIFT);\n        }\n\n        if (!SendBufferedIo()) {\n            return false;\n        }\n\n        SNAP_LOG(DEBUG) << \"SendBufferedIo success total_bytes_read: \" << total_bytes_read\n                        << \" remaining_size: \" << remaining_size;\n        remaining_size -= total_bytes_read;\n    } while (remaining_size > 0);\n\n    return true;\n}\n\nbool ReadWorker::IsMappingPresent(const CowOperation* cow_op, loff_t requested_offset,\n                                  loff_t cow_op_offset) {\n    const bool replace_op = (cow_op->type() == kCowReplaceOp);\n    if (replace_op) {\n        size_t max_compressed_size = CowOpCompressionSize(cow_op, BLOCK_SZ);\n        if ((requested_offset >= cow_op_offset) &&\n            (requested_offset < (cow_op_offset + max_compressed_size))) {\n            return true;\n        }\n    }\n    return false;\n}\n\nint ReadWorker::ReadUnalignedSector(\n        sector_t sector, size_t size,\n        std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {\n    SNAP_LOG(DEBUG) << \"ReadUnalignedSector: sector \" << sector << \" size: \" << size\n                    << \" Aligned sector: \" << it->first;\n\n    loff_t requested_offset = sector << SECTOR_SHIFT;\n    loff_t final_offset = (it->first) << SECTOR_SHIFT;\n\n    const CowOperation* cow_op = it->second;\n    if (IsMappingPresent(cow_op, requested_offset, final_offset)) {\n        size_t buffer_size = CowOpCompressionSize(cow_op, BLOCK_SZ);\n        uint8_t chunk[buffer_size];\n        // Read the entire decompressed buffer based on the block-size\n        if (!ProcessReplaceOp(cow_op, chunk, buffer_size)) {\n            return -1;\n        }\n        size_t skip_offset = (requested_offset - final_offset);\n        size_t write_sz = std::min(size, buffer_size - skip_offset);\n\n        auto buffer =\n                reinterpret_cast<uint8_t*>(block_server_->GetResponseBuffer(BLOCK_SZ, write_sz));\n        if (!buffer) {\n            SNAP_LOG(ERROR) << \"ReadUnalignedSector failed to allocate buffer\";\n            return -1;\n        }\n\n        std::memcpy(buffer, (char*)chunk + skip_offset, write_sz);\n        return write_sz;\n    }\n\n    int num_sectors_skip = sector - it->first;\n    size_t skip_size = num_sectors_skip << SECTOR_SHIFT;\n    size_t write_size = std::min(size, BLOCK_SZ - skip_size);\n    auto buffer =\n            reinterpret_cast<uint8_t*>(block_server_->GetResponseBuffer(BLOCK_SZ, write_size));\n    if (!buffer) {\n        SNAP_LOG(ERROR) << \"ProcessCowOp failed to allocate buffer\";\n        return -1;\n    }\n\n    if (!ProcessCowOp(it->second, buffer)) {\n        SNAP_LOG(ERROR) << \"ReadUnalignedSector: \" << sector << \" failed of size: \" << size\n                        << \" Aligned sector: \" << it->first;\n        return -1;\n    }\n\n    if (skip_size) {\n        if (skip_size == BLOCK_SZ) {\n            SNAP_LOG(ERROR) << \"Invalid un-aligned IO request at sector: \" << sector\n                            << \" Base-sector: \" << it->first;\n            return -1;\n        }\n        memmove(buffer, buffer + skip_size, write_size);\n    }\n    return write_size;\n}\n\nbool ReadWorker::ReadUnalignedSector(sector_t sector, size_t size) {\n    std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();\n\n    auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),\n                               SnapshotHandler::compare);\n\n    // |-------|-------|-------|\n    // 0       1       2       3\n    //\n    // Block 0 - op 1\n    // Block 1 - op 2\n    // Block 2 - op 3\n    //\n    // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.\n    //\n    // Each block is 4k bytes. Thus, the last block will span 8 sectors\n    // ranging till block 3 (However, block 3 won't be in chunk_vec as\n    // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector\n    // spanning between block 2 and block 3, we need to step back\n    // and get hold of the last element.\n    //\n    // Additionally, we need to make sure that the requested sector is\n    // indeed within the range of the final sector. It is perfectly valid\n    // to get an I/O request for block 3 and beyond which are not mapped\n    // to any COW ops. In that case, we just need to read from the base\n    // device.\n    bool merge_complete = false;\n    if (it == chunk_vec.end()) {\n        if (chunk_vec.size() > 0) {\n            // I/O request beyond the last mapped sector\n            it = std::prev(chunk_vec.end());\n        } else {\n            // This can happen when a partition merge is complete but snapshot\n            // state in /metadata is not yet deleted; during this window if the\n            // device is rebooted, subsequent attempt will mount the snapshot.\n            // However, since the merge was completed we wouldn't have any\n            // mapping to COW ops thus chunk_vec will be empty. In that case,\n            // mark this as merge_complete and route the I/O to the base device.\n            merge_complete = true;\n        }\n    } else if (it->first != sector) {\n        if (it != chunk_vec.begin()) {\n            --it;\n        }\n    } else {\n        return ReadAlignedSector(sector, size);\n    }\n\n    loff_t requested_offset = sector << SECTOR_SHIFT;\n\n    loff_t final_offset = 0;\n    if (!merge_complete) {\n        final_offset = it->first << SECTOR_SHIFT;\n    }\n\n    // Since a COW op span 4k block size, we need to make sure that the requested\n    // offset is within the 4k region. Consider the following case:\n    //\n    // |-------|-------|-------|\n    // 0       1       2       3\n    //\n    // Block 0 - op 1\n    // Block 1 - op 2\n    //\n    // We have an I/O request for a sector between block 2 and block 3. However,\n    // we have mapping to COW ops only for block 0 and block 1. Thus, the\n    // requested offset in this case is beyond the last mapped COW op size (which\n    // is block 1 in this case).\n\n    size_t remaining_size = size;\n    int ret = 0;\n\n    const CowOperation* cow_op = it->second;\n    if (!merge_complete && (requested_offset >= final_offset) &&\n        (((requested_offset - final_offset) < BLOCK_SZ) ||\n         IsMappingPresent(cow_op, requested_offset, final_offset))) {\n        // Read the partial un-aligned data\n        ret = ReadUnalignedSector(sector, remaining_size, it);\n        if (ret < 0) {\n            SNAP_LOG(ERROR) << \"ReadUnalignedSector failed for sector: \" << sector\n                            << \" size: \" << size << \" it->sector: \" << it->first;\n            return false;\n        }\n\n        remaining_size -= ret;\n        sector += (ret >> SECTOR_SHIFT);\n\n        // Send the data back\n        if (!SendBufferedIo()) {\n            return false;\n        }\n\n        // If we still have pending data to be processed, this will be aligned I/O\n        if (remaining_size) {\n            return ReadAlignedSector(sector, remaining_size);\n        }\n    } else {\n        // This is all about handling I/O request to be routed to base device\n        // as the I/O is not mapped to any of the COW ops.\n        loff_t aligned_offset = requested_offset;\n        // Align to nearest 4k\n        aligned_offset += BLOCK_SZ - 1;\n        aligned_offset &= ~(BLOCK_SZ - 1);\n        // Find the diff of the aligned offset\n        size_t diff_size = aligned_offset - requested_offset;\n        CHECK(diff_size <= BLOCK_SZ);\n\n        size_t read_size = std::min(remaining_size, diff_size);\n        void* buffer = block_server_->GetResponseBuffer(BLOCK_SZ, read_size);\n        if (!buffer) {\n            SNAP_LOG(ERROR) << \"AcquireBuffer failed in ReadUnalignedSector\";\n            return false;\n        }\n        if (!ReadDataFromBaseDevice(sector, buffer, read_size)) {\n            return false;\n        }\n        if (!SendBufferedIo()) {\n            return false;\n        }\n\n        if (remaining_size >= diff_size) {\n            remaining_size -= diff_size;\n            size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);\n            sector += num_sectors_read;\n            CHECK(IsBlockAligned(sector << SECTOR_SHIFT));\n\n            // If we still have pending data to be processed, this will be aligned I/O\n            return ReadAlignedSector(sector, remaining_size);\n        }\n    }\n\n    return true;\n}\n\nbool ReadWorker::RequestSectors(uint64_t sector, uint64_t len) {\n    // Unaligned I/O request\n    if (!IsBlockAligned(sector << SECTOR_SHIFT)) {\n        return ReadUnalignedSector(sector, len);\n    }\n\n    return ReadAlignedSector(sector, len);\n}\n\nbool ReadWorker::SendBufferedIo() {\n    return block_server_->SendBufferedIo();\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <utility>\n#include <vector>\n\n#include <snapuserd/block_server.h>\n#include \"worker.h\"\n\nnamespace android {\nnamespace snapshot {\n\nclass ReadWorker : public Worker, public IBlockServer::Delegate {\n  public:\n    ReadWorker(const std::string& cow_device, const std::string& backing_device,\n               const std::string& misc_name, const std::string& base_path_merge,\n               std::shared_ptr<SnapshotHandler> snapuserd,\n               std::shared_ptr<IBlockServerOpener> opener, bool direct_read = false);\n\n    bool Run();\n    bool Init() override;\n    void CloseFds() override;\n    bool RequestSectors(uint64_t sector, uint64_t size) override;\n\n    IBlockServer* block_server() const { return block_server_.get(); }\n\n  private:\n    bool SendBufferedIo();\n\n    bool ProcessCowOp(const CowOperation* cow_op, void* buffer);\n    bool ProcessXorOp(const CowOperation* cow_op, void* buffer);\n    bool ProcessOrderedOp(const CowOperation* cow_op, void* buffer);\n    bool ProcessCopyOp(const CowOperation* cow_op, void* buffer);\n    bool ProcessReplaceOp(const CowOperation* cow_op, void* buffer, size_t buffer_size);\n    bool ProcessZeroOp(void* buffer);\n\n    bool IsMappingPresent(const CowOperation* cow_op, loff_t requested_offset,\n                          loff_t cow_op_offset);\n    bool GetCowOpBlockOffset(const CowOperation* cow_op, uint64_t io_block, off_t* block_offset);\n    bool ReadAlignedSector(sector_t sector, size_t sz);\n    bool ReadUnalignedSector(sector_t sector, size_t size);\n    int ReadUnalignedSector(sector_t sector, size_t size,\n                            std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it);\n    bool ReadFromSourceDevice(const CowOperation* cow_op, void* buffer);\n    bool ReadDataFromBaseDevice(sector_t sector, void* buffer, size_t read_size);\n\n    constexpr bool IsBlockAligned(uint64_t size) { return ((size & (BLOCK_SZ - 1)) == 0); }\n    constexpr sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }\n    constexpr chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }\n\n    std::string backing_store_device_;\n    unique_fd backing_store_fd_;\n    unique_fd backing_store_direct_fd_;\n    bool direct_read_ = false;\n\n    std::shared_ptr<IBlockServerOpener> block_server_opener_;\n    std::unique_ptr<IBlockServer> block_server_;\n\n    std::vector<uint8_t> xor_buffer_;\n    std::unique_ptr<void, decltype(&::free)> aligned_buffer_;\n    std::unique_ptr<uint8_t[]> decompressed_buffer_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"snapuserd_core.h\"\n\n#include <android-base/chrono_utils.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <snapuserd/dm_user_block_server.h>\n\n#include \"merge_worker.h\"\n#include \"read_worker.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android;\nusing namespace android::dm;\nusing android::base::unique_fd;\n\nSnapshotHandler::SnapshotHandler(std::string misc_name, std::string cow_device,\n                                 std::string backing_device, std::string base_path_merge,\n                                 std::shared_ptr<IBlockServerOpener> opener, int num_worker_threads,\n                                 bool use_iouring, bool perform_verification, bool o_direct,\n                                 uint32_t cow_op_merge_size) {\n    misc_name_ = std::move(misc_name);\n    cow_device_ = std::move(cow_device);\n    backing_store_device_ = std::move(backing_device);\n    block_server_opener_ = std::move(opener);\n    base_path_merge_ = std::move(base_path_merge);\n    num_worker_threads_ = num_worker_threads;\n    is_io_uring_enabled_ = use_iouring;\n    perform_verification_ = perform_verification;\n    o_direct_ = o_direct;\n    cow_op_merge_size_ = cow_op_merge_size;\n}\n\nbool SnapshotHandler::InitializeWorkers() {\n    for (int i = 0; i < num_worker_threads_; i++) {\n        auto wt = std::make_unique<ReadWorker>(cow_device_, backing_store_device_, misc_name_,\n                                               base_path_merge_, GetSharedPtr(),\n                                               block_server_opener_, o_direct_);\n        if (!wt->Init()) {\n            SNAP_LOG(ERROR) << \"Thread initialization failed\";\n            return false;\n        }\n\n        worker_threads_.push_back(std::move(wt));\n    }\n    merge_thread_ = std::make_unique<MergeWorker>(cow_device_, misc_name_, base_path_merge_,\n                                                  GetSharedPtr(), cow_op_merge_size_);\n\n    read_ahead_thread_ = std::make_unique<ReadAhead>(cow_device_, backing_store_device_, misc_name_,\n                                                     GetSharedPtr(), cow_op_merge_size_);\n\n    update_verify_ = std::make_unique<UpdateVerify>(misc_name_);\n\n    return true;\n}\n\nstd::unique_ptr<CowReader> SnapshotHandler::CloneReaderForWorker() {\n    return reader_->CloneCowReader();\n}\n\nvoid SnapshotHandler::UpdateMergeCompletionPercentage() {\n    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);\n    merge_completion_percentage_ = (ch->num_merge_ops * 100.0) / reader_->get_num_total_data_ops();\n\n    SNAP_LOG(DEBUG) << \"Merge-complete %: \" << merge_completion_percentage_\n                    << \" num_merge_ops: \" << ch->num_merge_ops\n                    << \" total-ops: \" << reader_->get_num_total_data_ops();\n\n    if (ch->num_merge_ops == reader_->get_num_total_data_ops()) {\n        MarkMergeComplete();\n    }\n}\n\nbool SnapshotHandler::CommitMerge(int num_merge_ops) {\n    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);\n    ch->num_merge_ops += num_merge_ops;\n\n    if (scratch_space_) {\n        if (ra_thread_) {\n            struct BufferState* ra_state = GetBufferState();\n            ra_state->read_ahead_state = kCowReadAheadInProgress;\n        }\n\n        int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);\n        if (ret < 0) {\n            SNAP_PLOG(ERROR) << \"msync header failed: \" << ret;\n            return false;\n        }\n    } else {\n        reader_->UpdateMergeOpsCompleted(num_merge_ops);\n        const auto& header = reader_->GetHeader();\n\n        if (lseek(cow_fd_.get(), 0, SEEK_SET) < 0) {\n            SNAP_PLOG(ERROR) << \"lseek failed\";\n            return false;\n        }\n\n        if (!android::base::WriteFully(cow_fd_, &header, header.prefix.header_size)) {\n            SNAP_PLOG(ERROR) << \"Write to header failed\";\n            return false;\n        }\n\n        if (fsync(cow_fd_.get()) < 0) {\n            SNAP_PLOG(ERROR) << \"fsync failed\";\n            return false;\n        }\n    }\n\n    // Update the merge completion - this is used by update engine\n    // to track the completion. No need to take a lock. It is ok\n    // even if there is a miss on reading a latest updated value.\n    // Subsequent polling will eventually converge to completion.\n    UpdateMergeCompletionPercentage();\n\n    return true;\n}\n\nvoid SnapshotHandler::PrepareReadAhead() {\n    struct BufferState* ra_state = GetBufferState();\n    // Check if the data has to be re-constructed from COW device\n    if (ra_state->read_ahead_state == kCowReadAheadDone) {\n        populate_data_from_cow_ = true;\n    } else {\n        populate_data_from_cow_ = false;\n    }\n\n    NotifyRAForMergeReady();\n}\n\nbool SnapshotHandler::CheckMergeCompletionStatus() {\n    if (!merge_initiated_) {\n        SNAP_LOG(INFO) << \"Merge was not initiated. Total-data-ops: \"\n                       << reader_->get_num_total_data_ops();\n        return false;\n    }\n\n    struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);\n\n    SNAP_LOG(INFO) << \"Merge-status: Total-Merged-ops: \" << ch->num_merge_ops\n                   << \" Total-data-ops: \" << reader_->get_num_total_data_ops();\n    return true;\n}\n\nbool SnapshotHandler::ReadMetadata() {\n    reader_ = std::make_unique<CowReader>(CowReader::ReaderFlags::USERSPACE_MERGE, true);\n    CowOptions options;\n\n    SNAP_LOG(DEBUG) << \"ReadMetadata: Parsing cow file\";\n\n    if (!reader_->Parse(cow_fd_)) {\n        SNAP_LOG(ERROR) << \"Failed to parse\";\n        return false;\n    }\n\n    const auto& header = reader_->GetHeader();\n    if (!(header.block_size == BLOCK_SZ)) {\n        SNAP_LOG(ERROR) << \"Invalid header block size found: \" << header.block_size;\n        return false;\n    }\n\n    SNAP_LOG(INFO) << \"Merge-ops: \" << header.num_merge_ops;\n    if (header.num_merge_ops) {\n        resume_merge_ = true;\n        SNAP_LOG(INFO) << \"Resume Snapshot-merge\";\n    }\n\n    if (!MmapMetadata()) {\n        SNAP_LOG(ERROR) << \"mmap failed\";\n        return false;\n    }\n\n    UpdateMergeCompletionPercentage();\n\n    // Initialize the iterator for reading metadata\n    std::unique_ptr<ICowOpIter> cowop_iter = reader_->GetOpIter(true);\n\n    int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);\n    int ra_index = 0;\n\n    size_t copy_ops = 0, replace_ops = 0, zero_ops = 0, xor_ops = 0;\n\n    while (!cowop_iter->AtEnd()) {\n        const CowOperation* cow_op = cowop_iter->Get();\n\n        if (cow_op->type() == kCowCopyOp) {\n            copy_ops += 1;\n        } else if (cow_op->type() == kCowReplaceOp) {\n            replace_ops += 1;\n        } else if (cow_op->type() == kCowZeroOp) {\n            zero_ops += 1;\n        } else if (cow_op->type() == kCowXorOp) {\n            xor_ops += 1;\n        }\n\n        chunk_vec_.push_back(std::make_pair(ChunkToSector(cow_op->new_block), cow_op));\n\n        if (IsOrderedOp(*cow_op)) {\n            ra_thread_ = true;\n            block_to_ra_index_[cow_op->new_block] = ra_index;\n            num_ra_ops_per_iter -= 1;\n\n            if ((ra_index + 1) - merge_blk_state_.size() == 1) {\n                std::unique_ptr<MergeGroupState> blk_state = std::make_unique<MergeGroupState>(\n                        MERGE_GROUP_STATE::GROUP_MERGE_PENDING, 0);\n\n                merge_blk_state_.push_back(std::move(blk_state));\n            }\n\n            // Move to next RA block\n            if (num_ra_ops_per_iter == 0) {\n                num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);\n                ra_index += 1;\n            }\n        }\n        cowop_iter->Next();\n    }\n\n    chunk_vec_.shrink_to_fit();\n\n    // Sort the vector based on sectors as we need this during un-aligned access\n    std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);\n\n    PrepareReadAhead();\n\n    SNAP_LOG(INFO) << \"Merged-ops: \" << header.num_merge_ops\n                   << \" Total-data-ops: \" << reader_->get_num_total_data_ops()\n                   << \" Unmerged-ops: \" << chunk_vec_.size() << \" Copy-ops: \" << copy_ops\n                   << \" Zero-ops: \" << zero_ops << \" Replace-ops: \" << replace_ops\n                   << \" Xor-ops: \" << xor_ops;\n\n    return true;\n}\n\nbool SnapshotHandler::MmapMetadata() {\n    const auto& header = reader_->GetHeader();\n\n    total_mapped_addr_length_ = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;\n\n    if (header.prefix.major_version >= 2 && header.buffer_size > 0) {\n        scratch_space_ = true;\n    }\n\n    if (scratch_space_) {\n        mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE, MAP_SHARED,\n                            cow_fd_.get(), 0);\n    } else {\n        mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE,\n                            MAP_SHARED | MAP_ANONYMOUS, -1, 0);\n        struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);\n        ch->num_merge_ops = header.num_merge_ops;\n    }\n\n    if (mapped_addr_ == MAP_FAILED) {\n        SNAP_LOG(ERROR) << \"mmap metadata failed\";\n        return false;\n    }\n\n    return true;\n}\n\nvoid SnapshotHandler::UnmapBufferRegion() {\n    int ret = munmap(mapped_addr_, total_mapped_addr_length_);\n    if (ret < 0) {\n        SNAP_PLOG(ERROR) << \"munmap failed\";\n    }\n}\n\nbool SnapshotHandler::InitCowDevice() {\n    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));\n    if (cow_fd_ < 0) {\n        SNAP_PLOG(ERROR) << \"Open Failed: \" << cow_device_;\n        return false;\n    }\n\n    return ReadMetadata();\n}\n\n/*\n * Entry point to launch threads\n */\nbool SnapshotHandler::Start() {\n    std::vector<std::future<bool>> threads;\n    std::future<bool> ra_thread_status;\n\n    if (ra_thread_) {\n        ra_thread_status =\n                std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get());\n        // If this is a merge-resume path, wait until RA thread is fully up as\n        // the data has to be re-constructed from the scratch space.\n        if (resume_merge_ && ShouldReconstructDataFromCow()) {\n            WaitForRaThreadToStart();\n        }\n    }\n\n    // Launch worker threads\n    for (int i = 0; i < worker_threads_.size(); i++) {\n        threads.emplace_back(\n                std::async(std::launch::async, &ReadWorker::Run, worker_threads_[i].get()));\n    }\n\n    std::future<bool> merge_thread =\n            std::async(std::launch::async, &MergeWorker::Run, merge_thread_.get());\n\n    // Now that the worker threads are up, scan the partitions.\n    // If the snapshot-merge is being resumed, there is no need to scan as the\n    // current slot is already marked as boot complete.\n    if (perform_verification_ && !resume_merge_) {\n        update_verify_->VerifyUpdatePartition();\n    }\n\n    bool ret = true;\n    for (auto& t : threads) {\n        ret = t.get() && ret;\n    }\n\n    // Worker threads are terminated by this point - this can only happen:\n    //\n    // 1: If dm-user device is destroyed\n    // 2: We had an I/O failure when reading root partitions\n    //\n    // In case (1), this would be a graceful shutdown. In this case, merge\n    // thread and RA thread should have already terminated by this point. We will be\n    // destroying the dm-user device only _after_ merge is completed.\n    //\n    // In case (2), if merge thread had started, then it will be\n    // continuing to merge; however, since we had an I/O failure and the\n    // I/O on root partitions are no longer served, we will terminate the\n    // merge\n\n    NotifyIOTerminated();\n\n    bool read_ahead_retval = false;\n\n    SNAP_LOG(INFO) << \"Snapshot I/O terminated. Waiting for merge thread....\";\n    bool merge_thread_status = merge_thread.get();\n\n    if (ra_thread_) {\n        read_ahead_retval = ra_thread_status.get();\n    }\n\n    SNAP_LOG(INFO) << \"Worker threads terminated with ret: \" << ret\n                   << \" Merge-thread with ret: \" << merge_thread_status\n                   << \" RA-thread with ret: \" << read_ahead_retval;\n    return ret;\n}\n\nuint64_t SnapshotHandler::GetBufferMetadataOffset() {\n    const auto& header = reader_->GetHeader();\n\n    return (header.prefix.header_size + sizeof(BufferState));\n}\n\n/*\n * Metadata for read-ahead is 16 bytes. For a 2 MB region, we will\n * end up with 8k (2 PAGE) worth of metadata. Thus, a 2MB buffer\n * region is split into:\n *\n * 1: 8k metadata\n * 2: Scratch space\n *\n */\nsize_t SnapshotHandler::GetBufferMetadataSize() {\n    const auto& header = reader_->GetHeader();\n    size_t buffer_size = header.buffer_size;\n\n    // If there is no scratch space, then just use the\n    // anonymous memory\n    if (buffer_size == 0) {\n        buffer_size = BUFFER_REGION_DEFAULT_SIZE;\n    }\n\n    return ((buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ);\n}\n\nsize_t SnapshotHandler::GetBufferDataOffset() {\n    const auto& header = reader_->GetHeader();\n\n    return (header.prefix.header_size + GetBufferMetadataSize());\n}\n\n/*\n * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.\n */\nsize_t SnapshotHandler::GetBufferDataSize() {\n    const auto& header = reader_->GetHeader();\n    size_t buffer_size = header.buffer_size;\n\n    // If there is no scratch space, then just use the\n    // anonymous memory\n    if (buffer_size == 0) {\n        buffer_size = BUFFER_REGION_DEFAULT_SIZE;\n    }\n\n    return (buffer_size - GetBufferMetadataSize());\n}\n\nstruct BufferState* SnapshotHandler::GetBufferState() {\n    const auto& header = reader_->GetHeader();\n\n    struct BufferState* ra_state =\n            reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.prefix.header_size);\n    return ra_state;\n}\n\nbool SnapshotHandler::IsIouringSupported() {\n    if (!KernelSupportsIoUring()) {\n        return false;\n    }\n\n    // During selinux init transition, libsnapshot will propagate the\n    // status of io_uring enablement. As properties are not initialized,\n    // we cannot query system property.\n    if (is_io_uring_enabled_) {\n        return true;\n    }\n\n    // Finally check the system property\n    return android::base::GetBoolProperty(\"ro.virtual_ab.io_uring.enabled\", false);\n}\n\nbool SnapshotHandler::CheckPartitionVerification() {\n    return update_verify_->CheckPartitionVerification();\n}\n\nvoid SnapshotHandler::FreeResources() {\n    worker_threads_.clear();\n    read_ahead_thread_ = nullptr;\n    merge_thread_ = nullptr;\n}\n\nuint64_t SnapshotHandler::GetNumSectors() const {\n    unique_fd fd(TEMP_FAILURE_RETRY(open(base_path_merge_.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd < 0) {\n        SNAP_LOG(ERROR) << \"Cannot open base path: \" << base_path_merge_;\n        return false;\n    }\n\n    uint64_t dev_sz = get_block_device_size(fd.get());\n    if (!dev_sz) {\n        SNAP_LOG(ERROR) << \"Failed to find block device size: \" << base_path_merge_;\n        return false;\n    }\n\n    return dev_sz / SECTOR_SIZE;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h",
    "content": "// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <linux/types.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sys/time.h>\n#include <unistd.h>\n\n#include <condition_variable>\n#include <cstring>\n#include <future>\n#include <iostream>\n#include <limits>\n#include <mutex>\n#include <ostream>\n#include <string>\n#include <thread>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/unique_fd.h>\n#include <ext4_utils/ext4_utils.h>\n#include <libdm/dm.h>\n#include <libsnapshot/cow_reader.h>\n#include <libsnapshot/cow_writer.h>\n#include <snapuserd/block_server.h>\n#include <snapuserd/snapuserd_buffer.h>\n#include <snapuserd/snapuserd_kernel.h>\n#include <storage_literals/storage_literals.h>\n#include <system/thread_defs.h>\n#include \"snapuserd_readahead.h\"\n#include \"snapuserd_verify.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\nusing namespace std::chrono_literals;\nusing namespace android::storage_literals;\n\nstatic constexpr size_t PAYLOAD_BUFFER_SZ = (1UL << 20);\nstatic_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ);\n\nstatic constexpr int kNumWorkerThreads = 4;\n\n#define SNAP_LOG(level) LOG(level) << misc_name_ << \": \"\n#define SNAP_PLOG(level) PLOG(level) << misc_name_ << \": \"\n\nenum class MERGE_IO_TRANSITION {\n    INVALID,\n    MERGE_READY,\n    MERGE_BEGIN,\n    MERGE_FAILED,\n    MERGE_COMPLETE,\n    IO_TERMINATED,\n    READ_AHEAD_FAILURE\n};\n\nclass MergeWorker;\nclass ReadWorker;\n\nenum class MERGE_GROUP_STATE {\n    GROUP_MERGE_PENDING,\n    GROUP_MERGE_RA_READY,\n    GROUP_MERGE_IN_PROGRESS,\n    GROUP_MERGE_COMPLETED,\n    GROUP_MERGE_FAILED,\n    GROUP_INVALID,\n};\n\nstruct MergeGroupState {\n    MERGE_GROUP_STATE merge_state_;\n    // Ref count I/O when group state\n    // is in \"GROUP_MERGE_PENDING\"\n    size_t num_ios_in_progress;\n    std::mutex m_lock;\n    std::condition_variable m_cv;\n\n    MergeGroupState(MERGE_GROUP_STATE state, size_t n_ios)\n        : merge_state_(state), num_ios_in_progress(n_ios) {}\n};\n\nclass SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> {\n  public:\n    SnapshotHandler(std::string misc_name, std::string cow_device, std::string backing_device,\n                    std::string base_path_merge, std::shared_ptr<IBlockServerOpener> opener,\n                    int num_workers, bool use_iouring, bool perform_verification, bool o_direct,\n                    uint32_t cow_op_merge_size);\n    bool InitCowDevice();\n    bool Start();\n\n    const std::string& GetControlDevicePath() { return control_device_; }\n    const std::string& GetMiscName() { return misc_name_; }\n    uint64_t GetNumSectors() const;\n    const bool& IsAttached() const { return attached_; }\n    void AttachControlDevice() { attached_ = true; }\n\n    bool CheckMergeCompletionStatus();\n    bool CommitMerge(int num_merge_ops);\n\n    void CloseFds() { cow_fd_ = {}; }\n    void FreeResources();\n\n    bool InitializeWorkers();\n    std::unique_ptr<CowReader> CloneReaderForWorker();\n    std::shared_ptr<SnapshotHandler> GetSharedPtr() { return shared_from_this(); }\n\n    std::vector<std::pair<sector_t, const CowOperation*>>& GetChunkVec() { return chunk_vec_; }\n\n    static bool compare(std::pair<sector_t, const CowOperation*> p1,\n                        std::pair<sector_t, const CowOperation*> p2) {\n        return p1.first < p2.first;\n    }\n\n    void UnmapBufferRegion();\n    bool MmapMetadata();\n\n    // Read-ahead related functions\n    void* GetMappedAddr() { return mapped_addr_; }\n    void PrepareReadAhead();\n    std::unordered_map<uint64_t, void*>& GetReadAheadMap() { return read_ahead_buffer_map_; }\n\n    // State transitions for merge\n    void InitiateMerge();\n    void MonitorMerge();\n    void WakeupMonitorMergeThread();\n    void WaitForMergeComplete();\n    bool WaitForMergeBegin();\n    void RaThreadStarted();\n    void WaitForRaThreadToStart();\n    void NotifyRAForMergeReady();\n    bool WaitForMergeReady();\n    void MergeFailed();\n    bool IsIOTerminated();\n    void MergeCompleted();\n    void NotifyIOTerminated();\n    bool ReadAheadIOCompleted(bool sync);\n    void ReadAheadIOFailed();\n\n    bool ShouldReconstructDataFromCow() { return populate_data_from_cow_; }\n    void FinishReconstructDataFromCow() { populate_data_from_cow_ = false; }\n    void MarkMergeComplete();\n    // Return the snapshot status\n    std::string GetMergeStatus();\n\n    // RA related functions\n    uint64_t GetBufferMetadataOffset();\n    size_t GetBufferMetadataSize();\n    size_t GetBufferDataOffset();\n    size_t GetBufferDataSize();\n\n    // Total number of blocks to be merged in a given read-ahead buffer region\n    void SetMergedBlockCountForNextCommit(int x) { total_ra_blocks_merged_ = x; }\n    int GetTotalBlocksToMerge() { return total_ra_blocks_merged_; }\n    bool MergeInitiated() { return merge_initiated_; }\n    bool MergeMonitored() { return merge_monitored_; }\n    double GetMergePercentage() { return merge_completion_percentage_; }\n    void PauseMergeThreads();\n    void ResumeMergeThreads();\n    void PauseMergeIfRequired();\n\n    // Merge Block State Transitions\n    void SetMergeCompleted(size_t block_index);\n    void SetMergeInProgress(size_t block_index);\n    void SetMergeFailed(size_t block_index);\n    void NotifyIOCompletion(uint64_t new_block);\n    bool GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer);\n    MERGE_GROUP_STATE ProcessMergingBlock(uint64_t new_block, void* buffer);\n\n    bool IsIouringSupported();\n    bool CheckPartitionVerification();\n    std::mutex& GetBufferLock() { return buffer_lock_; }\n\n  private:\n    bool ReadMetadata();\n    sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }\n    chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }\n    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }\n    struct BufferState* GetBufferState();\n    void UpdateMergeCompletionPercentage();\n\n    // COW device\n    std::string cow_device_;\n    // Source device\n    std::string backing_store_device_;\n    // dm-user control device\n    std::string control_device_;\n    std::string misc_name_;\n    // Base device for merging\n    std::string base_path_merge_;\n\n    unique_fd cow_fd_;\n\n    std::unique_ptr<CowReader> reader_;\n\n    // chunk_vec stores the pseudo mapping of sector\n    // to COW operations.\n    std::vector<std::pair<sector_t, const CowOperation*>> chunk_vec_;\n\n    std::mutex lock_;\n    std::condition_variable cv;\n\n    // Lock the buffer used for snapshot-merge\n    std::mutex buffer_lock_;\n\n    void* mapped_addr_;\n    size_t total_mapped_addr_length_;\n\n    std::vector<std::unique_ptr<ReadWorker>> worker_threads_;\n    // Read-ahead related\n    bool populate_data_from_cow_ = false;\n    bool ra_thread_ = false;\n    bool ra_thread_started_ = false;\n    int total_ra_blocks_merged_ = 0;\n    MERGE_IO_TRANSITION io_state_ = MERGE_IO_TRANSITION::INVALID;\n    std::unique_ptr<ReadAhead> read_ahead_thread_;\n    std::unordered_map<uint64_t, void*> read_ahead_buffer_map_;\n\n    // user-space-merging\n    std::unordered_map<uint64_t, int> block_to_ra_index_;\n\n    // Merge Block state\n    std::vector<std::unique_ptr<MergeGroupState>> merge_blk_state_;\n\n    std::unique_ptr<MergeWorker> merge_thread_;\n    double merge_completion_percentage_;\n\n    bool merge_initiated_ = false;\n    bool merge_monitored_ = false;\n    bool attached_ = false;\n    bool is_io_uring_enabled_ = false;\n    bool scratch_space_ = false;\n    int num_worker_threads_ = kNumWorkerThreads;\n    bool perform_verification_ = true;\n    bool resume_merge_ = false;\n    bool merge_complete_ = false;\n    bool o_direct_ = false;\n    uint32_t cow_op_merge_size_ = 0;\n    std::unique_ptr<UpdateVerify> update_verify_;\n    std::shared_ptr<IBlockServerOpener> block_server_opener_;\n\n    // Pause merge threads\n    bool pause_merge_ = false;\n    std::mutex pause_merge_lock_;\n    std::condition_variable pause_merge_cv_;\n};\n\nstd::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value);\nstatic_assert(sizeof(off_t) == sizeof(uint64_t));\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"snapuserd_readahead.h\"\n\n#include <pthread.h>\n\n#include \"android-base/properties.h\"\n#include \"snapuserd_core.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android;\nusing namespace android::dm;\nusing android::base::unique_fd;\n\nReadAhead::ReadAhead(const std::string& cow_device, const std::string& backing_device,\n                     const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd,\n                     uint32_t cow_op_merge_size) {\n    cow_device_ = cow_device;\n    backing_store_device_ = backing_device;\n    misc_name_ = misc_name;\n    snapuserd_ = snapuserd;\n    cow_op_merge_size_ = cow_op_merge_size;\n}\n\nvoid ReadAhead::CheckOverlap(const CowOperation* cow_op) {\n    uint64_t source_offset;\n    if (!reader_->GetSourceOffset(cow_op, &source_offset)) {\n        SNAP_LOG(ERROR) << \"ReadAhead operation has no source offset: \" << *cow_op;\n        return;\n    }\n\n    uint64_t source_block = GetBlockFromOffset(header_, source_offset);\n    bool misaligned = (GetBlockRelativeOffset(header_, source_offset) != 0);\n\n    if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) ||\n        (misaligned && source_blocks_.count(source_block + 1))) {\n        overlap_ = true;\n    }\n\n    dest_blocks_.insert(source_block);\n    if (source_offset > 0) {\n        dest_blocks_.insert(source_block + 1);\n    }\n    source_blocks_.insert(cow_op->new_block);\n}\n\nint ReadAhead::PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,\n                                    std::vector<uint64_t>& blocks,\n                                    std::vector<const CowOperation*>& xor_op_vec) {\n    int num_ops = *pending_ops;\n    if (cow_op_merge_size_ != 0) {\n        num_ops = std::min(static_cast<int>(cow_op_merge_size_), *pending_ops);\n    }\n\n    int nr_consecutive = 0;\n    bool is_ops_present = (!RAIterDone() && num_ops);\n\n    if (!is_ops_present) {\n        return nr_consecutive;\n    }\n\n    // Get the first block with offset\n    const CowOperation* cow_op = GetRAOpIter();\n\n    if (!reader_->GetSourceOffset(cow_op, source_offset)) {\n        SNAP_LOG(ERROR) << \"PrepareNextReadAhead operation has no source offset: \" << *cow_op;\n        return nr_consecutive;\n    }\n    if (cow_op->type() == kCowXorOp) {\n        xor_op_vec.push_back(cow_op);\n    }\n\n    RAIterNext();\n    num_ops -= 1;\n    nr_consecutive = 1;\n    blocks.push_back(cow_op->new_block);\n\n    if (!overlap_) {\n        CheckOverlap(cow_op);\n    }\n\n    /*\n     * Find number of consecutive blocks\n     */\n    while (!RAIterDone() && num_ops) {\n        const CowOperation* op = GetRAOpIter();\n        uint64_t next_offset;\n        if (!reader_->GetSourceOffset(op, &next_offset)) {\n            SNAP_LOG(ERROR) << \"PrepareNextReadAhead operation has no source offset: \" << *cow_op;\n            break;\n        }\n\n        // Check for consecutive blocks\n        if (next_offset != (*source_offset + nr_consecutive * BLOCK_SZ)) {\n            break;\n        }\n\n        if (op->type() == kCowXorOp) {\n            xor_op_vec.push_back(op);\n        }\n\n        nr_consecutive += 1;\n        num_ops -= 1;\n        blocks.push_back(op->new_block);\n        RAIterNext();\n\n        if (!overlap_) {\n            CheckOverlap(op);\n        }\n    }\n\n    return nr_consecutive;\n}\n\nclass [[nodiscard]] AutoNotifyReadAheadFailed {\n  public:\n    AutoNotifyReadAheadFailed(std::shared_ptr<SnapshotHandler> snapuserd) : snapuserd_(snapuserd) {}\n\n    ~AutoNotifyReadAheadFailed() {\n        if (cancelled_) {\n            return;\n        }\n        snapuserd_->ReadAheadIOFailed();\n    }\n\n    void Cancel() { cancelled_ = true; }\n\n  private:\n    std::shared_ptr<SnapshotHandler> snapuserd_;\n    bool cancelled_ = false;\n};\n\nbool ReadAhead::ReconstructDataFromCow() {\n    std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();\n    loff_t metadata_offset = 0;\n    loff_t start_data_offset = snapuserd_->GetBufferDataOffset();\n    int num_ops = 0;\n    int total_blocks_merged = 0;\n\n    // This memcpy is important as metadata_buffer_ will be an unaligned address and will fault\n    // on 32-bit systems\n    std::unique_ptr<uint8_t[]> metadata_buffer =\n            std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());\n    memcpy(metadata_buffer.get(), metadata_buffer_, snapuserd_->GetBufferMetadataSize());\n\n    while (true) {\n        struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(\n                (char*)metadata_buffer.get() + metadata_offset);\n\n        // Done reading metadata\n        if (bm->new_block == 0 && bm->file_offset == 0) {\n            break;\n        }\n\n        loff_t buffer_offset = bm->file_offset - start_data_offset;\n        void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + buffer_offset);\n        read_ahead_buffer_map[bm->new_block] = bufptr;\n        num_ops += 1;\n        total_blocks_merged += 1;\n\n        metadata_offset += sizeof(struct ScratchMetadata);\n    }\n\n    AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);\n\n    // We are done re-constructing the mapping; however, we need to make sure\n    // all the COW operations to-be merged are present in the re-constructed\n    // mapping.\n    while (!RAIterDone()) {\n        const CowOperation* op = GetRAOpIter();\n        if (read_ahead_buffer_map.find(op->new_block) != read_ahead_buffer_map.end()) {\n            num_ops -= 1;\n            RAIterNext();\n            continue;\n        }\n\n        // Verify that we have covered all the ops which were re-constructed\n        // from COW device - These are the ops which are being\n        // re-constructed after crash.\n        if (!(num_ops == 0)) {\n            SNAP_LOG(ERROR) << \"ReconstructDataFromCow failed. Not all ops recoverd \"\n                            << \" Pending ops: \" << num_ops;\n            return false;\n        }\n\n        break;\n    }\n\n    snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged);\n\n    snapuserd_->FinishReconstructDataFromCow();\n\n    if (!snapuserd_->ReadAheadIOCompleted(true)) {\n        SNAP_LOG(ERROR) << \"ReadAheadIOCompleted failed...\";\n        return false;\n    }\n\n    snapuserd_->RaThreadStarted();\n    SNAP_LOG(INFO) << \"ReconstructDataFromCow success\";\n    notify_read_ahead_failed.Cancel();\n    return true;\n}\n\n/*\n * With io_uring, the data flow is slightly different.\n *\n * The data flow is as follows:\n *\n * 1: Queue the I/O requests to be read from backing source device.\n * This is done by retrieving the SQE entry from ring and populating\n * the SQE entry. Note that the I/O is not submitted yet.\n *\n * 2: Once the ring is full (aka queue_depth), we will submit all\n * the queued I/O request with a single system call. This essentially\n * cuts down \"queue_depth\" number of system calls to a single system call.\n *\n * 3: Once the I/O is submitted, user-space thread will now work\n * on processing the XOR Operations. This happens in parallel when\n * I/O requests are submitted to the kernel. This is ok because, for XOR\n * operations, we first need to retrieve the compressed data form COW block\n * device. Thus, we have offloaded the backing source I/O to the kernel\n * and user-space is parallely working on fetching the data for XOR operations.\n *\n * 4: After the XOR operations are read from COW device, poll the completion\n * queue for all the I/O submitted. If the I/O's were already completed,\n * then user-space thread will just read the CQE requests from the ring\n * without doing any system call. If none of the I/O were completed yet,\n * user-space thread will do a system call and wait for I/O completions.\n *\n * Flow diagram:\n *                                                    SQ-RING\n *  SQE1 <----------- Fetch SQE1 Entry ---------- |SQE1||SQE2|SQE3|\n *\n *  SQE1  ------------ Populate SQE1 Entry ------> |SQE1-X||SQE2|SQE3|\n *\n *  SQE2 <----------- Fetch SQE2 Entry ---------- |SQE1-X||SQE2|SQE3|\n *\n *  SQE2  ------------ Populate SQE2 Entry ------> |SQE1-X||SQE2-X|SQE3|\n *\n *  SQE3 <----------- Fetch SQE3 Entry ---------- |SQE1-X||SQE2-X|SQE3|\n *\n *  SQE3  ------------ Populate SQE3 Entry ------> |SQE1-X||SQE2-X|SQE3-X|\n *\n *  Submit-IO ---------------------------------> |SQE1-X||SQE2-X|SQE3-X|\n *     |                                                  |\n *     |                                        Process I/O entries in kernel\n *     |                                                  |\n *  Retrieve XOR                                          |\n *  data from COW                                         |\n *     |                                                  |\n *     |                                                  |\n *  Fetch CQ completions\n *     |                                              CQ-RING\n *                                               |CQE1-X||CQE2-X|CQE3-X|\n *                                                        |\n *   CQE1 <------------Fetch CQE1 Entry          |CQE1||CQE2-X|CQE3-X|\n *   CQE2 <------------Fetch CQE2 Entry          |CQE1||CQE2-|CQE3-X|\n *   CQE3 <------------Fetch CQE3 Entry          |CQE1||CQE2-|CQE3-|\n *    |\n *    |\n *  Continue Next set of operations in the RING\n */\n\nbool ReadAhead::ReadAheadAsyncIO() {\n    int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;\n    loff_t buffer_offset = 0;\n    total_blocks_merged_ = 0;\n    overlap_ = false;\n    dest_blocks_.clear();\n    source_blocks_.clear();\n    blocks_.clear();\n    std::vector<const CowOperation*> xor_op_vec;\n\n    int pending_sqe = queue_depth_;\n    int pending_ios_to_submit = 0;\n\n    size_t xor_op_index = 0;\n    size_t block_index = 0;\n\n    loff_t offset = 0;\n\n    bufsink_.ResetBufferOffset();\n\n    // Number of ops to be merged in this window. This is a fixed size\n    // except for the last window wherein the number of ops can be less\n    // than the size of the RA window.\n    while (num_ops) {\n        uint64_t source_offset;\n        struct io_uring_sqe* sqe;\n\n        int linear_blocks = PrepareNextReadAhead(&source_offset, &num_ops, blocks_, xor_op_vec);\n\n        if (linear_blocks != 0) {\n            size_t io_size = (linear_blocks * BLOCK_SZ);\n\n            // Get an SQE entry from the ring and populate the I/O variables\n            sqe = io_uring_get_sqe(ring_.get());\n            if (!sqe) {\n                SNAP_PLOG(ERROR) << \"io_uring_get_sqe failed during read-ahead\";\n                return false;\n            }\n\n            io_uring_prep_read(sqe, backing_store_fd_.get(),\n                               (char*)ra_temp_buffer_.get() + buffer_offset, io_size,\n                               source_offset);\n\n            buffer_offset += io_size;\n            num_ops -= linear_blocks;\n            total_blocks_merged_ += linear_blocks;\n\n            pending_sqe -= 1;\n            pending_ios_to_submit += 1;\n            sqe->flags |= IOSQE_ASYNC;\n        }\n\n        // pending_sqe == 0 : Ring is full\n        //\n        // num_ops == 0 : All the COW ops in this batch are processed - Submit\n        // pending I/O requests in the ring\n        //\n        // linear_blocks == 0 : All the COW ops processing is done. Submit\n        // pending I/O requests in the ring\n        if (pending_sqe == 0 || num_ops == 0 || (linear_blocks == 0 && pending_ios_to_submit)) {\n            // Submit the IO for all the COW ops in a single syscall\n            int ret = io_uring_submit(ring_.get());\n            if (ret != pending_ios_to_submit) {\n                SNAP_PLOG(ERROR) << \"io_uring_submit failed for read-ahead: \"\n                                 << \" io submit: \" << ret << \" expected: \" << pending_ios_to_submit;\n                return false;\n            }\n\n            int pending_ios_to_complete = pending_ios_to_submit;\n            pending_ios_to_submit = 0;\n\n            bool xor_processing_required = (xor_op_vec.size() > 0);\n\n            // Read XOR data from COW file in parallel when I/O's are in-flight\n            if (xor_processing_required && !ReadXorData(block_index, xor_op_index, xor_op_vec)) {\n                SNAP_LOG(ERROR) << \"ReadXorData failed\";\n                return false;\n            }\n\n            // Fetch I/O completions\n            if (!ReapIoCompletions(pending_ios_to_complete)) {\n                SNAP_LOG(ERROR) << \"ReapIoCompletions failed\";\n                return false;\n            }\n\n            // Retrieve XOR'ed data\n            if (xor_processing_required) {\n                ProcessXorData(block_index, xor_op_index, xor_op_vec, ra_temp_buffer_.get(),\n                               offset);\n            }\n\n            // All the I/O in the ring is processed.\n            pending_sqe = queue_depth_;\n        }\n\n        if (linear_blocks == 0) {\n            break;\n        }\n    }\n\n    // Done with merging ordered ops\n    if (RAIterDone() && total_blocks_merged_ == 0) {\n        return true;\n    }\n\n    CHECK(blocks_.size() == total_blocks_merged_);\n\n    UpdateScratchMetadata();\n\n    return true;\n}\n\nvoid ReadAhead::UpdateScratchMetadata() {\n    loff_t metadata_offset = 0;\n\n    struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(\n            (char*)ra_temp_meta_buffer_.get() + metadata_offset);\n\n    bm->new_block = 0;\n    bm->file_offset = 0;\n\n    loff_t file_offset = snapuserd_->GetBufferDataOffset();\n\n    for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {\n        uint64_t new_block = blocks_[block_index];\n        // Track the metadata blocks which are stored in scratch space\n        bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +\n                                                       metadata_offset);\n\n        bm->new_block = new_block;\n        bm->file_offset = file_offset;\n\n        metadata_offset += sizeof(struct ScratchMetadata);\n        file_offset += BLOCK_SZ;\n    }\n\n    // This is important - explicitly set the contents to zero. This is used\n    // when re-constructing the data after crash. This indicates end of\n    // reading metadata contents when re-constructing the data\n    bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +\n                                                   metadata_offset);\n    bm->new_block = 0;\n    bm->file_offset = 0;\n}\n\nbool ReadAhead::ReapIoCompletions(int pending_ios_to_complete) {\n    bool status = true;\n\n    // Reap I/O completions\n    while (pending_ios_to_complete) {\n        struct io_uring_cqe* cqe;\n\n        // io_uring_wait_cqe can potentially return -EAGAIN or -EINTR;\n        // these error codes are not truly I/O errors; we can retry them\n        // by re-populating the SQE entries and submitting the I/O\n        // request back. However, we don't do that now; instead we\n        // will fallback to synchronous I/O.\n        int ret = io_uring_wait_cqe(ring_.get(), &cqe);\n        if (ret) {\n            SNAP_LOG(ERROR) << \"Read-ahead - io_uring_wait_cqe failed: \" << strerror(-ret);\n            status = false;\n            break;\n        }\n\n        if (cqe->res < 0) {\n            SNAP_LOG(ERROR) << \"Read-ahead - io_uring_Wait_cqe failed with res: \" << cqe->res;\n            status = false;\n            break;\n        }\n\n        io_uring_cqe_seen(ring_.get(), cqe);\n        pending_ios_to_complete -= 1;\n    }\n\n    return status;\n}\n\nvoid ReadAhead::ProcessXorData(size_t& block_xor_index, size_t& xor_index,\n                               std::vector<const CowOperation*>& xor_op_vec, void* buffer,\n                               loff_t& buffer_offset) {\n    using WordType = std::conditional_t<sizeof(void*) == sizeof(uint64_t), uint64_t, uint32_t>;\n    loff_t xor_buf_offset = 0;\n\n    while (block_xor_index < blocks_.size()) {\n        void* bufptr = static_cast<void*>((char*)buffer + buffer_offset);\n        uint64_t new_block = blocks_[block_xor_index];\n\n        if (xor_index < xor_op_vec.size()) {\n            const CowOperation* xor_op = xor_op_vec[xor_index];\n\n            // Check if this block is an XOR op\n            if (xor_op->new_block == new_block) {\n                // Pointer to the data read from base device\n                auto buffer_words = reinterpret_cast<WordType*>(bufptr);\n                // Get the xor'ed data read from COW device\n                auto xor_data_words = reinterpret_cast<WordType*>(\n                        (char*)bufsink_.GetPayloadBufPtr() + xor_buf_offset);\n                auto num_words = BLOCK_SZ / sizeof(WordType);\n\n                for (auto i = 0; i < num_words; i++) {\n                    buffer_words[i] ^= xor_data_words[i];\n                }\n\n                // Move to next XOR op\n                xor_index += 1;\n                xor_buf_offset += BLOCK_SZ;\n            }\n        }\n\n        buffer_offset += BLOCK_SZ;\n        block_xor_index += 1;\n    }\n\n    bufsink_.ResetBufferOffset();\n}\n\nbool ReadAhead::ReadXorData(size_t block_index, size_t xor_op_index,\n                            std::vector<const CowOperation*>& xor_op_vec) {\n    // Process the XOR ops in parallel - We will be reading data\n    // from COW file for XOR ops processing.\n    while (block_index < blocks_.size()) {\n        uint64_t new_block = blocks_[block_index];\n\n        if (xor_op_index < xor_op_vec.size()) {\n            const CowOperation* xor_op = xor_op_vec[xor_op_index];\n            if (xor_op->new_block == new_block) {\n                void* buffer = bufsink_.AcquireBuffer(BLOCK_SZ);\n                if (!buffer) {\n                    SNAP_LOG(ERROR) << \"ReadAhead - failed to allocate buffer for block: \"\n                                    << xor_op->new_block;\n                    return false;\n                }\n                if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {\n                    SNAP_LOG(ERROR)\n                            << \" ReadAhead - XorOp Read failed for block: \" << xor_op->new_block\n                            << \", return value: \" << rv;\n                    return false;\n                }\n\n                xor_op_index += 1;\n            }\n        }\n        block_index += 1;\n    }\n    return true;\n}\n\nbool ReadAhead::ReadAheadSyncIO() {\n    int num_ops = (snapuserd_->GetBufferDataSize()) / BLOCK_SZ;\n    loff_t buffer_offset = 0;\n    total_blocks_merged_ = 0;\n    overlap_ = false;\n    dest_blocks_.clear();\n    source_blocks_.clear();\n    blocks_.clear();\n    std::vector<const CowOperation*> xor_op_vec;\n\n    AutoNotifyReadAheadFailed notify_read_ahead_failed(snapuserd_);\n\n    bufsink_.ResetBufferOffset();\n\n    // Number of ops to be merged in this window. This is a fixed size\n    // except for the last window wherein the number of ops can be less\n    // than the size of the RA window.\n    while (num_ops) {\n        uint64_t source_offset;\n\n        int linear_blocks = PrepareNextReadAhead(&source_offset, &num_ops, blocks_, xor_op_vec);\n        if (linear_blocks == 0) {\n            // No more blocks to read\n            SNAP_LOG(DEBUG) << \" Read-ahead completed....\";\n            break;\n        }\n\n        size_t io_size = (linear_blocks * BLOCK_SZ);\n\n        // Read from the base device consecutive set of blocks in one shot\n        if (!android::base::ReadFullyAtOffset(backing_store_fd_,\n                                              (char*)ra_temp_buffer_.get() + buffer_offset, io_size,\n                                              source_offset)) {\n            SNAP_PLOG(ERROR) << \"Ordered-op failed. Read from backing store: \"\n                             << backing_store_device_ << \"at block :\" << source_offset / BLOCK_SZ\n                             << \" offset :\" << source_offset % BLOCK_SZ\n                             << \" buffer_offset : \" << buffer_offset << \" io_size : \" << io_size\n                             << \" buf-addr : \" << read_ahead_buffer_;\n            return false;\n        }\n\n        buffer_offset += io_size;\n        total_blocks_merged_ += linear_blocks;\n        num_ops -= linear_blocks;\n    }\n\n    // Done with merging ordered ops\n    if (RAIterDone() && total_blocks_merged_ == 0) {\n        notify_read_ahead_failed.Cancel();\n        return true;\n    }\n\n    loff_t metadata_offset = 0;\n\n    struct ScratchMetadata* bm = reinterpret_cast<struct ScratchMetadata*>(\n            (char*)ra_temp_meta_buffer_.get() + metadata_offset);\n\n    bm->new_block = 0;\n    bm->file_offset = 0;\n\n    loff_t file_offset = snapuserd_->GetBufferDataOffset();\n\n    loff_t offset = 0;\n    CHECK(blocks_.size() == total_blocks_merged_);\n\n    size_t xor_index = 0;\n    BufferSink bufsink;\n    bufsink.Initialize(BLOCK_SZ * 2);\n\n    for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {\n        void* bufptr = static_cast<void*>((char*)ra_temp_buffer_.get() + offset);\n        uint64_t new_block = blocks_[block_index];\n\n        if (xor_index < xor_op_vec.size()) {\n            const CowOperation* xor_op = xor_op_vec[xor_index];\n\n            // Check if this block is an XOR op\n            if (xor_op->new_block == new_block) {\n                // Read the xor'ed data from COW\n                void* buffer = bufsink.GetPayloadBuffer(BLOCK_SZ);\n                if (!buffer) {\n                    SNAP_LOG(ERROR) << \"ReadAhead - failed to allocate buffer\";\n                    return false;\n                }\n                if (ssize_t rv = reader_->ReadData(xor_op, buffer, BLOCK_SZ); rv != BLOCK_SZ) {\n                    SNAP_LOG(ERROR)\n                            << \" ReadAhead - XorOp Read failed for block: \" << xor_op->new_block\n                            << \", return value: \" << rv;\n                    return false;\n                }\n                // Pointer to the data read from base device\n                uint8_t* read_buffer = reinterpret_cast<uint8_t*>(bufptr);\n                // Get the xor'ed data read from COW device\n                uint8_t* xor_data = reinterpret_cast<uint8_t*>(bufsink.GetPayloadBufPtr());\n\n                // Retrieve the original data\n                for (size_t byte_offset = 0; byte_offset < BLOCK_SZ; byte_offset++) {\n                    read_buffer[byte_offset] ^= xor_data[byte_offset];\n                }\n\n                // Move to next XOR op\n                xor_index += 1;\n            }\n        }\n\n        offset += BLOCK_SZ;\n        // Track the metadata blocks which are stored in scratch space\n        bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +\n                                                       metadata_offset);\n\n        bm->new_block = new_block;\n        bm->file_offset = file_offset;\n\n        metadata_offset += sizeof(struct ScratchMetadata);\n        file_offset += BLOCK_SZ;\n    }\n\n    // Verify if all the xor blocks were scanned to retrieve the original data\n    CHECK(xor_index == xor_op_vec.size());\n\n    // This is important - explicitly set the contents to zero. This is used\n    // when re-constructing the data after crash. This indicates end of\n    // reading metadata contents when re-constructing the data\n    bm = reinterpret_cast<struct ScratchMetadata*>((char*)ra_temp_meta_buffer_.get() +\n                                                   metadata_offset);\n    bm->new_block = 0;\n    bm->file_offset = 0;\n\n    notify_read_ahead_failed.Cancel();\n    return true;\n}\n\nbool ReadAhead::ReadAheadIOStart() {\n    // Check if the data has to be constructed from the COW file.\n    // This will be true only once during boot up after a crash\n    // during merge.\n    if (snapuserd_->ShouldReconstructDataFromCow()) {\n        return ReconstructDataFromCow();\n    }\n\n    bool retry = false;\n    bool ra_status;\n\n    // Start Async read-ahead\n    if (read_ahead_async_) {\n        ra_status = ReadAheadAsyncIO();\n        if (!ra_status) {\n            SNAP_LOG(ERROR) << \"ReadAheadAsyncIO failed - Falling back synchronous I/O\";\n            FinalizeIouring();\n            RAResetIter(total_blocks_merged_);\n            retry = true;\n            read_ahead_async_ = false;\n        }\n    }\n\n    // Check if we need to fallback and retry the merge\n    //\n    // If the device doesn't support async operations, we\n    // will directly enter here (aka devices with 4.x kernels)\n\n    const bool ra_sync_required = (retry || !read_ahead_async_);\n\n    if (ra_sync_required) {\n        ra_status = ReadAheadSyncIO();\n        if (!ra_status) {\n            SNAP_LOG(ERROR) << \"ReadAheadSyncIO failed\";\n            return false;\n        }\n    }\n\n    SNAP_LOG(DEBUG) << \"Read-ahead: total_ra_blocks_merged: \" << total_ra_blocks_completed_;\n\n    // Wait for the merge to finish for the previous RA window. We shouldn't\n    // be touching the scratch space until merge is complete of previous RA\n    // window. If there is a crash during this time frame, merge should resume\n    // based on the contents of the scratch space.\n    if (!snapuserd_->WaitForMergeReady()) {\n        SNAP_LOG(VERBOSE) << \"ReadAhead failed to wait for merge ready\";\n        return false;\n    }\n\n    // Acquire buffer lock before doing memcpy to the scratch buffer. Although,\n    // by now snapshot-merge thread shouldn't be working on this scratch space\n    // but we take additional measure to ensure that the buffer is not being\n    // used by the merge thread at this point. see b/377819507\n    {\n        std::lock_guard<std::mutex> buffer_lock(snapuserd_->GetBufferLock());\n        // Copy the data to scratch space\n        memcpy(metadata_buffer_, ra_temp_meta_buffer_.get(), snapuserd_->GetBufferMetadataSize());\n        memcpy(read_ahead_buffer_, ra_temp_buffer_.get(), total_blocks_merged_ * BLOCK_SZ);\n\n        loff_t offset = 0;\n        std::unordered_map<uint64_t, void*>& read_ahead_buffer_map = snapuserd_->GetReadAheadMap();\n        read_ahead_buffer_map.clear();\n\n        for (size_t block_index = 0; block_index < blocks_.size(); block_index++) {\n            void* bufptr = static_cast<void*>((char*)read_ahead_buffer_ + offset);\n            uint64_t new_block = blocks_[block_index];\n\n            read_ahead_buffer_map[new_block] = bufptr;\n            offset += BLOCK_SZ;\n        }\n\n        total_ra_blocks_completed_ += total_blocks_merged_;\n        snapuserd_->SetMergedBlockCountForNextCommit(total_blocks_merged_);\n    }\n\n    // Flush the scratch data - Technically, we should flush only for overlapping\n    // blocks; However, since this region is mmap'ed, the dirty pages can still\n    // get flushed to disk at any random point in time. Instead, make sure\n    // the data in scratch is in the correct state before merge thread resumes.\n    //\n    // Notify the Merge thread to resume merging this window\n    if (!snapuserd_->ReadAheadIOCompleted(true)) {\n        SNAP_LOG(ERROR) << \"ReadAheadIOCompleted failed...\";\n        snapuserd_->ReadAheadIOFailed();\n        return false;\n    }\n\n    return true;\n}\n\nbool ReadAhead::InitializeIouring() {\n    if (!snapuserd_->IsIouringSupported()) {\n        return false;\n    }\n\n    ring_ = std::make_unique<struct io_uring>();\n\n    int ret = io_uring_queue_init(queue_depth_, ring_.get(), 0);\n    if (ret) {\n        SNAP_LOG(ERROR) << \"io_uring_queue_init failed with ret: \" << ret;\n        return false;\n    }\n\n    // For xor ops processing\n    bufsink_.Initialize(PAYLOAD_BUFFER_SZ * 2);\n    read_ahead_async_ = true;\n\n    SNAP_LOG(INFO) << \"Read-ahead: io_uring initialized with queue depth: \" << queue_depth_;\n    return true;\n}\n\nvoid ReadAhead::FinalizeIouring() {\n    if (read_ahead_async_) {\n        io_uring_queue_exit(ring_.get());\n    }\n}\n\nbool ReadAhead::RunThread() {\n    SNAP_LOG(INFO) << \"ReadAhead thread started.\";\n\n    pthread_setname_np(pthread_self(), \"ReadAhead\");\n\n    if (!InitializeFds()) {\n        return false;\n    }\n\n    InitializeBuffer();\n\n    if (!InitReader()) {\n        return false;\n    }\n\n    InitializeRAIter();\n\n    InitializeIouring();\n\n    if (!SetThreadPriority(ANDROID_PRIORITY_BACKGROUND)) {\n        SNAP_PLOG(ERROR) << \"Failed to set thread priority\";\n    }\n\n    if (!SetProfiles({\"CPUSET_SP_BACKGROUND\"})) {\n        SNAP_PLOG(ERROR) << \"Failed to assign task profile to readahead thread\";\n    }\n\n    SNAP_LOG(INFO) << \"ReadAhead processing.\";\n    while (!RAIterDone()) {\n        if (!ReadAheadIOStart()) {\n            break;\n        }\n    }\n\n    FinalizeIouring();\n    CloseFds();\n    reader_->CloseCowFd();\n\n    SNAP_LOG(INFO) << \" ReadAhead thread terminating.\";\n    return true;\n}\n\n// Initialization\nbool ReadAhead::InitializeFds() {\n    backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));\n    if (backing_store_fd_ < 0) {\n        SNAP_PLOG(ERROR) << \"Open Failed: \" << backing_store_device_;\n        return false;\n    }\n\n    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));\n    if (cow_fd_ < 0) {\n        SNAP_PLOG(ERROR) << \"Open Failed: \" << cow_device_;\n        return false;\n    }\n\n    return true;\n}\n\nbool ReadAhead::InitReader() {\n    reader_ = snapuserd_->CloneReaderForWorker();\n\n    if (!reader_->InitForMerge(std::move(cow_fd_))) {\n        return false;\n    }\n    header_ = reader_->GetHeader();\n    return true;\n}\n\nvoid ReadAhead::InitializeRAIter() {\n    cowop_iter_ = reader_->GetOpIter(true);\n}\n\nbool ReadAhead::RAIterDone() {\n    if (cowop_iter_->AtEnd()) {\n        return true;\n    }\n\n    const CowOperation* cow_op = GetRAOpIter();\n\n    if (!IsOrderedOp(*cow_op)) {\n        return true;\n    }\n\n    return false;\n}\n\nvoid ReadAhead::RAIterNext() {\n    cowop_iter_->Next();\n}\n\nvoid ReadAhead::RAResetIter(uint64_t num_blocks) {\n    while (num_blocks && !cowop_iter_->AtBegin()) {\n        cowop_iter_->Prev();\n        num_blocks -= 1;\n    }\n}\n\nconst CowOperation* ReadAhead::GetRAOpIter() {\n    return cowop_iter_->Get();\n}\n\nvoid ReadAhead::InitializeBuffer() {\n    void* mapped_addr = snapuserd_->GetMappedAddr();\n    // Map the scratch space region into memory\n    metadata_buffer_ =\n            static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferMetadataOffset());\n    read_ahead_buffer_ = static_cast<void*>((char*)mapped_addr + snapuserd_->GetBufferDataOffset());\n\n    ra_temp_buffer_ = std::make_unique<uint8_t[]>(snapuserd_->GetBufferDataSize());\n    ra_temp_meta_buffer_ = std::make_unique<uint8_t[]>(snapuserd_->GetBufferMetadataSize());\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <memory>\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <libsnapshot/cow_reader.h>\n#include <liburing.h>\n#include <snapuserd/snapuserd_buffer.h>\n\nnamespace android {\nnamespace snapshot {\n\nclass SnapshotHandler;\n\nclass ReadAhead {\n  public:\n    ReadAhead(const std::string& cow_device, const std::string& backing_device,\n              const std::string& misc_name, std::shared_ptr<SnapshotHandler> snapuserd,\n              uint32_t cow_op_merge_size);\n    bool RunThread();\n\n  private:\n    void InitializeRAIter();\n    bool RAIterDone();\n    void RAIterNext();\n    void RAResetIter(uint64_t num_blocks);\n    const CowOperation* GetRAOpIter();\n\n    void InitializeBuffer();\n    bool InitReader();\n    bool InitializeFds();\n\n    void CloseFds() { backing_store_fd_ = {}; }\n\n    bool ReadAheadIOStart();\n    int PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops,\n                             std::vector<uint64_t>& blocks,\n                             std::vector<const CowOperation*>& xor_op_vec);\n    bool ReconstructDataFromCow();\n    void CheckOverlap(const CowOperation* cow_op);\n\n    bool ReadAheadAsyncIO();\n    bool ReapIoCompletions(int pending_ios_to_complete);\n    bool ReadXorData(size_t block_index, size_t xor_op_index,\n                     std::vector<const CowOperation*>& xor_op_vec);\n    void ProcessXorData(size_t& block_xor_index, size_t& xor_index,\n                        std::vector<const CowOperation*>& xor_op_vec, void* buffer,\n                        loff_t& buffer_offset);\n    void UpdateScratchMetadata();\n\n    bool ReadAheadSyncIO();\n    bool InitializeIouring();\n    void FinalizeIouring();\n\n    void* read_ahead_buffer_;\n    void* metadata_buffer_;\n\n    std::unique_ptr<ICowOpIter> cowop_iter_;\n\n    std::string cow_device_;\n    std::string backing_store_device_;\n    std::string misc_name_;\n\n    android::base::unique_fd cow_fd_;\n    android::base::unique_fd backing_store_fd_;\n\n    std::shared_ptr<SnapshotHandler> snapuserd_;\n    std::unique_ptr<CowReader> reader_;\n    CowHeader header_;\n\n    std::unordered_set<uint64_t> dest_blocks_;\n    std::unordered_set<uint64_t> source_blocks_;\n    bool overlap_;\n    std::vector<uint64_t> blocks_;\n    int total_blocks_merged_ = 0;\n    std::unique_ptr<uint8_t[]> ra_temp_buffer_;\n    std::unique_ptr<uint8_t[]> ra_temp_meta_buffer_;\n    BufferSink bufsink_;\n\n    uint64_t total_ra_blocks_completed_ = 0;\n    bool read_ahead_async_ = false;\n    // Queue depth of 8 seems optimal. We don't want\n    // to have a huge depth as it may put more memory pressure\n    // on the kernel worker threads given that we use\n    // IOSQE_ASYNC flag - ASYNC flags can potentially\n    // result in EINTR; Since we don't restart\n    // syscalls and fallback to synchronous I/O, we\n    // don't want huge queue depth\n    int queue_depth_ = 8;\n    uint32_t cow_op_merge_size_;\n    std::unique_ptr<struct io_uring> ring_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <arpa/inet.h>\n#include <cutils/sockets.h>\n#include <errno.h>\n#include <netinet/in.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/socket.h>\n#include <sys/system_properties.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/cmsg.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <fs_mgr/file_wait.h>\n#include <snapuserd/dm_user_block_server.h>\n#include <snapuserd/snapuserd_client.h>\n#include \"snapuserd_server.h\"\n#include \"user-space-merge/snapuserd_core.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace std::string_literals;\n\nusing android::base::borrowed_fd;\nusing android::base::unique_fd;\n\nUserSnapshotServer::UserSnapshotServer() {\n    terminating_ = false;\n    handlers_ = std::make_unique<SnapshotHandlerManager>();\n    block_server_factory_ = std::make_unique<DmUserBlockServerFactory>();\n}\n\nUserSnapshotServer::~UserSnapshotServer() {\n    // Close any client sockets that were added via AcceptClient().\n    for (size_t i = 1; i < watched_fds_.size(); i++) {\n        close(watched_fds_[i].fd);\n    }\n}\n\nstd::string UserSnapshotServer::GetDaemonStatus() {\n    std::string msg = \"\";\n\n    if (IsTerminating())\n        msg = \"passive\";\n    else\n        msg = \"active\";\n\n    return msg;\n}\n\nvoid UserSnapshotServer::Parsemsg(std::string const& msg, const char delim,\n                                  std::vector<std::string>& out) {\n    std::stringstream ss(msg);\n    std::string s;\n\n    while (std::getline(ss, s, delim)) {\n        out.push_back(s);\n    }\n}\n\nvoid UserSnapshotServer::ShutdownThreads() {\n    terminating_ = true;\n    handlers_->JoinAllThreads();\n}\n\nbool UserSnapshotServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {\n    ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), MSG_NOSIGNAL));\n    if (ret < 0) {\n        PLOG(ERROR) << \"Snapuserd:server: send() failed\";\n        return false;\n    }\n\n    if (ret < msg.size()) {\n        LOG(ERROR) << \"Partial send; expected \" << msg.size() << \" bytes, sent \" << ret;\n        return false;\n    }\n    return true;\n}\n\nbool UserSnapshotServer::Recv(android::base::borrowed_fd fd, std::string* data) {\n    char msg[kMaxPacketSize];\n    ssize_t rv = TEMP_FAILURE_RETRY(recv(fd.get(), msg, sizeof(msg), 0));\n    if (rv < 0) {\n        PLOG(ERROR) << \"recv failed\";\n        return false;\n    }\n    *data = std::string(msg, rv);\n    return true;\n}\n\nbool UserSnapshotServer::Receivemsg(android::base::borrowed_fd fd, const std::string& str) {\n    const char delim = ',';\n\n    std::vector<std::string> out;\n    Parsemsg(str, delim, out);\n\n    const auto& cmd = out[0];\n    if (cmd == \"init\") {\n        // Message format:\n        // init,<misc_name>,<cow_device_path>,<backing_device>,<base_path_merge>\n        //\n        // Reads the metadata and send the number of sectors\n        if (out.size() != 5) {\n            LOG(ERROR) << \"Malformed init message, \" << out.size() << \" parts\";\n            return Sendmsg(fd, \"fail\");\n        }\n\n        auto handler = AddHandler(out[1], out[2], out[3], out[4], std::nullopt);\n        if (!handler) {\n            return Sendmsg(fd, \"fail\");\n        }\n\n        auto num_sectors = handler->snapuserd()->GetNumSectors();\n        if (!num_sectors) {\n            return Sendmsg(fd, \"fail\");\n        }\n\n        auto retval = \"success,\" + std::to_string(num_sectors);\n        return Sendmsg(fd, retval);\n    } else if (cmd == \"start\") {\n        // Message format:\n        // start,<misc_name>\n        //\n        // Start the new thread which binds to dm-user misc device\n        if (out.size() != 2) {\n            LOG(ERROR) << \"Malformed start message, \" << out.size() << \" parts\";\n            return Sendmsg(fd, \"fail\");\n        }\n\n        if (!handlers_->StartHandler(out[1])) {\n            return Sendmsg(fd, \"fail\");\n        }\n        return Sendmsg(fd, \"success\");\n    } else if (cmd == \"stop\") {\n        // Message format: stop\n        //\n        // Stop all the threads gracefully and then shutdown the\n        // main thread\n        SetTerminating();\n        ShutdownThreads();\n        return true;\n    } else if (cmd == \"query\") {\n        // Message format: query\n        //\n        // As part of transition, Second stage daemon will be\n        // created before terminating the first stage daemon. Hence,\n        // for a brief period client may have to distiguish between\n        // first stage daemon and second stage daemon.\n        //\n        // Second stage daemon is marked as active and hence will\n        // be ready to receive control message.\n        return Sendmsg(fd, GetDaemonStatus());\n    } else if (cmd == \"delete\") {\n        // Message format:\n        // delete,<misc_name>\n        if (out.size() != 2) {\n            LOG(ERROR) << \"Malformed delete message, \" << out.size() << \" parts\";\n            return Sendmsg(fd, \"fail\");\n        }\n        if (!handlers_->DeleteHandler(out[1])) {\n            return Sendmsg(fd, \"fail\");\n        }\n        return Sendmsg(fd, \"success\");\n    } else if (cmd == \"detach\") {\n        handlers_->TerminateMergeThreads();\n        terminating_ = true;\n        return true;\n    } else if (cmd == \"supports\") {\n        if (out.size() != 2) {\n            LOG(ERROR) << \"Malformed supports message, \" << out.size() << \" parts\";\n            return Sendmsg(fd, \"fail\");\n        }\n        if (out[1] == \"second_stage_socket_handoff\") {\n            return Sendmsg(fd, \"success\");\n        }\n        return Sendmsg(fd, \"fail\");\n    } else if (cmd == \"initiate_merge\") {\n        if (out.size() != 2) {\n            LOG(ERROR) << \"Malformed initiate-merge message, \" << out.size() << \" parts\";\n            return Sendmsg(fd, \"fail\");\n        }\n        if (out[0] == \"initiate_merge\") {\n            if (!handlers_->InitiateMerge(out[1])) {\n                return Sendmsg(fd, \"fail\");\n            }\n            return Sendmsg(fd, \"success\");\n        }\n        return Sendmsg(fd, \"fail\");\n    } else if (cmd == \"merge_percent\") {\n        double percentage = handlers_->GetMergePercentage();\n        return Sendmsg(fd, std::to_string(percentage));\n    } else if (cmd == \"getstatus\") {\n        // Message format:\n        // getstatus,<misc_name>\n        if (out.size() != 2) {\n            LOG(ERROR) << \"Malformed delete message, \" << out.size() << \" parts\";\n            return Sendmsg(fd, \"snapshot-merge-failed\");\n        }\n        auto status = handlers_->GetMergeStatus(out[1]);\n        if (status.empty()) {\n            return Sendmsg(fd, \"snapshot-merge-failed\");\n        }\n        return Sendmsg(fd, status);\n    } else if (cmd == \"update-verify\") {\n        if (!handlers_->GetVerificationStatus()) {\n            return Sendmsg(fd, \"fail\");\n        }\n        return Sendmsg(fd, \"success\");\n    } else if (cmd == \"pause_merge\") {\n        handlers_->PauseMerge();\n        return Sendmsg(fd, \"success\");\n    } else if (cmd == \"resume_merge\") {\n        handlers_->ResumeMerge();\n        return Sendmsg(fd, \"success\");\n    } else {\n        LOG(ERROR) << \"Received unknown message type from client\";\n        Sendmsg(fd, \"fail\");\n        return false;\n    }\n}\n\nbool UserSnapshotServer::Start(const std::string& socketname) {\n    bool start_listening = true;\n\n    sockfd_.reset(android_get_control_socket(socketname.c_str()));\n    if (sockfd_ < 0) {\n        sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,\n                                          SOCK_STREAM));\n        if (sockfd_ < 0) {\n            PLOG(ERROR) << \"Failed to create server socket \" << socketname;\n            return false;\n        }\n        start_listening = false;\n    }\n    return StartWithSocket(start_listening);\n}\n\nbool UserSnapshotServer::StartWithSocket(bool start_listening) {\n    if (start_listening && listen(sockfd_.get(), 4) < 0) {\n        PLOG(ERROR) << \"listen socket failed\";\n        return false;\n    }\n\n    AddWatchedFd(sockfd_, POLLIN);\n    is_socket_present_ = true;\n\n    // If started in first-stage init, the property service won't be online.\n    if (access(\"/dev/socket/property_service\", F_OK) == 0) {\n        if (!android::base::SetProperty(\"snapuserd.ready\", \"true\")) {\n            LOG(ERROR) << \"Failed to set snapuserd.ready property\";\n            return false;\n        }\n    }\n\n    LOG(DEBUG) << \"Snapuserd server now accepting connections\";\n    return true;\n}\n\nbool UserSnapshotServer::Run() {\n    LOG(INFO) << \"Now listening on snapuserd socket\";\n\n    while (!IsTerminating()) {\n        int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));\n        if (rv < 0) {\n            PLOG(ERROR) << \"poll failed\";\n            return false;\n        }\n        if (!rv) {\n            continue;\n        }\n\n        if (watched_fds_[0].revents) {\n            AcceptClient();\n        }\n\n        auto iter = watched_fds_.begin() + 1;\n        while (iter != watched_fds_.end()) {\n            if (iter->revents && !HandleClient(iter->fd, iter->revents)) {\n                close(iter->fd);\n                iter = watched_fds_.erase(iter);\n            } else {\n                iter++;\n            }\n        }\n    }\n\n    handlers_->JoinAllThreads();\n    return true;\n}\n\nvoid UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) {\n    struct pollfd p = {};\n    p.fd = fd.get();\n    p.events = events;\n    watched_fds_.emplace_back(std::move(p));\n}\n\nvoid UserSnapshotServer::AcceptClient() {\n    int fd = TEMP_FAILURE_RETRY(accept4(sockfd_.get(), nullptr, nullptr, SOCK_CLOEXEC));\n    if (fd < 0) {\n        PLOG(ERROR) << \"accept4 failed\";\n        return;\n    }\n\n    AddWatchedFd(fd, POLLIN);\n}\n\nbool UserSnapshotServer::HandleClient(android::base::borrowed_fd fd, int revents) {\n    std::string str;\n    if (!Recv(fd, &str)) {\n        return false;\n    }\n    if (str.empty() && (revents & POLLHUP)) {\n        LOG(DEBUG) << \"Snapuserd client disconnected\";\n        return false;\n    }\n    if (!Receivemsg(fd, str)) {\n        LOG(ERROR) << \"Encountered error handling client message, revents: \" << revents;\n        return false;\n    }\n    return true;\n}\n\nvoid UserSnapshotServer::Interrupt() {\n    // Force close the socket so poll() fails.\n    sockfd_ = {};\n    SetTerminating();\n}\n\nstd::shared_ptr<HandlerThread> UserSnapshotServer::AddHandler(\n        const std::string& misc_name, const std::string& cow_device_path,\n        const std::string& backing_device, const std::string& base_path_merge,\n        std::optional<uint32_t> num_worker_threads, const bool o_direct,\n        uint32_t cow_op_merge_size) {\n    // We will need multiple worker threads only during\n    // device boot after OTA. For all other purposes,\n    // one thread is sufficient. We don't want to consume\n    // unnecessary memory especially during OTA install phase\n    // when daemon will be up during entire post install phase.\n    //\n    // During boot up, we need multiple threads primarily for\n    // update-verification.\n    if (!num_worker_threads.has_value()) {\n        num_worker_threads = kNumWorkerThreads;\n    }\n    if (is_socket_present_) {\n        num_worker_threads = 1;\n    }\n\n    if (android::base::EndsWith(misc_name, \"-init\") || is_socket_present_ ||\n        (access(kBootSnapshotsWithoutSlotSwitch, F_OK) == 0)) {\n        handlers_->DisableVerification();\n    }\n\n    auto opener = block_server_factory_->CreateOpener(misc_name);\n\n    return handlers_->AddHandler(misc_name, cow_device_path, backing_device, base_path_merge,\n                                 opener, num_worker_threads.value(), io_uring_enabled_, o_direct,\n                                 cow_op_merge_size);\n}\n\nbool UserSnapshotServer::WaitForSocket() {\n    auto scope_guard =\n            android::base::make_scope_guard([this]() -> void { handlers_->JoinAllThreads(); });\n\n    auto socket_path = ANDROID_SOCKET_DIR \"/\"s + kSnapuserdSocketProxy;\n\n    if (!android::fs_mgr::WaitForFile(socket_path, std::chrono::milliseconds::max())) {\n        LOG(ERROR)\n                << \"Failed to wait for proxy socket, second-stage snapuserd will fail to connect\";\n        return false;\n    }\n\n    // This initialization of system property is important. When daemon is\n    // launched post selinux transition (before init second stage),\n    // bionic libc initializes system property as part of __libc_init_common();\n    // however that initialization fails silently given that fact that we don't\n    // have /dev/__properties__ setup which is created at init second stage.\n    //\n    // At this point, we have the handlers setup and is safe to setup property.\n    __system_properties_init();\n\n    if (!android::base::WaitForProperty(\"snapuserd.proxy_ready\", \"true\")) {\n        LOG(ERROR)\n                << \"Failed to wait for proxy property, second-stage snapuserd will fail to connect\";\n        return false;\n    }\n\n    unique_fd fd(socket_local_client(kSnapuserdSocketProxy, ANDROID_SOCKET_NAMESPACE_RESERVED,\n                                     SOCK_SEQPACKET));\n    if (fd < 0) {\n        PLOG(ERROR) << \"Failed to connect to socket proxy\";\n        return false;\n    }\n\n    char code[1];\n    std::vector<unique_fd> fds;\n    ssize_t rv = android::base::ReceiveFileDescriptorVector(fd, code, sizeof(code), 1, &fds);\n    if (rv < 0) {\n        PLOG(ERROR) << \"Failed to receive server socket over proxy\";\n        return false;\n    }\n    if (fds.empty()) {\n        LOG(ERROR) << \"Expected at least one file descriptor from proxy\";\n        return false;\n    }\n\n    // We don't care if the ACK is received.\n    code[0] = 'a';\n    if (TEMP_FAILURE_RETRY(send(fd, code, sizeof(code), MSG_NOSIGNAL)) < 0) {\n        PLOG(ERROR) << \"Failed to send ACK to proxy\";\n        return false;\n    }\n\n    sockfd_ = std::move(fds[0]);\n    if (!StartWithSocket(true)) {\n        return false;\n    }\n\n    return Run();\n}\n\nbool UserSnapshotServer::RunForSocketHandoff() {\n    unique_fd proxy_fd(android_get_control_socket(kSnapuserdSocketProxy));\n    if (proxy_fd < 0) {\n        PLOG(FATAL) << \"Proxy could not get android control socket \" << kSnapuserdSocketProxy;\n    }\n    borrowed_fd server_fd(android_get_control_socket(kSnapuserdSocket));\n    if (server_fd < 0) {\n        PLOG(FATAL) << \"Proxy could not get android control socket \" << kSnapuserdSocket;\n    }\n\n    if (listen(proxy_fd.get(), 4) < 0) {\n        PLOG(FATAL) << \"Proxy listen socket failed\";\n    }\n\n    if (!android::base::SetProperty(\"snapuserd.proxy_ready\", \"true\")) {\n        LOG(FATAL) << \"Proxy failed to set ready property\";\n    }\n\n    unique_fd client_fd(\n            TEMP_FAILURE_RETRY(accept4(proxy_fd.get(), nullptr, nullptr, SOCK_CLOEXEC)));\n    if (client_fd < 0) {\n        PLOG(FATAL) << \"Proxy accept failed\";\n    }\n\n    char code[1] = {'a'};\n    std::vector<int> fds = {server_fd.get()};\n    ssize_t rv = android::base::SendFileDescriptorVector(client_fd, code, sizeof(code), fds);\n    if (rv < 0) {\n        PLOG(FATAL) << \"Proxy could not send file descriptor to snapuserd\";\n    }\n    // Wait for an ACK - results don't matter, we just don't want to risk closing\n    // the proxy socket too early.\n    if (recv(client_fd, code, sizeof(code), 0) < 0) {\n        PLOG(FATAL) << \"Proxy could not receive terminating code from snapuserd\";\n    }\n    return true;\n}\n\nbool UserSnapshotServer::StartHandler(const std::string& misc_name) {\n    return handlers_->StartHandler(misc_name);\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <poll.h>\n#include <sys/eventfd.h>\n\n#include <cstdio>\n#include <cstring>\n#include <functional>\n#include <future>\n#include <iostream>\n#include <mutex>\n#include <optional>\n#include <queue>\n#include <sstream>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <snapuserd/block_server.h>\n#include \"handler_manager.h\"\n#include \"snapuserd_core.h\"\n\nnamespace android {\nnamespace snapshot {\n\nstatic constexpr uint32_t kMaxPacketSize = 512;\n\nstatic constexpr char kBootSnapshotsWithoutSlotSwitch[] =\n        \"/metadata/ota/snapshot-boot-without-slot-switch\";\n\nclass UserSnapshotServer {\n  private:\n    android::base::unique_fd sockfd_;\n    bool terminating_;\n    volatile bool received_socket_signal_ = false;\n    std::vector<struct pollfd> watched_fds_;\n    bool is_socket_present_ = false;\n    bool is_server_running_ = false;\n    bool io_uring_enabled_ = false;\n    std::unique_ptr<ISnapshotHandlerManager> handlers_;\n    std::unique_ptr<IBlockServerFactory> block_server_factory_;\n\n    std::mutex lock_;\n\n    void AddWatchedFd(android::base::borrowed_fd fd, int events);\n    void AcceptClient();\n    bool HandleClient(android::base::borrowed_fd fd, int revents);\n    bool Recv(android::base::borrowed_fd fd, std::string* data);\n    bool Sendmsg(android::base::borrowed_fd fd, const std::string& msg);\n    bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);\n\n    void ShutdownThreads();\n    std::string GetDaemonStatus();\n    void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);\n\n    bool IsTerminating() { return terminating_; }\n\n    void JoinAllThreads();\n    bool StartWithSocket(bool start_listening);\n\n  public:\n    UserSnapshotServer();\n    ~UserSnapshotServer();\n\n    bool Start(const std::string& socketname);\n    bool Run();\n    void Interrupt();\n    bool RunForSocketHandoff();\n    bool WaitForSocket();\n\n    std::shared_ptr<HandlerThread> AddHandler(const std::string& misc_name,\n                                              const std::string& cow_device_path,\n                                              const std::string& backing_device,\n                                              const std::string& base_path_merge,\n                                              std::optional<uint32_t> num_worker_threads,\n                                              bool o_direct = false,\n                                              uint32_t cow_op_merge_size = 0);\n    bool StartHandler(const std::string& misc_name);\n\n    void SetTerminating() { terminating_ = true; }\n    void ReceivedSocketSignal() { received_socket_signal_ = true; }\n    void SetServerRunning() { is_server_running_ = true; }\n    bool IsServerRunning() { return is_server_running_; }\n    void SetIouringEnabled() { io_uring_enabled_ = true; }\n    bool IsIouringEnabled() { return io_uring_enabled_; }\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <android-base/strings.h>\n#include <gflags/gflags.h>\n\n#include <fcntl.h>\n#include <linux/fs.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <iostream>\n#include <memory>\n#include <string_view>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n#include <fs_mgr/file_wait.h>\n#include <gtest/gtest.h>\n#include <libdm/dm.h>\n#include <libdm/loop_control.h>\n#include <libsnapshot/cow_writer.h>\n#include <snapuserd/dm_user_block_server.h>\n#include <storage_literals/storage_literals.h>\n#include \"handler_manager.h\"\n#include \"merge_worker.h\"\n#include \"read_worker.h\"\n#include \"snapuserd_core.h\"\n#include \"testing/dm_user_harness.h\"\n#include \"testing/host_harness.h\"\n#include \"testing/temp_device.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android::storage_literals;\nusing android::base::unique_fd;\nusing LoopDevice = android::dm::LoopDevice;\nusing namespace std::chrono_literals;\nusing namespace android::dm;\nusing namespace std;\nusing testing::AssertionFailure;\nusing testing::AssertionResult;\nusing testing::AssertionSuccess;\nusing ::testing::TestWithParam;\n\nstruct TestParam {\n    bool io_uring;\n    bool o_direct;\n    std::string compression;\n    int block_size;\n    int num_threads;\n    uint32_t cow_op_merge_size;\n};\n\nclass SnapuserdTestBase : public ::testing::TestWithParam<TestParam> {\n  protected:\n    virtual void SetUp() override;\n    void TearDown() override;\n    void CreateBaseDevice();\n    void CreateCowDevice();\n    void SetDeviceControlName();\n    std::unique_ptr<ICowWriter> CreateCowDeviceInternal();\n    std::unique_ptr<ICowWriter> CreateV3Cow();\n\n    unique_fd GetCowFd() { return unique_fd{dup(cow_system_->fd)}; }\n\n    bool ShouldSkipSetUp();\n\n    std::unique_ptr<ITestHarness> harness_;\n    size_t size_ = 10_MiB;\n    int total_base_size_ = 0;\n    std::string system_device_ctrl_name_;\n    std::string system_device_name_;\n\n    unique_ptr<IBackingDevice> base_dev_;\n    unique_fd base_fd_;\n\n    std::unique_ptr<TemporaryFile> cow_system_;\n\n    std::unique_ptr<uint8_t[]> orig_buffer_;\n};\n\nvoid SnapuserdTestBase::SetUp() {\n    if (ShouldSkipSetUp()) {\n        GTEST_SKIP() << \"snapuserd not supported on this device\";\n    }\n\n#if __ANDROID__\n    harness_ = std::make_unique<DmUserTestHarness>();\n#else\n    harness_ = std::make_unique<HostTestHarness>();\n#endif\n}\n\nbool SnapuserdTestBase::ShouldSkipSetUp() {\n#ifdef __ANDROID__\n    if (!android::snapshot::CanUseUserspaceSnapshots() ||\n        android::snapshot::IsVendorFromAndroid12()) {\n        return true;\n    }\n#endif\n    return false;\n}\n\nvoid SnapuserdTestBase::TearDown() {\n    cow_system_ = nullptr;\n}\n\nvoid SnapuserdTestBase::CreateBaseDevice() {\n    total_base_size_ = (size_ * 5);\n\n    base_dev_ = harness_->CreateBackingDevice(total_base_size_);\n    ASSERT_NE(base_dev_, nullptr);\n\n    base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC));\n    ASSERT_GE(base_fd_, 0);\n\n    unique_fd rnd_fd(open(\"/dev/random\", O_RDONLY));\n    ASSERT_GE(rnd_fd, 0);\n\n    std::unique_ptr<uint8_t[]> random_buffer = std::make_unique<uint8_t[]>(1_MiB);\n\n    for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) {\n        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);\n        ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true);\n    }\n\n    ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0);\n}\n\nstd::unique_ptr<ICowWriter> SnapuserdTestBase::CreateCowDeviceInternal() {\n    std::string path = android::base::GetExecutableDirectory();\n    cow_system_ = std::make_unique<TemporaryFile>(path);\n\n    CowOptions options;\n    options.compression = \"gz\";\n\n    return CreateCowWriter(2, options, GetCowFd());\n}\n\nstd::unique_ptr<ICowWriter> SnapuserdTestBase::CreateV3Cow() {\n    const TestParam params = GetParam();\n\n    CowOptions options;\n    options.op_count_max = 100000;\n    options.compression = params.compression;\n    options.num_compress_threads = params.num_threads;\n    options.batch_write = true;\n    options.compression_factor = params.block_size;\n\n    std::string path = android::base::GetExecutableDirectory();\n    cow_system_ = std::make_unique<TemporaryFile>(path);\n\n    return CreateCowWriter(3, options, GetCowFd());\n}\n\nvoid SnapuserdTestBase::CreateCowDevice() {\n    unique_fd rnd_fd;\n    loff_t offset = 0;\n\n    auto writer = CreateCowDeviceInternal();\n    ASSERT_NE(writer, nullptr);\n\n    rnd_fd.reset(open(\"/dev/random\", O_RDONLY));\n    ASSERT_TRUE(rnd_fd > 0);\n\n    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);\n\n    // Fill random data\n    for (size_t j = 0; j < (size_ / 1_MiB); j++) {\n        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),\n                  true);\n\n        offset += 1_MiB;\n    }\n\n    size_t num_blocks = size_ / writer->GetBlockSize();\n    size_t blk_end_copy = num_blocks * 2;\n    size_t source_blk = num_blocks - 1;\n    size_t blk_src_copy = blk_end_copy - 1;\n\n    uint32_t sequence[num_blocks * 2];\n    // Sequence for Copy ops\n    for (int i = 0; i < num_blocks; i++) {\n        sequence[i] = num_blocks - 1 - i;\n    }\n    // Sequence for Xor ops\n    for (int i = 0; i < num_blocks; i++) {\n        sequence[num_blocks + i] = 5 * num_blocks - 1 - i;\n    }\n    ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence));\n\n    size_t x = num_blocks;\n    while (1) {\n        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));\n        x -= 1;\n        if (x == 0) {\n            break;\n        }\n        source_blk -= 1;\n        blk_src_copy -= 1;\n    }\n\n    source_blk = num_blocks;\n    blk_src_copy = blk_end_copy;\n\n    ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_));\n\n    size_t blk_zero_copy_start = source_blk + num_blocks;\n    size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks;\n\n    ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks));\n\n    size_t blk_random2_replace_start = blk_zero_copy_end;\n\n    ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_));\n\n    size_t blk_xor_start = blk_random2_replace_start + num_blocks;\n    size_t xor_offset = BLOCK_SZ / 2;\n    ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks,\n                                     xor_offset));\n\n    // Flush operations\n    ASSERT_TRUE(writer->Finalize());\n    // Construct the buffer required for validation\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n    std::string zero_buffer(size_, 0);\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true);\n    memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_);\n    memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_);\n    memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_);\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_,\n                                               size_ + xor_offset),\n              true);\n    for (int i = 0; i < size_; i++) {\n        orig_buffer_.get()[(size_ * 4) + i] =\n                (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]);\n    }\n}\n\nvoid SnapuserdTestBase::SetDeviceControlName() {\n    system_device_name_.clear();\n    system_device_ctrl_name_.clear();\n\n    std::string str(cow_system_->path);\n    std::size_t found = str.find_last_of(\"/\\\\\");\n    ASSERT_NE(found, std::string::npos);\n    system_device_name_ = str.substr(found + 1);\n\n    system_device_ctrl_name_ = system_device_name_ + \"-ctrl\";\n}\n\nclass SnapuserdTest : public SnapuserdTestBase {\n  public:\n    void SetupDefault();\n    void SetupOrderedOps();\n    void SetupOrderedOpsInverted();\n    void SetupCopyOverlap_1();\n    void SetupCopyOverlap_2();\n    void SetupDeviceForPassthrough();\n    bool Merge();\n    void ValidateMerge();\n    void ReadSnapshotDeviceAndValidate();\n    void ReadSnapshotAndValidateOverlappingBlocks();\n    void Shutdown();\n    void MergeInterrupt();\n    void MergeInterruptFixed(int duration);\n    void MergeInterruptAndValidate(int duration);\n    void MergeInterruptRandomly(int max_duration);\n    bool StartMerge();\n    void CheckMergeCompletion();\n\n    static const uint64_t kSectorSize = 512;\n\n  protected:\n    void SetUp() override;\n    void TearDown() override;\n\n    void SetupImpl();\n\n    void SimulateDaemonRestart();\n\n    void CreateCowDeviceWithNoBlockChanges();\n    void ValidateDeviceWithNoBlockChanges();\n\n    void CreateCowDeviceOrderedOps();\n    void CreateCowDeviceOrderedOpsInverted();\n    void CreateCowDeviceWithCopyOverlap_1();\n    void CreateCowDeviceWithCopyOverlap_2();\n    void SetupDaemon();\n    void InitCowDevice();\n    void InitDaemon();\n    void CreateUserDevice();\n\n    unique_ptr<IUserDevice> dmuser_dev_;\n\n    std::unique_ptr<uint8_t[]> merged_buffer_;\n    std::unique_ptr<SnapshotHandlerManager> handlers_;\n    int cow_num_sectors_;\n};\n\nvoid SnapuserdTest::SetUp() {\n    if (ShouldSkipSetUp()) {\n        GTEST_SKIP() << \"snapuserd not supported on this device\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());\n    handlers_ = std::make_unique<SnapshotHandlerManager>();\n}\n\nvoid SnapuserdTest::TearDown() {\n    SnapuserdTestBase::TearDown();\n    Shutdown();\n}\n\nvoid SnapuserdTest::Shutdown() {\n    if (!handlers_) {\n        return;\n    }\n    if (dmuser_dev_) {\n        ASSERT_TRUE(dmuser_dev_->Destroy());\n    }\n\n    auto misc_device = \"/dev/dm-user/\" + system_device_ctrl_name_;\n    ASSERT_TRUE(handlers_->DeleteHandler(system_device_ctrl_name_));\n    ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s));\n    handlers_->TerminateMergeThreads();\n    handlers_->JoinAllThreads();\n    handlers_ = std::make_unique<SnapshotHandlerManager>();\n}\n\nvoid SnapuserdTest::SetupDefault() {\n    ASSERT_NO_FATAL_FAILURE(SetupImpl());\n}\n\nvoid SnapuserdTest::SetupOrderedOps() {\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOps());\n    ASSERT_NO_FATAL_FAILURE(SetupDaemon());\n}\n\nvoid SnapuserdTest::SetupDeviceForPassthrough() {\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithNoBlockChanges());\n    ASSERT_NO_FATAL_FAILURE(SetupDaemon());\n}\n\nvoid SnapuserdTest::SetupOrderedOpsInverted() {\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOpsInverted());\n    ASSERT_NO_FATAL_FAILURE(SetupDaemon());\n}\n\nvoid SnapuserdTest::SetupCopyOverlap_1() {\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_1());\n    ASSERT_NO_FATAL_FAILURE(SetupDaemon());\n}\n\nvoid SnapuserdTest::SetupCopyOverlap_2() {\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_2());\n    ASSERT_NO_FATAL_FAILURE(SetupDaemon());\n}\n\nvoid SnapuserdTest::SetupDaemon() {\n    SetDeviceControlName();\n\n    ASSERT_NO_FATAL_FAILURE(CreateUserDevice());\n    ASSERT_NO_FATAL_FAILURE(InitCowDevice());\n    ASSERT_NO_FATAL_FAILURE(InitDaemon());\n}\n\nvoid SnapuserdTest::ReadSnapshotDeviceAndValidate() {\n    unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));\n    ASSERT_GE(fd, 0);\n    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);\n\n    // COPY\n    loff_t offset = 0;\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);\n\n    // REPLACE\n    offset += size_;\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);\n\n    // ZERO\n    offset += size_;\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);\n\n    // REPLACE\n    offset += size_;\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);\n\n    // XOR\n    offset += size_;\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapuserd_buffer.get(), size_, offset), true);\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);\n}\n\nvoid SnapuserdTest::ReadSnapshotAndValidateOverlappingBlocks() {\n    // Open COW device\n    unique_fd fd(open(cow_system_->path, O_RDONLY));\n    ASSERT_GE(fd, 0);\n\n    CowReader reader;\n    ASSERT_TRUE(reader.Parse(fd));\n\n    const auto& header = reader.GetHeader();\n    size_t total_mapped_addr_length = header.prefix.header_size + BUFFER_REGION_DEFAULT_SIZE;\n\n    ASSERT_GE(header.prefix.major_version, 2);\n\n    void* mapped_addr = mmap(NULL, total_mapped_addr_length, PROT_READ, MAP_SHARED, fd.get(), 0);\n    ASSERT_NE(mapped_addr, MAP_FAILED);\n\n    bool populate_data_from_scratch = false;\n    struct BufferState* ra_state =\n            reinterpret_cast<struct BufferState*>((char*)mapped_addr + header.prefix.header_size);\n    if (ra_state->read_ahead_state == kCowReadAheadDone) {\n        populate_data_from_scratch = true;\n    }\n\n    size_t num_merge_ops = header.num_merge_ops;\n    // We have some partial merge operations completed.\n    // To test the merge-resume path, forcefully corrupt the data of the base\n    // device for the offsets where the merge is still pending.\n    if (num_merge_ops && populate_data_from_scratch) {\n        std::string corrupt_buffer(4096, 0);\n        // Corrupt two blocks from the point where the merge has to be resumed by\n        // writing down zeroe's.\n        //\n        // Now, since this is a merge-resume path, the \"correct\" data should be\n        // in the scratch space of the COW device. When there is an I/O request\n        // from the snapshot device, the data has to be retrieved from the\n        // scratch space. If not and I/O is routed to the base device, we\n        // may end up with corruption.\n        off_t corrupt_offset = (num_merge_ops + 2) * 4096;\n\n        if (corrupt_offset < size_) {\n            ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),\n                                                        4096, corrupt_offset),\n                      true);\n            corrupt_offset -= 4096;\n            ASSERT_EQ(android::base::WriteFullyAtOffset(base_fd_, (void*)corrupt_buffer.c_str(),\n                                                        4096, corrupt_offset),\n                      true);\n            fsync(base_fd_.get());\n        }\n    }\n\n    // Time to read the snapshot device.\n    unique_fd snapshot_fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY | O_DIRECT | O_SYNC));\n    ASSERT_GE(snapshot_fd, 0);\n\n    void* buff_addr;\n    ASSERT_EQ(posix_memalign(&buff_addr, 4096, size_), 0);\n\n    std::unique_ptr<void, decltype(&::free)> snapshot_buffer(buff_addr, ::free);\n\n    // Scan the entire snapshot device and read the data and verify data\n    // integrity. Since the base device was forcefully corrupted, the data from\n    // this scan should be retrieved from scratch space of the COW partition.\n    //\n    // Furthermore, after the merge is complete, base device data is again\n    // verified as the aforementioned corrupted blocks aren't persisted.\n    ASSERT_EQ(ReadFullyAtOffset(snapshot_fd, snapshot_buffer.get(), size_, 0), true);\n    ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);\n}\n\nvoid SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() {\n    auto writer = CreateCowDeviceInternal();\n    ASSERT_NE(writer, nullptr);\n\n    size_t num_blocks = size_ / writer->GetBlockSize();\n    size_t x = num_blocks;\n    size_t blk_src_copy = 0;\n\n    // Create overlapping copy operations\n    while (1) {\n        ASSERT_TRUE(writer->AddCopy(blk_src_copy, blk_src_copy + 1));\n        x -= 1;\n        if (x == 1) {\n            break;\n        }\n        blk_src_copy += 1;\n    }\n\n    // Flush operations\n    ASSERT_TRUE(writer->Finalize());\n\n    // Construct the buffer required for validation\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n\n    // Read the entire base device\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),\n              true);\n\n    // Merged operations required for validation\n    int block_size = 4096;\n    x = num_blocks;\n    loff_t src_offset = block_size;\n    loff_t dest_offset = 0;\n\n    while (1) {\n        memmove((char*)orig_buffer_.get() + dest_offset, (char*)orig_buffer_.get() + src_offset,\n                block_size);\n        x -= 1;\n        if (x == 1) {\n            break;\n        }\n        src_offset += block_size;\n        dest_offset += block_size;\n    }\n}\n\nvoid SnapuserdTest::CreateCowDeviceWithNoBlockChanges() {\n    auto writer = CreateCowDeviceInternal();\n    ASSERT_NE(writer, nullptr);\n\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);\n    std::memset(buffer.get(), 'A', BLOCK_SZ);\n\n    // This test focusses on not changing all the blocks thereby validating\n    // the pass-through I/O\n\n    // Replace the first block\n    ASSERT_TRUE(writer->AddRawBlocks(1, buffer.get(), BLOCK_SZ));\n\n    // Set zero block of Block 3\n    ASSERT_TRUE(writer->AddZeroBlocks(3, 1));\n\n    ASSERT_TRUE(writer->Finalize());\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n\n    // Read the entire base device\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),\n              true);\n\n    off_t offset = BLOCK_SZ;\n    std::memcpy(orig_buffer_.get() + offset, buffer.get(), BLOCK_SZ);\n    offset = 3 * BLOCK_SZ;\n    std::memset(orig_buffer_.get() + offset, 0, BLOCK_SZ);\n}\n\nvoid SnapuserdTest::ValidateDeviceWithNoBlockChanges() {\n    unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));\n    ASSERT_GE(fd, 0);\n    std::unique_ptr<uint8_t[]> snapshot_buffer = std::make_unique<uint8_t[]>(size_);\n    std::memset(snapshot_buffer.get(), 'B', size_);\n\n    // All the I/O request should be a pass through to base device except for\n    // Block 1 and Block 3.\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapshot_buffer.get(), size_, 0), true);\n    ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);\n}\n\nvoid SnapuserdTest::CreateCowDeviceWithCopyOverlap_1() {\n    auto writer = CreateCowDeviceInternal();\n    ASSERT_NE(writer, nullptr);\n\n    size_t num_blocks = size_ / writer->GetBlockSize();\n    size_t x = num_blocks;\n    size_t blk_src_copy = num_blocks - 1;\n\n    // Create overlapping copy operations\n    while (1) {\n        ASSERT_TRUE(writer->AddCopy(blk_src_copy + 1, blk_src_copy));\n        x -= 1;\n        if (x == 0) {\n            ASSERT_EQ(blk_src_copy, 0);\n            break;\n        }\n        blk_src_copy -= 1;\n    }\n\n    // Flush operations\n    ASSERT_TRUE(writer->Finalize());\n\n    // Construct the buffer required for validation\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n\n    // Read the entire base device\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),\n              true);\n\n    // Merged operations\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), writer->GetBlockSize(),\n                                               0),\n              true);\n    ASSERT_EQ(android::base::ReadFullyAtOffset(\n                      base_fd_, (char*)orig_buffer_.get() + writer->GetBlockSize(), size_, 0),\n              true);\n}\n\nvoid SnapuserdTest::CreateCowDeviceOrderedOpsInverted() {\n    unique_fd rnd_fd;\n    loff_t offset = 0;\n\n    auto writer = CreateCowDeviceInternal();\n    ASSERT_NE(writer, nullptr);\n\n    rnd_fd.reset(open(\"/dev/random\", O_RDONLY));\n    ASSERT_TRUE(rnd_fd > 0);\n\n    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);\n\n    // Fill random data\n    for (size_t j = 0; j < (size_ / 1_MiB); j++) {\n        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),\n                  true);\n\n        offset += 1_MiB;\n    }\n\n    size_t num_blocks = size_ / writer->GetBlockSize();\n    size_t blk_end_copy = num_blocks * 3;\n    size_t source_blk = num_blocks - 1;\n    size_t blk_src_copy = blk_end_copy - 1;\n    uint16_t xor_offset = 5;\n\n    size_t x = num_blocks;\n    while (1) {\n        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));\n        x -= 1;\n        if (x == 0) {\n            break;\n        }\n        source_blk -= 1;\n        blk_src_copy -= 1;\n    }\n\n    for (size_t i = num_blocks; i > 0; i--) {\n        ASSERT_TRUE(writer->AddXorBlocks(\n                num_blocks + i - 1, &random_buffer_1_.get()[writer->GetBlockSize() * (i - 1)],\n                writer->GetBlockSize(), 2 * num_blocks + i - 1, xor_offset));\n    }\n    // Flush operations\n    ASSERT_TRUE(writer->Finalize());\n    // Construct the buffer required for validation\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n    // Read the entire base device\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),\n              true);\n    // Merged Buffer\n    memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);\n    memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);\n    for (int i = 0; i < size_; i++) {\n        orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];\n    }\n}\n\nvoid SnapuserdTest::CreateCowDeviceOrderedOps() {\n    unique_fd rnd_fd;\n    loff_t offset = 0;\n\n    auto writer = CreateCowDeviceInternal();\n    ASSERT_NE(writer, nullptr);\n\n    rnd_fd.reset(open(\"/dev/random\", O_RDONLY));\n    ASSERT_TRUE(rnd_fd > 0);\n\n    std::unique_ptr<uint8_t[]> random_buffer_1_ = std::make_unique<uint8_t[]>(size_);\n\n    // Fill random data\n    for (size_t j = 0; j < (size_ / 1_MiB); j++) {\n        ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0),\n                  true);\n\n        offset += 1_MiB;\n    }\n    memset(random_buffer_1_.get(), 0, size_);\n\n    size_t num_blocks = size_ / writer->GetBlockSize();\n    size_t x = num_blocks;\n    size_t source_blk = 0;\n    size_t blk_src_copy = 2 * num_blocks;\n    uint16_t xor_offset = 5;\n\n    while (1) {\n        ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy));\n\n        x -= 1;\n        if (x == 0) {\n            break;\n        }\n        source_blk += 1;\n        blk_src_copy += 1;\n    }\n\n    ASSERT_TRUE(writer->AddXorBlocks(num_blocks, random_buffer_1_.get(), size_, 2 * num_blocks,\n                                     xor_offset));\n    // Flush operations\n    ASSERT_TRUE(writer->Finalize());\n    // Construct the buffer required for validation\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n    // Read the entire base device\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),\n              true);\n    // Merged Buffer\n    memmove(orig_buffer_.get(), (char*)orig_buffer_.get() + 2 * size_, size_);\n    memmove(orig_buffer_.get() + size_, (char*)orig_buffer_.get() + 2 * size_ + xor_offset, size_);\n    for (int i = 0; i < size_; i++) {\n        orig_buffer_.get()[size_ + i] ^= random_buffer_1_.get()[i];\n    }\n}\n\nvoid SnapuserdTest::InitCowDevice() {\n    auto factory = harness_->GetBlockServerFactory();\n    auto opener = factory->CreateOpener(system_device_ctrl_name_);\n    handlers_->DisableVerification();\n    const TestParam params = GetParam();\n    auto handler = handlers_->AddHandler(\n            system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),\n            opener, 1, params.io_uring, params.o_direct, params.cow_op_merge_size);\n    ASSERT_NE(handler, nullptr);\n    ASSERT_NE(handler->snapuserd(), nullptr);\n#ifdef __ANDROID__\n    ASSERT_NE(handler->snapuserd()->GetNumSectors(), 0);\n#endif\n}\n\nvoid SnapuserdTest::CreateUserDevice() {\n    auto dev_sz = base_dev_->GetSize();\n    ASSERT_NE(dev_sz, 0);\n\n    cow_num_sectors_ = dev_sz >> 9;\n\n    dmuser_dev_ = harness_->CreateUserDevice(system_device_name_, system_device_ctrl_name_,\n                                             cow_num_sectors_);\n    ASSERT_NE(dmuser_dev_, nullptr);\n}\n\nvoid SnapuserdTest::InitDaemon() {\n    ASSERT_TRUE(handlers_->StartHandler(system_device_ctrl_name_));\n}\n\nvoid SnapuserdTest::CheckMergeCompletion() {\n    while (true) {\n        double percentage = handlers_->GetMergePercentage();\n        if ((int)percentage == 100) {\n            break;\n        }\n\n        std::this_thread::sleep_for(1s);\n    }\n}\n\nvoid SnapuserdTest::SetupImpl() {\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(CreateCowDevice());\n\n    SetDeviceControlName();\n\n    ASSERT_NO_FATAL_FAILURE(CreateUserDevice());\n    ASSERT_NO_FATAL_FAILURE(InitCowDevice());\n    ASSERT_NO_FATAL_FAILURE(InitDaemon());\n}\n\nbool SnapuserdTest::Merge() {\n    if (!StartMerge()) {\n        return false;\n    }\n    CheckMergeCompletion();\n    return true;\n}\n\nbool SnapuserdTest::StartMerge() {\n    return handlers_->InitiateMerge(system_device_ctrl_name_);\n}\n\nvoid SnapuserdTest::ValidateMerge() {\n    merged_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, merged_buffer_.get(), total_base_size_, 0),\n              true);\n    ASSERT_EQ(memcmp(merged_buffer_.get(), orig_buffer_.get(), total_base_size_), 0);\n}\n\nvoid SnapuserdTest::SimulateDaemonRestart() {\n    ASSERT_NO_FATAL_FAILURE(Shutdown());\n    std::this_thread::sleep_for(500ms);\n    SetDeviceControlName();\n    ASSERT_NO_FATAL_FAILURE(CreateUserDevice());\n    ASSERT_NO_FATAL_FAILURE(InitCowDevice());\n    ASSERT_NO_FATAL_FAILURE(InitDaemon());\n}\n\nvoid SnapuserdTest::MergeInterruptRandomly(int max_duration) {\n    std::srand(std::time(nullptr));\n    ASSERT_TRUE(StartMerge());\n\n    for (int i = 0; i < 20; i++) {\n        int duration = std::rand() % max_duration;\n        std::this_thread::sleep_for(std::chrono::milliseconds(duration));\n        ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n        ASSERT_TRUE(StartMerge());\n    }\n\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n    ASSERT_TRUE(Merge());\n}\n\nvoid SnapuserdTest::MergeInterruptFixed(int duration) {\n    ASSERT_TRUE(StartMerge());\n\n    for (int i = 0; i < 25; i++) {\n        std::this_thread::sleep_for(std::chrono::milliseconds(duration));\n        ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n        ASSERT_TRUE(StartMerge());\n    }\n\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n    ASSERT_TRUE(Merge());\n}\n\nvoid SnapuserdTest::MergeInterruptAndValidate(int duration) {\n    ASSERT_TRUE(StartMerge());\n\n    for (int i = 0; i < 15; i++) {\n        std::this_thread::sleep_for(std::chrono::milliseconds(duration));\n        ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n        ReadSnapshotAndValidateOverlappingBlocks();\n        ASSERT_TRUE(StartMerge());\n    }\n\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n    ASSERT_TRUE(Merge());\n}\n\nvoid SnapuserdTest::MergeInterrupt() {\n    // Interrupt merge at various intervals\n    ASSERT_TRUE(StartMerge());\n    std::this_thread::sleep_for(250ms);\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n\n    ASSERT_TRUE(StartMerge());\n    std::this_thread::sleep_for(250ms);\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n\n    ASSERT_TRUE(StartMerge());\n    std::this_thread::sleep_for(150ms);\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n\n    ASSERT_TRUE(StartMerge());\n    std::this_thread::sleep_for(100ms);\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n\n    ASSERT_TRUE(StartMerge());\n    std::this_thread::sleep_for(800ms);\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n\n    ASSERT_TRUE(StartMerge());\n    std::this_thread::sleep_for(600ms);\n    ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart());\n\n    ASSERT_TRUE(Merge());\n}\n\nTEST_P(SnapuserdTest, Snapshot_Passthrough) {\n    if (!harness_->HasUserDevice()) {\n        GTEST_SKIP() << \"Skipping snapshot read; not supported\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SetupDeviceForPassthrough());\n    // I/O before merge\n    ASSERT_NO_FATAL_FAILURE(ValidateDeviceWithNoBlockChanges());\n    ASSERT_TRUE(Merge());\n    ValidateMerge();\n    // I/O after merge - daemon should read directly\n    // from base device\n    ASSERT_NO_FATAL_FAILURE(ValidateDeviceWithNoBlockChanges());\n}\n\nTEST_P(SnapuserdTest, Snapshot_IO_TEST) {\n    if (!harness_->HasUserDevice()) {\n        GTEST_SKIP() << \"Skipping snapshot read; not supported\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SetupDefault());\n    // I/O before merge\n    ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());\n    ASSERT_TRUE(Merge());\n    ValidateMerge();\n    // I/O after merge - daemon should read directly\n    // from base device\n    ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate());\n}\n\nTEST_P(SnapuserdTest, Snapshot_MERGE_IO_TEST) {\n    if (!harness_->HasUserDevice()) {\n        GTEST_SKIP() << \"Skipping snapshot read; not supported\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SetupDefault());\n    // Issue I/O before merge begins\n    auto read_future =\n            std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);\n    // Start the merge\n    ASSERT_TRUE(Merge());\n    ValidateMerge();\n    read_future.wait();\n}\n\nTEST_P(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) {\n    if (!harness_->HasUserDevice()) {\n        GTEST_SKIP() << \"Skipping snapshot read; not supported\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SetupDefault());\n    // Start the merge\n    ASSERT_TRUE(StartMerge());\n    // Issue I/O in parallel when merge is in-progress\n    auto read_future =\n            std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);\n    CheckMergeCompletion();\n    ValidateMerge();\n    read_future.wait();\n}\n\nTEST_P(SnapuserdTest, Snapshot_MERGE_PAUSE_RESUME) {\n    if (!harness_->HasUserDevice()) {\n        GTEST_SKIP() << \"Skipping snapshot read; not supported\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SetupDefault());\n    // Start the merge\n    ASSERT_TRUE(StartMerge());\n    std::this_thread::sleep_for(300ms);\n    // Pause merge\n    handlers_->PauseMerge();\n    // Issue I/O after pausing the merge and validate\n    auto read_future =\n            std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this);\n    // Resume the merge\n    handlers_->ResumeMerge();\n    CheckMergeCompletion();\n    ValidateMerge();\n    read_future.wait();\n}\n\nTEST_P(SnapuserdTest, Snapshot_Merge_Resume) {\n    ASSERT_NO_FATAL_FAILURE(SetupDefault());\n    ASSERT_NO_FATAL_FAILURE(MergeInterrupt());\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) {\n    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());\n    ASSERT_TRUE(Merge());\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) {\n    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());\n    ASSERT_TRUE(Merge());\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) {\n    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1());\n    ASSERT_NO_FATAL_FAILURE(MergeInterrupt());\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_IO_Validate_TEST) {\n    if (!harness_->HasUserDevice()) {\n        GTEST_SKIP() << \"Skipping snapshot read; not supported\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2());\n    ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300));\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) {\n    ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());\n    ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300));\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) {\n    ASSERT_NO_FATAL_FAILURE(SetupOrderedOps());\n    ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(500));\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) {\n    ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());\n    ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(50));\n    ValidateMerge();\n}\n\nTEST_P(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) {\n    ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted());\n    ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(50));\n    ValidateMerge();\n}\n\nclass SnapuserdVariableBlockSizeTest : public SnapuserdTest {\n  public:\n    void SetupCowV3ForVariableBlockSize();\n    void ReadSnapshotWithVariableBlockSize();\n\n  protected:\n    void SetUp() override;\n    void TearDown() override;\n\n    void CreateV3CowDeviceForVariableBlockSize();\n};\n\nvoid SnapuserdVariableBlockSizeTest::SetupCowV3ForVariableBlockSize() {\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(CreateV3CowDeviceForVariableBlockSize());\n    ASSERT_NO_FATAL_FAILURE(SetupDaemon());\n}\n\nvoid SnapuserdVariableBlockSizeTest::CreateV3CowDeviceForVariableBlockSize() {\n    auto writer = CreateV3Cow();\n\n    ASSERT_NE(writer, nullptr);\n    size_t total_data_to_write = size_;\n\n    size_t total_blocks_to_write = total_data_to_write / BLOCK_SZ;\n    size_t num_blocks_per_op = total_blocks_to_write / 4;\n    size_t source_block = 0;\n\n    size_t seq_len = num_blocks_per_op;\n    uint32_t sequence[seq_len];\n    size_t xor_block_start = seq_len * 3;\n    for (size_t i = 0; i < seq_len; i++) {\n        sequence[i] = xor_block_start + i;\n    }\n    ASSERT_TRUE(writer->AddSequenceData(seq_len, sequence));\n\n    size_t total_replace_blocks = num_blocks_per_op;\n    // Write some data which can be compressed\n    std::string data;\n    data.resize(total_replace_blocks * BLOCK_SZ, '\\0');\n    for (size_t i = 0; i < data.size(); i++) {\n        data[i] = static_cast<char>('A' + i / BLOCK_SZ);\n    }\n    // REPLACE ops\n    ASSERT_TRUE(writer->AddRawBlocks(source_block, data.data(), data.size()));\n\n    total_blocks_to_write -= total_replace_blocks;\n    source_block = source_block + total_replace_blocks;\n\n    // ZERO ops\n    size_t total_zero_blocks = total_blocks_to_write / 3;\n    ASSERT_TRUE(writer->AddZeroBlocks(source_block, total_zero_blocks));\n\n    total_blocks_to_write -= total_zero_blocks;\n    source_block = source_block + total_zero_blocks;\n\n    // Generate some random data wherein few blocks cannot be compressed.\n    // This is to test the I/O path for those blocks which aren't compressed.\n    size_t total_random_data_blocks = total_blocks_to_write / 2;\n    unique_fd rnd_fd(open(\"/dev/random\", O_RDONLY));\n\n    ASSERT_GE(rnd_fd, 0);\n    std::string random_buffer;\n    random_buffer.resize(total_random_data_blocks * BLOCK_SZ, '\\0');\n    ASSERT_EQ(\n            android::base::ReadFullyAtOffset(rnd_fd, random_buffer.data(), random_buffer.size(), 0),\n            true);\n    // REPLACE ops\n    ASSERT_TRUE(writer->AddRawBlocks(source_block, random_buffer.data(), random_buffer.size()));\n\n    total_blocks_to_write -= total_random_data_blocks;\n    source_block = source_block + total_random_data_blocks;\n\n    // XOR ops will always be 4k blocks\n    std::string xor_buffer;\n    xor_buffer.resize(total_blocks_to_write * BLOCK_SZ, '\\0');\n    for (size_t i = 0; i < xor_buffer.size(); i++) {\n        xor_buffer[i] = static_cast<char>('C' + i / BLOCK_SZ);\n    }\n    size_t xor_offset = 21;\n    std::string source_buffer;\n    source_buffer.resize(total_blocks_to_write * BLOCK_SZ, '\\0');\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, source_buffer.data(), source_buffer.size(),\n                                               size_ + xor_offset),\n              true);\n    for (size_t i = 0; i < xor_buffer.size(); i++) {\n        xor_buffer[i] ^= source_buffer[i];\n    }\n\n    ASSERT_EQ(xor_block_start, source_block);\n\n    ASSERT_TRUE(writer->AddXorBlocks(source_block, xor_buffer.data(), xor_buffer.size(),\n                                     (size_ / BLOCK_SZ), xor_offset));\n    // Flush operations\n    ASSERT_TRUE(writer->Finalize());\n\n    // Construct the buffer required for validation\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n\n    // Read the entire base device\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),\n              true);\n\n    // REPLACE ops which are compressed\n    std::memcpy(orig_buffer_.get(), data.data(), data.size());\n    size_t offset = data.size();\n\n    // ZERO ops\n    std::string zero_buffer(total_zero_blocks * BLOCK_SZ, 0);\n    std::memcpy((char*)orig_buffer_.get() + offset, (void*)zero_buffer.c_str(), zero_buffer.size());\n    offset += zero_buffer.size();\n\n    // REPLACE ops - Random buffers which aren't compressed\n    std::memcpy((char*)orig_buffer_.get() + offset, random_buffer.c_str(), random_buffer.size());\n    offset += random_buffer.size();\n\n    // XOR Ops which default to 4k block size compression irrespective of\n    // compression factor\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, (char*)orig_buffer_.get() + offset,\n                                               xor_buffer.size(), size_ + xor_offset),\n              true);\n    for (size_t i = 0; i < xor_buffer.size(); i++) {\n        orig_buffer_.get()[offset + i] = (uint8_t)(orig_buffer_.get()[offset + i] ^ xor_buffer[i]);\n    }\n}\n\nvoid SnapuserdVariableBlockSizeTest::ReadSnapshotWithVariableBlockSize() {\n    unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY | O_DIRECT));\n    ASSERT_GE(fd, 0);\n\n    void* addr;\n    ssize_t page_size = getpagesize();\n    ASSERT_EQ(posix_memalign(&addr, page_size, size_), 0);\n    std::unique_ptr<void, decltype(&::free)> snapshot_buffer(addr, ::free);\n\n    const TestParam params = GetParam();\n\n    // Issue I/O request with various block sizes\n    size_t num_blocks = size_ / params.block_size;\n    off_t offset = 0;\n    for (size_t i = 0; i < num_blocks; i++) {\n        ASSERT_EQ(ReadFullyAtOffset(fd, (char*)snapshot_buffer.get() + offset, params.block_size,\n                                    offset),\n                  true);\n        offset += params.block_size;\n    }\n    // Validate buffer\n    ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);\n\n    // Reset the buffer\n    std::memset(snapshot_buffer.get(), 0, size_);\n\n    // Read one full chunk in a single shot and re-validate.\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapshot_buffer.get(), size_, 0), true);\n    ASSERT_EQ(memcmp(snapshot_buffer.get(), orig_buffer_.get(), size_), 0);\n\n    // Reset the buffer\n    std::memset(snapshot_buffer.get(), 0, size_);\n\n    // Buffered I/O test\n    fd.reset(open(dmuser_dev_->GetPath().c_str(), O_RDONLY));\n    ASSERT_GE(fd, 0);\n\n    // Try not to cache\n    posix_fadvise(fd.get(), 0, size_, POSIX_FADV_DONTNEED);\n\n    size_t num_blocks_per_op = (size_ / BLOCK_SZ) / 4;\n    offset = num_blocks_per_op * BLOCK_SZ;\n    size_t read_size = 1019;  // bytes\n    offset -= 111;\n\n    // Issue a un-aligned read which crosses the boundary between a REPLACE block and a ZERO\n    // block.\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapshot_buffer.get(), read_size, offset), true);\n\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapshot_buffer.get(), (char*)orig_buffer_.get() + offset, read_size), 0);\n\n    offset = (num_blocks_per_op * 3) * BLOCK_SZ;\n    offset -= (BLOCK_SZ - 119);\n    read_size = 8111;\n\n    // Issue an un-aligned read which crosses the boundary between a REPLACE block of random\n    // un-compressed data and a XOR block\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapshot_buffer.get(), read_size, offset), true);\n\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapshot_buffer.get(), (char*)orig_buffer_.get() + offset, read_size), 0);\n\n    // Reset the buffer\n    std::memset(snapshot_buffer.get(), 0, size_);\n\n    // Read just one byte at an odd offset which is a REPLACE op\n    offset = 19;\n    read_size = 1;\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapshot_buffer.get(), read_size, offset), true);\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapshot_buffer.get(), (char*)orig_buffer_.get() + offset, read_size), 0);\n\n    // Reset the buffer\n    std::memset(snapshot_buffer.get(), 0, size_);\n\n    // Read a block which has no mapping to a COW operation. This read should be\n    // a pass-through to the underlying base device.\n    offset = size_ + 9342;\n    read_size = 30;\n    ASSERT_EQ(ReadFullyAtOffset(fd, snapshot_buffer.get(), read_size, offset), true);\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapshot_buffer.get(), (char*)orig_buffer_.get() + offset, read_size), 0);\n}\n\nvoid SnapuserdVariableBlockSizeTest::SetUp() {\n    if (ShouldSkipSetUp()) {\n        GTEST_SKIP() << \"snapuserd not supported on this device\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SnapuserdTest::SetUp());\n}\n\nvoid SnapuserdVariableBlockSizeTest::TearDown() {\n    SnapuserdTest::TearDown();\n}\n\nTEST_P(SnapuserdVariableBlockSizeTest, Snapshot_Test_Variable_Block_Size) {\n    if (!harness_->HasUserDevice()) {\n        GTEST_SKIP() << \"Skipping snapshot read; not supported\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SetupCowV3ForVariableBlockSize());\n    ASSERT_NO_FATAL_FAILURE(ReadSnapshotWithVariableBlockSize());\n    ASSERT_TRUE(StartMerge());\n    CheckMergeCompletion();\n    ValidateMerge();\n    ASSERT_NO_FATAL_FAILURE(ReadSnapshotWithVariableBlockSize());\n}\n\nclass HandlerTest : public SnapuserdTestBase {\n  protected:\n    void SetUp() override;\n    void TearDown() override;\n\n    void SetUpV2Cow();\n    void InitializeDevice();\n    AssertionResult ReadSectors(sector_t sector, uint64_t size, void* buffer);\n\n    TestBlockServerFactory factory_;\n    std::shared_ptr<TestBlockServerOpener> opener_;\n    std::shared_ptr<SnapshotHandler> handler_;\n    std::unique_ptr<ReadWorker> read_worker_;\n    TestBlockServer* block_server_;\n    std::future<bool> handler_thread_;\n};\n\nvoid HandlerTest::SetUpV2Cow() {\n    ASSERT_NO_FATAL_FAILURE(CreateCowDevice());\n}\n\nvoid HandlerTest::InitializeDevice() {\n    ASSERT_NO_FATAL_FAILURE(SetDeviceControlName());\n\n    opener_ = factory_.CreateTestOpener(system_device_ctrl_name_);\n    ASSERT_NE(opener_, nullptr);\n\n    const TestParam params = GetParam();\n    handler_ = std::make_shared<SnapshotHandler>(\n            system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), base_dev_->GetPath(),\n            opener_, 1, false, false, params.o_direct, params.cow_op_merge_size);\n    ASSERT_TRUE(handler_->InitCowDevice());\n    ASSERT_TRUE(handler_->InitializeWorkers());\n\n    read_worker_ = std::make_unique<ReadWorker>(cow_system_->path, base_dev_->GetPath(),\n                                                system_device_ctrl_name_, base_dev_->GetPath(),\n                                                handler_->GetSharedPtr(), opener_);\n    ASSERT_TRUE(read_worker_->Init());\n    block_server_ = static_cast<TestBlockServer*>(read_worker_->block_server());\n\n    handler_thread_ = std::async(std::launch::async, &SnapshotHandler::Start, handler_.get());\n}\n\nvoid HandlerTest::SetUp() {\n    if (ShouldSkipSetUp()) {\n        GTEST_SKIP() << \"snapuserd not supported on this device\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(SetUpV2Cow());\n    ASSERT_NO_FATAL_FAILURE(InitializeDevice());\n}\n\nvoid HandlerTest::TearDown() {\n    if (ShouldSkipSetUp()) {\n        return;\n    }\n    ASSERT_TRUE(factory_.DeleteQueue(system_device_ctrl_name_));\n    ASSERT_TRUE(handler_thread_.get());\n    SnapuserdTestBase::TearDown();\n}\n\nAssertionResult HandlerTest::ReadSectors(sector_t sector, uint64_t size, void* buffer) {\n    if (!read_worker_->RequestSectors(sector, size)) {\n        return AssertionFailure() << \"request sectors failed\";\n    }\n\n    std::string result = std::move(block_server_->sent_io());\n    if (result.size() != size) {\n        return AssertionFailure() << \"size mismatch in result, got \" << result.size()\n                                  << \", expected \" << size;\n    }\n\n    memcpy(buffer, result.data(), size);\n    return AssertionSuccess();\n}\n\n// This test mirrors ReadSnapshotDeviceAndValidate.\nTEST_P(HandlerTest, Read) {\n    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);\n\n    // COPY\n    loff_t offset = 0;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), size_), 0);\n\n    // REPLACE\n    offset += size_;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + size_, size_), 0);\n\n    // ZERO\n    offset += size_;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 2), size_), 0);\n\n    // REPLACE\n    offset += size_;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 3), size_), 0);\n\n    // XOR\n    offset += size_;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, size_, snapuserd_buffer.get()));\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0);\n}\n\nTEST_P(HandlerTest, ReadUnalignedSector) {\n    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(BLOCK_SZ);\n\n    ASSERT_TRUE(ReadSectors(1, BLOCK_SZ, snapuserd_buffer.get()));\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get() + SECTOR_SIZE, BLOCK_SZ), 0);\n}\n\nTEST_P(HandlerTest, ReadUnalignedSize) {\n    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(SECTOR_SIZE);\n\n    ASSERT_TRUE(ReadSectors(0, SECTOR_SIZE, snapuserd_buffer.get()));\n    ASSERT_EQ(memcmp(snapuserd_buffer.get(), orig_buffer_.get(), SECTOR_SIZE), 0);\n}\n\nclass HandlerTestV3 : public HandlerTest {\n  public:\n    void ReadSnapshotWithVariableBlockSize();\n\n  protected:\n    void SetUp() override;\n    void TearDown() override;\n    void SetUpV3Cow();\n};\n\nvoid HandlerTestV3::SetUp() {\n    if (ShouldSkipSetUp()) {\n        GTEST_SKIP() << \"snapuserd not supported on this device\";\n    }\n    ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp());\n    ASSERT_NO_FATAL_FAILURE(CreateBaseDevice());\n    ASSERT_NO_FATAL_FAILURE(SetUpV3Cow());\n    ASSERT_NO_FATAL_FAILURE(InitializeDevice());\n}\n\nvoid HandlerTestV3::TearDown() {\n    ASSERT_NO_FATAL_FAILURE(HandlerTest::TearDown());\n}\n\nvoid HandlerTestV3::SetUpV3Cow() {\n    auto writer = CreateV3Cow();\n\n    ASSERT_NE(writer, nullptr);\n    size_t total_data_to_write = size_;\n\n    size_t total_blocks_to_write = total_data_to_write / BLOCK_SZ;\n    size_t num_blocks_per_op = total_blocks_to_write / 4;\n    size_t source_block = 0;\n\n    size_t total_replace_blocks = num_blocks_per_op;\n    // Write some data which can be compressed\n    std::string data;\n    data.resize(total_replace_blocks * BLOCK_SZ, '\\0');\n    for (size_t i = 0; i < data.size(); i++) {\n        data[i] = static_cast<char>('A' + i / BLOCK_SZ);\n    }\n    // REPLACE ops\n    ASSERT_TRUE(writer->AddRawBlocks(source_block, data.data(), data.size()));\n\n    total_blocks_to_write -= total_replace_blocks;\n    source_block = source_block + total_replace_blocks;\n\n    // ZERO ops\n    size_t total_zero_blocks = total_blocks_to_write / 3;\n    ASSERT_TRUE(writer->AddZeroBlocks(source_block, total_zero_blocks));\n\n    total_blocks_to_write -= total_zero_blocks;\n    source_block = source_block + total_zero_blocks;\n\n    // Generate some random data wherein few blocks cannot be compressed.\n    // This is to test the I/O path for those blocks which aren't compressed.\n    size_t total_random_data_blocks = total_blocks_to_write;\n    unique_fd rnd_fd(open(\"/dev/random\", O_RDONLY));\n\n    ASSERT_GE(rnd_fd, 0);\n    std::string random_buffer;\n    random_buffer.resize(total_random_data_blocks * BLOCK_SZ, '\\0');\n    ASSERT_EQ(\n            android::base::ReadFullyAtOffset(rnd_fd, random_buffer.data(), random_buffer.size(), 0),\n            true);\n    // REPLACE ops\n    ASSERT_TRUE(writer->AddRawBlocks(source_block, random_buffer.data(), random_buffer.size()));\n    // Flush operations\n    ASSERT_TRUE(writer->Finalize());\n\n    // Construct the buffer required for validation\n    orig_buffer_ = std::make_unique<uint8_t[]>(total_base_size_);\n\n    // Read the entire base device\n    ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), total_base_size_, 0),\n              true);\n\n    // REPLACE ops which are compressed\n    std::memcpy(orig_buffer_.get(), data.data(), data.size());\n    size_t offset = data.size();\n\n    // ZERO ops\n    std::string zero_buffer(total_zero_blocks * BLOCK_SZ, 0);\n    std::memcpy((char*)orig_buffer_.get() + offset, (void*)zero_buffer.c_str(), zero_buffer.size());\n    offset += zero_buffer.size();\n\n    // REPLACE ops - Random buffers which aren't compressed\n    std::memcpy((char*)orig_buffer_.get() + offset, random_buffer.c_str(), random_buffer.size());\n}\n\nTEST_P(HandlerTestV3, Read) {\n    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);\n\n    size_t read_size = SECTOR_SIZE;\n    off_t offset = 0;\n    // Read the first sector\n    ASSERT_TRUE(ReadSectors(1, read_size, snapuserd_buffer.get()));\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapuserd_buffer.get(), orig_buffer_.get(), read_size), 0);\n\n    // Read the second block at offset 7680 (Sector 15). This will map to the\n    // first COW operation for variable block size\n    offset += (((BLOCK_SZ * 2) - SECTOR_SIZE));\n    read_size = BLOCK_SZ;  // Span across two REPLACE ops\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, read_size, snapuserd_buffer.get()));\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + offset, read_size),\n              0);\n\n    // Fill some other data since we are going to read zero blocks\n    std::memset(snapuserd_buffer.get(), 'Z', size_);\n\n    size_t num_blocks_per_op = (size_ / BLOCK_SZ) / 4;\n    offset = num_blocks_per_op * BLOCK_SZ;\n    // Issue read spanning between a REPLACE op and ZERO ops. The starting point\n    // is the last REPLACE op at sector 5118\n    offset -= (SECTOR_SIZE * 2);\n    // This will make sure it falls back to aligned reads after reading the\n    // first unaligned block\n    read_size = BLOCK_SZ * 6;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, read_size, snapuserd_buffer.get()));\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + offset, read_size),\n              0);\n\n    // Issue I/O request at the last block. The first chunk of (SECTOR_SIZE * 2)\n    // will be from REPLACE op which has random buffers\n    offset = (size_ - (SECTOR_SIZE * 2));\n    // Request will span beyond the COW mapping, thereby fetching data from base\n    // device.\n    read_size = BLOCK_SZ * 8;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, read_size, snapuserd_buffer.get()));\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + offset, read_size),\n              0);\n\n    // Issue I/O request which are not mapped to any COW operations\n    offset = (size_ + (SECTOR_SIZE * 3));\n    read_size = BLOCK_SZ * 3;\n    ASSERT_TRUE(ReadSectors(offset / SECTOR_SIZE, read_size, snapuserd_buffer.get()));\n    // Validate the data\n    ASSERT_EQ(std::memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + offset, read_size),\n              0);\n}\n\nstd::vector<bool> GetIoUringConfigs() {\n#if __ANDROID__\n    if (!android::base::GetBoolProperty(\"ro.virtual_ab.io_uring.enabled\", false)) {\n        return {false};\n    }\n#endif\n    if (!KernelSupportsIoUring()) {\n        return {false};\n    }\n    return {false, true};\n}\n\nstd::vector<TestParam> GetTestConfigs() {\n    std::vector<TestParam> testParams;\n    std::vector<bool> uring_configs = GetIoUringConfigs();\n\n    for (bool config : uring_configs) {\n        TestParam param;\n        param.io_uring = config;\n        param.o_direct = false;\n        testParams.push_back(std::move(param));\n    }\n\n    for (bool config : uring_configs) {\n        TestParam param;\n        param.io_uring = config;\n        param.o_direct = true;\n        testParams.push_back(std::move(param));\n    }\n    return testParams;\n}\n\nstd::vector<TestParam> GetVariableBlockTestConfigs() {\n    std::vector<TestParam> testParams;\n\n    std::vector<int> block_sizes = {4096, 8192, 16384, 32768, 65536, 131072};\n    std::vector<std::string> compression_algo = {\"none\", \"lz4\", \"zstd\", \"gz\"};\n    std::vector<int> threads = {1, 2};\n    std::vector<bool> uring_configs = GetIoUringConfigs();\n\n    // This should test 96 combination and validates the I/O path\n    for (auto block : block_sizes) {\n        for (auto compression : compression_algo) {\n            for (auto thread : threads) {\n                for (auto io_uring : uring_configs) {\n                    TestParam param;\n                    param.block_size = block;\n                    param.compression = compression;\n                    param.num_threads = thread;\n                    param.io_uring = io_uring;\n                    param.o_direct = false;\n                    param.cow_op_merge_size = 0;\n                    testParams.push_back(std::move(param));\n                }\n            }\n        }\n    }\n\n    return testParams;\n}\n\nINSTANTIATE_TEST_SUITE_P(Io, SnapuserdVariableBlockSizeTest,\n                         ::testing::ValuesIn(GetVariableBlockTestConfigs()));\nINSTANTIATE_TEST_SUITE_P(Io, HandlerTestV3, ::testing::ValuesIn(GetVariableBlockTestConfigs()));\nINSTANTIATE_TEST_SUITE_P(Io, SnapuserdTest, ::testing::ValuesIn(GetTestConfigs()));\nINSTANTIATE_TEST_SUITE_P(Io, HandlerTest, ::testing::ValuesIn(GetTestConfigs()));\n\n}  // namespace snapshot\n}  // namespace android\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n\n    gflags::ParseCommandLineFlags(&argc, &argv, false);\n\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"snapuserd_core.h\"\n\n/*\n * Readahead is used to optimize the merge of COPY and XOR Ops.\n *\n * We create a scratch space of 2MB to store the read-ahead data in the COW\n * device.\n *\n *      +-----------------------+\n *      |     Header (fixed)    |\n *      +-----------------------+\n *      |    Scratch space      |  <-- 2MB\n *      +-----------------------+\n *\n *      Scratch space is as follows:\n *\n *      +-----------------------+\n *      |       Metadata        | <- 4k page\n *      +-----------------------+\n *      |       Metadata        | <- 4k page\n *      +-----------------------+\n *      |                       |\n *      |    Read-ahead data    |\n *      |                       |\n *      +-----------------------+\n *\n *\n * * ===================================================================\n *\n * Example:\n *\n * We have 6 copy operations to be executed in OTA. Update-engine\n * will write to COW file as follows:\n *\n * Op-1: 20 -> 23\n * Op-2: 19 -> 22\n * Op-3: 18 -> 21\n * Op-4: 17 -> 20\n * Op-5: 16 -> 19\n * Op-6: 15 -> 18\n *\n * Read-ahead thread will read all the 6 source blocks and store the data in the\n * scratch space. Metadata will contain the destination block numbers. Thus,\n * scratch space will look something like this:\n *\n * +--------------+\n * | Block   23   |\n * | offset - 1   |\n * +--------------+\n * | Block   22   |\n * | offset - 2   |\n * +--------------+\n * | Block   21   |\n * | offset - 3   |\n * +--------------+\n *    ...\n *    ...\n * +--------------+\n * | Data-Block 20| <-- offset - 1\n * +--------------+\n * | Data-Block 19| <-- offset - 2\n * +--------------+\n * | Data-Block 18| <-- offset - 3\n * +--------------+\n *     ...\n *     ...\n *\n * ====================================================================\n *\n *\n *  Read-ahead thread will process the COW Ops in fixed set. Consider\n *  the following example:\n *\n *  +--------------------------+\n *  |op-1|op-2|op-3|....|op-510|\n *  +--------------------------+\n *\n *  <------ One RA Block ------>\n *\n *  RA thread will read 510 ordered COW ops at a time and will store\n *  the data in the scratch space.\n *\n *  RA thread and Merge thread will go lock-step wherein RA thread\n *  will make sure that 510 COW operation data are read upfront\n *  and is in memory. Thus, when merge thread will pick up the data\n *  directly from memory and write it back to base device.\n *\n *\n *  +--------------------------+------------------------------------+\n *  |op-1|op-2|op-3|....|op-510|op-511|op-512|op-513........|op-1020|\n *  +--------------------------+------------------------------------+\n *\n *  <------Merge 510 Blocks----><-Prepare 510 blocks for merge by RA->\n *           ^                                  ^\n *           |                                  |\n *      Merge thread                        RA thread\n *\n * Both Merge and RA thread will strive to work in parallel.\n *\n * ===========================================================================\n *\n * State transitions and communication between RA thread and Merge thread:\n *\n *  Merge Thread                                      RA Thread\n *  ----------------------------------------------------------------------------\n *\n *          |                                         |\n *    WAIT for RA Block N                     READ one RA Block (N)\n *        for merge                                   |\n *          |                                         |\n *          |                                         |\n *          <--------------MERGE BEGIN--------READ Block N done(copy to scratch)\n *          |                                         |\n *          |                                         |\n *    Merge Begin Block N                     READ one RA BLock (N+1)\n *          |                                         |\n *          |                                         |\n *          |                                  READ done. Wait for merge complete\n *          |                                         |\n *          |                                        WAIT\n *          |                                         |\n *    Merge done Block N                              |\n *          ----------------MERGE READY-------------->|\n *    WAIT for RA Block N+1                     Copy RA Block (N+1)\n *        for merge                              to scratch space\n *          |                                         |\n *          <---------------MERGE BEGIN---------BLOCK N+1 Done\n *          |                                         |\n *          |                                         |\n *    Merge Begin Block N+1                   READ one RA BLock (N+2)\n *          |                                         |\n *          |                                         |\n *          |                                  READ done. Wait for merge complete\n *          |                                         |\n *          |                                        WAIT\n *          |                                         |\n *    Merge done Block N+1                            |\n *          ----------------MERGE READY-------------->|\n *    WAIT for RA Block N+2                     Copy RA Block (N+2)\n *        for merge                              to scratch space\n *          |                                         |\n *          <---------------MERGE BEGIN---------BLOCK N+2 Done\n */\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android;\nusing namespace android::dm;\nusing android::base::unique_fd;\n\nvoid SnapshotHandler::MonitorMerge() {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        merge_monitored_ = true;\n    }\n}\n\n// This is invoked once primarily by update-engine to initiate\n// the merge\nvoid SnapshotHandler::InitiateMerge() {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        merge_initiated_ = true;\n\n        // If there are only REPLACE ops to be merged, then we need\n        // to explicitly set the state to MERGE_BEGIN as there\n        // is no read-ahead thread\n        if (!ra_thread_) {\n            io_state_ = MERGE_IO_TRANSITION::MERGE_BEGIN;\n        }\n    }\n    cv.notify_all();\n}\n\nstatic inline bool IsMergeBeginError(MERGE_IO_TRANSITION io_state) {\n    return io_state == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE ||\n           io_state == MERGE_IO_TRANSITION::IO_TERMINATED;\n}\n\n// Invoked by Merge thread - Waits on RA thread to resume merging. Will\n// be waken up RA thread.\nbool SnapshotHandler::WaitForMergeBegin() {\n    std::unique_lock<std::mutex> lock(lock_);\n\n    cv.wait(lock, [this]() -> bool { return MergeInitiated() || IsMergeBeginError(io_state_); });\n\n    if (IsMergeBeginError(io_state_)) {\n        SNAP_LOG(VERBOSE) << \"WaitForMergeBegin failed with state: \" << io_state_;\n        return false;\n    }\n\n    cv.wait(lock, [this]() -> bool {\n        return io_state_ == MERGE_IO_TRANSITION::MERGE_BEGIN || IsMergeBeginError(io_state_);\n    });\n\n    if (IsMergeBeginError(io_state_)) {\n        SNAP_LOG(ERROR) << \"WaitForMergeBegin failed with state: \" << io_state_;\n        return false;\n    }\n    return true;\n}\n\n// Invoked by RA thread - Flushes the RA block to scratch space if necessary\n// and then notifies the merge thread to resume merging\nbool SnapshotHandler::ReadAheadIOCompleted(bool sync) {\n    if (sync) {\n        // Flush the entire buffer region\n        int ret = msync(mapped_addr_, total_mapped_addr_length_, MS_SYNC);\n        if (ret < 0) {\n            PLOG(ERROR) << \"msync failed after ReadAheadIOCompleted: \" << ret;\n            return false;\n        }\n\n        // Metadata and data are synced. Now, update the state.\n        // We need to update the state after flushing data; if there is a crash\n        // when read-ahead IO is in progress, the state of data in the COW file\n        // is unknown. kCowReadAheadDone acts as a checkpoint wherein the data\n        // in the scratch space is good and during next reboot, read-ahead thread\n        // can safely re-construct the data.\n        struct BufferState* ra_state = GetBufferState();\n        ra_state->read_ahead_state = kCowReadAheadDone;\n\n        ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);\n        if (ret < 0) {\n            PLOG(ERROR) << \"msync failed to flush Readahead completion state...\";\n            return false;\n        }\n    }\n\n    // Notify the merge thread to resume merging\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        if (io_state_ != MERGE_IO_TRANSITION::IO_TERMINATED &&\n            io_state_ != MERGE_IO_TRANSITION::MERGE_FAILED) {\n            io_state_ = MERGE_IO_TRANSITION::MERGE_BEGIN;\n        }\n    }\n\n    cv.notify_all();\n    return true;\n}\n\nvoid SnapshotHandler::PauseMergeIfRequired() {\n    {\n        std::unique_lock<std::mutex> lock(pause_merge_lock_);\n        while (pause_merge_) {\n            SNAP_LOG(INFO) << \"Merge thread paused\";\n            pause_merge_cv_.wait(lock);\n            if (!pause_merge_) {\n                SNAP_LOG(INFO) << \"Merge thread resumed\";\n            }\n        }\n    }\n}\n\n// Invoked by RA thread - Waits for merge thread to finish merging\n// RA Block N - RA thread would be ready will with Block N+1 but\n// will wait to merge thread to finish Block N. Once Block N\n// is merged, RA thread will be woken up by Merge thread and will\n// flush the data of Block N+1 to scratch space\nbool SnapshotHandler::WaitForMergeReady() {\n    {\n        std::unique_lock<std::mutex> lock(lock_);\n        while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_READY ||\n                 io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||\n                 io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||\n                 io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {\n            cv.wait(lock);\n        }\n\n        // Check if merge failed\n        if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||\n            io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||\n            io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) {\n            if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED) {\n                SNAP_LOG(ERROR) << \"Wait for merge ready failed: \" << io_state_;\n            }\n            return false;\n        }\n    }\n\n    // This is a safe place to check if the RA thread should be\n    // paused. Since the scratch space isn't flushed yet, it is safe\n    // to wait here until resume is invoked.\n    PauseMergeIfRequired();\n    return true;\n}\n\n// Invoked by Merge thread - Notify RA thread about Merge completion\n// for Block N and wake up\nvoid SnapshotHandler::NotifyRAForMergeReady() {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        if (io_state_ != MERGE_IO_TRANSITION::IO_TERMINATED &&\n            io_state_ != MERGE_IO_TRANSITION::READ_AHEAD_FAILURE) {\n            io_state_ = MERGE_IO_TRANSITION::MERGE_READY;\n        }\n    }\n\n    cv.notify_all();\n\n    // This is a safe place to check if the merge thread should be\n    // paused. The data from the scratch space is merged to disk and is safe\n    // to wait.\n    PauseMergeIfRequired();\n}\n\n// The following transitions are mostly in the failure paths\nvoid SnapshotHandler::MergeFailed() {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        io_state_ = MERGE_IO_TRANSITION::MERGE_FAILED;\n    }\n\n    cv.notify_all();\n}\n\nvoid SnapshotHandler::MergeCompleted() {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        io_state_ = MERGE_IO_TRANSITION::MERGE_COMPLETE;\n    }\n\n    cv.notify_all();\n}\n\n// This is invoked by worker threads.\n//\n// Worker threads are terminated either by two scenarios:\n//\n// 1: If dm-user device is destroyed\n// 2: We had an I/O failure when reading root partitions\n//\n// In case (1), this would be a graceful shutdown. In this case, merge\n// thread and RA thread should have _already_ terminated by this point. We will be\n// destroying the dm-user device only _after_ merge is completed.\n//\n// In case (2), if merge thread had started, then it will be\n// continuing to merge; however, since we had an I/O failure and the\n// I/O on root partitions are no longer served, we will terminate the\n// merge.\n//\n// This functions is about handling case (2)\nvoid SnapshotHandler::NotifyIOTerminated() {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        io_state_ = MERGE_IO_TRANSITION::IO_TERMINATED;\n    }\n\n    cv.notify_all();\n}\n\nbool SnapshotHandler::IsIOTerminated() {\n    std::lock_guard<std::mutex> lock(lock_);\n    return (io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED);\n}\n\n// Invoked by RA thread\nvoid SnapshotHandler::ReadAheadIOFailed() {\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n        io_state_ = MERGE_IO_TRANSITION::READ_AHEAD_FAILURE;\n    }\n\n    cv.notify_all();\n}\n\nvoid SnapshotHandler::WaitForMergeComplete() {\n    std::unique_lock<std::mutex> lock(lock_);\n    while (!(io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE ||\n             io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED ||\n             io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED)) {\n        cv.wait(lock);\n    }\n}\n\nvoid SnapshotHandler::RaThreadStarted() {\n    std::unique_lock<std::mutex> lock(lock_);\n    ra_thread_started_ = true;\n}\n\nvoid SnapshotHandler::WaitForRaThreadToStart() {\n    auto now = std::chrono::system_clock::now();\n    auto deadline = now + 3s;\n    {\n        std::unique_lock<std::mutex> lock(lock_);\n        while (!ra_thread_started_) {\n            auto status = cv.wait_until(lock, deadline);\n            if (status == std::cv_status::timeout) {\n                SNAP_LOG(ERROR) << \"Read-ahead thread did not start\";\n                return;\n            }\n        }\n    }\n}\n\nvoid SnapshotHandler::MarkMergeComplete() {\n    std::lock_guard<std::mutex> lock(lock_);\n    merge_complete_ = true;\n}\n\nvoid SnapshotHandler::PauseMergeThreads() {\n    {\n        std::lock_guard<std::mutex> lock(pause_merge_lock_);\n        pause_merge_ = true;\n    }\n}\n\nvoid SnapshotHandler::ResumeMergeThreads() {\n    {\n        std::lock_guard<std::mutex> lock(pause_merge_lock_);\n        pause_merge_ = false;\n    }\n}\n\nstd::string SnapshotHandler::GetMergeStatus() {\n    bool merge_not_initiated = false;\n    bool merge_monitored = false;\n    bool merge_failed = false;\n    bool merge_complete = false;\n\n    {\n        std::lock_guard<std::mutex> lock(lock_);\n\n        if (MergeMonitored()) {\n            merge_monitored = true;\n        }\n\n        if (!MergeInitiated()) {\n            merge_not_initiated = true;\n        }\n\n        if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED) {\n            merge_failed = true;\n        }\n\n        merge_complete = merge_complete_;\n    }\n\n    if (merge_not_initiated) {\n        // Merge was not initiated yet; however, we have merge completion\n        // recorded in the COW Header. This can happen if the device was\n        // rebooted during merge. During next reboot, libsnapshot will\n        // query the status and if the merge is completed, then snapshot-status\n        // file will be deleted\n        if (merge_complete) {\n            return \"snapshot-merge-complete\";\n        }\n\n        // Merge monitor thread is tracking the merge but the merge thread\n        // is not started yet.\n        if (merge_monitored) {\n            return \"snapshot-merge\";\n        }\n\n        // Return the state as \"snapshot\". If the device was rebooted during\n        // merge, we will return the status as \"snapshot\". This is ok, as\n        // libsnapshot will explicitly resume the merge. This is slightly\n        // different from kernel snapshot wherein once the snapshot was switched\n        // to merge target, during next boot, we immediately switch to merge\n        // target. We don't do that here because, during first stage init, we\n        // don't want to initiate the merge. The problem is that we have daemon\n        // transition between first and second stage init. If the merge was\n        // started, then we will have to quiesce the merge before switching\n        // the dm tables. Instead, we just wait until second stage daemon is up\n        // before resuming the merge.\n        return \"snapshot\";\n    }\n\n    if (merge_failed) {\n        return \"snapshot-merge-failed\";\n    }\n\n    if (merge_complete) {\n        return \"snapshot-merge-complete\";\n    }\n\n    // Merge is in-progress\n    return \"snapshot-merge\";\n}\n\n//========== End of Read-ahead state transition functions ====================\n\n/*\n * Root partitions are mounted off dm-user and the I/O's are served\n * by snapuserd worker threads.\n *\n * When there is an I/O request to be served by worker threads, we check\n * if the corresponding sector is \"changed\" due to OTA by doing a lookup.\n * If the lookup succeeds then the sector has been changed and that can\n * either fall into 4 COW operations viz: COPY, XOR, REPLACE and ZERO.\n *\n * For the case of REPLACE and ZERO ops, there is not much of a concern\n * as there is no dependency between blocks. Hence all the I/O request\n * mapped to these two COW operations will be served by reading the COW device.\n *\n * However, COPY and XOR ops are tricky. Since the merge operations are\n * in-progress, we cannot just go and read from the source device. We need\n * to be in sync with the state of the merge thread before serving the I/O.\n *\n * Given that we know merge thread processes a set of COW ops called as RA\n * Blocks - These set of COW ops are fixed size wherein each Block comprises\n * of 510 COW ops.\n *\n *  +--------------------------+\n *  |op-1|op-2|op-3|....|op-510|\n *  +--------------------------+\n *\n *  <------ Merge Group Block N ------>\n *\n * Thus, a Merge Group Block N, will fall into one of these states and will\n * transition the states in the following order:\n *\n * 1: GROUP_MERGE_PENDING\n * 2: GROUP_MERGE_RA_READY\n * 2: GROUP_MERGE_IN_PROGRESS\n * 3: GROUP_MERGE_COMPLETED\n * 4: GROUP_MERGE_FAILED\n *\n * Let's say that we have the I/O request from dm-user whose sector gets mapped\n * to a COPY operation with op-10 in the above \"Merge Group Block N\".\n *\n * 1: If the Group is in \"GROUP_MERGE_PENDING\" state:\n *\n *    Just read the data from source block based on COW op->source field. Note,\n *    that we will take a ref count on \"Block N\". This ref count will prevent\n *    merge thread to begin merging if there are any pending I/Os. Once the I/O\n *    is completed, ref count on \"Group N\" is decremented. Merge thread will\n *    resume merging \"Group N\" if there are no pending I/Os.\n *\n * 2: If the Group is in \"GROUP_MERGE_IN_PROGRESS\" or \"GROUP_MERGE_RA_READY\" state:\n *\n *    When the merge thread is ready to process a \"Group\", it will first move\n *    the state to GROUP_MERGE_PENDING -> GROUP_MERGE_RA_READY. From this point\n *    onwards, I/O will be served from Read-ahead buffer. However, merge thread\n *    cannot start merging this \"Group\" immediately. If there were any in-flight\n *    I/O requests, merge thread should wait and allow those I/O's to drain.\n *    Once all the in-flight I/O's are completed, merge thread will move the\n *    state from \"GROUP_MERGE_RA_READY\" -> \"GROUP_MERGE_IN_PROGRESS\". I/O will\n *    be continued to serve from Read-ahead buffer during the entire duration\n *    of the merge.\n *\n *    See SetMergeInProgress().\n *\n * 3: If the Group is in \"GROUP_MERGE_COMPLETED\" state:\n *\n *    This is straightforward. We just read the data directly from \"Base\"\n *    device. We should not be reading the COW op->source field.\n *\n * 4: If the Block is in \"GROUP_MERGE_FAILED\" state:\n *\n *    Terminate the I/O with an I/O error as we don't know which \"op\" in the\n *    \"Group\" failed.\n *\n *    Transition ensures that the I/O from root partitions are never made to\n *    wait and are processed immediately. Thus the state transition for any\n *    \"Group\" is:\n *\n *    GROUP_MERGE_PENDING\n *          |\n *          |\n *          v\n *    GROUP_MERGE_RA_READY\n *          |\n *          |\n *          v\n *    GROUP_MERGE_IN_PROGRESS\n *          |\n *          |----------------------------(on failure)\n *          |                           |\n *          v                           v\n *    GROUP_MERGE_COMPLETED           GROUP_MERGE_FAILED\n *\n */\n\n// Invoked by Merge thread\nvoid SnapshotHandler::SetMergeCompleted(size_t ra_index) {\n    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();\n    {\n        std::lock_guard<std::mutex> lock(blk_state->m_lock);\n\n        CHECK(blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS);\n        CHECK(blk_state->num_ios_in_progress == 0);\n\n        // Merge is complete - All I/O henceforth should be read directly\n        // from base device\n        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED;\n    }\n}\n\n// Invoked by Merge thread. This is called just before the beginning\n// of merging a given Block of 510 ops. If there are any in-flight I/O's\n// from dm-user then wait for them to complete.\nvoid SnapshotHandler::SetMergeInProgress(size_t ra_index) {\n    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();\n    {\n        std::unique_lock<std::mutex> lock(blk_state->m_lock);\n\n        // We may have fallback from Async-merge to synchronous merging\n        // on the existing block. There is no need to reset as the\n        // merge is already in progress.\n        if (blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS) {\n            return;\n        }\n\n        CHECK(blk_state->merge_state_ == MERGE_GROUP_STATE::GROUP_MERGE_PENDING);\n\n        // First set the state to RA_READY so that in-flight I/O will drain\n        // and any new I/O will start reading from RA buffer\n        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_RA_READY;\n\n        // Wait if there are any in-flight I/O's - we cannot merge at this point\n        while (!(blk_state->num_ios_in_progress == 0)) {\n            blk_state->m_cv.wait(lock);\n        }\n\n        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS;\n    }\n}\n\n// Invoked by Merge thread on failure\nvoid SnapshotHandler::SetMergeFailed(size_t ra_index) {\n    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();\n    {\n        std::unique_lock<std::mutex> lock(blk_state->m_lock);\n\n        blk_state->merge_state_ = MERGE_GROUP_STATE::GROUP_MERGE_FAILED;\n    }\n}\n\n// Invoked by worker threads when I/O is complete on a \"MERGE_PENDING\"\n// Block. If there are no more in-flight I/Os, wake up merge thread\n// to resume merging.\nvoid SnapshotHandler::NotifyIOCompletion(uint64_t new_block) {\n    auto it = block_to_ra_index_.find(new_block);\n    CHECK(it != block_to_ra_index_.end()) << \" invalid block: \" << new_block;\n\n    bool pending_ios = true;\n\n    int ra_index = it->second;\n    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();\n    {\n        std::unique_lock<std::mutex> lock(blk_state->m_lock);\n\n        blk_state->num_ios_in_progress -= 1;\n        if (blk_state->num_ios_in_progress == 0) {\n            pending_ios = false;\n        }\n    }\n\n    // Give a chance to merge-thread to resume merge\n    // as there are no pending I/O.\n    if (!pending_ios) {\n        blk_state->m_cv.notify_all();\n    }\n}\n\nbool SnapshotHandler::GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block,\n                                  void* buffer) {\n    if (!lock->owns_lock()) {\n        SNAP_LOG(ERROR) << \"GetRABuffer - Lock not held\";\n        return false;\n    }\n    std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);\n\n    if (it == read_ahead_buffer_map_.end()) {\n        return false;\n    }\n\n    memcpy(buffer, it->second, BLOCK_SZ);\n    return true;\n}\n\n// Invoked by worker threads in the I/O path. This is called when a sector\n// is mapped to a COPY/XOR COW op.\nMERGE_GROUP_STATE SnapshotHandler::ProcessMergingBlock(uint64_t new_block, void* buffer) {\n    auto it = block_to_ra_index_.find(new_block);\n    if (it == block_to_ra_index_.end()) {\n        return MERGE_GROUP_STATE::GROUP_INVALID;\n    }\n\n    int ra_index = it->second;\n    MergeGroupState* blk_state = merge_blk_state_[ra_index].get();\n    {\n        std::unique_lock<std::mutex> lock(blk_state->m_lock);\n\n        MERGE_GROUP_STATE state = blk_state->merge_state_;\n        switch (state) {\n            case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {\n                // If this is a merge-resume path, check if the data is\n                // available from scratch space. Data from scratch space takes\n                // higher precedence than from source device for overlapping\n                // blocks.\n                if (resume_merge_ && GetRABuffer(&lock, new_block, buffer)) {\n                    return (MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS);\n                }\n                blk_state->num_ios_in_progress += 1;  // ref count\n                [[fallthrough]];\n            }\n            case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {\n                [[fallthrough]];\n            }\n            case MERGE_GROUP_STATE::GROUP_MERGE_FAILED: {\n                return state;\n            }\n            // Fetch the data from RA buffer.\n            case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {\n                [[fallthrough]];\n            }\n            case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {\n                if (!GetRABuffer(&lock, new_block, buffer)) {\n                    return MERGE_GROUP_STATE::GROUP_INVALID;\n                }\n                return state;\n            }\n            default: {\n                return MERGE_GROUP_STATE::GROUP_INVALID;\n            }\n        }\n    }\n}\n\nstd::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value) {\n    switch (value) {\n        case MERGE_IO_TRANSITION::INVALID:\n            return os << \"INVALID\";\n        case MERGE_IO_TRANSITION::MERGE_READY:\n            return os << \"MERGE_READY\";\n        case MERGE_IO_TRANSITION::MERGE_BEGIN:\n            return os << \"MERGE_BEGIN\";\n        case MERGE_IO_TRANSITION::MERGE_FAILED:\n            return os << \"MERGE_FAILED\";\n        case MERGE_IO_TRANSITION::MERGE_COMPLETE:\n            return os << \"MERGE_COMPLETE\";\n        case MERGE_IO_TRANSITION::IO_TERMINATED:\n            return os << \"IO_TERMINATED\";\n        case MERGE_IO_TRANSITION::READ_AHEAD_FAILURE:\n            return os << \"READ_AHEAD_FAILURE\";\n        default:\n            return os << \"unknown\";\n    }\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"snapuserd_verify.h\"\n\n#include <android-base/chrono_utils.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n\n#include \"android-base/properties.h\"\n#include \"snapuserd_core.h\"\n#include \"utility.h\"\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android;\nusing namespace android::dm;\nusing android::base::unique_fd;\n\nUpdateVerify::UpdateVerify(const std::string& misc_name)\n    : misc_name_(misc_name), state_(UpdateVerifyState::VERIFY_UNKNOWN) {}\n\nbool UpdateVerify::CheckPartitionVerification() {\n    auto now = std::chrono::system_clock::now();\n    auto deadline = now + 10s;\n    {\n        std::unique_lock<std::mutex> cv_lock(m_lock_);\n        while (state_ == UpdateVerifyState::VERIFY_UNKNOWN) {\n            auto status = m_cv_.wait_until(cv_lock, deadline);\n            if (status == std::cv_status::timeout) {\n                return false;\n            }\n        }\n    }\n\n    return (state_ == UpdateVerifyState::VERIFY_SUCCESS);\n}\n\nvoid UpdateVerify::UpdatePartitionVerificationState(UpdateVerifyState state) {\n    {\n        std::lock_guard<std::mutex> lock(m_lock_);\n        state_ = state;\n    }\n    m_cv_.notify_all();\n}\n\nvoid UpdateVerify::VerifyUpdatePartition() {\n    bool succeeded = false;\n\n    auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {\n        if (!succeeded) {\n            UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);\n        }\n    });\n\n    auto& dm = DeviceMapper::Instance();\n    auto dm_block_devices = dm.FindDmPartitions();\n    if (dm_block_devices.empty()) {\n        SNAP_LOG(ERROR) << \"No dm-enabled block device is found.\";\n        return;\n    }\n\n    const auto parts = android::base::Split(misc_name_, \"-\");\n    std::string partition_name = parts[0];\n\n    constexpr auto&& suffix_b = \"_b\";\n    constexpr auto&& suffix_a = \"_a\";\n\n    partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1);\n    partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1);\n\n    if (dm_block_devices.find(partition_name) == dm_block_devices.end()) {\n        SNAP_LOG(ERROR) << \"Failed to find dm block device for \" << partition_name;\n        return;\n    }\n\n    if (!VerifyPartition(partition_name, dm_block_devices.at(partition_name))) {\n        SNAP_LOG(ERROR) << \"Partition: \" << partition_name\n                        << \" Block-device: \" << dm_block_devices.at(partition_name)\n                        << \" verification failed\";\n    }\n    succeeded = true;\n}\n\nbool UpdateVerify::VerifyBlocks(const std::string& partition_name,\n                                const std::string& dm_block_device, off_t offset, int skip_blocks,\n                                uint64_t dev_sz) {\n    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));\n    if (fd < 0) {\n        SNAP_LOG(ERROR) << \"open failed: \" << dm_block_device;\n        return false;\n    }\n\n    int queue_depth = std::max(queue_depth_, 1);\n    int verify_block_size = verify_block_size_;\n\n    // Smaller partitions don't need a bigger queue-depth.\n    // This is required for low-memory devices.\n    if (dev_sz < threshold_size_) {\n        queue_depth = std::max(queue_depth / 2, 1);\n        verify_block_size >>= 2;\n    }\n\n    if (!IsBlockAligned(verify_block_size)) {\n        verify_block_size = EXT4_ALIGN(verify_block_size, BLOCK_SZ);\n    }\n\n    std::unique_ptr<io_uring_cpp::IoUringInterface> ring =\n            io_uring_cpp::IoUringInterface::CreateLinuxIoUring(queue_depth, 0);\n    if (ring.get() == nullptr) {\n        PLOG(ERROR) << \"Verify: io_uring_queue_init failed for queue_depth: \" << queue_depth;\n        return false;\n    }\n\n    std::unique_ptr<struct iovec[]> vecs = std::make_unique<struct iovec[]>(queue_depth);\n    std::vector<std::unique_ptr<void, decltype(&::free)>> buffers;\n    for (int i = 0; i < queue_depth; i++) {\n        void* addr;\n        ssize_t page_size = getpagesize();\n        if (posix_memalign(&addr, page_size, verify_block_size) < 0) {\n            LOG(ERROR) << \"posix_memalign failed\";\n            return false;\n        }\n\n        buffers.emplace_back(addr, ::free);\n        vecs[i].iov_base = addr;\n        vecs[i].iov_len = verify_block_size;\n    }\n\n    auto ret = ring->RegisterBuffers(vecs.get(), queue_depth);\n    if (!ret.IsOk()) {\n        SNAP_LOG(ERROR) << \"io_uring_register_buffers failed: \" << ret.ErrCode();\n        return false;\n    }\n\n    loff_t file_offset = offset;\n    const uint64_t read_sz = verify_block_size;\n    uint64_t total_read = 0;\n    int num_submitted = 0;\n\n    SNAP_LOG(DEBUG) << \"VerifyBlocks: queue_depth: \" << queue_depth\n                    << \" verify_block_size: \" << verify_block_size << \" dev_sz: \" << dev_sz\n                    << \" file_offset: \" << file_offset << \" skip_blocks: \" << skip_blocks;\n\n    while (file_offset < dev_sz) {\n        for (size_t i = 0; i < queue_depth; i++) {\n            uint64_t to_read = std::min((dev_sz - file_offset), read_sz);\n            if (to_read <= 0) break;\n\n            const auto sqe =\n                    ring->PrepReadFixed(fd.get(), vecs[i].iov_base, to_read, file_offset, i);\n            if (!sqe.IsOk()) {\n                SNAP_PLOG(ERROR) << \"PrepReadFixed failed\";\n                return false;\n            }\n            file_offset += (skip_blocks * to_read);\n            total_read += to_read;\n            num_submitted += 1;\n            if (file_offset >= dev_sz) {\n                break;\n            }\n        }\n\n        if (num_submitted == 0) {\n            break;\n        }\n\n        const auto io_submit = ring->SubmitAndWait(num_submitted);\n        if (!io_submit.IsOk()) {\n            SNAP_LOG(ERROR) << \"SubmitAndWait failed: \" << io_submit.ErrMsg()\n                            << \" for: \" << num_submitted << \" entries.\";\n            return false;\n        }\n\n        SNAP_LOG(DEBUG) << \"io_uring_submit: \" << total_read << \"num_submitted: \" << num_submitted\n                        << \"ret: \" << ret;\n\n        const auto cqes = ring->PopCQE(num_submitted);\n        if (cqes.IsErr()) {\n            SNAP_LOG(ERROR) << \"PopCqe failed for: \" << num_submitted\n                            << \" error: \" << cqes.GetError().ErrMsg();\n            return false;\n        }\n        for (const auto& cqe : cqes.GetResult()) {\n            if (cqe.res < 0) {\n                SNAP_LOG(ERROR) << \"I/O failed: cqe->res: \" << cqe.res;\n                return false;\n            }\n            num_submitted -= 1;\n        }\n    }\n\n    SNAP_LOG(DEBUG) << \"Verification success with io_uring: \"\n                    << \" dev_sz: \" << dev_sz << \" partition_name: \" << partition_name\n                    << \" total_read: \" << total_read;\n\n    return true;\n}\n\nbool UpdateVerify::VerifyPartition(const std::string& partition_name,\n                                   const std::string& dm_block_device) {\n    android::base::Timer timer;\n\n    SNAP_LOG(INFO) << \"VerifyPartition: \" << partition_name << \" Block-device: \" << dm_block_device;\n\n    bool succeeded = false;\n    auto scope_guard = android::base::make_scope_guard([this, &succeeded]() -> void {\n        if (!succeeded) {\n            UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_FAILED);\n        }\n    });\n\n    unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_DIRECT)));\n    if (fd < 0) {\n        SNAP_LOG(ERROR) << \"open failed: \" << dm_block_device;\n        return false;\n    }\n\n    uint64_t dev_sz = get_block_device_size(fd.get());\n    if (!dev_sz) {\n        SNAP_PLOG(ERROR) << \"Could not determine block device size: \" << dm_block_device;\n        return false;\n    }\n\n    if (!IsBlockAligned(dev_sz)) {\n        SNAP_LOG(ERROR) << \"dev_sz: \" << dev_sz << \" is not block aligned\";\n        return false;\n    }\n\n    if (!KernelSupportsIoUring()) {\n        SNAP_LOG(INFO) << \"Kernel does not support io_uring. Skipping verification.\\n\";\n        // This will fallback to update_verifier to do the verification.\n        return false;\n    }\n\n    int num_threads = kMinThreadsToVerify;\n    if (dev_sz > threshold_size_) {\n        num_threads = kMaxThreadsToVerify;\n    }\n\n    std::vector<std::future<bool>> threads;\n    off_t start_offset = 0;\n    const int skip_blocks = num_threads;\n\n    while (num_threads) {\n        threads.emplace_back(std::async(std::launch::async, &UpdateVerify::VerifyBlocks, this,\n                                        partition_name, dm_block_device, start_offset, skip_blocks,\n                                        dev_sz));\n        start_offset += verify_block_size_;\n        num_threads -= 1;\n        if (start_offset >= dev_sz) {\n            break;\n        }\n    }\n\n    bool ret = true;\n    for (auto& t : threads) {\n        ret = t.get() && ret;\n    }\n\n    if (ret) {\n        succeeded = true;\n        UpdatePartitionVerificationState(UpdateVerifyState::VERIFY_SUCCESS);\n        SNAP_LOG(INFO) << \"Partition verification success: \" << partition_name\n                       << \" Block-device: \" << dm_block_device << \" Size: \" << dev_sz\n                       << \" Duration : \" << timer.duration().count() << \" ms\";\n        return true;\n    }\n\n    return false;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_verify.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <liburing.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <condition_variable>\n#include <mutex>\n#include <string>\n\n#include <liburing_cpp/IoUring.h>\n#include <snapuserd/snapuserd_kernel.h>\n#include <storage_literals/storage_literals.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing namespace android::storage_literals;\n\nclass UpdateVerify {\n  public:\n    UpdateVerify(const std::string& misc_name);\n    void VerifyUpdatePartition();\n    bool CheckPartitionVerification();\n\n  private:\n    enum class UpdateVerifyState {\n        VERIFY_UNKNOWN,\n        VERIFY_FAILED,\n        VERIFY_SUCCESS,\n    };\n\n    std::string misc_name_;\n    UpdateVerifyState state_;\n    std::mutex m_lock_;\n    std::condition_variable m_cv_;\n\n    int kMinThreadsToVerify = 1;\n    int kMaxThreadsToVerify = 3;\n\n    /*\n     * To optimize partition scanning speed without significantly impacting boot time,\n     * we employ O_DIRECT, bypassing the page-cache. However, O_DIRECT's memory\n     * allocation from CMA can be problematic on devices with restricted CMA space.\n     * To address this, io_uring_register_buffers() pre-registers I/O buffers,\n     * preventing CMA usage. See b/401952955 for more details.\n     *\n     * These numbers were derived by monitoring the memory and CPU pressure\n     * (/proc/pressure/{cpu,memory}; and monitoring the Inactive(file) and\n     * Active(file) pages from /proc/meminfo.\n     */\n    uint64_t verify_block_size_ = 1_MiB;\n    uint64_t threshold_size_ = 2_GiB;\n    int queue_depth_ = 4;\n\n    bool IsBlockAligned(uint64_t read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); }\n    void UpdatePartitionVerificationState(UpdateVerifyState state);\n    bool VerifyPartition(const std::string& partition_name, const std::string& dm_block_device);\n    bool VerifyBlocks(const std::string& partition_name, const std::string& dm_block_device,\n                      off_t offset, int skip_blocks, uint64_t dev_sz);\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"worker.h\"\n\n#include \"snapuserd_core.h\"\n\nnamespace android {\nnamespace snapshot {\n\nWorker::Worker(const std::string& cow_device, const std::string& misc_name,\n               const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {\n    cow_device_ = cow_device;\n    misc_name_ = misc_name;\n    base_path_merge_ = base_path_merge;\n    snapuserd_ = snapuserd;\n}\n\nbool Worker::Init() {\n    if (!InitializeFds()) {\n        return false;\n    }\n\n    if (!InitReader()) {\n        return false;\n    }\n\n    return true;\n}\n\nbool Worker::InitReader() {\n    reader_ = snapuserd_->CloneReaderForWorker();\n\n    if (!reader_->InitForMerge(std::move(cow_fd_))) {\n        return false;\n    }\n    return true;\n}\n\nbool Worker::InitializeFds() {\n    cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));\n    if (cow_fd_ < 0) {\n        SNAP_PLOG(ERROR) << \"Open Failed: \" << cow_device_;\n        return false;\n    }\n\n    // Base device used by merge thread\n    base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));\n    if (base_path_merge_fd_ < 0) {\n        SNAP_PLOG(ERROR) << \"Open Failed: \" << base_path_merge_;\n        return false;\n    }\n\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/user-space-merge/worker.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stddef.h>\n\n#include <memory>\n#include <string>\n\n#include <android-base/unique_fd.h>\n#include <libsnapshot/cow_reader.h>\n#include <snapuserd/snapuserd_buffer.h>\n#include <snapuserd/snapuserd_kernel.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\n\nclass SnapshotHandler;\n\nclass Worker {\n  public:\n    Worker(const std::string& cow_device, const std::string& misc_name,\n           const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd);\n    virtual ~Worker() = default;\n\n    virtual bool Init();\n\n  protected:\n    bool InitializeFds();\n    bool InitReader();\n    virtual void CloseFds() { base_path_merge_fd_ = {}; }\n\n    std::unique_ptr<CowReader> reader_;\n\n    std::string misc_name_;  // Needed for SNAP_LOG.\n\n    unique_fd base_path_merge_fd_;\n\n    std::shared_ptr<SnapshotHandler> snapuserd_;\n\n  private:\n    std::string cow_device_;\n    std::string base_path_merge_;\n    unique_fd cow_fd_;\n};\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/utility.cpp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"utility.h\"\n\n#include <android-base/properties.h>\n#include <sys/resource.h>\n#include <sys/utsname.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <libdm/dm.h>\n#include <processgroup/processgroup.h>\n\n#include <private/android_filesystem_config.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::unique_fd;\nusing android::dm::DeviceMapper;\n\nbool SetThreadPriority([[maybe_unused]] int priority) {\n#ifdef __ANDROID__\n    return setpriority(PRIO_PROCESS, gettid(), priority) != -1;\n#else\n    return true;\n#endif\n}\n\nbool SetProfiles([[maybe_unused]] std::initializer_list<std::string_view> profiles) {\n#ifdef __ANDROID__\n    if (setgid(AID_SYSTEM)) {\n        return false;\n    }\n    return SetTaskProfiles(gettid(), profiles);\n#else\n    return true;\n#endif\n}\n\nbool KernelSupportsIoUring() {\n    struct utsname uts {};\n    unsigned int major, minor;\n\n    uname(&uts);\n    if (sscanf(uts.release, \"%u.%u\", &major, &minor) != 2) {\n        return false;\n    }\n\n    // We will only support kernels from 5.6 onwards as IOSQE_ASYNC flag and\n    // IO_URING_OP_READ/WRITE opcodes were introduced only on 5.6 kernel\n    return major > 5 || (major == 5 && minor >= 6);\n}\n\nbool GetUserspaceSnapshotsEnabledProperty() {\n    return android::base::GetBoolProperty(\"ro.virtual_ab.userspace.snapshots.enabled\", false);\n}\n\nbool KernelSupportsCompressedSnapshots() {\n    auto& dm = DeviceMapper::Instance();\n    return dm.GetTargetByName(\"user\", nullptr);\n}\n\nbool IsVendorFromAndroid12() {\n    const std::string UNKNOWN = \"unknown\";\n    const std::string vendor_release =\n            android::base::GetProperty(\"ro.vendor.build.version.release_or_codename\", UNKNOWN);\n\n    if (vendor_release.find(\"12\") != std::string::npos) {\n        return true;\n    }\n    return false;\n}\n\nbool CanUseUserspaceSnapshots() {\n    if (!GetUserspaceSnapshotsEnabledProperty()) {\n        LOG(INFO) << \"Virtual A/B - Userspace snapshots disabled\";\n        return false;\n    }\n\n    if (!KernelSupportsCompressedSnapshots()) {\n        LOG(ERROR) << \"Userspace snapshots requested, but no kernel support is available.\";\n        return false;\n    }\n    return true;\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/snapuserd/utility.h",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <initializer_list>\n#include <string_view>\n\nnamespace android {\nnamespace snapshot {\n\nbool SetThreadPriority(int priority);\nbool SetProfiles(std::initializer_list<std::string_view> profiles);\nbool KernelSupportsIoUring();\n\nbool GetUserspaceSnapshotsEnabledProperty();\nbool KernelSupportsCompressedSnapshots();\nbool CanUseUserspaceSnapshots();\nbool IsVendorFromAndroid12();\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/test_helpers.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <libsnapshot/test_helpers.h>\n\n#include <sys/statvfs.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parsebool.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n#include <liblp/property_fetcher.h>\n#include <openssl/sha.h>\n#include <payload_consumer/file_descriptor.h>\n\nnamespace android {\nnamespace snapshot {\n\nusing android::base::ReadFully;\nusing android::base::unique_fd;\nusing android::base::WriteFully;\nusing android::fiemap::IImageManager;\nusing testing::AssertionFailure;\nusing testing::AssertionSuccess;\n\nvoid DeleteBackingImage(IImageManager* manager, const std::string& name) {\n    if (manager->IsImageMapped(name)) {\n        ASSERT_TRUE(manager->UnmapImageDevice(name));\n    }\n    if (manager->BackingImageExists(name)) {\n        ASSERT_TRUE(manager->DeleteBackingImage(name));\n    }\n}\n\nandroid::base::unique_fd TestPartitionOpener::Open(const std::string& partition_name,\n                                                   int flags) const {\n    if (partition_name == \"super\") {\n        return PartitionOpener::Open(fake_super_path_, flags);\n    }\n    return PartitionOpener::Open(partition_name, flags);\n}\n\nbool TestPartitionOpener::GetInfo(const std::string& partition_name,\n                                  android::fs_mgr::BlockDeviceInfo* info) const {\n    if (partition_name != \"super\") {\n        return PartitionOpener::GetInfo(partition_name, info);\n    }\n\n    if (PartitionOpener::GetInfo(fake_super_path_, info)) {\n        // SnapshotUpdateTest uses a relatively small super partition, which requires a small\n        // alignment and 0 offset to work. For the purpose of this test, hardcode the alignment\n        // and offset. This test isn't about testing liblp or libdm.\n        info->alignment_offset = 0;\n        info->alignment = std::min<uint32_t>(info->alignment, static_cast<uint32_t>(128_KiB));\n        return true;\n    }\n    return false;\n}\n\nstd::string TestPartitionOpener::GetDeviceString(const std::string& partition_name) const {\n    if (partition_name == \"super\") {\n        return fake_super_path_;\n    }\n    return PartitionOpener::GetDeviceString(partition_name);\n}\n\nstd::string ToHexString(const uint8_t* buf, size_t len) {\n    char lookup[] = \"0123456789abcdef\";\n    std::string out(len * 2 + 1, '\\0');\n    char* outp = out.data();\n    for (; len > 0; len--, buf++) {\n        *outp++ = (char)lookup[*buf >> 4];\n        *outp++ = (char)lookup[*buf & 0xf];\n    }\n    return out;\n}\n\nbool WriteRandomData(const std::string& path, std::optional<size_t> expect_size,\n                     std::string* hash) {\n    unique_fd rand(open(\"/dev/urandom\", O_RDONLY));\n    unique_fd fd(open(path.c_str(), O_WRONLY));\n\n    SHA256_CTX ctx;\n    if (hash) {\n        SHA256_Init(&ctx);\n    }\n\n    char buf[4096];\n    size_t total_written = 0;\n    while (!expect_size || total_written < *expect_size) {\n        ssize_t n = TEMP_FAILURE_RETRY(read(rand.get(), buf, sizeof(buf)));\n        if (n <= 0) return false;\n        if (!WriteFully(fd.get(), buf, n)) {\n            if (errno == ENOSPC) {\n                break;\n            }\n            PLOG(ERROR) << \"Cannot write \" << path;\n            return false;\n        }\n        total_written += n;\n        if (hash) {\n            SHA256_Update(&ctx, buf, n);\n        }\n    }\n\n    if (expect_size && total_written != *expect_size) {\n        PLOG(ERROR) << \"Written \" << total_written << \" bytes, expected \" << *expect_size;\n        return false;\n    }\n\n    if (hash) {\n        uint8_t out[32];\n        SHA256_Final(out, &ctx);\n        *hash = ToHexString(out, sizeof(out));\n    }\n    return true;\n}\n\nstd::string HashSnapshot(ICowWriter::FileDescriptor* reader) {\n    SHA256_CTX ctx;\n    SHA256_Init(&ctx);\n\n    uint64_t remaining = reader->BlockDevSize();\n    char buffer[4096];\n    while (remaining) {\n        size_t to_read =\n                static_cast<size_t>(std::min(remaining, static_cast<uint64_t>(sizeof(buffer))));\n        ssize_t read = reader->Read(&buffer, to_read);\n        if (read <= 0) {\n            if (read < 0) {\n                LOG(ERROR) << \"Failed to read from snapshot writer\";\n                return {};\n            }\n            break;\n        }\n        SHA256_Update(&ctx, buffer, to_read);\n        remaining -= static_cast<size_t>(read);\n    }\n\n    uint8_t out[32];\n    SHA256_Final(out, &ctx);\n    return ToHexString(out, sizeof(out));\n}\n\nstd::optional<std::string> GetHash(const std::string& path) {\n    std::string content;\n    if (!android::base::ReadFileToString(path, &content, true)) {\n        PLOG(ERROR) << \"Cannot access \" << path;\n        return std::nullopt;\n    }\n    SHA256_CTX ctx;\n    SHA256_Init(&ctx);\n    SHA256_Update(&ctx, content.c_str(), content.size());\n    uint8_t out[32];\n    SHA256_Final(out, &ctx);\n    return ToHexString(out, sizeof(out));\n}\n\nAssertionResult FillFakeMetadata(MetadataBuilder* builder, const DeltaArchiveManifest& manifest,\n                                 const std::string& suffix) {\n    for (const auto& group : manifest.dynamic_partition_metadata().groups()) {\n        if (!builder->AddGroup(group.name() + suffix, group.size())) {\n            return AssertionFailure()\n                   << \"Cannot add group \" << group.name() << \" with size \" << group.size();\n        }\n        for (const auto& partition_name : group.partition_names()) {\n            auto p = builder->AddPartition(partition_name + suffix, group.name() + suffix,\n                                           0 /* attr */);\n            if (!p) {\n                return AssertionFailure() << \"Cannot add partition \" << partition_name + suffix\n                                          << \" to group \" << group.name() << suffix;\n            }\n        }\n    }\n    for (const auto& partition : manifest.partitions()) {\n        auto p = builder->FindPartition(partition.partition_name() + suffix);\n        if (!p) {\n            return AssertionFailure() << \"Cannot resize partition \" << partition.partition_name()\n                                      << suffix << \"; it is not found.\";\n        }\n        if (!builder->ResizePartition(p, partition.new_partition_info().size())) {\n            return AssertionFailure()\n                   << \"Cannot resize partition \" << partition.partition_name() << suffix\n                   << \" to size \" << partition.new_partition_info().size();\n        }\n    }\n    return AssertionSuccess();\n}\n\nvoid SetSize(PartitionUpdate* partition_update, uint64_t size) {\n    partition_update->mutable_new_partition_info()->set_size(size);\n}\n\nuint64_t GetSize(PartitionUpdate* partition_update) {\n    return partition_update->mutable_new_partition_info()->size();\n}\n\nbool IsVirtualAbEnabled() {\n    return android::base::GetBoolProperty(\"ro.virtual_ab.enabled\", false);\n}\n\nSnapshotTestPropertyFetcher::SnapshotTestPropertyFetcher(\n        const std::string& slot_suffix, std::unordered_map<std::string, std::string>&& props)\n    : properties_(std::move(props)) {\n    properties_[\"ro.boot.slot_suffix\"] = slot_suffix;\n    properties_[\"ro.boot.dynamic_partitions\"] = \"true\";\n    properties_[\"ro.boot.dynamic_partitions_retrofit\"] = \"false\";\n    properties_[\"ro.virtual_ab.enabled\"] = \"true\";\n}\n\nstd::string SnapshotTestPropertyFetcher::GetProperty(const std::string& key,\n                                                     const std::string& defaultValue) {\n    auto iter = properties_.find(key);\n    if (iter == properties_.end()) {\n        return android::base::GetProperty(key, defaultValue);\n    }\n    return iter->second;\n}\n\nbool SnapshotTestPropertyFetcher::GetBoolProperty(const std::string& key, bool defaultValue) {\n    auto iter = properties_.find(key);\n    if (iter == properties_.end()) {\n        return android::base::GetBoolProperty(key, defaultValue);\n    }\n    switch (android::base::ParseBool(iter->second)) {\n        case android::base::ParseBoolResult::kTrue:\n            return true;\n        case android::base::ParseBoolResult::kFalse:\n            return false;\n        default:\n            return defaultValue;\n    }\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/tools/Android.bp",
    "content": "\ncc_binary {\n    name: \"cow_benchmark\",\n    host_supported: true,\n    defaults: [\n        \"fs_mgr_defaults\",\n        \"libsnapshot_cow_defaults\",\n    ],\n\n    srcs: [\"cow_benchmark.cpp\"],\n\n    static_libs: [\n        \"libsnapshot_cow\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n\n    cflags: [\"-Werror\"],\n}\n\n\ncc_binary {\n    name: \"write_cow\",\n    host_supported: true,\n    defaults: [\n        \"fs_mgr_defaults\",\n        \"libsnapshot_cow_defaults\",\n    ],\n\n    srcs: [\"write_cow.cpp\"],\n\n    static_libs: [\n        \"libsnapshot_cow\",\n        \"libgflags\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n\n    cflags: [\"-Werror\"],\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/tools/cow_benchmark.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#include <memory>\n\n#include <array>\n#include <iostream>\n#include <random>\n\n#include <libsnapshot/cow_compress.h>\n#include <libsnapshot/cow_format.h>\n\nstatic const uint32_t BLOCK_SZ = 4096;\nstatic const uint32_t SEED_NUMBER = 10;\n\nnamespace android {\nnamespace snapshot {\n\nstatic std::string CompressionToString(CowCompression& compression) {\n    std::string output;\n    switch (compression.algorithm) {\n        case kCowCompressBrotli:\n            output.append(\"brotli\");\n            break;\n        case kCowCompressGz:\n            output.append(\"gz\");\n            break;\n        case kCowCompressLz4:\n            output.append(\"lz4\");\n            break;\n        case kCowCompressZstd:\n            output.append(\"zstd\");\n            break;\n        case kCowCompressNone:\n            return \"No Compression\";\n    }\n    output.append(\" \" + std::to_string(compression.compression_level));\n    return output;\n}\n\nvoid OneShotCompressionTest() {\n    std::cout << \"\\n-------One Shot Compressor Perf Analysis-------\\n\";\n\n    std::vector<CowCompression> compression_list = {\n            {kCowCompressLz4, 0},     {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},\n            {kCowCompressBrotli, 11}, {kCowCompressZstd, 3},   {kCowCompressZstd, 6},\n            {kCowCompressZstd, 9},    {kCowCompressZstd, 22},  {kCowCompressGz, 1},\n            {kCowCompressGz, 3},      {kCowCompressGz, 6},     {kCowCompressGz, 9}};\n    std::vector<std::unique_ptr<ICompressor>> compressors;\n    for (auto i : compression_list) {\n        compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));\n    }\n\n    // Allocate a buffer of size 8 blocks.\n    std::array<char, 32768> buffer;\n\n    // Generate a random 4k buffer of characters\n    std::default_random_engine gen(SEED_NUMBER);\n    std::uniform_int_distribution<int> distribution(0, 10);\n    for (int i = 0; i < buffer.size(); i++) {\n        buffer[i] = static_cast<char>(distribution(gen));\n    }\n\n    std::vector<std::pair<double, std::string>> latencies;\n    std::vector<std::pair<double, std::string>> ratios;\n\n    for (size_t i = 0; i < compressors.size(); i++) {\n        const auto start = std::chrono::steady_clock::now();\n        std::vector<uint8_t> compressed_data =\n                compressors[i]->Compress(buffer.data(), buffer.size());\n        const auto end = std::chrono::steady_clock::now();\n        const auto latency =\n                std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;\n        const double compression_ratio =\n                static_cast<uint16_t>(compressed_data.size()) * 1.00 / buffer.size();\n\n        std::cout << \"Metrics for \" << CompressionToString(compression_list[i]) << \": latency -> \"\n                  << latency.count() << \"ms \"\n                  << \" compression ratio ->\" << compression_ratio << \" \\n\";\n\n        latencies.emplace_back(\n                std::make_pair(latency.count(), CompressionToString(compression_list[i])));\n        ratios.emplace_back(\n                std::make_pair(compression_ratio, CompressionToString(compression_list[i])));\n    }\n\n    int best_speed = 0;\n    int best_ratio = 0;\n\n    for (size_t i = 1; i < latencies.size(); i++) {\n        if (latencies[i].first < latencies[best_speed].first) {\n            best_speed = i;\n        }\n        if (ratios[i].first < ratios[best_ratio].first) {\n            best_ratio = i;\n        }\n    }\n\n    std::cout << \"BEST SPEED: \" << latencies[best_speed].first << \"ms \"\n              << latencies[best_speed].second << \"\\n\";\n    std::cout << \"BEST RATIO: \" << ratios[best_ratio].first << \" \" << ratios[best_ratio].second\n              << \"\\n\";\n}\n\nvoid IncrementalCompressionTest() {\n    std::cout << \"\\n-------Incremental Compressor Perf Analysis-------\\n\";\n\n    std::vector<CowCompression> compression_list = {\n            {kCowCompressLz4, 0},     {kCowCompressBrotli, 1}, {kCowCompressBrotli, 3},\n            {kCowCompressBrotli, 11}, {kCowCompressZstd, 3},   {kCowCompressZstd, 6},\n            {kCowCompressZstd, 9},    {kCowCompressZstd, 22},  {kCowCompressGz, 1},\n            {kCowCompressGz, 3},      {kCowCompressGz, 6},     {kCowCompressGz, 9}};\n    std::vector<std::unique_ptr<ICompressor>> compressors;\n    for (auto i : compression_list) {\n        compressors.emplace_back(ICompressor::Create(i, BLOCK_SZ));\n    }\n\n    // Allocate a buffer of size 8 blocks.\n    std::array<char, 32768> buffer;\n\n    // Generate a random 4k buffer of characters\n    std::default_random_engine gen(SEED_NUMBER);\n    std::uniform_int_distribution<int> distribution(0, 10);\n    for (int i = 0; i < buffer.size(); i++) {\n        buffer[i] = static_cast<char>(distribution(gen));\n    }\n\n    std::vector<std::pair<double, std::string>> latencies;\n    std::vector<std::pair<double, std::string>> ratios;\n\n    for (size_t i = 0; i < compressors.size(); i++) {\n        std::vector<std::vector<uint8_t>> compressed_data_vec;\n        int num_blocks = buffer.size() / BLOCK_SZ;\n        const uint8_t* iter = reinterpret_cast<const uint8_t*>(buffer.data());\n\n        const auto start = std::chrono::steady_clock::now();\n        while (num_blocks > 0) {\n            std::vector<uint8_t> compressed_data = compressors[i]->Compress(iter, BLOCK_SZ);\n            compressed_data_vec.emplace_back(compressed_data);\n            num_blocks--;\n            iter += BLOCK_SZ;\n        }\n\n        const auto end = std::chrono::steady_clock::now();\n        const auto latency =\n                std::chrono::duration_cast<std::chrono::nanoseconds>(end - start) / 1000.0;\n\n        size_t size = 0;\n        for (auto& i : compressed_data_vec) {\n            size += i.size();\n        }\n        const double compression_ratio = size * 1.00 / buffer.size();\n\n        std::cout << \"Metrics for \" << CompressionToString(compression_list[i]) << \": latency -> \"\n                  << latency.count() << \"ms \"\n                  << \" compression ratio ->\" << compression_ratio << \" \\n\";\n\n        latencies.emplace_back(\n                std::make_pair(latency.count(), CompressionToString(compression_list[i])));\n        ratios.emplace_back(\n                std::make_pair(compression_ratio, CompressionToString(compression_list[i])));\n    }\n\n    int best_speed = 0;\n    int best_ratio = 0;\n\n    for (size_t i = 1; i < latencies.size(); i++) {\n        if (latencies[i].first < latencies[best_speed].first) {\n            best_speed = i;\n        }\n        if (ratios[i].first < ratios[best_ratio].first) {\n            best_ratio = i;\n        }\n    }\n\n    std::cout << \"BEST SPEED: \" << latencies[best_speed].first << \"ms \"\n              << latencies[best_speed].second << \"\\n\";\n    std::cout << \"BEST RATIO: \" << ratios[best_ratio].first << \" \" << ratios[best_ratio].second\n              << \"\\n\";\n}\n\n}  // namespace snapshot\n}  // namespace android\n\nint main() {\n    android::snapshot::OneShotCompressionTest();\n    android::snapshot::IncrementalCompressionTest();\n\n    return 0;\n}"
  },
  {
    "path": "fs_mgr/libsnapshot/tools/write_cow.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <libsnapshot/cow_compress.h>\n#include <libsnapshot/cow_format.h>\n#include <libsnapshot/cow_writer.h>\n\n#include <gflags/gflags.h>\n#include <iostream>\n\n#include \"android-base/unique_fd.h\"\n\nDEFINE_bool(silent, false, \"Run silently\");\nDEFINE_int32(writer_version, 2, \"which version of COW writer to be used\");\nDEFINE_bool(write_legacy, false,\n            \"Writes a legacy cow_v2 in current directory, this cow was used to test backwards \"\n            \"compatibility between version 2 and version 3\");\nDEFINE_bool(write_header, false, \"Test reading/writing just the header\");\nusing namespace android::snapshot;\n\n// This writes a simple cow v2 file in the current directory. This file will serve as testdata for\n// ensuring our v3 cow reader will be able to read a cow file created by the v2 writer.\n//\n// WARNING: We should not be overriding this test file, as it will serve as historic marker for what\n// a device with old writer_v2 will write as a cow.\nstatic void write_legacy_cow_v2() {\n    CowOptions options;\n    options.cluster_ops = 5;\n    options.num_merge_ops = 1;\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    char cwd_buffer[1024];\n    size_t cwd_buffer_size = sizeof(cwd_buffer);\n\n    // Get the current working directory path.\n    char* err = getcwd(cwd_buffer, cwd_buffer_size);\n    if (!err) {\n        LOG(ERROR) << \"Couldn't get current directory\";\n    }\n    android::base::unique_fd fd(open(strcat(cwd_buffer, \"/cow_v2\"), O_CREAT | O_RDWR, 0666));\n    if (fd.get() == -1) {\n        LOG(FATAL) << \"couldn't open tmp_cow\";\n    }\n    std::unique_ptr<ICowWriter> writer = CreateCowWriter(2, options, std::move(fd));\n    writer->AddCopy(0, 5);\n    writer->AddRawBlocks(2, data.data(), data.size());\n    writer->AddLabel(1);\n    writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);\n    writer->AddZeroBlocks(5, 10);\n    writer->AddLabel(2);\n    writer->Finalize();\n}\n\nstatic bool WriteCow(const std::string& path) {\n    android::base::unique_fd fd(open(path.c_str(), O_RDONLY));\n    fd.reset(open(path.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0664));\n    if (fd < 0) {\n        PLOG(ERROR) << \"could not open \" << path << \" for writing\";\n        return false;\n    }\n    CowOptions options;\n    std::string data = \"This is some data, believe it\";\n    data.resize(options.block_size, '\\0');\n\n    std::unique_ptr<ICowWriter> writer =\n            CreateCowWriter(FLAGS_writer_version, options, std::move(fd));\n    if (!writer) {\n        return false;\n    }\n\n    writer->AddCopy(0, 5);\n    writer->AddRawBlocks(2, data.data(), data.size());\n    writer->AddLabel(1);\n    writer->AddXorBlocks(50, data.data(), data.size(), 24, 10);\n    writer->AddZeroBlocks(5, 10);\n    writer->AddLabel(2);\n    writer->Finalize();\n\n    if (!FLAGS_silent) {\n        std::cout << \"Writing COW with writer v\" << FLAGS_writer_version << \"\\n\";\n    }\n\n    return true;\n}\n\nint main(int argc, char** argv) {\n    gflags::ParseCommandLineFlags(&argc, &argv, true);\n    if (FLAGS_write_legacy) {\n        write_legacy_cow_v2();\n        return 0;\n    }\n    if (argc < 2) {\n        gflags::ShowUsageWithFlags(argv[0]);\n        return 1;\n    }\n    if (!WriteCow(argv[1])) {\n        return 1;\n    }\n}\n"
  },
  {
    "path": "fs_mgr/libsnapshot/utility.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"utility.h\"\n\n#include <errno.h>\n#include <time.h>\n\n#include <filesystem>\n#include <iomanip>\n#include <sstream>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <fs_mgr/roots.h>\n#include <liblp/property_fetcher.h>\n\nusing android::dm::DeviceMapper;\nusing android::dm::kSectorSize;\nusing android::fiemap::FiemapStatus;\nusing android::fs_mgr::EnsurePathMounted;\nusing android::fs_mgr::EnsurePathUnmounted;\nusing android::fs_mgr::Fstab;\nusing android::fs_mgr::GetEntryForPath;\nusing android::fs_mgr::IPropertyFetcher;\nusing android::fs_mgr::MetadataBuilder;\nusing android::fs_mgr::Partition;\nusing android::fs_mgr::ReadDefaultFstab;\nusing google::protobuf::RepeatedPtrField;\n\nnamespace android {\nnamespace snapshot {\n\nvoid AutoDevice::Release() {\n    name_.clear();\n}\n\nAutoDeviceList::~AutoDeviceList() {\n    // Destroy devices in the reverse order because newer devices may have dependencies\n    // on older devices.\n    for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) {\n        it->reset();\n    }\n}\n\nvoid AutoDeviceList::Release() {\n    for (auto&& p : devices_) {\n        p->Release();\n    }\n}\n\nAutoUnmapDevice::~AutoUnmapDevice() {\n    if (name_.empty()) return;\n    if (!dm_->DeleteDeviceIfExists(name_)) {\n        LOG(ERROR) << \"Failed to auto unmap device \" << name_;\n    }\n}\n\nAutoUnmapImage::~AutoUnmapImage() {\n    if (name_.empty()) return;\n    if (!images_->UnmapImageIfExists(name_)) {\n        LOG(ERROR) << \"Failed to auto unmap cow image \" << name_;\n    }\n}\n\nstd::vector<Partition*> ListPartitionsWithSuffix(MetadataBuilder* builder,\n                                                 const std::string& suffix) {\n    std::vector<Partition*> ret;\n    for (const auto& group : builder->ListGroups()) {\n        for (auto* partition : builder->ListPartitionsInGroup(group)) {\n            if (!base::EndsWith(partition->name(), suffix)) {\n                continue;\n            }\n            ret.push_back(partition);\n        }\n    }\n    return ret;\n}\n\nAutoDeleteSnapshot::~AutoDeleteSnapshot() {\n    if (!name_.empty() && !manager_->DeleteSnapshot(lock_, name_)) {\n        LOG(ERROR) << \"Failed to auto delete snapshot \" << name_;\n    }\n}\n\nReturn InitializeKernelCow(const std::string& device) {\n    // When the kernel creates a persistent dm-snapshot, it requires a CoW file\n    // to store the modifications. The kernel interface does not specify how\n    // the CoW is used, and there is no standard associated.\n    // By looking at the current implementation, the CoW file is treated as:\n    // - a _NEW_ snapshot if its first 32 bits are zero, so the newly created\n    // dm-snapshot device will look like a perfect copy of the origin device;\n    // - an _EXISTING_ snapshot if the first 32 bits are equal to a\n    // kernel-specified magic number and the CoW file metadata is set as valid,\n    // so it can be used to resume the last state of a snapshot device;\n    // - an _INVALID_ snapshot otherwise.\n    // To avoid zero-filling the whole CoW file when a new dm-snapshot is\n    // created, here we zero-fill only the first chunk to be compliant with\n    // lvm.\n    constexpr ssize_t kDmSnapZeroFillSize = kSectorSize * kSnapshotChunkSize;\n\n    std::vector<uint8_t> zeros(kDmSnapZeroFillSize, 0);\n    android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));\n    if (fd < 0) {\n        PLOG(ERROR) << \"Can't open COW device: \" << device;\n        return Return(FiemapStatus::FromErrno(errno));\n    }\n\n    LOG(INFO) << \"Zero-filling COW device: \" << device;\n    if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {\n        PLOG(ERROR) << \"Can't zero-fill COW device for \" << device;\n        return Return(FiemapStatus::FromErrno(errno));\n    }\n    return Return::Ok();\n}\n\nstd::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {\n    Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        LOG(ERROR) << \"Cannot read default fstab\";\n        return nullptr;\n    }\n\n    if (GetEntryForPath(&fstab, path) == nullptr) {\n        LOG(INFO) << \"EnsureMetadataMounted can't find entry for \" << path << \", skipping\";\n        return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(\"\", {}));\n    }\n\n    if (!EnsurePathMounted(&fstab, path)) {\n        LOG(ERROR) << \"Cannot mount \" << path;\n        return nullptr;\n    }\n    return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(path, std::move(fstab)));\n}\n\nAutoUnmountDevice::~AutoUnmountDevice() {\n    if (name_.empty()) return;\n    if (!EnsurePathUnmounted(&fstab_, name_)) {\n        LOG(ERROR) << \"Cannot unmount \" << name_;\n    }\n}\n\nbool FsyncDirectory(const char* dirname) {\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname, O_RDONLY | O_CLOEXEC)));\n    if (fd == -1) {\n        PLOG(ERROR) << \"Failed to open \" << dirname;\n        return false;\n    }\n    if (fsync(fd) == -1) {\n        if (errno == EROFS || errno == EINVAL) {\n            PLOG(WARNING) << \"Skip fsync \" << dirname\n                          << \" on a file system does not support synchronization\";\n        } else {\n            PLOG(ERROR) << \"Failed to fsync \" << dirname;\n            return false;\n        }\n    }\n    return true;\n}\n\nbool WriteStringToFileAtomic(const std::string& content, const std::string& path) {\n    const std::string tmp_path = path + \".tmp\";\n    {\n        const int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;\n        android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_path.c_str(), flags, 0666)));\n        if (fd == -1) {\n            PLOG(ERROR) << \"Failed to open \" << path;\n            return false;\n        }\n        if (!android::base::WriteStringToFd(content, fd)) {\n            PLOG(ERROR) << \"Failed to write to fd \" << fd;\n            return false;\n        }\n        // rename() without fsync() is not safe. Data could still be living on page cache. To ensure\n        // atomiticity, call fsync()\n        if (fsync(fd) != 0) {\n            PLOG(ERROR) << \"Failed to fsync \" << tmp_path;\n        }\n    }\n    if (rename(tmp_path.c_str(), path.c_str()) == -1) {\n        PLOG(ERROR) << \"rename failed from \" << tmp_path << \" to \" << path;\n        return false;\n    }\n    return FsyncDirectory(std::filesystem::path(path).parent_path().c_str());\n}\n\nstd::ostream& operator<<(std::ostream& os, const Now&) {\n    struct tm now{};\n    time_t t = time(nullptr);\n    localtime_r(&t, &now);\n    return os << std::put_time(&now, \"%Y%m%d-%H%M%S\");\n}\n\nstd::ostream& operator<<(std::ostream& os, CancelResult result) {\n    switch (result) {\n        case CancelResult::OK:\n            return os << \"ok\";\n        case CancelResult::ERROR:\n            return os << \"error\";\n        case CancelResult::LIVE_SNAPSHOTS:\n            return os << \"live_snapshots\";\n        case CancelResult::NEEDS_MERGE:\n            return os << \"needs_merge\";\n        default:\n            LOG(ERROR) << \"Unknown cancel result: \" << static_cast<uint32_t>(result);\n            return os;\n    }\n}\n\nvoid AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent>* extents, uint64_t start_block,\n                  uint64_t num_blocks) {\n    if (extents->size() > 0) {\n        auto last_extent = extents->rbegin();\n        auto next_block = last_extent->start_block() + last_extent->num_blocks();\n        if (start_block == next_block) {\n            last_extent->set_num_blocks(last_extent->num_blocks() + num_blocks);\n            return;\n        }\n    }\n    auto* new_extent = extents->Add();\n    new_extent->set_start_block(start_block);\n    new_extent->set_num_blocks(num_blocks);\n}\n\nbool GetLegacyCompressionEnabledProperty() {\n    auto fetcher = IPropertyFetcher::GetInstance();\n    return fetcher->GetBoolProperty(\"ro.virtual_ab.compression.enabled\", false);\n}\n\nbool GetUserspaceSnapshotsEnabledProperty() {\n    auto fetcher = IPropertyFetcher::GetInstance();\n    return fetcher->GetBoolProperty(\"ro.virtual_ab.userspace.snapshots.enabled\", false);\n}\n\nbool IsVendorFromAndroid12() {\n    auto fetcher = IPropertyFetcher::GetInstance();\n\n    const std::string UNKNOWN = \"unknown\";\n    const std::string vendor_release =\n            fetcher->GetProperty(\"ro.vendor.build.version.release_or_codename\", UNKNOWN);\n\n    // No user-space snapshots if vendor partition is on Android 12\n    if (vendor_release.find(\"12\") != std::string::npos) {\n        return true;\n    }\n\n    return false;\n}\n\nbool CanUseUserspaceSnapshots() {\n    if (!GetUserspaceSnapshotsEnabledProperty()) {\n        LOG(INFO) << \"Virtual A/B - Userspace snapshots disabled\";\n        return false;\n    }\n\n    if (IsDmSnapshotTestingEnabled()) {\n        LOG(INFO) << \"Userspace snapshots disabled for testing\";\n        return false;\n    }\n    if (!KernelSupportsCompressedSnapshots()) {\n        LOG(ERROR) << \"Userspace snapshots requested, but no kernel support is available.\";\n        return false;\n    }\n    return true;\n}\n\nbool GetIouringEnabledProperty() {\n    auto fetcher = IPropertyFetcher::GetInstance();\n    return fetcher->GetBoolProperty(\"ro.virtual_ab.io_uring.enabled\", false);\n}\n\nbool GetXorCompressionEnabledProperty() {\n    auto fetcher = IPropertyFetcher::GetInstance();\n    return fetcher->GetBoolProperty(\"ro.virtual_ab.compression.xor.enabled\", false);\n}\n\nbool GetODirectEnabledProperty() {\n    auto fetcher = IPropertyFetcher::GetInstance();\n    return fetcher->GetBoolProperty(\"ro.virtual_ab.o_direct.enabled\", false);\n}\n\nstd::string GetOtherPartitionName(const std::string& name) {\n    auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);\n    CHECK(suffix == \"_a\" || suffix == \"_b\");\n\n    auto other_suffix = (suffix == \"_a\") ? \"_b\" : \"_a\";\n    return name.substr(0, name.size() - suffix.size()) + other_suffix;\n}\n\nbool IsDmSnapshotTestingEnabled() {\n    auto fetcher = IPropertyFetcher::GetInstance();\n    return fetcher->GetBoolProperty(\"snapuserd.test.dm.snapshots\", false);\n}\n\nbool KernelSupportsCompressedSnapshots() {\n    auto& dm = DeviceMapper::Instance();\n    return dm.GetTargetByName(\"user\", nullptr);\n}\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/utility.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <functional>\n#include <iostream>\n#include <string>\n\n#include <android-base/macros.h>\n#include <fstab/fstab.h>\n#include <libdm/dm.h>\n#include <libfiemap/image_manager.h>\n#include <liblp/builder.h>\n#include <libsnapshot/snapshot.h>\n#include <update_engine/update_metadata.pb.h>\n\n#include <libsnapshot/auto_device.h>\n#include <libsnapshot/snapshot.h>\n\nnamespace android {\nnamespace snapshot {\n\n// Unit is sectors, this is a 4K chunk.\nstatic constexpr uint32_t kSnapshotChunkSize = 8;\n\n// A list of devices we created along the way.\n// - Whenever a device is created that is subject to GC'ed at the end of\n//   this function, add it to this list.\n// - If any error has occurred, the list is destroyed, and all these devices\n//   are cleaned up.\n// - Upon success, Release() should be called so that the created devices\n//   are kept.\nstruct AutoDeviceList {\n    ~AutoDeviceList();\n    template <typename T, typename... Args>\n    void EmplaceBack(Args&&... args) {\n        devices_.emplace_back(std::make_unique<T>(std::forward<Args>(args)...));\n    }\n    void Release();\n\n  private:\n    std::vector<std::unique_ptr<AutoDevice>> devices_;\n};\n\n// Automatically unmap a device upon deletion.\nstruct AutoUnmapDevice : AutoDevice {\n    // On destruct, delete |name| from device mapper.\n    AutoUnmapDevice(android::dm::IDeviceMapper* dm, const std::string& name)\n        : AutoDevice(name), dm_(dm) {}\n    ~AutoUnmapDevice();\n\n  private:\n    DISALLOW_COPY_AND_ASSIGN(AutoUnmapDevice);\n    android::dm::IDeviceMapper* dm_ = nullptr;\n};\n\n// Automatically unmap an image upon deletion.\nstruct AutoUnmapImage : AutoDevice {\n    // On destruct, delete |name| from image manager.\n    AutoUnmapImage(android::fiemap::IImageManager* images, const std::string& name)\n        : AutoDevice(name), images_(images) {}\n    ~AutoUnmapImage();\n\n  private:\n    DISALLOW_COPY_AND_ASSIGN(AutoUnmapImage);\n    android::fiemap::IImageManager* images_ = nullptr;\n};\n\n// Automatically deletes a snapshot. |name| should be the name of the partition, e.g. \"system_a\".\n// Client is responsible for maintaining the lifetime of |manager| and |lock|.\nstruct AutoDeleteSnapshot : AutoDevice {\n    AutoDeleteSnapshot(SnapshotManager* manager, SnapshotManager::LockedFile* lock,\n                       const std::string& name)\n        : AutoDevice(name), manager_(manager), lock_(lock) {}\n    ~AutoDeleteSnapshot();\n\n  private:\n    DISALLOW_COPY_AND_ASSIGN(AutoDeleteSnapshot);\n    SnapshotManager* manager_ = nullptr;\n    SnapshotManager::LockedFile* lock_ = nullptr;\n};\n\nstruct AutoUnmountDevice : AutoDevice {\n    // Empty object that does nothing.\n    AutoUnmountDevice() : AutoDevice(\"\") {}\n    static std::unique_ptr<AutoUnmountDevice> New(const std::string& path);\n    ~AutoUnmountDevice();\n\n  private:\n    AutoUnmountDevice(const std::string& path, android::fs_mgr::Fstab&& fstab)\n        : AutoDevice(path), fstab_(std::move(fstab)) {}\n    android::fs_mgr::Fstab fstab_;\n};\n\n// Return a list of partitions in |builder| with the name ending in |suffix|.\nstd::vector<android::fs_mgr::Partition*> ListPartitionsWithSuffix(\n        android::fs_mgr::MetadataBuilder* builder, const std::string& suffix);\n\n// Initialize a device before using it as the COW device for a dm-snapshot device.\nReturn InitializeKernelCow(const std::string& device);\n\n// \"Atomically\" write string to file. This is done by a series of actions:\n// 1. Write to path + \".tmp\"\n// 2. Move temporary file to path using rename()\n// Note that rename() is an atomic operation. This function may not work properly if there\n// is an open fd to |path|, because that fd has an old view of the file.\nbool WriteStringToFileAtomic(const std::string& content, const std::string& path);\nbool FsyncDirectory(const char* dirname);\n\n// Writes current time to a given stream.\nstruct Now {};\nstd::ostream& operator<<(std::ostream& os, const Now&);\n\nstd::ostream& operator<<(std::ostream& os, CancelResult);\n\n// Append to |extents|. Merged into the last element if possible.\nvoid AppendExtent(google::protobuf::RepeatedPtrField<chromeos_update_engine::Extent>* extents,\n                  uint64_t start_block, uint64_t num_blocks);\n\nbool KernelSupportsCompressedSnapshots();\n\nbool GetLegacyCompressionEnabledProperty();\nbool GetUserspaceSnapshotsEnabledProperty();\nbool GetIouringEnabledProperty();\nbool GetXorCompressionEnabledProperty();\nbool GetODirectEnabledProperty();\n\nbool CanUseUserspaceSnapshots();\nbool IsDmSnapshotTestingEnabled();\nbool IsVendorFromAndroid12();\n\n// Swap the suffix of a partition name.\nstd::string GetOtherPartitionName(const std::string& name);\n\n}  // namespace snapshot\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libsnapshot/vts_ota_config_test.cpp",
    "content": "//\n// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <android-base/properties.h>\n#include <gtest/gtest.h>\n\nstatic int GetVsrLevel() {\n    return android::base::GetIntProperty(\"ro.vendor.api_level\", -1);\n}\n\n// @VsrTest = 3.7.6\nTEST(VAB, Enabled) {\n    if (!android::base::GetBoolProperty(\"ro.build.ab_update\", false) && (GetVsrLevel() < __ANDROID_API_T__)) {\n        GTEST_SKIP();\n    }\n    ASSERT_TRUE(android::base::GetBoolProperty(\"ro.virtual_ab.enabled\", false));\n    if (GetVsrLevel() < __ANDROID_API_T__) {\n        GTEST_SKIP();\n    }\n    ASSERT_TRUE(android::base::GetBoolProperty(\"ro.virtual_ab.userspace.snapshots.enabled\", false));\n}\n"
  },
  {
    "path": "fs_mgr/libstorage_literals/Android.bp",
    "content": "\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_headers {\n    name: \"libstorage_literals_headers\",\n    host_supported: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    export_include_dirs: [\".\"],\n    target: {\n        windows: {\n            enabled: true,\n        },\n    },\n}\n"
  },
  {
    "path": "fs_mgr/libstorage_literals/storage_literals/storage_literals.h",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <stdint.h>\n#include <stdlib.h>\n\nnamespace android {\nnamespace storage_literals {\n\ntemplate <size_t Power>\nstruct Size {\n    static constexpr size_t power = Power;\n    explicit constexpr Size(uint64_t count) : value_(count) {}\n\n    constexpr uint64_t bytes() const { return value_ << power; }\n    constexpr uint64_t count() const { return value_; }\n    constexpr operator uint64_t() const { return bytes(); }\n\n  private:\n    uint64_t value_;\n};\n\nusing B = Size<0>;\nusing KiB = Size<10>;\nusing MiB = Size<20>;\nusing GiB = Size<30>;\nusing TiB = Size<40>;\n\nconstexpr B operator\"\"_B(unsigned long long v) {  // NOLINT\n    return B{v};\n}\n\nconstexpr KiB operator\"\"_KiB(unsigned long long v) {  // NOLINT\n    return KiB{v};\n}\n\nconstexpr MiB operator\"\"_MiB(unsigned long long v) {  // NOLINT\n    return MiB{v};\n}\n\nconstexpr GiB operator\"\"_GiB(unsigned long long v) {  // NOLINT\n    return GiB{v};\n}\n\nconstexpr TiB operator\"\"_TiB(unsigned long long v) {  // NOLINT\n    return TiB{v};\n}\n\ntemplate <typename Dest, typename Src>\nconstexpr Dest size_cast(Src src) {\n    if (Src::power < Dest::power) {\n        return Dest(src.count() >> (Dest::power - Src::power));\n    }\n    if (Src::power > Dest::power) {\n        return Dest(src.count() << (Src::power - Dest::power));\n    }\n    return Dest(src.count());\n}\n\nstatic_assert(1_B == 1);\nstatic_assert(1_KiB == 1 << 10);\nstatic_assert(1_MiB == 1 << 20);\nstatic_assert(1_GiB == 1 << 30);\nstatic_assert(1_TiB == 1ULL << 40);\nstatic_assert(size_cast<KiB>(1_B).count() == 0);\nstatic_assert(size_cast<KiB>(1024_B).count() == 1);\nstatic_assert(size_cast<KiB>(1_MiB).count() == 1024);\n\n}  // namespace storage_literals\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libvbmeta/Android.bp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nlibvbmeta_lib_deps = [\n    \"libbase\",\n    \"libcrypto\",\n]\n\ncc_library {\n    name: \"libvbmeta\",\n    host_supported: true,\n    srcs: [\n        \"builder.cpp\",\n        \"reader.cpp\",\n        \"utility.cpp\",\n        \"writer.cpp\",\n    ],\n    shared_libs: [\n        \"liblog\",\n    ] + libvbmeta_lib_deps,\n    export_include_dirs: [\"include\"],\n}\n\ncc_test_host {\n    name: \"libvbmeta_test\",\n    static_libs: [\n        \"liblog\",\n        \"libsparse\",\n        \"libvbmeta\",\n        \"libz\",\n    ] + libvbmeta_lib_deps,\n    srcs: [\n        \"builder_test.cpp\",\n        \"super_vbmeta_test.cpp\",\n    ],\n    required: [\n        \"avbtool\",\n        \"vbmake\",\n    ],\n    data: [\n        \"data/*\",\n    ],\n    // Not unit tests due to several binary and lib dependencies currently hard to replicate in continuous execution\n    test_options: {\n        unit_test: false,\n    },\n}\n"
  },
  {
    "path": "fs_mgr/libvbmeta/builder.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"builder.h\"\n\n#include <android-base/file.h>\n#include <openssl/sha.h>\n\n#include \"reader.h\"\n#include \"utility.h\"\n#include \"writer.h\"\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\nusing android::base::unique_fd;\n\nnamespace android {\nnamespace fs_mgr {\n\nSuperVBMetaBuilder::SuperVBMetaBuilder() {}\n\nSuperVBMetaBuilder::SuperVBMetaBuilder(const int super_vbmeta_fd,\n                                       const std::map<std::string, std::string>& images_path)\n    : super_vbmeta_fd_(super_vbmeta_fd), images_path_(images_path) {}\n\nResult<void> SuperVBMetaBuilder::Build() {\n    for (const auto& [vbmeta_name, file_path] : images_path_) {\n        Result<std::string> content = ReadVBMetaImageFromFile(file_path);\n        if (!content.ok()) {\n            return content.error();\n        }\n\n        Result<uint8_t> vbmeta_index = AddVBMetaImage(vbmeta_name);\n        if (!vbmeta_index.ok()) {\n            return vbmeta_index.error();\n        }\n\n        Result<void> rv_export_vbmeta_image =\n                ExportVBMetaImageToFile(vbmeta_index.value(), content.value());\n        if (!rv_export_vbmeta_image.ok()) {\n            return rv_export_vbmeta_image;\n        }\n    }\n    return {};\n}\n\nResult<std::string> SuperVBMetaBuilder::ReadVBMetaImageFromFile(const std::string& file) {\n    unique_fd source_fd(TEMP_FAILURE_RETRY(open(file.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (source_fd < 0) {\n        return ErrnoError() << \"Couldn't open vbmeta image file \" << file;\n    }\n\n    Result<uint64_t> file_size = GetFileSize(source_fd);\n    if (!file_size.ok()) {\n        return file_size.error();\n    }\n\n    if (file_size.value() > VBMETA_IMAGE_MAX_SIZE) {\n        return Error() << \"vbmeta image file size \" << file_size.value() << \" is too large\";\n    }\n\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);\n    if (!android::base::ReadFully(source_fd, buffer.get(), file_size.value())) {\n        return ErrnoError() << \"Couldn't read vbmeta image file \" << file;\n    }\n\n    return std::string(reinterpret_cast<const char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);\n}\n\nResult<uint8_t> SuperVBMetaBuilder::GetEmptySlot() {\n    for (uint8_t i = 0; i < VBMETA_IMAGE_MAX_NUM; ++i) {\n        if ((table_.header.in_use & (1 << i)) == 0) return i;\n    }\n    return Error() << \"There isn't empty slot in super vbmeta\";\n}\n\nResult<uint8_t> SuperVBMetaBuilder::AddVBMetaImage(const std::string& vbmeta_name) {\n    auto desc = std::find_if(\n            table_.descriptors.begin(), table_.descriptors.end(),\n            [&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });\n\n    uint8_t slot_number = 0;\n    if (desc != table_.descriptors.end()) {\n        slot_number = desc->vbmeta_index;\n    } else {\n        Result<uint8_t> new_slot = GetEmptySlot();\n        if (!new_slot.ok()) {\n            return new_slot;\n        }\n        slot_number = new_slot.value();\n\n        // insert new descriptor into table\n        InternalVBMetaDescriptor new_desc;\n        new_desc.vbmeta_index = slot_number;\n        new_desc.vbmeta_name_length = vbmeta_name.length();\n        new_desc.vbmeta_name = vbmeta_name;\n        memset(new_desc.reserved, 0, sizeof(new_desc.reserved));\n        table_.descriptors.emplace_back(std::move(new_desc));\n\n        // mark slot as in use\n        table_.header.in_use |= (1 << slot_number);\n    }\n\n    return slot_number;\n}\n\nvoid SuperVBMetaBuilder::DeleteVBMetaImage(const std::string& vbmeta_name) {\n    auto desc = std::find_if(\n            table_.descriptors.begin(), table_.descriptors.end(),\n            [&vbmeta_name](const auto& entry) { return entry.vbmeta_name == vbmeta_name; });\n\n    if (desc != table_.descriptors.end()) {\n        // mark slot as not in use\n        table_.header.in_use &= ~(1 << desc->vbmeta_index);\n\n        // erase descriptor in table\n        table_.descriptors.erase(desc);\n    }\n}\n\nstd::unique_ptr<VBMetaTable> SuperVBMetaBuilder::ExportVBMetaTable() {\n    // calculate descriptors size\n    uint32_t descriptors_size = 0;\n    for (const auto& desc : table_.descriptors) {\n        descriptors_size += SUPER_VBMETA_DESCRIPTOR_SIZE + desc.vbmeta_name_length * sizeof(char);\n    }\n\n    // export header\n    table_.header.magic = SUPER_VBMETA_MAGIC;\n    table_.header.major_version = SUPER_VBMETA_MAJOR_VERSION;\n    table_.header.minor_version = SUPER_VBMETA_MINOR_VERSION;\n    table_.header.header_size = SUPER_VBMETA_HEADER_SIZE;\n    table_.header.total_size = SUPER_VBMETA_HEADER_SIZE + descriptors_size;\n    memset(table_.header.checksum, 0, sizeof(table_.header.checksum));\n    table_.header.descriptors_size = descriptors_size;\n    memset(table_.header.reserved, 0, sizeof(table_.header.reserved));\n    std::string serialized_table = SerializeVBMetaTable(table_);\n    ::SHA256(reinterpret_cast<const uint8_t*>(serialized_table.c_str()), table_.header.total_size,\n             &table_.header.checksum[0]);\n\n    return std::make_unique<VBMetaTable>(table_);\n}\n\nResult<void> SuperVBMetaBuilder::ExportVBMetaTableToFile() {\n    std::unique_ptr<VBMetaTable> table = ExportVBMetaTable();\n\n    std::string serialized_table = SerializeVBMetaTable(*table);\n\n    android::base::Result<void> rv_write_primary_vbmeta_table =\n            WritePrimaryVBMetaTable(super_vbmeta_fd_, serialized_table);\n    if (!rv_write_primary_vbmeta_table.ok()) {\n        return rv_write_primary_vbmeta_table;\n    }\n\n    android::base::Result<void> rv_write_backup_vbmeta_table =\n            WriteBackupVBMetaTable(super_vbmeta_fd_, serialized_table);\n    return rv_write_backup_vbmeta_table;\n}\n\nResult<void> SuperVBMetaBuilder::ExportVBMetaImageToFile(const uint8_t vbmeta_index,\n                                                         const std::string& vbmeta_image) {\n    Result<void> rv_write_vbmeta_image =\n            WriteVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);\n    if (!rv_write_vbmeta_image.ok()) {\n        return rv_write_vbmeta_image;\n    }\n\n    Result<void> rv_validate_vbmeta_image =\n            ValidateVBMetaImage(super_vbmeta_fd_, vbmeta_index, vbmeta_image);\n    return rv_validate_vbmeta_image;\n}\n\nbool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,\n                            const std::map<std::string, std::string>& images_path) {\n    unique_fd super_vbmeta_fd(TEMP_FAILURE_RETRY(\n            open(super_vbmeta_file.c_str(), O_CREAT | O_RDWR | O_TRUNC | O_CLOEXEC, 0644)));\n    if (super_vbmeta_fd < 0) {\n        PERROR << \"Couldn't open super vbmeta file \" << super_vbmeta_file;\n        return false;\n    }\n\n    SuperVBMetaBuilder builder(super_vbmeta_fd, images_path);\n\n    Result<void> rv_build = builder.Build();\n    if (!rv_build.ok()) {\n        LERROR << rv_build.error();\n        return false;\n    }\n\n    Result<void> rv_export = builder.ExportVBMetaTableToFile();\n    if (!rv_export.ok()) {\n        LERROR << rv_export.error();\n        return false;\n    }\n\n    return true;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libvbmeta/builder.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <string>\n\n#include <android-base/result.h>\n\n#include \"super_vbmeta_format.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nclass SuperVBMetaBuilder {\n  public:\n    SuperVBMetaBuilder();\n    SuperVBMetaBuilder(const int super_vbmeta_fd,\n                       const std::map<std::string, std::string>& images_path);\n    android::base::Result<void> Build();\n    android::base::Result<std::string> ReadVBMetaImageFromFile(const std::string& file);\n    // It adds the vbmeta image in super_vbmeta and returns the index\n    // (which has the same meaning with vbmeta_index of VBMetaDescriptor).\n    android::base::Result<uint8_t> AddVBMetaImage(const std::string& vbmeta_name);\n    void DeleteVBMetaImage(const std::string& vbmeta_name);\n    std::unique_ptr<VBMetaTable> ExportVBMetaTable();\n    android::base::Result<void> ExportVBMetaTableToFile();\n    android::base::Result<void> ExportVBMetaImageToFile(const uint8_t vbmeta_index,\n                                                        const std::string& vbmeta_image);\n\n  private:\n    android::base::Result<uint8_t> GetEmptySlot();\n\n    int super_vbmeta_fd_;\n    VBMetaTable table_;\n    // Maps vbmeta image name to vbmeta image file path.\n    std::map<std::string, std::string> images_path_;\n};\n\n}  // namespace fs_mgr\n}  // namespace android"
  },
  {
    "path": "fs_mgr/libvbmeta/builder_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include \"builder.h\"\n#include \"super_vbmeta_format.h\"\n\nusing android::base::Result;\nusing android::fs_mgr::SuperVBMetaBuilder;\n\nTEST(BuilderTest, VBMetaTableBasic) {\n    std::unique_ptr<SuperVBMetaBuilder> builder = std::make_unique<SuperVBMetaBuilder>();\n    ASSERT_NE(builder, nullptr);\n\n    Result<uint8_t> vbmeta_index = builder->AddVBMetaImage(\"vbmeta\" /* vbmeta_name */);\n    EXPECT_RESULT_OK(vbmeta_index);\n\n    Result<uint8_t> vbmeta_system_slot = builder->AddVBMetaImage(\"vbmeta_system\" /* vbmeta_name */);\n    EXPECT_RESULT_OK(vbmeta_system_slot);\n\n    Result<uint8_t> vbmeta_vendor_slot = builder->AddVBMetaImage(\"vbmeta_vendor\" /* vbmeta_name */);\n    EXPECT_RESULT_OK(vbmeta_vendor_slot);\n\n    builder->DeleteVBMetaImage(\"vbmeta_system\" /* vbmeta_name */);\n\n    Result<uint8_t> vbmeta_product_slot =\n            builder->AddVBMetaImage(\"vbmeta_product\" /* vbmeta_name */);\n    EXPECT_RESULT_OK(vbmeta_product_slot);\n\n    std::unique_ptr<VBMetaTable> table = builder->ExportVBMetaTable();\n    ASSERT_NE(table, nullptr);\n\n    // check for vbmeta table header\n    EXPECT_EQ(table->header.magic, SUPER_VBMETA_MAGIC);\n    EXPECT_EQ(table->header.major_version, SUPER_VBMETA_MAJOR_VERSION);\n    EXPECT_EQ(table->header.minor_version, SUPER_VBMETA_MINOR_VERSION);\n    EXPECT_EQ(table->header.header_size, SUPER_VBMETA_HEADER_SIZE);\n    EXPECT_EQ(table->header.total_size,\n              SUPER_VBMETA_HEADER_SIZE + SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);\n    EXPECT_EQ(table->header.descriptors_size, SUPER_VBMETA_DESCRIPTOR_SIZE * 3 + 33);\n\n    // Test for vbmeta table descriptors\n    EXPECT_EQ(table->descriptors.size(), 3);\n\n    EXPECT_EQ(table->descriptors[0].vbmeta_index, 0);\n    EXPECT_EQ(table->descriptors[0].vbmeta_name_length, 6);\n    for (int i = 0; i < sizeof(table->descriptors[0].reserved); i++)\n        EXPECT_EQ(table->descriptors[0].reserved[i], 0);\n    EXPECT_EQ(table->descriptors[0].vbmeta_name, \"vbmeta\");\n\n    EXPECT_EQ(table->descriptors[1].vbmeta_index, 2);\n    EXPECT_EQ(table->descriptors[1].vbmeta_name_length, 13);\n    for (int i = 0; i < sizeof(table->descriptors[1].reserved); i++)\n        EXPECT_EQ(table->descriptors[1].reserved[i], 0);\n    EXPECT_EQ(table->descriptors[1].vbmeta_name, \"vbmeta_vendor\");\n\n    EXPECT_EQ(table->descriptors[2].vbmeta_index, 1);\n    EXPECT_EQ(table->descriptors[2].vbmeta_name_length, 14);\n    for (int i = 0; i < sizeof(table->descriptors[2].reserved); i++)\n        EXPECT_EQ(table->descriptors[2].reserved[i], 0);\n    EXPECT_EQ(table->descriptors[2].vbmeta_name, \"vbmeta_product\");\n}\n"
  },
  {
    "path": "fs_mgr/libvbmeta/data/testkey_rsa2048.pem",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAxlVR3TIkouAOvH79vaJTgFhpfvVKQIeVkFRZPVXK/zY0Gvrh\n4JAqGjJoW/PfrQv5sdD36qtHH3a+G5hLZ6Ni+t/mtfjucxZfuLGC3kmJ1T3XqEKZ\ngXXI2IR7vVSoImREvDQGEDyJwtHzLANlkbGg0cghVhWZSCAndO8BenalC2v94/rt\nDfkPekH6dgU3Sf40T0sBSeSY94mOzTaqOR2pfV1rWlLRdWmo33zeHBv52Rlbt0dM\nuXAureXWiHztkm5GCBC1dgM+CaxNtizNEgC91KcD0xuRCCM2WxH+r1lpszyIJDct\nYbrFmVEYl/kjQpafhy7Nsk1fqSTyRdriZSYmTQIDAQABAoIBAQC+kJgaCuX8wYAn\nSXWQ0fmdZlXnMNRpcF0a0pD0SAzGb1RdYBXMaXiqtyhiwc53PPxsCDdNecjayIMd\njJVXPTwLhTruOgMS/bp3gcgWwV34UHV4LJXGOGAE+jbS0hbDBMiudOYmj6RmVshp\nz9G1zZCSQNMXHaWsEYkX59XpzzoB384nRul2QgEtwzUNR9XlpzgtJBLk3SACkvsN\nmQ/DW8IWHXLg8vLn1LzVJ2e3B16H4MoE2TCHxqfMgr03IDRRJogkenQuQsFhevYT\no/mJyHSWavVgzMHG9I5m+eepF4Wyhj1Y4WyKAuMI+9dHAX/h7Lt8XFCQCh5DbkVG\nzGr34sWBAoGBAOs7n7YZqNaaguovfIdRRsxxZr1yJAyDsr6w3yGImDZYju4c4WY9\n5esO2kP3FA4p0c7FhQF5oOb1rBuHEPp36cpL4aGeK87caqTfq63WZAujoTZpr9Lp\nBRbkL7w/xG7jpQ/clpA8sHzHGQs/nelxoOtC7E118FiRgvD/jdhlMyL9AoGBANfX\nvyoN1pplfT2xR8QOjSZ+Q35S/+SAtMuBnHx3l0qH2bbBjcvM1MNDWjnRDyaYhiRu\ni+KA7tqfib09+XpB3g5D6Ov7ls/Ldx0S/VcmVWtia2HK8y8iLGtokoBZKQ5AaFX2\niQU8+tC4h69GnJYQKqNwgCUzh8+gHX5Y46oDiTmRAoGAYpOx8lX+czB8/Da6MNrW\nmIZNT8atZLEsDs2ANEVRxDSIcTCZJId7+m1W+nRoaycLTWNowZ1+2ErLvR10+AGY\nb7Ys79Wg9idYaY9yGn9lnZsMzAiuLeyIvXcSqgjvAKlVWrhOQFOughvNWvFl85Yy\noWSCMlPiTLtt7CCsCKsgKuECgYBgdIp6GZsIfkgclKe0hqgvRoeU4TR3gcjJlM9A\nlBTo+pKhaBectplx9RxR8AnsPobbqwcaHnIfAuKDzjk5mEvKZjClnFXF4HAHbyAF\nnRzZEy9XkWFhc80T5rRpZO7C7qdxmu2aiKixM3V3L3/0U58qULEDbubHMw9bEhAT\nPudI8QKBgHEEiMm/hr9T41hbQi/LYanWnlFw1ue+osKuF8bXQuxnnHNuFT/c+9/A\nvWhgqG6bOEHu+p/IPrYm4tBMYlwsyh4nXCyGgDJLbLIfzKwKAWCtH9LwnyDVhOow\nGH9shdR+sW3Ew97xef02KAH4VlNANEmBV4sQNqWWvsYrcFm2rOdL\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "fs_mgr/libvbmeta/include/libvbmeta/libvbmeta.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <string>\n\nnamespace android {\nnamespace fs_mgr {\n\nbool WriteToSuperVBMetaFile(const std::string& super_vbmeta_file,\n                            const std::map<std::string, std::string>& images_path);\n\n}  // namespace fs_mgr\n}  // namespace android"
  },
  {
    "path": "fs_mgr/libvbmeta/reader.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"reader.h\"\n\n#include <android-base/file.h>\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\n\nnamespace android {\nnamespace fs_mgr {\n\nResult<void> LoadAndVerifySuperVBMetaHeader(const void* buffer, SuperVBMetaHeader* header) {\n    memcpy(header, buffer, sizeof(*header));\n\n    // Do basic validation of super vbmeta.\n    if (header->magic != SUPER_VBMETA_MAGIC) {\n        return Error() << \"Super VBMeta has invalid magic value\";\n    }\n\n    // Check that the version is compatible.\n    if (header->major_version != SUPER_VBMETA_MAJOR_VERSION ||\n        header->minor_version > SUPER_VBMETA_MINOR_VERSION) {\n        return Error() << \"Super VBMeta has incompatible version\";\n    }\n    return {};\n}\n\nvoid LoadVBMetaDescriptors(const void* buffer, uint32_t size,\n                           std::vector<InternalVBMetaDescriptor>* descriptors) {\n    for (int p = 0; p < size;) {\n        InternalVBMetaDescriptor descriptor;\n        memcpy(&descriptor, (char*)buffer + p, SUPER_VBMETA_DESCRIPTOR_SIZE);\n        p += SUPER_VBMETA_DESCRIPTOR_SIZE;\n\n        descriptor.vbmeta_name = std::string((char*)buffer + p, descriptor.vbmeta_name_length);\n        p += descriptor.vbmeta_name_length;\n\n        descriptors->emplace_back(std::move(descriptor));\n    }\n}\n\nResult<void> ReadVBMetaTable(int fd, uint64_t offset, VBMetaTable* table) {\n    std::unique_ptr<uint8_t[]> header_buffer =\n            std::make_unique<uint8_t[]>(SUPER_VBMETA_HEADER_SIZE);\n    if (!android::base::ReadFullyAtOffset(fd, header_buffer.get(), SUPER_VBMETA_HEADER_SIZE,\n                                          offset)) {\n        return ErrnoError() << \"Couldn't read super vbmeta header at offset \" << offset;\n    }\n\n    Result<void> rv_header = LoadAndVerifySuperVBMetaHeader(header_buffer.get(), &table->header);\n    if (!rv_header.ok()) {\n        return rv_header;\n    }\n\n    const uint64_t descriptors_offset = offset + table->header.header_size;\n    std::unique_ptr<uint8_t[]> descriptors_buffer =\n            std::make_unique<uint8_t[]>(table->header.descriptors_size);\n    if (!android::base::ReadFullyAtOffset(fd, descriptors_buffer.get(),\n                                          table->header.descriptors_size, descriptors_offset)) {\n        return ErrnoError() << \"Couldn't read super vbmeta descriptors at offset \"\n                            << descriptors_offset;\n    }\n\n    LoadVBMetaDescriptors(descriptors_buffer.get(), table->header.descriptors_size,\n                          &table->descriptors);\n    return {};\n}\n\nResult<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table) {\n    uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;\n    return ReadVBMetaTable(fd, offset, table);\n}\n\nResult<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table) {\n    uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;\n    return ReadVBMetaTable(fd, offset, table);\n}\n\nResult<std::string> ReadVBMetaImage(int fd, int slot) {\n    const uint64_t offset = 2 * SUPER_VBMETA_TABLE_MAX_SIZE + slot * VBMETA_IMAGE_MAX_SIZE;\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);\n    if (!android::base::ReadFullyAtOffset(fd, buffer.get(), VBMETA_IMAGE_MAX_SIZE, offset)) {\n        return ErrnoError() << \"Couldn't read vbmeta image at offset \" << offset;\n    }\n    return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);\n}\n\nResult<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,\n                                 const std::string& vbmeta_image) {\n    Result<std::string> content = ReadVBMetaImage(super_vbmeta_fd, vbmeta_index);\n    if (!content.ok()) {\n        return content.error();\n    }\n\n    if (vbmeta_image != content.value()) {\n        return Error() << \"VBMeta Image in Super VBMeta differ from the original one.\";\n    }\n    return {};\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libvbmeta/reader.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android-base/result.h>\n\n#include \"super_vbmeta_format.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nandroid::base::Result<void> ReadPrimaryVBMetaTable(int fd, VBMetaTable* table);\nandroid::base::Result<void> ReadBackupVBMetaTable(int fd, VBMetaTable* table);\nandroid::base::Result<std::string> ReadVBMetaImage(int fd, int slot);\n\nandroid::base::Result<void> ValidateVBMetaImage(int super_vbmeta_fd, int vbmeta_index,\n                                                const std::string& vbmeta_image);\n\n}  // namespace fs_mgr\n}  // namespace android"
  },
  {
    "path": "fs_mgr/libvbmeta/super_vbmeta_format.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* This .h file is intended for CPP clients (usually fastbootd, recovery and update_engine)  */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"super_vbmeta_format_c.h\"\n\nstruct InternalVBMetaDescriptor : VBMetaDescriptor {\n    /*  64: The vbmeta image's name */\n    std::string vbmeta_name;\n};\n\nstruct VBMetaTable {\n    SuperVBMetaHeader header;\n    std::vector<InternalVBMetaDescriptor> descriptors;\n};"
  },
  {
    "path": "fs_mgr/libvbmeta/super_vbmeta_format_c.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* This .h file is intended for C clients (usually bootloader).  */\n\n#pragma once\n\n#include <stdint.h>\n\n/* Magic signature for super vbmeta. */\n#define SUPER_VBMETA_MAGIC 0x5356424d\n\n/* Current super vbmeta version. */\n#define SUPER_VBMETA_MAJOR_VERSION 1\n#define SUPER_VBMETA_MINOR_VERSION 0\n\n/* super vbmeta size. */\n#define SUPER_VBMETA_HEADER_SIZE sizeof(SuperVBMetaHeader)\n#define SUPER_VBMETA_DESCRIPTOR_SIZE sizeof(VBMetaDescriptor)\n#define SUPER_VBMETA_TABLE_MAX_SIZE 2048\n\n/* super vbmeta offset. */\n#define PRIMARY_SUPER_VBMETA_TABLE_OFFSET 0\n#define BACKUP_SUPER_VBMETA_TABLE_OFFSET SUPER_VBMETA_TABLE_MAX_SIZE\n\n/* restriction of vbmeta image */\n#define VBMETA_IMAGE_MAX_NUM 32\n#define VBMETA_IMAGE_MAX_SIZE 64 * 1024\n\n/* Binary format of the super vbmeta image.\n *\n * The super vbmeta image consists of two blocks:\n *\n *  +------------------------------------------+\n *  | Super VBMeta Table - fixed size          |\n *  +------------------------------------------+\n *  | Backup Super VBMeta Table - fixed size   |\n *  +------------------------------------------+\n *  | VBMeta Images - fixed size               |\n *  +------------------------------------------+\n *\n *  The \"Super VBMeta Table\" records the offset\n *  and the size of each vbmeta_partition within\n *  /super_vbmeta.\n *\n *  The \"VBMeta Image\" is copied from each vbmeta_partition\n *  and filled with 0 until 64K bytes.\n *\n * The super vbmeta table consists of two blocks:\n *\n *  +-----------------------------------------+\n *  | Header data - fixed size                |\n *  +-----------------------------------------+\n *  | VBMeta descriptors - variable size      |\n *  +-----------------------------------------+\n *\n * The \"Header data\" block is described by the following struct and\n * is always 128 bytes long.\n *\n * The \"VBMeta descriptor\" is |descriptors_size| + |vbmeta_name_length|\n * bytes long. It contains the offset and size for each vbmeta image\n * and is followed by |vbmeta_name_length| bytes of the partition name\n * (UTF-8 encoded).\n */\n\ntypedef struct SuperVBMetaHeader {\n    /*  0: Magic signature (SUPER_VBMETA_MAGIC). */\n    uint32_t magic;\n\n    /*  4: Major version. Version number required to read this super vbmeta. If the version is not\n     * equal to the library version, the super vbmeta should be considered incompatible.\n     */\n    uint16_t major_version;\n\n    /*  6: Minor version. A library supporting newer features should be able to\n     * read super vbmeta with an older minor version. However, an older library\n     * should not support reading super vbmeta if its minor version is higher.\n     */\n    uint16_t minor_version;\n\n    /*  8: The size of this header struct. */\n    uint32_t header_size;\n\n    /*  12: The size of this super vbmeta table. */\n    uint32_t total_size;\n\n    /*  16: SHA256 checksum of this super vbmeta table, with this field set to 0. */\n    uint8_t checksum[32];\n\n    /*  48: The size of the vbmeta table descriptors. */\n    uint32_t descriptors_size;\n\n    /*  52: mark which slot is in use. */\n    uint32_t in_use = 0;\n\n    /*  56: reserved for other usage, filled with 0. */\n    uint8_t reserved[72];\n} __attribute__((packed)) SuperVBMetaHeader;\n\ntypedef struct VBMetaDescriptor {\n    /*  0: The slot number of the vbmeta image. */\n    uint8_t vbmeta_index;\n\n    /*  12: The length of the vbmeta image name. */\n    uint32_t vbmeta_name_length;\n\n    /*  16: Space reserved for other usage, filled with 0. */\n    uint8_t reserved[48];\n} __attribute__((packed)) VBMetaDescriptor;"
  },
  {
    "path": "fs_mgr/libvbmeta/super_vbmeta_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n#include <openssl/sha.h>\n#include <sparse/sparse.h>\n\n#include \"reader.h\"\n#include \"super_vbmeta_format.h\"\n#include \"utility.h\"\n#include \"writer.h\"\n\n#define FAKE_DATA_SIZE 40960\n#define FAKE_PARTITION_SIZE FAKE_DATA_SIZE * 25\n\nusing android::base::Result;\nusing android::fs_mgr::GetFileSize;\nusing android::fs_mgr::ReadVBMetaImage;\nusing SparsePtr = std::unique_ptr<sparse_file, decltype(&sparse_file_destroy)>;\n\nvoid GeneratePartitionImage(int fd, const std::string& file_name,\n                            const std::string& partition_name) {\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(FAKE_DATA_SIZE);\n    for (size_t c = 0; c < FAKE_DATA_SIZE; c++) {\n        buffer[c] = uint8_t(c);\n    }\n\n    SparsePtr file(sparse_file_new(512 /* block size */, FAKE_DATA_SIZE), sparse_file_destroy);\n    EXPECT_TRUE(file);\n    EXPECT_EQ(0, sparse_file_add_data(file.get(), buffer.get(), FAKE_DATA_SIZE,\n                                      0 /* offset in blocks */));\n    EXPECT_EQ(0, sparse_file_write(file.get(), fd, false /* gz */, true /* sparse */,\n                                   false /* crc */));\n\n    std::stringstream cmd;\n    cmd << \"avbtool add_hashtree_footer\"\n        << \" --image \" << file_name << \" --partition_name \" << partition_name\n        << \" --partition_size \" << FAKE_PARTITION_SIZE << \" --algorithm SHA256_RSA2048\"\n        << \" --key data/testkey_rsa2048.pem\";\n\n    int rc = system(cmd.str().c_str());\n    EXPECT_TRUE(WIFEXITED(rc));\n    EXPECT_EQ(WEXITSTATUS(rc), 0);\n}\n\nvoid GenerateVBMetaImage(const std::string& vbmeta_file_name,\n                         const std::string& include_file_name) {\n    std::stringstream cmd;\n    cmd << \"avbtool make_vbmeta_image\"\n        << \" --output \" << vbmeta_file_name << \" --include_descriptors_from_image \"\n        << include_file_name;\n\n    int rc = system(cmd.str().c_str());\n    EXPECT_TRUE(WIFEXITED(rc));\n    EXPECT_EQ(WEXITSTATUS(rc), 0);\n}\n\nstd::string ReadVBMetaImageFromFile(const std::string& file) {\n    android::base::unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));\n    EXPECT_GT(fd, 0);\n    Result<uint64_t> file_size = GetFileSize(fd);\n    EXPECT_RESULT_OK(file_size);\n    std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(VBMETA_IMAGE_MAX_SIZE);\n    EXPECT_TRUE(android::base::ReadFully(fd, buffer.get(), file_size.value()));\n    return std::string(reinterpret_cast<char*>(buffer.get()), VBMETA_IMAGE_MAX_SIZE);\n}\n\nTEST(VBMetaTableTest, VBMetaTableBasic) {\n    TemporaryDir td;\n\n    // Generate Partition Image\n    TemporaryFile system_tf(std::string(td.path));\n    std::string system_path(system_tf.path);\n    GeneratePartitionImage(system_tf.fd, system_path, \"system\");\n    system_tf.release();\n\n    TemporaryFile vendor_tf(std::string(td.path));\n    std::string vendor_path(vendor_tf.path);\n    GeneratePartitionImage(vendor_tf.fd, vendor_path, \"vendor\");\n    vendor_tf.release();\n\n    TemporaryFile product_tf(std::string(td.path));\n    std::string product_path(product_tf.path);\n    GeneratePartitionImage(product_tf.fd, product_path, \"product\");\n    product_tf.release();\n\n    // Generate VBMeta Image\n    std::string vbmeta_system_path(td.path);\n    vbmeta_system_path.append(\"/vbmeta_system.img\");\n    GenerateVBMetaImage(vbmeta_system_path, system_path);\n\n    std::string vbmeta_vendor_path(td.path);\n    vbmeta_vendor_path.append(\"/vbmeta_vendor.img\");\n    GenerateVBMetaImage(vbmeta_vendor_path, vendor_path);\n\n    std::string vbmeta_product_path(td.path);\n    vbmeta_product_path.append(\"/vbmeta_product.img\");\n    GenerateVBMetaImage(vbmeta_product_path, product_path);\n\n    // Generate Super VBMeta Image\n    std::string super_vbmeta_path(td.path);\n    super_vbmeta_path.append(\"/super_vbmeta.img\");\n\n    std::stringstream cmd;\n    cmd << \"vbmake\"\n        << \" --image \"\n        << \"vbmeta_system\"\n        << \"=\" << vbmeta_system_path << \" --image \"\n        << \"vbmeta_vendor\"\n        << \"=\" << vbmeta_vendor_path << \" --image \"\n        << \"vbmeta_product\"\n        << \"=\" << vbmeta_product_path << \" --output=\" << super_vbmeta_path;\n\n    int rc = system(cmd.str().c_str());\n    ASSERT_TRUE(WIFEXITED(rc));\n    ASSERT_EQ(WEXITSTATUS(rc), 0);\n\n    android::base::unique_fd fd(open(super_vbmeta_path.c_str(), O_RDONLY | O_CLOEXEC));\n    EXPECT_GT(fd, 0);\n\n    // Check the size of vbmeta table\n    Result<uint64_t> super_vbmeta_size = GetFileSize(fd);\n    EXPECT_RESULT_OK(super_vbmeta_size);\n    EXPECT_EQ(super_vbmeta_size.value(),\n              SUPER_VBMETA_TABLE_MAX_SIZE * 2 + VBMETA_IMAGE_MAX_SIZE * 3);\n\n    // Check Primary vbmeta table is equal to Backup one\n    VBMetaTable table;\n    EXPECT_RESULT_OK(android::fs_mgr::ReadPrimaryVBMetaTable(fd, &table));\n    VBMetaTable table_backup;\n    EXPECT_RESULT_OK(android::fs_mgr::ReadBackupVBMetaTable(fd, &table_backup));\n    EXPECT_EQ(android::fs_mgr::SerializeVBMetaTable(table),\n              android::fs_mgr::SerializeVBMetaTable(table_backup));\n\n    // Check vbmeta table Header Checksum\n    std::string serial_table = android::fs_mgr::SerializeVBMetaTable(table);\n    std::string serial_removed_checksum(serial_table);\n    // Replace checksum 32 bytes (starts at 16th byte) with 0\n    serial_removed_checksum.replace(16, 32, 32, 0);\n    uint8_t test_checksum[32];\n    ::SHA256(reinterpret_cast<const uint8_t*>(serial_removed_checksum.c_str()),\n             table.header.total_size, &test_checksum[0]);\n    EXPECT_EQ(memcmp(table.header.checksum, test_checksum, 32), 0);\n\n    // Check vbmeta table descriptors and vbmeta images\n    EXPECT_EQ(table.descriptors.size(), 3);\n\n    EXPECT_EQ(table.descriptors[0].vbmeta_index, 0);\n    EXPECT_EQ(table.descriptors[0].vbmeta_name_length, 14);\n    EXPECT_EQ(table.descriptors[0].vbmeta_name, \"vbmeta_product\");\n    Result<std::string> vbmeta_product_content = ReadVBMetaImage(fd, 0);\n    EXPECT_RESULT_OK(vbmeta_product_content);\n    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_product_path), vbmeta_product_content.value());\n\n    EXPECT_EQ(table.descriptors[1].vbmeta_index, 1);\n    EXPECT_EQ(table.descriptors[1].vbmeta_name_length, 13);\n    EXPECT_EQ(table.descriptors[1].vbmeta_name, \"vbmeta_system\");\n    Result<std::string> vbmeta_system_content = ReadVBMetaImage(fd, 1);\n    EXPECT_RESULT_OK(vbmeta_system_content);\n    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_system_path), vbmeta_system_content.value());\n\n    EXPECT_EQ(table.descriptors[2].vbmeta_index, 2);\n    EXPECT_EQ(table.descriptors[2].vbmeta_name_length, 13);\n    EXPECT_EQ(table.descriptors[2].vbmeta_name, \"vbmeta_vendor\");\n    Result<std::string> vbmeta_vendor_content = ReadVBMetaImage(fd, 2);\n    EXPECT_RESULT_OK(vbmeta_vendor_content);\n    EXPECT_EQ(ReadVBMetaImageFromFile(vbmeta_vendor_path), vbmeta_vendor_content.value());\n}\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "fs_mgr/libvbmeta/utility.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"utility.h\"\n\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"super_vbmeta_format.h\"\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\n\nnamespace android {\nnamespace fs_mgr {\n\nResult<uint64_t> GetFileSize(int fd) {\n    struct stat sb;\n    if (fstat(fd, &sb) == -1) {\n        return ErrnoError() << \"Couldn't get the file size\";\n    }\n    return sb.st_size;\n}\n\nuint64_t IndexOffset(const uint8_t vbmeta_index) {\n    /* There are primary and backup vbmeta table in super_vbmeta,\n       so SUPER_VBMETA_TABLE_MAX_SIZE is counted twice. */\n    return 2 * SUPER_VBMETA_TABLE_MAX_SIZE + vbmeta_index * VBMETA_IMAGE_MAX_SIZE;\n}\n\n}  // namespace fs_mgr\n}  // namespace android\n"
  },
  {
    "path": "fs_mgr/libvbmeta/utility.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android-base/logging.h>\n#include <android-base/result.h>\n\n#define VBMETA_TAG \"[libvbmeta] \"\n#define LWARN LOG(WARNING) << VBMETA_TAG\n#define LINFO LOG(INFO) << VBMETA_TAG\n#define LERROR LOG(ERROR) << VBMETA_TAG\n#define PWARNING PLOG(WARNING) << VBMETA_TAG\n#define PERROR PLOG(ERROR) << VBMETA_TAG\n\nnamespace android {\nnamespace fs_mgr {\n\nandroid::base::Result<uint64_t> GetFileSize(int fd);\n\nuint64_t IndexOffset(const uint8_t vbmeta_index);\n\n}  // namespace fs_mgr\n}  // namespace android"
  },
  {
    "path": "fs_mgr/libvbmeta/writer.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"writer.h\"\n\n#include <android-base/file.h>\n\n#include \"utility.h\"\n\nusing android::base::ErrnoError;\nusing android::base::Result;\n\nnamespace android {\nnamespace fs_mgr {\n\nstd::string SerializeVBMetaTable(const VBMetaTable& input) {\n    std::string table;\n    table.append(reinterpret_cast<const char*>(&input.header), SUPER_VBMETA_HEADER_SIZE);\n\n    for (const auto& desc : input.descriptors) {\n        table.append(reinterpret_cast<const char*>(&desc), SUPER_VBMETA_DESCRIPTOR_SIZE);\n        table.append(desc.vbmeta_name);\n    }\n\n    // Ensure the size of vbmeta table is SUPER_VBMETA_TABLE_MAX_SIZE\n    table.resize(SUPER_VBMETA_TABLE_MAX_SIZE, '\\0');\n\n    return table;\n}\n\nResult<void> WritePrimaryVBMetaTable(int fd, const std::string& table) {\n    const uint64_t offset = PRIMARY_SUPER_VBMETA_TABLE_OFFSET;\n    if (lseek(fd, offset, SEEK_SET) < 0) {\n        return ErrnoError() << __PRETTY_FUNCTION__ << \" lseek failed\";\n    }\n\n    if (!android::base::WriteFully(fd, table.data(), table.size())) {\n        return ErrnoError() << \"Failed to write primary vbmeta table at offset \" << offset;\n    }\n    return {};\n}\n\nResult<void> WriteBackupVBMetaTable(int fd, const std::string& table) {\n    const uint64_t offset = BACKUP_SUPER_VBMETA_TABLE_OFFSET;\n    if (lseek(fd, offset, SEEK_SET) < 0) {\n        return ErrnoError() << __PRETTY_FUNCTION__ << \" lseek failed\";\n    }\n\n    if (!android::base::WriteFully(fd, table.data(), table.size())) {\n        return ErrnoError() << \"Failed to write backup vbmeta table at offset \" << offset;\n    }\n    return {};\n}\n\nResult<void> WriteVBMetaImage(int fd, const uint8_t slot_number, const std::string& vbmeta_image) {\n    const uint64_t offset = IndexOffset(slot_number);\n    if (lseek(fd, offset, SEEK_SET) < 0) {\n        return ErrnoError() << __PRETTY_FUNCTION__ << \" lseek failed\";\n    }\n\n    if (!android::base::WriteFully(fd, vbmeta_image.data(), vbmeta_image.size())) {\n        return ErrnoError() << \"Failed to write vbmeta image at offset \" << offset;\n    }\n    return {};\n}\n\n}  // namespace fs_mgr\n}  // namespace android"
  },
  {
    "path": "fs_mgr/libvbmeta/writer.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <android-base/result.h>\n\n#include \"super_vbmeta_format.h\"\n\nnamespace android {\nnamespace fs_mgr {\n\nstd::string SerializeVBMetaTable(const VBMetaTable& input);\n\nandroid::base::Result<void> WritePrimaryVBMetaTable(int fd, const std::string& table);\nandroid::base::Result<void> WriteBackupVBMetaTable(int fd, const std::string& table);\nandroid::base::Result<void> WriteVBMetaImage(int fd, const uint8_t slot_number,\n                                             const std::string& vbmeta_image);\n\n}  // namespace fs_mgr\n}  // namespace android"
  },
  {
    "path": "fs_mgr/tests/Android.bp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n    default_team: \"trendy_team_android_kernel\",\n}\n\ncc_test {\n    name: \"CtsFsMgrTestCases\",\n    test_suites: [\n        \"cts\",\n        \"device-tests\",\n    ],\n    compile_multilib: \"both\",\n    multilib: {\n        lib32: {\n            suffix: \"32\",\n        },\n        lib64: {\n            suffix: \"64\",\n        },\n    },\n\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libfs_mgr\",\n        \"libgmock\",\n        \"libgtest\",\n    ],\n    srcs: [\n        \"file_wait_test.cpp\",\n        \"fs_mgr_test.cpp\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n}\n\nsh_binary_host {\n    name: \"adb-remount-test\",\n    src: \"adb-remount-test.sh\",\n    filename_from_src: true,\n    target: {\n        darwin: {\n            enabled: false,\n        },\n        windows: {\n            enabled: false,\n        },\n    },\n}\n\nsh_test {\n    name: \"adb-remount-sh\",\n    src: \"adb-remount-test.sh\",\n    filename_from_src: true,\n    test_suites: [\"general-tests\"],\n    test_config: \"adb-remount-sh.xml\",\n}\n\njava_test_host {\n    name: \"fs_mgr_vendor_overlay_test\",\n\n    srcs:  [\"src/**/VendorOverlayHostTest.java\"],\n\n    libs: [\"tradefed\"],\n\n    test_config: \"vendor-overlay-test.xml\",\n\n    test_suites: [\"general-tests\"],\n}\n\ncc_test {\n    name: \"vts_fs_test\",\n    test_suites: [\n        \"vts\",\n        \"device-tests\",\n    ],\n    test_options: {\n        min_shipping_api_level: 29,\n    },\n    require_root: true,\n    auto_gen_config: true,\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    srcs: [\n        \"vts_fs_test.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    static_libs: [\n        \"libfs_mgr\",\n        \"libgmock\",\n        \"libgtest\",\n    ],\n}\n"
  },
  {
    "path": "fs_mgr/tests/AndroidTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n          http://www.apache.org/licenses/LICENSE-2.0\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config for CTS fs_mgr test cases\">\n    <option name=\"test-suite-tag\" value=\"cts\" />\n    <option name=\"config-descriptor:metadata\" key=\"component\" value=\"systems\" />\n    <option name=\"config-descriptor:metadata\" key=\"parameter\" value=\"not_instant_app\" />\n    <option name=\"config-descriptor:metadata\" key=\"parameter\" value=\"multi_abi\" />\n    <option name=\"config-descriptor:metadata\" key=\"parameter\" value=\"secondary_user\" />\n    <option name=\"config-descriptor:metadata\" key=\"parameter\" value=\"secondary_user_on_secondary_display\" />\n    <target_preparer class=\"com.android.compatibility.common.tradefed.targetprep.FilePusher\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"CtsFsMgrTestCases->/data/local/tmp/CtsFsMgrTestCases\" />\n        <option name=\"append-bitness\" value=\"true\" />\n    </target_preparer>\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\">\n        <option name=\"throw-on-error\" value=\"false\" />\n    </target_preparer>\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        <option name=\"native-test-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"module-name\" value=\"CtsFsMgrTestCases\" />\n        <option name=\"runtime-hint\" value=\"65s\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "fs_mgr/tests/adb-remount-sh.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n-->\n<configuration description=\"Config for adb remount test cases\">\n    <option name=\"test-suite-tag\" value=\"adb-remount\" />\n    <!-- This test requires a device, so it's not annotated with a null-device -->\n    <test class=\"com.android.tradefed.testtype.binary.ExecutableHostTest\" >\n        <option name=\"binary\" value=\"adb-remount-test.sh\" />\n        <!-- Increase default timeout as script is quite long -->\n        <option name=\"per-binary-timeout\" value=\"1h\" />\n    </test>\n</configuration>\n\n"
  },
  {
    "path": "fs_mgr/tests/adb-remount-test.sh",
    "content": "#! /bin/bash\n#\n# Divided into four section:\n#\n##  USAGE\n##  Helper Variables\n##  Helper Functions\n##  MAINLINE\n\n##\n##  USAGE\n##\n\nUSAGE=\"USAGE: `basename ${0}` [--help] [--serial <SerialNumber>] [options]\n\nadb remount tests\n\n-c --color                     Dress output with highlighting colors\n-h --help                      This help\n-D --no-wait-screen            Do not wait for display screen to settle\n-t --print-time                Report the test duration\n-s --serial                    Specify device (must if multiple are present)\"\nif [ -n \"`which timeout`\" ]; then\n  USAGE=\"${USAGE}\n-a --wait-adb <duration>       adb wait timeout\n-f --wait-fastboot <duration>  fastboot wait timeout\"\nfi\nUSAGE=\"${USAGE}\n\nConditions:\n - Must be a userdebug build.\n - Must be in adb mode.\n - Also tests overlayfs\n  - Kernel must have overlayfs enabled and patched to support override_creds.\n  - Must have either erofs, squashfs, ext4-dedupe or full partitions.\n  - Minimum expectation system and vender are overlayfs covered partitions.\n\"\n\n##\n##  Helper Variables\n##\n\nEMPTY=\"\"\nSPACE=\" \"\n# Line up wrap to [  XXXXXXX ] messages.\nINDENT=\"             \"\n# A _real_ embedded tab character\nTAB=\"`echo | tr '\\n' '\\t'`\"\n# A _real_ embedded escape character\nESCAPE=\"`echo | tr '\\n' '\\033'`\"\n# A _real_ embedded carriage return character\nCR=\"`echo | tr '\\n' '\\r'`\"\nRED=\nGREEN=\nYELLOW=\nBLUE=\nNORMAL=\ncolor=false\n# Assume support color if stdout is terminal.\n[ -t 1 ] && color=true\nprint_time=true\nstart_time=`date +%s`\nACTIVE_SLOT=\nOVERLAYFS_BACKING=\"cache mnt/scratch\"\n\nADB_WAIT=4m\nFASTBOOT_WAIT=2m\nscreen_wait=true\n\n##\n##  Helper Functions\n##\n\n[ \"USAGE: LOG [RUN|OK|PASSED|WARNING|ERROR|FAILED|INFO] [message]...\" ]\nLOG() {\n  if ${print_time}; then\n    echo -n \"$(date '+%m-%d %T') \"\n  fi >&2\n  case \"${1}\" in\n    R*)\n      shift\n      echo \"${GREEN}[ RUN      ]${NORMAL}\" \"${@}\"\n      ;;\n    OK)\n      shift\n      echo \"${GREEN}[       OK ]${NORMAL}\" \"${@}\"\n      ;;\n    P*)\n      shift\n      echo \"${GREEN}[  PASSED  ]${NORMAL}\" \"${@}\"\n      ;;\n    W*)\n      shift\n      echo \"${YELLOW}[  WARNING ]${NORMAL}\" \"${@}\"\n      ;;\n    E*)\n      shift\n      echo \"${RED}[    ERROR ]${NORMAL}\" \"${@}\"\n      ;;\n    F*)\n      shift\n      echo \"${RED}[  FAILED  ]${NORMAL}\" \"${@}\"\n      ;;\n    I*)\n      shift\n      echo \"${BLUE}[     INFO ]${NORMAL}\" \"${@}\"\n      ;;\n    *)\n      echo \"${BLUE}[     INFO ]${NORMAL}\" \"${@}\"\n      ;;\n  esac >&2\n}\n\n[ \"USAGE: inFastboot\n\nReturns: true if device is in fastboot mode\" ]\ninFastboot() {\n  fastboot devices |\n    if [ -n \"${ANDROID_SERIAL}\" ]; then\n      grep \"^${ANDROID_SERIAL}[${SPACE}${TAB}]\" > /dev/null\n    else\n      wc -l | grep \"^[${SPACE}${TAB}]*1\\$\" >/dev/null\n    fi\n}\n\n[ \"USAGE: inAdb\n\nReturns: true if device is in adb mode\" ]\ninAdb() {\n  adb devices |\n    grep -v -e 'List of devices attached' -e '^$' -e \"[${SPACE}${TAB}]recovery\\$\" |\n    if [ -n \"${ANDROID_SERIAL}\" ]; then\n      grep \"^${ANDROID_SERIAL}[${SPACE}${TAB}]\" > /dev/null\n    else\n      wc -l | grep \"^[${SPACE}${TAB}]*1\\$\" >/dev/null\n    fi\n}\n\n[ \"USAGE: inRecovery\n\nReturns: true if device is in recovery mode\" ]\ninRecovery() {\n  local list=\"`adb devices |\n              grep -v -e 'List of devices attached' -e '^$'`\"\n  if [ -n \"${ANDROID_SERIAL}\" ]; then\n    echo \"${list}\" |\n      grep \"^${ANDROID_SERIAL}[${SPACE}${TAB}][${SPACE}${TAB}]*recovery\\$\" >/dev/null\n    return ${?}\n  fi\n  if echo \"${list}\" | wc -l | grep \"^[${SPACE}${TAB}]*1\\$\" >/dev/null; then\n    echo \"${list}\" |\n      grep \"[${SPACE}${TAB}]recovery\\$\" >/dev/null\n    return ${?}\n  fi\n  false\n}\n\n[ \"USAGE: adb_sh <commands> </dev/stdin >/dev/stdout 2>/dev/stderr\n\nReturns: true if the command succeeded\" ]\nadb_sh() {\n  local args=\n  for i in \"${@}\"; do\n    [ -z \"${args}\" ] || args=\"${args} \"\n    if [ X\"${i}\" != X\"${i#\\'}\" ]; then\n      args=\"${args}${i}\"\n    elif [ X\"${i}\" != X\"${i#* }\" ]; then\n      args=\"${args}'${i}'\"\n    elif [ X\"${i}\" != X\"${i#*${TAB}}\" ]; then\n      args=\"${args}'${i}'\"\n    else\n      args=\"${args}${i}\"\n    fi\n  done\n  adb shell \"${args}\"\n}\n\n[ \"USAGE: adb_date >/dev/stdout\n\nReturns: report device epoch time (suitable for logcat -t)\" ]\nadb_date() {\n  adb_sh date +%s.%N </dev/null\n}\n\n[ \"USAGE: adb_logcat [arguments] >/dev/stdout\n\nReturns: the logcat output\" ]\nadb_logcat() {\n  LOG INFO \"logcat ${*}\"\n  adb logcat \"${@}\" </dev/null |\n    tr -d '\\r' |\n    grep -v 'logd    : logdr: UID=' |\n    sed -e '${ /------- beginning of kernel/d }' -e 's/^[0-1][0-9]-[0-3][0-9] //'\n}\n\n[ \"USAGE: avc_check >/dev/stderr\n\nReturns: worrisome avc violations\" ]\navc_check() {\n  if ! ${overlayfs_needed:-false}; then\n    return\n  fi\n  local L=`adb_logcat -b all -v brief -d \\\n                      -e 'context=u:object_r:unlabeled:s0' 2>/dev/null |\n             sed -n 's/.*avc: //p' |\n             sort -u`\n  if [ -z \"${L}\" ]; then\n    return\n  fi\n  LOG WARNING \"unlabeled sepolicy violations:\"\n  echo \"${L}\" | sed \"s/^/${INDENT}/\" >&2\n}\n\n[ \"USAGE: get_property <prop>\n\nReturns the property value\" ]\nget_property() {\n  adb_sh getprop ${1} </dev/null\n}\n\n[ \"USAGE: adb_su <commands> </dev/stdin >/dev/stdout 2>/dev/stderr\n\nReturns: true if the command running as root succeeded\" ]\nadb_su() {\n  adb_sh su root \"${@}\"\n}\n\n[ \"USAGE: adb_cat <file> >stdout\n\nReturns: content of file to stdout with carriage returns skipped,\n         true if the file exists\" ]\nadb_cat() {\n    local OUTPUT=\"`adb_sh cat ${1} </dev/null 2>&1`\"\n    local ret=${?}\n    echo \"${OUTPUT}\" | tr -d '\\r'\n    return ${ret}\n}\n\n[ \"USAGE: adb_test <expression>\n\nReturns: exit status of the test expression\" ]\nadb_test() {\n  adb_sh test \"${@}\" </dev/null\n}\n\n[ \"USAGE: adb_reboot\n\nReturns: true if the reboot command succeeded\" ]\nadb_reboot() {\n  avc_check\n  adb reboot remount-test </dev/null || true\n  sleep 2\n  adb_wait \"${ADB_WAIT}\"\n}\n\n[ \"USAGE: format_duration [<seconds>|<seconds>s|<minutes>m|<hours>h|<days>d]\n\nhuman readable output whole seconds, whole minutes or mm:ss\" ]\nformat_duration() {\n  if [ -z \"${1}\" ]; then\n    echo unknown\n    return\n  fi\n  local duration=\"${1}\"\n  if [ X\"${duration}\" != X\"${duration%s}\" ]; then\n    duration=${duration%s}\n  elif [ X\"${duration}\" != X\"${duration%m}\" ]; then\n    duration=$(( ${duration%m} * 60 ))\n  elif [ X\"${duration}\" != X\"${duration%h}\" ]; then\n    duration=$(( ${duration%h} * 3600 ))\n  elif [ X\"${duration}\" != X\"${duration%d}\" ]; then\n    duration=$(( ${duration%d} * 86400 ))\n  fi\n  local seconds=$(( ${duration} % 60 ))\n  local minutes=$(( ( ${duration} / 60 ) % 60 ))\n  local hours=$(( ${duration} / 3600 ))\n  if [ 0 -eq ${minutes} -a 0 -eq ${hours} ]; then\n    if [ 1 -eq ${duration} ]; then\n      echo 1 second\n      return\n    fi\n    echo ${duration} seconds\n    return\n  elif [ 60 -eq ${duration} ]; then\n    echo 1 minute\n    return\n  elif [ 0 -eq ${seconds} -a 0 -eq ${hours} ]; then\n    echo ${minutes} minutes\n    return\n  fi\n  if [ 0 -eq ${hours} ]; then\n    echo ${minutes}:$(( ${seconds} / 10 ))$(( ${seconds} % 10 ))\n    return\n  fi\n  echo ${hours}:$(( ${minutes} / 10 ))$(( ${minutes} % 10 )):$(( ${seconds} / 10 ))$(( ${seconds} % 10))\n}\n\n[ \"USAGE: USB_DEVICE=\\`usb_devnum [--next]\\`\n\nUSB_DEVICE contains cache. Update if system changes.\n\nReturns: the devnum for the USB_SERIAL device\" ]\nusb_devnum() {\n  if [ -n \"${USB_SERIAL}\" ]; then\n    local usb_device=`cat ${USB_SERIAL%/serial}/devnum 2>/dev/null | tr -d ' \\t\\r\\n'`\n    if [ -n \"${usb_device}\" ]; then\n      USB_DEVICE=dev${usb_device}\n    elif [ -n \"${USB_DEVICE}\" -a \"${1}\" ]; then\n      USB_DEVICE=dev$(( ${USB_DEVICE#dev} + 1 ))\n    fi\n    echo \"${USB_DEVICE}\"\n  fi\n}\n\n[ \"USAGE: adb_wait [timeout]\n\nReturns: waits until the device has returned for adb or optional timeout\" ]\nadb_wait() {\n  local start=`date +%s`\n  local duration=\n  local ret\n  if [ -n \"${1}\" -a -n \"`which timeout`\" ]; then\n    USB_DEVICE=`usb_devnum --next`\n    duration=`format_duration ${1}`\n    echo -n \". . . waiting ${duration}\" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} \"${CR}\" >&2\n    timeout --preserve-status --signal=KILL ${1} adb wait-for-device 2>/dev/null\n    ret=${?}\n    echo -n \"                                                                             ${CR}\" >&2\n  else\n    adb wait-for-device\n    ret=${?}\n  fi\n  USB_DEVICE=`usb_devnum`\n  if [ 0 = ${ret} -a -n \"${ACTIVE_SLOT}\" ]; then\n    local active_slot=`get_active_slot`\n    if [ X\"${ACTIVE_SLOT}\" != X\"${active_slot}\" ]; then\n      LOG WARNING \"Active slot changed from ${ACTIVE_SLOT} to ${active_slot}\"\n    fi\n  fi\n  local end=`date +%s`\n  local diff_time=$(( ${end} - ${start} ))\n  local _print_time=${print_time}\n  if [ ${diff_time} -lt 15 ]; then\n    _print_time=false\n  fi\n  diff_time=`format_duration ${diff_time}`\n  if [ \"${diff_time}\" = \"${duration}\" ]; then\n    _print_time=false\n  fi\n\n  local reason=\n  if inAdb; then\n    reason=`get_property ro.boot.bootreason`\n  fi\n  case ${reason} in\n    reboot*)\n      reason=\n      ;;\n    ${EMPTY})\n      ;;\n    *)\n      reason=\" for boot reason ${reason}\"\n      ;;\n  esac\n  if ${_print_time} || [ -n \"${reason}\" ]; then\n    LOG INFO \"adb wait duration ${diff_time}${reason}\"\n  fi\n\n  return ${ret}\n}\n\n[ \"USAGE: adb_user > /dev/stdout\n\nReturns: the adb daemon user\" ]\nadb_user() {\n  adb_sh echo '${USER}' </dev/null\n}\n\n[ \"USAGE: usb_status > stdout 2> stderr\n\nAssumes referenced right after adb_wait or fastboot_wait failued.\nIf wait failed, check if device is in adb, recovery or fastboot mode\nand report status strings like  \\\"(USB stack borken?)\\\",\n\\\"(In fastboot mode)\\\", \\\"(In recovery mode)\\\" or \\\"(in adb mode)\\\".\nAdditional diagnostics may be provided to the stderr output.\n\nReturns: USB status string\" ]\nusb_status() {\n  if inFastboot; then\n    echo \"(In fastboot mode)\"\n  elif inRecovery; then\n    echo \"(In recovery mode)\"\n  elif inAdb; then\n    echo \"(In adb mode `adb_user`)\"\n  else\n    echo \"(USB stack borken for ${USB_ADDRESS})\"\n    if [ -n \"`which usb_devnum`\" ]; then\n      USB_DEVICE=`usb_devnum`\n      if [ -n \"`which lsusb`\" ]; then\n        if [ -n \"${USB_DEVICE}\" ]; then\n          echo \"# lsusb -v -s ${USB_DEVICE#dev}\"\n          local D=`lsusb -v -s ${USB_DEVICE#dev} 2>&1`\n          if [ -n \"${D}\" ]; then\n            echo \"${D}\"\n          else\n            lsusb -v\n          fi\n        else\n          echo \"# lsusb -v (expected device missing)\"\n          lsusb -v\n        fi\n      fi\n    fi >&2\n  fi\n}\n\n[ \"USAGE: fastboot_wait [timeout]\n\nReturns: waits until the device has returned for fastboot or optional timeout\" ]\nfastboot_wait() {\n  local ret\n  # fastboot has no wait-for-device, but it does an automatic\n  # wait and requires (even a nonsensical) command to do so.\n  if [ -n \"${1}\" -a -n \"`which timeout`\" ]; then\n    USB_DEVICE=`usb_devnum --next`\n    echo -n \". . . waiting `format_duration ${1}`\" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} \"${CR}\"\n    timeout --preserve-status --signal=KILL ${1} fastboot wait-for-device >/dev/null 2>/dev/null\n    ret=${?}\n    echo -n \"                                                                             ${CR}\"\n    ( exit ${ret} )\n  else\n    fastboot wait-for-device >/dev/null 2>/dev/null\n  fi ||\n    inFastboot\n  ret=${?}\n  USB_DEVICE=`usb_devnum`\n  if [ 0 = ${ret} -a -n \"${ACTIVE_SLOT}\" ]; then\n    local active_slot=`get_active_slot`\n    if [ X\"${ACTIVE_SLOT}\" != X\"${active_slot}\" ]; then\n      LOG WARNING \"Active slot changed from ${ACTIVE_SLOT} to ${active_slot}\"\n    fi\n  fi\n  return ${ret}\n}\n\n[ \"USAGE: recovery_wait [timeout]\n\nReturns: waits until the device has returned for recovery or optional timeout\" ]\nrecovery_wait() {\n  local ret\n  if [ -n \"${1}\" -a -n \"`which timeout`\" ]; then\n    USB_DEVICE=`usb_devnum --next`\n    echo -n \". . . waiting `format_duration ${1}`\" ${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE} \"${CR}\"\n    timeout --preserve-status --signal=KILL ${1} adb wait-for-recovery 2>/dev/null\n    ret=${?}\n    echo -n \"                                                                             ${CR}\"\n  else\n    adb wait-for-recovery\n    ret=${?}\n  fi\n  USB_DEVICE=`usb_devnum`\n  if [ 0 = ${ret} -a -n \"${ACTIVE_SLOT}\" ]; then\n    local active_slot=`get_active_slot`\n    if [ X\"${ACTIVE_SLOT}\" != X\"${active_slot}\" ]; then\n      LOG WARNING \"Active slot changed from ${ACTIVE_SLOT} to ${active_slot}\"\n    fi\n  fi\n  return ${ret}\n}\n\n[ \"any_wait [timeout]\n\nReturns: waits until a device has returned or optional timeout\" ]\nany_wait() {\n  (\n    adb_wait ${1} &\n    adb_pid=${!}\n    fastboot_wait ${1} &\n    fastboot_pid=${!}\n    recovery_wait ${1} &\n    recovery_pid=${!}\n    wait -n\n    kill \"${adb_pid}\" \"${fastboot_pid}\" \"${recovery_pid}\"\n  ) >/dev/null 2>/dev/null\n  inFastboot || inAdb || inRecovery\n}\n\nwait_for_screen_timeout=900\n[ \"USAGE: wait_for_screen [-n] [TIMEOUT]\n\n-n - echo newline at exit\nTIMEOUT - default `format_duration ${wait_for_screen_timeout}`\" ]\nwait_for_screen() {\n  if ! ${screen_wait}; then\n    adb_wait\n    return\n  fi\n  exit_function=true\n  if [ X\"-n\" = X\"${1}\" ]; then\n    exit_function=echo\n    shift\n  fi\n  timeout=${wait_for_screen_timeout}\n  if [ ${#} -gt 0 ]; then\n    timeout=${1}\n    shift\n  fi\n  counter=0\n  while true; do\n    if inFastboot; then\n      fastboot reboot\n    elif inAdb; then\n      if [ 0 != ${counter} ]; then\n        adb_wait\n      fi\n      if [ \"1\" = \"`get_property sys.boot_completed`\" ]; then\n        sleep 1\n        break\n      fi\n    fi\n    counter=$(( ${counter} + 1 ))\n    if [ ${counter} -gt ${timeout} ]; then\n      ${exit_function}\n      LOG ERROR \"wait_for_screen() timed out ($(format_duration ${timeout}))\"\n      return 1\n    fi\n    sleep 1\n  done\n  ${exit_function}\n}\n\n[ \"USAGE: adb_root\n\nNB: This can be flakey on devices due to USB state\n\nReturns: true if device in root state\" ]\nadb_root() {\n  [ root != \"`adb_user`\" ] || return 0\n  adb root >/dev/null </dev/null 2>/dev/null\n  sleep 2\n  adb_wait ${ADB_WAIT} &&\n    [ root = \"`adb_user`\" ]\n}\n\n[ \"USAGE: adb_unroot\n\nNB: This can be flakey on devices due to USB state\n\nReturns: true if device in un root state\" ]\nadb_unroot() {\n  [ root = \"`adb_user`\" ] || return 0\n  adb unroot >/dev/null </dev/null 2>/dev/null\n  sleep 2\n  adb_wait ${ADB_WAIT} &&\n    [ root != \"`adb_user`\" ]\n}\n\n[ \"USAGE: fastboot_getvar var expected >/dev/stderr\n\nReturns: true if var output matches expected\" ]\nfastboot_getvar() {\n  local O=`fastboot getvar ${1} 2>&1`\n  local ret=${?}\n  O=\"${O#< waiting for * >?}\"\n  O=\"${O%%?Finished. Total time: *}\"\n  if [ 0 -ne ${ret} ]; then\n    echo ${O} >&2\n    false\n    return\n  fi\n  if [ \"${O}\" != \"${O#*FAILED}\" ]; then\n    O=\"${1}: <empty>\"\n  fi\n  if [ -n \"${2}\" -a \"${1}: ${2}\" != \"${O}\" ]; then\n    echo \"${2} != ${O}\"\n    false\n    return\n  fi >&2\n  echo ${O} >&2\n}\n\n[ \"USAGE: get_active_slot >/dev/stdout\n\nReturns: with a or b string reporting active slot\" ]\nget_active_slot() {\n  if inAdb || inRecovery; then\n    get_property ro.boot.slot_suffix | tr -d _\n  elif inFastboot; then\n    fastboot_getvar current-slot 2>&1 | sed -n 's/current-slot: //p'\n  else\n    false\n  fi\n}\n\n[ \"USAGE: restore\n\nDo nothing: should be redefined when necessary.\n\nReturns: reverses configurations\" ]\nrestore() {\n  true\n}\n\n[ \"USAGE: test_duration >/dev/stderr\n\nPrints the duration of the test\n\nReturns: reports duration\" ]\ntest_duration() {\n  if ${print_time}; then\n    LOG INFO \"end $(date)\"\n    [ -n \"${start_time}\" ] || return\n    end_time=`date +%s`\n    local diff_time=$(( ${end_time} - ${start_time} ))\n    LOG INFO \"duration $(format_duration ${diff_time})\"\n  fi\n}\n\n[ \"USAGE: die [-d|-t <epoch>] [message] >/dev/stderr\n\nIf -d, or -t <epoch> argument is supplied, dump logcat.\n\nReturns: exit failure, report status\" ]\ndie() {\n  if [ X\"-d\" = X\"${1}\" ]; then\n    adb_logcat -b all -v nsec -d\n    shift\n  elif [ X\"-t\" = X\"${1}\" ]; then\n    if [ -n \"${2}\" ]; then\n      adb_logcat -b all -v nsec -t ${2}\n    else\n      adb_logcat -b all -v nsec -d\n    fi\n    shift 2\n  fi >&2\n  LOG FAILED \"${@}\"\n  exit 1\n}\n\n[ \"USAGE: check_eq <lval> <rval> [--warning [message]]\n\nExits if (regex) lval mismatches rval.\n\nReturns: true if lval matches rval\" ]\ncheck_eq() {\n  local lval=\"${1}\"\n  local rval=\"${2}\"\n  shift 2\n  if [[ \"${rval}\" =~ ^${lval}$ ]]; then\n    return 0\n  fi\n\n  local error=true\n  local logt=ERROR\n  if [ X\"${1}\" = X\"--warning\" ]; then\n    shift 1\n    error=false\n    logt=WARNING\n  fi\n  if [ $(( ${#lval} + ${#rval} )) -gt 40 ]; then\n    LOG \"${logt}\" \"expected \\\"${lval}\\\"\n${INDENT}got      \\\"${rval}\\\"\"\n  else\n    LOG \"${logt}\" \"expected \\\"${lval}\\\" got \\\"${rval}\\\"\"\n  fi\n  ${error} && die \"${*}\"\n  [ -n \"${*}\" ] && LOG \"${logt}\" \"${*}\"\n  return 1\n}\n\n[ \"USAGE: check_ne <lval> <rval> [--warning [message]]\n\nExits if (regex) lval matches rval.\n\nReturns: true if lval mismatches rval\" ]\ncheck_ne() {\n  local lval=\"${1}\"\n  local rval=\"${2}\"\n  shift 2\n  if ! [[ \"${rval}\" =~ ^${lval}$ ]]; then\n    return 0\n  fi\n\n  local error=true\n  local logt=ERROR\n  if [ X\"${1}\" = X\"--warning\" ]; then\n      shift 1\n      error=false\n      logt=WARNING\n  fi\n  LOG \"${logt}\" \"unexpected \\\"${rval}\\\"\"\n  ${error} && die \"${*}\"\n  [ -n \"${*}\" ] && LOG \"${logt}\" \"${*}\"\n  return 1\n}\n\n[ \"USAGE: join_with <delimiter> <strings>\n\nJoins strings with delimiter\" ]\njoin_with() {\n  if [ \"${#}\" -lt 2 ]; then\n    echo\n    return\n  fi\n  local delimiter=\"${1}\"\n  local result=\"${2}\"\n  shift 2\n  for element in \"${@}\"; do\n    result+=\"${delimiter}${element}\"\n  done\n  echo \"${result}\"\n}\n\n[ \"USAGE: skip_administrative_mounts < /proc/mounts\n\nFilters out all administrative (eg: sysfs) mounts uninteresting to the test\" ]\nskip_administrative_mounts() {\n  local exclude_filesystems=(\n    \"overlay\" \"tmpfs\" \"none\" \"sysfs\" \"proc\" \"selinuxfs\" \"debugfs\" \"bpf\"\n    \"binfmt_misc\" \"cg2_bpf\" \"pstore\" \"tracefs\" \"adb\" \"mtp\" \"ptp\" \"devpts\"\n    \"ramdumpfs\" \"binder\" \"securityfs\" \"functionfs\" \"rootfs\" \"fuse\"\n  )\n  local exclude_devices=(\n    \"\\/sys\\/kernel\\/debug\" \"\\/data\\/media\" \"\\/dev\\/block\\/loop[0-9]*\"\n    \"\\/dev\\/block\\/vold\\/[^ ]+\"\n    \"${exclude_filesystems[@]}\"\n  )\n  local exclude_mount_points=(\n    \"\\/cache\" \"\\/mnt\\/scratch\" \"\\/mnt\\/vendor\\/persist\" \"\\/persist\"\n    \"\\/metadata\" \"\\/apex\\/[^ ]+\"\n  )\n  awk '$1 !~ /^('\"$(join_with \"|\" \"${exclude_devices[@]}\")\"')$/ &&\n      $2 !~ /^('\"$(join_with \"|\" \"${exclude_mount_points[@]}\")\"')$/ &&\n      $3 !~ /^('\"$(join_with \"|\" \"${exclude_filesystems[@]}\")\"')$/'\n}\n\n[ \"USAGE: surgically_wipe_overlayfs\n\nSurgically wipe any mounted overlayfs scratch files.\n\nReturns: true if wiped anything\" ]\nsurgically_wipe_overlayfs() {\n  local wiped_anything=false\n  for d in ${OVERLAYFS_BACKING}; do\n    if adb_su test -d \"/${d}/overlay\" </dev/null; then\n      LOG INFO \"/${d}/overlay is setup, surgically wiping\"\n      adb_su rm -rf \"/${d}/overlay\" </dev/null\n      wiped_anything=true\n    fi\n  done\n  ${wiped_anything}\n}\n\n[ \"USAGE: is_overlayfs_mounted [mountpoint]\n\nDiagnostic output of overlayfs df lines to stderr.\n\nReturns: true if overlayfs is mounted [on mountpoint]\" ]\nis_overlayfs_mounted() {\n  local df_output=$(adb_su df -k </dev/null)\n  local df_header_line=$(echo \"${df_output}\" | head -1)\n  # KISS (we do not support sub-mounts for system partitions currently)\n  local overlay_mounts=$(echo \"${df_output}\" | tail +2 |\n                         grep -vE \"[%] /(apex|system|vendor)/[^ ]+$\" |\n                         awk '$1 == \"overlay\" || $6 == \"/mnt/scratch\"')\n  if ! echo \"${overlay_mounts}\" | grep -q '^overlay '; then\n    return 1\n  fi >/dev/null 2>/dev/null\n  ( echo \"${df_header_line}\"\n    echo \"${overlay_mounts}\"\n  ) >&2\n  if [ \"${#}\" -gt 0 ] && ! ( echo \"${overlay_mounts}\" | grep -qE \" ${1}\\$\" ); then\n    return 1\n  fi >/dev/null 2>/dev/null\n  return 0\n}\n\n##\n##  MAINLINE\n##\n\nHOSTOS=`uname`\nGETOPTS=\"--alternative --unquoted\n         --longoptions help,serial:,colour,color,no-colour,no-color\n         --longoptions wait-adb:,wait-fastboot:\n         --longoptions wait-screen,wait-display\n         --longoptions no-wait-screen,no-wait-display\n         --longoptions gtest_print_time,print-time,no-print-time\n         --\"\nif [ \"Darwin\" = \"${HOSTOS}\" ]; then\n  GETOPTS=\n  USAGE=\"`echo \\\"${USAGE}\\\" |\n            sed 's/--color/       /g\n                 1s/--help/-h/\n                 s/--help/      /g\n                 s/--no-wait-screen/                /g\n                 s/--print-time/            /g\n                 1s/--serial/-s/\n                 s/--serial/        /g\n                 s/--wait-adb/          /g\n                 s/--wait-fastboot/               /g'`\"\nfi\nOPTIONS=`getopt ${GETOPTS} \"?a:cCdDf:hs:tT\" ${*}` ||\n  ( echo \"${USAGE}\" >&2 ; false ) ||\n  die \"getopt failure\"\nset -- ${OPTIONS}\n\nwhile [ ${#} -gt 0 ]; do\n  case ${1} in\n    -h | --help | -\\?)\n      echo \"${USAGE}\" >&2\n      exit 0\n      ;;\n    -s | --serial)\n      export ANDROID_SERIAL=${2}\n      shift\n      ;;\n    -c | --color | --colour)\n      color=true\n      ;;\n    -C | --no-color | --no-colour)\n      color=false\n      ;;\n    -D | --no-wait-display | --no-wait-screen)\n      screen_wait=false\n      ;;\n    -d | --wait-display | --wait-screen)\n      screen_wait=true\n      ;;\n    -t | --print-time | --gtest_print_time)\n      print_time=true\n      ;;\n    -T | --no-print-time)\n      print_time=false\n      ;;\n    -a | --wait-adb)\n      ADB_WAIT=${2}\n      shift\n      ;;\n    -f | --wait-fastboot)\n      FASTBOOT_WAIT=${2}\n      shift\n      ;;\n    --)\n      shift\n      break\n      ;;\n    -*)\n      echo \"${USAGE}\" >&2\n      die \"${0}: error unknown option ${1}\"\n      ;;\n    *)\n      break\n      ;;\n  esac\n  shift\ndone\n\nif ${color}; then\n  RED=\"${ESCAPE}[31m\"\n  GREEN=\"${ESCAPE}[32m\"\n  YELLOW=\"${ESCAPE}[33m\"\n  BLUE=\"${ESCAPE}[34m\"\n  NORMAL=\"${ESCAPE}[0m\"\nfi\n\nTMPDIR=\n\nexit_handler() {\n  [ -n \"${TMPDIR}\" ] && rm -rf \"${TMPDIR}\"\n  local err=0\n  if ! restore; then\n    LOG ERROR \"restore failed\"\n    err=1\n  fi >&2\n  test_duration || true\n  if [ \"${err}\" != 0 ]; then\n    exit \"${err}\"\n  fi\n}\ntrap 'exit_handler' EXIT\n\nTMPDIR=$(mktemp -d)\n\nif ${print_time}; then\n  LOG INFO \"start $(date)\"\nfi\n\nif [ -z \"${ANDROID_SERIAL}\" ]; then\n  inAdb || die \"no device or more than one device in adb mode\"\n  D=$(adb devices | awk '$2 == \"device\" { print $1; exit }')\n  [ -n \"${D}\" ] || die \"cannot get device serial\"\n  ANDROID_SERIAL=\"${D}\"\nfi\nexport ANDROID_SERIAL\n\ninFastboot && die \"device in fastboot mode\"\ninRecovery && die \"device in recovery mode\"\nif ! inAdb; then\n  LOG WARNING \"device not in adb mode\"\n  adb_wait ${ADB_WAIT}\nfi\ninAdb || die \"specified device not in adb mode\"\n[ \"1\" = \"$(get_property ro.debuggable)\" ] || die \"device not a debug build\"\n[ \"orange\" = \"$(get_property ro.boot.verifiedbootstate)\" ] || die \"device not bootloader unlocked\"\n\n################################################################################\n# Collect characteristics of the device and report.\ncan_restore_verity=true\nif [ \"2\" != \"$(get_property partition.system.verified)\" ]; then\n  LOG WARNING \"device might not support verity\"\n  can_restore_verity=false\nfi\nenforcing=true\nif ! adb_su getenforce </dev/null | grep 'Enforcing' >/dev/null; then\n  LOG WARNING \"device does not have sepolicy in enforcing mode\"\n  enforcing=false\nfi\n\nUSB_SERIAL=\nif [ -n \"${ANDROID_SERIAL}\" -a \"Darwin\" != \"${HOSTOS}\" ]; then\n  USB_SERIAL=\"`find /sys/devices -name serial | grep usb || true`\"\n  if [ -n \"${USB_SERIAL}\" ]; then\n    USB_SERIAL=`echo \"${USB_SERIAL}\" |\n                  xargs grep -l ${ANDROID_SERIAL} || true`\n  fi\nfi\nUSB_ADDRESS=\nif [ -n \"${USB_SERIAL}\" ]; then\n  USB_ADDRESS=${USB_SERIAL%/serial}\n  USB_ADDRESS=usb${USB_ADDRESS##*/}\nfi\nUSB_DEVICE=$(usb_devnum)\n[ -z \"${ANDROID_SERIAL}${USB_ADDRESS}${USB_DEVICE}\" ] ||\n  LOG INFO \"${ANDROID_SERIAL} ${USB_ADDRESS} ${USB_DEVICE}\"\nBUILD_DESCRIPTION=`get_property ro.build.description`\n[ -z \"${BUILD_DESCRIPTION}\" ] ||\n  LOG INFO \"${BUILD_DESCRIPTION}\"\nKERNEL_VERSION=\"`adb_su cat /proc/version </dev/null 2>/dev/null`\"\n[ -z \"${KERNEL_VERSION}\" ] ||\n  LOG INFO \"${KERNEL_VERSION}\"\nACTIVE_SLOT=`get_active_slot`\n[ -z \"${ACTIVE_SLOT}\" ] ||\n  LOG INFO \"active slot is ${ACTIVE_SLOT}\"\n\n# Acquire list of system partitions\nFSTAB_SUFFIXES=(\n  \"$(get_property ro.boot.fstab_suffix)\"\n  \"$(get_property ro.boot.hardware)\"\n  \"$(get_property ro.boot.hardware.platform)\"\n)\nFSTAB_PATTERN='\\.('\"$(join_with \"|\" \"${FSTAB_SUFFIXES[@]}\")\"')$'\nFSTAB_FILE=$(adb_su ls -1 '/vendor/etc/fstab*' </dev/null |\n             grep -E \"${FSTAB_PATTERN}\" |\n             head -1)\n\n# KISS (assume system partition mount point is \"/<partition name>\")\nif [ -n \"${FSTAB_FILE}\" ]; then\n  PARTITIONS=$(adb_su grep -v \"^[#${SPACE}${TAB}]\" \"${FSTAB_FILE}\" |\n               skip_administrative_mounts |\n               awk '$1 ~ /^[^\\/]+$/ && \"/\"$1 == $2 && $4 ~ /(^|,)ro(,|$)/ { print $1 }' |\n               sort -u |\n               tr '\\n' ' ')\nelse\n  PARTITIONS=\"system vendor\"\nfi\n\n# KISS (we do not support sub-mounts for system partitions currently)\n# Ensure /system and /vendor mountpoints are in mounts list\nMOUNTS=$(for i in system vendor ${PARTITIONS}; do\n           echo \"/${i}\"\n         done | sort -u | tr '\\n' ' ')\nLOG INFO \"System Partitions list: ${PARTITIONS}\"\n\n# Report existing partition sizes\nadb_sh ls -l /dev/block/by-name/ /dev/block/mapper/ </dev/null 2>/dev/null |\n  sed -n 's@.* \\([^ ]*\\) -> /dev/block/\\([^ ]*\\)$@\\1 \\2@p' |\n  while read name device; do\n    [ super = ${name} -o cache = ${name} ] ||\n      (\n        for i in ${PARTITIONS}; do\n          [ ${i} = ${name} -o ${i} = ${name%_[ab]} ] && exit\n        done\n        exit 1\n      ) ||\n      continue\n\n    case ${device} in\n      sd*)\n        device=${device%%[0-9]*}/${device}\n        ;;\n    esac\n    size=`adb_su cat /sys/block/${device}/size 2>/dev/null </dev/null` &&\n      size=$(( ${size} / 2 )) &&\n      LOG INFO \"partition ${name} device ${device} size ${size}K\"\n  done\n\nrestore() {\n  LOG INFO \"restoring device\"\n  inFastboot &&\n    fastboot reboot &&\n    adb_wait \"${ADB_WAIT}\" ||\n    true\n  if ! inAdb; then\n    LOG ERROR \"expect adb device\"\n    return 1\n  fi\n  adb_root || true\n  local reboot=false\n  if surgically_wipe_overlayfs; then\n    reboot=true\n  fi\n  if ${can_restore_verity}; then\n    if ! adb enable-verity; then\n      LOG ERROR \"adb enable-verity\"\n      return 1\n    fi\n    LOG INFO \"restored verity\"\n    reboot=true\n  fi >&2\n  if ${reboot}; then\n    adb_reboot\n  fi\n}\n\n# If reboot too soon after fresh flash, could trip device update failure logic\nif ${screen_wait}; then\n  LOG INFO \"waiting for screen to come up. Consider --no-wait-screen option\"\nfi\nif ! wait_for_screen && ${screen_wait}; then\n  screen_wait=false\n  LOG WARNING \"not healthy, no launcher, skipping wait for screen\"\nfi\n\n################################################################################\nLOG RUN \"Checking current overlayfs status\"\n\nadb_wait || die \"wait for device failed\"\nadb_root || die \"adb root failed\"\n\n# We can not universally use adb enable-verity to ensure device is\n# in a overlayfs disabled state since it can prevent reboot on\n# devices that remount the physical content rather than overlayfs.\n# So lets do our best to surgically wipe the overlayfs state without\n# having to go through enable-verity transition.\nif surgically_wipe_overlayfs; then\n  LOG WARNING \"rebooting before test\"\n  adb_reboot ||\n    die \"lost device after reboot after overlay wipe $(usb_status)\"\n  adb_root ||\n    die \"lost device after elevation to root after wipe `usb_status`\"\nfi\nis_overlayfs_mounted &&\n  die \"overlay takeover unexpected at this phase\"\n\noverlayfs_needed=true\ndata_device=$(adb_sh awk '$2 == \"/data\" { print $1; exit }' /proc/mounts)\nD=$(adb_sh grep \" ro,\" /proc/mounts </dev/null |\n    grep -v \"^${data_device}\" |\n    skip_administrative_mounts |\n    awk '{ print $1 }' |\n    sed 's|/dev/root|/|' |\n    sort -u)\nno_dedupe=true\nfor d in ${D}; do\n  adb_sh tune2fs -l $d </dev/null 2>&1 |\n    grep \"Filesystem features:.*shared_blocks\" >/dev/null &&\n  no_dedupe=false\ndone\nD=$(adb_sh df -k ${D} </dev/null)\necho \"${D}\" >&2\nif [ X\"${D}\" = X\"${D##* 100[%] }\" ] && ${no_dedupe} ; then\n  overlayfs_needed=false\n  # if device does not need overlays, then adb enable-verity will brick device\n  can_restore_verity=false\nfi\nLOG OK \"no overlay present before setup\"\n\n################################################################################\n# Precondition is overlayfs *not* setup.\nLOG RUN \"Testing adb disable-verity -R\"\n\nT=$(adb_date)\nadb_su disable-verity -R >&2\nerr=${?}\n[[ ${err} -eq 0 || ${err} -eq 255 ]] ||\n  die -t \"${T}\" \"disable-verity -R failed\"\nsleep 2\nadb_wait \"${ADB_WAIT}\" ||\n  die \"lost device after adb disable-verity -R $(usb_status)\"\n\nif [ \"2\" = \"$(get_property partition.system.verified)\" ]; then\n  LOG ERROR \"partition.system.verified=$(get_property partition.system.verified)\"\n  die \"verity not disabled after adb disable-verity -R\"\nfi\nif ${overlayfs_needed}; then\n  is_overlayfs_mounted ||\n    die -d \"no overlay takeover after adb disable-verity -R\"\n  LOG OK \"overlay takeover after adb disable-verity -R\"\nfi\nLOG OK \"adb disable-verity -R\"\n\n################################################################################\nLOG RUN \"Checking kernel has overlayfs required patches\"\n\nadb_root || die \"adb root\"\nif adb_test -d /sys/module/overlay ||\n    adb_sh grep -q \"nodev${TAB}overlay\" /proc/filesystems; then\n  LOG OK \"overlay module present\"\nelse\n  LOG INFO \"overlay module not present\"\nfi\nif is_overlayfs_mounted 2>/dev/null; then\n  if adb_test -f /sys/module/overlay/parameters/override_creds; then\n    LOG OK \"overlay module supports override_creds\"\n  else\n    case \"$(adb_sh uname -r </dev/null)\" in\n      4.[456789].* | 4.[1-9][0-9]* | [56789].*)\n        die \"overlay module does not support override_creds\"\n        ;;\n      *)\n        LOG OK \"overlay module uses caller's creds\"\n        ;;\n    esac\n  fi\nfi\n\n################################################################################\n# Precondition is a verity-disabled device with overlayfs already setup.\nLOG RUN \"Testing raw remount commands\"\n\nadb_sh grep -qE \" (/system|/) [^ ]* rw,\" /proc/mounts </dev/null &&\n  die \"/system is not RO\"\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null &&\n  die \"/vendor is not RO\"\n\nT=$(adb_date)\nadb_su mount -o remount,rw /vendor ||\n  die -t \"${T}\" \"mount -o remount,rw /vendor\"\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null ||\n  die \"/vendor is not RW after mount -o remount,rw\"\nLOG OK \"mount -o remount,rw\"\n\nT=$(adb_date)\nadb_su mount -o remount,ro /vendor ||\n  die -t \"${T}\" \"mount -o remount,ro /vendor\"\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null &&\n  die \"/vendor is not RO after mount -o remount,ro\"\nLOG OK \"mount -o remount,ro\"\n\nT=$(adb_date)\nadb_su remount vendor >&2 ||\n  die -t \"${T}\" \"adb remount vendor\"\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null ||\n  die -t \"${T}\" \"/vendor is not RW after adb remount vendor\"\nadb_sh grep -qE \" (/system|/) [^ ]* rw,\" /proc/mounts </dev/null &&\n  die -t \"${T}\" \"/system is not RO after adb remount vendor\"\nLOG OK \"adb remount vendor\"\n\nLOG INFO \"Restoring device RO state and destroying overlayfs\"\nT=$(adb_date)\nadb_su mount -o remount,ro /vendor ||\n  die -t \"${T}\" \"mount -o remount,ro /vendor\"\nif surgically_wipe_overlayfs; then\n  adb_reboot ||\n    die \"lost device after reboot after overlay wipe $(usb_status)\"\nfi\nis_overlayfs_mounted &&\n  die \"overlay takeover unexpected at this phase\"\n\n################################################################################\n# Precondition is a verity-disabled device with overlayfs *not* setup.\nLOG RUN \"Testing adb remount performs overlayfs setup from scratch\"\n\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null &&\n  die \"/vendor is not RO\"\nT=$(adb_date)\nadb_su remount vendor >&2 ||\n  die -t \"${T}\" \"adb remount vendor from scratch\"\nif ${overlayfs_needed}; then\n  is_overlayfs_mounted /vendor ||\n    die -t \"${T}\" \"expected overlay takeover /vendor\"\n  is_overlayfs_mounted /system 2>/dev/null &&\n    die -t \"${T}\" \"unexpected overlay takeover /system\"\nfi\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null ||\n  die -t \"${T}\" \"/vendor is not RW after adb remount vendor\"\nadb_sh grep -qE \" (/system|/) [^ ]* rw,\" /proc/mounts </dev/null &&\n  die -t \"${T}\" \"/system is not RO after adb remount vendor\"\nLOG OK \"adb remount from scratch\"\n\n################################################################################\n# Precondition is overlayfs partially setup by previous test.\nLOG RUN \"Testing adb remount -R\"\n\nT=$(adb_date)\nadb_su remount -R </dev/null >&2\nerr=${?}\n[[ ${err} -eq 0 || ${err} -eq 255 ]] ||\n  die -t \"${T}\" \"adb remount -R failed\"\nsleep 2\nadb_wait \"${ADB_WAIT}\" ||\n  die \"lost device after adb remount -R $(usb_status)\"\n\nif [ \"2\" = \"$(get_property partition.system.verified)\" ]; then\n  LOG ERROR \"partition.system.verified=$(get_property partition.system.verified)\"\n  die \"verity not disabled after adb remount -R\"\nfi\nif ${overlayfs_needed}; then\n  is_overlayfs_mounted /system ||\n    die -d \"expected overlay takeover /system\"\n  is_overlayfs_mounted /vendor 2>/dev/null ||\n    die -d \"expected overlay takeover /vendor\"\n  LOG OK \"overlay takeover after adb remount -R\"\nfi\nLOG OK \"adb remount -R\"\n\n# For devices using overlayfs, remount -R should reboot after overlayfs setup.\n# For legacy device, manual reboot to ensure device clean state.\nif ! ${overlayfs_needed}; then\n  LOG WARNING \"Reboot to RO (device doesn't use overlayfs)\"\n  adb_reboot ||\n    die \"lost device after reboot to RO $(usb_status)\"\nfi\n\n################################################################################\n# Precondition is a verity-disabled device with overlayfs already setup.\nLOG RUN \"Testing adb remount RW\"\n\n# Feed log with selinux denials as baseline before overlays\nadb_unroot\nadb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true\nadb_root\n\nadb_sh grep -qE \" (/system|/) [^ ]* rw,\" /proc/mounts </dev/null &&\n  die \"/system is not RO\"\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null &&\n  die \"/vendor is not RO\"\n\ndata_device=$(adb_sh awk '$2 == \"/data\" { print $1; exit }' /proc/mounts)\nRO=$(adb_sh grep \" ro,\" /proc/mounts </dev/null |\n    grep -v \"^${data_device}\" |\n    skip_administrative_mounts |\n    awk '{ print $1 }')\n\nT=$(adb_date)\nadb remount >&2 ||\n  die -t \"${T}\" \"adb remount\"\nadb_sh grep -qE \" (/system|/) [^ ]* rw,\" /proc/mounts </dev/null ||\n  die -t \"${T}\" \"/system is not RW\"\nadb_sh grep -q \" /vendor [^ ]* rw,\" /proc/mounts </dev/null ||\n  die -t \"${T}\" \"/vendor is not RW\"\n\n# Only find mounts that are remounted RO -> RW\nRW=$(adb_sh grep \" rw,\" /proc/mounts </dev/null |\n    grep -v \"^${data_device}\" |\n    skip_administrative_mounts |\n    grep -E \"^($(join_with '|' ${RO})) \")\n\nscratch_on_super=false\nif ${overlayfs_needed}; then\n  is_overlayfs_mounted /system ||\n    die -t \"${T}\" \"expected overlay to takeover /system after remount\"\n\n  # Collect information about the scratch device if we have one\n  M=$(adb_sh cat /proc/mounts </dev/null |\n      awk '$2 == \"/mnt/scratch\" { print $1, $3; exit }')\n  if [ -n \"${M}\" ]; then\n    scratch_device=$(echo \"${M}\" | awk '{ print $1 }')\n    scratch_filesystem=$(echo \"${M}\" | awk '{ print $2 }')\n    scratch_size=$(adb_sh df -k \"${scratch_device}\" </dev/null |\n                  tail +2 | head -1 | awk '{ print $2 }')\n    [ -z \"${scratch_size}\" ] && die \"cannot get size of scratch device (${scratch_device})\"\n\n    # Detect scratch partition backed by super?\n    for b in \"/dev/block/by-name/super\"{,_${ACTIVE_SLOT}}; do\n      if adb_test -e \"${b}\"; then\n        device=$(adb_su realpath \"${b}\")\n        D=$(adb_su stat -c '0x%t 0x%T' \"${device}\")\n        major=$(echo \"${D}\" | awk '{ print $1 }')\n        minor=$(echo \"${D}\" | awk '{ print $2 }')\n        super_devt=$(( major )):$(( minor ))\n        if adb_su dmctl table scratch | tail +2 | grep -q -w \"${super_devt}\"; then\n          scratch_on_super=true\n        fi\n        break\n      fi\n    done\n\n    if ${scratch_on_super}; then\n      LOG INFO \"using dynamic scratch partition on super\"\n    else\n      LOG INFO \"using dynamic scratch partition on /data (VAB device)\"\n    fi\n    LOG INFO \"scratch device ${scratch_device} filesystem ${scratch_filesystem} size ${scratch_size}KiB\"\n  else\n    LOG INFO \"cannot find any scratch device mounted on /mnt/scratch, using scratch on /cache\"\n  fi\n\n  for d in ${OVERLAYFS_BACKING}; do\n    if adb_test -d /${d}/overlay/system/upper; then\n      LOG INFO \"/${d}/overlay is setup\"\n    fi\n  done\n\n  # KISS (we do not support sub-mounts for system partitions currently)\n  adb_sh grep \"^overlay \" /proc/mounts </dev/null |\n    grep -vE \"^overlay.* /(apex|system|vendor)/[^ ]\" |\n    grep \" overlay ro,\" &&\n    die \"expected overlay to be RW after remount\"\n\n  D=$(echo \"${RW}\" |\n      awk '{ print $1 }' |\n      sed 's|/dev/root|/|' |\n      sort -u)\n  if [ -n \"${D}\" ]; then\n    adb_sh df -k ${D} </dev/null |\n      sed -e 's/^Filesystem     /Filesystem (rw)/'\n  fi >&2\n  for d in ${D}; do\n    if adb_sh tune2fs -l \"${d}\" </dev/null 2>&1 | grep -q \"Filesystem features:.*shared_blocks\" ||\n        adb_sh df -k \"${d}\" | grep -q \" 100% \"; then\n      # See b/397158623\n      # The new overlayfs mounter is a bit more limited due to sepolicy. Since we know of no use\n      # cases for these mounts, disabling for now\n      LOG OK \"remount overlayfs missed a spot (rw)\"\n    fi\n  done\nelse\n  is_overlayfs_mounted && die -t \"${T}\" \"unexpected overlay takeover\"\nfi\n\necho -n \"${RW}\" |\n  grep -v noatime &&\n  die \"mounts (rw) are not noatime\"\n\nLOG OK \"adb remount RW\"\n\n################################################################################\nLOG RUN \"push content to ${MOUNTS}\"\n\nadb_root || die \"adb root\"\nA=\"Hello World! $(date)\"\nfor i in ${MOUNTS} /system/priv-app; do\n  echo \"${A}\" | adb_sh cat - \">${i}/hello\"\n  B=\"`adb_cat ${i}/hello`\" ||\n    die \"${i#/} hello\"\n  check_eq \"${A}\" \"${B}\" ${i} before reboot\ndone\nSYSTEM_INO=`adb_sh stat --format=%i /system/hello </dev/null`\nVENDOR_INO=`adb_sh stat --format=%i /vendor/hello </dev/null`\ncheck_ne \"${SYSTEM_INO}\" \"${VENDOR_INO}\" vendor and system inode\n\n# Edit build.prop and check if properties are updated.\nsystem_build_prop_original=\"${TMPDIR}/system_build.prop.original\"\nsystem_build_prop_modified=\"${TMPDIR}/system_build.prop.modified\"\nsystem_build_prop_fromdevice=\"${TMPDIR}/system_build.prop.fromdevice\"\nadb pull /system/build.prop \"${system_build_prop_original}\" >/dev/null ||\n  die \"adb pull /system/build.prop\"\n# Prepend with extra newline in case the original file doesn't end with a newline.\ncat \"${system_build_prop_original}\" - <<EOF >\"${system_build_prop_modified}\"\n\n# Properties added by adb remount test\ntest.adb.remount.system.build.prop=true\nEOF\n\n# Move /system/build.prop to make sure we can move and then replace files\n# Note that as of kernel 6.1 mv creates the char_file that whites out the lower\n# file with different selabels than rm does\n# See b/394290609\nadb shell mv /system/build.prop /system/build.prop.backup >/dev/null ||\n  die \"adb shell rm /system/build.prop\"\n\nadb push \"${system_build_prop_modified}\" /system/build.prop >/dev/null ||\n  die \"adb push /system/build.prop\"\nadb pull /system/build.prop \"${system_build_prop_fromdevice}\" >/dev/null ||\n  die \"adb pull /system/build.prop\"\ndiff \"${system_build_prop_modified}\" \"${system_build_prop_fromdevice}\" >/dev/null ||\n  die \"/system/build.prop differs from pushed content\"\n\n################################################################################\nLOG RUN \"reboot to confirm content persistent\"\n\nfixup_from_recovery() {\n  inRecovery || return 1\n  LOG ERROR \"Device in recovery\"\n  adb reboot </dev/null\n  adb_wait ${ADB_WAIT}\n}\n\nadb_reboot ||\n  fixup_from_recovery ||\n  die \"reboot after override content added failed `usb_status`\"\n\nif ${overlayfs_needed}; then\n  is_overlayfs_mounted ||\n    die -d \"overlay takeover failed after reboot\"\n\n  adb_su sed -n '1,/overlay \\/system/p' /proc/mounts </dev/null |\n    skip_administrative_mounts |\n    grep -v ' \\(erofs\\|squashfs\\|ext4\\|f2fs\\|vfat\\) ' &&\n    LOG WARNING \"overlay takeover after first stage init\" ||\n    LOG OK \"overlay takeover in first stage init\"\nfi\n\nif ${enforcing}; then\n  adb_unroot ||\n    die \"device not in unroot'd state\"\n  B=\"`adb_cat /vendor/hello 2>&1`\"\n  check_eq \"cat: /vendor/hello: Permission denied\" \"${B}\" vendor after reboot w/o root\n  LOG OK \"/vendor content correct MAC after reboot\"\n  # Feed unprivileged log with selinux denials as a result of overlays\n  wait_for_screen\n  adb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true\nfi\n# If overlayfs has a nested security problem, this will fail.\nadb_sh ls /system >/dev/null || die \"ls /system\"\nadb_test -d /system/priv-app || die \"[ -d /system/priv-app ]\"\nB=\"`adb_cat /system/priv-app/hello`\"\ncheck_eq \"${A}\" \"${B}\" /system/priv-app after reboot\n\n# Only root can read vendor if sepolicy permissions are as expected.\nadb_root || die \"adb root\"\nfor i in ${MOUNTS}; do\n  B=\"`adb_cat ${i}/hello`\"\n  check_eq \"${A}\" \"${B}\" ${i#/} after reboot\n  LOG OK \"${i} content remains after reboot\"\ndone\n\ncheck_eq \"${SYSTEM_INO}\" \"`adb_sh stat --format=%i /system/hello </dev/null`\" system inode after reboot\ncheck_eq \"${VENDOR_INO}\" \"`adb_sh stat --format=%i /vendor/hello </dev/null`\" vendor inode after reboot\n\n# Feed log with selinux denials as a result of overlays\nadb_sh find ${MOUNTS} </dev/null >/dev/null 2>/dev/null || true\n\n# Check if the updated build.prop is persistent after reboot.\ncheck_eq \"true\" \"$(get_property 'test.adb.remount.system.build.prop')\" \"load modified build.prop\"\nadb pull /system/build.prop \"${system_build_prop_fromdevice}\" >/dev/null ||\n  die \"adb pull /system/build.prop\"\ndiff \"${system_build_prop_modified}\" \"${system_build_prop_fromdevice}\" >/dev/null ||\n  die \"/system/build.prop differs from pushed content\"\nLOG OK \"/system/build.prop content remains after reboot\"\n\n################################################################################\nLOG RUN \"flash vendor, and confirm vendor override disappears\"\n\nis_bootloader_fastboot=true\n# virtual device?\ncase \"$(get_property ro.product.vendor.device)\" in\n  vsoc_* | emulator_* | emulator64_*)\n    is_bootloader_fastboot=false\n    ;;\nesac\nis_userspace_fastboot=false\n\nif ! ${is_bootloader_fastboot}; then\n  LOG WARNING \"does not support fastboot flash, skipping\"\nelse\n  wait_for_screen\n  adb_root || die \"adb root\"\n\n  VENDOR_DEVICE_CANDIDATES=(\n    \"/dev/block/mapper/vendor\"{_${ACTIVE_SLOT},}\n    \"/dev/block/by-name/vendor\"{_${ACTIVE_SLOT},}\n  )\n  for b in \"${VENDOR_DEVICE_CANDIDATES[@]}\"; do\n    if adb_test -e \"${b}\"; then\n      adb pull \"${b}\" \"${TMPDIR}/vendor.img\" || die \"adb pull ${b}\"\n      LOG INFO \"pulled ${b} from device as vendor.img\"\n      break\n    fi\n  done\n  [ -f \"${TMPDIR}/vendor.img\" ] ||\n    die \"cannot find block device of vendor partition\"\n\n  avc_check\n  adb reboot fastboot </dev/null ||\n    die \"fastbootd not supported (wrong adb in path?)\"\n  any_wait ${ADB_WAIT} &&\n    inFastboot ||\n    die \"reboot into fastboot to flash vendor `usb_status` (bad bootloader?)\"\n  fastboot flash vendor \"${TMPDIR}/vendor.img\" ||\n    ( fastboot reboot && false) ||\n    die \"fastboot flash vendor\"\n  LOG OK \"flashed vendor\"\n\n  fastboot_getvar is-userspace yes &&\n    is_userspace_fastboot=true\n\n  if ${scratch_on_super}; then\n    fastboot_getvar partition-type:scratch raw ||\n      die \"fastboot cannot see parameter partition-type:scratch\"\n    fastboot_getvar has-slot:scratch no ||\n      die \"fastboot cannot see parameter has-slot:scratch\"\n    fastboot_getvar is-logical:scratch yes ||\n      die \"fastboot cannot see parameter is-logical:scratch\"\n    LOG INFO \"expect fastboot erase scratch to fail\"\n    fastboot erase scratch && die \"fastboot can erase scratch\"\n    LOG INFO \"expect fastboot format scratch to fail\"\n    fastboot format scratch && die \"fastboot can format scratch\"\n  fi\n\n  fastboot reboot || die \"cannot reboot out of fastboot\"\n  LOG INFO \"reboot from fastboot\"\n  adb_wait ${ADB_WAIT} ||\n    fixup_from_recovery ||\n    die \"cannot reboot after flash vendor $(usb_status)\"\n  if ${overlayfs_needed}; then\n    is_overlayfs_mounted /system ||\n      die  \"overlay /system takeover after flash vendor\"\n    if is_overlayfs_mounted /vendor 2>/dev/null; then\n      if ${is_userspace_fastboot}; then\n        die  \"overlay supposed to be minus /vendor takeover after flash vendor\"\n      else\n        LOG WARNING \"fastbootd missing required to invalidate, ignoring a failure\"\n        LOG WARNING \"overlay supposed to be minus /vendor takeover after flash vendor\"\n      fi\n    fi\n  fi\n  check_eq \"${A}\" \"$(adb_cat /system/hello)\" \"/system content after flash vendor\"\n  check_eq \"${SYSTEM_INO}\" \"$(adb_sh stat --format=%i /system/hello </dev/null)\" \"system inode after flash vendor\"\n  adb_sh ls /system >/dev/null || die \"ls /system\"\n  adb_test -d /system/priv-app || die \"[ -d /system/priv-app ]\"\n  check_eq \"${A}\" \"$(adb_cat /system/priv-app/hello)\" \"/system/priv-app content after flash vendor\"\n  adb_root || die \"adb root\"\n  if adb_test -e /vendor/hello; then\n    if ${is_userspace_fastboot} || ! ${overlayfs_needed}; then\n      die \"vendor content after flash vendor\"\n    else\n      LOG WARNING \"fastbootd missing required to invalidate, ignoring a failure\"\n      LOG WARNING \"vendor content after flash vendor\"\n    fi\n  fi\n  LOG OK \"vendor override destroyed after flash verdor\"\nfi >&2\n\nwait_for_screen\n\n################################################################################\nLOG RUN \"Clean up test content\"\n\nadb_root || die \"adb root\"\nT=$(adb_date)\nD=$(adb remount 2>&1) ||\n  die -t \"${T}\" \"adb remount\"\necho \"${D}\" >&2\nif [[ \"${D}\" =~ [Rr]eboot ]]; then\n  LOG OK \"adb remount calls for a reboot after partial flash\"\n  # but we don't really want to, since rebooting just recreates the already tore\n  # down vendor overlay.\nfi\n\nfor i in ${MOUNTS} /system/priv-app; do\n  adb_sh rm \"${i}/hello\" 2>/dev/null || true\n  adb_test -e \"${i}/hello\" &&\n    die -t \"${T}\" \"/${i}/hello lingers after rm\"\ndone\n\n################################################################################\nif ${is_bootloader_fastboot} && ${scratch_on_super}; then\n\n  LOG RUN \"test fastboot flash to scratch recovery\"\n\n  avc_check\n  adb reboot fastboot </dev/null ||\n    die \"Reboot into fastbootd\"\n  img=\"${TMPDIR}/adb-remount-test-${$}.img\"\n  dd if=/dev/zero of=${img} bs=4096 count=16 2>/dev/null &&\n    fastboot_wait ${FASTBOOT_WAIT} ||\n    die \"reboot into fastboot to flash scratch `usb_status`\"\n  fastboot flash --force scratch ${img}\n  err=${?}\n  fastboot reboot ||\n    die \"can not reboot out of fastboot\"\n  [ 0 -eq ${err} ] ||\n    die \"fastboot flash scratch\"\n  adb_wait ${ADB_WAIT} &&\n    adb_root ||\n    die \"did not reboot after flashing empty scratch $(usb_status)\"\n  T=`adb_date`\n  D=`adb disable-verity 2>&1`\n  err=${?}\n  if [ X\"${D}\" != \"${D%?Now reboot your device for settings to take effect*}\" ]\n  then\n    LOG WARNING \"adb disable-verity requires a reboot after partial flash\"\n    adb_reboot &&\n      adb_root ||\n      die \"failed to reboot\"\n    T=`adb_date`\n    D=\"${D}\n`adb disable-verity 2>&1`\"\n    err=${?}\n  fi\n\n  echo \"${D}\" >&2\n  [ ${err} = 0 ] &&\n    [ X\"${D}\" = X\"${D##*setup failed}\" ] &&\n    [ X\"${D}\" != X\"${D##*[Uu]sing overlayfs}\" ] &&\n    LOG OK \"recreated scratch\" ||\n    die -t ${T} \"setup for overlayfs\"\n  adb remount >&2 ||\n    die -t ${T} \"remount failed\"\nfi\n\n\nLOG PASSED \"adb remount test\"\n"
  },
  {
    "path": "fs_mgr/tests/file_wait_test.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <chrono>\n#include <string>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/unique_fd.h>\n#include <fs_mgr/file_wait.h>\n#include <gtest/gtest.h>\n\nusing namespace std::literals;\nusing android::base::unique_fd;\nusing android::fs_mgr::WaitForFile;\nusing android::fs_mgr::WaitForFileDeleted;\n\nclass FileWaitTest : public ::testing::Test {\n  protected:\n    void SetUp() override {\n        const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();\n        test_file_ = temp_dir_.path + \"/\"s + tinfo->name();\n    }\n\n    void TearDown() override { unlink(test_file_.c_str()); }\n\n    TemporaryDir temp_dir_;\n    std::string test_file_;\n};\n\nTEST_F(FileWaitTest, FileExists) {\n    unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));\n    ASSERT_GE(fd, 0);\n\n    ASSERT_TRUE(WaitForFile(test_file_, 500ms));\n    ASSERT_FALSE(WaitForFileDeleted(test_file_, 500ms));\n}\n\nTEST_F(FileWaitTest, FileDoesNotExist) {\n    ASSERT_FALSE(WaitForFile(test_file_, 500ms));\n    ASSERT_TRUE(WaitForFileDeleted(test_file_, 500ms));\n}\n\nTEST_F(FileWaitTest, CreateAsync) {\n    std::thread thread([this] {\n        std::this_thread::sleep_for(std::chrono::seconds(1));\n        unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));\n    });\n    EXPECT_TRUE(WaitForFile(test_file_, 3s));\n    thread.join();\n}\n\nTEST_F(FileWaitTest, CreateOtherAsync) {\n    std::thread thread([this] {\n        std::this_thread::sleep_for(std::chrono::seconds(1));\n        unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));\n    });\n    EXPECT_FALSE(WaitForFile(test_file_ + \".wontexist\", 2s));\n    thread.join();\n}\n\nTEST_F(FileWaitTest, DeleteAsync) {\n    // Note: need to close the file, otherwise inotify considers it not deleted.\n    {\n        unique_fd fd(open(test_file_.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0700));\n        ASSERT_GE(fd, 0);\n    }\n\n    std::thread thread([this] {\n        std::this_thread::sleep_for(std::chrono::seconds(1));\n        unlink(test_file_.c_str());\n    });\n    EXPECT_TRUE(WaitForFileDeleted(test_file_, 3s));\n    thread.join();\n}\n\nTEST_F(FileWaitTest, BadPath) {\n    ASSERT_FALSE(WaitForFile(\"/this/path/does/not/exist\", 5ms));\n    EXPECT_EQ(errno, ENOENT);\n}\n"
  },
  {
    "path": "fs_mgr/tests/fs_mgr_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <linux/fs.h>\n#include <mntent.h>\n\n#include <algorithm>\n#include <iterator>\n#include <set>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n#include <fstab/fstab.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n\n#include \"../fs_mgr_priv.h\"\n\nusing namespace android::fs_mgr;\nusing namespace testing;\n\nnamespace {\n\nconst std::string cmdline =\n        \"rcupdate.rcu_expedited=1 rootwait ro \"\n        \"init=/init androidboot.bootdevice=1d84000.ufshc \"\n        \"androidboot.baseband=sdy androidboot.keymaster=1  skip_initramfs \"\n        \"androidboot.serialno=BLAHBLAHBLAH androidboot.slot_suffix=_a \"\n        \"androidboot.hardware.platform=sdw813 androidboot.hardware=foo \"\n        \"androidboot.revision=EVT1.0 androidboot.bootloader=burp-0.1-7521 \"\n        \"androidboot.hardware.sku=mary androidboot.hardware.radio.subtype=0 \"\n        \"androidboot.dtbo_idx=2 androidboot.mode=normal \"\n        \"androidboot.hardware.ddr=1GB,combuchi,LPDDR4X \"\n        \"androidboot.ddr_info=combuchiandroidboot.ddr_size=2GB \"\n        \"androidboot.hardware.ufs=2GB,combushi \"\n        \"androidboot.boottime=0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123 \"\n        \"androidboot.ramdump=disabled \"\n        \"dm=\\\"1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684\\\" \"\n        \"root=/dev/dm-0 \"\n        \"androidboot.vbmeta.device=PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb \"\n        \"androidboot.vbmeta.avb_version=\\\"1.1\\\" \"\n        \"androidboot.vbmeta.device_state=unlocked \"\n        \"androidboot.vbmeta.hash_alg=sha256 androidboot.vbmeta.size=5248 \"\n        \"androidboot.vbmeta.digest=\"\n        \"ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860 \"\n        \"androidboot.vbmeta.invalidate_on_error=yes \"\n        \"androidboot.veritymode=enforcing androidboot.verifiedbootstate=orange \"\n        \"androidboot.space=\\\"sha256 5248 androidboot.nospace=nope\\\" \"\n        \"printk.devkmsg=on msm_rtb.filter=0x237 ehci-hcd.park=3 \"\n        \"\\\"string =\\\"\\\"string '\\\" \"\n        \"service_locator.enable=1 firmware_class.path=/vendor/firmware \"\n        \"cgroup.memory=nokmem lpm_levels.sleep_disabled=1 \"\n        \"buildvariant=userdebug  console=null \"\n        \"terminator=\\\"truncated\";\n\nconst std::vector<std::pair<std::string, std::string>> result_space = {\n        {\"rcupdate.rcu_expedited\", \"1\"},\n        {\"rootwait\", \"\"},\n        {\"ro\", \"\"},\n        {\"init\", \"/init\"},\n        {\"androidboot.bootdevice\", \"1d84000.ufshc\"},\n        {\"androidboot.baseband\", \"sdy\"},\n        {\"androidboot.keymaster\", \"1\"},\n        {\"skip_initramfs\", \"\"},\n        {\"androidboot.serialno\", \"BLAHBLAHBLAH\"},\n        {\"androidboot.slot_suffix\", \"_a\"},\n        {\"androidboot.hardware.platform\", \"sdw813\"},\n        {\"androidboot.hardware\", \"foo\"},\n        {\"androidboot.revision\", \"EVT1.0\"},\n        {\"androidboot.bootloader\", \"burp-0.1-7521\"},\n        {\"androidboot.hardware.sku\", \"mary\"},\n        {\"androidboot.hardware.radio.subtype\", \"0\"},\n        {\"androidboot.dtbo_idx\", \"2\"},\n        {\"androidboot.mode\", \"normal\"},\n        {\"androidboot.hardware.ddr\", \"1GB,combuchi,LPDDR4X\"},\n        {\"androidboot.ddr_info\", \"combuchiandroidboot.ddr_size=2GB\"},\n        {\"androidboot.hardware.ufs\", \"2GB,combushi\"},\n        {\"androidboot.boottime\", \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"},\n        {\"androidboot.ramdump\", \"disabled\"},\n        {\"dm\", \"1 vroot none ro 1,0 10416 verity 1 624684 fec_start 624684\"},\n        {\"root\", \"/dev/dm-0\"},\n        {\"androidboot.vbmeta.device\", \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"},\n        {\"androidboot.vbmeta.avb_version\", \"1.1\"},\n        {\"androidboot.vbmeta.device_state\", \"unlocked\"},\n        {\"androidboot.vbmeta.hash_alg\", \"sha256\"},\n        {\"androidboot.vbmeta.size\", \"5248\"},\n        {\"androidboot.vbmeta.digest\",\n         \"ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"},\n        {\"androidboot.vbmeta.invalidate_on_error\", \"yes\"},\n        {\"androidboot.veritymode\", \"enforcing\"},\n        {\"androidboot.verifiedbootstate\", \"orange\"},\n        {\"androidboot.space\", \"sha256 5248 androidboot.nospace=nope\"},\n        {\"printk.devkmsg\", \"on\"},\n        {\"msm_rtb.filter\", \"0x237\"},\n        {\"ehci-hcd.park\", \"3\"},\n        {\"string \", \"string '\"},\n        {\"service_locator.enable\", \"1\"},\n        {\"firmware_class.path\", \"/vendor/firmware\"},\n        {\"cgroup.memory\", \"nokmem\"},\n        {\"lpm_levels.sleep_disabled\", \"1\"},\n        {\"buildvariant\", \"userdebug\"},\n        {\"console\", \"null\"},\n        {\"terminator\", \"truncated\"},\n};\n\nconst std::string bootconfig = R\"(\nandroidboot.bootdevice = \"1d84000.ufshc\"\nandroidboot.boot_devices = \"dev1\", \"dev2,withcomma\", \"dev3\"\nandroidboot.baseband = \"sdy\"\nandroidboot.keymaster = \"1\"\nandroidboot.serialno = \"BLAHBLAHBLAH\"\nandroidboot.slot_suffix = \"_a\"\nandroidboot.hardware.platform = \"sdw813\"\nandroidboot.hardware = \"foo\"\nandroidboot.revision = \"EVT1.0\"\nandroidboot.bootloader = \"burp-0.1-7521\"\nandroidboot.hardware.sku = \"mary\"\nandroidboot.hardware.radio.subtype = \"0\"\nandroidboot.dtbo_idx = \"2\"\nandroidboot.mode = \"normal\"\nandroidboot.hardware.ddr = \"1GB,combuchi,LPDDR4X\"\nandroidboot.ddr_info = \"combuchiandroidboot.ddr_size=2GB\"\nandroidboot.hardware.ufs = \"2GB,combushi\"\nandroidboot.boottime = \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"\nandroidboot.ramdump = \"disabled\"\nandroidboot.vbmeta.device = \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"\nandroidboot.vbmeta.avb_version = \"1.1\"\nandroidboot.vbmeta.device_state = \"unlocked\"\nandroidboot.vbmeta.hash_alg = \"sha256\"\nandroidboot.vbmeta.size = \"5248\"\nandroidboot.vbmeta.digest = \"ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"\nandroidboot.vbmeta.invalidate_on_error = \"yes\"\nandroidboot.veritymode = \"enforcing\"\nandroidboot.verifiedbootstate = \"orange\"\nandroidboot.space = \"sha256 5248 androidboot.nospace = nope\"\njust.key\nkey.empty.value =\ndessert.value = \"ice, cream\"\ndessert.list = \"ice\", \"cream\"\nambiguous.list = \", \", \", \"\n)\";\n\nconst std::vector<std::pair<std::string, std::string>> bootconfig_result_space = {\n        {\"androidboot.bootdevice\", \"1d84000.ufshc\"},\n        {\"androidboot.boot_devices\", \"dev1, dev2,withcomma, dev3\"},\n        {\"androidboot.baseband\", \"sdy\"},\n        {\"androidboot.keymaster\", \"1\"},\n        {\"androidboot.serialno\", \"BLAHBLAHBLAH\"},\n        {\"androidboot.slot_suffix\", \"_a\"},\n        {\"androidboot.hardware.platform\", \"sdw813\"},\n        {\"androidboot.hardware\", \"foo\"},\n        {\"androidboot.revision\", \"EVT1.0\"},\n        {\"androidboot.bootloader\", \"burp-0.1-7521\"},\n        {\"androidboot.hardware.sku\", \"mary\"},\n        {\"androidboot.hardware.radio.subtype\", \"0\"},\n        {\"androidboot.dtbo_idx\", \"2\"},\n        {\"androidboot.mode\", \"normal\"},\n        {\"androidboot.hardware.ddr\", \"1GB,combuchi,LPDDR4X\"},\n        {\"androidboot.ddr_info\", \"combuchiandroidboot.ddr_size=2GB\"},\n        {\"androidboot.hardware.ufs\", \"2GB,combushi\"},\n        {\"androidboot.boottime\", \"0BLE:58,1BLL:22,1BLE:571,2BLL:105,ODT:0,AVB:123\"},\n        {\"androidboot.ramdump\", \"disabled\"},\n        {\"androidboot.vbmeta.device\", \"PARTUUID=aa08f1a4-c7c9-402e-9a66-9707cafa9ceb\"},\n        {\"androidboot.vbmeta.avb_version\", \"1.1\"},\n        {\"androidboot.vbmeta.device_state\", \"unlocked\"},\n        {\"androidboot.vbmeta.hash_alg\", \"sha256\"},\n        {\"androidboot.vbmeta.size\", \"5248\"},\n        {\"androidboot.vbmeta.digest\",\n         \"ac13147e959861c20f2a6da97d25fe79e60e902c022a371c5c039d31e7c68860\"},\n        {\"androidboot.vbmeta.invalidate_on_error\", \"yes\"},\n        {\"androidboot.veritymode\", \"enforcing\"},\n        {\"androidboot.verifiedbootstate\", \"orange\"},\n        {\"androidboot.space\", \"sha256 5248 androidboot.nospace = nope\"},\n        {\"just.key\", \"\"},\n        {\"key.empty.value\", \"\"},\n        {\"dessert.value\", \"ice, cream\"},\n        {\"dessert.list\", \"ice,cream\"},\n        {\"ambiguous.list\", \", ,, \"},\n};\n\nbool CompareFlags(FstabEntry::FsMgrFlags& lhs, FstabEntry::FsMgrFlags& rhs) {\n    // clang-format off\n    return lhs.wait == rhs.wait &&\n           lhs.check == rhs.check &&\n           lhs.crypt == rhs.crypt &&\n           lhs.nonremovable == rhs.nonremovable &&\n           lhs.vold_managed == rhs.vold_managed &&\n           lhs.recovery_only == rhs.recovery_only &&\n           lhs.no_emulated_sd == rhs.no_emulated_sd &&\n           lhs.no_trim == rhs.no_trim &&\n           lhs.file_encryption == rhs.file_encryption &&\n           lhs.formattable == rhs.formattable &&\n           lhs.slot_select == rhs.slot_select &&\n           lhs.late_mount == rhs.late_mount &&\n           lhs.no_fail == rhs.no_fail &&\n           lhs.quota == rhs.quota &&\n           lhs.avb == rhs.avb &&\n           lhs.logical == rhs.logical &&\n           lhs.checkpoint_blk == rhs.checkpoint_blk &&\n           lhs.checkpoint_fs == rhs.checkpoint_fs &&\n           lhs.first_stage_mount == rhs.first_stage_mount &&\n           lhs.slot_select_other == rhs.slot_select_other &&\n           lhs.fs_verity == rhs.fs_verity;\n    // clang-format on\n}\n\n}  // namespace\n\nTEST(fs_mgr, ImportKernelCmdline) {\n    std::vector<std::pair<std::string, std::string>> result;\n    ImportKernelCmdlineFromString(\n            cmdline, [&](std::string key, std::string value) { result.emplace_back(key, value); });\n    EXPECT_THAT(result, ContainerEq(result_space));\n}\n\nTEST(fs_mgr, GetKernelCmdline) {\n    std::string content;\n    for (const auto& [key, value] : result_space) {\n        EXPECT_TRUE(GetKernelCmdlineFromString(cmdline, key, &content)) << \" for \" << key;\n        EXPECT_EQ(content, value);\n    }\n\n    const std::string kUnmodifiedToken = \"<UNMODIFIED>\";\n    content = kUnmodifiedToken;\n    EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, \"\", &content));\n    EXPECT_EQ(content, kUnmodifiedToken) << \"output parameter shouldn't be overridden\";\n\n    content = kUnmodifiedToken;\n    EXPECT_FALSE(GetKernelCmdlineFromString(cmdline, \"androidboot.vbmeta.avb_versio\", &content));\n    EXPECT_EQ(content, kUnmodifiedToken) << \"output parameter shouldn't be overridden\";\n\n    content = kUnmodifiedToken;\n    EXPECT_FALSE(GetKernelCmdlineFromString(bootconfig, \"androidboot.nospace\", &content));\n    EXPECT_EQ(content, kUnmodifiedToken) << \"output parameter shouldn't be overridden\";\n}\n\nTEST(fs_mgr, ImportBootconfig) {\n    std::vector<std::pair<std::string, std::string>> result;\n    ImportBootconfigFromString(bootconfig, [&](std::string key, std::string value) {\n        result.emplace_back(key, value);\n    });\n    EXPECT_THAT(result, ContainerEq(bootconfig_result_space));\n}\n\nTEST(fs_mgr, GetBootconfig) {\n    std::string content;\n    for (const auto& [key, value] : bootconfig_result_space) {\n        EXPECT_TRUE(GetBootconfigFromString(bootconfig, key, &content)) << \" for \" << key;\n        EXPECT_EQ(content, value);\n    }\n\n    const std::string kUnmodifiedToken = \"<UNMODIFIED>\";\n    content = kUnmodifiedToken;\n    EXPECT_FALSE(GetBootconfigFromString(bootconfig, \"\", &content));\n    EXPECT_EQ(content, kUnmodifiedToken) << \"output parameter shouldn't be overridden\";\n\n    content = kUnmodifiedToken;\n    EXPECT_FALSE(GetBootconfigFromString(bootconfig, \"androidboot.vbmeta.avb_versio\", &content));\n    EXPECT_EQ(content, kUnmodifiedToken) << \"output parameter shouldn't be overridden\";\n\n    content = kUnmodifiedToken;\n    EXPECT_FALSE(GetBootconfigFromString(bootconfig, \"androidboot.nospace\", &content));\n    EXPECT_EQ(content, kUnmodifiedToken) << \"output parameter shouldn't be overridden\";\n}\n\nTEST(fs_mgr, fs_mgr_read_fstab_file_proc_mounts) {\n    Fstab fstab;\n    ASSERT_TRUE(ReadFstabFromFile(\"/proc/mounts\", &fstab));\n\n    std::unique_ptr<std::FILE, int (*)(std::FILE*)> mounts(setmntent(\"/proc/mounts\", \"re\"),\n                                                           endmntent);\n    ASSERT_NE(mounts, nullptr);\n\n    mntent* mentry;\n    size_t i = 0;\n    while ((mentry = getmntent(mounts.get())) != nullptr) {\n        ASSERT_LT(i, fstab.size());\n        auto& entry = fstab[i];\n\n        EXPECT_EQ(mentry->mnt_fsname, entry.blk_device);\n        EXPECT_EQ(mentry->mnt_dir, entry.mount_point);\n        EXPECT_EQ(mentry->mnt_type, entry.fs_type);\n\n        std::set<std::string> mnt_opts;\n        for (auto& s : android::base::Split(mentry->mnt_opts, \",\")) {\n            mnt_opts.emplace(s);\n        }\n        std::set<std::string> fs_options;\n        if (!entry.fs_options.empty()) {\n            for (auto& s : android::base::Split(entry.fs_options, \",\")) {\n                fs_options.emplace(s);\n            }\n        }\n        // matches private content in fs_mgr_fstab.c\n        static struct flag_list {\n            const char* name;\n            unsigned int flag;\n        } mount_flags[] = {\n                {\"noatime\", MS_NOATIME},\n                {\"noexec\", MS_NOEXEC},\n                {\"nosuid\", MS_NOSUID},\n                {\"nodev\", MS_NODEV},\n                {\"nodiratime\", MS_NODIRATIME},\n                {\"ro\", MS_RDONLY},\n                {\"rw\", 0},\n                {\"sync\", MS_SYNCHRONOUS},\n                {\"remount\", MS_REMOUNT},\n                {\"bind\", MS_BIND},\n                {\"rec\", MS_REC},\n                {\"unbindable\", MS_UNBINDABLE},\n                {\"private\", MS_PRIVATE},\n                {\"slave\", MS_SLAVE},\n                {\"shared\", MS_SHARED},\n                {\"lazytime\", MS_LAZYTIME},\n                {\"nosymfollow\", MS_NOSYMFOLLOW},\n                {\"defaults\", 0},\n                {0, 0},\n        };\n        for (auto f = 0; mount_flags[f].name; ++f) {\n            if (mount_flags[f].flag & entry.flags) {\n                fs_options.emplace(mount_flags[f].name);\n            }\n        }\n        if (!(entry.flags & MS_RDONLY)) {\n            fs_options.emplace(\"rw\");\n        }\n        EXPECT_EQ(mnt_opts, fs_options) << \"At line \" << i;\n        ++i;\n    }\n    EXPECT_EQ(i, fstab.size());\n}\n\nTEST(fs_mgr, ReadFstabFromFile_MountOptions) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource /            ext4    ro,barrier=1                    wait,avb\nsource /metadata    ext4    noatime,nosuid,nodev,discard    wait,formattable\n\nsource /data        f2fs    noatime,nosuid,nodev,discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier    latemount,wait,check,fileencryption=ice,keydirectory=/metadata/vold/metadata_encryption,quota,formattable,sysfs_path=/sys/devices/platform/soc/1d84000.ufshc,reservedsize=128M\n\nsource /misc        emmc    defaults                        defaults\n\nsource /vendor/firmware_mnt    vfat    ro,shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,context=u:object_r:firmware_file:s0    wait\n\nsource auto         vfat    defaults                        voldmanaged=usb:auto\nsource none         swap    defaults                        zramsize=1073741824,max_comp_streams=8\nsource none2        swap    nodiratime,remount,bind         zramsize=1073741824,max_comp_streams=8\nsource none3        swap    unbindable,private,slave        zramsize=1073741824,max_comp_streams=8\nsource none4        swap    noexec,shared,rec               zramsize=1073741824,max_comp_streams=8\nsource none5        swap    rw                              zramsize=1073741824,max_comp_streams=8\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(11U, fstab.size());\n\n    FstabEntry* entry = GetEntryForMountPoint(&fstab, \"/\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), entry->flags);\n    EXPECT_EQ(\"barrier=1\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"/metadata\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), entry->flags);\n    EXPECT_EQ(\"discard\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"/data\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(static_cast<unsigned long>(MS_NOATIME | MS_NOSUID | MS_NODEV), entry->flags);\n    EXPECT_EQ(\"discard,reserve_root=32768,resgid=1065,fsync_mode=nobarrier\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"/misc\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(0U, entry->flags);\n    EXPECT_EQ(\"\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"/vendor/firmware_mnt\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(static_cast<unsigned long>(MS_RDONLY), entry->flags);\n    EXPECT_EQ(\n            \"shortname=lower,uid=1000,gid=1000,dmask=227,fmask=337,\"\n            \"context=u:object_r:firmware_file:s0\",\n            entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"auto\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(0U, entry->flags);\n    EXPECT_EQ(\"\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"none\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(0U, entry->flags);\n    EXPECT_EQ(\"\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"none2\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(static_cast<unsigned long>(MS_NODIRATIME | MS_REMOUNT | MS_BIND), entry->flags);\n    EXPECT_EQ(\"\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"none3\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(static_cast<unsigned long>(MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE), entry->flags);\n    EXPECT_EQ(\"\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"none4\");\n    ASSERT_NE(nullptr, entry);\n    EXPECT_EQ(static_cast<unsigned long>(MS_NOEXEC | MS_SHARED | MS_REC), entry->flags);\n    EXPECT_EQ(\"\", entry->fs_options);\n\n    entry = GetEntryForMountPoint(&fstab, \"none5\");\n    ASSERT_NE(nullptr, entry);\n    // rw is the default.\n    EXPECT_EQ(0U, entry->flags);\n    EXPECT_EQ(\"\", entry->fs_options);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrFlags) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      wait,check,nonremovable,recoveryonly\nsource none1       swap   defaults      avb,noemulatedsd,notrim,formattable,nofail\nsource none2       swap   defaults      first_stage_mount,latemount,quota,logical\nsource none3       swap   defaults      checkpoint=block\nsource none4       swap   defaults      checkpoint=fs\nsource none5       swap   defaults      defaults\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(6U, fstab.size());\n\n    FstabEntry* entry = GetEntryForMountPoint(&fstab, \"none0\");\n    ASSERT_NE(nullptr, entry);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        flags.wait = true;\n        flags.check = true;\n        flags.nonremovable = true;\n        flags.recovery_only = true;\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n\n    entry = GetEntryForMountPoint(&fstab, \"none1\");\n    ASSERT_NE(nullptr, entry);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        flags.avb = true;\n        flags.no_emulated_sd = true;\n        flags.no_trim = true;\n        flags.formattable = true;\n        flags.no_fail = true;\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n\n    entry = GetEntryForMountPoint(&fstab, \"none2\");\n    ASSERT_NE(nullptr, entry);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        flags.first_stage_mount = true;\n        flags.late_mount = true;\n        flags.quota = true;\n        flags.logical = true;\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n\n    entry = GetEntryForMountPoint(&fstab, \"none3\");\n    ASSERT_NE(nullptr, entry);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        flags.checkpoint_blk = true;\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n\n    entry = GetEntryForMountPoint(&fstab, \"none4\");\n    ASSERT_NE(nullptr, entry);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        flags.checkpoint_fs = true;\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n\n    entry = GetEntryForMountPoint(&fstab, \"none5\");\n    ASSERT_NE(nullptr, entry);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_AllBad) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      fileencryption,keydirectory,length,swapprio,zramsize,max_comp_streams,reservedsize,eraseblk,logicalblk,sysfs_path,zram_backingdev_size\n\nsource none1       swap   defaults      fileencryption=,keydirectory=,length=,swapprio=,zramsize=,max_comp_streams=,avb=,reservedsize=,eraseblk=,logicalblk=,sysfs_path=,zram_backingdev_size=\n\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(2U, fstab.size());\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        flags.file_encryption = true;\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n    EXPECT_EQ(\"\", entry->metadata_key_dir);\n    EXPECT_EQ(0, entry->length);\n    EXPECT_EQ(\"\", entry->label);\n    EXPECT_EQ(-1, entry->partnum);\n    EXPECT_EQ(-1, entry->swap_prio);\n    EXPECT_EQ(0, entry->max_comp_streams);\n    EXPECT_EQ(0, entry->zram_size);\n    EXPECT_EQ(0, entry->reserved_size);\n    EXPECT_EQ(\"\", entry->encryption_options);\n    EXPECT_EQ(0, entry->erase_blk_size);\n    EXPECT_EQ(0, entry->logical_blk_size);\n    EXPECT_EQ(\"\", entry->sysfs_path);\n    EXPECT_EQ(0U, entry->zram_backingdev_size);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    {\n        FstabEntry::FsMgrFlags flags = {};\n        flags.file_encryption = true;\n        flags.avb = true;\n        EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    }\n    EXPECT_EQ(\"\", entry->metadata_key_dir);\n    EXPECT_EQ(0, entry->length);\n    EXPECT_EQ(\"\", entry->label);\n    EXPECT_EQ(-1, entry->partnum);\n    EXPECT_EQ(-1, entry->swap_prio);\n    EXPECT_EQ(0, entry->max_comp_streams);\n    EXPECT_EQ(0, entry->zram_size);\n    EXPECT_EQ(0, entry->reserved_size);\n    EXPECT_EQ(\"\", entry->encryption_options);\n    EXPECT_EQ(0, entry->erase_blk_size);\n    EXPECT_EQ(0, entry->logical_blk_size);\n    EXPECT_EQ(\"\", entry->sysfs_path);\n    EXPECT_EQ(0U, entry->zram_backingdev_size);\n}\n\n// FDE is no longer supported, so an fstab with FDE enabled should be rejected.\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FDE) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource /data        ext4    noatime    forceencrypt=footer\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_FALSE(ReadFstabFromFile(tf.path, &fstab));\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_AdoptableStorage) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      encryptable=userdata,voldmanaged=sdcard:auto\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(1U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n    flags.crypt = true;\n    flags.vold_managed = true;\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_VoldManaged) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      voldmanaged=:\nsource none1       swap   defaults      voldmanaged=sdcard\nsource none2       swap   defaults      voldmanaged=sdcard:3\nsource none3       swap   defaults      voldmanaged=sdcard:auto\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(4U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n    flags.vold_managed = true;\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_TRUE(entry->label.empty());\n    EXPECT_EQ(-1, entry->partnum);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_TRUE(entry->label.empty());\n    EXPECT_EQ(-1, entry->partnum);\n    entry++;\n\n    EXPECT_EQ(\"none2\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(\"sdcard\", entry->label);\n    EXPECT_EQ(3, entry->partnum);\n    entry++;\n\n    EXPECT_EQ(\"none3\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(\"sdcard\", entry->label);\n    EXPECT_EQ(-1, entry->partnum);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Length) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      length=blah\nsource none1       swap   defaults      length=123456\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(2U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->length);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(123456, entry->length);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Swapprio) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      swapprio=blah\nsource none1       swap   defaults      swapprio=123456\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(2U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(-1, entry->swap_prio);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(123456, entry->swap_prio);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ZramSize) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      zramsize=blah\nsource none1       swap   defaults      zramsize=123456\nsource none2       swap   defaults      zramsize=blah%\nsource none3       swap   defaults      zramsize=5%\nsource none4       swap   defaults      zramsize=105%\nsource none5       swap   defaults      zramsize=%\nsource none6       swap   defaults      zramsize=210%\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(6U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->zram_size);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(123456, entry->zram_size);\n    entry++;\n\n    EXPECT_EQ(\"none2\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->zram_size);\n    entry++;\n\n    EXPECT_EQ(\"none3\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_NE(0, entry->zram_size);\n    entry++;\n\n    EXPECT_EQ(\"none4\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_NE(0, entry->zram_size);\n    entry++;\n\n    EXPECT_EQ(\"none5\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->zram_size);\n    entry++;\n\n    EXPECT_EQ(\"none6\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->zram_size);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_FileEncryption) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      fileencryption=aes-256-xts:aes-256-cts:v1\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(1U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n    flags.file_encryption = true;\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(\"aes-256-xts:aes-256-cts:v1\", entry->encryption_options);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MaxCompStreams) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      max_comp_streams=blah\nsource none1       swap   defaults      max_comp_streams=123456\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(2U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->max_comp_streams);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(123456, entry->max_comp_streams);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_ReservedSize) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      reservedsize=blah\nsource none1       swap   defaults      reservedsize=2\nsource none2       swap   defaults      reservedsize=1K\nsource none3       swap   defaults      reservedsize=2m\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(4U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->reserved_size);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(2, entry->reserved_size);\n    entry++;\n\n    EXPECT_EQ(\"none2\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(1024, entry->reserved_size);\n    entry++;\n\n    EXPECT_EQ(\"none3\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(2 * 1024 * 1024, entry->reserved_size);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_EraseBlk) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      eraseblk=blah\nsource none1       swap   defaults      eraseblk=4000\nsource none2       swap   defaults      eraseblk=5000\nsource none3       swap   defaults      eraseblk=8192\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(4U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->erase_blk_size);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->erase_blk_size);\n    entry++;\n\n    EXPECT_EQ(\"none2\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->erase_blk_size);\n    entry++;\n\n    EXPECT_EQ(\"none3\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(8192, entry->erase_blk_size);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Logicalblk) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      logicalblk=blah\nsource none1       swap   defaults      logicalblk=4000\nsource none2       swap   defaults      logicalblk=5000\nsource none3       swap   defaults      logicalblk=8192\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(4U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->logical_blk_size);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->logical_blk_size);\n    entry++;\n\n    EXPECT_EQ(\"none2\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->logical_blk_size);\n    entry++;\n\n    EXPECT_EQ(\"none3\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(8192, entry->logical_blk_size);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Avb) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      avb=vbmeta_partition\nsource none1       swap   defaults      avb_keys=/path/to/test.avbpubkey\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(2U, fstab.size());\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n\n    FstabEntry::FsMgrFlags flags = {};\n    flags.avb = true;\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n\n    EXPECT_EQ(\"vbmeta_partition\", entry->vbmeta_partition);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    FstabEntry::FsMgrFlags empty_flags = {};  // no flags should be set for avb_keys.\n    EXPECT_TRUE(CompareFlags(empty_flags, entry->fs_mgr_flags));\n    EXPECT_EQ(\"/path/to/test.avbpubkey\", entry->avb_keys);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_KeyDirectory) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      keydirectory=/dir/key\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(1U, fstab.size());\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n\n    FstabEntry::FsMgrFlags flags = {};\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n\n    EXPECT_EQ(\"/dir/key\", entry->metadata_key_dir);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      keydirectory=/dir/key,metadata_encryption=adiantum\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(1U, fstab.size());\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"adiantum\", entry->metadata_encryption_options);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_MetadataEncryption_WrappedKey) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      keydirectory=/dir/key,metadata_encryption=aes-256-xts:wrappedkey_v0\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(1U, fstab.size());\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"aes-256-xts:wrappedkey_v0\", entry->metadata_encryption_options);\n    auto parts = android::base::Split(entry->metadata_encryption_options, \":\");\n    EXPECT_EQ(2U, parts.size());\n    EXPECT_EQ(\"aes-256-xts\", parts[0]);\n    EXPECT_EQ(\"wrappedkey_v0\", parts[1]);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_SysfsPath) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      sysfs_path=/sys/device\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(1U, fstab.size());\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n\n    FstabEntry::FsMgrFlags flags = {};\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n\n    EXPECT_EQ(\"/sys/device\", entry->sysfs_path);\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Zram) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none1       swap   defaults      zram_backingdev_size=blah\nsource none2       swap   defaults      zram_backingdev_size=2\nsource none3       swap   defaults      zram_backingdev_size=1K\nsource none4       swap   defaults      zram_backingdev_size=2m\n\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(4U, fstab.size());\n\n    auto entry = fstab.begin();\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_EQ(0U, entry->zram_backingdev_size);\n    entry++;\n\n    EXPECT_EQ(\"none2\", entry->mount_point);\n    EXPECT_EQ(2U, entry->zram_backingdev_size);\n    entry++;\n\n    EXPECT_EQ(\"none3\", entry->mount_point);\n    EXPECT_EQ(1024U, entry->zram_backingdev_size);\n    entry++;\n\n    EXPECT_EQ(\"none4\", entry->mount_point);\n    EXPECT_EQ(2U * 1024U * 1024U, entry->zram_backingdev_size);\n    entry++;\n}\n\nTEST(fs_mgr, DefaultFstabContainsUserdata) {\n    Fstab fstab;\n    ASSERT_TRUE(ReadDefaultFstab(&fstab)) << \"Failed to read default fstab\";\n    ASSERT_NE(nullptr, GetEntryForMountPoint(&fstab, \"/data\"))\n            << \"Default fstab doesn't contain /data entry\";\n}\n\nTEST(fs_mgr, ReadFstabFromFile_FsMgrOptions_Readahead_Size_KB) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsource none0       swap   defaults      readahead_size_kb=blah\nsource none1       swap   defaults      readahead_size_kb=128\nsource none2       swap   defaults      readahead_size_kb=5%\nsource none3       swap   defaults      readahead_size_kb=5kb\nsource none4       swap   defaults      readahead_size_kb=16385\nsource none5       swap   defaults      readahead_size_kb=-128\nsource none6       swap   defaults      readahead_size_kb=0\n)fs\";\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    ASSERT_LE(7U, fstab.size());\n\n    FstabEntry::FsMgrFlags flags = {};\n\n    auto entry = fstab.begin();\n    EXPECT_EQ(\"none0\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(-1, entry->readahead_size_kb);\n    entry++;\n\n    EXPECT_EQ(\"none1\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(128, entry->readahead_size_kb);\n    entry++;\n\n    EXPECT_EQ(\"none2\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(-1, entry->readahead_size_kb);\n    entry++;\n\n    EXPECT_EQ(\"none3\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(-1, entry->readahead_size_kb);\n    entry++;\n\n    EXPECT_EQ(\"none4\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(-1, entry->readahead_size_kb);\n    entry++;\n\n    EXPECT_EQ(\"none5\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(-1, entry->readahead_size_kb);\n    entry++;\n\n    EXPECT_EQ(\"none6\", entry->mount_point);\n    EXPECT_TRUE(CompareFlags(flags, entry->fs_mgr_flags));\n    EXPECT_EQ(0, entry->readahead_size_kb);\n}\n\nTEST(fs_mgr, TransformFstabForDsu) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\ndata   /data        f2fs    noatime     wait,latemount\nsystem /system      erofs   ro  wait,logical,first_stage_mount\nsystem /system      ext4    ro  wait,logical,first_stage_mount\nvendor /vendor      ext4    ro  wait,logical,first_stage_mount\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    // If GSI is installed, ReadFstabFromFile() would have called TransformFstabForDsu() implicitly.\n    // In other words, TransformFstabForDsu() would be called two times if running CTS-on-GSI,\n    // which implies TransformFstabForDsu() should be idempotent.\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    TransformFstabForDsu(&fstab, \"dsu\", {\"system_gsi\", \"userdata_gsi\"});\n    ASSERT_EQ(4U, fstab.size());\n\n    auto entry = fstab.begin();\n\n    EXPECT_EQ(\"/data\", entry->mount_point);\n    EXPECT_EQ(\"userdata_gsi\", entry->blk_device);\n    entry++;\n\n    EXPECT_EQ(\"/system\", entry->mount_point);\n    EXPECT_EQ(\"system_gsi\", entry->blk_device);\n    EXPECT_EQ(\"erofs\", entry->fs_type);\n    entry++;\n\n    EXPECT_EQ(\"/system\", entry->mount_point);\n    EXPECT_EQ(\"system_gsi\", entry->blk_device);\n    EXPECT_EQ(\"ext4\", entry->fs_type);\n    entry++;\n\n    EXPECT_EQ(\"/vendor\", entry->mount_point);\n    EXPECT_EQ(\"vendor\", entry->blk_device);\n    entry++;\n}\n\nTEST(fs_mgr, TransformFstabForDsu_synthesisExt4Entry) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\nsystem /system      erofs   ro  wait,logical,first_stage_mount\nvendor /vendor      ext4    ro  wait,logical,first_stage_mount\ndata   /data        f2fs    noatime     wait,latemount\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    TransformFstabForDsu(&fstab, \"dsu\", {\"system_gsi\", \"userdata_gsi\"});\n    ASSERT_EQ(4U, fstab.size());\n\n    auto entry = fstab.begin();\n\n    EXPECT_EQ(\"/system\", entry->mount_point);\n    EXPECT_EQ(\"system_gsi\", entry->blk_device);\n    EXPECT_EQ(\"erofs\", entry->fs_type);\n    entry++;\n\n    EXPECT_EQ(\"/system\", entry->mount_point);\n    EXPECT_EQ(\"system_gsi\", entry->blk_device);\n    EXPECT_EQ(\"ext4\", entry->fs_type);\n    entry++;\n\n    EXPECT_EQ(\"/vendor\", entry->mount_point);\n    EXPECT_EQ(\"vendor\", entry->blk_device);\n    entry++;\n\n    EXPECT_EQ(\"/data\", entry->mount_point);\n    EXPECT_EQ(\"userdata_gsi\", entry->blk_device);\n    entry++;\n}\n\nTEST(fs_mgr, TransformFstabForDsu_synthesisAllMissingEntries) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    std::string fstab_contents = R\"fs(\ndata   /data        f2fs    noatime     wait,latemount\nvendor /vendor      ext4    ro  wait,logical,first_stage_mount\n)fs\";\n\n    ASSERT_TRUE(android::base::WriteStringToFile(fstab_contents, tf.path));\n\n    Fstab fstab;\n    EXPECT_TRUE(ReadFstabFromFile(tf.path, &fstab));\n    TransformFstabForDsu(&fstab, \"dsu\", {\"system_gsi\", \"userdata_gsi\"});\n    ASSERT_EQ(4U, fstab.size());\n\n    auto entry = fstab.begin();\n\n    EXPECT_EQ(\"/data\", entry->mount_point);\n    EXPECT_EQ(\"userdata_gsi\", entry->blk_device);\n    entry++;\n\n    EXPECT_EQ(\"/vendor\", entry->mount_point);\n    EXPECT_EQ(\"vendor\", entry->blk_device);\n    entry++;\n\n    EXPECT_EQ(\"/system\", entry->mount_point);\n    EXPECT_EQ(\"system_gsi\", entry->blk_device);\n    EXPECT_EQ(\"ext4\", entry->fs_type);\n    entry++;\n\n    EXPECT_EQ(\"/system\", entry->mount_point);\n    EXPECT_EQ(\"system_gsi\", entry->blk_device);\n    EXPECT_EQ(\"erofs\", entry->fs_type);\n    entry++;\n}\n"
  },
  {
    "path": "fs_mgr/tests/src/com/android/tests/vendoroverlay/VendorOverlayHostTest.java",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.tests.vendoroverlay;\n\nimport com.android.tradefed.device.DeviceNotAvailableException;\nimport com.android.tradefed.testtype.DeviceJUnit4ClassRunner;\nimport com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;\nimport com.android.tradefed.util.CommandResult;\nimport com.android.tradefed.util.CommandStatus;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport org.junit.After;\nimport org.junit.Assert;\nimport org.junit.Assume;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\n/**\n * Test the vendor overlay feature. Requires adb remount with OverlayFS.\n */\n@RunWith(DeviceJUnit4ClassRunner.class)\npublic class VendorOverlayHostTest extends BaseHostJUnit4Test {\n  boolean wasRoot = false;\n  String vndkVersion = null;\n\n  @Before\n  public void setup() throws DeviceNotAvailableException {\n    vndkVersion = getDevice().executeShellV2Command(\"getprop ro.vndk.version\").getStdout();\n    Assume.assumeTrue(\n        \"Vendor Overlay is disabled for VNDK deprecated devices\",\n        vndkVersion != null && !vndkVersion.trim().isEmpty());\n\n    wasRoot = getDevice().isAdbRoot();\n    if (!wasRoot) {\n      Assume.assumeTrue(\"Test requires root\", getDevice().enableAdbRoot());\n    }\n\n    Assume.assumeTrue(\"Skipping vendor overlay test due to lack of necessary OverlayFS support\",\n        testConditionsMet());\n\n    getDevice().remountSystemWritable();\n    // Was OverlayFS used by adb remount? Without it we can't safely re-enable dm-verity.\n    Pattern vendorPattern = Pattern.compile(\"^overlay .+ /vendor$\", Pattern.MULTILINE);\n    Pattern productPattern = Pattern.compile(\"^overlay .+ /product$\", Pattern.MULTILINE);\n    CommandResult result = getDevice().executeShellV2Command(\"df\");\n    Assume.assumeTrue(\"OverlayFS not used for adb remount on /vendor\",\n        vendorPattern.matcher(result.getStdout()).find());\n    Assume.assumeTrue(\"OverlayFS not used for adb remount on /product\",\n        productPattern.matcher(result.getStdout()).find());\n  }\n\n  private boolean cmdSucceeded(CommandResult result) {\n    return result.getStatus() == CommandStatus.SUCCESS;\n  }\n\n  private void assumeMkdirSuccess(String dir) throws DeviceNotAvailableException {\n    CommandResult result = getDevice().executeShellV2Command(\"mkdir -p \" + dir);\n    Assume.assumeTrue(\"Couldn't create \" + dir, cmdSucceeded(result));\n  }\n\n  /**\n   * Tests that files in the appropriate /product/vendor_overlay dir are overlaid onto /vendor.\n   */\n  @Test\n  public void testVendorOverlay() throws DeviceNotAvailableException {\n    // Create files and modify policy\n    CommandResult result = getDevice().executeShellV2Command(\n        \"echo '/(product|system/product)/vendor_overlay/\" + vndkVersion +\n        \"/.* u:object_r:vendor_file:s0'\" + \" >> /system/etc/selinux/plat_file_contexts\");\n    Assume.assumeTrue(\"Couldn't modify plat_file_contexts\", cmdSucceeded(result));\n    assumeMkdirSuccess(\"/vendor/testdir\");\n    assumeMkdirSuccess(\"/vendor/diffcontext\");\n    assumeMkdirSuccess(\"/product/vendor_overlay/'\" + vndkVersion + \"'/testdir\");\n    result = getDevice().executeShellV2Command(\n        \"echo overlay > /product/vendor_overlay/'\" + vndkVersion + \"'/testdir/test\");\n    Assume.assumeTrue(\"Couldn't create text file in testdir\", cmdSucceeded(result));\n    assumeMkdirSuccess(\"/product/vendor_overlay/'\" + vndkVersion + \"'/noexist/test\");\n    assumeMkdirSuccess(\"/product/vendor_overlay/'\" + vndkVersion + \"'/diffcontext/test\");\n    result = getDevice().executeShellV2Command(\n        \"restorecon -r /product/vendor_overlay/'\" + vndkVersion + \"'/testdir\");\n    Assume.assumeTrue(\"Couldn't write testdir context\", cmdSucceeded(result));\n\n    getDevice().reboot();\n\n    // Test that the file was overlaid properly\n    result = getDevice().executeShellV2Command(\"[ $(cat /vendor/testdir/test) = overlay ]\");\n    Assert.assertTrue(\"test file was not overlaid onto /vendor/\", cmdSucceeded(result));\n    result = getDevice().executeShellV2Command(\"[ ! -d /vendor/noexist/test ]\");\n    Assert.assertTrue(\"noexist dir shouldn't exist on /vendor\", cmdSucceeded(result));\n    result = getDevice().executeShellV2Command(\"[ ! -d /vendor/diffcontext/test ]\");\n    Assert.assertTrue(\"diffcontext dir shouldn't exist on /vendor\", cmdSucceeded(result));\n  }\n\n  // Duplicate of fs_mgr_overlayfs_valid() logic\n  // Requires root\n  public boolean testConditionsMet() throws DeviceNotAvailableException {\n    if (cmdSucceeded(getDevice().executeShellV2Command(\n        \"[ -e /sys/module/overlay/parameters/override_creds ]\"))) {\n      return true;\n    }\n    if (cmdSucceeded(getDevice().executeShellV2Command(\"[ ! -e /sys/module/overlay ]\"))) {\n      return false;\n    }\n    CommandResult result = getDevice().executeShellV2Command(\"awk '{ print $3 }' /proc/version\");\n    Pattern kernelVersionPattern = Pattern.compile(\"([1-9])[.]([0-9]+).*\");\n    Matcher kernelVersionMatcher = kernelVersionPattern.matcher(result.getStdout());\n    kernelVersionMatcher.find();\n    int majorKernelVersion;\n    int minorKernelVersion;\n    try {\n      majorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(1));\n      minorKernelVersion = Integer.parseInt(kernelVersionMatcher.group(2));\n    } catch (Exception e) {\n      return false;\n    }\n    if (majorKernelVersion < 4) {\n      return true;\n    }\n    if (majorKernelVersion > 4) {\n      return false;\n    }\n    if (minorKernelVersion > 6) {\n      return false;\n    }\n    return true;\n  }\n\n  @After\n  public void tearDown() throws DeviceNotAvailableException {\n    if (getDevice().executeAdbCommand(\"enable-verity\").contains(\"Now reboot your device\")) {\n      getDevice().reboot();\n    }\n    if (!wasRoot) {\n      getDevice().disableAdbRoot();\n    }\n  }\n}\n\n"
  },
  {
    "path": "fs_mgr/tests/vendor-overlay-test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n        http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n-->\n<configuration description=\"Config for vendor overlay test cases\">\n    <test class=\"com.android.tradefed.testtype.HostTest\" >\n        <option name=\"jar\" value=\"fs_mgr_vendor_overlay_test.jar\" />\n    </test>\n</configuration>\n\n"
  },
  {
    "path": "fs_mgr/tests/vts_fs_test.cpp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <sys/mount.h>\n#include <sys/utsname.h>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <fstab/fstab.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <libdm/dm.h>\n\n#include \"../fs_mgr_priv.h\"\n\nusing testing::Contains;\nusing testing::Not;\n\nstatic int GetVsrLevel() {\n    return android::base::GetIntProperty(\"ro.vendor.api_level\", -1);\n}\n\n// Returns true iff the device has the specified feature.\nbool DeviceSupportsFeature(const char* feature) {\n    bool device_supports_feature = false;\n    FILE* p = popen(\"pm list features\", \"re\");\n    if (p) {\n        char* line = NULL;\n        size_t len = 0;\n        while (getline(&line, &len, p) > 0) {\n            if (strstr(line, feature)) {\n                device_supports_feature = true;\n                break;\n            }\n        }\n        pclose(p);\n    }\n    return device_supports_feature;\n}\n\nTEST(fs, ErofsSupported) {\n    // T-launch GKI kernels and higher must support EROFS.\n    if (GetVsrLevel() < __ANDROID_API_T__) {\n        GTEST_SKIP();\n    }\n\n    struct utsname uts;\n    ASSERT_EQ(uname(&uts), 0);\n\n    unsigned int major, minor;\n    ASSERT_EQ(sscanf(uts.release, \"%u.%u\", &major, &minor), 2);\n\n    // EROFS support only required in 5.10+\n    if (major < 5 || (major == 5 && minor < 10)) {\n        GTEST_SKIP();\n    }\n\n    std::string fs;\n    ASSERT_TRUE(android::base::ReadFileToString(\"/proc/filesystems\", &fs));\n    EXPECT_THAT(fs, ::testing::HasSubstr(\"\\terofs\\n\"));\n\n    ASSERT_EQ(access(\"/sys/fs/erofs\", F_OK), 0);\n}\n\n// @VsrTest = 3.7.10\nTEST(fs, PartitionTypes) {\n    // Requirements only apply to Android 13+, 5.10+ devices.\n    int vsr_level = GetVsrLevel();\n    if (vsr_level < __ANDROID_API_T__) {\n        GTEST_SKIP();\n    }\n\n    struct utsname uts;\n    ASSERT_EQ(uname(&uts), 0);\n\n    unsigned int major, minor;\n    ASSERT_EQ(sscanf(uts.release, \"%u.%u\", &major, &minor), 2);\n    if (major < 5 || (major == 5 && minor < 10)) {\n        GTEST_SKIP();\n    }\n\n    android::fs_mgr::Fstab fstab;\n    ASSERT_TRUE(android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &fstab));\n\n    auto& dm = android::dm::DeviceMapper::Instance();\n\n    std::string super_bdev, userdata_bdev;\n    ASSERT_TRUE(android::base::Readlink(\"/dev/block/by-name/super\", &super_bdev));\n    ASSERT_TRUE(android::base::Readlink(\"/dev/block/by-name/userdata\", &userdata_bdev));\n\n    std::vector<std::string> data_fs = {\"/data\", \"/metadata\"};\n    for (const auto& entry : fstab) {\n        std::string parent_bdev = entry.blk_device;\n        while (true) {\n            auto basename = android::base::Basename(parent_bdev);\n            if (!android::base::StartsWith(basename, \"dm-\")) {\n                break;\n            }\n\n            auto parent = dm.GetParentBlockDeviceByPath(parent_bdev);\n            if (!parent || *parent == parent_bdev) {\n                break;\n            }\n            parent_bdev = *parent;\n        }\n\n        if (parent_bdev == userdata_bdev ||\n            android::base::StartsWith(parent_bdev, \"/dev/block/loop\")) {\n            if (entry.flags & MS_RDONLY) {\n                // APEXes should not be F2FS.\n                EXPECT_NE(entry.fs_type, \"f2fs\");\n            }\n            continue;\n        }\n\n        if (entry.flags & MS_RDONLY) {\n            if (parent_bdev != super_bdev) {\n                // Ignore non-AOSP partitions (eg anything outside of super).\n                continue;\n            }\n\n            std::vector<std::string> allowed = {\"erofs\", \"ext4\", \"f2fs\"};\n            EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())\n                    << entry.mount_point;\n        } else if (std::find(data_fs.begin(), data_fs.end(), entry.mount_point) != data_fs.end()) {\n            std::vector<std::string> allowed = {\"ext4\", \"f2fs\"};\n            EXPECT_NE(std::find(allowed.begin(), allowed.end(), entry.fs_type), allowed.end())\n                    << entry.mount_point << \", \" << entry.fs_type;\n        }\n    }\n}\n\nTEST(fs, NoDtFstab) {\n    if (GetVsrLevel() < __ANDROID_API_Q__) {\n        GTEST_SKIP();\n    }\n\n    android::fs_mgr::Fstab fstab;\n    EXPECT_FALSE(android::fs_mgr::ReadFstabFromDt(&fstab, false));\n}\n\nTEST(fs, NoLegacyVerifiedBoot) {\n    if (GetVsrLevel() < __ANDROID_API_T__) {\n        GTEST_SKIP();\n    }\n\n    const auto& default_fstab_path = android::fs_mgr::GetFstabPath();\n    EXPECT_FALSE(default_fstab_path.empty());\n\n    std::string fstab_str;\n    EXPECT_TRUE(android::base::ReadFileToString(default_fstab_path, &fstab_str,\n                                                /* follow_symlinks = */ true));\n\n    for (const auto& line : android::base::Split(fstab_str, \"\\n\")) {\n        auto fields = android::base::Tokenize(line, \" \\t\");\n        // Ignores empty lines and comments.\n        if (fields.empty() || android::base::StartsWith(fields.front(), '#')) {\n            continue;\n        }\n        // Each line in a fstab should have at least five entries.\n        //   <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>\n        ASSERT_GE(fields.size(), 5);\n        EXPECT_THAT(android::base::Split(fields[4], \",\"), Not(Contains(\"verify\")))\n                << \"AVB 1.0 isn't supported now, but the 'verify' flag is found:\\n\"\n                << \"  \" << line;\n    }\n}\n"
  },
  {
    "path": "fs_mgr/tools/Android.bp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"dmctl\",\n    srcs: [\"dmctl.cpp\"],\n\n    static_libs: [\n        \"libfs_mgr\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n\n    cflags: [\"-Werror\"],\n}\n\ncc_binary {\n    name: \"dmuserd\",\n    srcs: [\"dmuserd.cpp\"],\n\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n\n    cflags: [\"-Werror\"],\n}\n"
  },
  {
    "path": "fs_mgr/tools/dmctl.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <linux/dm-ioctl.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/unique_fd.h>\n#include <libdm/dm.h>\n\n#include <fstream>\n#include <functional>\n#include <iomanip>\n#include <ios>\n#include <iostream>\n#include <map>\n#include <optional>\n#include <sstream>\n#include <string>\n#include <vector>\n\nusing namespace std::literals::string_literals;\nusing namespace std::chrono_literals;\nusing namespace android::dm;\nusing DmBlockDevice = ::android::dm::DeviceMapper::DmBlockDevice;\n\nstatic int Usage(void) {\n    std::cerr << \"usage: dmctl <command> [command options]\" << std::endl;\n    std::cerr << \"       dmctl -f file\" << std::endl;\n    std::cerr << \"commands:\" << std::endl;\n    std::cerr << \"  create <dm-name> [-ro] <targets...>\" << std::endl;\n    std::cerr << \"  delete <dm-name>\" << std::endl;\n    std::cerr << \"  list <devices | targets> [-v]\" << std::endl;\n    std::cerr << \"  message <dm-name> <sector> <message>\" << std::endl;\n    std::cerr << \"  getpath <dm-name>\" << std::endl;\n    std::cerr << \"  getuuid <dm-name>\" << std::endl;\n    std::cerr << \"  ima <dm-name>\" << std::endl;\n    std::cerr << \"  info <dm-name>\" << std::endl;\n    std::cerr << \"  replace <dm-name> <targets...>\" << std::endl;\n    std::cerr << \"  status <dm-name>\" << std::endl;\n    std::cerr << \"  resume <dm-name>\" << std::endl;\n    std::cerr << \"  suspend <dm-name>\" << std::endl;\n    std::cerr << \"  table <dm-name>\" << std::endl;\n    std::cerr << \"  help\" << std::endl;\n    std::cerr << std::endl;\n    std::cerr << \"-f file reads command and all parameters from named file\" << std::endl;\n    std::cerr << std::endl;\n    std::cerr << \"Target syntax:\" << std::endl;\n    std::cerr << \"  <target_type> <start_sector> <num_sectors> [target_data]\" << std::endl;\n    return -EINVAL;\n}\n\nclass TargetParser final {\n  public:\n    TargetParser(int argc, char** argv) : arg_index_(0), argc_(argc), argv_(argv) {}\n\n    bool More() const { return arg_index_ < argc_; }\n    std::unique_ptr<DmTarget> Next() {\n        if (!HasArgs(3)) {\n            std::cerr << \"Expected <target_type> <start_sector> <num_sectors>\" << std::endl;\n            return nullptr;\n        }\n\n        std::string target_type = NextArg();\n        uint64_t start_sector, num_sectors;\n        if (!android::base::ParseUint(NextArg(), &start_sector)) {\n            std::cerr << \"Expected start sector, got: \" << PreviousArg() << std::endl;\n            return nullptr;\n        }\n        if (!android::base::ParseUint(NextArg(), &num_sectors) || !num_sectors) {\n            std::cerr << \"Expected non-zero sector count, got: \" << PreviousArg() << std::endl;\n            return nullptr;\n        }\n\n        if (target_type == \"zero\") {\n            return std::make_unique<DmTargetZero>(start_sector, num_sectors);\n        } else if (target_type == \"linear\") {\n            if (!HasArgs(2)) {\n                std::cerr << \"Expected \\\"linear\\\" <block_device> <sector>\" << std::endl;\n                return nullptr;\n            }\n\n            std::string block_device = NextArg();\n            uint64_t physical_sector;\n            if (!android::base::ParseUint(NextArg(), &physical_sector)) {\n                std::cerr << \"Expected sector, got: \\\"\" << PreviousArg() << \"\\\"\" << std::endl;\n                return nullptr;\n            }\n            return std::make_unique<DmTargetLinear>(start_sector, num_sectors, block_device,\n                                                    physical_sector);\n        } else if (target_type == \"android-verity\") {\n            if (!HasArgs(2)) {\n                std::cerr << \"Expected \\\"android-verity\\\" <public-key-id> <block_device>\"\n                          << std::endl;\n                return nullptr;\n            }\n            std::string keyid = NextArg();\n            std::string block_device = NextArg();\n            return std::make_unique<DmTargetAndroidVerity>(start_sector, num_sectors, keyid,\n                                                           block_device);\n        } else if (target_type == \"striped\") {\n            if (!HasArgs(3)) {\n                std::cerr << \"Expected \\\"striped\\\" <block_device0> <block_device1> <chunksize>\"\n                          << std::endl;\n                return nullptr;\n            }\n            std::string block_device0 = NextArg();\n            std::string block_device1 = NextArg();\n            uint64_t chunk_size;\n            if (!android::base::ParseUint(NextArg(), &chunk_size)) {\n                std::cerr << \"Expected start sector, got: \" << PreviousArg() << std::endl;\n                return nullptr;\n            }\n            return std::make_unique<DmTargetStripe>(start_sector, num_sectors, chunk_size,\n                                                    block_device0, block_device1);\n        } else if (target_type == \"bow\") {\n            if (!HasArgs(1)) {\n                std::cerr << \"Expected \\\"bow\\\" <block_device>\" << std::endl;\n                return nullptr;\n            }\n            std::string block_device = NextArg();\n            return std::make_unique<DmTargetBow>(start_sector, num_sectors, block_device);\n        } else if (target_type == \"snapshot-origin\") {\n            if (!HasArgs(1)) {\n                std::cerr << \"Expected \\\"snapshot-origin\\\" <block_device>\" << std::endl;\n                return nullptr;\n            }\n            std::string block_device = NextArg();\n            return std::make_unique<DmTargetSnapshotOrigin>(start_sector, num_sectors,\n                                                            block_device);\n        } else if (target_type == \"snapshot\") {\n            if (!HasArgs(4)) {\n                std::cerr\n                        << \"Expected \\\"snapshot\\\" <block_device> <block_device> <mode> <chunk_size>\"\n                        << std::endl;\n                return nullptr;\n            }\n            std::string base_device = NextArg();\n            std::string cow_device = NextArg();\n            std::string mode_str = NextArg();\n            std::string chunk_size_str = NextArg();\n\n            SnapshotStorageMode mode;\n            if (mode_str == \"P\") {\n                mode = SnapshotStorageMode::Persistent;\n            } else if (mode_str == \"N\") {\n                mode = SnapshotStorageMode::Transient;\n            } else {\n                std::cerr << \"Unrecognized mode: \" << mode_str << \"\\n\";\n                return nullptr;\n            }\n\n            uint32_t chunk_size;\n            if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {\n                std::cerr << \"Chunk size must be an unsigned integer.\\n\";\n                return nullptr;\n            }\n            return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,\n                                                      cow_device, mode, chunk_size);\n        } else if (target_type == \"snapshot-merge\") {\n            if (!HasArgs(3)) {\n                std::cerr\n                        << \"Expected \\\"snapshot-merge\\\" <block_device> <block_device> <chunk_size>\"\n                        << std::endl;\n                return nullptr;\n            }\n            std::string base_device = NextArg();\n            std::string cow_device = NextArg();\n            std::string chunk_size_str = NextArg();\n            SnapshotStorageMode mode = SnapshotStorageMode::Merge;\n\n            uint32_t chunk_size;\n            if (!android::base::ParseUint(chunk_size_str, &chunk_size)) {\n                std::cerr << \"Chunk size must be an unsigned integer.\\n\";\n                return nullptr;\n            }\n            return std::make_unique<DmTargetSnapshot>(start_sector, num_sectors, base_device,\n                                                      cow_device, mode, chunk_size);\n        } else if (target_type == \"user\") {\n            if (!HasArgs(1)) {\n                std::cerr << \"Expected \\\"user\\\" <control_device_name>\" << std::endl;\n                return nullptr;\n            }\n            std::string control_device = NextArg();\n            return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);\n        } else if (target_type == \"error\") {\n            return std::make_unique<DmTargetError>(start_sector, num_sectors);\n        } else if (target_type == \"thin-pool\") {\n            if (!HasArgs(4)) {\n                std::cerr << \"Expected \\\"thin-pool\\\" <metadata dev> <data dev> <data block size> \"\n                             \"<low water mark> <feature args>\"\n                          << std::endl;\n                return nullptr;\n            }\n\n            std::string metadata_dev = NextArg();\n            std::string data_dev = NextArg();\n            std::string data_block_size_str = NextArg();\n            std::string low_water_mark_str = NextArg();\n\n            uint64_t data_block_size;\n            if (!android::base::ParseUint(data_block_size_str, &data_block_size)) {\n                std::cerr << \"Data block size must be an unsigned integer.\\n\";\n                return nullptr;\n            }\n            uint64_t low_water_mark;\n            if (!android::base::ParseUint(low_water_mark_str, &low_water_mark)) {\n                std::cerr << \"Low water mark must be an unsigned integer.\\n\";\n                return nullptr;\n            }\n            return std::make_unique<DmTargetThinPool>(start_sector, num_sectors, metadata_dev,\n                                                      data_dev, data_block_size, low_water_mark);\n        } else if (target_type == \"thin\") {\n            if (!HasArgs(2)) {\n                std::cerr << \"Expected \\\"thin\\\" <pool dev> <dev id>\" << std::endl;\n                return nullptr;\n            }\n\n            std::string pool_dev = NextArg();\n            std::string dev_id_str = NextArg();\n\n            uint64_t dev_id;\n            if (!android::base::ParseUint(dev_id_str, &dev_id)) {\n                std::cerr << \"Dev id must be an unsigned integer.\\n\";\n                return nullptr;\n            }\n            return std::make_unique<DmTargetThin>(start_sector, num_sectors, pool_dev, dev_id);\n        } else {\n            std::cerr << \"Unrecognized target type: \" << target_type << std::endl;\n            return nullptr;\n        }\n    }\n\n  private:\n    bool HasArgs(int count) { return arg_index_ + count <= argc_; }\n    const char* NextArg() {\n        CHECK(arg_index_ < argc_);\n        return argv_[arg_index_++];\n    }\n    const char* PreviousArg() {\n        CHECK(arg_index_ >= 0);\n        return argv_[arg_index_ - 1];\n    }\n\n  private:\n    int arg_index_;\n    int argc_;\n    char** argv_;\n};\n\nstruct TableArgs {\n    DmTable table;\n    bool suspended = false;\n};\n\nstatic std::optional<TableArgs> parse_table_args(int argc, char** argv) {\n    TableArgs out;\n\n    // Parse extended options first.\n    int arg_index = 1;\n    while (arg_index < argc && argv[arg_index][0] == '-') {\n        if (strcmp(argv[arg_index], \"-ro\") == 0) {\n            out.table.set_readonly(true);\n            arg_index++;\n        } else if (strcmp(argv[arg_index], \"-suspended\") == 0) {\n            out.suspended = true;\n            arg_index++;\n        } else {\n            std::cerr << \"Unrecognized option: \" << argv[arg_index] << std::endl;\n            return {};\n        }\n    }\n\n    // Parse everything else as target information.\n    TargetParser parser(argc - arg_index, argv + arg_index);\n    while (parser.More()) {\n        std::unique_ptr<DmTarget> target = parser.Next();\n        if (!target || !out.table.AddTarget(std::move(target))) {\n            return {};\n        }\n    }\n\n    if (out.table.num_targets() == 0) {\n        std::cerr << \"Must define at least one target.\" << std::endl;\n        return {};\n    }\n    return {std::move(out)};\n}\n\nstatic int DmCreateCmdHandler(int argc, char** argv) {\n    if (argc < 1) {\n        std::cerr << \"Usage: dmctl create <dm-name> [--suspended] [-ro] <targets...>\" << std::endl;\n        return -EINVAL;\n    }\n    std::string name = argv[0];\n\n    auto table_args = parse_table_args(argc, argv);\n    if (!table_args) {\n        return -EINVAL;\n    }\n\n    std::string ignore_path;\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.CreateEmptyDevice(name)) {\n        std::cerr << \"Failed to create device-mapper device with name: \" << name << std::endl;\n        return -EIO;\n    }\n    if (!dm.LoadTable(name, table_args->table)) {\n        std::cerr << \"Failed to load table for dm device: \" << name << std::endl;\n        return -EIO;\n    }\n    if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) {\n        std::cerr << \"Failed to activate table for \" << name << std::endl;\n        return -EIO;\n    }\n    return 0;\n}\n\nstatic int DmDeleteCmdHandler(int argc, char** argv) {\n    if (argc < 1) {\n        std::cerr << \"Usage: dmctl delete <name>\" << std::endl;\n        return -EINVAL;\n    }\n\n    std::string name = argv[0];\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.DeleteDevice(name)) {\n        std::cerr << \"Failed to delete [\" << name << \"]\" << std::endl;\n        return -EIO;\n    }\n    return 0;\n}\n\nstatic int DmReplaceCmdHandler(int argc, char** argv) {\n    if (argc < 1) {\n        std::cerr << \"Usage: dmctl replace <dm-name> <targets...>\" << std::endl;\n        return -EINVAL;\n    }\n    std::string name = argv[0];\n\n    auto table_args = parse_table_args(argc, argv);\n    if (!table_args) {\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.LoadTable(name, table_args->table)) {\n        std::cerr << \"Failed to replace device-mapper table to: \" << name << std::endl;\n        return -EIO;\n    }\n    if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) {\n        std::cerr << \"Failed to activate table for \" << name << std::endl;\n        return -EIO;\n    }\n    return 0;\n}\n\nstatic int DmListTargets(DeviceMapper& dm, [[maybe_unused]] int argc,\n                         [[maybe_unused]] char** argv) {\n    std::vector<DmTargetTypeInfo> targets;\n    if (!dm.GetAvailableTargets(&targets)) {\n        std::cerr << \"Failed to read available device mapper targets\" << std::endl;\n        return -errno;\n    }\n\n    std::cout << \"Available Device Mapper Targets:\" << std::endl;\n    if (targets.empty()) {\n        std::cout << \"  <empty>\" << std::endl;\n        return 0;\n    }\n\n    for (const auto& target : targets) {\n        std::cout << std::left << std::setw(20) << target.name() << \" : \" << target.version()\n                  << std::endl;\n    }\n\n    return 0;\n}\n\nstatic int DmListDevices(DeviceMapper& dm, int argc, char** argv) {\n    std::vector<DmBlockDevice> devices;\n    if (!dm.GetAvailableDevices(&devices)) {\n        std::cerr << \"Failed to read available device mapper devices\" << std::endl;\n        return -errno;\n    }\n    std::cout << \"Available Device Mapper Devices:\" << std::endl;\n    if (devices.empty()) {\n        std::cout << \"  <empty>\" << std::endl;\n        return 0;\n    }\n\n    bool verbose = (argc && (argv[0] == \"-v\"s));\n    for (const auto& dev : devices) {\n        std::cout << std::left << std::setw(20) << dev.name() << \" : \" << dev.Major() << \":\"\n                  << dev.Minor() << std::endl;\n        if (verbose) {\n            std::vector<DeviceMapper::TargetInfo> table;\n            if (!dm.GetTableInfo(dev.name(), &table)) {\n                std::cerr << \"Could not query table status for device \\\"\" << dev.name() << \"\\\".\"\n                          << std::endl;\n                return -EINVAL;\n            }\n\n            uint32_t target_num = 1;\n            for (const auto& target : table) {\n                std::cout << \"  target#\" << target_num << \": \";\n                std::cout << target.spec.sector_start << \"-\"\n                          << (target.spec.sector_start + target.spec.length) << \": \"\n                          << target.spec.target_type;\n                if (!target.data.empty()) {\n                    std::cout << \", \" << target.data;\n                }\n                std::cout << std::endl;\n                target_num++;\n            }\n        }\n    }\n\n    return 0;\n}\n\nstatic const std::map<std::string, std::function<int(DeviceMapper&, int, char**)>> listmap = {\n        {\"targets\", DmListTargets},\n        {\"devices\", DmListDevices},\n};\n\nstatic int DmListCmdHandler(int argc, char** argv) {\n    if (argc < 1) {\n        std::cerr << \"Invalid arguments, see \\'dmctl help\\'\" << std::endl;\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    for (const auto& l : listmap) {\n        if (l.first == argv[0]) return l.second(dm, argc - 1, argv + 1);\n    }\n\n    std::cerr << \"Invalid argument to \\'dmctl list\\': \" << argv[0] << std::endl;\n    return -EINVAL;\n}\n\nstatic int DmMessageCmdHandler(int argc, char** argv) {\n    if (argc != 3) {\n        std::cerr << \"Usage: dmctl message <name> <sector> <message>\" << std::endl;\n        return -EINVAL;\n    }\n    uint64_t sector;\n    if (!android::base::ParseUint(argv[1], &sector)) {\n        std::cerr << \"Invalid argument for sector: \" << argv[1] << std::endl;\n        return -EINVAL;\n    }\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.SendMessage(argv[0], sector, argv[2])) {\n        std::cerr << \"Could not send message to \" << argv[0] << std::endl;\n        return -EINVAL;\n    }\n    return 0;\n}\n\nstatic int HelpCmdHandler(int /* argc */, char** /* argv */) {\n    Usage();\n    return 0;\n}\n\nstatic int GetPathCmdHandler(int argc, char** argv) {\n    if (argc != 1) {\n        std::cerr << \"Invalid arguments, see \\'dmctl help\\'\" << std::endl;\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    std::string path;\n    if (!dm.GetDmDevicePathByName(argv[0], &path)) {\n        std::cerr << \"Could not query path of device \\\"\" << argv[0] << \"\\\".\" << std::endl;\n        return -EINVAL;\n    }\n    std::cout << path << std::endl;\n    return 0;\n}\n\nstatic int GetUuidCmdHandler(int argc, char** argv) {\n    if (argc != 1) {\n        std::cerr << \"Invalid arguments, see \\'dmctl help\\'\" << std::endl;\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    std::string uuid;\n    if (!dm.GetDmDeviceUuidByName(argv[0], &uuid)) {\n        std::cerr << \"Could not query uuid of device \\\"\" << argv[0] << \"\\\".\" << std::endl;\n        return -EINVAL;\n    }\n    std::cout << uuid << std::endl;\n    return 0;\n}\n\nstatic int InfoCmdHandler(int argc, char** argv) {\n    if (argc != 1) {\n        std::cerr << \"Invalid arguments, see \\'dmctl help\\'\" << std::endl;\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    auto info = dm.GetDetailedInfo(argv[0]);\n    if (!info) {\n        std::cerr << \"Invalid device \\\"\" << argv[0] << \"\\\".\" << std::endl;\n        return -EINVAL;\n    }\n\n    constexpr int spacing = 14;\n    std::cout << std::left << std::setw(spacing) << \"device\"\n              << \": \" << argv[0] << std::endl;\n    std::cout << std::left << std::setw(spacing) << \"active\"\n              << \": \" << std::boolalpha << !info->IsSuspended() << std::endl;\n    std::cout << std::left << std::setw(spacing) << \"access\"\n              << \": \";\n    if (info->IsReadOnly()) {\n        std::cout << \"ro \";\n    } else {\n        std::cout << \"rw \";\n    }\n    std::cout << std::endl;\n    std::cout << std::left << std::setw(spacing) << \"activeTable\"\n              << \": \" << std::boolalpha << info->IsActiveTablePresent() << std::endl;\n    std::cout << std::left << std::setw(spacing) << \"inactiveTable\"\n              << \": \" << std::boolalpha << info->IsInactiveTablePresent() << std::endl;\n    std::cout << std::left << std::setw(spacing) << \"bufferFull\"\n              << \": \" << std::boolalpha << info->IsBufferFull() << std::endl;\n    return 0;\n}\n\nstatic int DumpTable(const std::string& mode, int argc, char** argv) {\n    if (argc != 1) {\n        std::cerr << \"Invalid arguments, see \\'dmctl help\\'\" << std::endl;\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    std::vector<DeviceMapper::TargetInfo> table;\n    if (mode == \"status\") {\n        if (!dm.GetTableStatus(argv[0], &table)) {\n            std::cerr << \"Could not query table status of device \\\"\" << argv[0] << \"\\\".\"\n                      << std::endl;\n            return -EINVAL;\n        }\n    } else if (mode == \"table\") {\n        if (!dm.GetTableInfo(argv[0], &table)) {\n            std::cerr << \"Could not query table status of device \\\"\" << argv[0] << \"\\\".\"\n                      << std::endl;\n            return -EINVAL;\n        }\n    } else if (mode == \"ima\") {\n        if (!dm.GetTableStatusIma(argv[0], &table)) {\n            std::cerr << \"Could not query table status of device \\\"\" << argv[0] << \"\\\".\"\n                      << std::endl;\n            return -EINVAL;\n        }\n    }\n\n    std::cout << \"Targets in the device-mapper table for \" << argv[0] << \":\" << std::endl;\n    for (const auto& target : table) {\n        std::cout << target.spec.sector_start << \"-\"\n                  << (target.spec.sector_start + target.spec.length) << \": \"\n                  << target.spec.target_type;\n        if (!target.data.empty()) {\n            std::cout << \", \" << target.data;\n        }\n        std::cout << std::endl;\n    }\n    return 0;\n}\n\nstatic int TableCmdHandler(int argc, char** argv) {\n    return DumpTable(\"table\", argc, argv);\n}\n\nstatic int StatusCmdHandler(int argc, char** argv) {\n    return DumpTable(\"status\", argc, argv);\n}\n\nstatic int ImaCmdHandler(int argc, char** argv) {\n    return DumpTable(\"ima\", argc, argv);\n}\n\nstatic int ResumeCmdHandler(int argc, char** argv) {\n    if (argc != 1) {\n        std::cerr << \"Invalid arguments, see \\'dmctl help\\'\" << std::endl;\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.ChangeState(argv[0], DmDeviceState::ACTIVE)) {\n        std::cerr << \"Could not resume device \\\"\" << argv[0] << \"\\\".\" << std::endl;\n        return -EINVAL;\n    }\n    return 0;\n}\n\nstatic int SuspendCmdHandler(int argc, char** argv) {\n    if (argc != 1) {\n        std::cerr << \"Invalid arguments, see \\'dmctl help\\'\" << std::endl;\n        return -EINVAL;\n    }\n\n    DeviceMapper& dm = DeviceMapper::Instance();\n    if (!dm.ChangeState(argv[0], DmDeviceState::SUSPENDED)) {\n        std::cerr << \"Could not suspend device \\\"\" << argv[0] << \"\\\".\" << std::endl;\n        return -EINVAL;\n    }\n    return 0;\n}\n\nstatic std::map<std::string, std::function<int(int, char**)>> cmdmap = {\n        // clang-format off\n        {\"create\", DmCreateCmdHandler},\n        {\"delete\", DmDeleteCmdHandler},\n        {\"replace\", DmReplaceCmdHandler},\n        {\"list\", DmListCmdHandler},\n        {\"message\", DmMessageCmdHandler},\n        {\"help\", HelpCmdHandler},\n        {\"getpath\", GetPathCmdHandler},\n        {\"getuuid\", GetUuidCmdHandler},\n        {\"info\", InfoCmdHandler},\n        {\"table\", TableCmdHandler},\n        {\"status\", StatusCmdHandler},\n        {\"ima\", ImaCmdHandler},\n        {\"resume\", ResumeCmdHandler},\n        {\"suspend\", SuspendCmdHandler},\n        // clang-format on\n};\n\nstatic bool ReadFile(const char* filename, std::vector<std::string>* args,\n                     std::vector<char*>* arg_ptrs) {\n    std::ifstream file(filename);\n    if (!file) return false;\n\n    std::string arg;\n    while (file >> arg) args->push_back(arg);\n\n    for (auto const& i : *args) arg_ptrs->push_back(const_cast<char*>(i.c_str()));\n    return true;\n}\n\nint main(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::StderrLogger);\n    if (argc < 2) {\n        return Usage();\n    }\n\n    std::vector<std::string> args;\n    std::vector<char*> arg_ptrs;\n    if (std::string(\"-f\") == argv[1]) {\n        if (argc != 3) {\n            return Usage();\n        }\n\n        args.push_back(argv[0]);\n        if (!ReadFile(argv[2], &args, &arg_ptrs)) {\n            return Usage();\n        }\n\n        argc = arg_ptrs.size();\n        argv = &arg_ptrs[0];\n    }\n\n    for (const auto& cmd : cmdmap) {\n        if (cmd.first == argv[1]) {\n            return cmd.second(argc - 2, argv + 2);\n        }\n    }\n\n    return Usage();\n}\n"
  },
  {
    "path": "fs_mgr/tools/dmuserd.cpp",
    "content": "// SPDX-License-Identifier: Apache-2.0\n\n#define _LARGEFILE64_SOURCE\n\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <poll.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/prctl.h>\n#include <unistd.h>\n#include <iostream>\n#include <string>\n\n#define SECTOR_SIZE ((__u64)512)\n#define BUFFER_BYTES 4096\n\n#define MAX(a, b) ((a) > (b) ? (a) : (b))\n\n/* This should be replaced with linux/dm-user.h. */\n#ifndef _LINUX_DM_USER_H\n#define _LINUX_DM_USER_H\n\n#include <linux/types.h>\n\n#define DM_USER_REQ_MAP_READ 0\n#define DM_USER_REQ_MAP_WRITE 1\n#define DM_USER_REQ_MAP_FLUSH 2\n#define DM_USER_REQ_MAP_DISCARD 3\n#define DM_USER_REQ_MAP_SECURE_ERASE 4\n#define DM_USER_REQ_MAP_WRITE_SAME 5\n#define DM_USER_REQ_MAP_WRITE_ZEROES 6\n#define DM_USER_REQ_MAP_ZONE_OPEN 7\n#define DM_USER_REQ_MAP_ZONE_CLOSE 8\n#define DM_USER_REQ_MAP_ZONE_FINISH 9\n#define DM_USER_REQ_MAP_ZONE_APPEND 10\n#define DM_USER_REQ_MAP_ZONE_RESET 11\n#define DM_USER_REQ_MAP_ZONE_RESET_ALL 12\n\n#define DM_USER_REQ_MAP_FLAG_FAILFAST_DEV 0x00001\n#define DM_USER_REQ_MAP_FLAG_FAILFAST_TRANSPORT 0x00002\n#define DM_USER_REQ_MAP_FLAG_FAILFAST_DRIVER 0x00004\n#define DM_USER_REQ_MAP_FLAG_SYNC 0x00008\n#define DM_USER_REQ_MAP_FLAG_META 0x00010\n#define DM_USER_REQ_MAP_FLAG_PRIO 0x00020\n#define DM_USER_REQ_MAP_FLAG_NOMERGE 0x00040\n#define DM_USER_REQ_MAP_FLAG_IDLE 0x00080\n#define DM_USER_REQ_MAP_FLAG_INTEGRITY 0x00100\n#define DM_USER_REQ_MAP_FLAG_FUA 0x00200\n#define DM_USER_REQ_MAP_FLAG_PREFLUSH 0x00400\n#define DM_USER_REQ_MAP_FLAG_RAHEAD 0x00800\n#define DM_USER_REQ_MAP_FLAG_BACKGROUND 0x01000\n#define DM_USER_REQ_MAP_FLAG_NOWAIT 0x02000\n#define DM_USER_REQ_MAP_FLAG_CGROUP_PUNT 0x04000\n#define DM_USER_REQ_MAP_FLAG_NOUNMAP 0x08000\n#define DM_USER_REQ_MAP_FLAG_HIPRI 0x10000\n#define DM_USER_REQ_MAP_FLAG_DRV 0x20000\n#define DM_USER_REQ_MAP_FLAG_SWAP 0x40000\n\n#define DM_USER_RESP_SUCCESS 0\n#define DM_USER_RESP_ERROR 1\n#define DM_USER_RESP_UNSUPPORTED 2\n\nstruct dm_user_message {\n    __u64 seq;\n    __u64 type;\n    __u64 flags;\n    __u64 sector;\n    __u64 len;\n    __u8 buf[];\n};\n\n#endif\n\nstatic bool verbose = false;\n\nssize_t write_all(int fd, void* buf, size_t len) {\n    char* buf_c = (char*)buf;\n    ssize_t total = 0;\n    ssize_t once;\n\n    while (total < static_cast<ssize_t>(len)) {\n        once = write(fd, buf_c + total, len - total);\n        if (once < 0) return once;\n        if (once == 0) {\n            errno = ENOSPC;\n            return 0;\n        }\n        total += once;\n    }\n\n    return total;\n}\n\nssize_t read_all(int fd, void* buf, size_t len) {\n    char* buf_c = (char*)buf;\n    ssize_t total = 0;\n    ssize_t once;\n\n    while (total < static_cast<ssize_t>(len)) {\n        once = read(fd, buf_c + total, len - total);\n        if (once < 0) return once;\n        if (once == 0) {\n            errno = ENOSPC;\n            return 0;\n        }\n        total += once;\n    }\n\n    return total;\n}\n\nint not_splice(int from, int to, __u64 count) {\n    while (count > 0) {\n        char buf[BUFFER_BYTES];\n        __u64 max = count > BUFFER_BYTES ? BUFFER_BYTES : count;\n\n        if (read_all(from, buf, max) <= 0) {\n            perror(\"Unable to read\");\n            return -EIO;\n        }\n\n        if (write_all(to, buf, max) <= 0) {\n            perror(\"Unable to write\");\n            return -EIO;\n        }\n\n        count -= max;\n    }\n\n    return 0;\n}\n\nstatic int simple_daemon(const std::string& control_path, const std::string& backing_path) {\n    int control_fd = open(control_path.c_str(), O_RDWR);\n    if (control_fd < 0) {\n        fprintf(stderr, \"Unable to open control device %s\\n\", control_path.c_str());\n        return -1;\n    }\n\n    int backing_fd = open(backing_path.c_str(), O_RDWR);\n    if (backing_fd < 0) {\n        fprintf(stderr, \"Unable to open backing device %s\\n\", backing_path.c_str());\n        return -1;\n    }\n\n    while (1) {\n        struct dm_user_message msg;\n        char* base;\n        __u64 type;\n\n        if (verbose) std::cerr << \"dmuserd: Waiting for message...\\n\";\n\n        if (read_all(control_fd, &msg, sizeof(msg)) < 0) {\n            if (errno == ENOTBLK) return 0;\n\n            perror(\"unable to read msg\");\n            return -1;\n        }\n\n        if (verbose) {\n            std::string type;\n            switch (msg.type) {\n                case DM_USER_REQ_MAP_WRITE:\n                    type = \"write\";\n                    break;\n                case DM_USER_REQ_MAP_READ:\n                    type = \"read\";\n                    break;\n                case DM_USER_REQ_MAP_FLUSH:\n                    type = \"flush\";\n                    break;\n                default:\n                    /*\n                     * FIXME: Can't I do \"whatever\"s here rather that\n                     * std::string(\"whatever\")?\n                     */\n                    type = std::string(\"(unknown, id=\") + std::to_string(msg.type) + \")\";\n                    break;\n            }\n\n            std::string flags;\n            if (msg.flags & DM_USER_REQ_MAP_FLAG_SYNC) {\n                if (!flags.empty()) flags += \"|\";\n                flags += \"S\";\n            }\n            if (msg.flags & DM_USER_REQ_MAP_FLAG_META) {\n                if (!flags.empty()) flags += \"|\";\n                flags += \"M\";\n            }\n            if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {\n                if (!flags.empty()) flags += \"|\";\n                flags += \"FUA\";\n            }\n            if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH) {\n                if (!flags.empty()) flags += \"|\";\n                flags += \"F\";\n            }\n\n            std::cerr << \"dmuserd: Got \" << type << \" request \" << flags << \" for sector \"\n                      << std::to_string(msg.sector) << \" with length \" << std::to_string(msg.len)\n                      << \"\\n\";\n        }\n\n        type = msg.type;\n        switch (type) {\n            case DM_USER_REQ_MAP_READ:\n                msg.type = DM_USER_RESP_SUCCESS;\n                break;\n            case DM_USER_REQ_MAP_WRITE:\n                if (msg.flags & DM_USER_REQ_MAP_FLAG_PREFLUSH ||\n                    msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {\n                    if (fsync(backing_fd) < 0) {\n                        perror(\"Unable to fsync(), just sync()ing instead\");\n                        sync();\n                    }\n                }\n                msg.type = DM_USER_RESP_SUCCESS;\n                if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {\n                    perror(\"Unable to seek\");\n                    return -1;\n                }\n                if (not_splice(control_fd, backing_fd, msg.len) < 0) {\n                    if (errno == ENOTBLK) return 0;\n                    std::cerr << \"unable to handle write data\\n\";\n                    return -1;\n                }\n                if (msg.flags & DM_USER_REQ_MAP_FLAG_FUA) {\n                    if (fsync(backing_fd) < 0) {\n                        perror(\"Unable to fsync(), just sync()ing instead\");\n                        sync();\n                    }\n                }\n                break;\n            case DM_USER_REQ_MAP_FLUSH:\n                msg.type = DM_USER_RESP_SUCCESS;\n                if (fsync(backing_fd) < 0) {\n                    perror(\"Unable to fsync(), just sync()ing instead\");\n                    sync();\n                }\n                break;\n            default:\n                std::cerr << \"dmuserd: unsupported op \" << std::to_string(msg.type) << \"\\n\";\n                msg.type = DM_USER_RESP_UNSUPPORTED;\n                break;\n        }\n\n        if (verbose) std::cerr << \"dmuserd: Responding to message\\n\";\n\n        if (write_all(control_fd, &msg, sizeof(msg)) < 0) {\n            if (errno == ENOTBLK) return 0;\n            perror(\"unable to write msg\");\n            return -1;\n        }\n\n        switch (type) {\n            case DM_USER_REQ_MAP_READ:\n                if (verbose) std::cerr << \"dmuserd: Sending read data\\n\";\n                if (lseek64(backing_fd, msg.sector * SECTOR_SIZE, SEEK_SET) < 0) {\n                    perror(\"Unable to seek\");\n                    return -1;\n                }\n                if (not_splice(backing_fd, control_fd, msg.len) < 0) {\n                    if (errno == ENOTBLK) return 0;\n                    std::cerr << \"unable to handle read data\\n\";\n                    return -1;\n                }\n                break;\n        }\n    }\n\n    /* The daemon doesn't actully terminate for this test. */\n    perror(\"Unable to read from control device\");\n    return -1;\n}\n\nvoid usage(char* prog) {\n    printf(\"Usage: %s\\n\", prog);\n    printf(\"\tHandles block requests in userspace, backed by memory\\n\");\n    printf(\"  -h\t\t\tDisplay this help message\\n\");\n    printf(\"  -c <control dev>\t\tControl device to use for the test\\n\");\n    printf(\"  -b <store path>\t\tThe file to use as a backing store, otherwise memory\\n\");\n    printf(\"  -v                        Enable verbose mode\\n\");\n}\n\nint main(int argc, char* argv[]) {\n    std::string control_path;\n    std::string backing_path;\n    char* store;\n    int c;\n\n    prctl(PR_SET_IO_FLUSHER, 0, 0, 0, 0);\n\n    while ((c = getopt(argc, argv, \"h:c:s:b:v\")) != -1) {\n        switch (c) {\n            case 'h':\n                usage(basename(argv[0]));\n                exit(0);\n            case 'c':\n                control_path = optarg;\n                break;\n            case 'b':\n                backing_path = optarg;\n                break;\n            case 'v':\n                verbose = true;\n                break;\n            default:\n                usage(basename(argv[0]));\n                exit(1);\n        }\n    }\n\n    int r = simple_daemon(control_path, backing_path);\n    if (r) fprintf(stderr, \"simple_daemon() errored out\\n\");\n    return r;\n}\n"
  },
  {
    "path": "gatekeeperd/Android.bp",
    "content": "//\n// Copyright (C) 2015 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"gatekeeperd_defaults\",\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n        \"-Wunused\",\n    ],\n    srcs: [\n        \"gatekeeperd.cpp\",\n    ],\n    defaults: [\n        \"keymint_use_latest_hal_aidl_ndk_shared\",\n    ],\n    shared_libs: [\n        \"libbinder\",\n        \"libbinder_ndk\",\n        \"libgatekeeper\",\n        \"libgsi\",\n        \"liblog\",\n        \"libhardware\",\n        \"libbase\",\n        \"libutils\",\n        \"libcrypto\",\n        \"libhidlbase\",\n        \"lib_android_keymaster_keymint_utils\",\n        \"android.hardware.gatekeeper-V1-ndk\",\n        \"android.hardware.gatekeeper@1.0\",\n        \"libgatekeeper_aidl\",\n        \"android.security.authorization-ndk\",\n    ],\n\n    static_libs: [\"libscrypt_static\"],\n    include_dirs: [\"external/scrypt/lib/crypto\"],\n}\n\ncc_binary {\n    name: \"gatekeeperd\",\n    defaults: [\n        \"gatekeeperd_defaults\",\n    ],\n    srcs: [\n        \"main.cpp\",\n    ],\n    init_rc: [\"gatekeeperd.rc\"],\n}\n\nfilegroup {\n    name: \"gatekeeper_aidl\",\n    srcs: [\n        \"binder/android/service/gatekeeper/IGateKeeperService.aidl\",\n    ],\n    path: \"binder\",\n}\n\ncc_library_shared {\n    name: \"libgatekeeper_aidl\",\n    srcs: [\n        \":gatekeeper_aidl\",\n        \"GateKeeperResponse.cpp\",\n    ],\n    aidl: {\n        export_aidl_headers: true,\n        include_dirs: [\n            \"system/core/gatekeeperd/binder\",\n            \"frameworks/base/core/java/\",\n        ],\n    },\n    export_include_dirs: [\"include\"],\n    shared_libs: [\n        \"libbase\",\n        \"libbinder\",\n        \"libcutils\",\n        \"liblog\",\n        \"libutils\",\n    ],\n    export_shared_lib_headers: [\n        \"libbinder\",\n    ],\n}\n\ncc_fuzz {\n    name: \"gatekeeperd_service_fuzzer\",\n    defaults: [\n        \"gatekeeperd_defaults\",\n        \"service_fuzzer_defaults\"\n    ],\n    srcs: [\n        \"fuzzer/GateKeeperServiceFuzzer.cpp\",\n    ],\n    fuzz_config: {\n        cc: [\n            \"subrahmanyaman@google.com\",\n            \"swillden@google.com\",\n        ],\n    },\n}"
  },
  {
    "path": "gatekeeperd/GateKeeperResponse.cpp",
    "content": "/*\n**\n** Copyright 2019, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n\n#define LOG_TAG \"gatekeeperd\"\n\n#include <gatekeeper/GateKeeperResponse.h>\n\n#include <binder/Parcel.h>\n\n#include <android-base/logging.h>\n\nnamespace android {\nnamespace service {\nnamespace gatekeeper {\n\nstatus_t GateKeeperResponse::readFromParcel(const Parcel* in) {\n    if (in == nullptr) {\n        LOG(ERROR) << \"readFromParcel got null in parameter\";\n        return BAD_VALUE;\n    }\n    timeout_ = 0;\n    should_reenroll_ = false;\n    payload_ = {};\n    response_code_ = ResponseCode(in->readInt32());\n    if (response_code_ == ResponseCode::OK) {\n        should_reenroll_ = in->readInt32();\n        ssize_t length = in->readInt32();\n        if (length > 0) {\n            length = in->readInt32();\n            const uint8_t* buf = reinterpret_cast<const uint8_t*>(in->readInplace(length));\n            if (buf == nullptr) {\n                LOG(ERROR) << \"readInplace returned null buffer for length \" << length;\n                return BAD_VALUE;\n            }\n            payload_.resize(length);\n            std::copy(buf, buf + length, payload_.data());\n        }\n    } else if (response_code_ == ResponseCode::RETRY) {\n        timeout_ = in->readInt32();\n    }\n    return NO_ERROR;\n}\nstatus_t GateKeeperResponse::writeToParcel(Parcel* out) const {\n    if (out == nullptr) {\n        LOG(ERROR) << \"writeToParcel got null out parameter\";\n        return BAD_VALUE;\n    }\n    out->writeInt32(int32_t(response_code_));\n    if (response_code_ == ResponseCode::OK) {\n        out->writeInt32(should_reenroll_);\n        out->writeInt32(payload_.size());\n        if (payload_.size() != 0) {\n            out->writeInt32(payload_.size());\n            uint8_t* buf = reinterpret_cast<uint8_t*>(out->writeInplace(payload_.size()));\n            if (buf == nullptr) {\n                LOG(ERROR) << \"writeInplace returned null buffer for length \" << payload_.size();\n                return BAD_VALUE;\n            }\n            std::copy(payload_.begin(), payload_.end(), buf);\n        }\n    } else if (response_code_ == ResponseCode::RETRY) {\n        out->writeInt32(timeout_);\n    }\n    return NO_ERROR;\n}\n\n}  // namespace gatekeeper\n}  // namespace service\n}  // namespace android\n"
  },
  {
    "path": "gatekeeperd/OWNERS",
    "content": "# Bug component: 1124862\ndrysdale@google.com\noarbildo@google.com\nswillden@google.com\n"
  },
  {
    "path": "gatekeeperd/binder/android/service/gatekeeper/GateKeeperResponse.aidl",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.service.gatekeeper;\n\n/**\n * Response object for a GateKeeper verification request.\n * @hide\n */\nparcelable GateKeeperResponse cpp_header \"gatekeeper/GateKeeperResponse.h\";\n\n"
  },
  {
    "path": "gatekeeperd/binder/android/service/gatekeeper/IGateKeeperService.aidl",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.service.gatekeeper;\n\nimport android.service.gatekeeper.GateKeeperResponse;\n\n/**\n * Interface for communication with GateKeeper, the\n * secure password storage daemon.\n *\n * This must be kept manually in sync with system/core/gatekeeperd\n * until AIDL can generate both C++ and Java bindings.\n *\n * @hide\n */\n@SensitiveData\ninterface IGateKeeperService {\n    /**\n     * Enrolls a password, returning the handle to the enrollment to be stored locally.\n     * @param userId The Android user ID associated to this enrollment\n     * @param currentPasswordHandle The previously enrolled handle, or null if none\n     * @param currentPassword The previously enrolled plaintext password, or null if none.\n     *                        If provided, must verify against the currentPasswordHandle.\n     * @param desiredPassword The new desired password, for which a handle will be returned\n     *                        upon success.\n     * @return an EnrollResponse or null on failure\n     */\n    GateKeeperResponse enroll(int userId, in @nullable byte[] currentPasswordHandle,\n            in @nullable byte[] currentPassword, in byte[] desiredPassword);\n\n    /**\n     * Verifies an enrolled handle against a provided, plaintext blob.\n     * @param userId The Android user ID associated to this enrollment\n     * @param enrolledPasswordHandle The handle against which the provided password will be\n     *                               verified.\n     * @param The plaintext blob to verify against enrolledPassword.\n     * @return a VerifyResponse, or null on failure.\n     */\n    GateKeeperResponse verify(int userId, in byte[] enrolledPasswordHandle, in byte[] providedPassword);\n\n    /**\n     * Verifies an enrolled handle against a provided, plaintext blob.\n     * @param userId The Android user ID associated to this enrollment\n     * @param challenge a challenge to authenticate agaisnt the device credential. If successful\n     *                  authentication occurs, this value will be written to the returned\n     *                  authentication attestation.\n     * @param enrolledPasswordHandle The handle against which the provided password will be\n     *                               verified.\n     * @param The plaintext blob to verify against enrolledPassword.\n     * @return a VerifyResponse with an attestation, or null on failure.\n     */\n    GateKeeperResponse verifyChallenge(int userId, long challenge, in byte[] enrolledPasswordHandle,\n            in byte[] providedPassword);\n\n    /**\n     * Retrieves the secure identifier for the user with the provided Android ID,\n     * or 0 if none is found.\n     * @param userId the Android user id\n     */\n    long getSecureUserId(int userId);\n\n    /**\n     * Clears secure user id associated with the provided Android ID.\n     * Must be called when password is set to NONE.\n     * @param userId the Android user id.\n     */\n    void clearSecureUserId(int userId);\n\n    /**\n     * Notifies gatekeeper that device setup has been completed and any potentially still existing\n     * state from before a factory reset can be cleaned up (if it has not been already).\n     */\n    void reportDeviceSetupComplete();\n}\n"
  },
  {
    "path": "gatekeeperd/fuzzer/GateKeeperServiceFuzzer.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fuzzbinder/libbinder_driver.h>\n\n#include \"gatekeeperd.h\"\n\nusing android::fuzzService;\nusing android::GateKeeperProxy;\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    // TODO(b/183141167): need to rewrite 'dump' to avoid SIGPIPE.\n    signal(SIGPIPE, SIG_IGN);\n    auto gatekeeperService = new GateKeeperProxy();\n    fuzzService(gatekeeperService, FuzzedDataProvider(data, size));\n    return 0;\n}"
  },
  {
    "path": "gatekeeperd/gatekeeperd.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#define LOG_TAG \"gatekeeperd\"\n\n#include \"gatekeeperd.h\"\n\n#include <endian.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <memory>\n\n#include <KeyMintUtils.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android/binder_ibinder.h>\n#include <android/binder_manager.h>\n#include <binder/IPCThreadState.h>\n#include <binder/IServiceManager.h>\n#include <binder/PermissionCache.h>\n#include <gatekeeper/password_handle.h>  // for password_handle_t\n#include <hardware/hw_auth_token.h>\n#include <libgsi/libgsi.h>\n#include <log/log.h>\n#include <utils/String16.h>\n\n#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>\n#include <aidl/android/security/authorization/IKeystoreAuthorization.h>\n#include <hidl/HidlSupport.h>\n\nusing android::sp;\nusing android::hardware::Return;\nusing android::hardware::gatekeeper::V1_0::GatekeeperResponse;\nusing android::hardware::gatekeeper::V1_0::GatekeeperStatusCode;\n\nusing AidlGatekeeperEnrollResp = aidl::android::hardware::gatekeeper::GatekeeperEnrollResponse;\nusing AidlGatekeeperVerifyResp = aidl::android::hardware::gatekeeper::GatekeeperVerifyResponse;\n\nusing GKResponseCode = ::android::service::gatekeeper::ResponseCode;\nusing ::aidl::android::hardware::security::keymint::HardwareAuthenticatorType;\nusing ::aidl::android::hardware::security::keymint::HardwareAuthToken;\nusing ::aidl::android::hardware::security::keymint::km_utils::authToken2AidlVec;\nusing ::aidl::android::security::authorization::IKeystoreAuthorization;\n\nnamespace android {\n\nstatic const String16 KEYGUARD_PERMISSION(\"android.permission.ACCESS_KEYGUARD_SECURE_STORAGE\");\nstatic const String16 DUMP_PERMISSION(\"android.permission.DUMP\");\nconstexpr const char gatekeeperServiceName[] = \"android.hardware.gatekeeper.IGatekeeper/default\";\n\nGateKeeperProxy::GateKeeperProxy() {\n    clear_state_if_needed_done = false;\n    if (AServiceManager_isDeclared(gatekeeperServiceName)) {\n        ::ndk::SpAIBinder ks2Binder(AServiceManager_waitForService(gatekeeperServiceName));\n        aidl_hw_device = AidlIGatekeeper::fromBinder(ks2Binder);\n    }\n    if (!aidl_hw_device) {\n        hw_device = IGatekeeper::getService();\n    }\n    is_running_gsi = android::base::GetBoolProperty(android::gsi::kGsiBootedProp, false);\n\n    if (!aidl_hw_device && !hw_device) {\n        LOG(ERROR) << \"Could not find Gatekeeper device, which makes me very sad.\";\n    }\n}\n\nvoid GateKeeperProxy::store_sid(uint32_t userId, uint64_t sid) {\n    char filename[21];\n    snprintf(filename, sizeof(filename), \"%u\", userId);\n    int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);\n    if (fd < 0) {\n        ALOGE(\"could not open file: %s: %s\", filename, strerror(errno));\n        return;\n    }\n    write(fd, &sid, sizeof(sid));\n    close(fd);\n}\n\nvoid GateKeeperProxy::clear_state_if_needed() {\n    if (clear_state_if_needed_done) {\n        return;\n    }\n\n    if (mark_cold_boot() && !is_running_gsi) {\n        ALOGI(\"cold boot: clearing state\");\n        if (aidl_hw_device) {\n            aidl_hw_device->deleteAllUsers();\n        } else if (hw_device) {\n            hw_device->deleteAllUsers([](const GatekeeperResponse&) {});\n        }\n    }\n\n    clear_state_if_needed_done = true;\n}\n\nbool GateKeeperProxy::mark_cold_boot() {\n    const char* filename = \".coldboot\";\n    if (access(filename, F_OK) == -1) {\n        int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);\n        if (fd < 0) {\n            ALOGE(\"could not open file: %s : %s\", filename, strerror(errno));\n            return false;\n        }\n        close(fd);\n        return true;\n    }\n    return false;\n}\n\nvoid GateKeeperProxy::maybe_store_sid(uint32_t userId, uint64_t sid) {\n    char filename[21];\n    snprintf(filename, sizeof(filename), \"%u\", userId);\n    if (access(filename, F_OK) == -1) {\n        store_sid(userId, sid);\n    }\n}\n\nuint64_t GateKeeperProxy::read_sid(uint32_t userId) {\n    char filename[21];\n    uint64_t sid;\n    snprintf(filename, sizeof(filename), \"%u\", userId);\n    int fd = open(filename, O_RDONLY);\n    if (fd < 0) return 0;\n    read(fd, &sid, sizeof(sid));\n    close(fd);\n    return sid;\n}\n\nvoid GateKeeperProxy::clear_sid(uint32_t userId) {\n    char filename[21];\n    snprintf(filename, sizeof(filename), \"%u\", userId);\n    if (remove(filename) < 0 && errno != ENOENT) {\n        ALOGE(\"%s: could not remove file [%s], attempting 0 write\", __func__, strerror(errno));\n        store_sid(userId, 0);\n    }\n}\n\nStatus GateKeeperProxy::adjust_userId(uint32_t userId, uint32_t* hw_userId) {\n    static constexpr uint32_t kGsiOffset = 1000000;\n    if (userId >= kGsiOffset) {\n        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);\n    }\n\n    if ((aidl_hw_device == nullptr) && (hw_device == nullptr)) {\n        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);\n    }\n\n    if (is_running_gsi) {\n        *hw_userId = userId + kGsiOffset;\n        return Status::ok();\n    }\n    *hw_userId = userId;\n    return Status::ok();\n}\n\n#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()\n\nStatus GateKeeperProxy::enroll(int32_t userId,\n                               const std::optional<std::vector<uint8_t>>& currentPasswordHandle,\n                               const std::optional<std::vector<uint8_t>>& currentPassword,\n                               const std::vector<uint8_t>& desiredPassword,\n                               GKResponse* gkResponse) {\n    IPCThreadState* ipc = IPCThreadState::self();\n    const int calling_pid = ipc->getCallingPid();\n    const int calling_uid = ipc->getCallingUid();\n    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {\n        return GK_ERROR;\n    }\n\n    // Make sure to clear any state from before factory reset as soon as a credential is\n    // enrolled (which may happen during device setup).\n    clear_state_if_needed();\n\n    // need a desired password to enroll\n    if (desiredPassword.size() == 0) return GK_ERROR;\n\n    if (!aidl_hw_device && !hw_device) {\n        LOG(ERROR) << \"has no HAL to talk to\";\n        return GK_ERROR;\n    }\n\n    android::hardware::hidl_vec<uint8_t> curPwdHandle;\n    android::hardware::hidl_vec<uint8_t> curPwd;\n\n    if (currentPasswordHandle && currentPassword) {\n        if (hw_device) {\n            // Hidl Implementations expects passwordHandle to be in\n            // gatekeeper::password_handle_t format.\n            if (currentPasswordHandle->size() != sizeof(gatekeeper::password_handle_t)) {\n                LOG(INFO) << \"Password handle has wrong length\";\n                return GK_ERROR;\n            }\n        }\n        curPwdHandle.setToExternal(const_cast<uint8_t*>(currentPasswordHandle->data()),\n                                   currentPasswordHandle->size());\n        curPwd.setToExternal(const_cast<uint8_t*>(currentPassword->data()),\n                             currentPassword->size());\n    }\n\n    android::hardware::hidl_vec<uint8_t> newPwd;\n    newPwd.setToExternal(const_cast<uint8_t*>(desiredPassword.data()), desiredPassword.size());\n\n    uint32_t hw_userId = 0;\n    Status result = adjust_userId(userId, &hw_userId);\n    if (!result.isOk()) {\n        return result;\n    }\n\n    uint64_t secureUserId = 0;\n    if (aidl_hw_device) {\n        // AIDL gatekeeper service\n        AidlGatekeeperEnrollResp rsp;\n        auto result = aidl_hw_device->enroll(hw_userId, curPwdHandle, curPwd, newPwd, &rsp);\n        if (!result.isOk()) {\n            LOG(ERROR) << \"enroll transaction failed\";\n            return GK_ERROR;\n        }\n        if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {\n            *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});\n            secureUserId = static_cast<uint64_t>(rsp.secureUserId);\n        } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT && rsp.timeoutMs > 0) {\n            *gkResponse = GKResponse::retry(rsp.timeoutMs);\n        } else {\n            *gkResponse = GKResponse::error();\n        }\n    } else if (hw_device) {\n        // HIDL gatekeeper service\n        Return<void> hwRes = hw_device->enroll(\n                hw_userId, curPwdHandle, curPwd, newPwd,\n                [&gkResponse](const GatekeeperResponse& rsp) {\n                    if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {\n                        *gkResponse = GKResponse::ok({rsp.data.begin(), rsp.data.end()});\n                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT &&\n                               rsp.timeout > 0) {\n                        *gkResponse = GKResponse::retry(rsp.timeout);\n                    } else {\n                        *gkResponse = GKResponse::error();\n                    }\n                });\n        if (!hwRes.isOk()) {\n            LOG(ERROR) << \"enroll transaction failed\";\n            return GK_ERROR;\n        }\n        if (gkResponse->response_code() == GKResponseCode::OK) {\n            if (gkResponse->payload().size() != sizeof(gatekeeper::password_handle_t)) {\n                LOG(ERROR) << \"HAL returned password handle of invalid length \"\n                           << gkResponse->payload().size();\n                return GK_ERROR;\n            }\n\n            const gatekeeper::password_handle_t* handle =\n                    reinterpret_cast<const gatekeeper::password_handle_t*>(\n                            gkResponse->payload().data());\n            secureUserId = handle->user_id;\n        }\n    }\n\n    if (gkResponse->response_code() == GKResponseCode::OK && !gkResponse->should_reenroll()) {\n        store_sid(userId, secureUserId);\n\n        GKResponse verifyResponse;\n        // immediately verify this password so we don't ask the user to enter it again\n        // if they just created it.\n        auto status = verify(userId, gkResponse->payload(), desiredPassword, &verifyResponse);\n        if (!status.isOk() || verifyResponse.response_code() != GKResponseCode::OK) {\n            LOG(ERROR) << \"Failed to verify password after enrolling\";\n        }\n    }\n\n    return Status::ok();\n}\n\nStatus GateKeeperProxy::verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,\n                               const ::std::vector<uint8_t>& providedPassword,\n                               GKResponse* gkResponse) {\n    return verifyChallenge(userId, 0 /* challenge */, enrolledPasswordHandle, providedPassword,\n                           gkResponse);\n}\n\nStatus GateKeeperProxy::verifyChallenge(int32_t userId, int64_t challenge,\n                                        const std::vector<uint8_t>& enrolledPasswordHandle,\n                                        const std::vector<uint8_t>& providedPassword,\n                                        GKResponse* gkResponse) {\n    IPCThreadState* ipc = IPCThreadState::self();\n    const int calling_pid = ipc->getCallingPid();\n    const int calling_uid = ipc->getCallingUid();\n    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {\n        return GK_ERROR;\n    }\n\n    // can't verify if we're missing either param\n    if (enrolledPasswordHandle.size() == 0 || providedPassword.size() == 0) return GK_ERROR;\n\n    if (!aidl_hw_device && !hw_device) {\n        LOG(ERROR) << \"has no HAL to talk to\";\n        return GK_ERROR;\n    }\n\n    if (hw_device) {\n        // Hidl Implementations expects passwordHandle to be in gatekeeper::password_handle_t\n        if (enrolledPasswordHandle.size() != sizeof(gatekeeper::password_handle_t)) {\n            LOG(INFO) << \"Password handle has wrong length\";\n            return GK_ERROR;\n        }\n    }\n\n    uint32_t hw_userId = 0;\n    Status result = adjust_userId(userId, &hw_userId);\n    if (!result.isOk()) {\n        return result;\n    }\n\n    android::hardware::hidl_vec<uint8_t> curPwdHandle;\n    curPwdHandle.setToExternal(const_cast<uint8_t*>(enrolledPasswordHandle.data()),\n                               enrolledPasswordHandle.size());\n    android::hardware::hidl_vec<uint8_t> enteredPwd;\n    enteredPwd.setToExternal(const_cast<uint8_t*>(providedPassword.data()),\n                             providedPassword.size());\n\n    uint64_t secureUserId = 0;\n    if (aidl_hw_device) {\n        // AIDL gatekeeper service\n        AidlGatekeeperVerifyResp rsp;\n        auto result = aidl_hw_device->verify(hw_userId, challenge, curPwdHandle, enteredPwd, &rsp);\n        if (!result.isOk()) {\n            LOG(ERROR) << \"verify transaction failed\";\n            return GK_ERROR;\n        }\n        if (rsp.statusCode >= AidlIGatekeeper::STATUS_OK) {\n            secureUserId = rsp.hardwareAuthToken.userId;\n            // Serialize HardwareAuthToken to a vector as hw_auth_token_t.\n            *gkResponse = GKResponse::ok(\n                    authToken2AidlVec(rsp.hardwareAuthToken),\n                    rsp.statusCode == AidlIGatekeeper::STATUS_REENROLL /* reenroll */);\n        } else if (rsp.statusCode == AidlIGatekeeper::ERROR_RETRY_TIMEOUT) {\n            *gkResponse = GKResponse::retry(rsp.timeoutMs);\n        } else {\n            *gkResponse = GKResponse::error();\n        }\n    } else if (hw_device) {\n        // HIDL gatekeeper service\n        Return<void> hwRes = hw_device->verify(\n                hw_userId, challenge, curPwdHandle, enteredPwd,\n                [&gkResponse](const GatekeeperResponse& rsp) {\n                    if (rsp.code >= GatekeeperStatusCode::STATUS_OK) {\n                        *gkResponse = GKResponse::ok(\n                                {rsp.data.begin(), rsp.data.end()},\n                                rsp.code == GatekeeperStatusCode::STATUS_REENROLL /* reenroll */);\n                    } else if (rsp.code == GatekeeperStatusCode::ERROR_RETRY_TIMEOUT) {\n                        *gkResponse = GKResponse::retry(rsp.timeout);\n                    } else {\n                        *gkResponse = GKResponse::error();\n                    }\n                });\n\n        if (!hwRes.isOk()) {\n            LOG(ERROR) << \"verify transaction failed\";\n            return GK_ERROR;\n        }\n        const gatekeeper::password_handle_t* handle =\n                reinterpret_cast<const gatekeeper::password_handle_t*>(\n                        enrolledPasswordHandle.data());\n        secureUserId = handle->user_id;\n    }\n\n    if (gkResponse->response_code() == GKResponseCode::OK) {\n        if (gkResponse->payload().size() != 0) {\n            // try to connect to IKeystoreAuthorization AIDL service first.\n            AIBinder* authzAIBinder = AServiceManager_getService(\"android.security.authorization\");\n            ::ndk::SpAIBinder authzBinder(authzAIBinder);\n            auto authzService = IKeystoreAuthorization::fromBinder(authzBinder);\n            if (authzService) {\n                if (gkResponse->payload().size() != sizeof(hw_auth_token_t)) {\n                    LOG(ERROR) << \"Incorrect size of AuthToken payload.\";\n                    return GK_ERROR;\n                }\n\n                const hw_auth_token_t* hwAuthToken =\n                        reinterpret_cast<const hw_auth_token_t*>(gkResponse->payload().data());\n                HardwareAuthToken authToken;\n\n                authToken.timestamp.milliSeconds = betoh64(hwAuthToken->timestamp);\n                authToken.challenge = hwAuthToken->challenge;\n                authToken.userId = hwAuthToken->user_id;\n                authToken.authenticatorId = hwAuthToken->authenticator_id;\n                authToken.authenticatorType = static_cast<HardwareAuthenticatorType>(\n                        betoh32(hwAuthToken->authenticator_type));\n                authToken.mac.assign(&hwAuthToken->hmac[0], &hwAuthToken->hmac[32]);\n                auto result = authzService->addAuthToken(authToken);\n                if (!result.isOk()) {\n                    LOG(ERROR) << \"Failure in sending AuthToken to AuthorizationService.\";\n                    return GK_ERROR;\n                }\n            } else {\n                LOG(ERROR) << \"Cannot deliver auth token. Unable to communicate with \"\n                              \"Keystore.\";\n                return GK_ERROR;\n            }\n        }\n\n        maybe_store_sid(userId, secureUserId);\n    }\n\n    return Status::ok();\n}\n\nStatus GateKeeperProxy::getSecureUserId(int32_t userId, int64_t* sid) {\n    *sid = read_sid(userId);\n    return Status::ok();\n}\n\nStatus GateKeeperProxy::clearSecureUserId(int32_t userId) {\n    IPCThreadState* ipc = IPCThreadState::self();\n    const int calling_pid = ipc->getCallingPid();\n    const int calling_uid = ipc->getCallingUid();\n    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {\n        ALOGE(\"%s: permission denied for [%d:%d]\", __func__, calling_pid, calling_uid);\n        return Status::ok();\n    }\n    clear_sid(userId);\n\n    uint32_t hw_userId = 0;\n    Status result = adjust_userId(userId, &hw_userId);\n    if (!result.isOk()) {\n        return result;\n    }\n\n    if (aidl_hw_device) {\n        aidl_hw_device->deleteUser(hw_userId);\n    } else if (hw_device) {\n        hw_device->deleteUser(hw_userId, [](const GatekeeperResponse&) {});\n    }\n    return Status::ok();\n}\n\nStatus GateKeeperProxy::reportDeviceSetupComplete() {\n    IPCThreadState* ipc = IPCThreadState::self();\n    const int calling_pid = ipc->getCallingPid();\n    const int calling_uid = ipc->getCallingUid();\n    if (!PermissionCache::checkPermission(KEYGUARD_PERMISSION, calling_pid, calling_uid)) {\n        ALOGE(\"%s: permission denied for [%d:%d]\", __func__, calling_pid, calling_uid);\n        return Status::ok();\n    }\n\n    clear_state_if_needed();\n    return Status::ok();\n}\n\nstatus_t GateKeeperProxy::dump(int fd, const Vector<String16>&) {\n    IPCThreadState* ipc = IPCThreadState::self();\n    const int pid = ipc->getCallingPid();\n    const int uid = ipc->getCallingUid();\n    if (!PermissionCache::checkPermission(DUMP_PERMISSION, pid, uid)) {\n        return PERMISSION_DENIED;\n    }\n\n    if (aidl_hw_device == nullptr && hw_device == nullptr) {\n        const char* result = \"Device not available\";\n        write(fd, result, strlen(result) + 1);\n    } else {\n        const char* result = \"OK\";\n        write(fd, result, strlen(result) + 1);\n    }\n\n    return OK;\n}\n}  // namespace android\n"
  },
  {
    "path": "gatekeeperd/gatekeeperd.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <aidl/android/hardware/gatekeeper/IGatekeeper.h>\n#include <android/hardware/gatekeeper/1.0/IGatekeeper.h>\n#include <android/service/gatekeeper/BnGateKeeperService.h>\n#include <gatekeeper/GateKeeperResponse.h>\n\nusing ::android::hardware::gatekeeper::V1_0::IGatekeeper;\nusing AidlIGatekeeper = ::aidl::android::hardware::gatekeeper::IGatekeeper;\nusing ::android::binder::Status;\nusing ::android::service::gatekeeper::BnGateKeeperService;\nusing GKResponse = ::android::service::gatekeeper::GateKeeperResponse;\n\nnamespace android {\n\nclass GateKeeperProxy : public BnGateKeeperService {\n  public:\n    GateKeeperProxy();\n\n    virtual ~GateKeeperProxy() {}\n\n    void store_sid(uint32_t userId, uint64_t sid);\n\n    void clear_state_if_needed();\n\n    bool mark_cold_boot();\n\n    void maybe_store_sid(uint32_t userId, uint64_t sid);\n\n    uint64_t read_sid(uint32_t userId);\n\n    void clear_sid(uint32_t userId);\n\n    // This should only be called on userIds being passed to the GateKeeper HAL. It ensures that\n    // secure storage shared across a GSI image and a host image will not overlap.\n    Status adjust_userId(uint32_t userId, uint32_t* hw_userId);\n\n#define GK_ERROR *gkResponse = GKResponse::error(), Status::ok()\n\n    Status enroll(int32_t userId, const std::optional<std::vector<uint8_t>>& currentPasswordHandle,\n                  const std::optional<std::vector<uint8_t>>& currentPassword,\n                  const std::vector<uint8_t>& desiredPassword, GKResponse* gkResponse) override;\n\n    Status verify(int32_t userId, const ::std::vector<uint8_t>& enrolledPasswordHandle,\n                  const ::std::vector<uint8_t>& providedPassword, GKResponse* gkResponse) override;\n\n    Status verifyChallenge(int32_t userId, int64_t challenge,\n                           const std::vector<uint8_t>& enrolledPasswordHandle,\n                           const std::vector<uint8_t>& providedPassword,\n                           GKResponse* gkResponse) override;\n\n    Status getSecureUserId(int32_t userId, int64_t* sid) override;\n\n    Status clearSecureUserId(int32_t userId) override;\n\n    Status reportDeviceSetupComplete() override;\n\n    status_t dump(int fd, const Vector<String16>&) override;\n\n  private:\n    // AIDL gatekeeper service.\n    std::shared_ptr<AidlIGatekeeper> aidl_hw_device;\n    // HIDL gatekeeper service.\n    sp<IGatekeeper> hw_device;\n\n    bool clear_state_if_needed_done;\n    bool is_running_gsi;\n};\n}  // namespace android\n"
  },
  {
    "path": "gatekeeperd/gatekeeperd.rc",
    "content": "service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper\n    class late_start\n    user system\n    task_profiles ServiceCapacityLow\n"
  },
  {
    "path": "gatekeeperd/include/gatekeeper/GateKeeperResponse.h",
    "content": "/*\n**\n** Copyright 2019, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n\n#ifndef GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_\n#define GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_\n\n#include <binder/Parcelable.h>\n\nnamespace android {\nnamespace service {\nnamespace gatekeeper {\n\nenum class ResponseCode : int32_t {\n    ERROR = -1,\n    OK = 0,\n    RETRY = 1,\n};\n\nclass GateKeeperResponse : public ::android::Parcelable {\n    GateKeeperResponse(ResponseCode response_code, int32_t timeout = 0,\n                       std::vector<uint8_t> payload = {}, bool should_reenroll = false)\n        : response_code_(response_code),\n          timeout_(timeout),\n          payload_(std::move(payload)),\n          should_reenroll_(should_reenroll) {}\n\n  public:\n    GateKeeperResponse() = default;\n    GateKeeperResponse(GateKeeperResponse&&) = default;\n    GateKeeperResponse(const GateKeeperResponse&) = default;\n    GateKeeperResponse& operator=(GateKeeperResponse&&) = default;\n\n    static GateKeeperResponse error() { return GateKeeperResponse(ResponseCode::ERROR); }\n    static GateKeeperResponse retry(int32_t timeout) {\n        return GateKeeperResponse(ResponseCode::RETRY, timeout);\n    }\n    static GateKeeperResponse ok(std::vector<uint8_t> payload, bool reenroll = false) {\n        return GateKeeperResponse(ResponseCode::OK, 0, std::move(payload), reenroll);\n    }\n\n    status_t readFromParcel(const Parcel* in) override;\n    status_t writeToParcel(Parcel* out) const override;\n\n    const std::vector<uint8_t>& payload() const { return payload_; }\n\n    void payload(std::vector<uint8_t> payload) { payload_ = payload; }\n\n    ResponseCode response_code() const { return response_code_; }\n\n    void response_code(ResponseCode response_code) { response_code_ = response_code; }\n\n    bool should_reenroll() const { return should_reenroll_; }\n\n    void should_reenroll(bool should_reenroll) { should_reenroll_ = should_reenroll; }\n\n    int32_t timeout() const { return timeout_; }\n\n    void timeout(int32_t timeout) { timeout_ = timeout; }\n\n  private:\n    ResponseCode response_code_;\n    int32_t timeout_;\n    std::vector<uint8_t> payload_;\n    bool should_reenroll_;\n};\n\n}  // namespace gatekeeper\n}  // namespace service\n}  // namespace android\n\n#endif  // GATEKEEPERD_INCLUDE_GATEKEEPER_GATEKEEPERRESPONSE_H_\n"
  },
  {
    "path": "gatekeeperd/main.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <binder/IPCThreadState.h>\n#include <binder/IServiceManager.h>\n\n#include <log/log.h>\n\n#include \"gatekeeperd.h\"\n\nint main(int argc, char* argv[]) {\n    ALOGI(\"Starting gatekeeperd...\");\n    if (argc < 2) {\n        ALOGE(\"A directory must be specified!\");\n        return 1;\n    }\n    if (chdir(argv[1]) == -1) {\n        ALOGE(\"chdir: %s: %s\", argv[1], strerror(errno));\n        return 1;\n    }\n\n    android::sp<android::IServiceManager> sm = android::defaultServiceManager();\n    android::sp<android::GateKeeperProxy> proxy = new android::GateKeeperProxy();\n    android::status_t ret = sm->addService(\n            android::String16(\"android.service.gatekeeper.IGateKeeperService\"), proxy);\n    if (ret != android::OK) {\n        ALOGE(\"Couldn't register binder service!\");\n        return 1;\n    }\n\n    /*\n     * We're the only thread in existence, so we're just going to process\n     * Binder transaction as a single-threaded program.\n     */\n    android::IPCThreadState::self()->joinThreadPool();\n    return 1;\n}\n"
  },
  {
    "path": "healthd/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libbatterymonitor_defaults\",\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    vendor_available: true,\n    recovery_available: true,\n    export_include_dirs: [\"include\"],\n    shared_libs: [\n        \"libutils\",\n        \"libbase\",\n\n        // Need HealthInfo definition from headers of these shared\n        // libraries. Clients don't need to link to these.\n        \"android.hardware.health@2.1\",\n    ],\n    header_libs: [\"libhealthd_headers\"],\n    export_header_lib_headers: [\"libhealthd_headers\"],\n}\n\ncc_defaults {\n    name: \"libhealthd_charger_ui_defaults\",\n    vendor_available: true,\n    export_include_dirs: [\n        \"include\",\n        \"include_charger\",\n    ],\n\n    static_libs: [\n        \"libcharger_sysprop\",\n        \"libhealthd_draw\",\n        \"libhealthloop\",\n        \"libminui\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"liblog\",\n        \"libpng\",\n        \"libsuspend\",\n        \"libutils\",\n    ],\n\n    header_libs: [\n        \"libhealthd_headers\",\n    ],\n\n    srcs: [\n        \"healthd_mode_charger.cpp\",\n        \"AnimationParser.cpp\",\n    ],\n\n    target: {\n        vendor: {\n            exclude_static_libs: [\n                \"libcharger_sysprop\",\n            ],\n        },\n    },\n}\n\ncc_library_headers {\n    name: \"libhealthd_headers\",\n    vendor_available: true,\n    recovery_available: true,\n    export_include_dirs: [\"include\"],\n    header_libs: [\"libbatteryservice_headers\"],\n    export_header_lib_headers: [\"libbatteryservice_headers\"],\n}\n\ncc_library_static {\n    name: \"libbatterymonitor\",\n    defaults: [\"libbatterymonitor_defaults\"],\n    srcs: [\"BatteryMonitor.cpp\"],\n    static_libs: [\n        \"android.hardware.health-V4-ndk\",\n    ],\n    whole_static_libs: [\n        // Need to translate HIDL to AIDL to support legacy APIs in\n        // BatteryMonitor.\n        \"android.hardware.health-translate-ndk\",\n    ],\n}\n\n// TODO(b/251425963): remove when android.hardware.health is upgraded to V2.\ncc_library_static {\n    name: \"libbatterymonitor-V1\",\n    defaults: [\"libbatterymonitor_defaults\"],\n    srcs: [\"BatteryMonitor_v1.cpp\"],\n    static_libs: [\n        \"android.hardware.health-V1-ndk\",\n    ],\n    whole_static_libs: [\n        // Need to translate HIDL to AIDL to support legacy APIs in\n        // BatteryMonitor.\n        \"android.hardware.health-translate-V1-ndk\",\n    ],\n}\n\ncc_library_static {\n    name: \"libhealthd_charger_nops\",\n    recovery_available: true,\n\n    srcs: [\n        \"healthd_mode_charger_nops.cpp\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    header_libs: [\n        \"libhealthd_headers\",\n    ],\n\n    static_libs: [\n        \"libhealthloop\",\n        \"libhealth2impl\",\n    ],\n\n    shared_libs: [\n        \"android.hardware.health@2.1\",\n        \"libutils\",\n    ],\n}\n\nsysprop_library {\n    name: \"charger_sysprop\",\n    recovery_available: true,\n    srcs: [\"charger.sysprop\"],\n    property_owner: \"Platform\",\n    api_packages: [\"android.sysprop\"],\n}\n\ncc_library_static {\n    name: \"libhealthd_draw\",\n    vendor_available: true,\n    export_include_dirs: [\".\"],\n    static_libs: [\n        \"libcharger_sysprop\",\n        \"libminui\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    header_libs: [\"libbatteryservice_headers\"],\n\n    srcs: [\"healthd_draw.cpp\"],\n\n    target: {\n        vendor: {\n            exclude_static_libs: [\n                \"libcharger_sysprop\",\n            ],\n        },\n    },\n}\n\ncc_library_static {\n    name: \"libhealthd_charger_ui\",\n    defaults: [\"libhealthd_charger_ui_defaults\"],\n\n    static_libs: [\n        \"android.hardware.health-V4-ndk\",\n        \"android.hardware.health-translate-ndk\",\n    ],\n\n    export_static_lib_headers: [\n        \"android.hardware.health-V4-ndk\",\n    ],\n}\n\n// TODO(b/251425963): remove when android.hardware.health is upgraded to V2.\ncc_library_static {\n    name: \"libhealthd_charger_ui-V1\",\n    defaults: [\"libhealthd_charger_ui_defaults\"],\n\n    static_libs: [\n        \"android.hardware.health-V1-ndk\",\n        \"android.hardware.health-translate-V1-ndk\",\n    ],\n\n    export_static_lib_headers: [\n        \"android.hardware.health-V1-ndk\",\n    ],\n}\n\ncc_library_static {\n    name: \"libhealthd_charger\",\n    export_include_dirs: [\n        \"include\",\n        \"include_charger\",\n    ],\n\n    static_libs: [\n        \"android.hardware.health@1.0-convert\",\n        \"libcharger_sysprop\",\n        \"libhealth2impl\",\n        \"libhealthd_charger_ui\",\n    ],\n\n    shared_libs: [\n        \"android.hardware.health@2.1\",\n        \"libbase\",\n        \"libcutils\",\n        \"liblog\",\n        \"libutils\",\n    ],\n\n    srcs: [\n        \"healthd_mode_charger_hidl.cpp\",\n    ],\n}\n\ncc_defaults {\n    name: \"charger_defaults\",\n    local_include_dirs: [\n        \"include_charger\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    shared_libs: [\n        // common\n        \"libbase\",\n        \"libcutils\",\n        \"libhidlbase\",\n        \"liblog\",\n        \"libutils\",\n\n        // system charger only\n        \"libpng\",\n    ],\n\n    static_libs: [\n        // common\n        \"android.hardware.health@1.0-convert\",\n        \"android.hardware.health-V4-ndk\",\n        \"libbatterymonitor\",\n        \"libcharger_sysprop\",\n        \"libhealthd_charger_nops\",\n        \"libhealthloop\",\n        \"libhealth2impl\",\n\n        // system charger only\n        \"libhealthd_draw\",\n        \"libhealthd_charger\",\n        \"libhealthd_charger_ui\",\n        \"libminui\",\n        \"libsuspend\",\n    ],\n}\n\ncc_binary {\n    name: \"charger\",\n    defaults: [\"charger_defaults\"],\n    recovery_available: true,\n    srcs: [\n        \"charger.cpp\",\n        \"charger_utils.cpp\",\n    ],\n    shared_libs: [\n        \"android.hardware.health@2.0\",\n        \"android.hardware.health@2.1\",\n    ],\n\n    target: {\n        recovery: {\n            // No UI and libsuspend for recovery charger.\n            cflags: [\n                \"-DCHARGER_FORCE_NO_UI=1\",\n            ],\n            exclude_shared_libs: [\n                \"libpng\",\n            ],\n            exclude_static_libs: [\n                \"libhealthd_draw\",\n                \"libhealthd_charger\",\n                \"libhealthd_charger_ui\",\n                \"libminui\",\n                \"libsuspend\",\n            ],\n        },\n    },\n}\n\ncc_test {\n    name: \"charger_test\",\n    defaults: [\"charger_defaults\"],\n    srcs: [\"charger_test.cpp\"],\n    static_libs: [\n        \"android.hardware.health@1.0\",\n        \"android.hardware.health@2.0\",\n        \"android.hardware.health@2.1\",\n    ],\n}\n\ncc_test {\n    name: \"libhealthd_charger_test\",\n    defaults: [\"charger_defaults\"],\n    srcs: [\n        \"AnimationParser_test.cpp\",\n        \"healthd_mode_charger_test.cpp\",\n    ],\n    static_libs: [\n        \"android.hardware.health@1.0\",\n        \"android.hardware.health@2.0\",\n        \"android.hardware.health@2.1\",\n        \"libgmock\",\n    ],\n    test_suites: [\n        \"general-tests\",\n        \"device-tests\",\n    ],\n    data: [\n        \":libhealthd_charger_test_data\",\n    ],\n    require_root: true,\n}\n\n// /system/etc/res/images/charger/battery_fail.png\nprebuilt_etc {\n    name: \"system_core_charger_res_images_battery_fail.png\",\n    src: \"images/battery_fail.png\",\n    relative_install_path: \"res/images/charger\",\n    filename: \"battery_fail.png\",\n}\n\n// /system/etc/res/images/charger/battery_scale.png\nprebuilt_etc {\n    name: \"system_core_charger_res_images_battery_scale.png\",\n    src: \"images/battery_scale.png\",\n    relative_install_path: \"res/images/charger\",\n    filename: \"battery_scale.png\",\n}\n\nphony {\n    name: \"charger_res_images\",\n    required: [\n        \"system_core_charger_res_images_battery_fail.png\",\n        \"system_core_charger_res_images_battery_scale.png\",\n    ],\n}\n\n// /vendor/etc/res/images/default/charger/battery_fail.png\nprebuilt_etc {\n    name: \"system_core_charger_res_images_battery_fail.png_default_vendor\",\n    src: \"images/battery_fail.png\",\n    relative_install_path: \"res/images/default/charger\",\n    vendor: true,\n    filename: \"battery_fail.png\",\n}\n\n// /vendor/etc/res/images/default/charger/battery_scale.png\nprebuilt_etc {\n    name: \"system_core_charger_res_images_battery_scale.png_default_vendor\",\n    src: \"images/battery_scale.png\",\n    relative_install_path: \"res/images/default/charger\",\n    vendor: true,\n    filename: \"battery_scale.png\",\n}\n\nphony {\n    name: \"charger_res_images_vendor\",\n    required: [\n        \"system_core_charger_res_images_battery_fail.png_default_vendor\",\n        \"system_core_charger_res_images_battery_scale.png_default_vendor\",\n    ],\n}\n"
  },
  {
    "path": "healthd/AnimationParser.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"AnimationParser.h\"\n\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n\n#include <cutils/klog.h>\n\n#include \"animation.h\"\n\n#define LOGE(x...) do { KLOG_ERROR(\"charger\", x); } while (0)\n#define LOGW(x...) do { KLOG_WARNING(\"charger\", x); } while (0)\n#define LOGV(x...) do { KLOG_DEBUG(\"charger\", x); } while (0)\n\nnamespace android {\n\n// Lines consisting of only whitespace or whitespace followed by '#' can be ignored.\nbool can_ignore_line(const char* str) {\n    for (int i = 0; str[i] != '\\0' && str[i] != '#'; i++) {\n        if (!isspace(str[i])) return false;\n    }\n    return true;\n}\n\nbool remove_prefix(std::string_view line, const char* prefix, const char** rest) {\n    const char* str = line.data();\n    int start;\n    char c;\n\n    std::string format = base::StringPrintf(\" %s%%n%%c\", prefix);\n    if (sscanf(str, format.c_str(), &start, &c) != 1) {\n        return false;\n    }\n\n    *rest = &str[start];\n    return true;\n}\n\nbool parse_text_field(const char* in, animation::text_field* field) {\n    int* x = &field->pos_x;\n    int* y = &field->pos_y;\n    int* r = &field->color_r;\n    int* g = &field->color_g;\n    int* b = &field->color_b;\n    int* a = &field->color_a;\n\n    int start = 0, end = 0;\n\n    if (sscanf(in, \"c c %d %d %d %d %n%*s%n\", r, g, b, a, &start, &end) == 4) {\n        *x = CENTER_VAL;\n        *y = CENTER_VAL;\n    } else if (sscanf(in, \"c %d %d %d %d %d %n%*s%n\", y, r, g, b, a, &start, &end) == 5) {\n        *x = CENTER_VAL;\n    } else if (sscanf(in, \"%d c %d %d %d %d %n%*s%n\", x, r, g, b, a, &start, &end) == 5) {\n        *y = CENTER_VAL;\n    } else if (sscanf(in, \"%d %d %d %d %d %d %n%*s%n\", x, y, r, g, b, a, &start, &end) != 6) {\n        return false;\n    }\n\n    if (end == 0) return false;\n\n    field->font_file.assign(&in[start], end - start);\n\n    return true;\n}\n\nbool parse_animation_desc(const std::string& content, animation* anim) {\n    static constexpr const char* animation_prefix = \"animation: \";\n    static constexpr const char* fail_prefix = \"fail: \";\n    static constexpr const char* clock_prefix = \"clock_display: \";\n    static constexpr const char* percent_prefix = \"percent_display: \";\n\n    std::vector<animation::frame> frames;\n\n    for (const auto& line : base::Split(content, \"\\n\")) {\n        animation::frame frame;\n        const char* rest;\n\n        if (can_ignore_line(line.c_str())) {\n            continue;\n        } else if (remove_prefix(line, animation_prefix, &rest)) {\n            int start = 0, end = 0;\n            if (sscanf(rest, \"%d %d %n%*s%n\", &anim->num_cycles, &anim->first_frame_repeats,\n                    &start, &end) != 2 ||\n                end == 0) {\n                LOGE(\"Bad animation format: %s\\n\", line.c_str());\n                return false;\n            } else {\n                anim->animation_file.assign(&rest[start], end - start);\n            }\n        } else if (remove_prefix(line, fail_prefix, &rest)) {\n            anim->fail_file.assign(rest);\n        } else if (remove_prefix(line, clock_prefix, &rest)) {\n            if (!parse_text_field(rest, &anim->text_clock)) {\n                LOGE(\"Bad clock_display format: %s\\n\", line.c_str());\n                return false;\n            }\n        } else if (remove_prefix(line, percent_prefix, &rest)) {\n            if (!parse_text_field(rest, &anim->text_percent)) {\n                LOGE(\"Bad percent_display format: %s\\n\", line.c_str());\n                return false;\n            }\n        } else if (sscanf(line.c_str(), \" frame: %d %d %d\",\n                &frame.disp_time, &frame.min_level, &frame.max_level) == 3) {\n            frames.push_back(std::move(frame));\n        } else {\n            LOGE(\"Malformed animation description line: %s\\n\", line.c_str());\n            return false;\n        }\n    }\n\n    if (anim->animation_file.empty() || frames.empty()) {\n        LOGE(\"Bad animation description. Provide the 'animation: ' line and at least one 'frame: ' \"\n             \"line.\\n\");\n        return false;\n    }\n\n    anim->num_frames = frames.size();\n    anim->frames = new animation::frame[frames.size()];\n    std::copy(frames.begin(), frames.end(), anim->frames);\n\n    return true;\n}\n\n}  // namespace android\n"
  },
  {
    "path": "healthd/AnimationParser.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef HEALTHD_ANIMATION_PARSER_H\n#define HEALTHD_ANIMATION_PARSER_H\n\n#include <string_view>\n\n#include \"animation.h\"\n\nnamespace android {\n\nbool parse_animation_desc(const std::string& content, animation* anim);\n\nbool can_ignore_line(const char* str);\nbool remove_prefix(std::string_view str, const char* prefix, const char** rest);\nbool parse_text_field(const char* in, animation::text_field* field);\n}  // namespace android\n\n#endif // HEALTHD_ANIMATION_PARSER_H\n"
  },
  {
    "path": "healthd/AnimationParser_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"AnimationParser.h\"\n\n#include <gtest/gtest.h>\n\nusing namespace android;\n\nTEST(AnimationParserTest, Test_can_ignore_line) {\n    EXPECT_TRUE(can_ignore_line(\"\"));\n    EXPECT_TRUE(can_ignore_line(\"     \"));\n    EXPECT_TRUE(can_ignore_line(\"#\"));\n    EXPECT_TRUE(can_ignore_line(\"   # comment\"));\n\n    EXPECT_FALSE(can_ignore_line(\"text\"));\n    EXPECT_FALSE(can_ignore_line(\"text # comment\"));\n    EXPECT_FALSE(can_ignore_line(\"     text\"));\n    EXPECT_FALSE(can_ignore_line(\"     text # comment\"));\n}\n\nTEST(AnimationParserTest, Test_remove_prefix) {\n    static const char TEST_STRING[] = \"abcdef\";\n    const char* rest = nullptr;\n    EXPECT_FALSE(remove_prefix(TEST_STRING, \"def\", &rest));\n    // Ignore strings that only consist of the prefix\n    EXPECT_FALSE(remove_prefix(TEST_STRING, TEST_STRING, &rest));\n\n    EXPECT_TRUE(remove_prefix(TEST_STRING, \"abc\", &rest));\n    EXPECT_STREQ(\"def\", rest);\n\n    EXPECT_TRUE(remove_prefix(\"  abcdef\", \"abc\", &rest));\n    EXPECT_STREQ(\"def\", rest);\n}\n\nTEST(AnimationParserTest, Test_parse_text_field) {\n    static const char TEST_FILE_NAME[] = \"font_file\";\n    static const int TEST_X = 3;\n    static const int TEST_Y = 6;\n    static const int TEST_R = 1;\n    static const int TEST_G = 2;\n    static const int TEST_B = 4;\n    static const int TEST_A = 8;\n\n    static const char TEST_XCENT_YCENT[] = \"c c 1 2 4 8  font_file \";\n    static const char TEST_XCENT_YVAL[]  = \"c 6 1 2 4 8  font_file \";\n    static const char TEST_XVAL_YCENT[]  = \"3 c 1 2 4 8  font_file \";\n    static const char TEST_XVAL_YVAL[]   = \"3 6 1 2 4 8  font_file \";\n    static const char TEST_BAD_MISSING[] = \"c c 1 2 4 font_file\";\n    static const char TEST_BAD_NO_FILE[] = \"c c 1 2 4 8\";\n\n    animation::text_field out;\n\n    EXPECT_TRUE(parse_text_field(TEST_XCENT_YCENT, &out));\n    EXPECT_EQ(CENTER_VAL, out.pos_x);\n    EXPECT_EQ(CENTER_VAL, out.pos_y);\n    EXPECT_EQ(TEST_R, out.color_r);\n    EXPECT_EQ(TEST_G, out.color_g);\n    EXPECT_EQ(TEST_B, out.color_b);\n    EXPECT_EQ(TEST_A, out.color_a);\n    EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());\n\n    EXPECT_TRUE(parse_text_field(TEST_XCENT_YVAL, &out));\n    EXPECT_EQ(CENTER_VAL, out.pos_x);\n    EXPECT_EQ(TEST_Y, out.pos_y);\n    EXPECT_EQ(TEST_R, out.color_r);\n    EXPECT_EQ(TEST_G, out.color_g);\n    EXPECT_EQ(TEST_B, out.color_b);\n    EXPECT_EQ(TEST_A, out.color_a);\n    EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());\n\n    EXPECT_TRUE(parse_text_field(TEST_XVAL_YCENT, &out));\n    EXPECT_EQ(TEST_X, out.pos_x);\n    EXPECT_EQ(CENTER_VAL, out.pos_y);\n    EXPECT_EQ(TEST_R, out.color_r);\n    EXPECT_EQ(TEST_G, out.color_g);\n    EXPECT_EQ(TEST_B, out.color_b);\n    EXPECT_EQ(TEST_A, out.color_a);\n    EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());\n\n    EXPECT_TRUE(parse_text_field(TEST_XVAL_YVAL, &out));\n    EXPECT_EQ(TEST_X, out.pos_x);\n    EXPECT_EQ(TEST_Y, out.pos_y);\n    EXPECT_EQ(TEST_R, out.color_r);\n    EXPECT_EQ(TEST_G, out.color_g);\n    EXPECT_EQ(TEST_B, out.color_b);\n    EXPECT_EQ(TEST_A, out.color_a);\n    EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());\n\n    EXPECT_FALSE(parse_text_field(TEST_BAD_MISSING, &out));\n    EXPECT_FALSE(parse_text_field(TEST_BAD_NO_FILE, &out));\n}\n\nTEST(AnimationParserTest, Test_parse_animation_desc_basic) {\n    static const char TEST_ANIMATION[] = R\"desc(\n        # Basic animation\n        animation: 5 1 test/animation_file\n        frame: 1000 0 100\n    )desc\";\n    animation anim;\n\n    EXPECT_TRUE(parse_animation_desc(TEST_ANIMATION, &anim));\n}\n\nTEST(AnimationParserTest, Test_parse_animation_desc_bad_no_animation_line) {\n    static const char TEST_ANIMATION[] = R\"desc(\n        # Bad animation\n        frame: 1000 90  10\n    )desc\";\n    animation anim;\n\n    EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));\n}\n\nTEST(AnimationParserTest, Test_parse_animation_desc_bad_no_frame) {\n    static const char TEST_ANIMATION[] = R\"desc(\n        # Bad animation\n        animation: 5 1 test/animation_file\n    )desc\";\n    animation anim;\n\n    EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));\n}\n\nTEST(AnimationParserTest, Test_parse_animation_desc_bad_animation_line_format) {\n    static const char TEST_ANIMATION[] = R\"desc(\n        # Bad animation\n        animation: 5 1\n        frame: 1000 90  10\n    )desc\";\n    animation anim;\n\n    EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));\n}\n\nTEST(AnimationParserTest, Test_parse_animation_desc_full) {\n    static const char TEST_ANIMATION[] = R\"desc(\n        # Full animation\n        animation: 5 1 test/animation_file\n        clock_display:    11 12 13 14 15 16 test/time_font\n        percent_display:  21 22 23 24 25 26 test/percent_font\n\n        frame: 10 20 30\n        frame: 40 50 60\n    )desc\";\n    animation anim;\n\n    EXPECT_TRUE(parse_animation_desc(TEST_ANIMATION, &anim));\n\n    EXPECT_EQ(5, anim.num_cycles);\n    EXPECT_EQ(1, anim.first_frame_repeats);\n    EXPECT_STREQ(\"test/animation_file\", anim.animation_file.c_str());\n\n    EXPECT_EQ(11, anim.text_clock.pos_x);\n    EXPECT_EQ(12, anim.text_clock.pos_y);\n    EXPECT_EQ(13, anim.text_clock.color_r);\n    EXPECT_EQ(14, anim.text_clock.color_g);\n    EXPECT_EQ(15, anim.text_clock.color_b);\n    EXPECT_EQ(16, anim.text_clock.color_a);\n    EXPECT_STREQ(\"test/time_font\", anim.text_clock.font_file.c_str());\n\n    EXPECT_EQ(21, anim.text_percent.pos_x);\n    EXPECT_EQ(22, anim.text_percent.pos_y);\n    EXPECT_EQ(23, anim.text_percent.color_r);\n    EXPECT_EQ(24, anim.text_percent.color_g);\n    EXPECT_EQ(25, anim.text_percent.color_b);\n    EXPECT_EQ(26, anim.text_percent.color_a);\n    EXPECT_STREQ(\"test/percent_font\", anim.text_percent.font_file.c_str());\n\n    EXPECT_EQ(2, anim.num_frames);\n\n    EXPECT_EQ(10, anim.frames[0].disp_time);\n    EXPECT_EQ(20, anim.frames[0].min_level);\n    EXPECT_EQ(30, anim.frames[0].max_level);\n\n    EXPECT_EQ(40, anim.frames[1].disp_time);\n    EXPECT_EQ(50, anim.frames[1].min_level);\n    EXPECT_EQ(60, anim.frames[1].max_level);\n}\n"
  },
  {
    "path": "healthd/BatteryMonitor.cpp",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"healthd\"\n\n#include <healthd/healthd.h>\n#include <healthd/BatteryMonitor.h>\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n\n#include <aidl/android/hardware/health/HealthInfo.h>\n#include <android-base/file.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <android/hardware/health/2.1/types.h>\n#include <android/hardware/health/translate-ndk.h>\n#include <batteryservice/BatteryService.h>\n#include <cutils/klog.h>\n#include <cutils/properties.h>\n#include <utils/Errors.h>\n#include <utils/String8.h>\n#include <utils/Vector.h>\n\n#define POWER_SUPPLY_SUBSYSTEM \"power_supply\"\n#define POWER_SUPPLY_SYSFS_PATH \"/sys/class/\" POWER_SUPPLY_SUBSYSTEM\n#define FAKE_BATTERY_CAPACITY 42\n#define FAKE_BATTERY_TEMPERATURE 424\n#define MILLION 1.0e6\n#define DEFAULT_VBUS_VOLTAGE 5000000\n\nusing HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;\nusing HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;\nusing HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;\nusing aidl::android::hardware::health::BatteryCapacityLevel;\nusing aidl::android::hardware::health::BatteryChargingPolicy;\nusing aidl::android::hardware::health::BatteryChargingState;\nusing aidl::android::hardware::health::BatteryHealth;\nusing aidl::android::hardware::health::BatteryHealthData;\nusing aidl::android::hardware::health::BatteryPartStatus;\nusing aidl::android::hardware::health::BatteryStatus;\nusing aidl::android::hardware::health::HealthInfo;\n\nnamespace {\n\n// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.\n// Skips storageInfo and diskStats.\nvoid translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,\n                     ::android::hardware::health::V1_0::HealthInfo* out) {\n    out->chargerAcOnline = in.chargerAcOnline;\n    out->chargerUsbOnline = in.chargerUsbOnline;\n    out->chargerWirelessOnline = in.chargerWirelessOnline;\n    out->maxChargingCurrent = in.maxChargingCurrentMicroamps;\n    out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;\n    out->batteryStatus =\n            static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);\n    out->batteryHealth =\n            static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);\n    out->batteryPresent = in.batteryPresent;\n    out->batteryLevel = in.batteryLevel;\n    out->batteryVoltage = in.batteryVoltageMillivolts;\n    out->batteryTemperature = in.batteryTemperatureTenthsCelsius;\n    out->batteryCurrent = in.batteryCurrentMicroamps;\n    out->batteryCycleCount = in.batteryCycleCount;\n    out->batteryFullCharge = in.batteryFullChargeUah;\n    out->batteryChargeCounter = in.batteryChargeCounterUah;\n    out->batteryTechnology = in.batteryTechnology;\n}\n\nvoid translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,\n                     ::android::hardware::health::V2_0::HealthInfo* out) {\n    translateToHidl(in, &out->legacy);\n    out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;\n    // Skip storageInfo and diskStats\n}\n\nvoid translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,\n                     ::android::hardware::health::V2_1::HealthInfo* out) {\n    translateToHidl(in, &out->legacy);\n    out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(\n            in.batteryCapacityLevel);\n    out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;\n    out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;\n}\n\n}  // namespace\n\nnamespace android {\n\ntemplate <typename T>\nstruct SysfsStringEnumMap {\n    const char* s;\n    T val;\n};\n\ntemplate <typename T>\nstatic std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {\n    for (int i = 0; map[i].s; i++)\n        if (!strcmp(str, map[i].s))\n            return map[i].val;\n\n    return std::nullopt;\n}\n\nstatic void initHealthInfo(HealthInfo* health_info) {\n    *health_info = {\n            .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,\n            .batteryChargeTimeToFullNowSeconds =\n                    (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,\n            .batteryStatus = BatteryStatus::UNKNOWN,\n            .batteryHealth = BatteryHealth::UNKNOWN,\n            .batteryHealthData = std::nullopt,\n    };\n}\n\nBatteryMonitor::BatteryMonitor()\n    : mHealthdConfig(nullptr),\n      mBatteryDevicePresent(false),\n      mBatteryFixedCapacity(0),\n      mBatteryFixedTemperature(0),\n      mBatteryHealthStatus(BatteryMonitor::BH_UNKNOWN),\n      mHealthInfo(std::make_unique<HealthInfo>()) {\n    initHealthInfo(mHealthInfo.get());\n}\n\nBatteryMonitor::~BatteryMonitor() {}\n\nHealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {\n    HealthInfo_1_0 health_info_1_0;\n    translateToHidl(*mHealthInfo, &health_info_1_0);\n    return health_info_1_0;\n}\n\nHealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {\n    HealthInfo_2_0 health_info_2_0;\n    translateToHidl(*mHealthInfo, &health_info_2_0);\n    return health_info_2_0;\n}\n\nHealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {\n    HealthInfo_2_1 health_info_2_1;\n    translateToHidl(*mHealthInfo, &health_info_2_1);\n    return health_info_2_1;\n}\n\nconst HealthInfo& BatteryMonitor::getHealthInfo() const {\n    return *mHealthInfo;\n}\n\nBatteryStatus getBatteryStatus(const char* status) {\n    static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {\n            {\"Unknown\", BatteryStatus::UNKNOWN},\n            {\"Charging\", BatteryStatus::CHARGING},\n            {\"Discharging\", BatteryStatus::DISCHARGING},\n            {\"Not charging\", BatteryStatus::NOT_CHARGING},\n            {\"Full\", BatteryStatus::FULL},\n            {NULL, BatteryStatus::UNKNOWN},\n    };\n\n    auto ret = mapSysfsString(status, batteryStatusMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unknown battery status '%s'\\n\", status);\n        *ret = BatteryStatus::UNKNOWN;\n    }\n\n    return *ret;\n}\n\nBatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {\n    static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {\n            {\"Unknown\", BatteryCapacityLevel::UNKNOWN},\n            {\"Critical\", BatteryCapacityLevel::CRITICAL},\n            {\"Low\", BatteryCapacityLevel::LOW},\n            {\"Normal\", BatteryCapacityLevel::NORMAL},\n            {\"High\", BatteryCapacityLevel::HIGH},\n            {\"Full\", BatteryCapacityLevel::FULL},\n            {NULL, BatteryCapacityLevel::UNSUPPORTED},\n    };\n\n    auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unsupported battery capacity level '%s'\\n\", capacityLevel);\n        *ret = BatteryCapacityLevel::UNSUPPORTED;\n    }\n\n    return *ret;\n}\n\nBatteryHealth getBatteryHealth(const char* status) {\n    static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {\n            {\"Unknown\", BatteryHealth::UNKNOWN},\n            {\"Good\", BatteryHealth::GOOD},\n            {\"Overheat\", BatteryHealth::OVERHEAT},\n            {\"Dead\", BatteryHealth::DEAD},\n            {\"Over voltage\", BatteryHealth::OVER_VOLTAGE},\n            {\"Unspecified failure\", BatteryHealth::UNSPECIFIED_FAILURE},\n            {\"Cold\", BatteryHealth::COLD},\n            // battery health values from JEITA spec\n            {\"Warm\", BatteryHealth::GOOD},\n            {\"Cool\", BatteryHealth::GOOD},\n            {\"Hot\", BatteryHealth::OVERHEAT},\n            {\"Calibration required\", BatteryHealth::INCONSISTENT},\n            {NULL, BatteryHealth::UNKNOWN},\n    };\n\n    auto ret = mapSysfsString(status, batteryHealthMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unknown battery health '%s'\\n\", status);\n        *ret = BatteryHealth::UNKNOWN;\n    }\n\n    return *ret;\n}\n\nBatteryHealth getBatteryHealthStatus(int status) {\n    BatteryHealth value;\n\n    if (status == BatteryMonitor::BH_NOMINAL)\n        value = BatteryHealth::GOOD;\n    else if (status == BatteryMonitor::BH_MARGINAL)\n        value = BatteryHealth::FAIR;\n    else if (status == BatteryMonitor::BH_NEEDS_REPLACEMENT)\n        value = BatteryHealth::DEAD;\n    else if (status == BatteryMonitor::BH_FAILED)\n        value = BatteryHealth::UNSPECIFIED_FAILURE;\n    else if (status == BatteryMonitor::BH_NOT_AVAILABLE)\n        value = BatteryHealth::NOT_AVAILABLE;\n    else if (status == BatteryMonitor::BH_INCONSISTENT)\n        value = BatteryHealth::INCONSISTENT;\n    else\n        value = BatteryHealth::UNKNOWN;\n\n    return value;\n}\n\nBatteryChargingPolicy getBatteryChargingPolicy(const char* chargingPolicy) {\n    static SysfsStringEnumMap<BatteryChargingPolicy> batteryChargingPolicyMap[] = {\n            {\"0\", BatteryChargingPolicy::INVALID},   {\"1\", BatteryChargingPolicy::DEFAULT},\n            {\"2\", BatteryChargingPolicy::LONG_LIFE}, {\"3\", BatteryChargingPolicy::ADAPTIVE},\n            {NULL, BatteryChargingPolicy::DEFAULT},\n    };\n\n    auto ret = mapSysfsString(chargingPolicy, batteryChargingPolicyMap);\n    if (!ret) {\n        *ret = BatteryChargingPolicy::DEFAULT;\n    }\n\n    return *ret;\n}\n\nBatteryChargingState getBatteryChargingState(const char* chargingState) {\n    static SysfsStringEnumMap<BatteryChargingState> batteryChargingStateMap[] = {\n            {\"0\", BatteryChargingState::INVALID},   {\"1\", BatteryChargingState::NORMAL},\n            {\"2\", BatteryChargingState::TOO_COLD},  {\"3\", BatteryChargingState::TOO_HOT},\n            {\"4\", BatteryChargingState::LONG_LIFE}, {\"5\", BatteryChargingState::ADAPTIVE},\n            {NULL, BatteryChargingState::NORMAL},\n    };\n\n    auto ret = mapSysfsString(chargingState, batteryChargingStateMap);\n    if (!ret) {\n        *ret = BatteryChargingState::NORMAL;\n    }\n\n    return *ret;\n}\n\nstatic int readFromFile(const String8& path, std::string* buf) {\n    buf->clear();\n    if (android::base::ReadFileToString(path.c_str(), buf)) {\n        *buf = android::base::Trim(*buf);\n    }\n    return buf->length();\n}\n\nstatic bool writeToFile(const String8& path, int32_t in_value) {\n    return android::base::WriteStringToFile(std::to_string(in_value), path.c_str());\n}\n\nstatic BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {\n    static SysfsStringEnumMap<int> supplyTypeMap[] = {\n            {\"Unknown\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},\n            {\"Battery\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},\n            {\"UPS\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"Mains\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},\n            {\"USB_DCP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_HVDCP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_CDP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_ACA\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_C\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_PD\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_PD_DRP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},\n            {\"Wireless\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},\n            {\"Dock\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},\n            {NULL, 0},\n    };\n    std::string buf;\n\n    if (readFromFile(path, &buf) <= 0) {\n        return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;\n    }\n\n    auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unknown power supply type '%s'\\n\", buf.c_str());\n        *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;\n    }\n\n    return static_cast<BatteryMonitor::PowerSupplyType>(*ret);\n}\n\nstatic bool getBooleanField(const String8& path) {\n    std::string buf;\n    bool value = false;\n\n    if (readFromFile(path, &buf) > 0)\n        if (buf[0] != '0')\n            value = true;\n\n    return value;\n}\n\nstatic int getIntField(const String8& path) {\n    std::string buf;\n    int value = 0;\n\n    if (readFromFile(path, &buf) > 0)\n        android::base::ParseInt(buf, &value);\n\n    return value;\n}\n\nstatic bool isScopedPowerSupply(const char* name) {\n    constexpr char kScopeDevice[] = \"Device\";\n\n    String8 path;\n    path.appendFormat(\"%s/%s/scope\", POWER_SUPPLY_SYSFS_PATH, name);\n    std::string scope;\n    return (readFromFile(path, &scope) > 0 && scope == kScopeDevice);\n}\n\nstatic BatteryHealthData *ensureBatteryHealthData(HealthInfo *info) {\n    if (!info->batteryHealthData.has_value()) {\n        return &info->batteryHealthData.emplace();\n    }\n\n    return &info->batteryHealthData.value();\n}\n\nvoid BatteryMonitor::updateValues(void) {\n    initHealthInfo(mHealthInfo.get());\n\n    if (!mHealthdConfig->batteryPresentPath.empty())\n        mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);\n    else\n        mHealthInfo->batteryPresent = mBatteryDevicePresent;\n\n    mHealthInfo->batteryLevel = mBatteryFixedCapacity\n                                        ? mBatteryFixedCapacity\n                                        : getIntField(mHealthdConfig->batteryCapacityPath);\n    mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;\n\n    if (!mHealthdConfig->batteryCurrentNowPath.empty())\n        mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);\n\n    if (!mHealthdConfig->batteryFullChargePath.empty())\n        mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);\n\n    if (!mHealthdConfig->batteryCycleCountPath.empty())\n        mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);\n\n    if (!mHealthdConfig->batteryChargeCounterPath.empty())\n        mHealthInfo->batteryChargeCounterUah =\n                getIntField(mHealthdConfig->batteryChargeCounterPath);\n\n    if (!mHealthdConfig->batteryCurrentAvgPath.empty())\n        mHealthInfo->batteryCurrentAverageMicroamps =\n                getIntField(mHealthdConfig->batteryCurrentAvgPath);\n\n    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())\n        mHealthInfo->batteryChargeTimeToFullNowSeconds =\n                getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);\n\n    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())\n        mHealthInfo->batteryFullChargeDesignCapacityUah =\n                getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);\n\n    if (!mHealthdConfig->batteryHealthStatusPath.empty())\n        mBatteryHealthStatus = getIntField(mHealthdConfig->batteryHealthStatusPath);\n\n    if (!mHealthdConfig->batteryStateOfHealthPath.empty())\n        ensureBatteryHealthData(mHealthInfo.get())->batteryStateOfHealth =\n                getIntField(mHealthdConfig->batteryStateOfHealthPath);\n\n    if (!mHealthdConfig->batteryManufacturingDatePath.empty())\n        ensureBatteryHealthData(mHealthInfo.get())->batteryManufacturingDateSeconds =\n                getIntField(mHealthdConfig->batteryManufacturingDatePath);\n\n    if (!mHealthdConfig->batteryFirstUsageDatePath.empty())\n        ensureBatteryHealthData(mHealthInfo.get())->batteryFirstUsageSeconds =\n                getIntField(mHealthdConfig->batteryFirstUsageDatePath);\n\n    mHealthInfo->batteryTemperatureTenthsCelsius =\n            mBatteryFixedTemperature ? mBatteryFixedTemperature\n                                     : getIntField(mHealthdConfig->batteryTemperaturePath);\n\n    std::string buf;\n\n    if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)\n        mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());\n\n    if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)\n        mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());\n\n    // Backward compatible with android.hardware.health V1\n    if (mBatteryHealthStatus < BatteryMonitor::BH_MARGINAL) {\n        if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)\n            mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());\n    } else {\n        mHealthInfo->batteryHealth = getBatteryHealthStatus(mBatteryHealthStatus);\n    }\n\n    if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)\n        mHealthInfo->batteryTechnology = buf;\n\n    if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)\n        mHealthInfo->chargingPolicy = getBatteryChargingPolicy(buf.c_str());\n\n    if (readFromFile(mHealthdConfig->chargingStatePath, &buf) > 0)\n        mHealthInfo->chargingState = getBatteryChargingState(buf.c_str());\n\n    double MaxPower = 0;\n\n    for (size_t i = 0; i < mChargerNames.size(); i++) {\n        String8 path;\n        path.appendFormat(\"%s/%s/online\", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());\n        if (getIntField(path)) {\n            path.clear();\n            path.appendFormat(\"%s/%s/type\", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());\n            switch(readPowerSupplyType(path)) {\n            case ANDROID_POWER_SUPPLY_TYPE_AC:\n                mHealthInfo->chargerAcOnline = true;\n                break;\n            case ANDROID_POWER_SUPPLY_TYPE_USB:\n                mHealthInfo->chargerUsbOnline = true;\n                break;\n            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:\n                mHealthInfo->chargerWirelessOnline = true;\n                break;\n            case ANDROID_POWER_SUPPLY_TYPE_DOCK:\n                mHealthInfo->chargerDockOnline = true;\n                break;\n            default:\n                path.clear();\n                path.appendFormat(\"%s/%s/is_dock\", POWER_SUPPLY_SYSFS_PATH,\n                                  mChargerNames[i].c_str());\n                if (access(path.c_str(), R_OK) == 0)\n                    mHealthInfo->chargerDockOnline = true;\n                else\n                    KLOG_WARNING(LOG_TAG, \"%s: Unknown power supply type\\n\",\n                                 mChargerNames[i].c_str());\n            }\n            path.clear();\n            path.appendFormat(\"%s/%s/current_max\", POWER_SUPPLY_SYSFS_PATH,\n                              mChargerNames[i].c_str());\n            int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;\n\n            path.clear();\n            path.appendFormat(\"%s/%s/voltage_max\", POWER_SUPPLY_SYSFS_PATH,\n                              mChargerNames[i].c_str());\n\n            int ChargingVoltage =\n                    (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;\n\n            double power = ((double)ChargingCurrent / MILLION) *\n                           ((double)ChargingVoltage / MILLION);\n            if (MaxPower < power) {\n                mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;\n                mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;\n                MaxPower = power;\n            }\n        }\n    }\n}\n\nstatic void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {\n    char dmesgline[256];\n    size_t len;\n    if (props.batteryPresent) {\n        snprintf(dmesgline, sizeof(dmesgline), \"battery l=%d v=%d t=%s%d.%d h=%d st=%d\",\n                 props.batteryLevel, props.batteryVoltageMillivolts,\n                 props.batteryTemperatureTenthsCelsius < 0 ? \"-\" : \"\",\n                 abs(props.batteryTemperatureTenthsCelsius / 10),\n                 abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,\n                 props.batteryStatus);\n\n        len = strlen(dmesgline);\n        if (!healthd_config.batteryCurrentNowPath.empty()) {\n            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, \" c=%d\",\n                            props.batteryCurrentMicroamps);\n        }\n\n        if (!healthd_config.batteryFullChargePath.empty()) {\n            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, \" fc=%d\",\n                            props.batteryFullChargeUah);\n        }\n\n        if (!healthd_config.batteryCycleCountPath.empty()) {\n            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, \" cc=%d\",\n                            props.batteryCycleCount);\n        }\n    } else {\n        len = snprintf(dmesgline, sizeof(dmesgline), \"battery none\");\n    }\n\n    snprintf(dmesgline + len, sizeof(dmesgline) - len, \" chg=%s%s%s%s\",\n             props.chargerAcOnline ? \"a\" : \"\", props.chargerUsbOnline ? \"u\" : \"\",\n             props.chargerWirelessOnline ? \"w\" : \"\", props.chargerDockOnline ? \"d\" : \"\");\n\n    KLOG_WARNING(LOG_TAG, \"%s\\n\", dmesgline);\n}\n\nvoid BatteryMonitor::logValues(const HealthInfo_2_1& health_info,\n                               const struct healthd_config& healthd_config) {\n    HealthInfo aidl_health_info;\n    (void)android::h2a::translate(health_info, &aidl_health_info);\n    doLogValues(aidl_health_info, healthd_config);\n}\n\nvoid BatteryMonitor::logValues(void) {\n    doLogValues(*mHealthInfo, *mHealthdConfig);\n}\n\nbool BatteryMonitor::isChargerOnline() {\n    const HealthInfo& props = *mHealthInfo;\n    return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |\n           props.chargerDockOnline;\n}\n\nint BatteryMonitor::getChargeStatus() {\n    BatteryStatus result = BatteryStatus::UNKNOWN;\n    if (!mHealthdConfig->batteryStatusPath.empty()) {\n        std::string buf;\n        if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)\n            result = getBatteryStatus(buf.c_str());\n    }\n    return static_cast<int>(result);\n}\n\nstatus_t BatteryMonitor::setChargingPolicy(int value) {\n    status_t ret = NAME_NOT_FOUND;\n    bool result;\n    if (!mHealthdConfig->chargingPolicyPath.empty()) {\n        result = writeToFile(mHealthdConfig->chargingPolicyPath, value);\n        if (!result) {\n            KLOG_WARNING(LOG_TAG, \"setChargingPolicy fail\\n\");\n            ret = BAD_VALUE;\n        } else {\n            ret = OK;\n        }\n    }\n    return ret;\n}\n\nint BatteryMonitor::getChargingPolicy() {\n    BatteryChargingPolicy result = BatteryChargingPolicy::DEFAULT;\n    if (!mHealthdConfig->chargingPolicyPath.empty()) {\n        std::string buf;\n        if (readFromFile(mHealthdConfig->chargingPolicyPath, &buf) > 0)\n            result = getBatteryChargingPolicy(buf.c_str());\n    }\n    return static_cast<int>(result);\n}\n\nint BatteryMonitor::getBatteryHealthData(int id) {\n    if (id == BATTERY_PROP_MANUFACTURING_DATE) {\n        if (!mHealthdConfig->batteryManufacturingDatePath.empty())\n            return getIntField(mHealthdConfig->batteryManufacturingDatePath);\n    }\n    if (id == BATTERY_PROP_FIRST_USAGE_DATE) {\n        if (!mHealthdConfig->batteryFirstUsageDatePath.empty())\n            return getIntField(mHealthdConfig->batteryFirstUsageDatePath);\n    }\n    if (id == BATTERY_PROP_STATE_OF_HEALTH) {\n        if (!mHealthdConfig->batteryStateOfHealthPath.empty())\n            return getIntField(mHealthdConfig->batteryStateOfHealthPath);\n    }\n    if (id == BATTERY_PROP_PART_STATUS) {\n        return static_cast<int>(BatteryPartStatus::UNSUPPORTED);\n    }\n    return 0;\n}\n\nstatus_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {\n    status_t ret = BAD_VALUE;\n    std::string buf;\n\n    val->valueInt64 = LONG_MIN;\n\n    switch(id) {\n    case BATTERY_PROP_CHARGE_COUNTER:\n        if (!mHealthdConfig->batteryChargeCounterPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryChargeCounterPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_CURRENT_NOW:\n        if (!mHealthdConfig->batteryCurrentNowPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryCurrentNowPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_CURRENT_AVG:\n        if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryCurrentAvgPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_CAPACITY:\n        if (!mHealthdConfig->batteryCapacityPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryCapacityPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_ENERGY_COUNTER:\n        if (mHealthdConfig->energyCounter) {\n            ret = mHealthdConfig->energyCounter(&val->valueInt64);\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_BATTERY_STATUS:\n        val->valueInt64 = getChargeStatus();\n        ret = OK;\n        break;\n\n    case BATTERY_PROP_CHARGING_POLICY:\n        val->valueInt64 = getChargingPolicy();\n        ret = OK;\n        break;\n\n    case BATTERY_PROP_MANUFACTURING_DATE:\n        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_MANUFACTURING_DATE);\n        ret = OK;\n        break;\n\n    case BATTERY_PROP_FIRST_USAGE_DATE:\n        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_FIRST_USAGE_DATE);\n        ret = OK;\n        break;\n\n    case BATTERY_PROP_STATE_OF_HEALTH:\n        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_STATE_OF_HEALTH);\n        ret = OK;\n        break;\n\n    case BATTERY_PROP_PART_STATUS:\n        val->valueInt64 = getBatteryHealthData(BATTERY_PROP_PART_STATUS);\n        ret = OK;\n        break;\n\n    default:\n        break;\n    }\n\n    return ret;\n}\n\nstatus_t BatteryMonitor::getSerialNumber(std::optional<std::string>* out) {\n    *out = std::nullopt;\n    return OK;\n}\n\nvoid BatteryMonitor::dumpState(int fd) {\n    int v;\n    char vs[128];\n    const HealthInfo& props = *mHealthInfo;\n\n    snprintf(vs, sizeof(vs),\n             \"ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\\n\",\n             props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,\n             props.chargerDockOnline, props.maxChargingCurrentMicroamps,\n             props.maxChargingVoltageMicrovolts);\n    write(fd, vs, strlen(vs));\n    snprintf(vs, sizeof(vs), \"status: %d health: %d present: %d\\n\",\n             props.batteryStatus, props.batteryHealth, props.batteryPresent);\n    write(fd, vs, strlen(vs));\n    snprintf(vs, sizeof(vs), \"level: %d voltage: %d temp: %d\\n\", props.batteryLevel,\n             props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);\n    write(fd, vs, strlen(vs));\n\n    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {\n        v = getIntField(mHealthdConfig->batteryCurrentNowPath);\n        snprintf(vs, sizeof(vs), \"current now: %d\\n\", v);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {\n        v = getIntField(mHealthdConfig->batteryCurrentAvgPath);\n        snprintf(vs, sizeof(vs), \"current avg: %d\\n\", v);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryChargeCounterPath.empty()) {\n        v = getIntField(mHealthdConfig->batteryChargeCounterPath);\n        snprintf(vs, sizeof(vs), \"charge counter: %d\\n\", v);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {\n        snprintf(vs, sizeof(vs), \"current now: %d\\n\", props.batteryCurrentMicroamps);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryCycleCountPath.empty()) {\n        snprintf(vs, sizeof(vs), \"cycle count: %d\\n\", props.batteryCycleCount);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryFullChargePath.empty()) {\n        snprintf(vs, sizeof(vs), \"Full charge: %d\\n\", props.batteryFullChargeUah);\n        write(fd, vs, strlen(vs));\n    }\n}\n\nvoid BatteryMonitor::init(struct healthd_config *hc) {\n    String8 path;\n    char pval[PROPERTY_VALUE_MAX];\n\n    mHealthdConfig = hc;\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);\n    if (dir == NULL) {\n        KLOG_ERROR(LOG_TAG, \"Could not open %s\\n\", POWER_SUPPLY_SYSFS_PATH);\n    } else {\n        struct dirent* entry;\n\n        while ((entry = readdir(dir.get()))) {\n            const char* name = entry->d_name;\n\n            if (!strcmp(name, \".\") || !strcmp(name, \"..\"))\n                continue;\n\n            std::vector<String8>::iterator itIgnoreName =\n                    find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),\n                         String8(name));\n            if (itIgnoreName != hc->ignorePowerSupplyNames.end())\n                continue;\n\n            // Look for \"type\" file in each subdirectory\n            path.clear();\n            path.appendFormat(\"%s/%s/type\", POWER_SUPPLY_SYSFS_PATH, name);\n            switch(readPowerSupplyType(path)) {\n            case ANDROID_POWER_SUPPLY_TYPE_AC:\n            case ANDROID_POWER_SUPPLY_TYPE_USB:\n            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:\n            case ANDROID_POWER_SUPPLY_TYPE_DOCK:\n                path.clear();\n                path.appendFormat(\"%s/%s/online\", POWER_SUPPLY_SYSFS_PATH, name);\n                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));\n                break;\n\n            case ANDROID_POWER_SUPPLY_TYPE_BATTERY:\n                // Some devices expose the battery status of sub-component like\n                // stylus. Such a device-scoped battery info needs to be skipped\n                // in BatteryMonitor, which is intended to report the status of\n                // the battery supplying the power to the whole system.\n                if (isScopedPowerSupply(name)) continue;\n                mBatteryDevicePresent = true;\n\n                if (mHealthdConfig->batteryStatusPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/status\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;\n                }\n\n                if (mHealthdConfig->batteryHealthPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/health\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;\n                }\n\n                if (mHealthdConfig->batteryPresentPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/present\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;\n                }\n\n                if (mHealthdConfig->batteryCapacityPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/capacity\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;\n                }\n\n                if (mHealthdConfig->batteryVoltagePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/voltage_now\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryVoltagePath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryFullChargePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charge_full\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryFullChargePath = path;\n                }\n\n                if (mHealthdConfig->batteryCurrentNowPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/current_now\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryCurrentNowPath = path;\n                }\n\n                if (mHealthdConfig->batteryCycleCountPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/cycle_count\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryCycleCountPath = path;\n                }\n\n                if (mHealthdConfig->batteryCapacityLevelPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/capacity_level\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryCapacityLevelPath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/time_to_full_now\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryChargeTimeToFullNowPath = path;\n                }\n\n                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charge_full_design\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;\n                }\n\n                if (mHealthdConfig->batteryCurrentAvgPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/current_avg\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryCurrentAvgPath = path;\n                }\n\n                if (mHealthdConfig->batteryChargeCounterPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charge_counter\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryChargeCounterPath = path;\n                }\n\n                if (mHealthdConfig->batteryTemperaturePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/temp\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryTemperaturePath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryTechnologyPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/technology\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryTechnologyPath = path;\n                }\n\n                if (mHealthdConfig->batteryStateOfHealthPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/state_of_health\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryStateOfHealthPath = path;\n                    } else {\n                        path.clear();\n                        path.appendFormat(\"%s/%s/health_index\", POWER_SUPPLY_SYSFS_PATH, name);\n                        if (access(path.c_str(), R_OK) == 0)\n                            mHealthdConfig->batteryStateOfHealthPath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryHealthStatusPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/health_status\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryHealthStatusPath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryManufacturingDatePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/manufacturing_date\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryManufacturingDatePath = path;\n                }\n\n                if (mHealthdConfig->batteryFirstUsageDatePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/first_usage_date\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryFirstUsageDatePath = path;\n                    }\n                }\n\n                if (mHealthdConfig->chargingStatePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charging_state\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingStatePath = path;\n                }\n\n                if (mHealthdConfig->chargingPolicyPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charging_policy\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->chargingPolicyPath = path;\n                }\n\n                break;\n\n            case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:\n                break;\n            }\n\n            // Look for \"is_dock\" file\n            path.clear();\n            path.appendFormat(\"%s/%s/is_dock\", POWER_SUPPLY_SYSFS_PATH, name);\n            if (access(path.c_str(), R_OK) == 0) {\n                path.clear();\n                path.appendFormat(\"%s/%s/online\", POWER_SUPPLY_SYSFS_PATH, name);\n                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));\n            }\n        }\n    }\n\n    // Typically the case for devices which do not have a battery and\n    // and are always plugged into AC mains.\n    if (!mBatteryDevicePresent) {\n        KLOG_WARNING(LOG_TAG, \"No battery devices found\\n\");\n        hc->periodic_chores_interval_fast = -1;\n        hc->periodic_chores_interval_slow = -1;\n    } else {\n        if (mHealthdConfig->batteryStatusPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryStatusPath not found\\n\");\n        if (mHealthdConfig->batteryHealthPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryHealthPath not found\\n\");\n        if (mHealthdConfig->batteryPresentPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryPresentPath not found\\n\");\n        if (mHealthdConfig->batteryCapacityPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryCapacityPath not found\\n\");\n        if (mHealthdConfig->batteryVoltagePath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryVoltagePath not found\\n\");\n        if (mHealthdConfig->batteryTemperaturePath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryTemperaturePath not found\\n\");\n        if (mHealthdConfig->batteryTechnologyPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryTechnologyPath not found\\n\");\n        if (mHealthdConfig->batteryCurrentNowPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryCurrentNowPath not found\\n\");\n        if (mHealthdConfig->batteryFullChargePath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryFullChargePath not found\\n\");\n        if (mHealthdConfig->batteryCycleCountPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryCycleCountPath not found\\n\");\n        if (mHealthdConfig->batteryCapacityLevelPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryCapacityLevelPath not found\\n\");\n        if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryChargeTimeToFullNowPath. not found\\n\");\n        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryFullChargeDesignCapacityUahPath. not found\\n\");\n        if (mHealthdConfig->batteryStateOfHealthPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryStateOfHealthPath not found\\n\");\n        if (mHealthdConfig->batteryHealthStatusPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryHealthStatusPath not found\\n\");\n        if (mHealthdConfig->batteryManufacturingDatePath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryManufacturingDatePath not found\\n\");\n        if (mHealthdConfig->batteryFirstUsageDatePath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryFirstUsageDatePath not found\\n\");\n        if (mHealthdConfig->chargingStatePath.empty())\n            KLOG_WARNING(LOG_TAG, \"chargingStatePath not found\\n\");\n        if (mHealthdConfig->chargingPolicyPath.empty())\n            KLOG_WARNING(LOG_TAG, \"chargingPolicyPath not found\\n\");\n    }\n\n    if (property_get(\"ro.boot.fake_battery\", pval, NULL) > 0\n                                               && strtol(pval, NULL, 10) != 0) {\n        mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;\n        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;\n    }\n}\n\n}; // namespace android\n"
  },
  {
    "path": "healthd/BatteryMonitor_v1.cpp",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"healthd\"\n\n#include <healthd/healthd.h>\n#include <healthd/BatteryMonitor_v1.h>\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <memory>\n#include <optional>\n\n#include <aidl/android/hardware/health/HealthInfo.h>\n#include <android-base/file.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <android/hardware/health/2.1/types.h>\n#include <android/hardware/health/translate-ndk.h>\n#include <batteryservice/BatteryService.h>\n#include <cutils/klog.h>\n#include <cutils/properties.h>\n#include <utils/Errors.h>\n#include <utils/String8.h>\n#include <utils/Vector.h>\n\n#define POWER_SUPPLY_SUBSYSTEM \"power_supply\"\n#define POWER_SUPPLY_SYSFS_PATH \"/sys/class/\" POWER_SUPPLY_SUBSYSTEM\n#define FAKE_BATTERY_CAPACITY 42\n#define FAKE_BATTERY_TEMPERATURE 424\n#define MILLION 1.0e6\n#define DEFAULT_VBUS_VOLTAGE 5000000\n\nusing HealthInfo_1_0 = android::hardware::health::V1_0::HealthInfo;\nusing HealthInfo_2_0 = android::hardware::health::V2_0::HealthInfo;\nusing HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;\nusing aidl::android::hardware::health::BatteryCapacityLevel;\nusing aidl::android::hardware::health::BatteryHealth;\nusing aidl::android::hardware::health::BatteryStatus;\nusing aidl::android::hardware::health::HealthInfo;\n\nnamespace {\n\n// Translate from AIDL back to HIDL definition for getHealthInfo_*_* calls.\n// Skips storageInfo and diskStats.\nvoid translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,\n                     ::android::hardware::health::V1_0::HealthInfo* out) {\n    out->chargerAcOnline = in.chargerAcOnline;\n    out->chargerUsbOnline = in.chargerUsbOnline;\n    out->chargerWirelessOnline = in.chargerWirelessOnline;\n    out->maxChargingCurrent = in.maxChargingCurrentMicroamps;\n    out->maxChargingVoltage = in.maxChargingVoltageMicrovolts;\n    out->batteryStatus =\n            static_cast<::android::hardware::health::V1_0::BatteryStatus>(in.batteryStatus);\n    out->batteryHealth =\n            static_cast<::android::hardware::health::V1_0::BatteryHealth>(in.batteryHealth);\n    out->batteryPresent = in.batteryPresent;\n    out->batteryLevel = in.batteryLevel;\n    out->batteryVoltage = in.batteryVoltageMillivolts;\n    out->batteryTemperature = in.batteryTemperatureTenthsCelsius;\n    out->batteryCurrent = in.batteryCurrentMicroamps;\n    out->batteryCycleCount = in.batteryCycleCount;\n    out->batteryFullCharge = in.batteryFullChargeUah;\n    out->batteryChargeCounter = in.batteryChargeCounterUah;\n    out->batteryTechnology = in.batteryTechnology;\n}\n\nvoid translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,\n                     ::android::hardware::health::V2_0::HealthInfo* out) {\n    translateToHidl(in, &out->legacy);\n    out->batteryCurrentAverage = in.batteryCurrentAverageMicroamps;\n    // Skip storageInfo and diskStats\n}\n\nvoid translateToHidl(const ::aidl::android::hardware::health::HealthInfo& in,\n                     ::android::hardware::health::V2_1::HealthInfo* out) {\n    translateToHidl(in, &out->legacy);\n    out->batteryCapacityLevel = static_cast<android::hardware::health::V2_1::BatteryCapacityLevel>(\n            in.batteryCapacityLevel);\n    out->batteryChargeTimeToFullNowSeconds = in.batteryChargeTimeToFullNowSeconds;\n    out->batteryFullChargeDesignCapacityUah = in.batteryFullChargeDesignCapacityUah;\n}\n\n}  // namespace\n\nnamespace android {\n\ntemplate <typename T>\nstruct SysfsStringEnumMap {\n    const char* s;\n    T val;\n};\n\ntemplate <typename T>\nstatic std::optional<T> mapSysfsString(const char* str, SysfsStringEnumMap<T> map[]) {\n    for (int i = 0; map[i].s; i++)\n        if (!strcmp(str, map[i].s))\n            return map[i].val;\n\n    return std::nullopt;\n}\n\nstatic void initHealthInfo(HealthInfo* health_info) {\n    *health_info = {\n            .batteryCapacityLevel = BatteryCapacityLevel::UNSUPPORTED,\n            .batteryChargeTimeToFullNowSeconds =\n                    (int64_t)HealthInfo::BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED,\n            .batteryStatus = BatteryStatus::UNKNOWN,\n            .batteryHealth = BatteryHealth::UNKNOWN,\n    };\n}\n\nBatteryMonitor::BatteryMonitor()\n    : mHealthdConfig(nullptr),\n      mBatteryDevicePresent(false),\n      mBatteryFixedCapacity(0),\n      mBatteryFixedTemperature(0),\n      mHealthInfo(std::make_unique<HealthInfo>()) {\n    initHealthInfo(mHealthInfo.get());\n}\n\nBatteryMonitor::~BatteryMonitor() {}\n\nHealthInfo_1_0 BatteryMonitor::getHealthInfo_1_0() const {\n    HealthInfo_1_0 health_info_1_0;\n    translateToHidl(*mHealthInfo, &health_info_1_0);\n    return health_info_1_0;\n}\n\nHealthInfo_2_0 BatteryMonitor::getHealthInfo_2_0() const {\n    HealthInfo_2_0 health_info_2_0;\n    translateToHidl(*mHealthInfo, &health_info_2_0);\n    return health_info_2_0;\n}\n\nHealthInfo_2_1 BatteryMonitor::getHealthInfo_2_1() const {\n    HealthInfo_2_1 health_info_2_1;\n    translateToHidl(*mHealthInfo, &health_info_2_1);\n    return health_info_2_1;\n}\n\nconst HealthInfo& BatteryMonitor::getHealthInfo() const {\n    return *mHealthInfo;\n}\n\nBatteryStatus getBatteryStatus(const char* status) {\n    static SysfsStringEnumMap<BatteryStatus> batteryStatusMap[] = {\n            {\"Unknown\", BatteryStatus::UNKNOWN},\n            {\"Charging\", BatteryStatus::CHARGING},\n            {\"Discharging\", BatteryStatus::DISCHARGING},\n            {\"Not charging\", BatteryStatus::NOT_CHARGING},\n            {\"Full\", BatteryStatus::FULL},\n            {NULL, BatteryStatus::UNKNOWN},\n    };\n\n    auto ret = mapSysfsString(status, batteryStatusMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unknown battery status '%s'\\n\", status);\n        *ret = BatteryStatus::UNKNOWN;\n    }\n\n    return *ret;\n}\n\nBatteryCapacityLevel getBatteryCapacityLevel(const char* capacityLevel) {\n    static SysfsStringEnumMap<BatteryCapacityLevel> batteryCapacityLevelMap[] = {\n            {\"Unknown\", BatteryCapacityLevel::UNKNOWN},\n            {\"Critical\", BatteryCapacityLevel::CRITICAL},\n            {\"Low\", BatteryCapacityLevel::LOW},\n            {\"Normal\", BatteryCapacityLevel::NORMAL},\n            {\"High\", BatteryCapacityLevel::HIGH},\n            {\"Full\", BatteryCapacityLevel::FULL},\n            {NULL, BatteryCapacityLevel::UNSUPPORTED},\n    };\n\n    auto ret = mapSysfsString(capacityLevel, batteryCapacityLevelMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unsupported battery capacity level '%s'\\n\", capacityLevel);\n        *ret = BatteryCapacityLevel::UNSUPPORTED;\n    }\n\n    return *ret;\n}\n\nBatteryHealth getBatteryHealth(const char* status) {\n    static SysfsStringEnumMap<BatteryHealth> batteryHealthMap[] = {\n            {\"Unknown\", BatteryHealth::UNKNOWN},\n            {\"Good\", BatteryHealth::GOOD},\n            {\"Overheat\", BatteryHealth::OVERHEAT},\n            {\"Dead\", BatteryHealth::DEAD},\n            {\"Over voltage\", BatteryHealth::OVER_VOLTAGE},\n            {\"Unspecified failure\", BatteryHealth::UNSPECIFIED_FAILURE},\n            {\"Cold\", BatteryHealth::COLD},\n            // battery health values from JEITA spec\n            {\"Warm\", BatteryHealth::GOOD},\n            {\"Cool\", BatteryHealth::GOOD},\n            {\"Hot\", BatteryHealth::OVERHEAT},\n            {NULL, BatteryHealth::UNKNOWN},\n    };\n\n    auto ret = mapSysfsString(status, batteryHealthMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unknown battery health '%s'\\n\", status);\n        *ret = BatteryHealth::UNKNOWN;\n    }\n\n    return *ret;\n}\n\nstatic int readFromFile(const String8& path, std::string* buf) {\n    buf->clear();\n    if (android::base::ReadFileToString(path.c_str(), buf)) {\n        *buf = android::base::Trim(*buf);\n    }\n    return buf->length();\n}\n\nstatic BatteryMonitor::PowerSupplyType readPowerSupplyType(const String8& path) {\n    static SysfsStringEnumMap<int> supplyTypeMap[] = {\n            {\"Unknown\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN},\n            {\"Battery\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_BATTERY},\n            {\"UPS\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"Mains\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},\n            {\"USB_DCP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_HVDCP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_CDP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_ACA\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_C\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_PD\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_AC},\n            {\"USB_PD_DRP\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_USB},\n            {\"Wireless\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_WIRELESS},\n            {\"Dock\", BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_DOCK},\n            {NULL, 0},\n    };\n    std::string buf;\n\n    if (readFromFile(path, &buf) <= 0) {\n        return BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;\n    }\n\n    auto ret = mapSysfsString(buf.c_str(), supplyTypeMap);\n    if (!ret) {\n        KLOG_WARNING(LOG_TAG, \"Unknown power supply type '%s'\\n\", buf.c_str());\n        *ret = BatteryMonitor::ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;\n    }\n\n    return static_cast<BatteryMonitor::PowerSupplyType>(*ret);\n}\n\nstatic bool getBooleanField(const String8& path) {\n    std::string buf;\n    bool value = false;\n\n    if (readFromFile(path, &buf) > 0)\n        if (buf[0] != '0')\n            value = true;\n\n    return value;\n}\n\nstatic int getIntField(const String8& path) {\n    std::string buf;\n    int value = 0;\n\n    if (readFromFile(path, &buf) > 0)\n        android::base::ParseInt(buf, &value);\n\n    return value;\n}\n\nstatic bool isScopedPowerSupply(const char* name) {\n    constexpr char kScopeDevice[] = \"Device\";\n\n    String8 path;\n    path.appendFormat(\"%s/%s/scope\", POWER_SUPPLY_SYSFS_PATH, name);\n    std::string scope;\n    return (readFromFile(path, &scope) > 0 && scope == kScopeDevice);\n}\n\nvoid BatteryMonitor::updateValues(void) {\n    initHealthInfo(mHealthInfo.get());\n\n    if (!mHealthdConfig->batteryPresentPath.empty())\n        mHealthInfo->batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);\n    else\n        mHealthInfo->batteryPresent = mBatteryDevicePresent;\n\n    mHealthInfo->batteryLevel = mBatteryFixedCapacity\n                                        ? mBatteryFixedCapacity\n                                        : getIntField(mHealthdConfig->batteryCapacityPath);\n    mHealthInfo->batteryVoltageMillivolts = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;\n\n    if (!mHealthdConfig->batteryCurrentNowPath.empty())\n        mHealthInfo->batteryCurrentMicroamps = getIntField(mHealthdConfig->batteryCurrentNowPath);\n\n    if (!mHealthdConfig->batteryFullChargePath.empty())\n        mHealthInfo->batteryFullChargeUah = getIntField(mHealthdConfig->batteryFullChargePath);\n\n    if (!mHealthdConfig->batteryCycleCountPath.empty())\n        mHealthInfo->batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);\n\n    if (!mHealthdConfig->batteryChargeCounterPath.empty())\n        mHealthInfo->batteryChargeCounterUah =\n                getIntField(mHealthdConfig->batteryChargeCounterPath);\n\n    if (!mHealthdConfig->batteryCurrentAvgPath.empty())\n        mHealthInfo->batteryCurrentAverageMicroamps =\n                getIntField(mHealthdConfig->batteryCurrentAvgPath);\n\n    if (!mHealthdConfig->batteryChargeTimeToFullNowPath.empty())\n        mHealthInfo->batteryChargeTimeToFullNowSeconds =\n                getIntField(mHealthdConfig->batteryChargeTimeToFullNowPath);\n\n    if (!mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())\n        mHealthInfo->batteryFullChargeDesignCapacityUah =\n                getIntField(mHealthdConfig->batteryFullChargeDesignCapacityUahPath);\n\n    mHealthInfo->batteryTemperatureTenthsCelsius =\n            mBatteryFixedTemperature ? mBatteryFixedTemperature\n                                     : getIntField(mHealthdConfig->batteryTemperaturePath);\n\n    std::string buf;\n\n    if (readFromFile(mHealthdConfig->batteryCapacityLevelPath, &buf) > 0)\n        mHealthInfo->batteryCapacityLevel = getBatteryCapacityLevel(buf.c_str());\n\n    if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)\n        mHealthInfo->batteryStatus = getBatteryStatus(buf.c_str());\n\n    if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)\n        mHealthInfo->batteryHealth = getBatteryHealth(buf.c_str());\n\n    if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)\n        mHealthInfo->batteryTechnology = buf;\n\n    double MaxPower = 0;\n\n    for (size_t i = 0; i < mChargerNames.size(); i++) {\n        String8 path;\n        path.appendFormat(\"%s/%s/online\", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());\n        if (getIntField(path)) {\n            path.clear();\n            path.appendFormat(\"%s/%s/type\", POWER_SUPPLY_SYSFS_PATH, mChargerNames[i].c_str());\n            switch(readPowerSupplyType(path)) {\n            case ANDROID_POWER_SUPPLY_TYPE_AC:\n                mHealthInfo->chargerAcOnline = true;\n                break;\n            case ANDROID_POWER_SUPPLY_TYPE_USB:\n                mHealthInfo->chargerUsbOnline = true;\n                break;\n            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:\n                mHealthInfo->chargerWirelessOnline = true;\n                break;\n            case ANDROID_POWER_SUPPLY_TYPE_DOCK:\n                mHealthInfo->chargerDockOnline = true;\n                break;\n            default:\n                path.clear();\n                path.appendFormat(\"%s/%s/is_dock\", POWER_SUPPLY_SYSFS_PATH,\n                                  mChargerNames[i].c_str());\n                if (access(path.c_str(), R_OK) == 0)\n                    mHealthInfo->chargerDockOnline = true;\n                else\n                    KLOG_WARNING(LOG_TAG, \"%s: Unknown power supply type\\n\",\n                                 mChargerNames[i].c_str());\n            }\n            path.clear();\n            path.appendFormat(\"%s/%s/current_max\", POWER_SUPPLY_SYSFS_PATH,\n                              mChargerNames[i].c_str());\n            int ChargingCurrent = (access(path.c_str(), R_OK) == 0) ? getIntField(path) : 0;\n\n            path.clear();\n            path.appendFormat(\"%s/%s/voltage_max\", POWER_SUPPLY_SYSFS_PATH,\n                              mChargerNames[i].c_str());\n\n            int ChargingVoltage =\n                    (access(path.c_str(), R_OK) == 0) ? getIntField(path) : DEFAULT_VBUS_VOLTAGE;\n\n            double power = ((double)ChargingCurrent / MILLION) *\n                           ((double)ChargingVoltage / MILLION);\n            if (MaxPower < power) {\n                mHealthInfo->maxChargingCurrentMicroamps = ChargingCurrent;\n                mHealthInfo->maxChargingVoltageMicrovolts = ChargingVoltage;\n                MaxPower = power;\n            }\n        }\n    }\n}\n\nstatic void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {\n    char dmesgline[256];\n    size_t len;\n    if (props.batteryPresent) {\n        snprintf(dmesgline, sizeof(dmesgline), \"battery l=%d v=%d t=%s%d.%d h=%d st=%d\",\n                 props.batteryLevel, props.batteryVoltageMillivolts,\n                 props.batteryTemperatureTenthsCelsius < 0 ? \"-\" : \"\",\n                 abs(props.batteryTemperatureTenthsCelsius / 10),\n                 abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,\n                 props.batteryStatus);\n\n        len = strlen(dmesgline);\n        if (!healthd_config.batteryCurrentNowPath.empty()) {\n            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, \" c=%d\",\n                            props.batteryCurrentMicroamps);\n        }\n\n        if (!healthd_config.batteryFullChargePath.empty()) {\n            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, \" fc=%d\",\n                            props.batteryFullChargeUah);\n        }\n\n        if (!healthd_config.batteryCycleCountPath.empty()) {\n            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, \" cc=%d\",\n                            props.batteryCycleCount);\n        }\n    } else {\n        len = snprintf(dmesgline, sizeof(dmesgline), \"battery none\");\n    }\n\n    snprintf(dmesgline + len, sizeof(dmesgline) - len, \" chg=%s%s%s%s\",\n             props.chargerAcOnline ? \"a\" : \"\", props.chargerUsbOnline ? \"u\" : \"\",\n             props.chargerWirelessOnline ? \"w\" : \"\", props.chargerDockOnline ? \"d\" : \"\");\n\n    KLOG_WARNING(LOG_TAG, \"%s\\n\", dmesgline);\n}\n\nvoid BatteryMonitor::logValues(const HealthInfo_2_1& health_info,\n                               const struct healthd_config& healthd_config) {\n    HealthInfo aidl_health_info;\n    (void)android::h2a::translate(health_info, &aidl_health_info);\n    doLogValues(aidl_health_info, healthd_config);\n}\n\nvoid BatteryMonitor::logValues(void) {\n    doLogValues(*mHealthInfo, *mHealthdConfig);\n}\n\nbool BatteryMonitor::isChargerOnline() {\n    const HealthInfo& props = *mHealthInfo;\n    return props.chargerAcOnline | props.chargerUsbOnline | props.chargerWirelessOnline |\n           props.chargerDockOnline;\n}\n\nint BatteryMonitor::getChargeStatus() {\n    BatteryStatus result = BatteryStatus::UNKNOWN;\n    if (!mHealthdConfig->batteryStatusPath.empty()) {\n        std::string buf;\n        if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)\n            result = getBatteryStatus(buf.c_str());\n    }\n    return static_cast<int>(result);\n}\n\nstatus_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {\n    status_t ret = BAD_VALUE;\n    std::string buf;\n\n    val->valueInt64 = LONG_MIN;\n\n    switch(id) {\n    case BATTERY_PROP_CHARGE_COUNTER:\n        if (!mHealthdConfig->batteryChargeCounterPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryChargeCounterPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_CURRENT_NOW:\n        if (!mHealthdConfig->batteryCurrentNowPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryCurrentNowPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_CURRENT_AVG:\n        if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryCurrentAvgPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_CAPACITY:\n        if (!mHealthdConfig->batteryCapacityPath.empty()) {\n            val->valueInt64 =\n                getIntField(mHealthdConfig->batteryCapacityPath);\n            ret = OK;\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_ENERGY_COUNTER:\n        if (mHealthdConfig->energyCounter) {\n            ret = mHealthdConfig->energyCounter(&val->valueInt64);\n        } else {\n            ret = NAME_NOT_FOUND;\n        }\n        break;\n\n    case BATTERY_PROP_BATTERY_STATUS:\n        val->valueInt64 = getChargeStatus();\n        ret = OK;\n        break;\n\n    default:\n        break;\n    }\n\n    return ret;\n}\n\nvoid BatteryMonitor::dumpState(int fd) {\n    int v;\n    char vs[128];\n    const HealthInfo& props = *mHealthInfo;\n\n    snprintf(vs, sizeof(vs),\n             \"ac: %d usb: %d wireless: %d dock: %d current_max: %d voltage_max: %d\\n\",\n             props.chargerAcOnline, props.chargerUsbOnline, props.chargerWirelessOnline,\n             props.chargerDockOnline, props.maxChargingCurrentMicroamps,\n             props.maxChargingVoltageMicrovolts);\n    write(fd, vs, strlen(vs));\n    snprintf(vs, sizeof(vs), \"status: %d health: %d present: %d\\n\",\n             props.batteryStatus, props.batteryHealth, props.batteryPresent);\n    write(fd, vs, strlen(vs));\n    snprintf(vs, sizeof(vs), \"level: %d voltage: %d temp: %d\\n\", props.batteryLevel,\n             props.batteryVoltageMillivolts, props.batteryTemperatureTenthsCelsius);\n    write(fd, vs, strlen(vs));\n\n    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {\n        v = getIntField(mHealthdConfig->batteryCurrentNowPath);\n        snprintf(vs, sizeof(vs), \"current now: %d\\n\", v);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryCurrentAvgPath.empty()) {\n        v = getIntField(mHealthdConfig->batteryCurrentAvgPath);\n        snprintf(vs, sizeof(vs), \"current avg: %d\\n\", v);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryChargeCounterPath.empty()) {\n        v = getIntField(mHealthdConfig->batteryChargeCounterPath);\n        snprintf(vs, sizeof(vs), \"charge counter: %d\\n\", v);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryCurrentNowPath.empty()) {\n        snprintf(vs, sizeof(vs), \"current now: %d\\n\", props.batteryCurrentMicroamps);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryCycleCountPath.empty()) {\n        snprintf(vs, sizeof(vs), \"cycle count: %d\\n\", props.batteryCycleCount);\n        write(fd, vs, strlen(vs));\n    }\n\n    if (!mHealthdConfig->batteryFullChargePath.empty()) {\n        snprintf(vs, sizeof(vs), \"Full charge: %d\\n\", props.batteryFullChargeUah);\n        write(fd, vs, strlen(vs));\n    }\n}\n\nvoid BatteryMonitor::init(struct healthd_config *hc) {\n    String8 path;\n    char pval[PROPERTY_VALUE_MAX];\n\n    mHealthdConfig = hc;\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);\n    if (dir == NULL) {\n        KLOG_ERROR(LOG_TAG, \"Could not open %s\\n\", POWER_SUPPLY_SYSFS_PATH);\n    } else {\n        struct dirent* entry;\n\n        while ((entry = readdir(dir.get()))) {\n            const char* name = entry->d_name;\n\n            if (!strcmp(name, \".\") || !strcmp(name, \"..\"))\n                continue;\n\n            std::vector<String8>::iterator itIgnoreName =\n                    find(hc->ignorePowerSupplyNames.begin(), hc->ignorePowerSupplyNames.end(),\n                         String8(name));\n            if (itIgnoreName != hc->ignorePowerSupplyNames.end())\n                continue;\n\n            // Look for \"type\" file in each subdirectory\n            path.clear();\n            path.appendFormat(\"%s/%s/type\", POWER_SUPPLY_SYSFS_PATH, name);\n            switch(readPowerSupplyType(path)) {\n            case ANDROID_POWER_SUPPLY_TYPE_AC:\n            case ANDROID_POWER_SUPPLY_TYPE_USB:\n            case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:\n            case ANDROID_POWER_SUPPLY_TYPE_DOCK:\n                path.clear();\n                path.appendFormat(\"%s/%s/online\", POWER_SUPPLY_SYSFS_PATH, name);\n                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));\n                break;\n\n            case ANDROID_POWER_SUPPLY_TYPE_BATTERY:\n                // Some devices expose the battery status of sub-component like\n                // stylus. Such a device-scoped battery info needs to be skipped\n                // in BatteryMonitor, which is intended to report the status of\n                // the battery supplying the power to the whole system.\n                if (isScopedPowerSupply(name)) continue;\n                mBatteryDevicePresent = true;\n\n                if (mHealthdConfig->batteryStatusPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/status\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryStatusPath = path;\n                }\n\n                if (mHealthdConfig->batteryHealthPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/health\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryHealthPath = path;\n                }\n\n                if (mHealthdConfig->batteryPresentPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/present\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryPresentPath = path;\n                }\n\n                if (mHealthdConfig->batteryCapacityPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/capacity\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) mHealthdConfig->batteryCapacityPath = path;\n                }\n\n                if (mHealthdConfig->batteryVoltagePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/voltage_now\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryVoltagePath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryFullChargePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charge_full\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryFullChargePath = path;\n                }\n\n                if (mHealthdConfig->batteryCurrentNowPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/current_now\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryCurrentNowPath = path;\n                }\n\n                if (mHealthdConfig->batteryCycleCountPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/cycle_count\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryCycleCountPath = path;\n                }\n\n                if (mHealthdConfig->batteryCapacityLevelPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/capacity_level\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryCapacityLevelPath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/time_to_full_now\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryChargeTimeToFullNowPath = path;\n                }\n\n                if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charge_full_design\", POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryFullChargeDesignCapacityUahPath = path;\n                }\n\n                if (mHealthdConfig->batteryCurrentAvgPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/current_avg\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryCurrentAvgPath = path;\n                }\n\n                if (mHealthdConfig->batteryChargeCounterPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/charge_counter\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryChargeCounterPath = path;\n                }\n\n                if (mHealthdConfig->batteryTemperaturePath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/temp\", POWER_SUPPLY_SYSFS_PATH,\n                                      name);\n                    if (access(path.c_str(), R_OK) == 0) {\n                        mHealthdConfig->batteryTemperaturePath = path;\n                    }\n                }\n\n                if (mHealthdConfig->batteryTechnologyPath.empty()) {\n                    path.clear();\n                    path.appendFormat(\"%s/%s/technology\",\n                                      POWER_SUPPLY_SYSFS_PATH, name);\n                    if (access(path.c_str(), R_OK) == 0)\n                        mHealthdConfig->batteryTechnologyPath = path;\n                }\n\n                break;\n\n            case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:\n                break;\n            }\n\n            // Look for \"is_dock\" file\n            path.clear();\n            path.appendFormat(\"%s/%s/is_dock\", POWER_SUPPLY_SYSFS_PATH, name);\n            if (access(path.c_str(), R_OK) == 0) {\n                path.clear();\n                path.appendFormat(\"%s/%s/online\", POWER_SUPPLY_SYSFS_PATH, name);\n                if (access(path.c_str(), R_OK) == 0) mChargerNames.add(String8(name));\n            }\n        }\n    }\n\n    // Typically the case for devices which do not have a battery and\n    // and are always plugged into AC mains.\n    if (!mBatteryDevicePresent) {\n        KLOG_WARNING(LOG_TAG, \"No battery devices found\\n\");\n        hc->periodic_chores_interval_fast = -1;\n        hc->periodic_chores_interval_slow = -1;\n    } else {\n        if (mHealthdConfig->batteryStatusPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryStatusPath not found\\n\");\n        if (mHealthdConfig->batteryHealthPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryHealthPath not found\\n\");\n        if (mHealthdConfig->batteryPresentPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryPresentPath not found\\n\");\n        if (mHealthdConfig->batteryCapacityPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryCapacityPath not found\\n\");\n        if (mHealthdConfig->batteryVoltagePath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryVoltagePath not found\\n\");\n        if (mHealthdConfig->batteryTemperaturePath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryTemperaturePath not found\\n\");\n        if (mHealthdConfig->batteryTechnologyPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryTechnologyPath not found\\n\");\n        if (mHealthdConfig->batteryCurrentNowPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryCurrentNowPath not found\\n\");\n        if (mHealthdConfig->batteryFullChargePath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryFullChargePath not found\\n\");\n        if (mHealthdConfig->batteryCycleCountPath.empty())\n            KLOG_WARNING(LOG_TAG, \"BatteryCycleCountPath not found\\n\");\n        if (mHealthdConfig->batteryCapacityLevelPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryCapacityLevelPath not found\\n\");\n        if (mHealthdConfig->batteryChargeTimeToFullNowPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryChargeTimeToFullNowPath. not found\\n\");\n        if (mHealthdConfig->batteryFullChargeDesignCapacityUahPath.empty())\n            KLOG_WARNING(LOG_TAG, \"batteryFullChargeDesignCapacityUahPath. not found\\n\");\n    }\n\n    if (property_get(\"ro.boot.fake_battery\", pval, NULL) > 0\n                                               && strtol(pval, NULL, 10) != 0) {\n        mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;\n        mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;\n    }\n}\n\n}; // namespace android\n"
  },
  {
    "path": "healthd/OWNERS",
    "content": "include platform/hardware/interfaces:/health/OWNERS\n"
  },
  {
    "path": "healthd/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"libhealthd_charger_test\"\n    }\n  ],\n  \"hwasan-postsubmit\": [\n    {\n      \"name\": \"libhealthd_charger_test\"\n    }\n  ]\n}\n"
  },
  {
    "path": "healthd/animation.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef HEALTHD_ANIMATION_H\n#define HEALTHD_ANIMATION_H\n\n#include <inttypes.h>\n\n#include <string>\n\nclass GRSurface;\nstruct GRFont;\n\nnamespace android {\n\n#define CENTER_VAL INT_MAX\n\nstruct animation {\n    struct frame {\n        int disp_time;\n        int min_level;\n        int max_level;\n\n        GRSurface* surface;\n    };\n\n    struct text_field {\n        std::string font_file;\n        int pos_x;\n        int pos_y;\n        int color_r;\n        int color_g;\n        int color_b;\n        int color_a;\n\n        GRFont* font;\n    };\n\n    // When libminui loads PNG images:\n    // - When treating paths as relative paths, it adds \".png\" suffix.\n    // - When treating paths as absolute paths, it doesn't add the suffix. Hence, the suffix\n    //   is added here.\n    // If |backup_root| is provided, additionally check if file under |root| is accessbile or not.\n    // If not accessbile, use |backup_root| instead.\n    // Require that |root| starts and ends with \"/\". If |backup_root| is provided, require that\n    // |backup_root| starts and ends with \"/\".\n    void set_resource_root(const std::string& root, const std::string& backup_root = \"\");\n\n    std::string animation_file;\n    std::string fail_file;\n\n    text_field text_clock;\n    text_field text_percent;\n\n    bool run;\n\n    frame* frames = nullptr;\n    int cur_frame;\n    int num_frames;\n    int first_frame_repeats;  // Number of times to repeat the first frame in the current cycle\n\n    int cur_cycle;\n    int num_cycles;  // Number of cycles to complete before blanking the screen\n\n    int cur_level;  // current battery level being animated (0-100)\n    int cur_status;  // current battery status - see BatteryService.h for BATTERY_STATUS_*\n\n    ~animation() { delete frames; }\n};\n\n}\n\n#endif // HEALTHD_ANIMATION_H\n"
  },
  {
    "path": "healthd/api/charger_sysprop-current.txt",
    "content": ""
  },
  {
    "path": "healthd/api/charger_sysprop-latest.txt",
    "content": "props {\n  module: \"android.sysprop.ChargerProperties\"\n  prop {\n    api_name: \"disable_init_blank\"\n    scope: Internal\n    prop_name: \"ro.charger.disable_init_blank\"\n  }\n  prop {\n    api_name: \"draw_split_offset\"\n    type: Long\n    scope: Internal\n    prop_name: \"ro.charger.draw_split_offset\"\n  }\n  prop {\n    api_name: \"draw_split_screen\"\n    scope: Internal\n    prop_name: \"ro.charger.draw_split_screen\"\n  }\n  prop {\n    api_name: \"enable_suspend\"\n    scope: Internal\n    prop_name: \"ro.charger.enable_suspend\"\n  }\n  prop {\n    api_name: \"no_ui\"\n    scope: Internal\n    prop_name: \"ro.charger.no_ui\"\n  }\n}\n"
  },
  {
    "path": "healthd/charger.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n#include <charger.sysprop.h>\n\n#include \"healthd_mode_charger_hidl.h\"\n#include \"healthd_mode_charger_nops.h\"\n\n#ifndef CHARGER_FORCE_NO_UI\n#define CHARGER_FORCE_NO_UI 0\n#endif\n\nint main(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n    if (CHARGER_FORCE_NO_UI || android::sysprop::ChargerProperties::no_ui().value_or(false)) {\n        return healthd_charger_nops(argc, argv);\n    } else {\n        return healthd_charger_main(argc, argv);\n    }\n}\n"
  },
  {
    "path": "healthd/charger.sysprop",
    "content": "owner: Platform\nmodule: \"android.sysprop.ChargerProperties\"\n\nprop {\n    api_name: \"draw_split_screen\"\n    type: Boolean\n    prop_name: \"ro.charger.draw_split_screen\"\n    scope: Internal\n    access: Readonly\n}\nprop {\n    api_name: \"draw_split_offset\"\n    type: Long\n    prop_name: \"ro.charger.draw_split_offset\"\n    scope: Internal\n    access: Readonly\n}\nprop {\n    api_name: \"disable_init_blank\"\n    type: Boolean\n    prop_name: \"ro.charger.disable_init_blank\"\n    scope: Internal\n    access: Readonly\n}\nprop {\n    api_name: \"enable_suspend\"\n    type: Boolean\n    prop_name: \"ro.charger.enable_suspend\"\n    scope: Internal\n    access: Readonly\n}\nprop {\n    api_name: \"no_ui\"\n    type: Boolean\n    prop_name: \"ro.charger.no_ui\"\n    scope: Internal\n    access: Readonly\n}\n"
  },
  {
    "path": "healthd/charger_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"charger_test\"\n#include <android/log.h>\n\n#include <chrono>\n#include <condition_variable>\n#include <fstream>\n#include <iostream>\n#include <memory>\n#include <mutex>\n#include <streambuf>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <health/utils.h>\n#include <health2impl/Health.h>\n\n#include \"healthd_mode_charger_hidl.h\"\n\nusing android::hardware::health::InitHealthdConfig;\nusing android::hardware::health::V2_1::HealthInfo;\nusing android::hardware::health::V2_1::IHealth;\nusing android::hardware::health::V2_1::implementation::Health;\n\n#define LOG_THIS(fmt, ...)     \\\n    ALOGE(fmt, ##__VA_ARGS__); \\\n    printf(fmt \"\\n\", ##__VA_ARGS__);\n\ntemplate <typename T>\nclass Atomic {\n  public:\n    Atomic(T&& init) : mValue(std::move(init)) {}\n    void set(T&& newVal) {\n        {\n            std::lock_guard<std::mutex> lock(mMutex);\n            mValue = std::move(newVal);\n        }\n        mChanged.notify_all();\n    }\n    bool waitFor(long ms, const T& expectVal) {\n        std::unique_lock<std::mutex> lock(mMutex);\n        return mChanged.wait_for(lock, std::chrono::milliseconds(ms),\n                                 [this, &expectVal] { return mValue == expectVal; });\n    }\n  private:\n    std::mutex mMutex;\n    std::condition_variable mChanged;\n    T mValue;\n};\n\nAtomic<bool>& getUpdateNotifier() {\n    static Atomic<bool> val(false);\n    return val;\n}\n\nint energyCounter(int64_t* counter) {\n    *counter = 0xEC12345;\n    return 0;\n}\n\nconst char* createFile(const char* path, const char* content) {\n    std::ofstream stream(path);\n    if (!stream.is_open()) {\n        LOG_THIS(\"Cannot create file %s\", path);\n        return NULL;\n    }\n    stream << content << std::endl;\n    stream.close();\n    return path;\n}\n\nstd::string openToString(const char* path) {\n    std::ifstream stream(path);\n    if (!stream.is_open()) {\n        LOG_THIS(\"Cannot open file %s\", path);\n        return \"\";\n    }\n    return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>());\n}\n\nint expectContains(const std::string& content, const std::vector<std::string>& fields) {\n    int status = 0;\n    for (const auto& field : fields) {\n        auto pos = content.find(field);\n        if (pos == std::string::npos) {\n            LOG_THIS(\"Cannot find substr '%s'\", field.c_str());\n            status = 1;\n        }\n    }\n    return status;\n}\n\n::android::hardware::hidl_handle createHidlHandle(const char* filepath) {\n    int fd = creat(filepath, S_IRUSR | S_IWUSR);\n    if (fd < 0) return {};\n    native_handle_t* nativeHandle = native_handle_create(1, 0);\n    nativeHandle->data[0] = fd;\n    ::android::hardware::hidl_handle handle;\n    handle.setTo(nativeHandle, true /* shouldOwn */);\n    return handle;\n}\n\nvoid healthd_board_init(struct healthd_config* config) {\n    config->periodic_chores_interval_fast = 60;\n    config->periodic_chores_interval_slow = 600;\n\n    config->batteryStatusPath = createFile(\"/data/local/tmp/batteryStatus\", \"Not charging\");\n    config->batteryHealthPath = createFile(\"/data/local/tmp/batteryHealth\", \"Unspecified failure\");\n    config->batteryPresentPath = createFile(\"/data/local/tmp/batteryPresent\", \"1\");\n    config->batteryCapacityPath = createFile(\"/data/local/tmp/batteryCapacity\", \"47\");\n    config->batteryVoltagePath = createFile(\"/data/local/tmp/batteryVoltage\", \"45000\");\n    config->batteryTemperaturePath = createFile(\"/data/local/tmp/batteryTemperature\", \"987\");\n    config->batteryTechnologyPath = createFile(\"/data/local/tmp/batteryTechnology\", \"NiCd\");\n    config->batteryCurrentNowPath = createFile(\"/data/local/tmp/batteryCurrentNow\", \"99000\");\n    config->batteryCurrentAvgPath = createFile(\"/data/local/tmp/batteryCurrentAvg\", \"98000\");\n    config->batteryChargeCounterPath = createFile(\"/data/local/tmp/batteryChargeCounter\", \"600\");\n    config->batteryFullChargePath = createFile(\"/data/local/tmp/batteryFullCharge\", \"3515547\");\n    config->batteryCycleCountPath = createFile(\"/data/local/tmp/batteryCycleCount\", \"77\");\n\n    config->energyCounter = energyCounter;\n    config->boot_min_cap = 50;\n    config->screen_on = NULL;\n}\n\nclass TestHealth : public Health {\n  protected:\n    using Health::Health;\n    void UpdateHealthInfo(HealthInfo*) override { getUpdateNotifier().set(true /* updated */); }\n};\n\nint main(int /*argc*/, char** /*argv*/) {\n    const char* dumpFile = \"/data/local/tmp/dump.txt\";\n\n    auto config = std::make_unique<healthd_config>();\n    InitHealthdConfig(config.get());\n    healthd_board_init(config.get());\n    sp<IHealth> passthrough = new TestHealth(std::move(config));\n\n    std::thread bgThread([=] {\n        android::ChargerHidl charger(passthrough);\n        charger.StartLoop();\n    });\n\n    // wait for healthd_init to finish\n    if (!getUpdateNotifier().waitFor(1000 /* wait ms */, true /* updated */)) {\n        LOG_THIS(\"Time out.\");\n        exit(1);\n    }\n\n    passthrough->debug(createHidlHandle(dumpFile), {} /* options */);\n\n    std::string content = openToString(dumpFile);\n    int status = expectContains(content, {\n        \"status: 4\",\n        \"health: 6\",\n        \"present: 1\",\n        \"level: 47\",\n        \"voltage: 45\",\n        \"temp: 987\",\n        \"current now: 99000\",\n        \"current avg: 98000\",\n        \"charge counter: 600\",\n        \"current now: 99\",\n        \"cycle count: 77\",\n        \"Full charge: 3515547\"\n    });\n\n    if (status == 0) {\n        LOG_THIS(\"Test success.\");\n    } else {\n        LOG_THIS(\"Actual dump:\\n%s\", content.c_str());\n    }\n\n    exit(status);  // force bgThread to exit\n}\n"
  },
  {
    "path": "healthd/charger_utils.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"charger_utils.h\"\n\n#include <android-base/logging.h>\n#include <android/hardware/health/2.1/IHealth.h>\n#include <health/utils.h>\n#include <health2impl/Health.h>\n\nnamespace android {\nnamespace hardware {\nnamespace health {\n\nsp<V2_1::IHealth> GetHealthServiceOrDefault() {\n    // No need to use get_health_service from libhealthhalutils that\n    // checks for \"backup\" instance provided by healthd, since\n    // V2_1::implementation::Health does the same thing.\n    sp<V2_1::IHealth> service = V2_1::IHealth::getService();\n    if (service != nullptr) {\n        LOG(INFO) << \"Charger uses health HAL service.\";\n    } else {\n        LOG(WARNING) << \"Charger uses system defaults.\";\n        auto config = std::make_unique<healthd_config>();\n        InitHealthdConfig(config.get());\n        service = new V2_1::implementation::Health(std::move(config));\n    }\n    return service;\n}\n\n}  // namespace health\n}  // namespace hardware\n}  // namespace android\n"
  },
  {
    "path": "healthd/charger_utils.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android/hardware/health/2.1/IHealth.h>\n\nnamespace android {\nnamespace hardware {\nnamespace health {\n// Return health HAL service. If it is not supported on the device (with\n// VINTF checks), return a default passthrough implementation.\nsp<V2_1::IHealth> GetHealthServiceOrDefault();\n}  // namespace health\n}  // namespace hardware\n}  // namespace android\n"
  },
  {
    "path": "healthd/healthd_draw.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/stringprintf.h>\n#include <batteryservice/BatteryService.h>\n#include <cutils/klog.h>\n\n#include \"healthd_draw.h\"\n\n#if !defined(__ANDROID_VNDK__)\n#include \"charger.sysprop.h\"\n#endif\n\n#define LOGE(x...) KLOG_ERROR(\"charger\", x);\n#define LOGW(x...) KLOG_WARNING(\"charger\", x);\n#define LOGV(x...) KLOG_DEBUG(\"charger\", x);\n\nstatic bool get_split_screen() {\n#if !defined(__ANDROID_VNDK__)\n    return android::sysprop::ChargerProperties::draw_split_screen().value_or(false);\n#else\n    return false;\n#endif\n}\n\nstatic int get_split_offset() {\n#if !defined(__ANDROID_VNDK__)\n    int64_t value = android::sysprop::ChargerProperties::draw_split_offset().value_or(0);\n#else\n    int64_t value = 0;\n#endif\n    if (value < static_cast<int64_t>(std::numeric_limits<int>::min())) {\n        LOGW(\"draw_split_offset = %\" PRId64 \" overflow for an int; resetting to %d.\\n\", value,\n             std::numeric_limits<int>::min());\n        value = std::numeric_limits<int>::min();\n    }\n    if (value > static_cast<int64_t>(std::numeric_limits<int>::max())) {\n        LOGW(\"draw_split_offset = %\" PRId64 \" overflow for an int; resetting to %d.\\n\", value,\n             std::numeric_limits<int>::max());\n        value = std::numeric_limits<int>::max();\n    }\n    return static_cast<int>(value);\n}\n\nHealthdDraw::HealthdDraw(animation* anim)\n    : kSplitScreen(get_split_screen()), kSplitOffset(get_split_offset()) {\n    graphics_available = true;\n    sys_font = gr_sys_font();\n    if (sys_font == nullptr) {\n        LOGW(\"No system font, screen fallback text not available\\n\");\n    } else {\n        gr_font_size(sys_font, &char_width_, &char_height_);\n    }\n\n    screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);\n    screen_height_ = gr_fb_height();\n\n    int res;\n    if (!anim->text_clock.font_file.empty() &&\n        (res = gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {\n        LOGE(\"Could not load time font (%d)\\n\", res);\n    }\n    if (!anim->text_percent.font_file.empty() &&\n        (res = gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {\n        LOGE(\"Could not load percent font (%d)\\n\", res);\n    }\n}\n\nHealthdDraw::~HealthdDraw() {}\n\nvoid HealthdDraw::redraw_screen(const animation* batt_anim, GRSurface* surf_unknown) {\n    if (!graphics_available) return;\n    clear_screen();\n\n    /* try to display *something* */\n    if (batt_anim->cur_status == BATTERY_STATUS_UNKNOWN || batt_anim->cur_level < 0 ||\n        batt_anim->num_frames == 0)\n        draw_unknown(surf_unknown);\n    else\n        draw_battery(batt_anim);\n    gr_flip();\n}\n\nvoid HealthdDraw::blank_screen(bool blank, int drm) {\n    if (!graphics_available) return;\n    gr_fb_blank(blank, drm);\n}\n\n// support screen rotation for foldable phone\nvoid HealthdDraw::rotate_screen(int drm) {\n    if (!graphics_available) return;\n    if (drm == 0)\n        gr_rotate(GRRotation::RIGHT /* landscape mode */);\n    else\n        gr_rotate(GRRotation::NONE /* Portrait mode */);\n}\n\n// detect dual display\nbool HealthdDraw::has_multiple_connectors() {\n    return graphics_available && gr_has_multiple_connectors();\n}\n\nvoid HealthdDraw::clear_screen(void) {\n    if (!graphics_available) return;\n    gr_color(0, 0, 0, 255);\n    gr_clear();\n}\n\nint HealthdDraw::draw_surface_centered(GRSurface* surface) {\n    if (!graphics_available) return 0;\n\n    int w = gr_get_width(surface);\n    int h = gr_get_height(surface);\n    int x = (screen_width_ - w) / 2 + kSplitOffset;\n    int y = (screen_height_ - h) / 2;\n\n    LOGV(\"drawing surface %dx%d+%d+%d\\n\", w, h, x, y);\n    gr_blit(surface, 0, 0, w, h, x, y);\n    if (kSplitScreen) {\n        x += screen_width_ - 2 * kSplitOffset;\n        LOGV(\"drawing surface %dx%d+%d+%d\\n\", w, h, x, y);\n        gr_blit(surface, 0, 0, w, h, x, y);\n    }\n\n    return y + h;\n}\n\nint HealthdDraw::draw_text(const GRFont* font, int x, int y, const char* str) {\n    if (!graphics_available) return 0;\n    int str_len_px = gr_measure(font, str);\n\n    if (x < 0) x = (screen_width_ - str_len_px) / 2;\n    if (y < 0) y = (screen_height_ - char_height_) / 2;\n    gr_text(font, x + kSplitOffset, y, str, false /* bold */);\n    if (kSplitScreen) gr_text(font, x - kSplitOffset + screen_width_, y, str, false /* bold */);\n\n    return y + char_height_;\n}\n\nvoid HealthdDraw::determine_xy(const animation::text_field& field,\n                               const int length, int* x, int* y) {\n  *x = field.pos_x;\n  screen_width_ = gr_fb_width() / (kSplitScreen ? 2 : 1);\n  screen_height_ = gr_fb_height();\n\n  int str_len_px = length * field.font->char_width;\n  if (field.pos_x == CENTER_VAL) {\n    *x = (screen_width_ - str_len_px) / 2;\n  } else if (field.pos_x >= 0) {\n    *x = field.pos_x;\n  } else {  // position from max edge\n    *x = screen_width_ + field.pos_x - str_len_px - kSplitOffset;\n  }\n\n  *y = field.pos_y;\n\n  if (field.pos_y == CENTER_VAL) {\n    *y = (screen_height_ - field.font->char_height) / 2;\n  } else if (field.pos_y >= 0) {\n    *y = field.pos_y;\n  } else {  // position from max edge\n    *y = screen_height_ + field.pos_y - field.font->char_height;\n  }\n}\n\nvoid HealthdDraw::draw_clock(const animation* anim) {\n    static constexpr char CLOCK_FORMAT[] = \"%H:%M\";\n    static constexpr int CLOCK_LENGTH = 6;\n\n    const animation::text_field& field = anim->text_clock;\n\n    if (!graphics_available || field.font == nullptr || field.font->char_width == 0 ||\n        field.font->char_height == 0)\n        return;\n\n    time_t rawtime;\n    time(&rawtime);\n    tm* time_info = localtime(&rawtime);\n\n    char clock_str[CLOCK_LENGTH];\n    size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);\n    if (length != CLOCK_LENGTH - 1) {\n        LOGE(\"Could not format time\\n\");\n        return;\n    }\n\n    int x, y;\n    determine_xy(field, length, &x, &y);\n\n    LOGV(\"drawing clock %s %d %d\\n\", clock_str, x, y);\n    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);\n    draw_text(field.font, x, y, clock_str);\n}\n\nvoid HealthdDraw::draw_percent(const animation* anim) {\n    if (!graphics_available) return;\n    int cur_level = anim->cur_level;\n    if (anim->cur_status == BATTERY_STATUS_FULL) {\n        cur_level = 100;\n    }\n\n    if (cur_level < 0) return;\n\n    const animation::text_field& field = anim->text_percent;\n    if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {\n        return;\n    }\n\n    std::string str = base::StringPrintf(\"%d%%\", cur_level);\n\n    int x, y;\n    determine_xy(field, str.size(), &x, &y);\n\n    LOGV(\"drawing percent %s %d %d\\n\", str.c_str(), x, y);\n    gr_color(field.color_r, field.color_g, field.color_b, field.color_a);\n    draw_text(field.font, x, y, str.c_str());\n}\n\nvoid HealthdDraw::draw_battery(const animation* anim) {\n    if (!graphics_available) return;\n    const animation::frame& frame = anim->frames[anim->cur_frame];\n\n    if (anim->num_frames != 0) {\n        draw_surface_centered(frame.surface);\n        LOGV(\"drawing frame #%d min_cap=%d time=%d\\n\", anim->cur_frame, frame.min_level,\n             frame.disp_time);\n    }\n    draw_clock(anim);\n    draw_percent(anim);\n}\n\nvoid HealthdDraw::draw_unknown(GRSurface* surf_unknown) {\n  int y;\n  if (surf_unknown) {\n      draw_surface_centered(surf_unknown);\n  } else if (sys_font) {\n      gr_color(0xa4, 0xc6, 0x39, 255);\n      y = draw_text(sys_font, -1, -1, \"Charging!\");\n      draw_text(sys_font, -1, y + 25, \"?\\?/100\");\n  } else {\n      LOGW(\"Charging, level unknown\\n\");\n  }\n}\n\nstd::unique_ptr<HealthdDraw> HealthdDraw::Create(animation *anim) {\n    if (gr_init() < 0) {\n        LOGE(\"gr_init failed\\n\");\n        return nullptr;\n    }\n    return std::unique_ptr<HealthdDraw>(new HealthdDraw(anim));\n}\n"
  },
  {
    "path": "healthd/healthd_draw.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef HEALTHD_DRAW_H\n#define HEALTHD_DRAW_H\n\n#include <linux/input.h>\n#include <minui/minui.h>\n\n#include \"animation.h\"\n\nusing namespace android;\n\nclass HealthdDraw {\n public:\n  virtual ~HealthdDraw();\n\n  // Redraws screen.\n  void redraw_screen(const animation* batt_anim, GRSurface* surf_unknown);\n\n  // According to the index of Direct Rendering Manager,\n  // Blanks screen if true, unblanks if false.\n  virtual void blank_screen(bool blank, int drm);\n\n  // Rotate screen.\n  virtual void rotate_screen(int drm);\n\n  // Detect dual display\n  virtual bool has_multiple_connectors();\n\n  static std::unique_ptr<HealthdDraw> Create(animation *anim);\n\n protected:\n  virtual void clear_screen();\n\n  // returns the last y-offset of where the surface ends.\n  virtual int draw_surface_centered(GRSurface* surface);\n  // Negative x or y coordinates center text.\n  virtual int draw_text(const GRFont* font, int x, int y, const char* str);\n\n  // Negative x or y coordinates position the text away from the opposite edge\n  // that positive ones do.\n  virtual void determine_xy(const animation::text_field& field,\n                            const int length, int* x, int* y);\n\n  // Draws battery animation, if it exists.\n  virtual void draw_battery(const animation* anim);\n  // Draws clock text, if animation contains text_field data.\n  virtual void draw_clock(const animation* anim);\n  // Draws battery percentage text if animation contains text_field data.\n  virtual void draw_percent(const animation* anim);\n  // Draws charger->surf_unknown or basic text.\n  virtual void draw_unknown(GRSurface* surf_unknown);\n\n  // Pixel sizes of characters for default font.\n  int char_width_;\n  int char_height_;\n\n  // Width and height of screen in pixels.\n  int screen_width_;\n  int screen_height_;\n\n  // Device screen is split vertically.\n  const bool kSplitScreen;\n  // Pixels to offset graphics towards center split.\n  const int kSplitOffset;\n\n  // system text font, may be nullptr\n  const GRFont* sys_font;\n\n  // true if minui init'ed OK, false if minui init failed\n  bool graphics_available;\n\n private:\n  // Configures font using given animation.\n  HealthdDraw(animation* anim);\n};\n\n#endif  // HEALTHD_DRAW_H\n"
  },
  {
    "path": "healthd/healthd_mode_charger.cpp",
    "content": "/*\n * Copyright (C) 2011-2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <charger/healthd_mode_charger.h>\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/epoll.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/un.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <optional>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/strings.h>\n\n#include <linux/netlink.h>\n#include <sys/socket.h>\n\n#include <cutils/android_get_control_file.h>\n#include <cutils/klog.h>\n#include <cutils/misc.h>\n#include <cutils/properties.h>\n#include <cutils/uevent.h>\n#include <sys/reboot.h>\n\n#include <suspend/autosuspend.h>\n\n#include \"AnimationParser.h\"\n#include \"healthd_draw.h\"\n\n#include <aidl/android/hardware/health/BatteryStatus.h>\n#include <health/HealthLoop.h>\n#include <healthd/healthd.h>\n\n#if !defined(__ANDROID_VNDK__)\n#include \"charger.sysprop.h\"\n#endif\n\nusing std::string_literals::operator\"\"s;\nusing namespace android;\nusing aidl::android::hardware::health::BatteryStatus;\nusing android::hardware::health::HealthLoop;\n\n// main healthd loop\nextern int healthd_main(void);\n\n// minui globals\nchar* locale;\n\n#ifndef max\n#define max(a, b) ((a) > (b) ? (a) : (b))\n#endif\n\n#ifndef min\n#define min(a, b) ((a) < (b) ? (a) : (b))\n#endif\n\n#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))\n\n#define MSEC_PER_SEC (1000LL)\n#define NSEC_PER_MSEC (1000000LL)\n\n#define BATTERY_UNKNOWN_TIME (2 * MSEC_PER_SEC)\n#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)\n#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)\n#define UNPLUGGED_DISPLAY_TIME (3 * MSEC_PER_SEC)\n#define MAX_BATT_LEVEL_WAIT_TIME (5 * MSEC_PER_SEC)\n#define UNPLUGGED_SHUTDOWN_TIME_PROP \"ro.product.charger.unplugged_shutdown_time\"\n\n#define LAST_KMSG_MAX_SZ (32 * 1024)\n\n#define LOGE(x...) KLOG_ERROR(\"charger\", x);\n#define LOGW(x...) KLOG_WARNING(\"charger\", x);\n#define LOGV(x...) KLOG_DEBUG(\"charger\", x);\n\nnamespace android {\n\n#if defined(__ANDROID_VNDK__)\nstatic constexpr const char* vendor_animation_desc_path =\n        \"/vendor/etc/res/values/charger/animation.txt\";\nstatic constexpr const char* vendor_animation_root = \"/vendor/etc/res/images/\";\nstatic constexpr const char* vendor_default_animation_root = \"/vendor/etc/res/images/default/\";\n#else\n\n// Legacy animation resources are loaded from this directory.\nstatic constexpr const char* legacy_animation_root = \"/res/images/\";\n\n// Built-in animation resources are loaded from this directory.\nstatic constexpr const char* system_animation_root = \"/system/etc/res/images/\";\n\n// Resources in /product/etc/res overrides resources in /res and /system/etc/res.\n// If the device is using the Generic System Image (GSI), resources may exist in\n// both paths.\nstatic constexpr const char* product_animation_desc_path =\n        \"/product/etc/res/values/charger/animation.txt\";\nstatic constexpr const char* product_animation_root = \"/product/etc/res/images/\";\nstatic constexpr const char* animation_desc_path = \"/res/values/charger/animation.txt\";\n#endif\n\nstatic const animation BASE_ANIMATION = {\n    .text_clock =\n        {\n            .pos_x = 0,\n            .pos_y = 0,\n\n            .color_r = 255,\n            .color_g = 255,\n            .color_b = 255,\n            .color_a = 255,\n\n            .font = nullptr,\n        },\n    .text_percent =\n        {\n            .pos_x = 0,\n            .pos_y = 0,\n\n            .color_r = 255,\n            .color_g = 255,\n            .color_b = 255,\n            .color_a = 255,\n        },\n\n    .run = false,\n\n    .frames = nullptr,\n    .cur_frame = 0,\n    .num_frames = 0,\n    .first_frame_repeats = 2,\n\n    .cur_cycle = 0,\n    .num_cycles = 3,\n\n    .cur_level = 0,\n    .cur_status = BATTERY_STATUS_UNKNOWN,\n};\n\nvoid Charger::InitDefaultAnimationFrames() {\n    owned_frames_ = {\n            {\n                    .disp_time = 750,\n                    .min_level = 0,\n                    .max_level = 19,\n                    .surface = NULL,\n            },\n            {\n                    .disp_time = 750,\n                    .min_level = 0,\n                    .max_level = 39,\n                    .surface = NULL,\n            },\n            {\n                    .disp_time = 750,\n                    .min_level = 0,\n                    .max_level = 59,\n                    .surface = NULL,\n            },\n            {\n                    .disp_time = 750,\n                    .min_level = 0,\n                    .max_level = 79,\n                    .surface = NULL,\n            },\n            {\n                    .disp_time = 750,\n                    .min_level = 80,\n                    .max_level = 95,\n                    .surface = NULL,\n            },\n            {\n                    .disp_time = 750,\n                    .min_level = 0,\n                    .max_level = 100,\n                    .surface = NULL,\n            },\n    };\n}\n\nCharger::Charger(ChargerConfigurationInterface* configuration)\n    : batt_anim_(BASE_ANIMATION), configuration_(configuration) {}\n\nCharger::~Charger() {}\n\n/* current time in milliseconds */\nstatic int64_t curr_time_ms() {\n    timespec tm;\n    clock_gettime(CLOCK_MONOTONIC, &tm);\n    return tm.tv_sec * MSEC_PER_SEC + (tm.tv_nsec / NSEC_PER_MSEC);\n}\n\n#define MAX_KLOG_WRITE_BUF_SZ 256\n\nstatic void dump_last_kmsg(void) {\n    std::string buf;\n    char* ptr;\n    size_t len;\n\n    LOGW(\"*************** LAST KMSG ***************\\n\");\n    const char* kmsg[] = {\n        // clang-format off\n        \"/sys/fs/pstore/console-ramoops-0\",\n        \"/sys/fs/pstore/console-ramoops\",\n        \"/proc/last_kmsg\",\n        // clang-format on\n    };\n    for (size_t i = 0; i < arraysize(kmsg) && buf.empty(); ++i) {\n        auto fd = android_get_control_file(kmsg[i]);\n        if (fd >= 0) {\n            android::base::ReadFdToString(fd, &buf);\n        } else {\n            android::base::ReadFileToString(kmsg[i], &buf);\n        }\n    }\n\n    if (buf.empty()) {\n        LOGW(\"last_kmsg not found. Cold reset?\\n\");\n        goto out;\n    }\n\n    len = min(buf.size(), LAST_KMSG_MAX_SZ);\n    ptr = &buf[buf.size() - len];\n\n    while (len > 0) {\n        size_t cnt = min(len, MAX_KLOG_WRITE_BUF_SZ);\n        char yoink;\n        char* nl;\n\n        nl = (char*)memrchr(ptr, '\\n', cnt - 1);\n        if (nl) cnt = nl - ptr + 1;\n\n        yoink = ptr[cnt];\n        ptr[cnt] = '\\0';\n        klog_write(6, \"<4>%s\", ptr);\n        ptr[cnt] = yoink;\n\n        len -= cnt;\n        ptr += cnt;\n    }\n\nout:\n    LOGW(\"************* END LAST KMSG *************\\n\");\n}\n\nint Charger::RequestEnableSuspend() {\n    if (!configuration_->ChargerEnableSuspend()) {\n        return 0;\n    }\n    return autosuspend_enable();\n}\n\nint Charger::RequestDisableSuspend() {\n    if (!configuration_->ChargerEnableSuspend()) {\n        return 0;\n    }\n    return autosuspend_disable();\n}\n\nstatic void kick_animation(animation* anim) {\n    anim->run = true;\n}\n\nstatic void reset_animation(animation* anim) {\n    anim->cur_cycle = 0;\n    anim->cur_frame = 0;\n    anim->run = false;\n}\n\nvoid Charger::BlankSecScreen() {\n    int drm = drm_ == DRM_INNER ? 1 : 0;\n\n    if (!init_screen_) {\n        /* blank the secondary screen */\n        healthd_draw_->blank_screen(false, drm);\n        healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);\n        healthd_draw_->blank_screen(true, drm);\n        init_screen_ = true;\n    }\n}\n\nvoid Charger::UpdateScreenState(int64_t now) {\n    int disp_time;\n\n    if (!batt_anim_.run || now < next_screen_transition_) return;\n\n    // If battery status is not ready, keep checking in the defined time\n    if (health_info_.battery_status == BatteryStatus::UNKNOWN) {\n        if (wait_batt_level_timestamp_ == 0) {\n            // Set max delay time and skip drawing screen\n            wait_batt_level_timestamp_ = now + MAX_BATT_LEVEL_WAIT_TIME;\n            LOGV(\"[%\" PRId64 \"] wait for battery capacity ready\\n\", now);\n            return;\n        } else if (now <= wait_batt_level_timestamp_) {\n            // Do nothing, keep waiting\n            return;\n        }\n        // If timeout and battery status is still not ready, draw unknown battery\n    }\n\n    if (healthd_draw_ == nullptr) return;\n\n    /* animation is over, blank screen and leave */\n    if (batt_anim_.num_cycles > 0 && batt_anim_.cur_cycle == batt_anim_.num_cycles) {\n        reset_animation(&batt_anim_);\n        next_screen_transition_ = -1;\n        healthd_draw_->blank_screen(true, static_cast<int>(drm_));\n        if (healthd_draw_->has_multiple_connectors()) {\n            BlankSecScreen();\n        }\n        screen_blanked_ = true;\n        LOGV(\"[%\" PRId64 \"] animation done\\n\", now);\n        if (configuration_->ChargerIsOnline()) {\n            RequestEnableSuspend();\n        }\n        return;\n    }\n\n    disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time;\n\n    /* turn off all screen */\n    if (screen_switch_ == SCREEN_SWITCH_ENABLE) {\n        healthd_draw_->blank_screen(true, 0 /* drm */);\n        healthd_draw_->blank_screen(true, 1 /* drm */);\n        healthd_draw_->rotate_screen(static_cast<int>(drm_));\n        screen_blanked_ = true;\n        screen_switch_ = SCREEN_SWITCH_DISABLE;\n    }\n\n    if (screen_blanked_) {\n        healthd_draw_->blank_screen(false, static_cast<int>(drm_));\n        screen_blanked_ = false;\n    }\n\n    /* animation starting, set up the animation */\n    if (batt_anim_.cur_frame == 0) {\n        LOGV(\"[%\" PRId64 \"] animation starting\\n\", now);\n        batt_anim_.cur_level = health_info_.battery_level;\n        batt_anim_.cur_status = (int)health_info_.battery_status;\n        if (health_info_.battery_level >= 0 && batt_anim_.num_frames != 0) {\n            /* find first frame given current battery level */\n            for (int i = 0; i < batt_anim_.num_frames; i++) {\n                if (batt_anim_.cur_level >= batt_anim_.frames[i].min_level &&\n                    batt_anim_.cur_level <= batt_anim_.frames[i].max_level) {\n                    batt_anim_.cur_frame = i;\n                    break;\n                }\n            }\n\n            if (configuration_->ChargerIsOnline()) {\n                // repeat the first frame first_frame_repeats times\n                disp_time = batt_anim_.frames[batt_anim_.cur_frame].disp_time *\n                            batt_anim_.first_frame_repeats;\n            } else {\n                disp_time = UNPLUGGED_DISPLAY_TIME / batt_anim_.num_cycles;\n            }\n\n            LOGV(\"cur_frame=%d disp_time=%d\\n\", batt_anim_.cur_frame, disp_time);\n        }\n    }\n\n    /* draw the new frame (@ cur_frame) */\n    healthd_draw_->redraw_screen(&batt_anim_, surf_unknown_);\n\n    /* if we don't have anim frames, we only have one image, so just bump\n     * the cycle counter and exit\n     */\n    if (batt_anim_.num_frames == 0 || batt_anim_.cur_level < 0) {\n        LOGW(\"[%\" PRId64 \"] animation missing or unknown battery status\\n\", now);\n        next_screen_transition_ = now + BATTERY_UNKNOWN_TIME;\n        batt_anim_.cur_cycle++;\n        return;\n    }\n\n    /* schedule next screen transition */\n    next_screen_transition_ = curr_time_ms() + disp_time;\n\n    /* advance frame cntr to the next valid frame only if we are charging\n     * if necessary, advance cycle cntr, and reset frame cntr\n     */\n    if (configuration_->ChargerIsOnline()) {\n        batt_anim_.cur_frame++;\n\n        while (batt_anim_.cur_frame < batt_anim_.num_frames &&\n               (batt_anim_.cur_level < batt_anim_.frames[batt_anim_.cur_frame].min_level ||\n                batt_anim_.cur_level > batt_anim_.frames[batt_anim_.cur_frame].max_level)) {\n            batt_anim_.cur_frame++;\n        }\n        if (batt_anim_.cur_frame >= batt_anim_.num_frames) {\n            batt_anim_.cur_cycle++;\n            batt_anim_.cur_frame = 0;\n\n            /* don't reset the cycle counter, since we use that as a signal\n             * in a test above to check if animation is over\n             */\n        }\n    } else {\n        /* Stop animating if we're not charging.\n         * If we stop it immediately instead of going through this loop, then\n         * the animation would stop somewhere in the middle.\n         */\n        batt_anim_.cur_frame = 0;\n        batt_anim_.cur_cycle++;\n    }\n}\n\nint Charger::SetKeyCallback(int code, int value) {\n    int64_t now = curr_time_ms();\n    int down = !!value;\n\n    if (code > KEY_MAX) return -1;\n\n    /* ignore events that don't modify our state */\n    if (keys_[code].down == down) return 0;\n\n    /* only record the down even timestamp, as the amount\n     * of time the key spent not being pressed is not useful */\n    if (down) keys_[code].timestamp = now;\n    keys_[code].down = down;\n    keys_[code].pending = true;\n    if (down) {\n        LOGV(\"[%\" PRId64 \"] key[%d] down\\n\", now, code);\n    } else {\n        int64_t duration = now - keys_[code].timestamp;\n        int64_t secs = duration / 1000;\n        int64_t msecs = duration - secs * 1000;\n        LOGV(\"[%\" PRId64 \"] key[%d] up (was down for %\" PRId64 \".%\" PRId64 \"sec)\\n\", now, code,\n             secs, msecs);\n    }\n\n    return 0;\n}\n\nint Charger::SetSwCallback(int code, int value) {\n    if (code > SW_MAX) return -1;\n    if (code == SW_LID) {\n        if ((screen_switch_ == SCREEN_SWITCH_DEFAULT) || ((value != 0) && (drm_ == DRM_INNER)) ||\n            ((value == 0) && (drm_ == DRM_OUTER))) {\n            screen_switch_ = SCREEN_SWITCH_ENABLE;\n            drm_ = (value != 0) ? DRM_OUTER : DRM_INNER;\n            keys_[code].pending = true;\n        }\n    }\n\n    return 0;\n}\n\nvoid Charger::UpdateInputState(input_event* ev) {\n    if (ev->type == EV_SW && ev->code == SW_LID) {\n        SetSwCallback(ev->code, ev->value);\n        return;\n    }\n\n    if (ev->type != EV_KEY) return;\n    SetKeyCallback(ev->code, ev->value);\n}\n\nvoid Charger::SetNextKeyCheck(key_state* key, int64_t timeout) {\n    int64_t then = key->timestamp + timeout;\n\n    if (next_key_check_ == -1 || then < next_key_check_) next_key_check_ = then;\n}\n\nvoid Charger::ProcessKey(int code, int64_t now) {\n    key_state* key = &keys_[code];\n\n    if (code == KEY_POWER) {\n        if (key->down) {\n            int64_t reboot_timeout = key->timestamp + POWER_ON_KEY_TIME;\n            if (now >= reboot_timeout) {\n                /* We do not currently support booting from charger mode on\n                   all devices. Check the property and continue booting or reboot\n                   accordingly. */\n                if (property_get_bool(\"ro.enable_boot_charger_mode\", false)) {\n                    LOGW(\"[%\" PRId64 \"] booting from charger mode\\n\", now);\n                    property_set(\"sys.boot_from_charger_mode\", \"1\");\n                } else {\n                    if (batt_anim_.cur_level >= boot_min_cap_) {\n                        LOGW(\"[%\" PRId64 \"] rebooting\\n\", now);\n                        reboot(RB_AUTOBOOT);\n                    } else {\n                        LOGV(\"[%\" PRId64\n                             \"] ignore power-button press, battery level \"\n                             \"less than minimum\\n\",\n                             now);\n                    }\n                }\n            } else {\n                /* if the key is pressed but timeout hasn't expired,\n                 * make sure we wake up at the right-ish time to check\n                 */\n                SetNextKeyCheck(key, POWER_ON_KEY_TIME);\n\n                /* Turn on the display and kick animation on power-key press\n                 * rather than on key release\n                 */\n                kick_animation(&batt_anim_);\n                RequestDisableSuspend();\n            }\n        } else {\n            /* if the power key got released, force screen state cycle */\n            if (key->pending) {\n                kick_animation(&batt_anim_);\n                RequestDisableSuspend();\n            }\n        }\n    }\n\n    key->pending = false;\n}\n\nvoid Charger::ProcessHallSensor(int code) {\n    key_state* key = &keys_[code];\n\n    if (code == SW_LID) {\n        if (key->pending) {\n            reset_animation(&batt_anim_);\n            kick_animation(&batt_anim_);\n            RequestDisableSuspend();\n        }\n    }\n\n    key->pending = false;\n}\n\nvoid Charger::HandleInputState(int64_t now) {\n    ProcessKey(KEY_POWER, now);\n\n    if (next_key_check_ != -1 && now > next_key_check_) next_key_check_ = -1;\n\n    ProcessHallSensor(SW_LID);\n}\n\nvoid Charger::HandlePowerSupplyState(int64_t now) {\n    int timer_shutdown = UNPLUGGED_SHUTDOWN_TIME;\n    if (!have_battery_state_) return;\n\n    if (!configuration_->ChargerIsOnline()) {\n        RequestDisableSuspend();\n        if (next_pwr_check_ == -1) {\n            /* Last cycle would have stopped at the extreme top of battery-icon\n             * Need to show the correct level corresponding to capacity.\n             *\n             * Reset next_screen_transition_ to update screen immediately.\n             * Reset & kick animation to show complete animation cycles\n             * when charger disconnected.\n             */\n            timer_shutdown =\n                    property_get_int32(UNPLUGGED_SHUTDOWN_TIME_PROP, UNPLUGGED_SHUTDOWN_TIME);\n            next_screen_transition_ = now - 1;\n            reset_animation(&batt_anim_);\n            kick_animation(&batt_anim_);\n            next_pwr_check_ = now + timer_shutdown;\n            LOGW(\"[%\" PRId64 \"] device unplugged: shutting down in %\" PRId64 \" (@ %\" PRId64 \")\\n\",\n                 now, (int64_t)timer_shutdown, next_pwr_check_);\n        } else if (now >= next_pwr_check_) {\n            LOGW(\"[%\" PRId64 \"] shutting down\\n\", now);\n            reboot(RB_POWER_OFF);\n        } else {\n            /* otherwise we already have a shutdown timer scheduled */\n        }\n    } else {\n        /* online supply present, reset shutdown timer if set */\n        if (next_pwr_check_ != -1) {\n            /* Reset next_screen_transition_ to update screen immediately.\n             * Reset & kick animation to show complete animation cycles\n             * when charger connected again.\n             */\n            RequestDisableSuspend();\n            next_screen_transition_ = now - 1;\n            reset_animation(&batt_anim_);\n            kick_animation(&batt_anim_);\n            LOGW(\"[%\" PRId64 \"] device plugged in: shutdown cancelled\\n\", now);\n        }\n        next_pwr_check_ = -1;\n    }\n}\n\nvoid Charger::OnHeartbeat() {\n    // charger* charger = &charger_state;\n    int64_t now = curr_time_ms();\n\n    HandleInputState(now);\n    HandlePowerSupplyState(now);\n\n    /* do screen update last in case any of the above want to start\n     * screen transitions (animations, etc)\n     */\n    UpdateScreenState(now);\n}\n\nvoid Charger::OnHealthInfoChanged(const ChargerHealthInfo& health_info) {\n    if (!have_battery_state_) {\n        have_battery_state_ = true;\n        next_screen_transition_ = curr_time_ms() - 1;\n        RequestDisableSuspend();\n        reset_animation(&batt_anim_);\n        kick_animation(&batt_anim_);\n    }\n    health_info_ = health_info;\n\n    if (property_get_bool(\"ro.charger_mode_autoboot\", false)) {\n        if (health_info_.battery_level >= boot_min_cap_) {\n            if (property_get_bool(\"ro.enable_boot_charger_mode\", false)) {\n                LOGW(\"booting from charger mode\\n\");\n                property_set(\"sys.boot_from_charger_mode\", \"1\");\n            } else {\n                LOGW(\"Battery SOC = %d%%, Automatically rebooting\\n\", health_info_.battery_level);\n                reboot(RB_AUTOBOOT);\n            }\n        }\n    }\n}\n\nint Charger::OnPrepareToWait(void) {\n    int64_t now = curr_time_ms();\n    int64_t next_event = INT64_MAX;\n    int64_t timeout;\n\n    LOGV(\"[%\" PRId64 \"] next screen: %\" PRId64 \" next key: %\" PRId64 \" next pwr: %\" PRId64 \"\\n\",\n         now, next_screen_transition_, next_key_check_, next_pwr_check_);\n\n    if (next_screen_transition_ != -1) next_event = next_screen_transition_;\n    if (next_key_check_ != -1 && next_key_check_ < next_event) next_event = next_key_check_;\n    if (next_pwr_check_ != -1 && next_pwr_check_ < next_event) next_event = next_pwr_check_;\n\n    if (next_event != -1 && next_event != INT64_MAX)\n        timeout = max(0, next_event - now);\n    else\n        timeout = -1;\n\n    return (int)timeout;\n}\n\nint Charger::InputCallback(int fd, unsigned int epevents) {\n    input_event ev;\n    int ret;\n\n    ret = ev_get_input(fd, epevents, &ev);\n    if (ret) return -1;\n    UpdateInputState(&ev);\n    return 0;\n}\n\nstatic void charger_event_handler(HealthLoop* /*charger_loop*/, uint32_t /*epevents*/) {\n    int ret;\n\n    ret = ev_wait(-1);\n    if (!ret) ev_dispatch();\n}\n\nvoid Charger::InitAnimation() {\n    bool parse_success;\n\n    std::string content;\n\n#if defined(__ANDROID_VNDK__)\n    if (base::ReadFileToString(vendor_animation_desc_path, &content)) {\n        parse_success = parse_animation_desc(content, &batt_anim_);\n        batt_anim_.set_resource_root(vendor_animation_root);\n    } else {\n        LOGW(\"Could not open animation description at %s\\n\", vendor_animation_desc_path);\n        parse_success = false;\n    }\n#else\n    if (base::ReadFileToString(product_animation_desc_path, &content)) {\n        parse_success = parse_animation_desc(content, &batt_anim_);\n        batt_anim_.set_resource_root(product_animation_root);\n    } else if (base::ReadFileToString(animation_desc_path, &content)) {\n        parse_success = parse_animation_desc(content, &batt_anim_);\n        // Fallback resources always exist in system_animation_root. On legacy devices with an old\n        // ramdisk image, resources may be overridden under root. For example,\n        // /res/images/charger/battery_fail.png may not be the same as\n        // system/core/healthd/images/battery_fail.png in the source tree, but is a device-specific\n        // image. Hence, load from /res, and fall back to /system/etc/res.\n        batt_anim_.set_resource_root(legacy_animation_root, system_animation_root);\n    } else {\n        LOGW(\"Could not open animation description at %s\\n\", animation_desc_path);\n        parse_success = false;\n    }\n#endif\n\n#if defined(__ANDROID_VNDK__)\n    auto default_animation_root = vendor_default_animation_root;\n#else\n    auto default_animation_root = system_animation_root;\n#endif\n\n    if (!parse_success) {\n        LOGW(\"Could not parse animation description. \"\n             \"Using default animation with resources at %s\\n\",\n             default_animation_root);\n        batt_anim_ = BASE_ANIMATION;\n        batt_anim_.animation_file.assign(default_animation_root + \"charger/battery_scale.png\"s);\n        InitDefaultAnimationFrames();\n        batt_anim_.frames = owned_frames_.data();\n        batt_anim_.num_frames = owned_frames_.size();\n    }\n    if (batt_anim_.fail_file.empty()) {\n        batt_anim_.fail_file.assign(default_animation_root + \"charger/battery_fail.png\"s);\n    }\n\n    LOGV(\"Animation Description:\\n\");\n    LOGV(\"  animation: %d %d '%s' (%d)\\n\", batt_anim_.num_cycles, batt_anim_.first_frame_repeats,\n         batt_anim_.animation_file.c_str(), batt_anim_.num_frames);\n    LOGV(\"  fail_file: '%s'\\n\", batt_anim_.fail_file.c_str());\n    LOGV(\"  clock: %d %d %d %d %d %d '%s'\\n\", batt_anim_.text_clock.pos_x,\n         batt_anim_.text_clock.pos_y, batt_anim_.text_clock.color_r, batt_anim_.text_clock.color_g,\n         batt_anim_.text_clock.color_b, batt_anim_.text_clock.color_a,\n         batt_anim_.text_clock.font_file.c_str());\n    LOGV(\"  percent: %d %d %d %d %d %d '%s'\\n\", batt_anim_.text_percent.pos_x,\n         batt_anim_.text_percent.pos_y, batt_anim_.text_percent.color_r,\n         batt_anim_.text_percent.color_g, batt_anim_.text_percent.color_b,\n         batt_anim_.text_percent.color_a, batt_anim_.text_percent.font_file.c_str());\n    for (int i = 0; i < batt_anim_.num_frames; i++) {\n        LOGV(\"  frame %.2d: %d %d %d\\n\", i, batt_anim_.frames[i].disp_time,\n             batt_anim_.frames[i].min_level, batt_anim_.frames[i].max_level);\n    }\n}\n\nvoid Charger::InitHealthdDraw() {\n    if (healthd_draw_ == nullptr) {\n        std::optional<bool> out_screen_on = configuration_->ChargerShouldKeepScreenOn();\n        if (out_screen_on.has_value()) {\n            if (!*out_screen_on) {\n                LOGV(\"[%\" PRId64 \"] leave screen off\\n\", curr_time_ms());\n                batt_anim_.run = false;\n                next_screen_transition_ = -1;\n                if (configuration_->ChargerIsOnline()) {\n                    RequestEnableSuspend();\n                }\n                return;\n            }\n        }\n\n        healthd_draw_ = HealthdDraw::Create(&batt_anim_);\n        if (healthd_draw_ == nullptr) return;\n\n#if !defined(__ANDROID_VNDK__)\n        if (android::sysprop::ChargerProperties::disable_init_blank().value_or(false)) {\n            healthd_draw_->blank_screen(true, static_cast<int>(drm_));\n            screen_blanked_ = true;\n        }\n#endif\n    }\n}\n\nvoid Charger::OnInit(struct healthd_config* config) {\n    int ret;\n    int i;\n    int epollfd;\n\n    dump_last_kmsg();\n\n    LOGW(\"--------------- STARTING CHARGER MODE ---------------\\n\");\n\n    ret = ev_init(\n            std::bind(&Charger::InputCallback, this, std::placeholders::_1, std::placeholders::_2));\n    if (!ret) {\n        epollfd = ev_get_epollfd();\n        configuration_->ChargerRegisterEvent(epollfd, &charger_event_handler, EVENT_WAKEUP_FD);\n    }\n\n    InitAnimation();\n    InitHealthdDraw();\n\n    ret = CreateDisplaySurface(batt_anim_.fail_file, &surf_unknown_);\n    if (ret < 0) {\n#if !defined(__ANDROID_VNDK__)\n        LOGE(\"Cannot load custom battery_fail image. Reverting to built in: %d\\n\", ret);\n        ret = CreateDisplaySurface((system_animation_root + \"charger/battery_fail.png\"s).c_str(),\n                                   &surf_unknown_);\n#endif\n        if (ret < 0) {\n            LOGE(\"Cannot load built in battery_fail image\\n\");\n            surf_unknown_ = NULL;\n        }\n    }\n\n    GRSurface** scale_frames;\n    int scale_count;\n    int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text\n                    // chunk). We are using hard-coded frame.disp_time instead.\n    ret = CreateMultiDisplaySurface(batt_anim_.animation_file, &scale_count, &scale_fps,\n                                    &scale_frames);\n    if (ret < 0) {\n        LOGE(\"Cannot load battery_scale image\\n\");\n        batt_anim_.num_frames = 0;\n        batt_anim_.num_cycles = 1;\n    } else if (scale_count != batt_anim_.num_frames) {\n        LOGE(\"battery_scale image has unexpected frame count (%d, expected %d)\\n\", scale_count,\n             batt_anim_.num_frames);\n        batt_anim_.num_frames = 0;\n        batt_anim_.num_cycles = 1;\n    } else {\n        for (i = 0; i < batt_anim_.num_frames; i++) {\n            batt_anim_.frames[i].surface = scale_frames[i];\n        }\n    }\n    drm_ = DRM_INNER;\n    screen_switch_ = SCREEN_SWITCH_DEFAULT;\n    ev_sync_key_state(std::bind(&Charger::SetKeyCallback, this, std::placeholders::_1,\n                                std::placeholders::_2));\n\n    (void)ev_sync_sw_state(\n            std::bind(&Charger::SetSwCallback, this, std::placeholders::_1, std::placeholders::_2));\n\n    next_screen_transition_ = -1;\n    next_key_check_ = -1;\n    next_pwr_check_ = -1;\n    wait_batt_level_timestamp_ = 0;\n\n    // Retrieve healthd_config from the existing health HAL.\n    configuration_->ChargerInitConfig(config);\n\n    boot_min_cap_ = config->boot_min_cap;\n}\n\nint Charger::CreateDisplaySurface(const std::string& name, GRSurface** surface) {\n    return res_create_display_surface(name.c_str(), surface);\n}\n\nint Charger::CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,\n                                       GRSurface*** surface) {\n    return res_create_multi_display_surface(name.c_str(), frames, fps, surface);\n}\n\nvoid set_resource_root_for(const std::string& root, const std::string& backup_root,\n                           std::string* value) {\n    if (value->empty()) {\n        return;\n    }\n\n    std::string new_value = root + *value + \".png\";\n    // If |backup_root| is provided, additionally check whether the file under |root| is\n    // accessible or not. If not accessible, fallback to file under |backup_root|.\n    if (!backup_root.empty() && access(new_value.data(), F_OK) == -1) {\n        new_value = backup_root + *value + \".png\";\n    }\n\n    *value = new_value;\n}\n\nvoid animation::set_resource_root(const std::string& root, const std::string& backup_root) {\n    CHECK(android::base::StartsWith(root, \"/\") && android::base::EndsWith(root, \"/\"))\n            << \"animation root \" << root << \" must start and end with /\";\n    CHECK(backup_root.empty() || (android::base::StartsWith(backup_root, \"/\") &&\n                                  android::base::EndsWith(backup_root, \"/\")))\n            << \"animation backup root \" << backup_root << \" must start and end with /\";\n    set_resource_root_for(root, backup_root, &animation_file);\n    set_resource_root_for(root, backup_root, &fail_file);\n    set_resource_root_for(root, backup_root, &text_clock.font_file);\n    set_resource_root_for(root, backup_root, &text_percent.font_file);\n}\n\n}  // namespace android\n"
  },
  {
    "path": "healthd/healthd_mode_charger_hidl.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"healthd_mode_charger_hidl.h\"\n\n#include <android/hardware/health/2.0/types.h>\n#include <charger.sysprop.h>\n#include <cutils/klog.h>\n\n#include \"charger_utils.h\"\n\nusing android::hardware::health::GetHealthServiceOrDefault;\nusing android::hardware::health::V2_0::Result;\n\nnamespace android {\n\nChargerHidl::ChargerHidl(const sp<android::hardware::health::V2_1::IHealth>& service)\n    : HalHealthLoop(\"charger\", service), charger_(std::make_unique<Charger>(this)) {}\n\nvoid ChargerHidl::OnHealthInfoChanged(const HealthInfo_2_1& health_info) {\n    set_charger_online(health_info);\n\n    charger_->OnHealthInfoChanged(ChargerHealthInfo{\n            .battery_level = health_info.legacy.legacy.batteryLevel,\n            .battery_status = static_cast<::aidl::android::hardware::health::BatteryStatus>(\n                    health_info.legacy.legacy.batteryStatus),\n    });\n\n    AdjustWakealarmPeriods(charger_online());\n}\n\nstd::optional<bool> ChargerHidl::ChargerShouldKeepScreenOn() {\n    std::optional<bool> out_screen_on;\n    service()->shouldKeepScreenOn([&](Result res, bool screen_on) {\n        if (res == Result::SUCCESS) {\n            *out_screen_on = screen_on;\n        }\n    });\n    return out_screen_on;\n}\n\nbool ChargerHidl::ChargerEnableSuspend() {\n    return android::sysprop::ChargerProperties::enable_suspend().value_or(false);\n}\n\n}  // namespace android\n\nint healthd_charger_main(int argc, char** argv) {\n    int ch;\n\n    while ((ch = getopt(argc, argv, \"cr\")) != -1) {\n        switch (ch) {\n            case 'c':\n                // -c is now a noop\n                break;\n            case 'r':\n                // -r is now a noop\n                break;\n            case '?':\n            default:\n                KLOG_ERROR(\"charger\", \"Unrecognized charger option: %c\\n\", optopt);\n                exit(1);\n        }\n    }\n\n    android::ChargerHidl charger(GetHealthServiceOrDefault());\n    return charger.StartLoop();\n}\n"
  },
  {
    "path": "healthd/healthd_mode_charger_hidl.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <health2impl/HalHealthLoop.h>\n\n#include <charger/healthd_mode_charger.h>\n\nnamespace android {\n\n// An implementation of Charger backed by HIDL implementation. Uses HIDL health\n// HAL's HalHealthLoop.\nclass ChargerHidl : public ::android::ChargerConfigurationInterface,\n                    public ::android::hardware::health::V2_1::implementation::HalHealthLoop {\n    using HalHealthLoop = ::android::hardware::health::V2_1::implementation::HalHealthLoop;\n    using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;\n\n  public:\n    explicit ChargerHidl(const sp<android::hardware::health::V2_1::IHealth>& service);\n    std::optional<bool> ChargerShouldKeepScreenOn() override;\n    bool ChargerIsOnline() override { return HalHealthLoop::charger_online(); }\n    void ChargerInitConfig(healthd_config* config) override { return HalHealthLoop::Init(config); }\n    int ChargerRegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) override {\n        return HalHealthLoop::RegisterEvent(fd, func, wakeup);\n    }\n    bool ChargerEnableSuspend() override;\n    // HealthLoop overrides\n    void Heartbeat() override { charger_->OnHeartbeat(); }\n    int PrepareToWait() override { return charger_->OnPrepareToWait(); }\n    void Init(struct healthd_config* config) override { charger_->OnInit(config); }\n    // HalHealthLoop overrides\n    void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;\n\n  private:\n    sp<android::hardware::health::V2_1::IHealth> service_;\n    std::unique_ptr<Charger> charger_;\n};\n\n}  // namespace android\n\nint healthd_charger_main(int argc, char** argv);\n"
  },
  {
    "path": "healthd/healthd_mode_charger_nops.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"healthd_mode_charger_nops.h\"\n\n#include <health2impl/HalHealthLoop.h>\n\n#include \"charger_utils.h\"\n\nusing android::hardware::health::GetHealthServiceOrDefault;\nusing android::hardware::health::V2_1::implementation::HalHealthLoop;\n\nint healthd_charger_nops(int /* argc */, char** /* argv */) {\n    HalHealthLoop charger(\"charger\", GetHealthServiceOrDefault());\n    return charger.StartLoop();\n}\n"
  },
  {
    "path": "healthd/healthd_mode_charger_nops.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nint healthd_charger_nops(int argc, char** argv);\n"
  },
  {
    "path": "healthd/healthd_mode_charger_test.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sysexits.h>\n#include <unistd.h>\n\n#include <iostream>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <android/hardware/health/2.1/IHealth.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <health/utils.h>\n\n#include \"healthd_mode_charger_hidl.h\"\n\nusing android::hardware::Return;\nusing android::hardware::health::InitHealthdConfig;\nusing std::string_literals::operator\"\"s;\nusing testing::_;\nusing testing::Invoke;\nusing testing::NiceMock;\nusing testing::StrEq;\nusing testing::Test;\n\nnamespace android {\n\n// A replacement to ASSERT_* to be used in a forked process. When the condition is not met,\n// print a gtest message, then exit abnormally.\nclass ChildAssertHelper : public std::stringstream {\n  public:\n    ChildAssertHelper(bool res, const char* expr, const char* file, int line) : res_(res) {\n        (*this) << file << \":\" << line << \": `\" << expr << \"` evaluates to false\\n\";\n    }\n    ~ChildAssertHelper() {\n        EXPECT_TRUE(res_) << str();\n        if (!res_) exit(EX_SOFTWARE);\n    }\n\n  private:\n    bool res_;\n    DISALLOW_COPY_AND_ASSIGN(ChildAssertHelper);\n};\n#define CHILD_ASSERT_TRUE(expr) ChildAssertHelper(expr, #expr, __FILE__, __LINE__)\n\n// Run |test_body| in a chroot jail in a forked process. |subdir| is a sub-directory in testdata.\n// Within |test_body|,\n// - non-fatal errors may be reported using EXPECT_* macro as usual.\n// - fatal errors must be reported using CHILD_ASSERT_TRUE macro. ASSERT_* must not be used.\nvoid ForkTest(const std::string& subdir, const std::function<void(void)>& test_body) {\n    pid_t pid = fork();\n    ASSERT_GE(pid, 0) << \"Fork fails: \" << strerror(errno);\n    if (pid == 0) {\n        // child\n        CHILD_ASSERT_TRUE(\n                chroot((android::base::GetExecutableDirectory() + \"/\" + subdir).c_str()) != -1)\n                << \"Failed to chroot to \" << subdir << \": \" << strerror(errno);\n        test_body();\n        // EXPECT_* macros may set the HasFailure bit without calling exit(). Set exit status\n        // accordingly.\n        exit(::testing::Test::HasFailure() ? EX_SOFTWARE : EX_OK);\n    }\n    // parent\n    int status;\n    ASSERT_NE(-1, waitpid(pid, &status, 0)) << \"waitpid() fails: \" << strerror(errno);\n    ASSERT_TRUE(WIFEXITED(status)) << \"Test fails, waitpid() returns \" << status;\n    ASSERT_EQ(EX_OK, WEXITSTATUS(status)) << \"Test fails, child process returns \" << status;\n}\n\nclass MockHealth : public android::hardware::health::V2_1::IHealth {\n    MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, registerCallback,\n                (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));\n    MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, unregisterCallback,\n                (const sp<::android::hardware::health::V2_0::IHealthInfoCallback>& callback));\n    MOCK_METHOD(Return<::android::hardware::health::V2_0::Result>, update, ());\n    MOCK_METHOD(Return<void>, getChargeCounter, (getChargeCounter_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getCurrentNow, (getCurrentNow_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getCurrentAverage, (getCurrentAverage_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getCapacity, (getCapacity_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getEnergyCounter, (getEnergyCounter_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getChargeStatus, (getChargeStatus_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getStorageInfo, (getStorageInfo_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getDiskStats, (getDiskStats_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getHealthInfo, (getHealthInfo_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getHealthConfig, (getHealthConfig_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, getHealthInfo_2_1, (getHealthInfo_2_1_cb _hidl_cb));\n    MOCK_METHOD(Return<void>, shouldKeepScreenOn, (shouldKeepScreenOn_cb _hidl_cb));\n};\n\nclass TestCharger : public ChargerHidl {\n  public:\n    // Inherit constructor.\n    using ChargerHidl::ChargerHidl;\n    // Expose protected functions to be used in tests.\n    void Init(struct healthd_config* config) override { ChargerHidl::Init(config); }\n    MOCK_METHOD(int, CreateDisplaySurface, (const std::string& name, GRSurface** surface));\n    MOCK_METHOD(int, CreateMultiDisplaySurface,\n                (const std::string& name, int* frames, int* fps, GRSurface*** surface));\n};\n\n// Intentionally leak TestCharger instance to avoid calling ~HealthLoop() because ~HealthLoop()\n// should never be called. But still verify expected calls upon destruction.\nclass VerifiedTestCharger {\n  public:\n    VerifiedTestCharger(TestCharger* charger) : charger_(charger) {\n        testing::Mock::AllowLeak(charger_);\n    }\n    TestCharger& operator*() { return *charger_; }\n    TestCharger* operator->() { return charger_; }\n    ~VerifiedTestCharger() { testing::Mock::VerifyAndClearExpectations(charger_); }\n\n  private:\n    TestCharger* charger_;\n};\n\n// Do not use SetUp and TearDown of a test suite, as they will be invoked in the parent process, not\n// the child process. In particular, if the test suite contains mocks, they will not be verified in\n// the child process. Instead, create mocks within closures in each tests.\nvoid ExpectChargerResAt(const std::string& root) {\n    sp<NiceMock<MockHealth>> health(new NiceMock<MockHealth>());\n    VerifiedTestCharger charger(new NiceMock<TestCharger>(health));\n\n    // Only one frame in all testdata/**/animation.txt\n    GRSurface* multi[] = {nullptr};\n\n    EXPECT_CALL(*charger, CreateDisplaySurface(StrEq(root + \"charger/battery_fail.png\"), _))\n            .WillRepeatedly(Invoke([](const auto&, GRSurface** surface) {\n                *surface = nullptr;\n                return 0;\n            }));\n    EXPECT_CALL(*charger,\n                CreateMultiDisplaySurface(StrEq(root + \"charger/battery_scale.png\"), _, _, _))\n            .WillRepeatedly(Invoke([&](const auto&, int* frames, int* fps, GRSurface*** surface) {\n                *frames = arraysize(multi);\n                *fps = 60;  // Unused fps value\n                *surface = multi;\n                return 0;\n            }));\n    struct healthd_config healthd_config;\n    InitHealthdConfig(&healthd_config);\n    charger->Init(&healthd_config);\n};\n\n// Test that if resources does not exist in /res or in /product/etc/res, load from /system.\nTEST(ChargerLoadAnimationRes, Empty) {\n    ForkTest(\"empty\", std::bind(&ExpectChargerResAt, \"/system/etc/res/images/\"));\n}\n\n// Test loading everything from /res\nTEST(ChargerLoadAnimationRes, Legacy) {\n    ForkTest(\"legacy\", std::bind(&ExpectChargerResAt, \"/res/images/\"));\n}\n\n// Test loading animation text from /res but images from /system if images does not exist under\n// /res.\nTEST(ChargerLoadAnimationRes, LegacyTextSystemImages) {\n    ForkTest(\"legacy_text_system_images\",\n             std::bind(&ExpectChargerResAt, \"/system/etc/res/images/\"));\n}\n\n// Test loading everything from /product\nTEST(ChargerLoadAnimationRes, Product) {\n    ForkTest(\"product\", std::bind(&ExpectChargerResAt, \"/product/etc/res/images/\"));\n}\n\n}  // namespace android\n"
  },
  {
    "path": "healthd/include/healthd/BatteryMonitor.h",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef HEALTHD_BATTERYMONITOR_H\n#define HEALTHD_BATTERYMONITOR_H\n\n#include <memory>\n#include <optional>\n\n#include <batteryservice/BatteryService.h>\n#include <utils/String8.h>\n#include <utils/Vector.h>\n\n#include <healthd/healthd.h>\n\nnamespace aidl::android::hardware::health {\nclass HealthInfo;\n}  // namespace aidl::android::hardware::health\n\nnamespace android {\nnamespace hardware {\nnamespace health {\nnamespace V1_0 {\nstruct HealthInfo;\n}  // namespace V1_0\nnamespace V2_0 {\nstruct HealthInfo;\n}  // namespace V2_0\nnamespace V2_1 {\nstruct HealthInfo;\n}  // namespace V2_1\n}  // namespace health\n}  // namespace hardware\n\nclass BatteryMonitor {\n  public:\n\n    enum PowerSupplyType {\n        ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,\n        ANDROID_POWER_SUPPLY_TYPE_AC,\n        ANDROID_POWER_SUPPLY_TYPE_USB,\n        ANDROID_POWER_SUPPLY_TYPE_WIRELESS,\n        ANDROID_POWER_SUPPLY_TYPE_BATTERY,\n        ANDROID_POWER_SUPPLY_TYPE_DOCK\n    };\n\n    enum BatteryHealthStatus {\n        BH_UNKNOWN = -1,\n        BH_NOMINAL,\n        BH_MARGINAL,\n        BH_NEEDS_REPLACEMENT,\n        BH_FAILED,\n        BH_NOT_AVAILABLE,\n        BH_INCONSISTENT,\n    };\n\n    BatteryMonitor();\n    ~BatteryMonitor();\n    void init(struct healthd_config *hc);\n    int getChargeStatus();\n    status_t getProperty(int id, struct BatteryProperty *val);\n    void dumpState(int fd);\n\n    android::hardware::health::V1_0::HealthInfo getHealthInfo_1_0() const;\n    android::hardware::health::V2_0::HealthInfo getHealthInfo_2_0() const;\n    android::hardware::health::V2_1::HealthInfo getHealthInfo_2_1() const;\n    const aidl::android::hardware::health::HealthInfo& getHealthInfo() const;\n\n    void updateValues(void);\n    void logValues(void);\n    bool isChargerOnline();\n\n    int setChargingPolicy(int value);\n    int getChargingPolicy();\n    int getBatteryHealthData(int id);\n\n    status_t getSerialNumber(std::optional<std::string>* out);\n\n    static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,\n                          const struct healthd_config& healthd_config);\n\n  private:\n    struct healthd_config *mHealthdConfig;\n    Vector<String8> mChargerNames;\n    bool mBatteryDevicePresent;\n    int mBatteryFixedCapacity;\n    int mBatteryFixedTemperature;\n    int mBatteryHealthStatus;\n    std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;\n};\n\n}; // namespace android\n\n#endif // HEALTHD_BATTERY_MONTIOR_H\n"
  },
  {
    "path": "healthd/include/healthd/BatteryMonitor_v1.h",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef HEALTHD_BATTERYMONITOR_V1_H\n#define HEALTHD_BATTERYMONITOR_V1_H\n\n#include <memory>\n\n#include <batteryservice/BatteryService.h>\n#include <utils/String8.h>\n#include <utils/Vector.h>\n\n#include <healthd/healthd.h>\n\nnamespace aidl::android::hardware::health {\nclass HealthInfo;\n}  // namespace aidl::android::hardware::health\n\nnamespace android {\nnamespace hardware {\nnamespace health {\nnamespace V1_0 {\nstruct HealthInfo;\n}  // namespace V1_0\nnamespace V2_0 {\nstruct HealthInfo;\n}  // namespace V2_0\nnamespace V2_1 {\nstruct HealthInfo;\n}  // namespace V2_1\n}  // namespace health\n}  // namespace hardware\n\nclass BatteryMonitor {\n  public:\n\n    enum PowerSupplyType {\n        ANDROID_POWER_SUPPLY_TYPE_UNKNOWN = 0,\n        ANDROID_POWER_SUPPLY_TYPE_AC,\n        ANDROID_POWER_SUPPLY_TYPE_USB,\n        ANDROID_POWER_SUPPLY_TYPE_WIRELESS,\n        ANDROID_POWER_SUPPLY_TYPE_BATTERY,\n        ANDROID_POWER_SUPPLY_TYPE_DOCK\n    };\n\n    BatteryMonitor();\n    ~BatteryMonitor();\n    void init(struct healthd_config *hc);\n    int getChargeStatus();\n    status_t getProperty(int id, struct BatteryProperty *val);\n    void dumpState(int fd);\n\n    android::hardware::health::V1_0::HealthInfo getHealthInfo_1_0() const;\n    android::hardware::health::V2_0::HealthInfo getHealthInfo_2_0() const;\n    android::hardware::health::V2_1::HealthInfo getHealthInfo_2_1() const;\n    const aidl::android::hardware::health::HealthInfo& getHealthInfo() const;\n\n    void updateValues(void);\n    void logValues(void);\n    bool isChargerOnline();\n\n    static void logValues(const android::hardware::health::V2_1::HealthInfo& health_info,\n                          const struct healthd_config& healthd_config);\n\n  private:\n    struct healthd_config *mHealthdConfig;\n    Vector<String8> mChargerNames;\n    bool mBatteryDevicePresent;\n    int mBatteryFixedCapacity;\n    int mBatteryFixedTemperature;\n    std::unique_ptr<aidl::android::hardware::health::HealthInfo> mHealthInfo;\n};\n\n}; // namespace android\n\n#endif // HEALTHD_BATTERYMONITOR_V1_H\n"
  },
  {
    "path": "healthd/include/healthd/healthd.h",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _HEALTHD_H_\n#define _HEALTHD_H_\n\n#include <batteryservice/BatteryService.h>\n#include <sys/types.h>\n#include <utils/Errors.h>\n#include <utils/String8.h>\n\n#include <vector>\n\n// periodic_chores_interval_fast, periodic_chores_interval_slow: intervals at\n// which healthd wakes up to poll health state and perform periodic chores,\n// in units of seconds:\n//\n//    periodic_chores_interval_fast is used while the device is not in\n//    suspend, or in suspend and connected to a charger (to watch for battery\n//    overheat due to charging).  The default value is 60 (1 minute).  Value\n//    -1 turns off periodic chores (and wakeups) in these conditions.\n//\n//    periodic_chores_interval_slow is used when the device is in suspend and\n//    not connected to a charger (to watch for a battery drained to zero\n//    remaining capacity).  The default value is 600 (10 minutes).  Value -1\n//    tuns off periodic chores (and wakeups) in these conditions.\n//\n// power_supply sysfs attribute file paths.  Set these to specific paths\n// to use for the associated battery parameters.  healthd will search for\n// appropriate power_supply attribute files to use for any paths left empty:\n//\n//    batteryStatusPath: charging status (POWER_SUPPLY_PROP_STATUS)\n//    batteryHealthPath: battery health (POWER_SUPPLY_PROP_HEALTH)\n//    batteryPresentPath: battery present (POWER_SUPPLY_PROP_PRESENT)\n//    batteryCapacityPath: remaining capacity (POWER_SUPPLY_PROP_CAPACITY)\n//    batteryVoltagePath: battery voltage (POWER_SUPPLY_PROP_VOLTAGE_NOW)\n//    batteryTemperaturePath: battery temperature (POWER_SUPPLY_PROP_TEMP)\n//    batteryTechnologyPath: battery technology (POWER_SUPPLY_PROP_TECHNOLOGY)\n//    batteryCurrentNowPath: battery current (POWER_SUPPLY_PROP_CURRENT_NOW)\n//    batteryChargeCounterPath: battery accumulated charge\n//                                         (POWER_SUPPLY_PROP_CHARGE_COUNTER)\n\nstruct healthd_config {\n    int periodic_chores_interval_fast;\n    int periodic_chores_interval_slow;\n\n    android::String8 batteryStatusPath;\n    android::String8 batteryHealthPath;\n    android::String8 batteryPresentPath;\n    android::String8 batteryCapacityPath;\n    android::String8 batteryVoltagePath;\n    android::String8 batteryTemperaturePath;\n    android::String8 batteryTechnologyPath;\n    android::String8 batteryCurrentNowPath;\n    android::String8 batteryCurrentAvgPath;\n    android::String8 batteryChargeCounterPath;\n    android::String8 batteryFullChargePath;\n    android::String8 batteryCycleCountPath;\n    android::String8 batteryCapacityLevelPath;\n    android::String8 batteryChargeTimeToFullNowPath;\n    android::String8 batteryFullChargeDesignCapacityUahPath;\n    android::String8 batteryStateOfHealthPath;\n    android::String8 batteryHealthStatusPath;\n    android::String8 batteryManufacturingDatePath;\n    android::String8 batteryFirstUsageDatePath;\n    android::String8 chargingStatePath;\n    android::String8 chargingPolicyPath;\n\n    int (*energyCounter)(int64_t *);\n    int boot_min_cap;\n    bool (*screen_on)(android::BatteryProperties *props);\n    std::vector<android::String8> ignorePowerSupplyNames;\n};\n\nenum EventWakeup {\n    EVENT_NO_WAKEUP_FD,\n    EVENT_WAKEUP_FD,\n};\n\n// Global helper functions\n\nint healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);\n\nstruct healthd_mode_ops {\n    void (*init)(struct healthd_config *config);\n    int (*preparetowait)(void);\n    void (*heartbeat)(void);\n    void (*battery_update)(struct android::BatteryProperties *props);\n};\n\nextern struct healthd_mode_ops *healthd_mode_ops;\n\n// Charger mode\n\nvoid healthd_mode_charger_init(struct healthd_config *config);\nint healthd_mode_charger_preparetowait(void);\nvoid healthd_mode_charger_heartbeat(void);\nvoid healthd_mode_charger_battery_update(\n    struct android::BatteryProperties *props);\n\n// The following are implemented in libhealthd_board to handle board-specific\n// behavior.\n//\n// healthd_board_init() is called at startup time to modify healthd's\n// configuration according to board-specific requirements.  config\n// points to the healthd configuration values described above.  To use default\n// values, this function can simply return without modifying the fields of the\n// config parameter.\n\nvoid healthd_board_init(struct healthd_config *config);\n\n// Process updated battery property values.  This function is called when\n// the kernel sends updated battery status via a uevent from the power_supply\n// subsystem, or when updated values are polled by healthd, as for periodic\n// poll of battery state.\n//\n// props are the battery properties read from the kernel.  These values may\n// be modified in this call, prior to sending the modified values to the\n// Android runtime.\n//\n// Return 0 to indicate the usual kernel log battery status heartbeat message\n// is to be logged, or non-zero to prevent logging this information.\n\nint healthd_board_battery_update(struct android::BatteryProperties *props);\n\n#endif /* _HEALTHD_H_ */\n"
  },
  {
    "path": "healthd/include_charger/charger/healthd_mode_charger.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <linux/input.h>\n\n#include <memory>\n#include <optional>\n#include <vector>\n\n#include <aidl/android/hardware/health/BatteryStatus.h>\n#include <health/HealthLoop.h>\n#include <healthd/healthd.h>\n\n#include \"animation.h\"\n\nclass GRSurface;\nclass HealthdDraw;\n\nnamespace android {\nstruct key_state {\n    bool pending;\n    bool down;\n    int64_t timestamp;\n};\n\n// Health info that interests charger\nstruct ChargerHealthInfo {\n    int32_t battery_level;\n    aidl::android::hardware::health::BatteryStatus battery_status;\n};\n\nenum DirectRenderManager {\n    DRM_INNER,\n    DRM_OUTER,\n};\n\nenum SrceenSwitch {\n    SCREEN_SWITCH_DEFAULT,\n    SCREEN_SWITCH_DISABLE,\n    SCREEN_SWITCH_ENABLE,\n};\n\n// Configuration interface for charger. This includes:\n// - HalHealthLoop APIs that interests charger.\n// - configuration values that used to be provided by sysprops\nclass ChargerConfigurationInterface {\n  public:\n    virtual ~ChargerConfigurationInterface() = default;\n    // HalHealthLoop related APIs\n    virtual std::optional<bool> ChargerShouldKeepScreenOn() = 0;\n    virtual bool ChargerIsOnline() = 0;\n    virtual void ChargerInitConfig(healthd_config* config) = 0;\n    using BoundFunction =\n            std::function<void(android::hardware::health::HealthLoop*, uint32_t /* epevents */)>;\n    virtual int ChargerRegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) = 0;\n\n    // Other configuration values\n    virtual bool ChargerEnableSuspend() = 0;\n};\n\n// charger UI\nclass Charger {\n  public:\n    explicit Charger(ChargerConfigurationInterface* configuration);\n    virtual ~Charger();\n\n    // Hooks for ChargerConfigurationInterface\n    void OnHeartbeat();\n    int OnPrepareToWait();\n    // |cookie| is passed to ChargerConfigurationInterface::ChargerInitConfig\n    void OnInit(struct healthd_config* config);\n    void OnHealthInfoChanged(const ChargerHealthInfo& health_info);\n\n  protected:\n    // Allowed to be mocked for testing.\n    virtual int CreateDisplaySurface(const std::string& name, GRSurface** surface);\n    virtual int CreateMultiDisplaySurface(const std::string& name, int* frames, int* fps,\n                                          GRSurface*** surface);\n\n  private:\n    void InitDefaultAnimationFrames();\n    void UpdateScreenState(int64_t now);\n    int SetKeyCallback(int code, int value);\n    int SetSwCallback(int code, int value);\n    void UpdateInputState(input_event* ev);\n    void SetNextKeyCheck(key_state* key, int64_t timeout);\n    void ProcessKey(int code, int64_t now);\n    void ProcessHallSensor(int code);\n    void HandleInputState(int64_t now);\n    void HandlePowerSupplyState(int64_t now);\n    int InputCallback(int fd, unsigned int epevents);\n    void InitHealthdDraw();\n    void InitAnimation();\n    int RequestEnableSuspend();\n    int RequestDisableSuspend();\n    void BlankSecScreen();\n\n    bool have_battery_state_ = false;\n    bool screen_blanked_ = false;\n    bool init_screen_ = false;\n    int64_t next_screen_transition_ = 0;\n    int64_t next_key_check_ = 0;\n    int64_t next_pwr_check_ = 0;\n    int64_t wait_batt_level_timestamp_ = 0;\n\n    DirectRenderManager drm_;\n    SrceenSwitch screen_switch_;\n\n    key_state keys_[KEY_MAX + 1] = {};\n\n    animation batt_anim_;\n    GRSurface* surf_unknown_ = nullptr;\n    int boot_min_cap_ = 0;\n\n    ChargerHealthInfo health_info_ = {};\n    std::unique_ptr<HealthdDraw> healthd_draw_;\n    std::vector<animation::frame> owned_frames_;\n\n    ChargerConfigurationInterface* configuration_;\n};\n}  // namespace android\n"
  },
  {
    "path": "healthd/testdata/Android.bp",
    "content": "//\n// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nfilegroup {\n    name: \"libhealthd_charger_test_data\",\n    srcs: [\"**/*.*\"],\n}\n"
  },
  {
    "path": "healthd/testdata/empty/ensure_directory_creation.txt",
    "content": "File is placed to ensure directory is created on the device.\n"
  },
  {
    "path": "healthd/testdata/legacy/res/values/charger/animation.txt",
    "content": "# Sample Animation file for testing.\n\n# animation: num_cycles, first_frame_repeats, animation_file\nanimation: 2 1 charger/battery_scale\n\nfail: charger/battery_fail\n\n# frame: disp_time min_level max_level\nframe: 15 0 100\n"
  },
  {
    "path": "healthd/testdata/legacy_text_system_images/res/values/charger/animation.txt",
    "content": "# Sample Animation file for testing.\n\n# animation: num_cycles, first_frame_repeats, animation_file\nanimation: 2 1 charger/battery_scale\n\nfail: charger/battery_fail\n\n# frame: disp_time min_level max_level\nframe: 15 0 100\n"
  },
  {
    "path": "healthd/testdata/product/product/etc/res/values/charger/animation.txt",
    "content": "# Sample Animation file for testing.\n\n# animation: num_cycles, first_frame_repeats, animation_file\nanimation: 2 1 charger/battery_scale\n\nfail: charger/battery_fail\n\n# frame: disp_time min_level max_level\nframe: 15 0 100\n"
  },
  {
    "path": "init/Android.bp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"system_core_init_license\"],\n}\n\n// Added automatically by a large-scale-change\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_init_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\ninit_common_sources = [\n    \"action.cpp\",\n    \"action_manager.cpp\",\n    \"action_parser.cpp\",\n    \"capabilities.cpp\",\n    \"epoll.cpp\",\n    \"import_parser.cpp\",\n    \"interprocess_fifo.cpp\",\n    \"keychords.cpp\",\n    \"parser.cpp\",\n    \"property_type.cpp\",\n    \"rlimit_parser.cpp\",\n    \"service.cpp\",\n    \"service_list.cpp\",\n    \"service_parser.cpp\",\n    \"service_utils.cpp\",\n    \"subcontext.cpp\",\n    \"subcontext.proto\",\n    \"tokenizer.cpp\",\n    \"util.cpp\",\n]\ninit_device_sources = [\n    \"apex_init_util.cpp\",\n    \"block_dev_initializer.cpp\",\n    \"bootchart.cpp\",\n    \"builtins.cpp\",\n    \"devices.cpp\",\n    \"firmware_handler.cpp\",\n    \"first_stage_console.cpp\",\n    \"first_stage_init.cpp\",\n    \"first_stage_mount.cpp\",\n    \"fscrypt_init_extensions.cpp\",\n    \"init.cpp\",\n    \"lmkd_service.cpp\",\n    \"modalias_handler.cpp\",\n    \"mount_handler.cpp\",\n    \"mount_namespace.cpp\",\n    \"persistent_properties.cpp\",\n    \"persistent_properties.proto\",\n    \"property_service.cpp\",\n    \"property_service.proto\",\n    \"reboot.cpp\",\n    \"reboot_utils.cpp\",\n    \"security.cpp\",\n    \"selabel.cpp\",\n    \"selinux.cpp\",\n    \"sigchld_handler.cpp\",\n    \"snapuserd_transition.cpp\",\n    \"switch_root.cpp\",\n    \"uevent_listener.cpp\",\n    \"ueventd.cpp\",\n    \"ueventd_parser.cpp\",\n]\n\nsoong_config_module_type {\n    name: \"libinit_cc_defaults\",\n    module_type: \"cc_defaults\",\n    config_namespace: \"ANDROID\",\n    bool_variables: [\n        \"PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT\",\n        \"release_write_appcompat_override_system_properties\",\n    ],\n    properties: [\n        \"cflags\",\n    ],\n}\n\nlibinit_cc_defaults {\n    name: \"init_defaults\",\n    sanitize: {\n        misc_undefined: [\"signed-integer-overflow\"],\n    },\n    cflags: [\n        \"-DALLOW_FIRST_STAGE_CONSOLE=0\",\n        \"-DALLOW_LOCAL_PROP_OVERRIDE=0\",\n        \"-DALLOW_PERMISSIVE_SELINUX=0\",\n        \"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION\",\n        \"-DDUMP_ON_UMOUNT_FAILURE=0\",\n        \"-DINIT_FULL_SOURCES\",\n        \"-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=0\",\n        \"-DLOG_UEVENTS=0\",\n        \"-DREBOOT_BOOTLOADER_ON_PANIC=0\",\n        \"-DSHUTDOWN_ZERO_TIMEOUT=0\",\n        \"-DWORLD_WRITABLE_KMSG=0\",\n        \"-Wall\",\n        \"-Werror\",\n        \"-Wextra\",\n        \"-Wno-unused-parameter\",\n        \"-Wthread-safety\",\n    ],\n    product_variables: {\n        debuggable: {\n            cppflags: [\n                \"-UALLOW_FIRST_STAGE_CONSOLE\",\n                \"-DALLOW_FIRST_STAGE_CONSOLE=1\",\n                \"-UALLOW_LOCAL_PROP_OVERRIDE\",\n                \"-DALLOW_LOCAL_PROP_OVERRIDE=1\",\n                \"-UALLOW_PERMISSIVE_SELINUX\",\n                \"-DALLOW_PERMISSIVE_SELINUX=1\",\n                \"-UREBOOT_BOOTLOADER_ON_PANIC\",\n                \"-DREBOOT_BOOTLOADER_ON_PANIC=1\",\n                \"-UWORLD_WRITABLE_KMSG\",\n                \"-DWORLD_WRITABLE_KMSG=1\",\n                \"-UDUMP_ON_UMOUNT_FAILURE\",\n                \"-DDUMP_ON_UMOUNT_FAILURE=1\",\n                \"-UALLOW_REMOUNT_OVERLAYS\",\n                \"-DALLOW_REMOUNT_OVERLAYS=1\",\n            ],\n        },\n        eng: {\n            cppflags: [\n                \"-USHUTDOWN_ZERO_TIMEOUT\",\n                \"-DSHUTDOWN_ZERO_TIMEOUT=1\",\n            ],\n        },\n        uml: {\n            cppflags: [\"-DUSER_MODE_LINUX\"],\n        },\n    },\n    soong_config_variables: {\n        PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT: {\n            cflags: [\n                \"-UINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT\",\n                \"-DINSTALL_DEBUG_POLICY_TO_SYSTEM_EXT=1\",\n            ],\n        },\n        release_write_appcompat_override_system_properties: {\n            cflags: [\"-DWRITE_APPCOMPAT_OVERRIDE_SYSTEM_PROPERTIES\"],\n        },\n    },\n    static_libs: [\n        \"libavb\",\n        \"libavf_cc_flags\",\n        \"libbootloader_message\",\n        \"liblmkd_utils\",\n        \"liblz4\",\n        \"libzstd\",\n        \"libmodprobe\",\n        \"libprocinfo\",\n        \"libprotobuf-cpp-lite\",\n        \"libpropertyinfoserializer\",\n        \"libpropertyinfoparser\",\n        \"libsnapshot_cow\",\n        \"libsnapshot_init\",\n        \"libxml2\",\n        \"lib_apex_manifest_proto_lite\",\n        \"update_metadata-protos\",\n        \"libgenfslabelsversion.ffi\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"libdl\",\n        \"libext4_utils\",\n        \"libfs_mgr\",\n        \"libgsi\",\n        \"liblog\",\n        \"liblogwrap\",\n        \"liblp\",\n        \"libprocessgroup\",\n        \"libprocessgroup_setup\",\n        \"libselinux\",\n        \"libunwindstack\",\n        \"libutils\",\n        \"libvendorsupport\",\n    ],\n    header_libs: [\"bionic_libc_platform_headers\"],\n    bootstrap: true,\n    visibility: [\":__subpackages__\"],\n}\n\ncc_library_headers {\n    name: \"libinit_headers\",\n    export_include_dirs: [\".\"],\n    visibility: [\":__subpackages__\"],\n}\n\ncc_defaults {\n    name: \"libinit_defaults\",\n    recovery_available: true,\n    defaults: [\n        \"init_defaults\",\n        \"selinux_policy_version\",\n    ],\n    srcs: init_common_sources + init_device_sources,\n    export_include_dirs: [\".\"],\n    generated_sources: [\n        \"apex-info-list\",\n    ],\n    whole_static_libs: [\n        \"libcap\",\n    ],\n    header_libs: [\"bootimg_headers\"],\n    proto: {\n        type: \"lite\",\n        export_proto_headers: true,\n    },\n\n    target: {\n        recovery: {\n            cflags: [\"-DRECOVERY\"],\n            exclude_static_libs: [\n                \"libxml2\",\n            ],\n            exclude_generated_sources: [\n                \"apex-info-list\",\n            ],\n            exclude_shared_libs: [\n                \"libbinder\",\n                \"libutils\",\n            ],\n        },\n    },\n}\n\ncc_library_static {\n    name: \"libinit\",\n    defaults: [\"libinit_defaults\"],\n}\n\ncc_library_static {\n    name: \"libinit.microdroid\",\n    defaults: [\n        \"avf_build_flags_cc\",\n        \"libinit_defaults\",\n    ],\n    recovery_available: false,\n    cflags: [\"-DMICRODROID=1\"],\n}\n\nphony {\n    name: \"init\",\n    required: [\n        \"init_second_stage\",\n    ] + select(product_variable(\"debuggable\"), {\n        true: [\"overlay_remounter\"],\n        false: [],\n    }),\n}\n\ncc_defaults {\n    name: \"init_second_stage_defaults\",\n    stem: \"init\",\n    defaults: [\"init_defaults\"],\n    srcs: [\"main.cpp\"],\n    symlinks: [\"ueventd\"],\n}\n\ncc_binary {\n    name: \"init_second_stage\",\n    defaults: [\"init_second_stage_defaults\"],\n    static_libs: [\"libinit\"],\n    visibility: [\"//visibility:any_system_partition\"],\n    required: [\n        \"init.rc\",\n        \"ueventd.rc\",\n        \"e2fsdroid\",\n        \"extra_free_kbytes\",\n        \"make_f2fs\",\n        \"mke2fs\",\n        \"sload_f2fs\",\n    ],\n}\n\ncc_binary {\n    name: \"init_second_stage.recovery\",\n    defaults: [\"init_second_stage_defaults\"],\n    static_libs: [\"libinit\"],\n    recovery: true,\n    cflags: [\"-DRECOVERY\"],\n    exclude_static_libs: [\n        \"libxml2\",\n    ],\n    exclude_shared_libs: [\n        \"libbinder\",\n        \"libutils\",\n    ],\n    required: [\n        \"init_recovery.rc\",\n        \"ueventd.rc.recovery\",\n        \"e2fsdroid.recovery\",\n        \"make_f2fs.recovery\",\n        \"mke2fs.recovery\",\n        \"sload_f2fs.recovery\",\n    ],\n}\n\ncc_binary {\n    name: \"init_second_stage.microdroid\",\n    defaults: [\n        \"avf_build_flags_cc\",\n        \"init_second_stage_defaults\",\n    ],\n    static_libs: [\"libinit.microdroid\"],\n    cflags: [\"-DMICRODROID=1\"],\n    no_full_install: true,\n    visibility: [\"//packages/modules/Virtualization/build/microdroid\"],\n}\n\nsoong_config_module_type {\n    name: \"init_first_stage_cc_defaults\",\n    module_type: \"cc_defaults\",\n    config_namespace: \"ANDROID\",\n    bool_variables: [\"BOARD_USES_RECOVERY_AS_BOOT\"],\n    properties: [\"no_full_install\"],\n}\n\n// Do not install init_first_stage even with mma if we're system-as-root.\n// Otherwise, it will overwrite the symlink.\ninit_first_stage_cc_defaults {\n    name: \"init_first_stage_defaults\",\n    soong_config_variables: {\n        BOARD_USES_RECOVERY_AS_BOOT: {\n            no_full_install: true,\n        },\n    },\n\n    stem: \"init\",\n\n    srcs: [\n        \"block_dev_initializer.cpp\",\n        \"devices.cpp\",\n        \"first_stage_console.cpp\",\n        \"first_stage_init.cpp\",\n        \"first_stage_main.cpp\",\n        \"first_stage_mount.cpp\",\n        \"reboot_utils.cpp\",\n        \"selabel.cpp\",\n        \"service_utils.cpp\",\n        \"snapuserd_transition.cpp\",\n        \"switch_root.cpp\",\n        \"uevent_listener.cpp\",\n        \"util.cpp\",\n    ],\n\n    static_libs: [\n        \"libfs_avb\",\n        \"libavf_cc_flags\",\n        \"libfs_mgr\",\n        \"libfec\",\n        \"libfec_rs\",\n        \"libsquashfs_utils\",\n        \"libcrypto_utils\",\n        \"libavb\",\n        \"liblp\",\n        \"libcutils\",\n        \"libbase\",\n        \"liblog\",\n        \"libcrypto_static\",\n        \"libselinux\",\n        \"libcap\",\n        \"libgsi\",\n        \"liblzma\",\n        \"libunwindstack_no_dex\",\n        \"libmodprobe\",\n        \"libext2_uuid\",\n        \"libprotobuf-cpp-lite\",\n        \"libsnapshot_cow\",\n        \"liblz4\",\n        \"libzstd\",\n        \"libsnapshot_init\",\n        \"update_metadata-protos\",\n        \"libprocinfo\",\n        \"libbootloader_message\",\n    ],\n\n    static_executable: true,\n    system_shared_libs: [],\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Wno-unused-parameter\",\n        \"-Werror\",\n        \"-DALLOW_FIRST_STAGE_CONSOLE=0\",\n        \"-DALLOW_LOCAL_PROP_OVERRIDE=0\",\n        \"-DALLOW_PERMISSIVE_SELINUX=0\",\n        \"-DREBOOT_BOOTLOADER_ON_PANIC=0\",\n        \"-DWORLD_WRITABLE_KMSG=0\",\n        \"-DDUMP_ON_UMOUNT_FAILURE=0\",\n        \"-DSHUTDOWN_ZERO_TIMEOUT=0\",\n        \"-DLOG_UEVENTS=0\",\n        \"-DSEPOLICY_VERSION=30\", // TODO(jiyong): externalize the version number\n    ],\n\n    product_variables: {\n        debuggable: {\n            cflags: [\n                \"-UALLOW_FIRST_STAGE_CONSOLE\",\n                \"-DALLOW_FIRST_STAGE_CONSOLE=1\",\n\n                \"-UALLOW_LOCAL_PROP_OVERRIDE\",\n                \"-DALLOW_LOCAL_PROP_OVERRIDE=1\",\n\n                \"-UALLOW_PERMISSIVE_SELINUX\",\n                \"-DALLOW_PERMISSIVE_SELINUX=1\",\n\n                \"-UREBOOT_BOOTLOADER_ON_PANIC\",\n                \"-DREBOOT_BOOTLOADER_ON_PANIC=1\",\n\n                \"-UWORLD_WRITABLE_KMSG\",\n                \"-DWORLD_WRITABLE_KMSG=1\",\n\n                \"-UDUMP_ON_UMOUNT_FAILURE\",\n                \"-DDUMP_ON_UMOUNT_FAILURE=1\",\n            ],\n        },\n\n        eng: {\n            cflags: [\n                \"-USHUTDOWN_ZERO_TIMEOUT\",\n                \"-DSHUTDOWN_ZERO_TIMEOUT=1\",\n            ],\n        },\n    },\n\n    sanitize: {\n        misc_undefined: [\"signed-integer-overflow\"],\n\n        // First stage init is weird: it may start without stdout/stderr, and no /proc.\n        hwaddress: false,\n        memtag_stack: false,\n    },\n\n    // Install adb_debug.prop into debug ramdisk.\n    // This allows adb root on a user build, when debug ramdisk is used.\n    required: [\"adb_debug.prop\"],\n\n    ramdisk: true,\n\n    install_in_root: true,\n}\n\ncc_binary {\n    name: \"init_first_stage\",\n    defaults: [\"init_first_stage_defaults\"],\n}\n\ncc_binary {\n    name: \"init_first_stage.microdroid\",\n    defaults: [\n        \"avf_build_flags_cc\",\n        \"init_first_stage_defaults\",\n    ],\n    cflags: [\"-DMICRODROID=1\"],\n    no_full_install: true,\n}\n\nphony {\n    name: \"init_system\",\n    required: [\"init_second_stage\"],\n}\n\n// Tests\n// ------------------------------------------------------------------------------\n\ncc_test {\n    // Note: This is NOT a CTS test. See b/320800872\n    name: \"CtsInitTestCases\",\n    defaults: [\"init_defaults\"],\n    require_root: true,\n\n    compile_multilib: \"first\",\n\n    srcs: [\n        \"devices_test.cpp\",\n        \"epoll_test.cpp\",\n        \"firmware_handler_test.cpp\",\n        \"init_test.cpp\",\n        \"interprocess_fifo_test.cpp\",\n        \"keychords_test.cpp\",\n        \"oneshot_on_test.cpp\",\n        \"persistent_properties_test.cpp\",\n        \"property_service_test.cpp\",\n        \"property_type_test.cpp\",\n        \"reboot_test.cpp\",\n        \"rlimit_parser_test.cpp\",\n        \"service_test.cpp\",\n        \"subcontext_test.cpp\",\n        \"tokenizer_test.cpp\",\n        \"ueventd_parser_test.cpp\",\n        \"ueventd_test.cpp\",\n        \"util_test.cpp\",\n    ],\n    static_libs: [\n        \"libgmock\",\n        \"libinit\",\n    ],\n\n    test_suites: [\n        \"device-tests\",\n    ],\n}\n\ncc_benchmark {\n    name: \"init_benchmarks\",\n    defaults: [\"init_defaults\"],\n    srcs: [\n        \"subcontext_benchmark.cpp\",\n    ],\n    static_libs: [\"libinit\"],\n}\n\ncc_defaults {\n    name: \"libinit_test_utils_libraries_defaults\",\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"libselinux\",\n        \"liblog\",\n        \"libprocessgroup\",\n        \"libprotobuf-cpp-lite\",\n    ],\n    static_libs: [\n        \"libfs_mgr\",\n        \"libhidl-gen-utils\",\n    ],\n}\n\ncc_library_static {\n    name: \"libinit_test_utils\",\n    defaults: [\"libinit_test_utils_libraries_defaults\"],\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Wno-unused-parameter\",\n        \"-Werror\",\n    ],\n    srcs: init_common_sources + [\n        \"test_utils/service_utils.cpp\",\n    ],\n    whole_static_libs: [\n        \"libcap\",\n    ],\n    export_include_dirs: [\"test_utils/include\"], // for tests\n    header_libs: [\"bionic_libc_platform_headers\"],\n    product_variables: {\n        shipping_api_level: {\n            cflags: [\"-DBUILD_SHIPPING_API_LEVEL=%s\"],\n        },\n    },\n}\n\n// Host Verifier\n// ------------------------------------------------------------------------------\n\ngenrule {\n    name: \"generated_stub_builtin_function_map\",\n    tool_files: [\"host_builtin_map.py\"],\n    out: [\"generated_stub_builtin_function_map.h\"],\n    srcs: [\n        \"builtins.cpp\",\n        \"check_builtins.cpp\",\n    ],\n    cmd: \"$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location check_builtins.cpp) > $(out)\",\n}\n\ncc_defaults {\n    name: \"init_host_defaults\",\n    host_supported: true,\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Wno-unused-parameter\",\n        \"-Werror\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"libfstab\",\n        \"libselinux\",\n        \"libpropertyinfoserializer\",\n        \"libpropertyinfoparser\",\n    ],\n    whole_static_libs: [\"libcap\"],\n    shared_libs: [\n        \"libcutils\",\n        \"liblog\",\n        \"libprocessgroup\",\n        \"libprotobuf-cpp-lite\",\n    ],\n    proto: {\n        type: \"lite\",\n    },\n    target: {\n        android: {\n            enabled: false,\n        },\n        darwin: {\n            enabled: false,\n        },\n    },\n    product_variables: {\n        shipping_api_level: {\n            cflags: [\"-DBUILD_SHIPPING_API_LEVEL=%s\"],\n        },\n    },\n}\n\ncc_binary {\n    name: \"host_init_verifier\",\n    defaults: [\"init_host_defaults\"],\n    srcs: [\n        \"check_builtins.cpp\",\n        \"host_import_parser.cpp\",\n        \"host_init_verifier.cpp\",\n        \"interface_utils.cpp\",\n    ] + init_common_sources,\n    generated_headers: [\n        \"generated_android_ids\",\n        \"generated_stub_builtin_function_map\",\n    ],\n    shared_libs: [\n        \"libhidl-gen-utils\",\n        \"libhidlmetadata\",\n    ],\n}\n\ngenrule {\n    name: \"noop_builtin_function_map\",\n    tool_files: [\"host_builtin_map.py\"],\n    out: [\"noop_builtin_function_map.h\"],\n    srcs: [\n        \"builtins.cpp\",\n        \"noop_builtins.cpp\",\n    ],\n    cmd: \"$(location host_builtin_map.py) --builtins $(location builtins.cpp) --check_builtins $(location noop_builtins.cpp) > $(out)\",\n}\n\ncc_library_host_static {\n    name: \"libinit_host\",\n    defaults: [\"init_host_defaults\"],\n    srcs: [\n        \"noop_builtins.cpp\",\n    ] + init_common_sources,\n    export_include_dirs: [\".\"],\n    generated_headers: [\n        \"noop_builtin_function_map\",\n    ],\n    proto: {\n        export_proto_headers: true,\n    },\n    visibility: [\n        // host_apex_verifier performs a subset of init.rc validation\n        \"//system/apex/tools\",\n    ],\n}\n\nsh_binary {\n    name: \"extra_free_kbytes\",\n    src: \"extra_free_kbytes.sh\",\n    filename_from_src: true,\n}\n\nphony {\n    name: \"init_vendor\",\n    required: select(soong_config_variable(\"ANDROID\", \"BOARD_USES_RECOVERY_AS_BOOT\"), {\n        true: [],\n        default: [\"init_first_stage\"],\n    }),\n}\n"
  },
  {
    "path": "init/AndroidTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2019 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config for CTS init test cases\">\n    <option name=\"test-suite-tag\" value=\"cts\" />\n    <option name=\"config-descriptor:metadata\" key=\"component\" value=\"systems\" />\n    <option name=\"config-descriptor:metadata\" key=\"parameter\" value=\"not_instant_app\" />\n    <option name=\"config-descriptor:metadata\" key=\"parameter\" value=\"not_multi_abi\" />\n    <option name=\"config-descriptor:metadata\" key=\"parameter\" value=\"secondary_user\" />\n    <target_preparer class=\"com.android.compatibility.common.tradefed.targetprep.FilePusher\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"CtsInitTestCases->/data/local/tmp/CtsInitTestCases\" />\n    </target_preparer>\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\">\n        <option name=\"throw-on-error\" value=\"false\" />\n    </target_preparer>\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        <option name=\"native-test-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"module-name\" value=\"CtsInitTestCases\" />\n        <option name=\"runtime-hint\" value=\"65s\" />\n    </test>\n    <!-- Controller that will skip the module if a native bridge situation is detected -->\n    <!-- For example: module wants to run arm32 and device is x86 -->\n    <object type=\"module_controller\" class=\"com.android.tradefed.testtype.suite.module.NativeBridgeModuleController\" />\n</configuration>\n"
  },
  {
    "path": "init/MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "init/NOTICE",
    "content": "\n   Copyright (c) 2005-2008, The Android Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "init/OWNERS",
    "content": "# Bug component: 1312227\ndvander@google.com\njiyong@google.com\n"
  },
  {
    "path": "init/README.md",
    "content": "Android Init Language\n---------------------\n\nThe Android Init Language consists of five broad classes of statements:\nActions, Commands, Services, Options, and Imports.\n\nAll of these are line-oriented, consisting of tokens separated by\nwhitespace.  The c-style backslash escapes may be used to insert\nwhitespace into a token.  Double quotes may also be used to prevent\nwhitespace from breaking text into multiple tokens.  The backslash,\nwhen it is the last character on a line, may be used for line-folding.\n\nLines which start with a `#` (leading whitespace allowed) are comments.\n\nSystem properties can be expanded using the syntax\n`${property.name}`. This also works in contexts where concatenation is\nrequired, such as `import /init.recovery.${ro.hardware}.rc`.\n\nActions and Services implicitly declare a new section.  All commands\nor options belong to the section most recently declared.  Commands\nor options before the first section are ignored.\n\nServices have unique names.  If a second Service is defined\nwith the same name as an existing one, it is ignored and an error\nmessage is logged.\n\n\nInit .rc Files\n--------------\nThe init language is used in plain text files that take the .rc file\nextension.  There are typically multiple of these in multiple\nlocations on the system, described below.\n\n`/system/etc/init/hw/init.rc` is the primary .rc file and is loaded by the init executable at the\nbeginning of its execution.  It is responsible for the initial set up of the system.\n\nInit loads all of the files contained within the\n`/{system,system_ext,vendor,odm,product}/etc/init/` directories immediately after loading\nthe primary `/system/etc/init/hw/init.rc`.  This is explained in more details in the\n[Imports](#imports) section of this file.\n\nLegacy devices without the first stage mount mechanism previously were\nable to import init scripts during mount_all, however that is deprecated\nand not allowed for devices launching after Q.\n\nThe intention of these directories is:\n\n   1. /system/etc/init/ is for core system items such as\n      SurfaceFlinger, MediaService, and logd.\n   2. /vendor/etc/init/ is for SoC vendor items such as actions or\n      daemons needed for core SoC functionality.\n   3. /odm/etc/init/ is for device manufacturer items such as\n      actions or daemons needed for motion sensor or other peripheral\n      functionality.\n\nAll services whose binaries reside on the system, vendor, or odm\npartitions should have their service entries placed into a\ncorresponding init .rc file, located in the /etc/init/\ndirectory of the partition where they reside.  There is a build\nsystem macro, LOCAL\\_INIT\\_RC, that handles this for developers.  Each\ninit .rc file should additionally contain any actions associated with\nits service.\n\nAn example is the userdebug logcatd.rc and Android.mk files located in the\nsystem/core/logcat directory.  The LOCAL\\_INIT\\_RC macro in the\nAndroid.mk file places logcatd.rc in /system/etc/init/ during the\nbuild process.  Init loads logcatd.rc during the mount\\_all command and\nallows the service to be run and the action to be queued when\nappropriate.\n\nThis break up of init .rc files according to their daemon is preferred\nto the previously used monolithic init .rc files.  This approach\nensures that the only service entries that init reads and the only\nactions that init performs correspond to services whose binaries are in\nfact present on the file system, which was not the case with the\nmonolithic init .rc files.  This additionally will aid in merge\nconflict resolution when multiple services are added to the system, as\neach one will go into a separate file.\n\nVersioned RC files within APEXs\n-------------------------------\n\nWith the arrival of mainline on Android Q, the individual mainline\nmodules carry their own init.rc files within their boundaries. Init\nprocesses these files according to the naming pattern `/apex/*/etc/*rc`.\n\nBecause APEX modules must run on more than one release of Android,\nthey may require different parameters as part of the services they\ndefine. This is achieved, starting in Android T, by incorporating\nthe SDK version information in the name of the init file.  The suffix\nis changed from `.rc` to `.#rc` where # is the first SDK where that\nRC file is accepted. An init file specific to SDK=31 might be named\n`init.31rc`. With this scheme, an APEX may include multiple init files. An\nexample is appropriate.\n\nFor an APEX module with the following files in /apex/sample-module/apex/etc/:\n\n   1. init.rc\n   2. init.32rc\n   4. init.35rc\n\nThe selection rule chooses the highest `.#rc` value that does not\nexceed the SDK of the currently running system. The unadorned `.rc`\nis interpreted as sdk=0.\n\nWhen this APEX is installed on a device with SDK <=31, the system will\nprocess init.rc.  When installed on a device running SDK 32, 33, or 34,\nit will use init.32rc.  When installed on a device running SDKs >= 35,\nit will choose init.35rc\n\nThis versioning scheme is used only for the init files within APEX\nmodules; it does not apply to the init files stored in /system/etc/init,\n/vendor/etc/init, or other directories.\n\nThis naming scheme is available after Android S.\n\nActions\n-------\nActions are named sequences of commands.  Actions have a trigger which\nis used to determine when the action is executed.  When an event\noccurs which matches an action's trigger, that action is added to\nthe tail of a to-be-executed queue (unless it is already on the\nqueue).\n\nEach action in the queue is dequeued in sequence and each command in\nthat action is executed in sequence.  Init handles other activities\n(device creation/destruction, property setting, process restarting)\n\"between\" the execution of the commands in activities.\n\nActions take the form of:\n\n    on <trigger> [&& <trigger>]*\n       <command>\n       <command>\n       <command>\n\nActions are added to the queue and executed based on the order that\nthe file that contains them was parsed (see the Imports section), then\nsequentially within an individual file.\n\nFor example if a file contains:\n\n    on boot\n       setprop a 1\n       setprop b 2\n\n    on boot && property:true=true\n       setprop c 1\n       setprop d 2\n\n    on boot\n       setprop e 1\n       setprop f 2\n\nThen when the `boot` trigger occurs and assuming the property `true`\nequals `true`, then the order of the commands executed will be:\n\n    setprop a 1\n    setprop b 2\n    setprop c 1\n    setprop d 2\n    setprop e 1\n    setprop f 2\n\nIf the property `true` wasn't `true` when the `boot` was triggered, then the\norder of the commands executed will be:\n\n    setprop a 1\n    setprop b 2\n    setprop e 1\n    setprop f 2\n\nIf the property `true` becomes `true` *AFTER* `boot` was triggered, nothing will\nbe executed. The condition `boot && property:true=true` will be evaluated to\nfalse because the `boot` trigger is a past event.\n\nNote that when `ro.property_service.async_persist_writes` is `true`, there is no\ndefined ordering between persistent setprops and non-persistent setprops. For\nexample:\n\n    on boot\n        setprop a 1\n        setprop persist.b 2\n\nWhen `ro.property_service.async_persist_writes` is `true`, triggers for these\ntwo properties may execute in any order.\n\nServices\n--------\nServices are programs which init launches and (optionally) restarts\nwhen they exit.  Services take the form of:\n\n    service <name> <pathname> [ <argument> ]*\n       <option>\n       <option>\n       ...\n\n\nOptions\n-------\nOptions are modifiers to services.  They affect how and when init\nruns the service.\n\n`capabilities [ <capability>\\* ]`\n> Set capabilities when exec'ing this service. 'capability' should be a Linux\n  capability without the \"CAP\\_\" prefix, like \"NET\\_ADMIN\" or \"SETPCAP\". See\n  http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux\n  capabilities.\n  If no capabilities are provided, then behaviour depends on the user the service runs under:\n    * if it's root, then the service will run with all the capabitilies (note: whether the\n        service can actually use them is controlled by selinux);\n    * otherwise all capabilities will be dropped.\n\n`class <name> [ <name>\\* ]`\n> Specify class names for the service.  All services in a\n  named class may be started or stopped together.  A service\n  is in the class \"default\" if one is not specified via the\n  class option. Additional classnames beyond the (required) first\n  one are used to group services.\n  The `animation` class should include all services necessary for both\n  boot animation and shutdown animation. As these services can be\n  launched very early during bootup and can run until the last stage\n  of shutdown, access to /data partition is not guaranteed. These\n  services can check files under /data but it should not keep files opened\n  and should work when /data is not available.\n\n`console [<console>]`\n> This service needs a console. The optional second parameter chooses a\n  specific console instead of the default. The default \"/dev/console\" can\n  be changed by setting the \"androidboot.console\" kernel parameter. In\n  all cases the leading \"/dev/\" should be omitted, so \"/dev/tty0\" would be\n  specified as just \"console tty0\".\n  This option connects stdin, stdout, and stderr to the console. It is mutually exclusive with the\n  stdio_to_kmsg option, which only connects stdout and stderr to kmsg.\n\n`critical [window=<fatal crash window mins>] [target=<fatal reboot target>]`\n> This is a device-critical service. If it exits more than four times in\n  _fatal crash window mins_ minutes or before boot completes, the device\n  will reboot into _fatal reboot target_.\n  The default value of _fatal crash window mins_ is 4, and default value\n  of _fatal reboot target_ is 'bootloader'.\n  For tests, the fatal reboot can be skipped by setting property\n  `init.svc_debug.no_fatal.<service-name>` to `true` for specified critical service.\n\n`disabled`\n> This service will not automatically start with its class.\n  It must be explicitly started by name or by interface name.\n\n`enter_namespace <type> <path>`\n> Enters the namespace of type _type_ located at _path_. Only network namespaces are supported with\n  _type_ set to \"net\". Note that only one namespace of a given _type_ may be entered.\n\n`file <path> <type>`\n> Open a file path and pass its fd to the launched process. _type_ must be\n  \"r\", \"w\" or \"rw\".  For native executables see libcutils\n  android\\_get\\_control\\_file().\n\n`gentle_kill`\n> This service will be sent SIGTERM instead of SIGKILL when stopped. After a 200 ms timeout, it will\n  be sent SIGKILL.\n\n`group <groupname> [ <groupname>\\* ]`\n> Change to 'groupname' before exec'ing this service.  Additional\n  groupnames beyond the (required) first one are used to set the\n  supplemental groups of the process (via setgroups()).\n  Currently defaults to root.  (??? probably should default to nobody)\n\n`interface <interface name> <instance name>`\n> Associates this service with a list of the AIDL or HIDL services that it provides. The interface\n  name must be a fully-qualified name and not a value name. For instance, this is used to allow\n  servicemanager or hwservicemanager to lazily start services. When multiple interfaces are served,\n  this tag should be used multiple times. An example of an entry for a HIDL\n  interface is `interface vendor.foo.bar@1.0::IBaz default`. For an AIDL interface, use\n  `interface aidl <instance name>`. The instance name for an AIDL interface is\n  whatever is registered with servicemanager, and these can be listed with `adb\n  shell dumpsys -l`.\n\n`ioprio <class> <priority>`\n> Sets the IO priority and IO priority class for this service via the SYS_ioprio_set syscall.\n  _class_ must be one of \"rt\", \"be\", or \"idle\". _priority_ must be an integer in the range 0 - 7.\n\n`keycodes <keycode> [ <keycode>\\* ]`\n> Sets the keycodes that will trigger this service. If all of the keys corresponding to the passed\n  keycodes are pressed at once, the service will start. This is typically used to start the\n  bugreport service.\n\n> This option may take a property instead of a list of keycodes. In this case, only one option is\n  provided: the property name in the typical property expansion format. The property must contain\n  a comma separated list of keycode values or the text 'none' to indicate that\n  this service does not respond to keycodes.\n\n> For example, `keycodes ${some.property.name:-none}` where some.property.name expands\n  to \"123,124,125\". Since keycodes are handled very early in init,\n  only PRODUCT_DEFAULT_PROPERTY_OVERRIDES properties can be used.\n\n`memcg.limit_in_bytes <value>` and `memcg.limit_percent <value>`\n> Sets the child's memory.limit_in_bytes to the minimum of `limit_in_bytes`\n  bytes and `limit_percent` which is interpreted as a percentage of the size\n  of the device's physical memory (only if memcg is mounted).\n  Values must be equal or greater than 0.\n\n`memcg.limit_property <value>`\n> Sets the child's memory.limit_in_bytes to the value of the specified property\n  (only if memcg is mounted). This property will override the values specified\n  via `memcg.limit_in_bytes` and `memcg.limit_percent`.\n\n`memcg.soft_limit_in_bytes <value>`\n> Sets the child's memory.soft_limit_in_bytes to the specified value (only if memcg is mounted),\n  which must be equal or greater than 0.\n\n`memcg.swappiness <value>`\n> Sets the child's memory.swappiness to the specified value (only if memcg is mounted),\n  which must be equal or greater than 0.\n\n`namespace <pid|mnt>`\n> Enter a new PID or mount namespace when forking the service.\n\n`oneshot`\n> Do not restart the service when it exits.\n\n`onrestart`\n> Execute a Command (see below) when service restarts.\n\n`oom_score_adjust <value>`\n> Sets the child's /proc/self/oom\\_score\\_adj to the specified value,\n  which must range from -1000 to 1000.\n\n`override`\n> Indicates that this service definition is meant to override a previous definition for a service\n  with the same name. This is typically meant for services on /odm to override those defined on\n  /vendor. The last service definition that init parses with this keyword is the service definition\n  will use for this service. Pay close attention to the order in which init.rc files are parsed,\n  since it has some peculiarities for backwards compatibility reasons. The 'imports' section of\n  this file has more details on the order.\n\n`priority <priority>`\n> Scheduling priority of the service process. This value has to be in range\n  -20 to 19. Default priority is 0. Priority is set via setpriority().\n\n`reboot_on_failure <target>`\n> If this process cannot be started or if the process terminates with an exit code other than\n  CLD_EXITED or an status other than '0', reboot the system with the target specified in\n  _target_. _target_ takes the same format as the parameter to sys.powerctl. This is particularly\n  intended to be used with the `exec_start` builtin for any must-have checks during boot.\n\n`restart_period <seconds>`\n> If a non-oneshot service exits, it will be restarted at its previous start time plus this period.\n  The default value is 5s. This can be used to implement periodic services together with the\n  `timeout_period` command below. For example, it may be set to 3600 to indicate that the service\n  should run every hour or 86400 to indicate that the service should run every day. This can be set\n  to a value shorter than 5s for example 0, but the minimum 5s delay is enforced if the restart was\n  due to a crash. This is to rate limit persistentally crashing services. In other words,\n  `<seconds>` smaller than 5 is respected only when the service exits deliverately and successfully\n  (i.e. by calling exit(0)).\n\n`rlimit <resource> <cur> <max>`\n> This applies the given rlimit to the service. rlimits are inherited by child\n  processes, so this effectively applies the given rlimit to the process tree\n  started by this service.\n  It is parsed similarly to the setrlimit command specified below.\n\n`seclabel <seclabel>`\n> Change to 'seclabel' before exec'ing this service.\n  Primarily for use by services run from the rootfs, e.g. ueventd, adbd.\n  Services on the system partition can instead use policy-defined transitions\n  based on their file security context.\n  If not specified and no transition is defined in policy, defaults to the init context.\n\n`setenv <name> <value>`\n> Set the environment variable _name_ to _value_ in the launched process.\n\n`shared_kallsyms`\n> If set, init will behave as if the service specified \"file /proc/kallsyms r\",\n  except the service will receive a duplicate of a single fd that init saved\n  during early second\\_stage. This fd retains address visibility even after the\n  systemwide kptr\\_restrict sysctl is set to its steady state on Android. The\n  ability to read from this fd is still constrained by selinux permissions,\n  which need to be granted separately and are gated by a neverallow.\n  Because of performance gotchas of concurrent use of this shared fd, all uses\n  need to coordinate via provisional flock(LOCK\\_EX) locks on separately opened\n  /proc/kallsyms fds (since locking requires distinct open file descriptions).\n\n`shutdown <shutdown_behavior>`\n> Set shutdown behavior of the service process. When this is not specified,\n  the service is killed during shutdown process by using SIGTERM and SIGKILL.\n  The service with shutdown_behavior of \"critical\" is not killed during shutdown\n  until shutdown times out. When shutdown times out, even services tagged with\n  \"shutdown critical\" will be killed. When the service tagged with \"shutdown critical\"\n  is not running when shut down starts, it will be started.\n\n`sigstop`\n> Send SIGSTOP to the service immediately before exec is called. This is intended for debugging.\n  See the below section on debugging for how this can be used.\n\n`socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]`\n> Create a UNIX domain socket named /dev/socket/_name_ and pass its fd to the\n  launched process.  The socket is created synchronously when the service starts.\n  _type_ must be \"dgram\", \"stream\" or \"seqpacket\".  _type_ may end with \"+passcred\"\n  to enable SO_PASSCRED on the socket or \"+listen\" to synchronously make it a listening socket.\n  User and group default to 0.  'seclabel' is the SELinux security context for the\n  socket.  It defaults to the service security context, as specified by\n  seclabel or computed based on the service executable file security context.\n  For native executables see libcutils android\\_get\\_control\\_socket().\n\n`stdio_to_kmsg`\n> Redirect stdout and stderr to /dev/kmsg_debug. This is useful for services that do not use native\n  Android logging during early boot and whose logs messages we want to capture. This is only enabled\n  when /dev/kmsg_debug is enabled, which is only enabled on userdebug and eng builds.\n  This is mutually exclusive with the console option, which additionally connects stdin to the\n  given console.\n\n`task_profiles <profile> [ <profile>\\* ]`\n> Set task profiles. Before Android U, the profiles are applied to the main thread of the service.\n  For Android U and later, the profiles are applied to the entire service process. This is designed\n  to replace the use of writepid option for moving a process into a cgroup.\n\n`timeout_period <seconds>`\n> Provide a timeout after which point the service will be killed. The oneshot keyword is respected\n  here, so oneshot services do not automatically restart, however all other services will.\n  This is particularly useful for creating a periodic service combined with the restart_period\n  option described above.\n\n`updatable`\n> Mark that the service can be overridden (via the 'override' option) later in\n  the boot sequence by APEXes. When a service with updatable option is started\n  before APEXes are all activated, the execution is delayed until the activation\n  is finished. A service that is not marked as updatable cannot be overridden by\n  APEXes.\n\n`user <username>`\n> Change to 'username' before exec'ing this service.\n  Currently defaults to root.  (??? probably should default to nobody)\n  As of Android M, processes should use this option even if they\n  require Linux capabilities.  Previously, to acquire Linux\n  capabilities, a process would need to run as root, request the\n  capabilities, then drop to its desired uid.  There is a new\n  mechanism through fs\\_config that allows device manufacturers to add\n  Linux capabilities to specific binaries on a file system that should\n  be used instead. This mechanism is described on\n  <http://source.android.com/devices/tech/config/filesystem.html>.  When\n  using this new mechanism, processes can use the user option to\n  select their desired uid without ever running as root.\n  As of Android O, processes can also request capabilities directly in their .rc\n  files. See the \"capabilities\" option above.\n\n`writepid <file> [ <file>\\* ]`\n> Write the child's pid to the given files when it forks. Meant for\n  cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the\n  system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.\n  '/foreground'), then the pid is written to file /dev/cpuset/_cpuset\\_name_/tasks.\n  The use of this option for moving a process into a cgroup is obsolete. Please\n  use task_profiles option instead.\n\n\nTriggers\n--------\nTriggers of an action specifies one or more conditions when satisfied\nexecute the commands in the action. A trigger encodes a single atomic\ncondition, and multiple triggers can be combined using the `&&`\noperator to form a bigger AND condition.\n\nThere are two types of triggers: event triggers and action triggers.\nAn action can have multiple property triggers but may have only one\nevent trigger.\n\nAn event trigger takes the simple form of `<event>` where `<event>` is\nthe name of a boot stage like `early-init` or `boot`. This trigger\nis satisfied when init reaches the stage via the `trigger` command or\nby the `QueueEventTrigger()` function in the init executable.\n\nA property trigger takes the form of `property:<name>=<value>`. This\ntrigger is satisfied when the property of name `<name>` is found to\nhave the value of `<value>` when the check is made. The `<value>` part\ncan be `\\*` to match with any value.\n\nThe check for property trigger is made in the following cases:\n\n* All property triggers get checked at least once when the `boot`\n  event is finished (i.e. when the last command under `on boot ...` is\nfinished).\n\n* After the one-time check, `property:a=b` is checked when property `a`\n  is newly created, or when the property is set to a new value.\n\n* Property triggers are also checked when other triggers in the same\n  action are checked. For example, `property:a=b && property:c=d` is\nchecked not only when property `a` gets a new value, but also when\nproperty `c` gets a new value (and of course when the one-time check\nis made).\n\n* Before the one-time check, `property:a=b` without an event trigger\n  is NOT checked, even if property `a` gets a new value. Care must be\ntaken since this is a non-intuitive behavior, which unfortunately\ncan't be changed due to compatibility concerns.\n\nSome examples:\n\n`on property:a=b` is executed in two cases:\n\n1. during the one-time check if property `a` is `b` at the moment.\n2. if property `a` is set to or changed to `b` after the one-time\n   check, but not before then.\n\n`on property:a=b && property:c=d` is executed in three cases:\n\n1. during the one-time check if property `a` is `b` and property `c`\n   is `d` at the moment.\n2. (after the one-time check) property `a` becomes `b` while property\n   `c` already equals to `d`.\n3. (after the one-time check) property `c` becomes `d` while property\n   `a` already equals to `b`.\n\n`on property:a=b && post-fs` is executed in one case only:\n\n1. `post-fs` is triggered while property `a` already equals to `b`.\n   This is NOT executed when property `a` becomes `b` AFTER `post-fs`.\n\nTrigger Sequence\n----------------\n\nInit uses the following sequence of triggers during early boot. These are the\nbuilt-in triggers defined in init.cpp.\n\n   1. `early-init` - The first in the sequence, triggered after cgroups has been configured\n      but before ueventd's coldboot is complete.\n   2. `init` - Triggered after coldboot is complete.\n   3. `charger` - Triggered if `ro.bootmode == \"charger\"`.\n   4. `late-init` - Triggered if `ro.bootmode != \"charger\"`, or via healthd triggering a boot\n      from charging mode.\n\nRemaining triggers are configured in `init.rc` and are not built-in. The default sequence for\nthese is specified under the \"on late-init\" event in `init.rc`. Actions internal to `init.rc`\nhave been omitted.\n\n   1. `early-fs` - Start vold.\n   2. `fs` - Vold is up. Mount partitions not marked as first-stage or latemounted.\n   3. `post-fs` - Configure anything dependent on early mounts.\n   4. `late-fs` - Mount partitions marked as latemounted.\n   5. `post-fs-data` - Mount and configure `/data`; set up encryption. `/metadata` is\n      reformatted here if it couldn't mount in first-stage init.\n   6. `post-fs-data-checkpointed` - Triggered when vold has completed committing a checkpoint\n      after an OTA update. Not triggered if checkpointing is not needed or supported.\n   7. `bpf-progs-loaded` - Starts things that want to start ASAP but need eBPF (incl. netd)\n   8. `zygote-start` - Start the zygote.\n   9. `early-boot` - After zygote has started.\n  10. `boot` - After `early-boot` actions have completed.\n\nCommands\n--------\n\n`bootchart [start|stop]`\n> Start/stop bootcharting. These are present in the default init.rc files,\n  but bootcharting is only active if the file /data/bootchart/enabled exists;\n  otherwise bootchart start/stop are no-ops.\n\n`chmod <octal-mode> <path>`\n> Change file access permissions.\n\n`chown <owner> <group> <path>`\n> Change file owner and group.\n\n`class_start <serviceclass>`\n> Start all services of the specified class if they are\n  not already running.  See the start entry for more information on\n  starting services.\n\n`class_stop <serviceclass>`\n> Stop and disable all services of the specified class if they are\n  currently running.\n\n`class_reset <serviceclass>`\n> Stop all services of the specified class if they are\n  currently running, without disabling them. They can be restarted\n  later using `class_start`.\n\n`class_restart [--only-enabled] <serviceclass>`\n> Restarts all services of the specified class. If `--only-enabled` is\n  specified, then disabled services are skipped.\n\n`copy <src> <dst>`\n> Copies a file. Similar to write, but useful for binary/large\n  amounts of data.\n  Regarding to the src file, copying from symbolic link file and world-writable\n  or group-writable files are not allowed.\n  Regarding to the dst file, the default mode created is 0600 if it does not\n  exist. And it will be truncated if dst file is a normal regular file and\n  already exists.\n\n`copy_per_line <src> <dst>`\n> Copies a file line by line. Similar to copy, but useful for dst is a sysfs node\n  that doesn't handle multiple lines of data.\n\n`domainname <name>`\n> Set the domain name.\n\n`enable <servicename>`\n> Turns a disabled service into an enabled one as if the service did not\n  specify disabled.\n  If the service is supposed to be running, it will be started now.\n  Typically used when the bootloader sets a variable that indicates a specific\n  service should be started when needed. E.g.\n\n    on property:ro.boot.myfancyhardware=1\n        enable my_fancy_service_for_my_fancy_hardware\n\n`exec [ <seclabel> [ <user> [ <group>\\* ] ] ] -- <command> [ <argument>\\* ]`\n> Fork and execute command with the given arguments. The command starts\n  after \"--\" so that an optional security context, user, and supplementary\n  groups can be provided. No other commands will be run until this one\n  finishes. _seclabel_ can be a - to denote default. Properties are expanded\n  within _argument_.\n  Init halts executing commands until the forked process exits.\n\n`exec_background [ <seclabel> [ <user> [ <group>\\* ] ] ] -- <command> [ <argument>\\* ]`\n> Fork and execute command with the given arguments. This is handled similarly\n  to the `exec` command. The difference is that init does not halt executing\n  commands until the process exits for `exec_background`.\n\n`exec_start <service>`\n> Start a given service and halt the processing of additional init commands\n  until it returns.  The command functions similarly to the `exec` command,\n  but uses an existing service definition in place of the exec argument vector.\n\n`export <name> <value>`\n> Set the environment variable _name_ equal to _value_ in the\n  global environment (which will be inherited by all processes\n  started after this command is executed)\n\n`hostname <name>`\n> Set the host name.\n\n`ifup <interface>`\n> Bring the network interface _interface_ online.\n\n`insmod [-f] <path> [<options>]`\n> Install the module at _path_ with the specified options.\n  -f: force installation of the module even if the version of the running kernel\n  and the version of the kernel for which the module was compiled do not match.\n\n`interface_start <name>` \\\n`interface_restart <name>` \\\n`interface_stop <name>`\n> Find the service that provides the interface _name_ if it exists and run the `start`, `restart`,\nor `stop` commands on it respectively.  _name_ may be either a fully qualified HIDL name, in which\ncase it is specified as `<interface>/<instance>`, or an AIDL name, in which case it is specified as\n`aidl/<interface>` for example `android.hardware.secure_element@1.1::ISecureElement/eSE1` or\n`aidl/aidl_lazy_test_1`.\n\n> Note that these commands only act on interfaces specified by the `interface` service option, not\non interfaces registered at runtime.\n\n> Example usage of these commands: \\\n`interface_start android.hardware.secure_element@1.1::ISecureElement/eSE1`\nwill start the HIDL Service that provides the `android.hardware.secure_element@1.1` and `eSI1`\ninstance. \\\n`interface_start aidl/aidl_lazy_test_1` will start the AIDL service that\nprovides the `aidl_lazy_test_1` interface.\n\n`load_exports <path>`\n> Open the file at _path_ and export global environment variables declared\n  there. Each line must be in the format `export <name> <value>`, as described\n  above.\n\n`load_system_props`\n> (This action is deprecated and no-op.)\n\n`load_persist_props`\n> Loads persistent properties when /data has been decrypted.\n  This is included in the default init.rc.\n\n`loglevel <level>`\n> Sets init's log level to the integer level, from 7 (all logging) to 0\n  (fatal logging only). The numeric values correspond to the kernel log\n  levels, but this command does not affect the kernel log level. Use the\n  `write` command to write to `/proc/sys/kernel/printk` to change that.\n  Properties are expanded within _level_.\n\n`mark_post_data`\n> (This action is deprecated and no-op.)\n\n`mkdir <path> [<mode>] [<owner>] [<group>] [encryption=<action>] [key=<key>]`\n> Create a directory at _path_, optionally with the given mode, owner, and\n  group. If not provided, the directory is created with permissions 755 and\n  owned by the root user and root group. If provided, the mode, owner and group\n  will be updated if the directory exists already.\n  If the directory does not exist, it will receive the security context from\n  the current SELinux policy or its parent if not specified in the policy. If\n  the directory exists, its security context will not be changed (even if\n  different from the policy).\n>\n> _action_ can be one of:\n>  * `None`: take no encryption action; directory will be encrypted if parent is.\n>  * `Require`: encrypt directory, abort boot process if encryption fails\n>  * `Attempt`: try to set an encryption policy, but continue if it fails\n>  * `DeleteIfNecessary`: recursively delete directory if necessary to set\n>  encryption policy.\n>\n> _key_ can be one of:\n>  * `ref`: use the systemwide DE key\n>  * `per_boot_ref`: use the key freshly generated on each boot.\n\n`mount_all [ <fstab> ] [--<option>]`\n> Calls fs\\_mgr\\_mount\\_all on the given fs\\_mgr-format fstab with optional\n  options \"early\" and \"late\".\n  With \"--early\" set, the init executable will skip mounting entries with\n  \"latemount\" flag and triggering fs encryption state event. With \"--late\" set,\n  init executable will only mount entries with \"latemount\" flag. By default,\n  no option is set, and mount\\_all will process all entries in the given fstab.\n  If the fstab parameter is not specified, fstab.${ro.boot.fstab_suffix},\n  fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for\n  under /odm/etc, /vendor/etc, or / at runtime, in that order.\n\n`mount <type> <device> <dir> [ <flag>\\* ] [<options>]`\n> Attempt to mount the named device at the directory _dir_\n  _flag_s include \"ro\", \"rw\", \"remount\", \"noatime\", ...\n  _options_ include \"barrier=1\", \"noauto\\_da\\_alloc\", \"discard\", ... as\n  a comma separated string, e.g. barrier=1,noauto\\_da\\_alloc\n\n`perform_apex_config [--bootstrap]`\n> Performs tasks after APEXes are mounted. For example, creates data directories\n  for the mounted APEXes, parses config file(s) from them, and updates linker\n  configurations. Intended to be used only once when apexd notifies the mount\n  event by setting `apexd.status` to ready.\n  Use --bootstrap when invoking in the bootstrap mount namespace.\n\n`restart [--only-if-running] <service>`\n> Stops and restarts a running service, does nothing if the service is currently\n  restarting, otherwise, it just starts the service. If \"--only-if-running\" is\n  specified, the service is only restarted if it is already running.\n\n`restorecon <path> [ <path>\\* ]`\n> Restore the file named by _path_ to the security context specified\n  in the file\\_contexts configuration.\n  Not required for directories created by the init.rc as these are\n  automatically labeled correctly by init.\n\n`restorecon_recursive <path> [ <path>\\* ]`\n> Recursively restore the directory tree named by _path_ to the\n  security contexts specified in the file\\_contexts configuration.\n\n`rm <path>`\n> Calls unlink(2) on the given path. You might want to\n  use \"exec -- rm ...\" instead (provided the system partition is\n  already mounted).\n\n`rmdir <path>`\n> Calls rmdir(2) on the given path.\n\n`readahead <file|dir> [--fully]`\n> Calls readahead(2) on the file or files within given directory.\n  Use option --fully to read the full file content.\n\n`setprop <name> <value>`\n> Set system property _name_ to _value_. Properties are expanded\n  within _value_.\n\n`setrlimit <resource> <cur> <max>`\n> Set the rlimit for a resource. This applies to all processes launched after\n  the limit is set. It is intended to be set early in init and applied globally.\n  _resource_ is best specified using its text representation ('cpu', 'rtio', etc\n  or 'RLIM_CPU', 'RLIM_RTIO', etc). It also may be specified as the int value\n  that the resource enum corresponds to.\n  _cur_ and _max_ can be 'unlimited' or '-1' to indicate an infinite rlimit.\n\n`start <service>`\n> Start a service running if it is not already running.\n  Note that this is _not_ synchronous, and even if it were, there is\n  no guarantee that the operating system's scheduler will execute the\n  service sufficiently to guarantee anything about the service's status.\n  See the `exec_start` command for a synchronous version of `start`.\n\n> This creates an important consequence that if the service offers\n  functionality to other services, such as providing a\n  communication channel, simply starting this service before those\n  services is _not_ sufficient to guarantee that the channel has\n  been set up before those services ask for it.  There must be a\n  separate mechanism to make any such guarantees.\n\n`stop <service>`\n> Stop a service from running if it is currently running.\n\n`swapon_all [ <fstab> ]`\n> Calls fs\\_mgr\\_swapon\\_all on the given fstab file.\n  If the fstab parameter is not specified, fstab.${ro.boot.fstab_suffix},\n  fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for\n  under /odm/etc, /vendor/etc, or / at runtime, in that order.\n\n> swapon_all is deprecated and will do nothing if `mmd_enabled` AConfig flag\n  in `system_performance` namespace and `mmd.zram.enabled` sysprop are enabled.\n  OEMs, who decided to use mmd to manage zram, must remove zram entry from fstab\n  or remove swapon_all call from their init script.\n\n> swapon_all continues to support setting up non-zram swap devices.\n\n> swapon_all on recovery mode continues to support setting up zram because mmd\n  does not support the recovery mode.\n\n`swapoff <path>`\n> Stops swapping to the file or block device specified by path.\n\n`symlink <target> <path>`\n> Create a symbolic link at _path_ with the value _target_\n\n`sysclktz <minutes_west_of_gmt>`\n> Set the system clock base (0 if system clock ticks in GMT)\n\n`trigger <event>`\n> Trigger an event.  Used to queue an action from another\n  action.\n\n`umount <path>`\n> Unmount the filesystem mounted at that path.\n\n`umount_all [ <fstab> ]`\n> Calls fs\\_mgr\\_umount\\_all on the given fstab file.\n  If the fstab parameter is not specified, fstab.${ro.boot.fstab_suffix},\n  fstab.${ro.hardware} or fstab.${ro.hardware.platform} will be scanned for\n  under /odm/etc, /vendor/etc, or / at runtime, in that order.\n\n`verity_update_state`\n> Internal implementation detail used to update dm-verity state and\n  set the partition._mount-point_.verified properties used by adb remount\n  because fs\\_mgr can't set them directly itself. This is required since\n  Android 12, because CtsNativeVerifiedBootTestCases will read property\n  \"partition.${partition}.verified.hash_alg\" to check that sha1 is not used.\n  See https://r.android.com/1546980 for more details.\n\n`wait <path> [ <timeout> ]`\n> Poll for the existence of the given file and return when found,\n  or the timeout has been reached. If timeout is not specified it\n  currently defaults to five seconds. The timeout value can be\n  fractional seconds, specified in floating point notation.\n\n`wait_for_prop <name> <value>`\n> Wait for system property _name_ to be _value_. Properties are expanded\n  within _value_. If property _name_ is already set to _value_, continue\n  immediately.\n\n`write <path> <content>`\n> Open the file at _path_ and write a string to it with write(2).\n  If the file does not exist, it will be created. If it does exist,\n  it will be truncated. Properties are expanded within _content_.\n\nImports\n-------\n`import <path>`\n> Parse an init config file, extending the current configuration.\n  If _path_ is a directory, each file in the directory is parsed as\n  a config file. It is not recursive, nested directories will\n  not be parsed.\n\nThe import keyword is not a command, but rather its own section,\nmeaning that it does not happen as part of an Action, but rather,\nimports are handled as a file is being parsed and follow the below logic.\n\nThere are only three times where the init executable imports .rc files:\n\n   1. When it imports `/system/etc/init/hw/init.rc` or the script indicated by the property\n      `ro.boot.init_rc` during initial boot.\n   2. When it imports `/{system,system_ext,vendor,odm,product}/etc/init/` immediately after\n      importing `/system/etc/init/hw/init.rc`.\n   3. (Deprecated) When it imports /{system,vendor,odm}/etc/init/ or .rc files\n      at specified paths during mount_all, not allowed for devices launching\n      after Q.\n\nThe order that files are imported is a bit complex for legacy reasons.  The below is guaranteed:\n\n1. `/system/etc/init/hw/init.rc` is parsed then recursively each of its imports are\n   parsed.\n2. The contents of `/system/etc/init/` are alphabetized and parsed sequentially, with imports\n   happening recursively after each file is parsed.\n3. Step 2 is repeated for `/system_ext/etc/init`, `/vendor/etc/init`, `/odm/etc/init`,\n   `/product/etc/init`\n\nThe below pseudocode may explain this more clearly:\n\n    fn Import(file)\n      Parse(file)\n      for (import : file.imports)\n        Import(import)\n\n    Import(/system/etc/init/hw/init.rc)\n    Directories = [/system/etc/init, /system_ext/etc/init, /vendor/etc/init, /odm/etc/init, /product/etc/init]\n    for (directory : Directories)\n      files = <Alphabetical order of directory's contents>\n      for (file : files)\n        Import(file)\n\nActions are executed in the order that they are parsed.  For example the `post-fs-data` action(s)\nin `/system/etc/init/hw/init.rc` are always the first `post-fs-data` action(s) to be executed in\norder of how they appear in that file.  Then the `post-fs-data` actions of the imports of\n`/system/etc/init/hw/init.rc` in the order that they're imported, etc.\n\nProperties\n----------\nInit provides state information with the following properties.\n\n`init.svc.<name>`\n> State of a named service (\"stopped\", \"stopping\", \"running\", \"restarting\")\n\n`dev.mnt.dev.<mount_point>`, `dev.mnt.blk.<mount_point>`, `dev.mnt.rootdisk.<mount_point>`\n> Block device base name associated with a *mount_point*.\n  The *mount_point* has / replaced by . and if referencing the root mount point\n  \"/\", it will use \"/root\".\n  `dev.mnt.dev.<mount_point>` indicates a block device attached to filesystems.\n    (e.g., dm-N or sdaN/mmcblk0pN to access `/sys/fs/ext4/${dev.mnt.dev.<mount_point>}/`)\n\n  `dev.mnt.blk.<mount_point>` indicates the disk partition to the above block device.\n    (e.g., sdaN / mmcblk0pN to access `/sys/class/block/${dev.mnt.blk.<mount_point>}/`)\n\n  `dev.mnt.rootdisk.<mount_point>` indicates the root disk to contain the above disk partition.\n    (e.g., sda / mmcblk0 to access `/sys/class/block/${dev.mnt.rootdisk.<mount_point>}/queue`)\n\nInit responds to properties that begin with `ctl.`.  These properties take the format of\n`ctl.[<target>_]<command>` and the _value_ of the system property is used as a parameter.  The\n_target_ is optional and specifies the service option that _value_ is meant to match with.  There is\nonly one option for _target_, `interface` which indicates that _value_ will refer to an interface\nthat a service provides and not the service name itself.\n\nFor example:\n\n`SetProperty(\"ctl.start\", \"logd\")` will run the `start` command on `logd`.\n\n`SetProperty(\"ctl.interface_start\", \"aidl/aidl_lazy_test_1\")` will run the `start` command on the\nservice that exposes the `aidl aidl_lazy_test_1` interface.\n\nNote that these\nproperties are only settable; they will have no value when read.\n\nThe _commands_ are listed below.\n\n`start` \\\n`restart` \\\n`stop` \\\nThese are equivalent to using the `start`, `restart`, and `stop` commands on the service specified\nby the _value_ of the property.\n\n`oneshot_on` and `oneshot_off` will turn on or off the _oneshot_\nflag for the service specified by the _value_ of the property.  This is\nparticularly intended for services that are conditionally lazy HALs.  When\nthey are lazy HALs, oneshot must be on, otherwise oneshot should be off.\n\n`sigstop_on` and `sigstop_off` will turn on or off the _sigstop_ feature for the service\nspecified by the _value_ of the property.  See the _Debugging init_ section below for more details\nabout this feature.\n\nBoot timing\n-----------\nInit records some boot timing information in system properties.\n\n`ro.boottime.init`\n> Time after boot in ns (via the CLOCK\\_BOOTTIME clock) at which the first\n  stage of init started.\n\n`ro.boottime.init.first_stage`\n> How long in ns it took to run first stage.\n\n`ro.boottime.init.selinux`\n> How long in ns it took to run SELinux stage.\n\n`ro.boottime.init.modules`\n> How long in ms it took to load kernel modules.\n\n`ro.boottime.init.cold_boot_wait`\n> How long init waited for ueventd's coldboot phase to end.\n\n`ro.boottime.<service-name>`\n> Time after boot in ns (via the CLOCK\\_BOOTTIME clock) that the service was\n  first started.\n\n\nBootcharting\n------------\nBootchart provides CPU and I/O load breakdown of all processes for the whole system.\nRefer to the instructions at\n <https://source.android.com/docs/core/perf/boot-times#bootchart>.\n\nOn the emulator, use the -bootchart _timeout_ option to boot with bootcharting\nactivated for _timeout_ seconds.\n\nOne thing to watch for is that the bootchart will show init as if it started\nrunning at 0s. You'll have to look at dmesg to work out when the kernel\nactually started init.\n\n\nComparing two bootcharts\n------------------------\nA handy script named compare-bootcharts.py can be used to compare the\nstart/end time of selected processes. The aforementioned grab-bootchart.sh\nwill leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.\nIf two such tarballs are preserved on the host machine under different\ndirectories, the script can list the timestamps differences. For example:\n\nUsage: system/core/init/compare-bootcharts.py _base-bootchart-dir_ _exp-bootchart-dir_\n\n    process: baseline experiment (delta) - Unit is ms (a jiffy is 10 ms on the system)\n    ------------------------------------\n    /init: 50 40 (-10)\n    /system/bin/surfaceflinger: 4320 4470 (+150)\n    /system/bin/bootanimation: 6980 6990 (+10)\n    zygote64: 10410 10640 (+230)\n    zygote: 10410 10640 (+230)\n    system_server: 15350 15150 (-200)\n    bootanimation ends at: 33790 31230 (-2560)\n\n\nSystrace\n--------\nSystrace (<http://developer.android.com/tools/help/systrace.html>) can be\nused for obtaining performance analysis reports during boot\ntime on userdebug or eng builds.\n\nHere is an example of trace events of \"wm\" and \"am\" categories:\n\n    $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py \\\n          wm am --boot\n\nThis command will cause the device to reboot. After the device is rebooted and\nthe boot sequence has finished, the trace report is obtained from the device\nand written as trace.html on the host by hitting Ctrl+C.\n\nLimitation: recording trace events is started after persistent properties are loaded, so\nthe trace events that are emitted before that are not recorded. Several\nservices such as vold, surfaceflinger, and servicemanager are affected by this\nlimitation since they are started before persistent properties are loaded.\nZygote initialization and the processes that are forked from the zygote are not\naffected.\n\n\nDebugging init\n--------------\nWhen a service starts from init, it may fail to `execv()` the service. This is not typical, and may\npoint to an error happening in the linker as the new service is started. The linker in Android\nprints its logs to `logd` and `stderr`, so they are visible in `logcat`. If the error is encountered\nbefore it is possible to access `logcat`, the `stdio_to_kmsg` service option may be used to direct\nthe logs that the linker prints to `stderr` to `kmsg`, where they can be read via a serial port.\n\nLaunching init services without init is not recommended as init sets up a significant amount of\nenvironment (user, groups, security label, capabilities, etc) that is hard to replicate manually.\n\nIf it is required to debug a service from its very start, the `sigstop` service option is added.\nThis option will send SIGSTOP to a service immediately before calling exec. This gives a window\nwhere developers can attach a debugger, strace, etc before continuing the service with SIGCONT.\n\nThis flag can also be dynamically controlled via the ctl.sigstop_on and ctl.sigstop_off properties.\n\nBelow is an example of dynamically debugging logd via the above:\n\n    stop logd\n    setprop ctl.sigstop_on logd\n    start logd\n    ps -e | grep logd\n    > logd          4343     1   18156   1684 do_signal_stop 538280 T init\n    gdbclient.py -p 4343\n    b main\n    c\n    c\n    c\n    > Breakpoint 1, main (argc=1, argv=0x7ff8c9a488) at system/core/logd/main.cpp:427\n\nBelow is an example of doing the same but with strace\n\n    stop logd\n    setprop ctl.sigstop_on logd\n    start logd\n    ps -e | grep logd\n    > logd          4343     1   18156   1684 do_signal_stop 538280 T init\n    strace -p 4343\n\n    (From a different shell)\n    kill -SIGCONT 4343\n\n    > strace runs\n\nHost Init Script Verification\n-----------------------------\n\nInit scripts are checked for correctness during build time. Specifically the below is checked.\n\n1) Well formatted action, service and import sections, e.g. no actions without a preceding 'on'\nline, and no extraneous lines after an 'import' statement.\n2) All commands map to a valid keyword and the argument count is within the correct range.\n3) All service options are valid. This is stricter than how commands are checked as the service\noptions' arguments are fully parsed, e.g. UIDs and GIDs must resolve.\n\nThere are other parts of init scripts that are only parsed at runtime and therefore not checked\nduring build time, among them are the below.\n\n1) The validity of the arguments of commands, e.g. no checking if file paths actually exist, if\nSELinux would permit the operation, or if the UIDs and GIDs resolve.\n2) No checking if a service exists or has a valid SELinux domain defined\n3) No checking if a service has not been previously defined in a different init script.\n\nEarly Init Boot Sequence\n------------------------\nThe early init boot sequence is broken up into three stages: first stage init, SELinux setup, and\nsecond stage init.\n\nFirst stage init is responsible for setting up the bare minimum requirements to load the rest of the\nsystem. Specifically this includes mounting /dev, /proc, mounting 'early mount' partitions (which\nneeds to include all partitions that contain system code, for example system and vendor), and moving\nthe system.img mount to / for devices with a ramdisk.\n\nNote that in Android Q, system.img always contains TARGET_ROOT_OUT and always is mounted at / by the\ntime first stage init finishes. Android Q will also require dynamic partitions and therefore will\nrequire using a ramdisk to boot Android. The recovery ramdisk can be used to boot to Android instead\nof a dedicated ramdisk as well.\n\nFirst stage init has three variations depending on the device configuration:\n1) For system-as-root devices, first stage init is part of /system/bin/init and a symlink at /init\npoints to /system/bin/init for backwards compatibility. These devices do not need to do anything to\nmount system.img, since it is by definition already mounted as the rootfs by the kernel.\n\n2) For devices with a ramdisk, first stage init is a static executable located at /init. These\ndevices mount system.img as /system then perform a switch root operation to move the mount at\n/system to /. The contents of the ramdisk are freed after mounting has completed.\n\n3) For devices that use recovery as a ramdisk, first stage init it contained within the shared init\nlocated at /init within the recovery ramdisk. These devices first switch root to\n/first_stage_ramdisk to remove the recovery components from the environment, then proceed the same\nas 2). Note that the decision to boot normally into Android instead of booting\ninto recovery mode is made if androidboot.force_normal_boot=1 is present in the\nkernel commandline, or in bootconfig with Android S and later.\n\nOnce first stage init finishes it execs /system/bin/init with the \"selinux_setup\" argument. This\nphase is where SELinux is optionally compiled and loaded onto the system. selinux.cpp contains more\ninformation on the specifics of this process.\n\nLastly once that phase finishes, it execs /system/bin/init again with the \"second_stage\"\nargument. At this point the main phase of init runs and continues the boot process via the init.rc\nscripts.\n"
  },
  {
    "path": "init/README.ueventd.md",
    "content": "# Ueventd\n-------\nUeventd manages `/dev`, sets permissions for `/sys`, and handles firmware uevents. It has default\nbehavior described below, along with a scripting language that allows customizing this behavior,\nbuilt on the same parser as init.\n\nUeventd has one generic customization parameter, the size of rcvbuf_size for the ueventd socket. It\nis customized by the `uevent_socket_rcvbuf_size` parameter, which takes the format of\n\n    uevent_socket_rcvbuf_size <size>\nFor example\n\n    uevent_socket_rcvbuf_size 16M\nSets the uevent socket rcvbuf_size to 16 megabytes.\n\n## Importing configuration files\n--------------------------------\nUeventd reads /system/etc/ueventd.rc, all other files are imported via the `import` command, which\ntakes the format of\n\n    import <path>\nThis command parses an ueventd config file, extending the current configuration.  If _path_ is a\ndirectory, each file in the directory is parsed as a config file. It is not recursive, nested\ndirectories will not be parsed.  Imported files are parsed after the current file has been parsed.\n\n## /dev\n----\nUeventd listens to the kernel uevent sockets and creates/deletes nodes in `/dev` based on the\nincoming add/remove uevents. It defaults to using `0600` mode and `root` user/group. It always\ncreates the nodes with the SELabel from the current loaded SEPolicy. It has three default behaviors\nfor the node path:\n\n  1. Block devices are created as `/dev/block/<basename uevent DEVPATH>`. There are symlinks created\n     to this node at `/dev/block/<type>/<parent device>/<basename uevent DEVPATH>`,\n     `/dev/block/<type>/<parent device>/by-name/<uevent PARTNAME>`, and `/dev/block/by-name/<uevent\n     PARTNAME>` if the device is a boot device.\n  2. USB devices are created as `/dev/<uevent DEVNAME>` if `DEVNAME` was specified for the uevent,\n     otherwise as `/dev/bus/usb/<bus_id>/<device_id>` where `bus_id` is `uevent MINOR / 128 + 1` and\n     `device_id` is `uevent MINOR % 128 + 1`.\n  3. All other devices are created as `/dev/<basename uevent DEVPATH>`\n\nWhether a device is considered a \"boot device\" is a bit complicated.\n\n - The recommended way to specify the boot device is to provide the \"partition UUID\" containing the\n   kernel (or, really, any parition on the boot device) and then boot device is the block device\n   containing that partition. This is passed via `androidboot.boot_part_uuid` which can be provided\n   either via the kernel bootconfig or via the kernel commandline. As an example, you could set\n   `androidboot.boot_part_uuid=12345678-abcd-ef01-0234-6789abcdef01`.\n - Though using `boot_part_uuid` is preferred, you can also specify the boot device via\n   `androidboot.boot_device` or `androidboot.boot_devices`. These can be passed via the kernel\n   bootconfig or the kernel command line. It is also possible to pass this via device tree by\n   creating a `boot_devices` property in the Android firmware node. In most cases the `boot_device`\n   is the sysfs path (without the `/sys/devices` or `/sys/devices/platform` prefix) to the closest\n   parent of the block device that's on the \"platform\" bus. As an example, if the block device is\n   `/sys/devices/platform/soc@0/7c4000.mmc/mmc_host/mmc1/mmc1:0001/block/mmcblk1` then the\n   `boot_device` is `soc@0/7c4000.mmc` since we strip off the `/sys/devices/platform` and nothing\n   past the `7c4000.mmc` directory represents a device on the \"platform\" bus. In the case that none\n   of the parents are on the \"platform\" bus there are special rules for block devices under PCI\n   and VBD (Virtual Block Device). NOTE: sysfs paths for block devices are not guaranteed to be\n   stable between kernel versions, which is one of the reasons why it is suggested to use\n   `boot_part_uuid` instead of `boot_devices`. ALSO NOTE: If more than one device matches (either\n   because multiple `boot_devices` were listed or because there was more than one block device\n   under the found sysfs directory) and these multiple matching devices provide some of the same\n   named partitions then the behavior is unspecified.\n - There is a further fallback to determine \"boot devices\" via the vstab, but providing at least\n   `boot_devices` has been required since Android 12 so this further fallback will not be described\n   here.\n\nThe permissions can be modified using a ueventd.rc script and a line that beings with `/dev`. These\nlines take the format of\n\n    devname mode uid gid [options]\nFor example\n\n    /dev/null 0666 root root\nWhen `/dev/null` is created, its mode will be set to `0666`, its user to `root` and its group to\n`root`.\n\nThe path can be modified using a ueventd.rc script and a `subsystem` and/or `driver` section.\nThere are three options to set for a subsystem or driver: the name, which device name to use,\nand which directory to place the device in. The section takes the below format of\n\n    subsystem <subsystem_name>\n      devname uevent_devname|uevent_devpath\n      [dirname <directory>]\n\n`subsystem_name` is used to match the uevent `SUBSYSTEM` value.\n\n`devname` takes one of three options:\n  1. `uevent_devname` specifies that the name of the node will be the uevent `DEVNAME`\n  2. `uevent_devpath` specifies that the name of the node will be basename uevent `DEVPATH`\n  3. `sys_name` specifies that the name of the node will be the contents of `/sys/DEVPATH/name`\n\n`dirname` is an optional parameter that specifies a directory within `/dev` where the node will be\ncreated.\n\nFor example\n\n    subsystem sound\n      devname uevent_devpath\n      dirname /dev/snd\nindicates that all uevents with `SUBSYSTEM=sound` will create nodes as `/dev/snd/<basename uevent\nDEVPATH>`.\n\nThe `driver` section has the exact same structure as a `subsystem` section, but\nwill instead match the `DRIVER` value in a `bind`/`unbind` uevent. However, the\n`driver` section will be ignored for block devices.\n\n## /sys\n----\nUeventd by default takes no action for `/sys`, however it can be instructed to set permissions for\ncertain files in `/sys` when matching uevents are generated. This is done using a ueventd.rc script\nand a line that begins with `/sys`. These lines take the format of\n\n    nodename attr mode uid gid [options]\nFor example\n\n    /sys/devices/system/cpu/cpu* cpufreq/scaling_max_freq 0664 system system\nWhen a uevent that matches the pattern `/sys/devices/system/cpu/cpu*` is sent, the matching sysfs\nattribute, `cpufreq/scaling_max_freq`, will have its mode set to `0664`, its user to to `system` and\nits group set to `system`.\n\n## Path matching\n----------------\nThe path for a `/dev` or `/sys` entry can contain a `*` anywhere in the path.\n1. If the only `*` appears at the end of the string or if the _options_ parameter is set to\n`no_fnm_pathname`, ueventd matches the entry by `fnmatch(entry_path, incoming_path, 0)`\n2. Otherwise, ueventd matches the entry by `fnmatch(entry_path, incoming_path, FNM_PATHNAME)`\n\nSee the [man page for fnmatch](https://www.man7.org/linux/man-pages/man3/fnmatch.3.html) for more\ndetails.\n\n## Firmware loading\n----------------\nUeventd by default serves firmware requests by searching through a list of firmware directories\nfor a file matching the uevent `FIRMWARE`. It then forks a process to serve this firmware to the\nkernel.\n\n`/apex/*/etc/firmware` is also searched after a list of firmware directories.\n\nThe list of firmware directories is customized by a `firmware_directories` line in a ueventd.rc\nfile. This line takes the format of\n\n    firmware_directories <firmware_directory> [ <firmware_directory> ]*\nFor example\n\n    firmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/\nAdds those 4 directories, in that order to the list of firmware directories that will be tried by\nueventd. Note that this option always accumulates to the list; it is not possible to remove previous\nentries.\n\nUeventd will wait until after `post-fs` in init, to keep retrying before believing the firmwares are\nnot present.\n\nThe exact firmware file to be served can be customized by running an external program by a\n`external_firmware_handler` line in a ueventd.rc file. This line takes the format of\n\n    external_firmware_handler <devpath> <user [group]> <path to external program>\n\nThe handler will be run as the given user, or if a group is provided, as the given user and group.\n\nFor example\n\n    external_firmware_handler /devices/leds/red/firmware/coeffs.bin system /vendor/bin/led_coeffs.bin\nWill launch `/vendor/bin/led_coeffs.bin` as the system user instead of serving the default firmware\nfor `/devices/leds/red/firmware/coeffs.bin`.\n\nThe `devpath` argument may include asterisks (`*`) to match multiple paths. For example, the string\n`/dev/*/red` will match `/dev/leds/red` as well as `/dev/lights/red`. The pattern matching follows\nthe rules of the fnmatch() function.\n\nUeventd will provide the uevent `DEVPATH` and `FIRMWARE` to this external program on the environment\nvia environment variables with the same names. Ueventd will use the string written to stdout as the\nnew name of the firmware to load. It will still look for the new firmware in the list of firmware\ndirectories stated above. It will also reject file names with `..` in them, to prevent leaving these\ndirectories. If stdout cannot be read, or the program returns with any exit code other than\n`EXIT_SUCCESS`, or the program crashes, the default firmware from the uevent will be loaded.\n\nUeventd will additionally log all messages sent to stderr from the external program to the serial\nconsole after the external program has exited.\n\nIf the kernel command-line argument `firmware_class.path` is set, this path\nwill be used first by the kernel to search for the firmware files. If found,\nueventd will not be called at all. See the\n[kernel documentation](https://www.kernel.org/doc/html/v5.10/driver-api/firmware/fw_search_path.html)\nfor more details on this feature.\n\n## Coldboot\n--------\nUeventd must create devices in `/dev` for all devices that have already sent their uevents before\nueventd has started. To do so, when ueventd is started it does what it calls a 'coldboot' on `/sys`,\nin which it writes 'add' to every 'uevent' file that it finds in `/sys/class`, `/sys/block`, and\n`/sys/devices`. This causes the kernel to regenerate the uevents for these paths, and thus for\nueventd to create the nodes.\n\nFor boot time purposes, this is done in parallel across a set of child processes. `ueventd.cpp` in\nthis directory contains documentation on how the parallelization is done.\n\nThere is an option to parallelize the restorecon function during cold boot as well. It is\nrecommended that devices use genfscon for labeling sysfs nodes. However, some devices may benefit\nfrom enabling the parallelization option:\n\n    parallel_restorecon enabled\n\nDo parallel restorecon to speed up boot process, subdirectories under `/sys`\ncan be sliced by ueventd.rc, and run on multiple process.\n    parallel_restorecon_dir <directory>\n\nFor example\n    parallel_restorecon_dir /sys\n    parallel_restorecon_dir /sys/devices\n    parallel_restorecon_dir /sys/devices/platform\n    parallel_restorecon_dir /sys/devices/platform/soc\n"
  },
  {
    "path": "init/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"CtsInitTestCases\"\n    },\n    {\n      \"name\": \"init_kill_services_test\"\n    },\n    {\n      \"name\": \"MicrodroidHostTestCases\"\n    }\n  ],\n  \"hwasan-presubmit\": [\n    {\n      \"name\": \"CtsInitTestCases\"\n    },\n    {\n      \"name\": \"init_kill_services_test\"\n    },\n    {\n      \"name\": \"MicrodroidHostTestCases\"\n    }\n  ]\n}\n"
  },
  {
    "path": "init/action.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"action.h\"\n\n#include <android-base/chrono_utils.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n\n#include \"util.h\"\n\nusing android::base::Join;\n\nnamespace android {\nnamespace init {\n\nResult<void> RunBuiltinFunction(const BuiltinFunction& function,\n                                const std::vector<std::string>& args, const std::string& context) {\n    BuiltinArguments builtin_arguments{.context = context};\n\n    builtin_arguments.args.resize(args.size());\n    builtin_arguments.args[0] = args[0];\n    for (std::size_t i = 1; i < args.size(); ++i) {\n        auto expanded_arg = ExpandProps(args[i]);\n        if (!expanded_arg.ok()) {\n            return expanded_arg.error();\n        }\n        builtin_arguments.args[i] = std::move(*expanded_arg);\n    }\n\n    return function(builtin_arguments);\n}\n\nCommand::Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,\n                 int line)\n    : func_(std::move(f)),\n      execute_in_subcontext_(execute_in_subcontext),\n      args_(std::move(args)),\n      line_(line) {}\n\nResult<void> Command::InvokeFunc(Subcontext* subcontext) const {\n    if (subcontext) {\n        if (execute_in_subcontext_) {\n            return subcontext->Execute(args_);\n        }\n\n        auto expanded_args = subcontext->ExpandArgs(args_);\n        if (!expanded_args.ok()) {\n            return expanded_args.error();\n        }\n        return RunBuiltinFunction(func_, *expanded_args, subcontext->context());\n    }\n\n    return RunBuiltinFunction(func_, args_, kInitContext);\n}\n\nResult<void> Command::CheckCommand() const {\n    BuiltinArguments builtin_arguments{.context = \"host_init_verifier\"};\n\n    builtin_arguments.args.resize(args_.size());\n    builtin_arguments.args[0] = args_[0];\n    for (size_t i = 1; i < args_.size(); ++i) {\n        auto expanded_arg = ExpandProps(args_[i]);\n        if (!expanded_arg.ok()) {\n            if (expanded_arg.error().message().find(\"doesn't exist while expanding\") !=\n                std::string::npos) {\n                // If we failed because we won't have a property, use an empty string, which is\n                // never returned from the parser, to indicate that this field cannot be checked.\n                builtin_arguments.args[i] = \"\";\n            } else {\n                return expanded_arg.error();\n            }\n        } else {\n            builtin_arguments.args[i] = std::move(*expanded_arg);\n        }\n    }\n\n    return func_(builtin_arguments);\n}\n\nstd::string Command::BuildCommandString() const {\n    return Join(args_, ' ');\n}\n\nAction::Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,\n               const std::string& event_trigger,\n               const std::map<std::string, std::string>& property_triggers)\n    : property_triggers_(property_triggers),\n      event_trigger_(event_trigger),\n      oneshot_(oneshot),\n      subcontext_(subcontext),\n      filename_(filename),\n      line_(line) {}\n\nconst BuiltinFunctionMap* Action::function_map_ = nullptr;\n\nResult<void> Action::AddCommand(std::vector<std::string>&& args, int line) {\n    if (!function_map_) {\n        return Error() << \"no function map available\";\n    }\n\n    auto map_result = function_map_->Find(args);\n    if (!map_result.ok()) {\n        return Error() << map_result.error();\n    }\n\n    commands_.emplace_back(map_result->function, map_result->run_in_subcontext, std::move(args),\n                           line);\n    return {};\n}\n\nvoid Action::AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line) {\n    commands_.emplace_back(std::move(f), false, std::move(args), line);\n}\n\nstd::size_t Action::NumCommands() const {\n    return commands_.size();\n}\n\nsize_t Action::CheckAllCommands() const {\n    size_t failures = 0;\n    for (const auto& command : commands_) {\n        if (auto result = command.CheckCommand(); !result.ok()) {\n            LOG(ERROR) << \"Command '\" << command.BuildCommandString() << \"' (\" << filename_ << \":\"\n                       << command.line() << \") failed: \" << result.error();\n            ++failures;\n        }\n    }\n    return failures;\n}\n\nvoid Action::ExecuteOneCommand(std::size_t command) const {\n    // We need a copy here since some Command execution may result in\n    // changing commands_ vector by importing .rc files through parser\n    Command cmd = commands_[command];\n    ExecuteCommand(cmd);\n}\n\nvoid Action::ExecuteAllCommands() const {\n    for (const auto& c : commands_) {\n        ExecuteCommand(c);\n    }\n}\n\nvoid Action::ExecuteCommand(const Command& command) const {\n    android::base::Timer t;\n    auto result = command.InvokeFunc(subcontext_);\n    auto duration = t.duration();\n\n    // Any action longer than 50ms will be warned to user as slow operation\n    if (!result.has_value() || duration > 50ms ||\n        android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {\n        std::string trigger_name = BuildTriggersString();\n        std::string cmd_str = command.BuildCommandString();\n\n        LOG(INFO) << \"Command '\" << cmd_str << \"' action=\" << trigger_name << \" (\" << filename_\n                  << \":\" << command.line() << \") took \" << duration.count() << \"ms and \"\n                  << (result.ok() ? \"succeeded\" : \"failed: \" + result.error().message());\n    }\n}\n\n// This function checks that all property triggers are satisfied, that is\n// for each (name, value) in property_triggers_, check that the current\n// value of the property 'name' == value.\n//\n// It takes an optional (name, value) pair, which if provided must\n// be present in property_triggers_; it skips the check of the current\n// property value for this pair.\nbool Action::CheckPropertyTriggers(const std::string& name, const std::string& value) const {\n    if (property_triggers_.empty()) {\n        return true;\n    }\n\n    if (!name.empty()) {\n        auto it = property_triggers_.find(name);\n        if (it == property_triggers_.end()) {\n            return false;\n        }\n        const auto& trigger_value = it->second;\n        if (trigger_value != \"*\" && trigger_value != value) {\n            return false;\n        }\n    }\n\n    for (const auto& [trigger_name, trigger_value] : property_triggers_) {\n        if (trigger_name != name) {\n            std::string prop_value = android::base::GetProperty(trigger_name, \"\");\n            if (trigger_value == \"*\" && !prop_value.empty()) {\n                continue;\n            }\n            if (trigger_value != prop_value) return false;\n        }\n    }\n    return true;\n}\n\nbool Action::CheckEvent(const EventTrigger& event_trigger) const {\n    return event_trigger == event_trigger_ && CheckPropertyTriggers();\n}\n\nbool Action::CheckEvent(const PropertyChange& property_change) const {\n    const auto& [name, value] = property_change;\n    return event_trigger_.empty() && CheckPropertyTriggers(name, value);\n}\n\nbool Action::CheckEvent(const BuiltinAction& builtin_action) const {\n    return this == builtin_action;\n}\n\nstd::string Action::BuildTriggersString() const {\n    std::vector<std::string> triggers;\n\n    for (const auto& [trigger_name, trigger_value] : property_triggers_) {\n        triggers.emplace_back(trigger_name + '=' + trigger_value);\n    }\n    if (!event_trigger_.empty()) {\n        triggers.emplace_back(event_trigger_);\n    }\n\n    return Join(triggers, \" && \");\n}\n\nvoid Action::DumpState() const {\n    std::string trigger_name = BuildTriggersString();\n    LOG(INFO) << \"on \" << trigger_name;\n\n    for (const auto& c : commands_) {\n        std::string cmd_str = c.BuildCommandString();\n        LOG(INFO) << \"  \" << cmd_str;\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/action.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <queue>\n#include <string>\n#include <variant>\n#include <vector>\n\n#include <android-base/strings.h>\n\n#include \"builtins.h\"\n#include \"keyword_map.h\"\n#include \"result.h\"\n#include \"subcontext.h\"\n\nnamespace android {\nnamespace init {\n\nResult<void> RunBuiltinFunction(const BuiltinFunction& function,\n                                const std::vector<std::string>& args, const std::string& context);\n\nclass Command {\n  public:\n    Command(BuiltinFunction f, bool execute_in_subcontext, std::vector<std::string>&& args,\n            int line);\n\n    Result<void> InvokeFunc(Subcontext* subcontext) const;\n    std::string BuildCommandString() const;\n    Result<void> CheckCommand() const;\n\n    int line() const { return line_; }\n\n  private:\n    BuiltinFunction func_;\n    bool execute_in_subcontext_;\n    std::vector<std::string> args_;\n    int line_;\n};\n\nusing EventTrigger = std::string;\nusing PropertyChange = std::pair<std::string, std::string>;\nusing BuiltinAction = class Action*;\n\nclass Action {\n  public:\n    Action(bool oneshot, Subcontext* subcontext, const std::string& filename, int line,\n           const std::string& event_trigger,\n           const std::map<std::string, std::string>& property_triggers);\n\n    Result<void> AddCommand(std::vector<std::string>&& args, int line);\n    void AddCommand(BuiltinFunction f, std::vector<std::string>&& args, int line);\n    size_t NumCommands() const;\n    void ExecuteOneCommand(std::size_t command) const;\n    void ExecuteAllCommands() const;\n    bool CheckEvent(const EventTrigger& event_trigger) const;\n    bool CheckEvent(const PropertyChange& property_change) const;\n    bool CheckEvent(const BuiltinAction& builtin_action) const;\n    std::string BuildTriggersString() const;\n    void DumpState() const;\n    size_t CheckAllCommands() const;\n\n    bool oneshot() const { return oneshot_; }\n    const std::string& filename() const { return filename_; }\n    int line() const { return line_; }\n    static void set_function_map(const BuiltinFunctionMap* function_map) {\n        function_map_ = function_map;\n    }\n    bool IsFromApex() const { return base::StartsWith(filename_, \"/apex/\"); }\n\n  private:\n    void ExecuteCommand(const Command& command) const;\n    bool CheckPropertyTriggers(const std::string& name = \"\",\n                               const std::string& value = \"\") const;\n\n    std::map<std::string, std::string> property_triggers_;\n    std::string event_trigger_;\n    std::vector<Command> commands_;\n    bool oneshot_;\n    Subcontext* subcontext_;\n    std::string filename_;\n    int line_;\n    static const BuiltinFunctionMap* function_map_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/action_manager.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"action_manager.h\"\n\n#include <android-base/logging.h>\n\nnamespace android {\nnamespace init {\n\nActionManager::ActionManager() : current_command_(0) {}\n\nsize_t ActionManager::CheckAllCommands() {\n    size_t failures = 0;\n    for (const auto& action : actions_) {\n        failures += action->CheckAllCommands();\n    }\n    return failures;\n}\n\nActionManager& ActionManager::GetInstance() {\n    static ActionManager instance;\n    return instance;\n}\n\nvoid ActionManager::AddAction(std::unique_ptr<Action> action) {\n    actions_.emplace_back(std::move(action));\n}\n\nvoid ActionManager::QueueEventTrigger(const std::string& trigger) {\n    auto lock = std::lock_guard{event_queue_lock_};\n    event_queue_.emplace(trigger);\n}\n\nvoid ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {\n    auto lock = std::lock_guard{event_queue_lock_};\n    event_queue_.emplace(std::make_pair(name, value));\n}\n\nvoid ActionManager::QueueAllPropertyActions() {\n    QueuePropertyChange(\"\", \"\");\n}\n\nvoid ActionManager::QueueBuiltinAction(BuiltinFunction func, const std::string& name) {\n    auto lock = std::lock_guard{event_queue_lock_};\n    auto action = std::make_unique<Action>(true, nullptr, \"<Builtin Action>\", 0, name,\n                                           std::map<std::string, std::string>{});\n    action->AddCommand(std::move(func), {name}, 0);\n\n    event_queue_.emplace(action.get());\n    actions_.emplace_back(std::move(action));\n}\n\nvoid ActionManager::ExecuteOneCommand() {\n    {\n        auto lock = std::lock_guard{event_queue_lock_};\n        // Loop through the event queue until we have an action to execute\n        while (current_executing_actions_.empty() && !event_queue_.empty()) {\n            for (const auto& action : actions_) {\n                if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },\n                               event_queue_.front())) {\n                    current_executing_actions_.emplace(action.get());\n                }\n            }\n            event_queue_.pop();\n        }\n    }\n\n    if (current_executing_actions_.empty()) {\n        return;\n    }\n\n    auto action = current_executing_actions_.front();\n\n    if (current_command_ == 0) {\n        std::string trigger_name = action->BuildTriggersString();\n        LOG(INFO) << \"processing action (\" << trigger_name << \") from (\" << action->filename()\n                  << \":\" << action->line() << \")\";\n    }\n\n    action->ExecuteOneCommand(current_command_);\n\n    // If this was the last command in the current action, then remove\n    // the action from the executing list.\n    // If this action was oneshot, then also remove it from actions_.\n    ++current_command_;\n    if (current_command_ == action->NumCommands()) {\n        current_executing_actions_.pop();\n        current_command_ = 0;\n        if (action->oneshot()) {\n            auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };\n            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),\n                           actions_.end());\n        }\n    }\n}\n\nbool ActionManager::HasMoreCommands() const {\n    auto lock = std::lock_guard{event_queue_lock_};\n    return !current_executing_actions_.empty() || !event_queue_.empty();\n}\n\nvoid ActionManager::DumpState() const {\n    for (const auto& a : actions_) {\n        a->DumpState();\n    }\n}\n\nvoid ActionManager::ClearQueue() {\n    auto lock = std::lock_guard{event_queue_lock_};\n    // We are shutting down so don't claim the oneshot builtin actions back\n    current_executing_actions_ = {};\n    event_queue_ = {};\n    current_command_ = 0;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/action_manager.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <mutex>\n#include <string>\n#include <vector>\n\n#include <android-base/thread_annotations.h>\n\n#include \"action.h\"\n#include \"builtins.h\"\n\nnamespace android {\nnamespace init {\n\nclass ActionManager {\n  public:\n    static ActionManager& GetInstance();\n\n    // Exposed for testing\n    ActionManager();\n    size_t CheckAllCommands();\n\n    void AddAction(std::unique_ptr<Action> action);\n    template <class UnaryPredicate>\n    void RemoveActionIf(UnaryPredicate predicate) {\n        actions_.erase(std::remove_if(actions_.begin(), actions_.end(), predicate), actions_.end());\n    }\n    void QueueEventTrigger(const std::string& trigger);\n    void QueuePropertyChange(const std::string& name, const std::string& value);\n    void QueueAllPropertyActions();\n    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);\n    void ExecuteOneCommand();\n    bool HasMoreCommands() const;\n    void DumpState() const;\n    void ClearQueue();\n    auto size() const { return actions_.size(); }\n\n  private:\n    ActionManager(ActionManager const&) = delete;\n    void operator=(ActionManager const&) = delete;\n\n    std::vector<std::unique_ptr<Action>> actions_;\n    std::queue<std::variant<EventTrigger, PropertyChange, BuiltinAction>> event_queue_\n            GUARDED_BY(event_queue_lock_);\n    mutable std::mutex event_queue_lock_;\n    std::queue<const Action*> current_executing_actions_;\n    std::size_t current_command_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/action_parser.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"action_parser.h\"\n\n#include <ctype.h>\n\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n\n#ifdef INIT_FULL_SOURCES\n#include \"property_service.h\"\n#include \"selinux.h\"\n#else\n#include \"host_init_stubs.h\"\n#endif\n\nusing android::base::GetBoolProperty;\nusing android::base::StartsWith;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nbool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {\n    static bool enabled = GetBoolProperty(\"ro.actionable_compatible_property.enabled\", false);\n\n    if (subcontext == nullptr || !enabled) {\n        return true;\n    }\n\n    static constexpr const char* kPartnerPrefixes[] = {\n            \"init.svc.vendor.\", \"ro.vendor.\",    \"persist.vendor.\",\n            \"vendor.\",          \"init.svc.odm.\", \"ro.odm.\",\n            \"persist.odm.\",     \"odm.\",          \"ro.boot.\",\n    };\n\n    for (const auto& prefix : kPartnerPrefixes) {\n        if (android::base::StartsWith(prop_name, prefix)) {\n            return true;\n        }\n    }\n\n    return CanReadProperty(subcontext->context(), prop_name);\n}\n\nResult<void> ParsePropertyTrigger(const std::string& trigger, Subcontext* subcontext,\n                                  std::map<std::string, std::string>* property_triggers) {\n    const static std::string prop_str(\"property:\");\n    std::string prop_name(trigger.substr(prop_str.length()));\n    size_t equal_pos = prop_name.find('=');\n    if (equal_pos == std::string::npos) {\n        return Error() << \"property trigger found without matching '='\";\n    }\n\n    std::string prop_value(prop_name.substr(equal_pos + 1));\n    prop_name.erase(equal_pos);\n\n    if (!IsActionableProperty(subcontext, prop_name)) {\n        return Error() << \"unexported property trigger found: \" << prop_name;\n    }\n\n    if (auto [it, inserted] = property_triggers->emplace(prop_name, prop_value); !inserted) {\n        return Error() << \"multiple property triggers found for same property\";\n    }\n    return {};\n}\n\nResult<void> ValidateEventTrigger(const std::string& event_trigger) {\n    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {\n        for (const char& c : event_trigger) {\n            if (c != '_' && c != '-' && !std::isalnum(c)) {\n                return Error() << \"Illegal character '\" << c << \"' in '\" << event_trigger << \"'\";\n            }\n        }\n    }\n    return {};\n}\n\nResult<void> ParseTriggers(const std::vector<std::string>& args, Subcontext* subcontext,\n                           std::string* event_trigger,\n                           std::map<std::string, std::string>* property_triggers) {\n    const static std::string prop_str(\"property:\");\n    for (std::size_t i = 0; i < args.size(); ++i) {\n        if (args[i].empty()) {\n            return Error() << \"empty trigger is not valid\";\n        }\n\n        if (i % 2) {\n            if (args[i] != \"&&\") {\n                return Error() << \"&& is the only symbol allowed to concatenate actions\";\n            } else {\n                continue;\n            }\n        }\n\n        if (!args[i].compare(0, prop_str.length(), prop_str)) {\n            if (auto result = ParsePropertyTrigger(args[i], subcontext, property_triggers);\n                !result.ok()) {\n                return result;\n            }\n        } else {\n            if (!event_trigger->empty()) {\n                return Error() << \"multiple event triggers are not allowed\";\n            }\n            if (auto result = ValidateEventTrigger(args[i]); !result.ok()) {\n                return result;\n            }\n\n            *event_trigger = args[i];\n        }\n    }\n\n    return {};\n}\n\n}  // namespace\n\nResult<void> ActionParser::ParseSection(std::vector<std::string>&& args,\n                                        const std::string& filename, int line) {\n    std::vector<std::string> triggers(args.begin() + 1, args.end());\n    if (triggers.size() < 1) {\n        return Error() << \"Actions must have a trigger\";\n    }\n\n    Subcontext* action_subcontext = nullptr;\n    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {\n        action_subcontext = subcontext_;\n    }\n\n    // We support 'on' for only Vendor APEXes from /{vendor, odm}.\n    // It is to prevent mainline modules from using 'on' triggers because events/properties are\n    // not stable for mainline modules.\n    // Note that this relies on Subcontext::PathMatchesSubcontext() to identify Vendor APEXes.\n    if (StartsWith(filename, \"/apex/\") && !action_subcontext) {\n        return Error() << \"ParseSection() failed: 'on' is supported for only Vendor APEXes.\";\n    }\n\n    std::string event_trigger;\n    std::map<std::string, std::string> property_triggers;\n\n    if (auto result =\n                ParseTriggers(triggers, action_subcontext, &event_trigger, &property_triggers);\n        !result.ok()) {\n        return Error() << \"ParseTriggers() failed: \" << result.error();\n    }\n\n    auto action = std::make_unique<Action>(false, action_subcontext, filename, line, event_trigger,\n                                           property_triggers);\n\n    action_ = std::move(action);\n    return {};\n}\n\nResult<void> ActionParser::ParseLineSection(std::vector<std::string>&& args, int line) {\n    return action_ ? action_->AddCommand(std::move(args), line) : Result<void>{};\n}\n\nResult<void> ActionParser::EndSection() {\n    if (action_ && action_->NumCommands() > 0) {\n        action_manager_->AddAction(std::move(action_));\n    }\n\n    return {};\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/action_parser.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_ACTION_PARSER_H\n#define _INIT_ACTION_PARSER_H\n\n#include <string>\n#include <vector>\n\n#include \"action.h\"\n#include \"action_manager.h\"\n#include \"parser.h\"\n#include \"subcontext.h\"\n\nnamespace android {\nnamespace init {\n\nclass ActionParser : public SectionParser {\n  public:\n    ActionParser(ActionManager* action_manager, Subcontext* subcontext)\n        : action_manager_(action_manager), subcontext_(subcontext), action_(nullptr) {}\n    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,\n                              int line) override;\n    Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;\n    Result<void> EndSection() override;\n\n  private:\n    ActionManager* action_manager_;\n    Subcontext* subcontext_;\n    std::unique_ptr<Action> action_;\n};\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/apex_init_util.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"apex_init_util.h\"\n\n#include <dirent.h>\n#include <glob.h>\n\n#include <set>\n#include <vector>\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/result.h>\n#include <android-base/strings.h>\n\n#include \"action_manager.h\"\n#include \"init.h\"\n#include \"parser.h\"\n#include \"service_list.h\"\n#include \"util.h\"\n\nnamespace android {\nnamespace init {\n\nstatic Result<std::vector<std::string>> CollectRcScriptsFromApex(\n        const std::string& apex_name, const std::set<std::string>& skip_apexes) {\n    glob_t glob_result;\n    // Pattern uses \"*rc\" instead of \".rc\" because APEXes can have versioned RC files\n    // like foo.34rc.\n    std::string glob_pattern =\n            apex_name.empty() ? \"/apex/*/etc/*rc\" : \"/apex/\" + apex_name + \"/etc/*rc\";\n\n    const int ret = glob(glob_pattern.c_str(), GLOB_MARK, nullptr, &glob_result);\n    if (ret != 0 && ret != GLOB_NOMATCH) {\n        globfree(&glob_result);\n        return Error() << \"Glob pattern '\" << glob_pattern << \"' failed\";\n    }\n    std::vector<std::string> configs;\n    for (size_t i = 0; i < glob_result.gl_pathc; i++) {\n        std::string path = glob_result.gl_pathv[i];\n\n        // Filter out directories\n        if (path.back() == '/') {\n            continue;\n        }\n\n        // Get apex name from path.\n        std::vector<std::string> paths = android::base::Split(path, \"/\");\n        if (paths.size() < 3) {\n            continue;\n        }\n        const std::string& apex_name = paths[2];\n\n        // Filter out /apex/<name>@<ver> paths. The paths are bind-mounted to\n        // /apex/<name> paths, so unless we filter them out, we will parse the\n        // same file twice.\n        if (apex_name.find('@') != std::string::npos) {\n            continue;\n        }\n\n        // Filter out skip_set apexes\n        if (skip_apexes.count(apex_name) > 0) {\n            continue;\n        }\n        configs.push_back(path);\n    }\n    globfree(&glob_result);\n    return configs;\n}\n\nstd::set<std::string> GetApexListFrom(const std::string& apex_dir) {\n    std::set<std::string> apex_list;\n    auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(apex_dir.c_str()), closedir);\n    if (!dirp) {\n        return apex_list;\n    }\n    struct dirent* entry;\n    while ((entry = readdir(dirp.get())) != nullptr) {\n        if (entry->d_type != DT_DIR) continue;\n\n        const char* name = entry->d_name;\n        if (name[0] == '.') continue;\n        if (strchr(name, '@') != nullptr) continue;\n        if (strcmp(name, \"sharedlibs\") == 0) continue;\n        apex_list.insert(name);\n    }\n    return apex_list;\n}\n\nstatic int GetCurrentSdk() {\n    bool is_preview = base::GetProperty(\"ro.build.version.codename\", \"\") != \"REL\";\n    if (is_preview) {\n        return __ANDROID_API_FUTURE__;\n    }\n    return android::base::GetIntProperty(\"ro.build.version.sdk\", __ANDROID_API_FUTURE__);\n}\n\nstatic Result<void> ParseRcScripts(const std::vector<std::string>& files) {\n    if (files.empty()) {\n        return {};\n    }\n    // APEXes can have versioned RC files. These should be filtered based on\n    // SDK version.\n    static int sdk = GetCurrentSdk();\n    auto filtered = FilterVersionedConfigs(files, sdk);\n    if (filtered.empty()) {\n        return {};\n    }\n\n    Parser parser =\n            CreateApexConfigParser(ActionManager::GetInstance(), ServiceList::GetInstance());\n    std::vector<std::string> errors;\n    for (const auto& c : filtered) {\n        auto result = parser.ParseConfigFile(c);\n        // We should handle other config files even when there's an error.\n        if (!result.ok()) {\n            errors.push_back(result.error().message());\n        }\n    }\n    if (!errors.empty()) {\n        return Error() << \"Unable to parse apex configs: \" << base::Join(errors, \"|\");\n    }\n    return {};\n}\n\nResult<void> ParseRcScriptsFromApex(const std::string& apex_name) {\n    auto configs = OR_RETURN(CollectRcScriptsFromApex(apex_name, /*skip_apexes=*/{}));\n    return ParseRcScripts(configs);\n}\n\nResult<void> ParseRcScriptsFromAllApexes(bool bootstrap) {\n    std::set<std::string> skip_apexes;\n    if (!bootstrap) {\n        // In case we already loaded config files from bootstrap APEXes, we need to avoid loading\n        // them again. We can get the list of bootstrap APEXes by scanning /bootstrap-apex and\n        // skip them in CollectRcScriptsFromApex.\n        skip_apexes = GetApexListFrom(\"/bootstrap-apex\");\n    }\n    auto configs = OR_RETURN(CollectRcScriptsFromApex(/*apex_name=*/\"\", skip_apexes));\n    return ParseRcScripts(configs);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/apex_init_util.h",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\n// Scans apex_dir (/apex) to get the list of active APEXes.\nstd::set<std::string> GetApexListFrom(const std::string& apex_dir);\n\n// Parse all RC scripts for a given apex.\nResult<void> ParseRcScriptsFromApex(const std::string& apex_name);\n\n// Parse all RC scripts for all apexes under /apex.\nResult<void> ParseRcScriptsFromAllApexes(bool bootstrap);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/block_dev_initializer.cpp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <chrono>\n#include <string_view>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n\n#include \"block_dev_initializer.h\"\n\nnamespace android {\nnamespace init {\n\nusing android::base::Timer;\nusing namespace std::chrono_literals;\n\nBlockDevInitializer::BlockDevInitializer() : uevent_listener_(16 * 1024 * 1024) {\n    auto boot_devices = android::fs_mgr::GetBootDevices();\n    device_handler_ = std::make_unique<DeviceHandler>(\n            std::vector<Permissions>{}, std::vector<SysfsPermissions>{}, std::vector<Subsystem>{},\n            std::vector<Subsystem>{}, std::move(boot_devices), android::fs_mgr::GetBootPartUuid(),\n            false);\n}\n\n// If boot_part_uuid is specified, use it to set boot_devices\n//\n// When `androidboot.boot_part_uuid` is specified then that's the partition UUID\n// of the kernel. Look for that partition and then set `boot_devices` to be\n// exactly one item: the block device containing that partition.\n//\n// NOTE that `boot_part_uuid` is only specified on newer devices. Older devices\n// specified `boot_devices` directly.\nbool BlockDevInitializer::InitBootDevicesFromPartUuid() {\n    bool uuid_check_done = false;\n\n    auto boot_part_callback = [&, this](const Uevent& uevent) -> ListenerAction {\n        uuid_check_done = device_handler_->CheckUeventForBootPartUuid(uevent);\n        return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;\n    };\n\n    // Re-run already arrived uevents looking for the boot partition UUID.\n    //\n    // NOTE: If we're not using the boot partition UUID to find the boot\n    // device then the first uevent we analyze will cause us to stop looking\n    // and set `uuid_check_done`. This will shortcut all of the UUID logic.\n    // Replaying one uevent is not expected to be slow.\n    uevent_listener_.RegenerateUevents(boot_part_callback);\n\n    // If we're not done looking, poll for uevents for longer\n    if (!uuid_check_done) {\n        Timer t;\n        uevent_listener_.Poll(boot_part_callback, 10s);\n        LOG(INFO) << \"Wait for boot partition returned after \" << t;\n    }\n\n    // Give a nicer error message if we were expecting to find the kernel boot\n    // partition but didn't. Later code would fail too but the message there\n    // is a bit further from the root cause of the problem.\n    if (!uuid_check_done) {\n        LOG(ERROR) << __PRETTY_FUNCTION__ << \": boot partition not found after polling timeout.\";\n        return false;\n    }\n\n    return true;\n}\n\nbool BlockDevInitializer::InitDeviceMapper() {\n    return InitMiscDevice(\"device-mapper\");\n}\n\nbool BlockDevInitializer::InitDmUser(const std::string& name) {\n    return InitMiscDevice(\"dm-user!\" + name);\n}\n\nbool BlockDevInitializer::InitMiscDevice(const std::string& name) {\n    const std::string dm_path = \"/devices/virtual/misc/\" + name;\n    bool found = false;\n    auto dm_callback = [this, &dm_path, &found](const Uevent& uevent) {\n        if (uevent.path == dm_path) {\n            device_handler_->HandleUevent(uevent);\n            found = true;\n            return ListenerAction::kStop;\n        }\n        return ListenerAction::kContinue;\n    };\n    uevent_listener_.RegenerateUeventsForPath(\"/sys\" + dm_path, dm_callback);\n    if (!found) {\n        LOG(INFO) << name << \" device not found in /sys, waiting for its uevent\";\n        Timer t;\n        uevent_listener_.Poll(dm_callback, 10s);\n        LOG(INFO) << \"Wait for \" << name << \" returned after \" << t;\n    }\n    if (!found) {\n        LOG(ERROR) << name << \" device not found after polling timeout\";\n        return false;\n    }\n    return true;\n}\n\nListenerAction BlockDevInitializer::HandleUevent(const Uevent& uevent,\n                                                 std::set<std::string>* devices) {\n    // Ignore everything that is not a block device.\n    if (uevent.subsystem != \"block\") {\n        return ListenerAction::kContinue;\n    }\n\n    auto name = uevent.partition_name;\n    if (name.empty()) {\n        size_t base_idx = uevent.path.rfind('/');\n        if (base_idx == std::string::npos) {\n            return ListenerAction::kContinue;\n        }\n        name = uevent.path.substr(base_idx + 1);\n    }\n\n    auto iter = devices->find(name);\n    if (iter == devices->end()) {\n        auto partition_name = DeviceHandler::GetPartitionNameForDevice(uevent.device_name);\n        if (!partition_name.empty()) {\n            iter = devices->find(partition_name);\n        }\n        if (iter == devices->end()) {\n            return ListenerAction::kContinue;\n        }\n    }\n\n    LOG(VERBOSE) << __PRETTY_FUNCTION__ << \": found partition: \" << name;\n\n    // Remove the partition from the list of partitions we're waiting for.\n    //\n    // Partitions that we're waiting for here are expected to be on the boot\n    // device, so only remove from the list if they're on the boot device.\n    // This prevents us from being confused if there are multiple disks (some\n    // perhaps connected via USB) that have matching partition names.\n    //\n    // ...but...\n    //\n    // Some products (especialy emulators) don't seem to set up boot_devices\n    // or possibly not all the partitions that we need to wait for are on the\n    // specified boot device. Thus, only require partitions to be on the boot\n    // device in \"strict\" mode, which should be used on newer systems.\n    if (device_handler_->IsBootDevice(uevent) || !device_handler_->IsBootDeviceStrict()) {\n        devices->erase(iter);\n    }\n\n    device_handler_->HandleUevent(uevent);\n    return devices->empty() ? ListenerAction::kStop : ListenerAction::kContinue;\n}\n\n// Wait for partitions that are expected to be on the \"boot device\" to initialize.\n//\n// Wait (for up to 10 seconds) for partitions passed in `devices` to show up.\n// All block devices found while waiting will be initialized, which includes\n// creating symlinks for them in /dev/block. Once all `devices` are found we'll\n// return success (true). If any devices aren't found we'll return failure\n// (false). As devices are found they will be removed from `devices`.\n//\n// The contents of `devices` is the names of the partitions. This can be:\n// - The `partition_name` reported by a uevent, or the final component in the\n//   `path` reported by a uevent if the `partition_name` is blank.\n// - The result of DeviceHandler::GetPartitionNameForDevice() on the\n//   `device_name` reported by a uevent.\n//\n// NOTE: on newer systems partitions _must_ be on the \"boot device\". See\n// comments inside HandleUevent().\nbool BlockDevInitializer::InitDevices(std::set<std::string> devices) {\n    auto uevent_callback = [&, this](const Uevent& uevent) -> ListenerAction {\n        return HandleUevent(uevent, &devices);\n    };\n    uevent_listener_.RegenerateUevents(uevent_callback);\n\n    // UeventCallback() will remove found partitions from |devices|. So if it\n    // isn't empty here, it means some partitions are not found.\n    if (!devices.empty()) {\n        LOG(INFO) << __PRETTY_FUNCTION__\n                  << \": partition(s) not found in /sys, waiting for their uevent(s): \"\n                  << android::base::Join(devices, \", \");\n        Timer t;\n        uevent_listener_.Poll(uevent_callback, 10s);\n        LOG(INFO) << \"Wait for partitions returned after \" << t;\n    }\n\n    if (!devices.empty()) {\n        LOG(ERROR) << __PRETTY_FUNCTION__ << \": partition(s) not found after polling timeout: \"\n                   << android::base::Join(devices, \", \");\n        return false;\n    }\n    return true;\n}\n\n// Creates \"/dev/block/dm-XX\" for dm nodes by running coldboot on /sys/block/dm-XX.\nbool BlockDevInitializer::InitDmDevice(const std::string& device) {\n    const std::string device_name(basename(device.c_str()));\n    const std::string syspath = \"/sys/block/\" + device_name;\n    return InitDevice(syspath, device_name);\n}\n\nbool BlockDevInitializer::InitPlatformDevice(const std::string& dev_name) {\n    return InitDevice(\"/sys/devices/platform\", dev_name);\n}\n\nbool BlockDevInitializer::InitHvcDevice(const std::string& dev_name) {\n    return InitDevice(\"/sys/devices/virtual/tty\", dev_name);\n}\n\nbool BlockDevInitializer::InitDevice(const std::string& syspath, const std::string& device_name) {\n    bool found = false;\n\n    auto uevent_callback = [&device_name, this, &found](const Uevent& uevent) {\n        if (uevent.device_name == device_name) {\n            LOG(VERBOSE) << \"Creating device : \" << device_name;\n            device_handler_->HandleUevent(uevent);\n            found = true;\n            return ListenerAction::kStop;\n        }\n        return ListenerAction::kContinue;\n    };\n\n    uevent_listener_.RegenerateUeventsForPath(syspath, uevent_callback);\n    if (!found) {\n        LOG(INFO) << \"device '\" << device_name << \"' not found in /sys, waiting for its uevent\";\n        Timer t;\n        uevent_listener_.Poll(uevent_callback, 10s);\n        LOG(INFO) << \"wait for device '\" << device_name << \"' returned after \" << t;\n    }\n    if (!found) {\n        LOG(ERROR) << \"device '\" << device_name << \"' not found after polling timeout\";\n        return false;\n    }\n    return true;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/block_dev_initializer.h",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <memory>\n#include <set>\n#include <string>\n\n#include \"devices.h\"\n#include \"uevent_listener.h\"\n\nnamespace android {\nnamespace init {\n\n// TODO: should this be renamed to FirstStageDevInitialize?\nclass BlockDevInitializer final {\n  public:\n    BlockDevInitializer();\n\n    bool InitBootDevicesFromPartUuid();\n    bool InitDeviceMapper();\n    bool InitDmUser(const std::string& name);\n    bool InitDevices(std::set<std::string> devices);\n    bool InitDmDevice(const std::string& device);\n    bool InitPlatformDevice(const std::string& device);\n    bool InitHvcDevice(const std::string& device);\n\n  private:\n    ListenerAction HandleUevent(const Uevent& uevent, std::set<std::string>* devices);\n\n    bool InitMiscDevice(const std::string& name);\n    bool InitDevice(const std::string& syspath, const std::string& device);\n\n    std::unique_ptr<DeviceHandler> device_handler_;\n    UeventListener uevent_listener_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/bootchart.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"bootchart.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/utsname.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <condition_variable>\n#include <memory>\n#include <mutex>\n#include <thread>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n\nusing android::base::StringPrintf;\nusing android::base::boot_clock;\nusing namespace std::chrono_literals;\n\nnamespace android {\nnamespace init {\n\nstatic std::thread* g_bootcharting_thread;\n\nstatic std::mutex g_bootcharting_finished_mutex;\nstatic std::condition_variable g_bootcharting_finished_cv;\nstatic bool g_bootcharting_finished;\n\nstatic long long get_uptime_jiffies() {\n    constexpr int64_t kNanosecondsPerJiffy = 10000000;\n    boot_clock::time_point uptime = boot_clock::now();\n    return uptime.time_since_epoch().count() / kNanosecondsPerJiffy;\n}\n\nstatic std::unique_ptr<FILE, decltype(&fclose)> fopen_unique(const char* filename,\n                                                             const char* mode) {\n  std::unique_ptr<FILE, decltype(&fclose)> result(fopen(filename, mode), fclose);\n  if (!result) PLOG(ERROR) << \"bootchart: failed to open \" << filename;\n  return result;\n}\n\nstatic void log_header() {\n  char date[32];\n  time_t now_t = time(NULL);\n  struct tm now = *localtime(&now_t);\n  strftime(date, sizeof(date), \"%F %T\", &now);\n\n  utsname uts;\n  if (uname(&uts) == -1) return;\n\n  std::string fingerprint = android::base::GetProperty(\"ro.build.fingerprint\", \"\");\n  if (fingerprint.empty()) return;\n\n  std::string kernel_cmdline;\n  android::base::ReadFileToString(\"/proc/cmdline\", &kernel_cmdline);\n\n  auto fp = fopen_unique(\"/data/bootchart/header\", \"we\");\n  if (!fp) return;\n  fprintf(&*fp, \"version = Android init 0.8\\n\");\n  fprintf(&*fp, \"title = Boot chart for Android (%s)\\n\", date);\n  fprintf(&*fp, \"system.uname = %s %s %s %s\\n\", uts.sysname, uts.release, uts.version, uts.machine);\n  fprintf(&*fp, \"system.release = %s\\n\", fingerprint.c_str());\n  // TODO: use /proc/cpuinfo \"model name\" line for x86, \"Processor\" line for arm.\n  fprintf(&*fp, \"system.cpu = %s\\n\", uts.machine);\n  fprintf(&*fp, \"system.kernel.options = %s\\n\", kernel_cmdline.c_str());\n}\n\nstatic void log_uptime(FILE* log) {\n  fprintf(log, \"%lld\\n\", get_uptime_jiffies());\n}\n\nstatic void log_file(FILE* log, const char* procfile) {\n  log_uptime(log);\n\n  std::string content;\n  if (android::base::ReadFileToString(procfile, &content)) {\n    fprintf(log, \"%s\\n\", content.c_str());\n  }\n}\n\nstatic void log_processes(FILE* log) {\n  log_uptime(log);\n\n  std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(\"/proc\"), closedir);\n  struct dirent* entry;\n  while ((entry = readdir(dir.get())) != NULL) {\n    // Only match numeric values.\n    int pid = atoi(entry->d_name);\n    if (pid == 0) continue;\n\n    // /proc/<pid>/stat only has truncated task names, so get the full\n    // name from /proc/<pid>/cmdline.\n    std::string cmdline;\n    android::base::ReadFileToString(StringPrintf(\"/proc/%d/cmdline\", pid), &cmdline);\n    const char* full_name = cmdline.c_str(); // So we stop at the first NUL.\n\n    // Read process stat line.\n    std::string stat;\n    if (android::base::ReadFileToString(StringPrintf(\"/proc/%d/stat\", pid), &stat)) {\n      if (!cmdline.empty()) {\n        // Substitute the process name with its real name.\n        size_t open = stat.find('(');\n        size_t close = stat.find_last_of(')');\n        if (open != std::string::npos && close != std::string::npos) {\n          stat.replace(open + 1, close - open - 1, full_name);\n        }\n      }\n      fputs(stat.c_str(), log);\n    }\n  }\n\n  fputc('\\n', log);\n}\n\nstatic void bootchart_thread_main() {\n  LOG(INFO) << \"Bootcharting started\";\n\n  // Unshare the mount namespace of this thread so that the init process itself can switch\n  // the mount namespace later while this thread is still running.\n  // Otherwise, setns() call invoked as part of `enter_default_mount_ns` fails with EINVAL.\n  //\n  // Note that after unshare()'ing the mount namespace from the main thread, this thread won't\n  // receive mount/unmount events from the other mount namespace unless the events are happening\n  // from under a sharable mount.\n  //\n  // The bootchart thread is safe to unshare the mount namespace because it only reads from /proc\n  // and write to /data which are not private mounts.\n  if (unshare(CLONE_NEWNS) == -1) {\n      PLOG(ERROR) << \"Cannot create mount namespace\";\n      return;\n  }\n  // Open log files.\n  auto stat_log = fopen_unique(\"/data/bootchart/proc_stat.log\", \"we\");\n  if (!stat_log) return;\n  auto proc_log = fopen_unique(\"/data/bootchart/proc_ps.log\", \"we\");\n  if (!proc_log) return;\n  auto disk_log = fopen_unique(\"/data/bootchart/proc_diskstats.log\", \"we\");\n  if (!disk_log) return;\n\n  log_header();\n\n  while (true) {\n    {\n      std::unique_lock<std::mutex> lock(g_bootcharting_finished_mutex);\n      g_bootcharting_finished_cv.wait_for(lock, 200ms);\n      if (g_bootcharting_finished) break;\n    }\n\n    log_file(&*stat_log, \"/proc/stat\");\n    log_file(&*disk_log, \"/proc/diskstats\");\n    log_processes(&*proc_log);\n  }\n\n  LOG(INFO) << \"Bootcharting finished\";\n}\n\nstatic Result<void> do_bootchart_start() {\n    // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.\n    std::string start;\n    if (!android::base::ReadFileToString(\"/data/bootchart/enabled\", &start)) {\n        LOG(VERBOSE) << \"Not bootcharting\";\n        return {};\n    }\n\n    g_bootcharting_thread = new std::thread(bootchart_thread_main);\n    return {};\n}\n\nstatic Result<void> do_bootchart_stop() {\n    if (!g_bootcharting_thread) return {};\n\n    // Tell the worker thread it's time to quit.\n    {\n        std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);\n        g_bootcharting_finished = true;\n        g_bootcharting_finished_cv.notify_one();\n    }\n\n    g_bootcharting_thread->join();\n    delete g_bootcharting_thread;\n    g_bootcharting_thread = nullptr;\n    return {};\n}\n\nResult<void> do_bootchart(const BuiltinArguments& args) {\n    if (args[1] == \"start\") return do_bootchart_start();\n    return do_bootchart_stop();\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/bootchart.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _BOOTCHART_H\n#define _BOOTCHART_H\n\n#include <string>\n#include <vector>\n\n#include \"builtin_arguments.h\"\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nResult<void> do_bootchart(const BuiltinArguments& args);\n\n}  // namespace init\n}  // namespace android\n\n#endif /* _BOOTCHART_H */\n"
  },
  {
    "path": "init/builtin_arguments.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_BUILTIN_ARGUMENTS_H\n#define _INIT_BUILTIN_ARGUMENTS_H\n\n#include <string>\n#include <vector>\n\nnamespace android {\nnamespace init {\n\nstruct BuiltinArguments {\n    const std::string& operator[](std::size_t i) const { return args[i]; }\n    auto begin() const { return args.begin(); }\n    auto end() const { return args.end(); }\n    auto size() const { return args.size(); }\n\n    std::vector<std::string> args;\n    const std::string& context;\n};\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/builtins.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"builtins.h\"\n\n#include <android/api-level.h>\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <fts.h>\n#include <glob.h>\n#include <linux/loop.h>\n#include <linux/module.h>\n#include <mntent.h>\n#include <net/if.h>\n#include <sched.h>\n#include <signal.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mount.h>\n#include <sys/resource.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/swap.h>\n#include <sys/syscall.h>\n#include <sys/system_properties.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <map>\n#include <memory>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parsedouble.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <bootloader_message/bootloader_message.h>\n#include <cutils/android_reboot.h>\n#include <fs_mgr.h>\n#include <fscrypt/fscrypt.h>\n#include <libdm/dm.h>\n#include <libdm/loop_control.h>\n#include <libgsi/libgsi.h>\n#include <logwrap/logwrap.h>\n#include <private/android_filesystem_config.h>\n#include <selinux/android.h>\n#include <selinux/label.h>\n#include <selinux/selinux.h>\n#include <system/thread_defs.h>\n\n#include \"action_manager.h\"\n#include \"apex_init_util.h\"\n#include \"bootchart.h\"\n#include \"builtin_arguments.h\"\n#include \"fscrypt_init_extensions.h\"\n#include \"init.h\"\n#include \"mount_namespace.h\"\n#include \"parser.h\"\n#include \"property_service.h\"\n#include \"reboot.h\"\n#include \"rlimit_parser.h\"\n#include \"selabel.h\"\n#include \"selinux.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n#include \"subcontext.h\"\n#include \"util.h\"\n\nusing namespace std::literals::string_literals;\n\nusing android::base::Basename;\nusing android::base::ResultError;\nusing android::base::SetProperty;\nusing android::base::Split;\nusing android::base::StartsWith;\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\nusing android::fs_mgr::Fstab;\nusing android::fs_mgr::ReadFstabFromFile;\n\n#define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW\n\nnamespace android {\nnamespace init {\n\n// There are many legacy paths in rootdir/init.rc that will virtually never exist on a new\n// device, such as '/sys/class/leds/jogball-backlight/brightness'.  As of this writing, there\n// are 81 such failures on cuttlefish.  Instead of spamming the log reporting them, we do not\n// report such failures unless we're running at the DEBUG log level.\nclass ErrorIgnoreEnoent {\n  public:\n    ErrorIgnoreEnoent()\n        : ignore_error_(errno == ENOENT &&\n                        android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}\n    explicit ErrorIgnoreEnoent(int errno_to_append)\n        : error_(errno_to_append),\n          ignore_error_(errno_to_append == ENOENT &&\n                        android::base::GetMinimumLogSeverity() > android::base::DEBUG) {}\n\n    template <typename T>\n    operator android::base::expected<T, ResultError<android::base::Errno>>() {\n        if (ignore_error_) {\n            return {};\n        }\n        return error_;\n    }\n\n    template <typename T>\n    ErrorIgnoreEnoent& operator<<(T&& t) {\n        error_ << t;\n        return *this;\n    }\n\n  private:\n    Error<> error_;\n    bool ignore_error_;\n};\n\ninline ErrorIgnoreEnoent ErrnoErrorIgnoreEnoent() {\n    return ErrorIgnoreEnoent(errno);\n}\n\nstd::vector<std::string> late_import_paths;\n\nstatic constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;\n\nstatic Result<void> reboot_into_recovery(const std::vector<std::string>& options) {\n    LOG(ERROR) << \"Rebooting into recovery\";\n    std::string err;\n    if (!write_bootloader_message(options, &err)) {\n        return Error() << \"Failed to set bootloader message: \" << err;\n    }\n    trigger_shutdown(\"reboot,recovery\");\n    return {};\n}\n\ntemplate <typename F>\nstatic void ForEachServiceInClass(const std::string& classname, F function) {\n    for (const auto& service : ServiceList::GetInstance()) {\n        if (service->classnames().count(classname)) std::invoke(function, service);\n    }\n}\n\nstatic Result<void> do_class_start(const BuiltinArguments& args) {\n    // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1.\n    if (android::base::GetBoolProperty(\"persist.init.dont_start_class.\" + args[1], false))\n        return {};\n    // Starting a class does not start services which are explicitly disabled.\n    // They must  be started individually.\n    for (const auto& service : ServiceList::GetInstance()) {\n        if (service->classnames().count(args[1])) {\n            if (auto result = service->StartIfNotDisabled(); !result.ok()) {\n                LOG(ERROR) << \"Could not start service '\" << service->name()\n                           << \"' as part of class '\" << args[1] << \"': \" << result.error();\n            }\n        }\n    }\n    return {};\n}\n\nstatic Result<void> do_class_stop(const BuiltinArguments& args) {\n    ForEachServiceInClass(args[1], &Service::Stop);\n    return {};\n}\n\nstatic Result<void> do_class_reset(const BuiltinArguments& args) {\n    ForEachServiceInClass(args[1], &Service::Reset);\n    return {};\n}\n\nstatic Result<void> do_class_restart(const BuiltinArguments& args) {\n    // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1.\n    if (android::base::GetBoolProperty(\"persist.init.dont_start_class.\" + args[1], false))\n        return {};\n\n    std::string classname;\n\n    CHECK(args.size() == 2 || args.size() == 3);\n\n    bool only_enabled = false;\n    if (args.size() == 3) {\n        if (args[1] != \"--only-enabled\") {\n            return Error() << \"Unexpected argument: \" << args[1];\n        }\n        only_enabled = true;\n        classname = args[2];\n    } else if (args.size() == 2) {\n        classname = args[1];\n    }\n\n    for (const auto& service : ServiceList::GetInstance()) {\n        if (!service->classnames().count(classname)) {\n            continue;\n        }\n        if (only_enabled && !service->IsEnabled()) {\n            continue;\n        }\n        service->Restart();\n    }\n    return {};\n}\n\nstatic Result<void> do_domainname(const BuiltinArguments& args) {\n    if (auto result = WriteFile(\"/proc/sys/kernel/domainname\", args[1]); !result.ok()) {\n        return Error() << \"Unable to write to /proc/sys/kernel/domainname: \" << result.error();\n    }\n    return {};\n}\n\nstatic Result<void> do_enable(const BuiltinArguments& args) {\n    Service* svc = ServiceList::GetInstance().FindService(args[1]);\n    if (!svc) return Error() << \"Could not find service\";\n\n    if (auto result = svc->Enable(); !result.ok()) {\n        return Error() << \"Could not enable service: \" << result.error();\n    }\n\n    return {};\n}\n\nstatic Result<void> do_exec(const BuiltinArguments& args) {\n    auto service = Service::MakeTemporaryOneshotService(args.args);\n    if (!service.ok()) {\n        return Error() << \"Could not create exec service: \" << service.error();\n    }\n    if (auto result = (*service)->ExecStart(); !result.ok()) {\n        return Error() << \"Could not start exec service: \" << result.error();\n    }\n\n    ServiceList::GetInstance().AddService(std::move(*service));\n    return {};\n}\n\nstatic Result<void> do_exec_background(const BuiltinArguments& args) {\n    auto service = Service::MakeTemporaryOneshotService(args.args);\n    if (!service.ok()) {\n        return Error() << \"Could not create exec background service: \" << service.error();\n    }\n    if (auto result = (*service)->Start(); !result.ok()) {\n        return Error() << \"Could not start exec background service: \" << result.error();\n    }\n\n    ServiceList::GetInstance().AddService(std::move(*service));\n    return {};\n}\n\nstatic Result<void> do_exec_start(const BuiltinArguments& args) {\n    Service* service = ServiceList::GetInstance().FindService(args[1]);\n    if (!service) {\n        return Error() << \"Service not found\";\n    }\n\n    if (auto result = service->ExecStart(); !result.ok()) {\n        return Error() << \"Could not start exec service: \" << result.error();\n    }\n\n    return {};\n}\n\nstatic Result<void> do_export(const BuiltinArguments& args) {\n    if (setenv(args[1].c_str(), args[2].c_str(), 1) == -1) {\n        return ErrnoError() << \"setenv() failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_load_exports(const BuiltinArguments& args) {\n    auto file_contents = ReadFile(args[1]);\n    if (!file_contents.ok()) {\n        return Error() << \"Could not read input file '\" << args[1]\n                       << \"': \" << file_contents.error();\n    }\n\n    auto lines = Split(*file_contents, \"\\n\");\n    for (const auto& line : lines) {\n        if (line.empty()) {\n            continue;\n        }\n\n        auto env = Split(line, \" \");\n\n        if (env.size() != 3) {\n            return ErrnoError() << \"Expected a line as `export <name> <value>`, found: `\" << line\n                                << \"`\";\n        }\n\n        if (env[0] != \"export\") {\n            return ErrnoError() << \"Unknown action: '\" << env[0] << \"', expected 'export'\";\n        }\n\n        if (setenv(env[1].c_str(), env[2].c_str(), 1) == -1) {\n            return ErrnoError() << \"Failed to export '\" << line << \"' from \" << args[1];\n        }\n    }\n\n    return {};\n}\n\nstatic Result<void> do_hostname(const BuiltinArguments& args) {\n    if (auto result = WriteFile(\"/proc/sys/kernel/hostname\", args[1]); !result.ok()) {\n        return Error() << \"Unable to write to /proc/sys/kernel/hostname: \" << result.error();\n    }\n    return {};\n}\n\nstatic Result<void> do_ifup(const BuiltinArguments& args) {\n    struct ifreq ifr;\n\n    strlcpy(ifr.ifr_name, args[1].c_str(), IFNAMSIZ);\n\n    unique_fd s(TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0)));\n    if (s < 0) return ErrnoError() << \"opening socket failed\";\n\n    if (ioctl(s.get(), SIOCGIFFLAGS, &ifr) < 0) {\n        return ErrnoError() << \"ioctl(..., SIOCGIFFLAGS, ...) failed\";\n    }\n\n    ifr.ifr_flags |= IFF_UP;\n\n    if (ioctl(s.get(), SIOCSIFFLAGS, &ifr) < 0) {\n        return ErrnoError() << \"ioctl(..., SIOCSIFFLAGS, ...) failed\";\n    }\n\n    return {};\n}\n\nstatic Result<void> do_insmod(const BuiltinArguments& args) {\n    int flags = 0;\n    auto it = args.begin() + 1;\n\n    if (!(*it).compare(\"-f\")) {\n        flags = MODULE_INIT_IGNORE_VERMAGIC | MODULE_INIT_IGNORE_MODVERSIONS;\n        it++;\n    }\n\n    std::string filename = *it++;\n    std::string options = android::base::Join(std::vector<std::string>(it, args.end()), ' ');\n\n    unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));\n    if (fd == -1) return ErrnoError() << \"open(\\\"\" << filename << \"\\\") failed\";\n\n    int rc = syscall(__NR_finit_module, fd.get(), options.c_str(), flags);\n    if (rc == -1) return ErrnoError() << \"finit_module for \\\"\" << filename << \"\\\" failed\";\n\n    return {};\n}\n\nstatic Result<void> do_interface_restart(const BuiltinArguments& args) {\n    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);\n    if (!svc) return Error() << \"interface \" << args[1] << \" not found\";\n    svc->Restart();\n    return {};\n}\n\nstatic Result<void> do_interface_start(const BuiltinArguments& args) {\n    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);\n    if (!svc) return Error() << \"interface \" << args[1] << \" not found\";\n    if (auto result = svc->Start(); !result.ok()) {\n        return Error() << \"Could not start interface: \" << result.error();\n    }\n    return {};\n}\n\nstatic Result<void> do_interface_stop(const BuiltinArguments& args) {\n    Service* svc = ServiceList::GetInstance().FindInterface(args[1]);\n    if (!svc) return Error() << \"interface \" << args[1] << \" not found\";\n    svc->Stop();\n    return {};\n}\n\nstatic Result<void> make_dir_with_options(const MkdirOptions& options) {\n    std::string ref_basename;\n    if (options.ref_option == \"ref\") {\n        ref_basename = fscrypt_key_ref;\n    } else if (options.ref_option == \"per_boot_ref\") {\n        ref_basename = fscrypt_key_per_boot_ref;\n    } else {\n        return Error() << \"Unknown key option: '\" << options.ref_option << \"'\";\n    }\n\n    struct stat mstat;\n    if (lstat(options.target.c_str(), &mstat) != 0) {\n        if (errno != ENOENT) {\n            return ErrnoError() << \"lstat() failed on \" << options.target;\n        }\n        if (!make_dir(options.target, options.mode)) {\n            return ErrnoErrorIgnoreEnoent() << \"mkdir() failed on \" << options.target;\n        }\n        if (lstat(options.target.c_str(), &mstat) != 0) {\n            return ErrnoError() << \"lstat() failed on new \" << options.target;\n        }\n    }\n    if (!S_ISDIR(mstat.st_mode)) {\n        return Error() << \"Not a directory on \" << options.target;\n    }\n    bool needs_chmod = (mstat.st_mode & ~S_IFMT) != options.mode;\n    if ((options.uid != static_cast<uid_t>(-1) && options.uid != mstat.st_uid) ||\n        (options.gid != static_cast<gid_t>(-1) && options.gid != mstat.st_gid)) {\n        if (lchown(options.target.c_str(), options.uid, options.gid) == -1) {\n            return ErrnoError() << \"lchown failed on \" << options.target;\n        }\n        // chown may have cleared S_ISUID and S_ISGID, chmod again\n        needs_chmod = true;\n    }\n    if (needs_chmod) {\n        if (fchmodat(AT_FDCWD, options.target.c_str(), options.mode, AT_SYMLINK_NOFOLLOW) == -1) {\n            return ErrnoError() << \"fchmodat() failed on \" << options.target;\n        }\n    }\n    if (IsFbeEnabled()) {\n        if (!FscryptSetDirectoryPolicy(ref_basename, options.fscrypt_action, options.target)) {\n            return reboot_into_recovery(\n                    {\"--prompt_and_wipe_data\", \"--reason=set_policy_failed:\"s + options.target});\n        }\n    }\n    return {};\n}\n\n// mkdir <path> [mode] [owner] [group] [<option> ...]\nstatic Result<void> do_mkdir(const BuiltinArguments& args) {\n    auto options = ParseMkdir(args.args);\n    if (!options.ok()) return options.error();\n    return make_dir_with_options(*options);\n}\n\n/* umount <path> */\nstatic Result<void> do_umount(const BuiltinArguments& args) {\n    if (umount(args[1].c_str()) < 0) {\n        return ErrnoError() << \"umount() failed\";\n    }\n    return {};\n}\n\nstatic struct {\n    const char *name;\n    unsigned flag;\n} mount_flags[] = {\n    { \"noatime\",    MS_NOATIME },\n    { \"noexec\",     MS_NOEXEC },\n    { \"nosuid\",     MS_NOSUID },\n    { \"nodev\",      MS_NODEV },\n    { \"nodiratime\", MS_NODIRATIME },\n    { \"ro\",         MS_RDONLY },\n    { \"rw\",         0 },\n    { \"remount\",    MS_REMOUNT },\n    { \"bind\",       MS_BIND },\n    { \"rec\",        MS_REC },\n    { \"unbindable\", MS_UNBINDABLE },\n    { \"private\",    MS_PRIVATE },\n    { \"slave\",      MS_SLAVE },\n    { \"shared\",     MS_SHARED },\n    { \"nosymfollow\", MS_NOSYMFOLLOW },\n    { \"defaults\",   0 },\n    { 0,            0 },\n};\n\n/* mount <type> <device> <path> <flags ...> <options> */\nstatic Result<void> do_mount(const BuiltinArguments& args) {\n    const char* options = nullptr;\n    unsigned flags = 0;\n    bool wait = false;\n\n    for (size_t na = 4; na < args.size(); na++) {\n        size_t i;\n        for (i = 0; mount_flags[i].name; i++) {\n            if (!args[na].compare(mount_flags[i].name)) {\n                flags |= mount_flags[i].flag;\n                break;\n            }\n        }\n\n        if (!mount_flags[i].name) {\n            if (!args[na].compare(\"wait\")) {\n                wait = true;\n                // If our last argument isn't a flag, wolf it up as an option string.\n            } else if (na + 1 == args.size()) {\n                options = args[na].c_str();\n            }\n        }\n    }\n\n    const char* system = args[1].c_str();\n    const char* source = args[2].c_str();\n    const char* target = args[3].c_str();\n\n    if (android::base::StartsWith(source, \"loop@\")) {\n        int mode = (flags & MS_RDONLY) ? O_RDONLY : O_RDWR;\n        const char* file_path = source + strlen(\"loop@\");\n\n        // Open source file\n        if (wait) {\n            wait_for_file(file_path, kCommandRetryTimeout);\n        }\n\n        unique_fd fd(TEMP_FAILURE_RETRY(open(file_path, mode | O_CLOEXEC)));\n        if (fd < 0) {\n            return ErrnoError() << \"open(\" << file_path << \", \" << mode << \") failed\";\n        }\n\n        // Allocate loop device and attach it to file_path.\n        android::dm::LoopControl loop_control;\n        std::string loop_device;\n        if (!loop_control.Attach(fd.get(), 5s, &loop_device)) {\n            return ErrnoError() << \"loop_control.Attach \" << file_path << \" failed\";\n        }\n\n        if (mount(loop_device.c_str(), target, system, flags, options) < 0) {\n            loop_control.Detach(loop_device);\n            return ErrnoError() << \"mount() failed\";\n        }\n    } else {\n        if (wait)\n            wait_for_file(source, kCommandRetryTimeout);\n        if (mount(source, target, system, flags, options) < 0) {\n            return ErrnoErrorIgnoreEnoent() << \"mount() failed\";\n        }\n\n    }\n\n    return {};\n}\n\n/* Imports .rc files from the specified paths. Default ones are applied if none is given.\n *\n * rc_paths: list of paths to rc files to import\n */\nstatic void import_late(const std::vector<std::string>& rc_paths) {\n    auto& action_manager = ActionManager::GetInstance();\n    auto& service_list = ServiceList::GetInstance();\n    Parser parser = CreateParser(action_manager, service_list);\n    if (rc_paths.empty()) {\n        // Fallbacks for partitions on which early mount isn't enabled.\n        for (const auto& path : late_import_paths) {\n            parser.ParseConfig(path);\n        }\n        late_import_paths.clear();\n    } else {\n        for (const auto& rc_path : rc_paths) {\n            parser.ParseConfig(rc_path);\n        }\n    }\n\n    // Turning this on and letting the INFO logging be discarded adds 0.2s to\n    // Nexus 9 boot time, so it's disabled by default.\n    if (false) DumpState();\n}\n\n/* Queue event based on fs_mgr return code.\n *\n * code: return code of fs_mgr_mount_all\n *\n * This function might request a reboot, in which case it will\n * not return.\n *\n * return code is processed based on input code\n */\nstatic Result<void> queue_fs_event(int code) {\n    if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {\n        SetProperty(\"ro.crypto.state\", \"unsupported\");\n        ActionManager::GetInstance().QueueEventTrigger(\"nonencrypted\");\n        return {};\n    } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {\n        /* Setup a wipe via recovery, and reboot into recovery */\n        if (android::gsi::IsGsiRunning()) {\n            return Error() << \"cannot wipe within GSI\";\n        }\n        PLOG(ERROR) << \"fs_mgr_mount_all suggested recovery, so wiping data via recovery.\";\n        const std::vector<std::string> options = {\"--wipe_data\", \"--reason=fs_mgr_mount_all\" };\n        return reboot_into_recovery(options);\n        /* If reboot worked, there is no return. */\n    } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED ||\n               code == FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED ||\n               code == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {\n        SetProperty(\"ro.crypto.state\", \"encrypted\");\n\n        // Although encrypted, vold has already set the device up, so we do not need to\n        // do anything different from the nonencrypted case.\n        ActionManager::GetInstance().QueueEventTrigger(\"nonencrypted\");\n        return {};\n    } else if (code > 0) {\n        Error() << \"fs_mgr_mount_all() returned unexpected error \" << code;\n    }\n    /* else ... < 0: error */\n\n    return Error() << \"Invalid code: \" << code;\n}\n\n/* <= Q: mount_all <fstab> [ <path> ]* [--<options>]*\n * >= R: mount_all [ <fstab> ] [--<options>]*\n *\n * This function might request a reboot, in which case it will\n * not return.\n */\nstatic Result<void> do_mount_all(const BuiltinArguments& args) {\n    auto mount_all = ParseMountAll(args.args);\n    if (!mount_all.ok()) return mount_all.error();\n\n    const char* prop_post_fix = \"default\";\n    bool queue_event = true;\n    if (mount_all->mode == MOUNT_MODE_EARLY) {\n        prop_post_fix = \"early\";\n        queue_event = false;\n    } else if (mount_all->mode == MOUNT_MODE_LATE) {\n        prop_post_fix = \"late\";\n    }\n\n    std::string prop_name = \"ro.boottime.init.mount_all.\"s + prop_post_fix;\n    android::base::Timer t;\n\n    Fstab fstab;\n    if (mount_all->fstab_path.empty()) {\n        if (!ReadDefaultFstab(&fstab)) {\n            return Error() << \"Could not read default fstab\";\n        }\n    } else {\n        if (!ReadFstabFromFile(mount_all->fstab_path, &fstab)) {\n            return Error() << \"Could not read fstab\";\n        }\n    }\n\n    auto mount_fstab_result = fs_mgr_mount_all(&fstab, mount_all->mode);\n    SetProperty(prop_name, std::to_string(t.duration().count()));\n\n    if (mount_all->import_rc) {\n        import_late(mount_all->rc_paths);\n    }\n\n    if (queue_event) {\n        /* queue_fs_event will queue event based on mount_fstab return code\n         * and return processed return code*/\n        auto queue_fs_result = queue_fs_event(mount_fstab_result);\n        if (!queue_fs_result.ok()) {\n            return Error() << \"queue_fs_event() failed: \" << queue_fs_result.error();\n        }\n    }\n\n    return {};\n}\n\n/* umount_all [ <fstab> ] */\nstatic Result<void> do_umount_all(const BuiltinArguments& args) {\n    auto umount_all = ParseUmountAll(args.args);\n    if (!umount_all.ok()) return umount_all.error();\n\n    Fstab fstab;\n    if (umount_all->empty()) {\n        if (!ReadDefaultFstab(&fstab)) {\n            return Error() << \"Could not read default fstab\";\n        }\n    } else {\n        if (!ReadFstabFromFile(*umount_all, &fstab)) {\n            return Error() << \"Could not read fstab\";\n        }\n    }\n\n    if (auto result = fs_mgr_umount_all(&fstab); result != 0) {\n        return Error() << \"umount_fstab() failed \" << result;\n    }\n    return {};\n}\n\n/* swapon_all [ <fstab> ] */\nstatic Result<void> do_swapon_all(const BuiltinArguments& args) {\n    auto swapon_all = ParseSwaponAll(args.args);\n    if (!swapon_all.ok()) return swapon_all.error();\n\n    Fstab fstab;\n    if (swapon_all->empty()) {\n        if (!ReadDefaultFstab(&fstab)) {\n            return Error() << \"Could not read default fstab\";\n        }\n    } else {\n        if (!ReadFstabFromFile(*swapon_all, &fstab)) {\n            return Error() << \"Could not read fstab '\" << *swapon_all << \"'\";\n        }\n    }\n\n    if (!fs_mgr_swapon_all(fstab)) {\n        return Error() << \"fs_mgr_swapon_all() failed\";\n    }\n\n    return {};\n}\n\nstatic Result<void> do_setprop(const BuiltinArguments& args) {\n    if (StartsWith(args[1], \"ctl.\")) {\n        return Error()\n               << \"Cannot set ctl. properties from init; call the Service functions directly\";\n    }\n    if (args[1] == kRestoreconProperty) {\n        return Error() << \"Cannot set '\" << kRestoreconProperty\n                       << \"' from init; use the restorecon builtin directly\";\n    }\n\n    SetProperty(args[1], args[2]);\n    return {};\n}\n\nstatic Result<void> do_setrlimit(const BuiltinArguments& args) {\n    auto rlimit = ParseRlimit(args.args);\n    if (!rlimit.ok()) return rlimit.error();\n\n    if (setrlimit(rlimit->first, &rlimit->second) == -1) {\n        return ErrnoError() << \"setrlimit failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_start(const BuiltinArguments& args) {\n    Service* svc = ServiceList::GetInstance().FindService(args[1]);\n    if (!svc) return Error() << \"service \" << args[1] << \" not found\";\n    errno = 0;\n    if (auto result = svc->Start(); !result.ok()) {\n        return ErrorIgnoreEnoent() << \"Could not start service: \" << result.error();\n    }\n    return {};\n}\n\nstatic Result<void> do_stop(const BuiltinArguments& args) {\n    Service* svc = ServiceList::GetInstance().FindService(args[1]);\n    if (!svc) return Error() << \"service \" << args[1] << \" not found\";\n    svc->Stop();\n    return {};\n}\n\nstatic Result<void> do_restart(const BuiltinArguments& args) {\n    bool only_if_running = false;\n    if (args.size() == 3) {\n        if (args[1] == \"--only-if-running\") {\n            only_if_running = true;\n        } else {\n            return Error() << \"Unknown argument to restart: \" << args[1];\n        }\n    }\n\n    const auto& classname = args[args.size() - 1];\n    Service* svc = ServiceList::GetInstance().FindService(classname);\n    if (!svc) return Error() << \"service \" << classname << \" not found\";\n    if (only_if_running && !svc->IsRunning()) {\n        return {};\n    }\n    svc->Restart();\n    return {};\n}\n\nstatic Result<void> do_trigger(const BuiltinArguments& args) {\n    ActionManager::GetInstance().QueueEventTrigger(args[1]);\n    return {};\n}\n\nstatic int MakeSymlink(const std::string& target, const std::string& linkpath) {\n    std::string secontext;\n    // Passing 0 for mode should work.\n    if (SelabelLookupFileContext(linkpath, 0, &secontext) && !secontext.empty()) {\n        setfscreatecon(secontext.c_str());\n    }\n\n    int rc = symlink(target.c_str(), linkpath.c_str());\n\n    if (!secontext.empty()) {\n        int save_errno = errno;\n        setfscreatecon(nullptr);\n        errno = save_errno;\n    }\n\n    return rc;\n}\n\nstatic Result<void> do_symlink(const BuiltinArguments& args) {\n    if (MakeSymlink(args[1], args[2]) < 0) {\n        // The symlink builtin is often used to create symlinks for older devices to be backwards\n        // compatible with new paths, therefore we skip reporting this error.\n        return ErrnoErrorIgnoreEnoent() << \"symlink() failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_rm(const BuiltinArguments& args) {\n    if (unlink(args[1].c_str()) < 0) {\n        return ErrnoError() << \"unlink() failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_rmdir(const BuiltinArguments& args) {\n    if (rmdir(args[1].c_str()) < 0) {\n        return ErrnoError() << \"rmdir() failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_sysclktz(const BuiltinArguments& args) {\n    struct timezone tz = {};\n    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {\n        return Error() << \"Unable to parse mins_west_of_gmt\";\n    }\n\n    if (settimeofday(nullptr, &tz) == -1) {\n        return ErrnoError() << \"settimeofday() failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_verity_update_state(const BuiltinArguments& args) {\n    int mode;\n    if (!fs_mgr_load_verity_state(&mode)) {\n        return Error() << \"fs_mgr_load_verity_state() failed\";\n    }\n\n    Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        return Error() << \"Failed to read default fstab\";\n    }\n\n    for (const auto& entry : fstab) {\n        if (!fs_mgr_is_verity_enabled(entry)) {\n            continue;\n        }\n\n        // To be consistent in vboot 1.0 and vboot 2.0 (AVB), use \"system\" for the partition even\n        // for system as root, so it has property [partition.system.verified].\n        std::string partition = entry.mount_point == \"/\" ? \"system\" : Basename(entry.mount_point);\n        SetProperty(\"partition.\" + partition + \".verified\", std::to_string(mode));\n\n        auto hashtree_info = fs_mgr_get_hashtree_info(entry);\n        if (hashtree_info) {\n            SetProperty(\"partition.\" + partition + \".verified.hash_alg\", hashtree_info->algorithm);\n            SetProperty(\"partition.\" + partition + \".verified.root_digest\",\n                        hashtree_info->root_digest);\n            SetProperty(\"partition.\" + partition + \".verified.check_at_most_once\",\n                        hashtree_info->check_at_most_once ? \"1\" : \"0\");\n        }\n    }\n\n    return {};\n}\n\nstatic Result<void> do_write(const BuiltinArguments& args) {\n    if (auto result = WriteFile(args[1], args[2]); !result.ok()) {\n        return ErrorIgnoreEnoent()\n               << \"Unable to write to file '\" << args[1] << \"': \" << result.error();\n    }\n\n    return {};\n}\n\nstatic Result<void> readahead_file(const std::string& filename, bool fully) {\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filename.c_str(), O_RDONLY | O_CLOEXEC)));\n    if (fd == -1) {\n        return ErrnoError() << \"Error opening file\";\n    }\n    if (posix_fadvise(fd.get(), 0, 0, POSIX_FADV_WILLNEED)) {\n        return ErrnoError() << \"Error posix_fadvise file\";\n    }\n    if (readahead(fd.get(), 0, std::numeric_limits<size_t>::max())) {\n        return ErrnoError() << \"Error readahead file\";\n    }\n    if (fully) {\n        char buf[BUFSIZ];\n        ssize_t n;\n        while ((n = TEMP_FAILURE_RETRY(read(fd.get(), &buf[0], sizeof(buf)))) > 0) {\n        }\n        if (n != 0) {\n            return ErrnoError() << \"Error reading file\";\n        }\n    }\n    return {};\n}\n\nstatic Result<void> do_readahead(const BuiltinArguments& args) {\n    struct stat sb;\n\n    if (stat(args[1].c_str(), &sb)) {\n        return ErrnoError() << \"Error opening \" << args[1];\n    }\n\n    bool readfully = false;\n    if (args.size() == 3 && args[2] == \"--fully\") {\n        readfully = true;\n    }\n    // We will do readahead in a forked process in order not to block init\n    // since it may block while it reads the\n    // filesystem metadata needed to locate the requested blocks.  This\n    // occurs frequently with ext[234] on large files using indirect blocks\n    // instead of extents, giving the appearance that the call blocks until\n    // the requested data has been read.\n    pid_t pid = fork();\n    if (pid == 0) {\n        if (setpriority(PRIO_PROCESS, 0, static_cast<int>(ANDROID_PRIORITY_LOWEST)) != 0) {\n            PLOG(WARNING) << \"setpriority failed\";\n        }\n        if (android_set_ioprio(0, IoSchedClass_IDLE, 7)) {\n            PLOG(WARNING) << \"ioprio_get failed\";\n        }\n        android::base::Timer t;\n        if (S_ISREG(sb.st_mode)) {\n            if (auto result = readahead_file(args[1], readfully); !result.ok()) {\n                LOG(WARNING) << \"Unable to readahead '\" << args[1] << \"': \" << result.error();\n                _exit(EXIT_FAILURE);\n            }\n        } else if (S_ISDIR(sb.st_mode)) {\n            char* paths[] = {const_cast<char*>(args[1].data()), nullptr};\n            std::unique_ptr<FTS, decltype(&fts_close)> fts(\n                fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr), fts_close);\n            if (!fts) {\n                PLOG(ERROR) << \"Error opening directory: \" << args[1];\n                _exit(EXIT_FAILURE);\n            }\n            // Traverse the entire hierarchy and do readahead\n            for (FTSENT* ftsent = fts_read(fts.get()); ftsent != nullptr;\n                 ftsent = fts_read(fts.get())) {\n                if (ftsent->fts_info & FTS_F) {\n                    const std::string filename = ftsent->fts_accpath;\n                    if (auto result = readahead_file(filename, readfully); !result.ok()) {\n                        LOG(WARNING)\n                            << \"Unable to readahead '\" << filename << \"': \" << result.error();\n                    }\n                }\n            }\n        }\n        LOG(INFO) << \"Readahead \" << args[1] << \" took \" << t << \" asynchronously\";\n        _exit(0);\n    } else if (pid < 0) {\n        return ErrnoError() << \"Fork failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_copy(const BuiltinArguments& args) {\n    auto file_contents = ReadFile(args[1]);\n    if (!file_contents.ok()) {\n        return Error() << \"Could not read input file '\" << args[1] << \"': \" << file_contents.error();\n    }\n    if (auto result = WriteFile(args[2], *file_contents); !result.ok()) {\n        return Error() << \"Could not write to output file '\" << args[2] << \"': \" << result.error();\n    }\n\n    return {};\n}\n\nstatic Result<void> do_copy_per_line(const BuiltinArguments& args) {\n    std::string file_contents;\n    if (!android::base::ReadFileToString(args[1], &file_contents, true)) {\n        return Error() << \"Could not read input file '\" << args[1] << \"'\";\n    }\n    auto lines = Split(file_contents, \"\\n\");\n    for (const auto& line : lines) {\n        auto result = WriteFile(args[2], line);\n        if (!result.ok()) {\n            LOG(VERBOSE) << \"Could not write to output file '\" << args[2] << \"' with '\" << line\n                         << \"' : \" << result.error();\n        }\n    }\n\n    return {};\n}\n\nstatic Result<void> do_chown(const BuiltinArguments& args) {\n    auto uid = DecodeUid(args[1]);\n    if (!uid.ok()) {\n        return Error() << \"Unable to decode UID for '\" << args[1] << \"': \" << uid.error();\n    }\n\n    // GID is optional and pushes the index of path out by one if specified.\n    const std::string& path = (args.size() == 4) ? args[3] : args[2];\n    Result<gid_t> gid = -1;\n\n    if (args.size() == 4) {\n        gid = DecodeUid(args[2]);\n        if (!gid.ok()) {\n            return Error() << \"Unable to decode GID for '\" << args[2] << \"': \" << gid.error();\n        }\n    }\n\n    if (lchown(path.c_str(), *uid, *gid) == -1) {\n        return ErrnoErrorIgnoreEnoent() << \"lchown() failed\";\n    }\n\n    return {};\n}\n\nstatic mode_t get_mode(const char *s) {\n    mode_t mode = 0;\n    while (*s) {\n        if (*s >= '0' && *s <= '7') {\n            mode = (mode<<3) | (*s-'0');\n        } else {\n            return -1;\n        }\n        s++;\n    }\n    return mode;\n}\n\nstatic Result<void> do_chmod(const BuiltinArguments& args) {\n    mode_t mode = get_mode(args[1].c_str());\n    if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {\n        return ErrnoErrorIgnoreEnoent() << \"fchmodat() failed\";\n    }\n    return {};\n}\n\nstatic Result<void> do_restorecon(const BuiltinArguments& args) {\n    auto restorecon_info = ParseRestorecon(args.args);\n    if (!restorecon_info.ok()) {\n        return restorecon_info.error();\n    }\n\n    const auto& [flag, paths] = *restorecon_info;\n\n    int ret = 0;\n    for (const auto& path : paths) {\n        if (selinux_android_restorecon(path.c_str(), flag) < 0) {\n            ret = errno;\n        }\n    }\n\n    if (ret) return ErrnoErrorIgnoreEnoent() << \"selinux_android_restorecon() failed\";\n    return {};\n}\n\nstatic Result<void> do_restorecon_recursive(const BuiltinArguments& args) {\n    std::vector<std::string> non_const_args(args.args);\n    non_const_args.insert(std::next(non_const_args.begin()), \"--recursive\");\n    return do_restorecon({.args = std::move(non_const_args), .context = args.context});\n}\n\nstatic Result<void> do_loglevel(const BuiltinArguments& args) {\n    // TODO: support names instead/as well?\n    int log_level = -1;\n    android::base::ParseInt(args[1], &log_level);\n    android::base::LogSeverity severity;\n    switch (log_level) {\n        case 7: severity = android::base::DEBUG; break;\n        case 6: severity = android::base::INFO; break;\n        case 5:\n        case 4: severity = android::base::WARNING; break;\n        case 3: severity = android::base::ERROR; break;\n        case 2:\n        case 1:\n        case 0: severity = android::base::FATAL; break;\n        default:\n            return Error() << \"invalid log level \" << log_level;\n    }\n    android::base::SetMinimumLogSeverity(severity);\n    return {};\n}\n\nstatic Result<void> do_load_persist_props(const BuiltinArguments& args) {\n    SendLoadPersistentPropertiesMessage();\n\n    start_waiting_for_property(\"ro.persistent_properties.ready\", \"true\");\n    return {};\n}\n\nstatic Result<void> do_load_system_props(const BuiltinArguments& args) {\n    LOG(INFO) << \"deprecated action `load_system_props` called.\";\n    return {};\n}\n\nstatic Result<void> do_wait(const BuiltinArguments& args) {\n    auto timeout = kCommandRetryTimeout;\n    if (args.size() == 3) {\n        double timeout_double;\n        if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {\n            return Error() << \"failed to parse timeout\";\n        }\n        timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(\n                std::chrono::duration<double>(timeout_double));\n    }\n\n    if (wait_for_file(args[1].c_str(), timeout) != 0) {\n        return Error() << \"wait_for_file() failed\";\n    }\n\n    return {};\n}\n\nstatic Result<void> do_wait_for_prop(const BuiltinArguments& args) {\n    const char* name = args[1].c_str();\n    const char* value = args[2].c_str();\n    size_t value_len = strlen(value);\n\n    if (!IsLegalPropertyName(name)) {\n        return Error() << \"IsLegalPropertyName(\" << name << \") failed\";\n    }\n    if (value_len >= PROP_VALUE_MAX) {\n        return Error() << \"value too long\";\n    }\n    if (!start_waiting_for_property(name, value)) {\n        return Error() << \"already waiting for a property\";\n    }\n    return {};\n}\n\nstatic bool is_file_crypto() {\n    return android::base::GetProperty(\"ro.crypto.type\", \"\") == \"file\";\n}\n\nstatic Result<void> ExecWithFunctionOnFailure(const std::vector<std::string>& args,\n                                              std::function<void(const std::string&)> function) {\n    auto service = Service::MakeTemporaryOneshotService(args);\n    if (!service.ok()) {\n        function(\"MakeTemporaryOneshotService failed: \" + service.error().message());\n    }\n    (*service)->AddReapCallback([function](const siginfo_t& siginfo) {\n        if (siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) {\n            function(StringPrintf(\"Exec service failed, status %d\", siginfo.si_status));\n        }\n    });\n    if (auto result = (*service)->ExecStart(); !result.ok()) {\n        function(\"ExecStart failed: \" + result.error().message());\n    }\n    ServiceList::GetInstance().AddService(std::move(*service));\n    return {};\n}\n\nstatic Result<void> ExecVdcRebootOnFailure(const std::string& vdc_arg) {\n    auto reboot_reason = vdc_arg + \"_failed\";\n\n    auto reboot = [reboot_reason](const std::string& message) {\n        // TODO (b/122850122): support this in gsi\n        if (IsFbeEnabled() && !android::gsi::IsGsiRunning()) {\n            LOG(ERROR) << message << \": Rebooting into recovery, reason: \" << reboot_reason;\n            if (auto result = reboot_into_recovery(\n                        {\"--prompt_and_wipe_data\", \"--reason=\"s + reboot_reason});\n                !result.ok()) {\n                LOG(FATAL) << \"Could not reboot into recovery: \" << result.error();\n            }\n        } else {\n            LOG(ERROR) << \"Failure (reboot suppressed): \" << reboot_reason;\n        }\n    };\n\n    std::vector<std::string> args = {\"exec\", \"/system/bin/vdc\", \"--wait\", \"cryptfs\", vdc_arg};\n    return ExecWithFunctionOnFailure(args, reboot);\n}\n\nstatic Result<void> do_installkey(const BuiltinArguments& args) {\n    if (!is_file_crypto()) return {};\n\n    auto unencrypted_dir = args[1] + fscrypt_unencrypted_folder;\n    if (!make_dir(unencrypted_dir, 0700) && errno != EEXIST) {\n        return ErrnoError() << \"Failed to create \" << unencrypted_dir;\n    }\n    return ExecVdcRebootOnFailure(\"enablefilecrypto\");\n}\n\nstatic Result<void> do_init_user0(const BuiltinArguments& args) {\n    return ExecVdcRebootOnFailure(\"init_user0\");\n}\n\nstatic Result<void> do_mark_post_data(const BuiltinArguments& args) {\n    LOG(INFO) << \"deprecated action `mark_post_data` called.\";\n    return {};\n}\n\nstatic Result<void> GenerateLinkerConfiguration() {\n    const char* linkerconfig_binary = \"/apex/com.android.runtime/bin/linkerconfig\";\n    const char* linkerconfig_target = \"/linkerconfig\";\n    const char* arguments[] = {linkerconfig_binary, \"--target\", linkerconfig_target};\n\n    if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,\n                            nullptr) != 0) {\n        return ErrnoError() << \"failed to execute linkerconfig\";\n    }\n\n    auto current_mount_ns = GetCurrentMountNamespace();\n    if (!current_mount_ns.ok()) {\n        return current_mount_ns.error();\n    }\n    if (*current_mount_ns == NS_DEFAULT) {\n        SetDefaultMountNamespaceReady();\n    }\n\n    LOG(INFO) << \"linkerconfig generated \" << linkerconfig_target\n              << \" with mounted APEX modules info\";\n\n    return {};\n}\n\nstatic Result<void> MountLinkerConfigForDefaultNamespace() {\n    // No need to mount linkerconfig for default mount namespace if the path does not exist (which\n    // would mean it is already mounted)\n    if (access(\"/linkerconfig/default\", 0) != 0) {\n        return {};\n    }\n\n    if (mount(\"/linkerconfig/default\", \"/linkerconfig\", nullptr, MS_BIND | MS_REC, nullptr) != 0) {\n        return ErrnoError() << \"Failed to mount linker configuration for default mount namespace.\";\n    }\n\n    return {};\n}\nstatic Result<void> do_update_linker_config(const BuiltinArguments&) {\n    return GenerateLinkerConfiguration();\n}\n\n/*\n * Creates a directory under /data/misc/apexdata/ for each APEX.\n */\nstatic void create_apex_data_dirs() {\n    for (const auto& name : GetApexListFrom(\"/apex\")) {\n        auto path = \"/data/misc/apexdata/\" + name;\n        auto options = MkdirOptions{path, 0771, AID_ROOT, AID_SYSTEM, FscryptAction::kNone, \"ref\"};\n        auto result = make_dir_with_options(options);\n        if (!result.ok()) {\n            LOG(ERROR) << result.error();\n        }\n    }\n}\n\nstatic Result<void> do_perform_apex_config(const BuiltinArguments& args) {\n    bool bootstrap = false;\n    if (args.size() == 2) {\n        if (args[1] != \"--bootstrap\") {\n            return Error() << \"Unexpected argument: \" << args[1];\n        }\n        bootstrap = true;\n    }\n\n    if (!bootstrap) {\n        create_apex_data_dirs();\n    }\n\n    auto parse_result = ParseRcScriptsFromAllApexes(bootstrap);\n    if (!parse_result.ok()) {\n        return parse_result.error();\n    }\n\n    auto update_linker_config = do_update_linker_config(args);\n    if (!update_linker_config.ok()) {\n        return update_linker_config.error();\n    }\n\n    if (!bootstrap) {\n        ServiceList::GetInstance().StartDelayedServices();\n    }\n    return {};\n}\n\nstatic Result<void> do_enter_default_mount_ns(const BuiltinArguments& args) {\n    if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {\n        return result.error();\n    }\n    if (auto result = MountLinkerConfigForDefaultNamespace(); !result.ok()) {\n        return result.error();\n    }\n    LOG(INFO) << \"Switched to default mount namespace\";\n    return {};\n}\n\nstatic Result<void> do_swapoff(const BuiltinArguments& args) {\n    if (!swapoff(args[1].c_str())) {\n        return ErrnoError() << \"swapoff() failed\";\n    }\n    return {};\n}\n\n// Builtin-function-map start\nconst BuiltinFunctionMap& GetBuiltinFunctionMap() {\n    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();\n    // clang-format off\n    static const BuiltinFunctionMap builtin_functions = {\n        {\"bootchart\",               {1,     1,    {false,  do_bootchart}}},\n        {\"chmod\",                   {2,     2,    {true,   do_chmod}}},\n        {\"chown\",                   {2,     3,    {true,   do_chown}}},\n        {\"class_reset\",             {1,     1,    {false,  do_class_reset}}},\n        {\"class_restart\",           {1,     2,    {false,  do_class_restart}}},\n        {\"class_start\",             {1,     1,    {false,  do_class_start}}},\n        {\"class_stop\",              {1,     1,    {false,  do_class_stop}}},\n        {\"copy\",                    {2,     2,    {true,   do_copy}}},\n        {\"copy_per_line\",           {2,     2,    {true,   do_copy_per_line}}},\n        {\"domainname\",              {1,     1,    {true,   do_domainname}}},\n        {\"enable\",                  {1,     1,    {false,  do_enable}}},\n        {\"exec\",                    {1,     kMax, {false,  do_exec}}},\n        {\"exec_background\",         {1,     kMax, {false,  do_exec_background}}},\n        {\"exec_start\",              {1,     1,    {false,  do_exec_start}}},\n        {\"export\",                  {2,     2,    {false,  do_export}}},\n        {\"hostname\",                {1,     1,    {true,   do_hostname}}},\n        {\"ifup\",                    {1,     1,    {true,   do_ifup}}},\n        {\"init_user0\",              {0,     0,    {false,  do_init_user0}}},\n        {\"insmod\",                  {1,     kMax, {true,   do_insmod}}},\n        {\"installkey\",              {1,     1,    {false,  do_installkey}}},\n        {\"interface_restart\",       {1,     1,    {false,  do_interface_restart}}},\n        {\"interface_start\",         {1,     1,    {false,  do_interface_start}}},\n        {\"interface_stop\",          {1,     1,    {false,  do_interface_stop}}},\n        {\"load_exports\",            {1,     1,    {false,  do_load_exports}}},\n        {\"load_persist_props\",      {0,     0,    {false,  do_load_persist_props}}},\n        {\"load_system_props\",       {0,     0,    {false,  do_load_system_props}}},\n        {\"loglevel\",                {1,     1,    {false,  do_loglevel}}},\n        {\"mark_post_data\",          {0,     0,    {false,  do_mark_post_data}}},\n        {\"mkdir\",                   {1,     6,    {true,   do_mkdir}}},\n        // TODO: Do mount operations in vendor_init.\n        // mount_all is currently too complex to run in vendor_init as it queues action triggers,\n        // imports rc scripts, etc.  It should be simplified and run in vendor_init context.\n        // mount and umount are run in the same context as mount_all for symmetry.\n        {\"mount_all\",               {0,     kMax, {false,  do_mount_all}}},\n        {\"mount\",                   {3,     kMax, {false,  do_mount}}},\n        {\"perform_apex_config\",     {0,     1,    {false,  do_perform_apex_config}}},\n        {\"umount\",                  {1,     1,    {false,  do_umount}}},\n        {\"umount_all\",              {0,     1,    {false,  do_umount_all}}},\n        {\"update_linker_config\",    {0,     0,    {false,  do_update_linker_config}}},\n        {\"readahead\",               {1,     2,    {true,   do_readahead}}},\n        {\"restart\",                 {1,     2,    {false,  do_restart}}},\n        {\"restorecon\",              {1,     kMax, {true,   do_restorecon}}},\n        {\"restorecon_recursive\",    {1,     kMax, {true,   do_restorecon_recursive}}},\n        {\"rm\",                      {1,     1,    {true,   do_rm}}},\n        {\"rmdir\",                   {1,     1,    {true,   do_rmdir}}},\n        {\"setprop\",                 {2,     2,    {true,   do_setprop}}},\n        {\"setrlimit\",               {3,     3,    {false,  do_setrlimit}}},\n        {\"start\",                   {1,     1,    {false,  do_start}}},\n        {\"stop\",                    {1,     1,    {false,  do_stop}}},\n        {\"swapon_all\",              {0,     1,    {false,  do_swapon_all}}},\n        {\"swapoff\",                 {1,     1,    {false,  do_swapoff}}},\n        {\"enter_default_mount_ns\",  {0,     0,    {false,  do_enter_default_mount_ns}}},\n        {\"symlink\",                 {2,     2,    {true,   do_symlink}}},\n        {\"sysclktz\",                {1,     1,    {false,  do_sysclktz}}},\n        {\"trigger\",                 {1,     1,    {false,  do_trigger}}},\n        {\"verity_update_state\",     {0,     0,    {false,  do_verity_update_state}}},\n        {\"wait\",                    {1,     2,    {true,   do_wait}}},\n        {\"wait_for_prop\",           {2,     2,    {false,  do_wait_for_prop}}},\n        {\"write\",                   {2,     2,    {true,   do_write}}},\n    };\n    // clang-format on\n    return builtin_functions;\n}\n// Builtin-function-map end\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/builtins.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"builtin_arguments.h\"\n#include \"keyword_map.h\"\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nusing BuiltinFunction = std::function<Result<void>(const BuiltinArguments&)>;\n\nstruct BuiltinFunctionMapValue {\n    bool run_in_subcontext;\n    BuiltinFunction function;\n};\n\nusing BuiltinFunctionMap = KeywordMap<BuiltinFunctionMapValue>;\n\nconst BuiltinFunctionMap& GetBuiltinFunctionMap();\n\nextern std::vector<std::string> late_import_paths;\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/capabilities.cpp",
    "content": "// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"capabilities.h\"\n\n#include <sys/prctl.h>\n\n#include <map>\n#include <memory>\n\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n\n#define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }\n\nnamespace android {\nnamespace init {\n\nstatic const std::map<std::string, int> cap_map = {\n        CAP_MAP_ENTRY(CHOWN),\n        CAP_MAP_ENTRY(DAC_OVERRIDE),\n        CAP_MAP_ENTRY(DAC_READ_SEARCH),\n        CAP_MAP_ENTRY(FOWNER),\n        CAP_MAP_ENTRY(FSETID),\n        CAP_MAP_ENTRY(KILL),\n        CAP_MAP_ENTRY(SETGID),\n        CAP_MAP_ENTRY(SETUID),\n        CAP_MAP_ENTRY(SETPCAP),\n        CAP_MAP_ENTRY(LINUX_IMMUTABLE),\n        CAP_MAP_ENTRY(NET_BIND_SERVICE),\n        CAP_MAP_ENTRY(NET_BROADCAST),\n        CAP_MAP_ENTRY(NET_ADMIN),\n        CAP_MAP_ENTRY(NET_RAW),\n        CAP_MAP_ENTRY(IPC_LOCK),\n        CAP_MAP_ENTRY(IPC_OWNER),\n        CAP_MAP_ENTRY(SYS_MODULE),\n        CAP_MAP_ENTRY(SYS_RAWIO),\n        CAP_MAP_ENTRY(SYS_CHROOT),\n        CAP_MAP_ENTRY(SYS_PTRACE),\n        CAP_MAP_ENTRY(SYS_PACCT),\n        CAP_MAP_ENTRY(SYS_ADMIN),\n        CAP_MAP_ENTRY(SYS_BOOT),\n        CAP_MAP_ENTRY(SYS_NICE),\n        CAP_MAP_ENTRY(SYS_RESOURCE),\n        CAP_MAP_ENTRY(SYS_TIME),\n        CAP_MAP_ENTRY(SYS_TTY_CONFIG),\n        CAP_MAP_ENTRY(MKNOD),\n        CAP_MAP_ENTRY(LEASE),\n        CAP_MAP_ENTRY(AUDIT_WRITE),\n        CAP_MAP_ENTRY(AUDIT_CONTROL),\n        CAP_MAP_ENTRY(SETFCAP),\n        CAP_MAP_ENTRY(MAC_OVERRIDE),\n        CAP_MAP_ENTRY(MAC_ADMIN),\n        CAP_MAP_ENTRY(SYSLOG),\n        CAP_MAP_ENTRY(WAKE_ALARM),\n        CAP_MAP_ENTRY(BLOCK_SUSPEND),\n        CAP_MAP_ENTRY(AUDIT_READ),\n        CAP_MAP_ENTRY(PERFMON),\n        CAP_MAP_ENTRY(BPF),\n        CAP_MAP_ENTRY(CHECKPOINT_RESTORE),\n};\n\nstatic_assert(CAP_LAST_CAP == CAP_CHECKPOINT_RESTORE, \"CAP_LAST_CAP is not CAP_CHECKPOINT_RESTORE\");\n\nstatic bool ComputeCapAmbientSupported() {\n#if defined(__ANDROID__)\n    return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;\n#else\n    return true;\n#endif\n}\n\nstatic unsigned int ComputeLastValidCap() {\n#if defined(__ANDROID__)\n    // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see\n    // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.\n    unsigned int last_valid_cap = CAP_WAKE_ALARM;\n    for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; ++last_valid_cap);\n\n    // |last_valid_cap| will be the first failing value.\n    return last_valid_cap - 1;\n#else\n    return CAP_LAST_CAP;\n#endif\n}\n\nstatic bool DropBoundingSet(const CapSet& to_keep) {\n    unsigned int last_valid_cap = GetLastValidCap();\n    // When dropping the bounding set, attempt to drop capabilities reported at\n    // run-time, not at compile-time.\n    // If the run-time kernel is older than the compile-time headers, this\n    // avoids dropping an invalid capability. If the run-time kernel is newer\n    // than the headers, this guarantees all capabilities (even those unknown at\n    // compile time) will be dropped.\n    for (size_t cap = 0; cap <= last_valid_cap; ++cap) {\n        if (cap < to_keep.size() && to_keep.test(cap)) {\n            // No need to drop this capability.\n            continue;\n        }\n        if (cap_drop_bound(cap) == -1) {\n            PLOG(ERROR) << \"cap_drop_bound(\" << cap << \") failed\";\n            return false;\n        }\n    }\n    return true;\n}\n\nstatic bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {\n    ScopedCaps caps(cap_init());\n\n    cap_clear(caps.get());\n    cap_value_t value[1];\n    for (size_t cap = 0; cap < to_keep.size(); ++cap) {\n        if (to_keep.test(cap)) {\n            value[0] = cap;\n            if (cap_set_flag(caps.get(), CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||\n                cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {\n                PLOG(ERROR) << \"cap_set_flag(INHERITABLE|PERMITTED, \" << cap << \") failed\";\n                return false;\n            }\n        }\n    }\n\n    if (add_setpcap) {\n        value[0] = CAP_SETPCAP;\n        if (cap_set_flag(caps.get(), CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||\n            cap_set_flag(caps.get(), CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {\n            PLOG(ERROR) << \"cap_set_flag(PERMITTED|EFFECTIVE, \" << CAP_SETPCAP << \") failed\";\n            return false;\n        }\n    }\n\n    if (cap_set_proc(caps.get()) != 0) {\n        PLOG(ERROR) << \"cap_set_proc(\" << to_keep.to_ulong() << \") failed\";\n        return false;\n    }\n    return true;\n}\n\nstatic bool SetAmbientCaps(const CapSet& to_raise) {\n#if defined(__ANDROID__)\n    for (size_t cap = 0; cap < to_raise.size(); ++cap) {\n        if (to_raise.test(cap)) {\n            if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {\n                PLOG(ERROR) << \"prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, \" << cap << \") failed\";\n                return false;\n            }\n        }\n    }\n#endif\n    return true;\n}\n\nint LookupCap(const std::string& cap_name) {\n    auto e = cap_map.find(cap_name);\n    if (e != cap_map.end()) {\n        return e->second;\n    } else {\n        return -1;\n    }\n}\n\nbool CapAmbientSupported() {\n    static bool cap_ambient_supported = ComputeCapAmbientSupported();\n    return cap_ambient_supported;\n}\n\nunsigned int GetLastValidCap() {\n    static unsigned int last_valid_cap = ComputeLastValidCap();\n    return last_valid_cap;\n}\n\nbool SetCapsForExec(const CapSet& to_keep) {\n    // Need to keep SETPCAP to drop bounding set below.\n    bool add_setpcap = true;\n    if (!SetProcCaps(to_keep, add_setpcap)) {\n        LOG(ERROR) << \"failed to apply initial capset\";\n        return false;\n    }\n\n    if (!DropBoundingSet(to_keep)) {\n        return false;\n    }\n\n    // If SETPCAP wasn't specifically requested, drop it now.\n    add_setpcap = false;\n    if (!SetProcCaps(to_keep, add_setpcap)) {\n        LOG(ERROR) << \"failed to apply final capset\";\n        return false;\n    }\n\n    // Add the capabilities to the ambient set so that they are preserved across\n    // execve(2).\n    // See http://man7.org/linux/man-pages/man7/capabilities.7.html.\n    return SetAmbientCaps(to_keep);\n}\n\nbool DropInheritableCaps() {\n    ScopedCaps caps(cap_get_proc());\n    if (cap_clear_flag(caps.get(), CAP_INHERITABLE) == -1) {\n        PLOG(ERROR) << \"cap_clear_flag(INHERITABLE) failed\";\n        return false;\n    }\n    if (cap_set_proc(caps.get()) != 0) {\n        PLOG(ERROR) << \"cap_set_proc() failed\";\n        return false;\n    }\n    return true;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/capabilities.h",
    "content": "// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef _INIT_CAPABILITIES_H\n#define _INIT_CAPABILITIES_H\n\n#include <sys/capability.h>\n\n#include <bitset>\n#include <memory>\n#include <string>\n#include <type_traits>\n\nnamespace android {\nnamespace init {\n\nstruct CapDeleter {\n    void operator()(cap_t caps) const { cap_free(caps); }\n};\n\nusing CapSet = std::bitset<CAP_LAST_CAP + 1>;\nusing ScopedCaps = std::unique_ptr<std::remove_pointer<cap_t>::type, CapDeleter>;\n\nint LookupCap(const std::string& cap_name);\nbool CapAmbientSupported();\nunsigned int GetLastValidCap();\nbool SetCapsForExec(const CapSet& to_keep);\nbool DropInheritableCaps();\n\n}  // namespace init\n}  // namespace android\n\n#endif  // _INIT_CAPABILITIES_H\n"
  },
  {
    "path": "init/check_builtins.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Note that these check functions cannot check expanded arguments from properties, since they will\n// not know what those properties would be at runtime.  They will be passed an empty string in the\n// situation that the input line had a property expansion without a default value, since an empty\n// string is otherwise an impossible value.  They should therefore disregard checking empty\n// arguments.\n\n#include \"check_builtins.h\"\n\n#include <sys/time.h>\n\n#include <android-base/logging.h>\n#include <android-base/parsedouble.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <property_info_parser/property_info_parser.h>\n\n#include \"builtin_arguments.h\"\n#include \"interface_utils.h\"\n#include \"property_type.h\"\n#include \"rlimit_parser.h\"\n#include \"service.h\"\n#include \"util.h\"\n\nusing android::base::ParseInt;\nusing android::base::StartsWith;\nusing android::properties::BuildTrie;\nusing android::properties::PropertyInfoArea;\nusing android::properties::PropertyInfoEntry;\n\n#define ReturnIfAnyArgsEmpty()     \\\n    for (const auto& arg : args) { \\\n        if (arg.empty()) {         \\\n            return {};             \\\n        }                          \\\n    }\n\nnamespace android {\nnamespace init {\n\nconst PropertyInfoArea* property_info_area;\n\nResult<void> InitializeHostPropertyInfoArea(const std::vector<PropertyInfoEntry>& property_infos) {\n    static std::string serialized_contexts;\n    std::string trie_error;\n    if (!BuildTrie(property_infos, \"u:object_r:default_prop:s0\", \"string\", &serialized_contexts,\n                   &trie_error)) {\n        return Error() << \"Unable to serialize property contexts: \" << trie_error;\n    }\n\n    property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_contexts.c_str());\n    return {};\n}\n\nstatic Result<void> check_stub(const BuiltinArguments& args) {\n    return {};\n}\n\n#include \"generated_stub_builtin_function_map.h\"\n\nResult<void> check_chown(const BuiltinArguments& args) {\n    if (!args[1].empty()) {\n        auto uid = DecodeUid(args[1]);\n        if (!uid.ok()) {\n            return Error() << \"Unable to decode UID for '\" << args[1] << \"': \" << uid.error();\n        }\n    }\n\n    // GID is optional and pushes the index of path out by one if specified.\n    if (args.size() == 4 && !args[2].empty()) {\n        auto gid = DecodeUid(args[2]);\n        if (!gid.ok()) {\n            return Error() << \"Unable to decode GID for '\" << args[2] << \"': \" << gid.error();\n        }\n    }\n\n    return {};\n}\n\nResult<void> check_exec(const BuiltinArguments& args) {\n    ReturnIfAnyArgsEmpty();\n\n    auto result = Service::MakeTemporaryOneshotService(args.args);\n    if (!result.ok()) {\n        return result.error();\n    }\n\n    return {};\n}\n\nResult<void> check_exec_background(const BuiltinArguments& args) {\n    return check_exec(std::move(args));\n}\n\nResult<void> check_exec_reboot_on_failure(const BuiltinArguments& args) {\n    BuiltinArguments remaining_args{.context = args.context};\n\n    remaining_args.args = std::vector<std::string>(args.begin() + 1, args.end());\n    remaining_args.args[0] = args[0];\n\n    return check_exec(remaining_args);\n}\n\nResult<void> check_interface_restart(const BuiltinArguments& args) {\n    if (auto result = IsKnownInterface(args[1]); !result.ok()) {\n        return result.error();\n    }\n    return {};\n}\n\nResult<void> check_interface_start(const BuiltinArguments& args) {\n    return check_interface_restart(std::move(args));\n}\n\nResult<void> check_interface_stop(const BuiltinArguments& args) {\n    return check_interface_restart(std::move(args));\n}\n\nResult<void> check_load_system_props(const BuiltinArguments& args) {\n    return Error() << \"'load_system_props' is deprecated\";\n}\n\nResult<void> check_loglevel(const BuiltinArguments& args) {\n    ReturnIfAnyArgsEmpty();\n\n    int log_level = -1;\n    ParseInt(args[1], &log_level);\n    if (log_level < 0 || log_level > 7) {\n        return Error() << \"loglevel must be in the range of 0-7\";\n    }\n    return {};\n}\n\nResult<void> check_mount_all(const BuiltinArguments& args) {\n    auto options = ParseMountAll(args.args);\n    if (!options.ok()) {\n        return options.error();\n    }\n    return {};\n}\n\nResult<void> check_mkdir(const BuiltinArguments& args) {\n    auto options = ParseMkdir(args.args);\n    if (!options.ok()) {\n        return options.error();\n    }\n    return {};\n}\n\nResult<void> check_restorecon(const BuiltinArguments& args) {\n    ReturnIfAnyArgsEmpty();\n\n    auto restorecon_info = ParseRestorecon(args.args);\n    if (!restorecon_info.ok()) {\n        return restorecon_info.error();\n    }\n\n    return {};\n}\n\nResult<void> check_restorecon_recursive(const BuiltinArguments& args) {\n    return check_restorecon(std::move(args));\n}\n\nResult<void> check_setprop(const BuiltinArguments& args) {\n    const std::string& name = args[1];\n    if (name.empty()) {\n        return {};\n    }\n    const std::string& value = args[2];\n\n    if (!IsLegalPropertyName(name)) {\n        return Error() << \"'\" << name << \"' is not a legal property name\";\n    }\n\n    if (!value.empty()) {\n        if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {\n            return result.error();\n        }\n    }\n\n    if (StartsWith(name, \"ctl.\")) {\n        return Error()\n               << \"Do not set ctl. properties from init; call the Service functions directly\";\n    }\n\n    static constexpr const char kRestoreconProperty[] = \"selinux.restorecon_recursive\";\n    if (name == kRestoreconProperty) {\n        return Error() << \"Do not set '\" << kRestoreconProperty\n                       << \"' from init; use the restorecon builtin directly\";\n    }\n\n    const char* target_context = nullptr;\n    const char* type = nullptr;\n    property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);\n\n    if (!CheckType(type, value)) {\n        return Error() << \"Property type check failed, value doesn't match expected type '\"\n                       << (type ?: \"(null)\") << \"'\";\n    }\n\n    return {};\n}\n\nResult<void> check_setrlimit(const BuiltinArguments& args) {\n    ReturnIfAnyArgsEmpty();\n\n    auto rlimit = ParseRlimit(args.args);\n    if (!rlimit.ok()) return rlimit.error();\n    return {};\n}\n\nResult<void> check_swapon_all(const BuiltinArguments& args) {\n    auto options = ParseSwaponAll(args.args);\n    if (!options.ok()) {\n        return options.error();\n    }\n    return {};\n}\n\nResult<void> check_sysclktz(const BuiltinArguments& args) {\n    ReturnIfAnyArgsEmpty();\n\n    struct timezone tz = {};\n    if (!android::base::ParseInt(args[1], &tz.tz_minuteswest)) {\n        return Error() << \"Unable to parse mins_west_of_gmt\";\n    }\n    return {};\n}\n\nResult<void> check_umount_all(const BuiltinArguments& args) {\n    auto options = ParseUmountAll(args.args);\n    if (!options.ok()) {\n        return options.error();\n    }\n    return {};\n}\n\nResult<void> check_wait(const BuiltinArguments& args) {\n    if (args.size() == 3 && !args[2].empty()) {\n        double timeout_double;\n        if (!android::base::ParseDouble(args[2], &timeout_double, 0)) {\n            return Error() << \"failed to parse timeout\";\n        }\n    }\n    return {};\n}\n\nResult<void> check_wait_for_prop(const BuiltinArguments& args) {\n    return check_setprop(std::move(args));\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/check_builtins.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include \"builtin_arguments.h\"\n#include \"result.h\"\n\n#include <vector>\n\n#include <property_info_serializer/property_info_serializer.h>\n\nnamespace android {\nnamespace init {\n\nResult<void> check_chown(const BuiltinArguments& args);\nResult<void> check_exec(const BuiltinArguments& args);\nResult<void> check_exec_background(const BuiltinArguments& args);\nResult<void> check_exec_reboot_on_failure(const BuiltinArguments& args);\nResult<void> check_interface_restart(const BuiltinArguments& args);\nResult<void> check_interface_start(const BuiltinArguments& args);\nResult<void> check_interface_stop(const BuiltinArguments& args);\nResult<void> check_load_system_props(const BuiltinArguments& args);\nResult<void> check_loglevel(const BuiltinArguments& args);\nResult<void> check_mkdir(const BuiltinArguments& args);\nResult<void> check_mount_all(const BuiltinArguments& args);\nResult<void> check_restorecon(const BuiltinArguments& args);\nResult<void> check_restorecon_recursive(const BuiltinArguments& args);\nResult<void> check_setprop(const BuiltinArguments& args);\nResult<void> check_setrlimit(const BuiltinArguments& args);\nResult<void> check_swapon_all(const BuiltinArguments& args);\nResult<void> check_sysclktz(const BuiltinArguments& args);\nResult<void> check_umount_all(const BuiltinArguments& args);\nResult<void> check_wait(const BuiltinArguments& args);\nResult<void> check_wait_for_prop(const BuiltinArguments& args);\n\nResult<void> InitializeHostPropertyInfoArea(\n        const std::vector<properties::PropertyInfoEntry>& property_infos);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/compare-bootcharts.py",
    "content": "#!/usr/bin/env python\n\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Compare two bootcharts and list start/end timestamps on key processes.\n\nThis script extracts two bootchart.tgz files and compares the timestamps\nin proc_ps.log for selected processes. The proc_ps.log file consists of\nrepetitive blocks of the following format:\n\ntimestamp1 (jiffies)\ndumps of /proc/<pid>/stat\n\ntimestamp2\ndumps of /proc/<pid>/stat\n\nThe timestamps are 200ms apart, and the creation time of selected processes\nare listed. The termination time of the boot animation process is also listed\nas a coarse indication about when the boot process is complete as perceived by\nthe user.\n\"\"\"\n\nimport sys\nimport tarfile\n\n# The bootchart timestamps are 200ms apart, but the USER_HZ value is not\n# reported in the bootchart, so we use the first two timestamps to calculate\n# the wall clock time of a jiffy.\njiffy_to_wallclock = {\n   '1st_timestamp': -1,\n   '2nd_timestamp': -1,\n   'jiffy_to_wallclock': -1\n}\n\ndef analyze_process_maps(process_map1, process_map2, jiffy_record):\n    # List interesting processes here\n    processes_of_interest = [\n        '/system/bin/init',\n        '/system/bin/surfaceflinger',\n        '/system/bin/bootanimation',\n        'zygote64',\n        'zygote',\n        'system_server'\n    ]\n\n    jw = jiffy_record['jiffy_to_wallclock']\n    print(\"process: baseline experiment (delta)\")\n    print(\" - Unit is ms (a jiffy is %d ms on the system)\" % jw)\n    print(\"------------------------------------\")\n    for p in processes_of_interest:\n        # e.g., 32-bit system doesn't have zygote64\n        if p in process_map1 and p in process_map2:\n            print(\"%s: %d %d (%+d)\" % (\n                p, process_map1[p]['start_time'] * jw,\n                process_map2[p]['start_time'] * jw,\n                (process_map2[p]['start_time'] -\n                 process_map1[p]['start_time']) * jw))\n\n    # Print the last tick for the bootanimation process\n    print(\"bootanimation ends at: %d %d (%+d)\" % (\n        process_map1['/system/bin/bootanimation']['last_tick'] * jw,\n        process_map2['/system/bin/bootanimation']['last_tick'] * jw,\n        (process_map2['/system/bin/bootanimation']['last_tick'] -\n            process_map1['/system/bin/bootanimation']['last_tick']) * jw))\n\ndef parse_proc_file(pathname, process_map, jiffy_record=None):\n    # Uncompress bootchart.tgz\n    with tarfile.open(pathname + '/bootchart.tgz', 'r:*') as tf:\n        try:\n            # Read proc_ps.log\n            f = tf.extractfile('proc_ps.log')\n\n            # Break proc_ps into chunks based on timestamps\n            blocks = f.read().decode('utf-8').split('\\n\\n')\n            for b in blocks:\n                lines = b.split('\\n')\n                if not lines[0]:\n                    break\n\n                # 200ms apart in jiffies\n                timestamp = int(lines[0]);\n\n                # Figure out the wall clock time of a jiffy\n                if jiffy_record is not None:\n                    if jiffy_record['1st_timestamp'] == -1:\n                        jiffy_record['1st_timestamp'] = timestamp\n                    elif jiffy_record['jiffy_to_wallclock'] == -1:\n                        # Not really needed but for debugging purposes\n                        jiffy_record['2nd_timestamp'] = timestamp\n                        value = 200 / (timestamp -\n                                       jiffy_record['1st_timestamp'])\n                        # Fix the rounding error\n                        # e.g., 201 jiffies in 200ms when USER_HZ is 1000\n                        if value == 0:\n                            value = 1\n                        # e.g., 21 jiffies in 200ms when USER_HZ is 100\n                        elif value == 9:\n                            value = 10\n                        jiffy_record['jiffy_to_wallclock'] = value\n\n                # Populate the process_map table\n                for line in lines[1:]:\n                    segs = line.split(' ')\n\n                    #  0: pid\n                    #  1: process name\n                    # 17: priority\n                    # 18: nice\n                    # 21: creation time\n\n                    proc_name = segs[1].strip('()')\n                    if proc_name in process_map:\n                        process = process_map[proc_name]\n                    else:\n                        process = {'start_time': int(segs[21])}\n                        process_map[proc_name] = process\n\n                    process['last_tick'] = timestamp\n        finally:\n            f.close()\n\ndef main():\n    if len(sys.argv) != 3:\n        print(\"Usage: %s base_bootchart_dir exp_bootchart_dir\" % sys.argv[0])\n        sys.exit(1)\n\n    process_map1 = {}\n    process_map2 = {}\n    parse_proc_file(sys.argv[1], process_map1, jiffy_to_wallclock)\n    parse_proc_file(sys.argv[2], process_map2)\n    analyze_process_maps(process_map1, process_map2, jiffy_to_wallclock)\n\nif __name__ == \"__main__\":\n    main()\n"
  },
  {
    "path": "init/debug_ramdisk.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace android {\nnamespace init {\n\nconstexpr const char kDebugRamdiskProp[] = \"/debug_ramdisk/adb_debug.prop\";\nconstexpr const char kDebugRamdiskSEPolicy[] = \"/debug_ramdisk/userdebug_plat_sepolicy.cil\";\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/devices.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"devices.h\"\n\n#include <errno.h>\n#include <fnmatch.h>\n#include <sys/sysmacros.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <filesystem>\n#include <memory>\n#include <string>\n#include <string_view>\n#include <thread>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n#include <libdm/dm.h>\n#include <private/android_filesystem_config.h>\n#include <selinux/android.h>\n#include <selinux/selinux.h>\n\n#include \"selabel.h\"\n#include \"util.h\"\n\nusing namespace std::chrono_literals;\n\nusing android::base::Basename;\nusing android::base::ConsumePrefix;\nusing android::base::Dirname;\nusing android::base::ReadFileToString;\nusing android::base::Readlink;\nusing android::base::Realpath;\nusing android::base::Split;\nusing android::base::StartsWith;\nusing android::base::StringPrintf;\nusing android::base::Trim;\n\nnamespace android {\nnamespace init {\n\n/* Given a path that may start with a PCI device, populate the supplied buffer\n * with the PCI domain/bus number and the peripheral ID and return 0.\n * If it doesn't start with a PCI device, or there is some error, return -1 */\nstatic bool FindPciDevicePrefix(const std::string& path, std::string* result) {\n    result->clear();\n\n    if (!StartsWith(path, \"/devices/pci\")) return false;\n\n    /* Beginning of the prefix is the initial \"pci\" after \"/devices/\" */\n    std::string::size_type start = 9;\n\n    /* End of the prefix is two path '/' later, capturing the domain/bus number\n     * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */\n    auto end = path.find('/', start);\n    if (end == std::string::npos) return false;\n\n    end = path.find('/', end + 1);\n    if (end == std::string::npos) return false;\n\n    auto length = end - start;\n    if (length <= 4) {\n        // The minimum string that will get to this check is 'pci/', which is malformed,\n        // so return false\n        return false;\n    }\n\n    *result = path.substr(start, length);\n    return true;\n}\n\n/* Given a path that may start with a virtual block device, populate\n * the supplied buffer with the virtual block device ID and return 0.\n * If it doesn't start with a virtual block device, or there is some\n * error, return -1 */\nstatic bool FindVbdDevicePrefix(const std::string& path, std::string* result) {\n    result->clear();\n\n    if (!StartsWith(path, \"/devices/vbd-\")) return false;\n\n    /* Beginning of the prefix is the initial \"vbd-\" after \"/devices/\" */\n    std::string::size_type start = 13;\n\n    /* End of the prefix is one path '/' later, capturing the\n       virtual block device ID. Example: 768 */\n    auto end = path.find('/', start);\n    if (end == std::string::npos) return false;\n\n    auto length = end - start;\n    if (length == 0) return false;\n\n    *result = path.substr(start, length);\n    return true;\n}\n\n// Given a path that may start with a virtual dm block device, populate\n// the supplied buffer with the dm module's instantiated name.\n// If it doesn't start with a virtual block device, or there is some\n// error, return false.\nstatic bool FindDmDevice(const Uevent& uevent, std::string* name, std::string* uuid) {\n    if (!StartsWith(uevent.path, \"/devices/virtual/block/dm-\")) return false;\n    if (uevent.action == \"remove\") return false;  // Avoid error spam from ioctl\n\n    dev_t dev = makedev(uevent.major, uevent.minor);\n\n    auto& dm = android::dm::DeviceMapper::Instance();\n    return dm.GetDeviceNameAndUuid(dev, name, uuid);\n}\n\nPermissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid,\n                         bool no_fnm_pathname)\n    : name_(name),\n      perm_(perm),\n      uid_(uid),\n      gid_(gid),\n      prefix_(false),\n      wildcard_(false),\n      no_fnm_pathname_(no_fnm_pathname) {\n    // Set 'prefix_' or 'wildcard_' based on the below cases:\n    //\n    // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict\n    //    equality with 'name'\n    //\n    // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and\n    //    Match() checks if 'name' is a prefix of a given path.\n    //\n    // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()\n    //    with FNM_PATHNAME to compare 'name' to a given path.\n    auto wildcard_position = name_.find('*');\n    if (wildcard_position != std::string::npos) {\n        if (wildcard_position == name_.length() - 1) {\n            prefix_ = true;\n            name_.pop_back();\n        } else {\n            wildcard_ = true;\n        }\n    }\n}\n\nbool Permissions::Match(const std::string& path) const {\n    if (prefix_) return StartsWith(path, name_);\n    if (wildcard_)\n        return fnmatch(name_.c_str(), path.c_str(), no_fnm_pathname_ ? 0 : FNM_PATHNAME) == 0;\n    return path == name_;\n}\n\nbool SysfsPermissions::MatchWithSubsystem(const std::string& path,\n                                          const std::string& subsystem) const {\n    std::string path_basename = Basename(path);\n    if (name().find(subsystem) != std::string::npos) {\n        if (Match(\"/sys/class/\" + subsystem + \"/\" + path_basename)) return true;\n        if (Match(\"/sys/bus/\" + subsystem + \"/devices/\" + path_basename)) return true;\n    }\n    return Match(path);\n}\n\nvoid SysfsPermissions::SetPermissions(const std::string& path) const {\n    std::string attribute_file = path + \"/\" + attribute_;\n    LOG(VERBOSE) << \"fixup \" << attribute_file << \" \" << uid() << \" \" << gid() << \" \" << std::oct\n                 << perm();\n\n    if (access(attribute_file.c_str(), F_OK) == 0) {\n        if (chown(attribute_file.c_str(), uid(), gid()) != 0) {\n            PLOG(ERROR) << \"chown(\" << attribute_file << \", \" << uid() << \", \" << gid()\n                        << \") failed\";\n        }\n        if (chmod(attribute_file.c_str(), perm()) != 0) {\n            PLOG(ERROR) << \"chmod(\" << attribute_file << \", \" << perm() << \") failed\";\n        }\n    }\n}\n\nBlockDeviceInfo DeviceHandler::GetBlockDeviceInfo(const std::string& uevent_path) const {\n    BlockDeviceInfo info;\n\n    if (!boot_part_uuid_.empty()) {\n        // Only use the more specific \"MMC\" / \"NVME\" / \"SCSI\" match if a\n        // partition UUID was passed.\n        //\n        // Old bootloaders that aren't passing the partition UUID instead\n        // pass the path to the closest \"platform\" device. It would\n        // break them if we chose this deeper (more specific) path.\n        //\n        // When we have a UUID we _want_ the more specific path since it can\n        // handle, for instance, differentiating two USB disks that are on\n        // the same USB controller. Using the closest platform device would\n        // classify them both the same by using the path to the USB controller.\n        if (FindMmcDevice(uevent_path, &info.str)) {\n            info.type = \"mmc\";\n        } else if (FindNvmeDevice(uevent_path, &info.str)) {\n            info.type = \"nvme\";\n        } else if (FindScsiDevice(uevent_path, &info.str)) {\n            info.type = \"scsi\";\n        }\n    } else if (FindPlatformDevice(uevent_path, &info.str)) {\n        info.type = \"platform\";\n    } else if (FindPciDevicePrefix(uevent_path, &info.str)) {\n        info.type = \"pci\";\n    } else if (FindVbdDevicePrefix(uevent_path, &info.str)) {\n        info.type = \"vbd\";\n    } else {\n        // Re-clear device to be extra certain in case one of the FindXXX()\n        // functions returned false but still modified it.\n        info.str = \"\";\n    }\n\n    info.is_boot_device = boot_devices_.find(info.str) != boot_devices_.end();\n\n    return info;\n}\n\nbool DeviceHandler::IsBootDeviceStrict() const {\n    // When using the newer \"boot_part_uuid\" to specify the boot device then\n    // we require all core system partitions to be on the boot device.\n    return !boot_part_uuid_.empty();\n}\n\nbool DeviceHandler::IsBootDevice(const Uevent& uevent) const {\n    auto device = GetBlockDeviceInfo(uevent.path);\n    return device.is_boot_device;\n}\n\nstd::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) {\n    static const auto partition_map = [] {\n        std::vector<std::pair<std::string, std::string>> partition_map;\n        auto parser = [&partition_map](const std::string& key, const std::string& value) {\n            if (key != \"androidboot.partition_map\") {\n                return;\n            }\n            for (const auto& map : Split(value, \";\")) {\n                auto map_pieces = Split(map, \",\");\n                if (map_pieces.size() != 2) {\n                    LOG(ERROR) << \"Expected a comma separated device,partition mapping, but found '\"\n                               << map << \"'\";\n                    continue;\n                }\n                partition_map.emplace_back(map_pieces[0], map_pieces[1]);\n            }\n        };\n        android::fs_mgr::ImportKernelCmdline(parser);\n        android::fs_mgr::ImportBootconfig(parser);\n        return partition_map;\n    }();\n\n    for (const auto& [device, partition] : partition_map) {\n        if (query_device == device) {\n            return partition;\n        }\n    }\n    return {};\n}\n\n// Given a path to a device that may have a parent in the passed set of\n// subsystems, find the parent device that's in the passed set of subsystems.\n// If we don't find a parent in the passed set of subsystems, return false.\nbool DeviceHandler::FindSubsystemDevice(std::string path, std::string* device_path,\n                                        const std::set<std::string>& subsystem_paths) const {\n    device_path->clear();\n\n    // Uevents don't contain the mount point, so we need to add it here.\n    path.insert(0, sysfs_mount_point_);\n\n    std::string directory = Dirname(path);\n\n    while (directory != \"/\" && directory != \".\") {\n        std::string subsystem_link_path;\n        if (Realpath(directory + \"/subsystem\", &subsystem_link_path) &&\n            subsystem_paths.find(subsystem_link_path) != subsystem_paths.end()) {\n            // We need to remove the mount point that we added above before returning.\n            directory.erase(0, sysfs_mount_point_.size());\n\n            // Skip /devices/platform or /devices/ if present\n            static constexpr std::string_view devices_platform_prefix = \"/devices/platform/\";\n            static constexpr std::string_view devices_prefix = \"/devices/\";\n            std::string_view sv = directory;\n\n            if (!ConsumePrefix(&sv, devices_platform_prefix)) {\n                ConsumePrefix(&sv, devices_prefix);\n            }\n            *device_path = sv;\n\n            return true;\n        }\n\n        auto last_slash = path.rfind('/');\n        if (last_slash == std::string::npos) return false;\n\n        path.erase(last_slash);\n        directory = Dirname(path);\n    }\n\n    return false;\n}\n\nbool DeviceHandler::FindPlatformDevice(const std::string& path,\n                                       std::string* platform_device_path) const {\n    const std::set<std::string> subsystem_paths = {\n            sysfs_mount_point_ + \"/bus/platform\",\n            sysfs_mount_point_ + \"/bus/amba\",\n    };\n\n    return FindSubsystemDevice(path, platform_device_path, subsystem_paths);\n}\n\nbool DeviceHandler::FindMmcDevice(const std::string& path, std::string* mmc_device_path) const {\n    const std::set<std::string> subsystem_paths = {\n            sysfs_mount_point_ + \"/bus/mmc\",\n    };\n\n    return FindSubsystemDevice(path, mmc_device_path, subsystem_paths);\n}\n\nbool DeviceHandler::FindNvmeDevice(const std::string& path, std::string* nvme_device_path) const {\n    const std::set<std::string> subsystem_paths = {\n            sysfs_mount_point_ + \"/class/nvme\",\n    };\n\n    return FindSubsystemDevice(path, nvme_device_path, subsystem_paths);\n}\n\nbool DeviceHandler::FindScsiDevice(const std::string& path, std::string* scsi_device_path) const {\n    const std::set<std::string> subsystem_paths = {\n            sysfs_mount_point_ + \"/bus/scsi\",\n    };\n\n    return FindSubsystemDevice(path, scsi_device_path, subsystem_paths);\n}\n\nvoid DeviceHandler::TrackDeviceUevent(const Uevent& uevent) {\n    // No need to track any events if we won't bother handling any bind events\n    // later.\n    if (drivers_.size() == 0) return;\n\n    // Only track add, and not for block devices. We don't track remove because\n    // unbind events may arrive after remove events, so unbind will be the\n    // trigger to untrack those events.\n    if ((uevent.action != \"add\") || uevent.subsystem == \"block\" ||\n        (uevent.major < 0 || uevent.minor < 0)) {\n        return;\n    }\n\n    std::string path = sysfs_mount_point_ + uevent.path + \"/device\";\n    std::string device;\n    if (!Realpath(path, &device)) return;\n\n    tracked_uevents_.emplace_back(uevent, device);\n}\n\nvoid DeviceHandler::FixupSysPermissions(const std::string& upath,\n                                        const std::string& subsystem) const {\n    // upaths omit the \"/sys\" that paths in this list\n    // contain, so we prepend it...\n    std::string path = \"/sys\" + upath;\n\n    for (const auto& s : sysfs_permissions_) {\n        if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);\n    }\n\n    if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {\n        LOG(VERBOSE) << \"restorecon_recursive: \" << path;\n        if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {\n            PLOG(ERROR) << \"selinux_android_restorecon(\" << path << \") failed\";\n        }\n    }\n}\n\nstd::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(\n    const std::string& path, const std::vector<std::string>& links) const {\n    // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.\n    for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {\n        if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),\n                                           [it](const auto& link) { return it->Match(link); })) {\n            return {it->perm(), it->uid(), it->gid()};\n        }\n    }\n    /* Default if nothing found. */\n    return {0600, 0, 0};\n}\n\nvoid DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,\n                               const std::vector<std::string>& links) const {\n    auto [mode, uid, gid] = GetDevicePermissions(path, links);\n    mode |= (block ? S_IFBLK : S_IFCHR);\n\n    std::string secontext;\n    if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {\n        PLOG(ERROR) << \"Device '\" << path << \"' not created; cannot find SELinux label\";\n        return;\n    }\n    if (!secontext.empty()) {\n        setfscreatecon(secontext.c_str());\n    }\n\n    gid_t new_group = -1;\n\n    dev_t dev = makedev(major, minor);\n    /* Temporarily change egid to avoid race condition setting the gid of the\n     * device node. Unforunately changing the euid would prevent creation of\n     * some device nodes, so the uid has to be set with chown() and is still\n     * racy. Fixing the gid race at least fixed the issue with system_server\n     * opening dynamic input devices under the AID_INPUT gid. */\n    if (setegid(gid)) {\n        PLOG(ERROR) << \"setegid(\" << gid << \") for \" << path << \" device failed\";\n        goto out;\n    }\n    /* If the node already exists update its SELinux label and the file mode to handle cases when\n     * it was created with the wrong context and file mode during coldboot procedure. */\n    if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {\n        char* fcon = nullptr;\n        int rc = lgetfilecon(path.c_str(), &fcon);\n        if (rc < 0) {\n            PLOG(ERROR) << \"Cannot get SELinux label on '\" << path << \"' device\";\n            goto out;\n        }\n\n        bool different = fcon != secontext;\n        freecon(fcon);\n\n        if (different && lsetfilecon(path.c_str(), secontext.c_str())) {\n            PLOG(ERROR) << \"Cannot set '\" << secontext << \"' SELinux label on '\" << path\n                        << \"' device\";\n        }\n\n        struct stat s;\n        if (stat(path.c_str(), &s) == 0) {\n            if (gid != s.st_gid) {\n                new_group = gid;\n            }\n            if (mode != s.st_mode) {\n                if (chmod(path.c_str(), mode) != 0) {\n                    PLOG(ERROR) << \"Cannot chmod \" << path << \" to \" << mode;\n                }\n            }\n        } else {\n            PLOG(ERROR) << \"Cannot stat \" << path;\n        }\n    }\n\nout:\n    if (chown(path.c_str(), uid, new_group) < 0) {\n        PLOG(ERROR) << \"Cannot chown \" << path << \" \" << uid << \" \" << new_group;\n    }\n    if (setegid(AID_ROOT)) {\n        PLOG(FATAL) << \"setegid(AID_ROOT) failed\";\n    }\n\n    if (!secontext.empty()) {\n        setfscreatecon(nullptr);\n    }\n}\n\n// replaces any unacceptable characters with '_', the\n// length of the resulting string is equal to the input string\nvoid SanitizePartitionName(std::string* string) {\n    const char* accept =\n        \"abcdefghijklmnopqrstuvwxyz\"\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n        \"0123456789\"\n        \"_-.\";\n\n    if (!string) return;\n\n    std::string::size_type pos = 0;\n    while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {\n        (*string)[pos] = '_';\n    }\n}\n\nstd::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {\n    BlockDeviceInfo info;\n    std::string partition;\n    std::string uuid;\n\n    if (FindDmDevice(uevent, &partition, &uuid)) {\n        std::vector<std::string> symlinks = {\"/dev/block/mapper/\" + partition};\n        if (!uuid.empty()) {\n            symlinks.emplace_back(\"/dev/block/mapper/by-uuid/\" + uuid);\n        }\n        return symlinks;\n    }\n\n    info = GetBlockDeviceInfo(uevent.path);\n\n    if (info.type.empty()) {\n        return {};\n    }\n\n    std::vector<std::string> links;\n\n    LOG(VERBOSE) << \"found \" << info.type << \" device \" << info.str;\n\n    auto link_path = \"/dev/block/\" + info.type + \"/\" + info.str;\n\n    if (!uevent.partition_name.empty()) {\n        std::string partition_name_sanitized(uevent.partition_name);\n        SanitizePartitionName(&partition_name_sanitized);\n        if (partition_name_sanitized != uevent.partition_name) {\n            LOG(VERBOSE) << \"Linking partition '\" << uevent.partition_name << \"' as '\"\n                         << partition_name_sanitized << \"'\";\n        }\n        links.emplace_back(link_path + \"/by-name/\" + partition_name_sanitized);\n        // Adds symlink: /dev/block/by-name/<partition_name>.\n        if (info.is_boot_device) {\n            links.emplace_back(\"/dev/block/by-name/\" + partition_name_sanitized);\n        }\n    } else if (info.is_boot_device) {\n        // If we don't have a partition name but we are a partition on a boot device, create a\n        // symlink of /dev/block/by-name/<device_name> for symmetry.\n        links.emplace_back(\"/dev/block/by-name/\" + uevent.device_name);\n        auto partition_name = GetPartitionNameForDevice(uevent.device_name);\n        if (!partition_name.empty()) {\n            links.emplace_back(\"/dev/block/by-name/\" + partition_name);\n        }\n    }\n\n    std::string model;\n    if (ReadFileToString(\"/sys/class/block/\" + uevent.device_name + \"/queue/zoned\", &model) &&\n        !StartsWith(model, \"none\")) {\n        links.emplace_back(\"/dev/block/by-name/zoned_device\");\n        links.emplace_back(\"/dev/sys/block/by-name/zoned_device\");\n    }\n\n    auto last_slash = uevent.path.rfind('/');\n    links.emplace_back(link_path + \"/\" + uevent.path.substr(last_slash + 1));\n\n    return links;\n}\n\nstatic void RemoveDeviceMapperLinks(const std::string& devpath) {\n    std::vector<std::string> dirs = {\n            \"/dev/block/mapper\",\n            \"/dev/block/mapper/by-uuid\",\n    };\n    for (const auto& dir : dirs) {\n        if (access(dir.c_str(), F_OK) != 0) continue;\n\n        std::unique_ptr<DIR, decltype(&closedir)> dh(opendir(dir.c_str()), closedir);\n        if (!dh) {\n            PLOG(ERROR) << \"Failed to open directory \" << dir;\n            continue;\n        }\n\n        struct dirent* dp;\n        std::string link_path;\n        while ((dp = readdir(dh.get())) != nullptr) {\n            if (dp->d_type != DT_LNK) continue;\n\n            auto path = dir + \"/\" + dp->d_name;\n            if (Readlink(path, &link_path) && link_path == devpath) {\n                unlink(path.c_str());\n            }\n        }\n    }\n}\n\nvoid DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,\n                                 int major, int minor, const std::vector<std::string>& links) const {\n    if (action == \"add\") {\n        MakeDevice(devpath, block, major, minor, links);\n    }\n\n    // Handle device-mapper nodes.\n    // On kernels <= 5.10, the \"add\" event is fired on DM_DEV_CREATE, but does not contain name\n    // information until DM_TABLE_LOAD - thus, we wait for a \"change\" event.\n    // On kernels >= 5.15, the \"add\" event is fired on DM_TABLE_LOAD, followed by a \"change\"\n    // event.\n    if (action == \"add\" || (action == \"change\" && StartsWith(devpath, \"/dev/block/dm-\"))) {\n        for (const auto& link : links) {\n            std::string target;\n            if (StartsWith(link, \"/dev/block/\")) {\n                target = devpath;\n            } else if (StartsWith(link, \"/dev/sys/block/\")) {\n                target = \"/sys/class/block/\" + Basename(devpath);\n            } else {\n                LOG(ERROR) << \"Unrecognized link type: \" << link;\n                continue;\n            }\n\n            if (!mkdir_recursive(Dirname(link), 0755)) {\n                PLOG(ERROR) << \"Failed to create directory \" << Dirname(link);\n            }\n\n            // Create symlink and make sure it's correctly labeled\n            std::string secontext;\n            // Passing 0 for mode should work.\n            if (SelabelLookupFileContext(link, 0, &secontext) && !secontext.empty()) {\n                setfscreatecon(secontext.c_str());\n            }\n\n            int rc = symlink(target.c_str(), link.c_str());\n\n            if (!secontext.empty()) {\n                int save_errno = errno;\n                setfscreatecon(nullptr);\n                errno = save_errno;\n            }\n\n            if (rc < 0) {\n                if (errno != EEXIST) {\n                    PLOG(ERROR) << \"Failed to symlink \" << devpath << \" to \" << link;\n                } else if (std::string link_path;\n                           Readlink(link, &link_path) && link_path != devpath) {\n                    PLOG(ERROR) << \"Failed to symlink \" << devpath << \" to \" << link\n                                << \", which already links to: \" << link_path;\n                }\n            }\n        }\n    }\n\n    if (action == \"remove\") {\n        if (StartsWith(devpath, \"/dev/block/dm-\")) {\n            RemoveDeviceMapperLinks(devpath);\n        }\n        for (const auto& link : links) {\n            std::string link_path;\n            if (Readlink(link, &link_path) && link_path == devpath) {\n                unlink(link.c_str());\n            }\n        }\n        unlink(devpath.c_str());\n    }\n}\n\nvoid DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {\n    if (uevent.device_name == \"ashmem\") {\n        static const std::string boot_id_path = \"/proc/sys/kernel/random/boot_id\";\n        std::string boot_id;\n        if (!ReadFileToString(boot_id_path, &boot_id)) {\n            PLOG(ERROR) << \"Cannot duplicate ashmem device node. Failed to read \" << boot_id_path;\n            return;\n        }\n        boot_id = Trim(boot_id);\n\n        Uevent dup_ashmem_uevent = uevent;\n        dup_ashmem_uevent.device_name += boot_id;\n        dup_ashmem_uevent.path += boot_id;\n        HandleUevent(dup_ashmem_uevent);\n    }\n}\n\n// Check Uevents looking for the kernel's boot partition UUID\n//\n// When we can stop checking uevents (either because we're done or because\n// we weren't looking for the kernel's boot partition UUID) then return\n// true. Return false if we're not done yet.\nbool DeviceHandler::CheckUeventForBootPartUuid(const Uevent& uevent) {\n    // If we aren't using boot_part_uuid then we're done.\n    if (boot_part_uuid_.empty()) {\n        return true;\n    }\n\n    // Finding the boot partition is a one-time thing that we do at init\n    // time, not steady state. This is because the boot partition isn't\n    // allowed to go away or change. Once we found the boot partition we don't\n    // expect to run again.\n    if (found_boot_part_uuid_) {\n        LOG(WARNING) << __PRETTY_FUNCTION__\n                     << \" shouldn't run after kernel boot partition is found\";\n        return true;\n    }\n\n    // We only need to look at newly-added block devices. Note that if someone\n    // is replaying events all existing devices will get \"add\"ed.\n    if (uevent.subsystem != \"block\" || uevent.action != \"add\") {\n        return false;\n    }\n\n    // If it's not the partition we care about then move on.\n    if (uevent.partition_uuid != boot_part_uuid_) {\n        return false;\n    }\n\n    auto device = GetBlockDeviceInfo(uevent.path);\n\n    LOG(INFO) << \"Boot device \" << device.str << \" found via partition UUID\";\n    found_boot_part_uuid_ = true;\n    boot_devices_.clear();\n    boot_devices_.insert(device.str);\n\n    return true;\n}\n\nvoid DeviceHandler::HandleBindInternal(std::string driver_name, std::string action,\n                                       const Uevent& uevent) {\n    if (uevent.subsystem == \"block\") {\n        LOG(FATAL) << \"Tried to handle bind event for block device\";\n    }\n\n    // Get tracked uevents for all devices that have this uevent's path as\n    // their canonical device path. Then handle those again if their driver\n    // is one of the ones we're interested in.\n    const auto driver = std::find(drivers_.cbegin(), drivers_.cend(), driver_name);\n    if (driver == drivers_.cend()) return;\n\n    std::string bind_path = sysfs_mount_point_ + uevent.path;\n    for (const TrackedUevent& tracked : tracked_uevents_) {\n        if (tracked.canonical_device_path != bind_path) continue;\n\n        LOG(VERBOSE) << \"Propagating \" << uevent.action << \" as \" << action << \" for \"\n                     << uevent.path;\n\n        std::string devpath = driver->ParseDevPath(tracked.uevent);\n        mkdir_recursive(Dirname(devpath), 0755);\n        HandleDevice(action, devpath, false, tracked.uevent.major, tracked.uevent.minor,\n                     std::vector<std::string>{});\n    }\n}\n\nvoid DeviceHandler::HandleUevent(const Uevent& uevent) {\n    if (uevent.action == \"add\" || uevent.action == \"change\" || uevent.action == \"bind\" ||\n        uevent.action == \"online\") {\n        FixupSysPermissions(uevent.path, uevent.subsystem);\n    }\n\n    if (uevent.action == \"bind\") {\n        bound_drivers_[uevent.path] = uevent.driver;\n        HandleBindInternal(uevent.driver, \"add\", uevent);\n        return;\n    } else if (uevent.action == \"unbind\") {\n        if (bound_drivers_.count(uevent.path) == 0) return;\n        HandleBindInternal(bound_drivers_[uevent.path], \"remove\", uevent);\n\n        std::string sys_path = sysfs_mount_point_ + uevent.path;\n        std::erase_if(tracked_uevents_, [&sys_path](const TrackedUevent& tracked) {\n            return sys_path == tracked.canonical_device_path;\n        });\n        return;\n    }\n\n    // if it's not a /dev device, nothing to do\n    if (uevent.major < 0 || uevent.minor < 0) return;\n\n    std::string devpath;\n    std::vector<std::string> links;\n    bool block = false;\n\n    TrackDeviceUevent(uevent);\n\n    if (uevent.subsystem == \"block\") {\n        block = true;\n        devpath = \"/dev/block/\" + Basename(uevent.path);\n\n        if (StartsWith(uevent.path, \"/devices\")) {\n            links = GetBlockDeviceSymlinks(uevent);\n        }\n    } else if (const auto subsystem =\n                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);\n               subsystem != subsystems_.cend()) {\n        devpath = subsystem->ParseDevPath(uevent);\n    } else if (uevent.subsystem == \"usb\") {\n        if (!uevent.device_name.empty()) {\n            devpath = \"/dev/\" + uevent.device_name;\n        } else {\n            // This imitates the file system that would be created\n            // if we were using devfs instead.\n            // Minors are broken up into groups of 128, starting at \"001\"\n            int bus_id = uevent.minor / 128 + 1;\n            int device_id = uevent.minor % 128 + 1;\n            devpath = StringPrintf(\"/dev/bus/usb/%03d/%03d\", bus_id, device_id);\n        }\n    } else if (StartsWith(uevent.subsystem, \"usb\")) {\n        // ignore other USB events\n        return;\n    } else if (uevent.subsystem == \"misc\" && StartsWith(uevent.device_name, \"dm-user/\")) {\n        devpath = \"/dev/dm-user/\" + uevent.device_name.substr(8);\n    } else if (uevent.subsystem == \"misc\" && uevent.device_name == \"vfio/vfio\") {\n        devpath = \"/dev/\" + uevent.device_name;\n    } else {\n        devpath = \"/dev/\" + Basename(uevent.path);\n    }\n\n    mkdir_recursive(Dirname(devpath), 0755);\n\n    HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);\n\n    // Duplicate /dev/ashmem device and name it /dev/ashmem<boot_id>.\n    // TODO(b/111903542): remove once all users of /dev/ashmem are migrated to libcutils API.\n    HandleAshmemUevent(uevent);\n}\n\nvoid DeviceHandler::ColdbootDone() {\n    skip_restorecon_ = false;\n}\n\nDeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,\n                             std::vector<SysfsPermissions> sysfs_permissions,\n                             std::vector<Subsystem> drivers, std::vector<Subsystem> subsystems,\n                             std::set<std::string> boot_devices, std::string boot_part_uuid,\n                             bool skip_restorecon)\n    : dev_permissions_(std::move(dev_permissions)),\n      sysfs_permissions_(std::move(sysfs_permissions)),\n      drivers_(std::move(drivers)),\n      subsystems_(std::move(subsystems)),\n      boot_devices_(std::move(boot_devices)),\n      boot_part_uuid_(boot_part_uuid),\n      skip_restorecon_(skip_restorecon),\n      sysfs_mount_point_(\"/sys\") {\n    // If both a boot partition UUID and a list of boot devices are\n    // specified then we ignore the boot_devices in favor of boot_part_uuid.\n    if (boot_devices_.size() && !boot_part_uuid.empty()) {\n        LOG(WARNING) << \"Both boot_devices and boot_part_uuid provided; ignoring bootdevices\";\n        boot_devices_.clear();\n    }\n}\n\nDeviceHandler::DeviceHandler()\n    : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},\n                    std::vector<Subsystem>{}, std::vector<Subsystem>{}, std::set<std::string>{}, \"\",\n                    false) {}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/devices.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_DEVICES_H\n#define _INIT_DEVICES_H\n\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <map>\n#include <set>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <selinux/label.h>\n\n#include \"uevent.h\"\n#include \"uevent_handler.h\"\n\nnamespace android {\nnamespace init {\n\nclass Permissions {\n  public:\n    friend void TestPermissions(const Permissions& expected, const Permissions& test);\n\n    Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid, bool no_fnm_pathname);\n\n    bool Match(const std::string& path) const;\n\n    mode_t perm() const { return perm_; }\n    uid_t uid() const { return uid_; }\n    gid_t gid() const { return gid_; }\n\n  protected:\n    const std::string& name() const { return name_; }\n\n  private:\n    std::string name_;\n    mode_t perm_;\n    uid_t uid_;\n    gid_t gid_;\n    bool prefix_;\n    bool wildcard_;\n    bool no_fnm_pathname_;\n};\n\nclass SysfsPermissions : public Permissions {\n  public:\n    friend void TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test);\n\n    SysfsPermissions(const std::string& name, const std::string& attribute, mode_t perm, uid_t uid,\n                     gid_t gid, bool no_fnm_pathname)\n        : Permissions(name, perm, uid, gid, no_fnm_pathname), attribute_(attribute) {}\n\n    bool MatchWithSubsystem(const std::string& path, const std::string& subsystem) const;\n    void SetPermissions(const std::string& path) const;\n\n  private:\n    const std::string attribute_;\n};\n\nclass Subsystem {\n  public:\n    friend class SubsystemParser;\n    friend void TestSubsystems(const Subsystem& expected, const Subsystem& test);\n\n    enum DevnameSource {\n        DEVNAME_UEVENT_DEVNAME,\n        DEVNAME_UEVENT_DEVPATH,\n        DEVNAME_SYS_NAME,\n    };\n\n    Subsystem() {}\n    Subsystem(std::string name) : name_(std::move(name)) {}\n    Subsystem(std::string name, DevnameSource source, std::string dir_name)\n        : name_(std::move(name)), devname_source_(source), dir_name_(std::move(dir_name)) {}\n\n    // Returns the full path for a uevent of a device that is a member of this subsystem,\n    // according to the rules parsed from ueventd.rc\n    std::string ParseDevPath(const Uevent& uevent) const {\n        std::string devname;\n        if (devname_source_ == DEVNAME_UEVENT_DEVNAME) {\n            devname = uevent.device_name;\n        } else if (devname_source_ == DEVNAME_UEVENT_DEVPATH) {\n            devname = android::base::Basename(uevent.path);\n        } else if (devname_source_ == DEVNAME_SYS_NAME) {\n            if (android::base::ReadFileToString(\"/sys/\" + uevent.path + \"/name\", &devname)) {\n                devname.pop_back();  // Remove terminating newline\n            } else {\n                devname = uevent.device_name;\n            }\n        }\n        return dir_name_ + \"/\" + devname;\n    }\n\n    bool operator==(const std::string& string_name) const { return name_ == string_name; }\n\n  private:\n    std::string name_;\n    DevnameSource devname_source_ = DEVNAME_UEVENT_DEVNAME;\n    std::string dir_name_ = \"/dev\";\n};\n\nstruct BlockDeviceInfo {\n    std::string str;\n    std::string type;\n    bool is_boot_device;\n};\n\nclass DeviceHandler : public UeventHandler {\n  public:\n    friend class DeviceHandlerTester;\n\n    DeviceHandler();\n    DeviceHandler(std::vector<Permissions> dev_permissions,\n                  std::vector<SysfsPermissions> sysfs_permissions, std::vector<Subsystem> drivers,\n                  std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,\n                  std::string boot_part_uuid, bool skip_restorecon);\n    virtual ~DeviceHandler() = default;\n\n    bool CheckUeventForBootPartUuid(const Uevent& uevent);\n    void HandleUevent(const Uevent& uevent) override;\n\n    // `androidboot.partition_map` allows associating a partition name for a raw block device\n    // through a comma separated and semicolon deliminated list. For example,\n    // `androidboot.partition_map=vdb,metadata;vdc,userdata` maps `vdb` to `metadata` and `vdc` to\n    // `userdata`.\n    static std::string GetPartitionNameForDevice(const std::string& device);\n    bool IsBootDeviceStrict() const;\n    bool IsBootDevice(const Uevent& uevent) const;\n\n  private:\n    struct TrackedUevent {\n        Uevent uevent;\n        std::string canonical_device_path;\n    };\n\n    void ColdbootDone() override;\n    BlockDeviceInfo GetBlockDeviceInfo(const std::string& uevent_path) const;\n    bool FindSubsystemDevice(std::string path, std::string* device_path,\n                             const std::set<std::string>& subsystem_paths) const;\n    bool FindPlatformDevice(const std::string& path, std::string* platform_device_path) const;\n    bool FindMmcDevice(const std::string& path, std::string* mmc_device_path) const;\n    bool FindNvmeDevice(const std::string& path, std::string* nvme_device_path) const;\n    bool FindScsiDevice(const std::string& path, std::string* scsi_device_path) const;\n    std::tuple<mode_t, uid_t, gid_t> GetDevicePermissions(\n        const std::string& path, const std::vector<std::string>& links) const;\n    void MakeDevice(const std::string& path, bool block, int major, int minor,\n                    const std::vector<std::string>& links) const;\n    std::vector<std::string> GetBlockDeviceSymlinks(const Uevent& uevent) const;\n    void HandleDevice(const std::string& action, const std::string& devpath, bool block, int major,\n                      int minor, const std::vector<std::string>& links) const;\n    void FixupSysPermissions(const std::string& upath, const std::string& subsystem) const;\n    void HandleAshmemUevent(const Uevent& uevent);\n\n    void TrackDeviceUevent(const Uevent& uevent);\n    void HandleBindInternal(std::string driver_name, std::string action, const Uevent& uevent);\n\n    std::vector<Permissions> dev_permissions_;\n    std::vector<SysfsPermissions> sysfs_permissions_;\n    std::vector<Subsystem> drivers_;\n    std::vector<Subsystem> subsystems_;\n    std::set<std::string> boot_devices_;\n    std::string boot_part_uuid_;\n    bool found_boot_part_uuid_;\n    bool skip_restorecon_;\n    std::string sysfs_mount_point_;\n\n    std::vector<TrackedUevent> tracked_uevents_;\n    std::map<std::string, std::string> bound_drivers_;\n};\n\n// Exposed for testing\nvoid SanitizePartitionName(std::string* string);\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/devices_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"devices.h\"\n\n#include <android-base/file.h>\n#include <android-base/scopeguard.h>\n#include <gtest/gtest.h>\n\n#include \"util.h\"\n\nusing namespace std::string_literals;\n\nnamespace android {\nnamespace init {\n\nclass DeviceHandlerTester {\n  public:\n    void TestGetSymlinks(const std::string& platform_device, const Uevent& uevent,\n                         const std::vector<std::string>& expected_links) {\n        TemporaryDir fake_sys_root;\n        device_handler_.sysfs_mount_point_ = fake_sys_root.path;\n\n        std::string platform_device_dir = fake_sys_root.path + platform_device;\n        mkdir_recursive(platform_device_dir, 0777);\n\n        std::string platform_bus = fake_sys_root.path + \"/bus/platform\"s;\n        mkdir_recursive(platform_bus, 0777);\n        symlink(platform_bus.c_str(), (platform_device_dir + \"/subsystem\").c_str());\n\n        mkdir_recursive(android::base::Dirname(fake_sys_root.path + uevent.path), 0777);\n\n        std::vector<std::string> result;\n        result = device_handler_.GetBlockDeviceSymlinks(uevent);\n\n        auto expected_size = expected_links.size();\n        ASSERT_EQ(expected_size, result.size());\n        if (expected_size == 0) return;\n\n        // Explicitly iterate so the results are visible if a failure occurs\n        for (unsigned int i = 0; i < expected_size; ++i) {\n            EXPECT_EQ(expected_links[i], result[i]);\n        }\n    }\n\n  private:\n    DeviceHandler device_handler_;\n};\n\nTEST(device_handler, get_block_device_symlinks_success_platform) {\n    // These are actual paths from bullhead\n    const char* platform_device = \"/devices/soc.0/f9824900.sdhci\";\n    Uevent uevent = {\n        .path = \"/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0\",\n        .partition_name = \"\",\n        .partition_num = -1,\n    };\n    std::vector<std::string> expected_result{\"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0\"};\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_success_platform_with_partition) {\n    // These are actual paths from bullhead\n    const char* platform_device = \"/devices/soc.0/f9824900.sdhci\";\n    Uevent uevent = {\n        .path = \"/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1\",\n        .partition_name = \"modem\",\n        .partition_num = 1,\n    };\n    std::vector<std::string> expected_result{\n        \"/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem\",\n        \"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1\",\n    };\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_num) {\n    const char* platform_device = \"/devices/soc.0/f9824900.sdhci\";\n    Uevent uevent = {\n        .path = \"/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1\",\n        .partition_name = \"\",\n        .partition_num = 1,\n    };\n    std::vector<std::string> expected_result{\n        \"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1\",\n    };\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_success_platform_with_partition_only_name) {\n    const char* platform_device = \"/devices/soc.0/f9824900.sdhci\";\n    Uevent uevent = {\n        .path = \"/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1\",\n        .partition_name = \"modem\",\n        .partition_num = -1,\n    };\n    std::vector<std::string> expected_result{\n        \"/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem\",\n        \"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1\",\n    };\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_success_pci) {\n    const char* platform_device = \"/devices/do/not/match\";\n    Uevent uevent = {\n        .path = \"/devices/pci0000:00/0000:00:1f.2/mmcblk0\", .partition_name = \"\", .partition_num = -1,\n    };\n    std::vector<std::string> expected_result{\"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0\"};\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_pci_bad_format) {\n    const char* platform_device = \"/devices/do/not/match\";\n    Uevent uevent = {\n        .path = \"/devices/pci//mmcblk0\", .partition_name = \"\", .partition_num = -1,\n    };\n    std::vector<std::string> expected_result{};\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_success_vbd) {\n    const char* platform_device = \"/devices/do/not/match\";\n    Uevent uevent = {\n        .path = \"/devices/vbd-1234/mmcblk0\", .partition_name = \"\", .partition_num = -1,\n    };\n    std::vector<std::string> expected_result{\"/dev/block/vbd/1234/mmcblk0\"};\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_vbd_bad_format) {\n    const char* platform_device = \"/devices/do/not/match\";\n    Uevent uevent = {\n        .path = \"/devices/vbd-/mmcblk0\", .partition_name = \"\", .partition_num = -1,\n    };\n    std::vector<std::string> expected_result{};\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, get_block_device_symlinks_no_matches) {\n    const char* platform_device = \"/devices/soc.0/f9824900.sdhci\";\n    Uevent uevent = {\n        .path = \"/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1\",\n        .partition_name = \"\",\n        .partition_num = -1,\n    };\n    std::vector<std::string> expected_result;\n\n    DeviceHandlerTester device_handler_tester_;\n    device_handler_tester_.TestGetSymlinks(platform_device, uevent, expected_result);\n}\n\nTEST(device_handler, sanitize_null) {\n    SanitizePartitionName(nullptr);\n}\n\nTEST(device_handler, sanitize_empty) {\n    std::string empty;\n    SanitizePartitionName(&empty);\n    EXPECT_EQ(0u, empty.size());\n}\n\nTEST(device_handler, sanitize_allgood) {\n    std::string good =\n        \"abcdefghijklmnopqrstuvwxyz\"\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n        \"0123456789\"\n        \"_-.\";\n    std::string good_copy = good;\n    SanitizePartitionName(&good);\n    EXPECT_EQ(good_copy, good);\n}\n\nTEST(device_handler, sanitize_somebad) {\n    std::string string = \"abc!@#$%^&*()\";\n    SanitizePartitionName(&string);\n    EXPECT_EQ(\"abc__________\", string);\n}\n\nTEST(device_handler, sanitize_allbad) {\n    std::string string = \"!@#$%^&*()\";\n    SanitizePartitionName(&string);\n    EXPECT_EQ(\"__________\", string);\n}\n\nTEST(device_handler, sanitize_onebad) {\n    std::string string = \")\";\n    SanitizePartitionName(&string);\n    EXPECT_EQ(\"_\", string);\n}\n\nTEST(device_handler, DevPermissionsMatchNormal) {\n    // Basic from ueventd.rc\n    // /dev/null                 0666   root       root\n    Permissions permissions(\"/dev/null\", 0666, 0, 0, false);\n    EXPECT_TRUE(permissions.Match(\"/dev/null\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/nullsuffix\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/nul\"));\n    EXPECT_EQ(0666U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(0U, permissions.gid());\n}\n\nTEST(device_handler, DevPermissionsMatchPrefix) {\n    // Prefix from ueventd.rc\n    // /dev/dri/*                0666   root       graphics\n    Permissions permissions(\"/dev/dri/*\", 0666, 0, 1000, false);\n    EXPECT_TRUE(permissions.Match(\"/dev/dri/some_dri_device\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/dri/some_other_dri_device\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/dri/\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/dr/non_match\"));\n    EXPECT_EQ(0666U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(1000U, permissions.gid());\n}\n\nTEST(device_handler, DevPermissionsMatchWildcard) {\n    // Wildcard example\n    // /dev/device*name                0666   root       graphics\n    Permissions permissions(\"/dev/device*name\", 0666, 0, 1000, false);\n    EXPECT_TRUE(permissions.Match(\"/dev/devicename\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/device123name\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/deviceabcname\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/device123name/subdevice\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/deviceame\"));\n    EXPECT_EQ(0666U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(1000U, permissions.gid());\n}\n\nTEST(device_handler, DevPermissionsMatchWildcardPrefix) {\n    // Wildcard+Prefix example\n    // /dev/device*name*                0666   root       graphics\n    Permissions permissions(\"/dev/device*name*\", 0666, 0, 1000, false);\n    EXPECT_TRUE(permissions.Match(\"/dev/devicename\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/device123name\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/deviceabcname\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/device123namesomething\"));\n    // FNM_PATHNAME doesn't match '/' with *\n    EXPECT_FALSE(permissions.Match(\"/dev/device123name/something\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/device/1/2/3name/something\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/deviceame\"));\n    EXPECT_EQ(0666U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(1000U, permissions.gid());\n}\n\nTEST(device_handler, DevPermissionsMatchWildcardPrefix_NoFnmPathName) {\n    // Wildcard+Prefix example with no_fnm_pathname\n    // /dev/device*name*                0666   root       graphics\n    Permissions permissions(\"/dev/device*name*\", 0666, 0, 1000, true);\n    EXPECT_TRUE(permissions.Match(\"/dev/devicename\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/device123name\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/deviceabcname\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/device123namesomething\"));\n    // With NoFnmPathName, the below matches, unlike DevPermissionsMatchWildcardPrefix.\n    EXPECT_TRUE(permissions.Match(\"/dev/device123name/something\"));\n    EXPECT_TRUE(permissions.Match(\"/dev/device/1/2/3name/something\"));\n    EXPECT_FALSE(permissions.Match(\"/dev/deviceame\"));\n    EXPECT_EQ(0666U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(1000U, permissions.gid());\n}\n\nTEST(device_handler, SysfsPermissionsMatchWithSubsystemNormal) {\n    // /sys/devices/virtual/input/input*   enable      0660  root   input\n    SysfsPermissions permissions(\"/sys/devices/virtual/input/input*\", \"enable\", 0660, 0, 1001,\n                                 false);\n    EXPECT_TRUE(permissions.MatchWithSubsystem(\"/sys/devices/virtual/input/input0\", \"input\"));\n    EXPECT_FALSE(permissions.MatchWithSubsystem(\"/sys/devices/virtual/input/not_input0\", \"input\"));\n    EXPECT_EQ(0660U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(1001U, permissions.gid());\n}\n\nTEST(device_handler, SysfsPermissionsMatchWithSubsystemClass) {\n    // /sys/class/input/event*   enable      0660  root   input\n    SysfsPermissions permissions(\"/sys/class/input/event*\", \"enable\", 0660, 0, 1001, false);\n    EXPECT_TRUE(permissions.MatchWithSubsystem(\n        \"/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0\", \"input\"));\n    EXPECT_FALSE(permissions.MatchWithSubsystem(\n        \"/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/not_event0\", \"input\"));\n    EXPECT_FALSE(permissions.MatchWithSubsystem(\n        \"/sys/devices/soc.0/f9924000.i2c/i2c-2/2-0020/input/input0/event0\", \"not_input\"));\n    EXPECT_EQ(0660U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(1001U, permissions.gid());\n}\n\nTEST(device_handler, SysfsPermissionsMatchWithSubsystemBus) {\n    // /sys/bus/i2c/devices/i2c-*   enable      0660  root   input\n    SysfsPermissions permissions(\"/sys/bus/i2c/devices/i2c-*\", \"enable\", 0660, 0, 1001, false);\n    EXPECT_TRUE(permissions.MatchWithSubsystem(\"/sys/devices/soc.0/f9967000.i2c/i2c-5\", \"i2c\"));\n    EXPECT_FALSE(permissions.MatchWithSubsystem(\"/sys/devices/soc.0/f9967000.i2c/not-i2c\", \"i2c\"));\n    EXPECT_FALSE(\n        permissions.MatchWithSubsystem(\"/sys/devices/soc.0/f9967000.i2c/i2c-5\", \"not_i2c\"));\n    EXPECT_EQ(0660U, permissions.perm());\n    EXPECT_EQ(0U, permissions.uid());\n    EXPECT_EQ(1001U, permissions.gid());\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/epoll.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"epoll.h\"\n\n#include <stdint.h>\n#include <sys/epoll.h>\n\n#include <chrono>\n#include <functional>\n#include <map>\n\n#include <android-base/logging.h>\n\nnamespace android {\nnamespace init {\n\nEpoll::Epoll() {}\n\nResult<void> Epoll::Open() {\n    if (epoll_fd_ >= 0) return {};\n    epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));\n\n    if (epoll_fd_ == -1) {\n        return ErrnoError() << \"epoll_create1 failed\";\n    }\n    return {};\n}\n\nResult<void> Epoll::RegisterHandler(int fd, Handler handler, uint32_t events) {\n    if (!events) {\n        return Error() << \"Must specify events\";\n    }\n\n    auto [it, inserted] = epoll_handlers_.emplace(\n            fd, Info{\n                        .handler = std::move(handler),\n                        .events = events,\n                });\n    if (!inserted) {\n        return Error() << \"Cannot specify two epoll handlers for a given FD\";\n    }\n    epoll_event ev = {\n            .events = events,\n            .data.fd = fd,\n    };\n    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, fd, &ev) == -1) {\n        Result<void> result = ErrnoError() << \"epoll_ctl failed to add fd\";\n        epoll_handlers_.erase(fd);\n        return result;\n    }\n    return {};\n}\n\nResult<void> Epoll::UnregisterHandler(int fd) {\n    if (epoll_ctl(epoll_fd_.get(), EPOLL_CTL_DEL, fd, nullptr) == -1) {\n        return ErrnoError() << \"epoll_ctl failed to remove fd\";\n    }\n    auto it = epoll_handlers_.find(fd);\n    if (it == epoll_handlers_.end()) {\n        return Error() << \"Attempting to remove epoll handler for FD without an existing handler\";\n    }\n    to_remove_.insert(it->first);\n    return {};\n}\n\nvoid Epoll::SetFirstCallback(std::function<void()> first_callback) {\n    first_callback_ = std::move(first_callback);\n}\n\nResult<int> Epoll::Wait(std::optional<std::chrono::milliseconds> timeout) {\n    int timeout_ms = -1;\n    if (timeout && timeout->count() < INT_MAX) {\n        timeout_ms = timeout->count();\n    }\n    const auto max_events = epoll_handlers_.size();\n    epoll_event ev[max_events];\n    auto num_events = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), ev, max_events, timeout_ms));\n    if (num_events == -1) {\n        return ErrnoError() << \"epoll_wait failed\";\n    }\n    if (num_events > 0 && first_callback_) {\n        first_callback_();\n    }\n    for (int i = 0; i < num_events; ++i) {\n        const auto it = epoll_handlers_.find(ev[i].data.fd);\n        if (it == epoll_handlers_.end()) {\n            continue;\n        }\n        const Info& info = it->second;\n        if ((info.events & (EPOLLIN | EPOLLPRI)) == (EPOLLIN | EPOLLPRI) &&\n            (ev[i].events & EPOLLIN) != ev[i].events) {\n            // This handler wants to know about exception events, and just got one.\n            // Log something informational.\n            LOG(ERROR) << \"Received unexpected epoll event set: \" << ev[i].events;\n        }\n        info.handler();\n        for (auto fd : to_remove_) {\n            epoll_handlers_.erase(fd);\n        }\n        to_remove_.clear();\n    }\n    return num_events;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/epoll.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <sys/epoll.h>\n\n#include <chrono>\n#include <functional>\n#include <map>\n#include <memory>\n#include <optional>\n#include <unordered_set>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nclass Epoll {\n  public:\n    Epoll();\n\n    typedef std::function<void()> Handler;\n\n    Result<void> Open();\n    Result<void> RegisterHandler(int fd, Handler handler, uint32_t events = EPOLLIN);\n    Result<void> UnregisterHandler(int fd);\n    void SetFirstCallback(std::function<void()> first_callback);\n    Result<int> Wait(std::optional<std::chrono::milliseconds> timeout);\n\n  private:\n    struct Info {\n        Handler handler;\n        uint32_t events;\n    };\n\n    android::base::unique_fd epoll_fd_;\n    std::map<int, Info> epoll_handlers_;\n    std::function<void()> first_callback_;\n    std::unordered_set<int> to_remove_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/epoll_test.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"epoll.h\"\n\n#include <sys/unistd.h>\n\n#include <unordered_set>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace init {\n\nstd::unordered_set<void*> sValidObjects;\n\nclass CatchDtor final {\n  public:\n    CatchDtor() { CHECK(sValidObjects.emplace(this).second); }\n    CatchDtor(const CatchDtor&) { CHECK(sValidObjects.emplace(this).second); }\n    CatchDtor(const CatchDtor&&) { CHECK(sValidObjects.emplace(this).second); }\n    ~CatchDtor() { CHECK_EQ(sValidObjects.erase(this), size_t{1}); }\n};\n\nTEST(epoll, UnregisterHandler) {\n    Epoll epoll;\n    ASSERT_RESULT_OK(epoll.Open());\n\n    int fds[2];\n    ASSERT_EQ(pipe(fds), 0);\n\n    CatchDtor catch_dtor;\n    bool handler_invoked = false;\n    auto handler = [&, catch_dtor]() -> void {\n        auto result = epoll.UnregisterHandler(fds[0]);\n        ASSERT_EQ(result.ok(), !handler_invoked);\n        handler_invoked = true;\n        // The assert statement below verifies that the UnregisterHandler() call\n        // above did not destroy the current std::function<> instance.\n        ASSERT_NE(sValidObjects.find((void*)&catch_dtor), sValidObjects.end());\n    };\n\n    epoll.RegisterHandler(fds[0], std::move(handler));\n\n    uint8_t byte = 0xee;\n    ASSERT_TRUE(android::base::WriteFully(fds[1], &byte, sizeof(byte)));\n\n    auto epoll_result = epoll.Wait({});\n    ASSERT_RESULT_OK(epoll_result);\n    ASSERT_EQ(*epoll_result, 1);\n    ASSERT_TRUE(handler_invoked);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/extra_free_kbytes.sh",
    "content": "#!/bin/sh\n\n# Script implements watermark_scale calculation which results in the same low\n# watermark as if extra_free_kbytes tunable were to be used.\n#\n# Usage: extra_free_kbytes.sh <extra_free_kbytes value>\n#\n# extra_free_kbytes is distributed between zones based on\n# zone.managed_pages/vm_total_pages ratio, where vm_total_pages is the sum of\n# zone.managed_pages for all zones (zone.high used in this calculation is 0\n# when this is calculated). Therefore for each zone its share is calculated as:\n#\n# extra_free_pages = extra_free_kbytes / page_size\n# extra_share = extra_free_pages * managed_pages / vm_total_pages\n#\n# This extra_share is added to the low and high watermarks:\n#\n# low = min + max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share\n# high = min + 2 * max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share\n#\n# Because Android uses extra_free_kbytes to adjust the low watermark, we ignore\n# the difference in how watermark_scale and extra_free_kbytes affect the high\n# watermark and will match the low watermark only.\n#\n# To eliminate extra_share and compansate the difference with watermark_scale,\n# a new watermark_scale_new is calculated as:\n#\n# (1) max(min / 4, managed_pages * (watermark_scale / 10000)) + extra_share =\n#   max(min / 4, managed_pages * (watermark_scale_new / 10000))\n#\n# Two cases to consider:\n# A. managed_pages * (watermark_scale / 10000) > min / 4\n# The formula (1) becomes:\n#\n# managed_pages * (watermark_scale / 10000) + extra_share =\n#   managed_pages * (watermark_scale_new / 10000)\n#\n# after simplifying and substituting extra_share formula becomes:\n#\n# (2) watermark_scale_new = watermark_scale + extra_free_pages / vm_total_pages * 10000\n#\n# B. managed_pages * (watermark_scale / 10000) < min / 4\n# The formula (1) becomes:\n#\n# min / 4 + extra_share = max(min / 4, managed_pages * (watermark_scale_new / 10000))\n#\n# after calculating watermark_scale_new, if (managed_pages * (watermark_scale_new / 10000))\n# is still smaller than min / 4 then we can't compensate extra_share with\n# watermark_scale anyway. Therefore calculation becomes:\n#\n# watermark_scale_new = (min / 4 + extra_share) / managed_pages * 10000\n#\n# after simplifying and substituting extra_share formula becomes:\n#\n# (3) watermark_scale_new = (min / 4) * 10000 / managed_pages + extra_free_pages / vm_total_pages * 10000\n#\n# After defining watermark_delta = extra_free_pages / vm_total_pages * 10000:\n#\n# if (managed_pages * (watermark_scale / 10000) > min / 4)\n#     watermark_scale_new = watermark_scale + watermark_delta\n# else\n#     watermark_scale_new = (min / 4) * 10000 / managed_pages + watermark_delta\n#\n\nif [ \"$#\" -ne 1 ]\nthen\n    echo \"Usage: $0 <extra_free_kbytes value>\"\n    exit\nfi\n\nextra_free_kbytes=$1\n\n# if extra_free_kbytes knob exists, use it and exit\nif [ -e /proc/sys/vm/extra_free_kbytes ]\nthen\n    echo $extra_free_kbytes > /proc/sys/vm/extra_free_kbytes\n    exit\nfi\n\n# record the original watermark_scale_factor value\nwatermark_scale=$(getprop \"ro.kernel.watermark_scale_factor\")\nif [ -z \"$watermark_scale\" ]\nthen\n    watermark_scale=$(cat /proc/sys/vm/watermark_scale_factor)\n    setprop \"ro.kernel.watermark_scale_factor\" \"$watermark_scale\"\n    # On older distributions with no policies configured setprop may fail.\n    # If that happens, use the kernel default of 10.\n    if [ -z $(getprop \"ro.kernel.watermark_scale_factor\") ]\n    then\n        watermark_scale=10\n    fi\nfi\n\n# convert extra_free_kbytes to pages\npage_size=$(getconf PAGESIZE)\npage_size_kb=$((page_size/1024))\nextra_free_pg=$((extra_free_kbytes/page_size_kb))\n\nmanaged=($(grep managed /proc/zoneinfo | awk '{print $2}'))\nlength=${#managed[@]}\nmin=($(grep \"min\" /proc/zoneinfo | awk '{print $2}'))\n\n# calculate vm_total_pages.\n# WARNING: if the final low watermark differs from the original, the source of\n# the error is likely vm_total_pages which is impossible to get exact from the\n# userspace. Grep for \"Total pages\" in the kernel logs to see the actual\n# vm_total_pages and plug it in the calculation to confirm the source of the\n# error. Error caused by this inaccuracy is normally within 1% range.\nvm_total_pages=0\ni=0\nwhile [ $i -lt $length ]\ndo\n    vm_total_pages=$((vm_total_pages + managed[i]))\n    i=$((i+1))\ndone\n\n# calculate watermark_scale_new for each zone and choose the max\nmax_watermark_scale=0\ni=0\nwhile [ $i -lt $length ]\ndo\n    # skip unmanaged zones\n    if [ ${managed[i]} -eq 0 ]\n    then\n        i=$((i+1))\n        continue\n    fi\n\n    base_margin=$((min[i] / 4))\n    calc_margin=$(echo \"${managed[i]} * $watermark_scale / 10000\" | bc)\n    # round the value by adding 0.5 and truncating the decimal part\n    watermark_delta=$(echo \"x=($extra_free_pg / ($vm_total_pages / 10000) + 0.5); scale = 0; x/1\" | bc -l)\n    if [ $calc_margin -gt $base_margin ]\n    then\n        watermark_scale_new=$(echo \"$watermark_scale + $watermark_delta\" | bc)\n    else\n        watermark_scale_new=$(echo \"$base_margin / (${managed[i]} / 10000) + $watermark_delta\" | bc)\n    fi\n\n    if [ $max_watermark_scale -lt $watermark_scale_new ]\n    then\n        max_watermark_scale=$watermark_scale_new\n    fi\n\n    i=$((i+1))\ndone\n\necho $max_watermark_scale > /proc/sys/vm/watermark_scale_factor\n"
  },
  {
    "path": "init/firmware_handler.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"firmware_handler.h\"\n\n#include <fcntl.h>\n#include <fnmatch.h>\n#include <glob.h>\n#include <grp.h>\n#include <pwd.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/sendfile.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <thread>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"exthandler/exthandler.h\"\n\nusing android::base::ReadFdToString;\nusing android::base::Socketpair;\nusing android::base::Split;\nusing android::base::Timer;\nusing android::base::Trim;\nusing android::base::unique_fd;\nusing android::base::WaitForProperty;\nusing android::base::WriteFully;\n\nnamespace android {\nnamespace init {\n\nnamespace {\nbool PrefixMatch(const std::string& pattern, const std::string& path) {\n    return android::base::StartsWith(path, pattern);\n}\n\nbool FnMatch(const std::string& pattern, const std::string& path) {\n    return fnmatch(pattern.c_str(), path.c_str(), 0) == 0;\n}\n\nbool EqualMatch(const std::string& pattern, const std::string& path) {\n    return pattern == path;\n}\n}  // namespace\n\nstatic void LoadFirmware(const std::string& firmware, const std::string& root, int fw_fd,\n                         size_t fw_size, int loading_fd, int data_fd) {\n    // Start transfer.\n    WriteFully(loading_fd, \"1\", 1);\n\n    // Copy the firmware.\n    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);\n    if (rc == -1) {\n        PLOG(ERROR) << \"firmware: sendfile failed { '\" << root << \"', '\" << firmware << \"' }\";\n    }\n\n    // Tell the firmware whether to abort or commit.\n    const char* response = (rc != -1) ? \"0\" : \"-1\";\n    WriteFully(loading_fd, response, strlen(response));\n}\n\nstatic bool IsBooting() {\n    return access(\"/dev/.booting\", F_OK) == 0;\n}\n\nstatic bool IsApexActivated() {\n    static bool apex_activated = []() {\n        // Wait for com.android.runtime.apex activation\n        // Property name and value must be kept in sync with system/apexd/apex/apex_constants.h\n        // 60s is the default firmware sysfs fallback timeout. (/sys/class/firmware/timeout)\n        if (!WaitForProperty(\"apexd.status\", \"activated\", 60s)) {\n            LOG(ERROR) << \"Apexd activation wait timeout\";\n            return false;\n        }\n        return true;\n    }();\n\n    return apex_activated;\n}\n\nstatic bool NeedsRerunExternalHandler() {\n    static bool first = true;\n\n    // Rerun external handler only on the first try and when apex is activated\n    if (first) {\n        first = false;\n        return IsApexActivated();\n    }\n\n    return first;\n}\n\nExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid,\n                                                 std::string handler_path)\n    : devpath(std::move(devpath)), uid(uid), gid(gid), handler_path(std::move(handler_path)) {\n    auto wildcard_position = this->devpath.find('*');\n    if (wildcard_position != std::string::npos) {\n        if (wildcard_position == this->devpath.length() - 1) {\n            this->devpath.pop_back();\n            match = std::bind(PrefixMatch, this->devpath, std::placeholders::_1);\n        } else {\n            match = std::bind(FnMatch, this->devpath, std::placeholders::_1);\n        }\n    } else {\n        match = std::bind(EqualMatch, this->devpath, std::placeholders::_1);\n    }\n}\n\nExternalFirmwareHandler::ExternalFirmwareHandler(std::string devpath, uid_t uid,\n                                                 std::string handler_path)\n    : ExternalFirmwareHandler(devpath, uid, 0, handler_path) {}\n\nFirmwareHandler::FirmwareHandler(std::vector<std::string> firmware_directories,\n                                 std::vector<ExternalFirmwareHandler> external_firmware_handlers)\n    : firmware_directories_(std::move(firmware_directories)),\n      external_firmware_handlers_(std::move(external_firmware_handlers)) {}\n\nstd::string FirmwareHandler::GetFirmwarePath(const Uevent& uevent) const {\n    for (const auto& external_handler : external_firmware_handlers_) {\n        if (external_handler.match(uevent.path)) {\n            LOG(INFO) << \"Launching external firmware handler '\" << external_handler.handler_path\n                      << \"' for devpath: '\" << uevent.path << \"' firmware: '\" << uevent.firmware\n                      << \"'\";\n\n            std::unordered_map<std::string, std::string> envs_map;\n            envs_map[\"FIRMWARE\"] = uevent.firmware;\n            envs_map[\"DEVPATH\"] = uevent.path;\n\n            auto result = RunExternalHandler(external_handler.handler_path, external_handler.uid,\n                                             external_handler.gid, envs_map);\n            if (!result.ok() && NeedsRerunExternalHandler()) {\n                auto res = RunExternalHandler(external_handler.handler_path, external_handler.uid,\n                                              external_handler.gid, envs_map);\n                result = std::move(res);\n            }\n            if (!result.ok()) {\n                LOG(ERROR) << \"Using default firmware; External firmware handler failed: \"\n                           << result.error();\n                return uevent.firmware;\n            }\n            if (result->find(\"..\") != std::string::npos) {\n                LOG(ERROR) << \"Using default firmware; External firmware handler provided an \"\n                              \"invalid path, '\"\n                           << *result << \"'\";\n                return uevent.firmware;\n            }\n            LOG(INFO) << \"Loading firmware '\" << *result << \"' in place of '\" << uevent.firmware\n                      << \"'\";\n            return *result;\n        }\n    }\n    LOG(INFO) << \"firmware: loading '\" << uevent.firmware << \"' for '\" << uevent.path << \"'\";\n    return uevent.firmware;\n}\n\nvoid FirmwareHandler::ProcessFirmwareEvent(const std::string& path,\n                                           const std::string& firmware) const {\n    std::string root = \"/sys\" + path;\n    std::string loading = root + \"/loading\";\n    std::string data = root + \"/data\";\n\n    unique_fd loading_fd(open(loading.c_str(), O_WRONLY | O_CLOEXEC));\n    if (loading_fd == -1) {\n        PLOG(ERROR) << \"couldn't open firmware loading fd for \" << firmware;\n        return;\n    }\n\n    unique_fd data_fd(open(data.c_str(), O_WRONLY | O_CLOEXEC));\n    if (data_fd == -1) {\n        PLOG(ERROR) << \"couldn't open firmware data fd for \" << firmware;\n        return;\n    }\n\n    std::vector<std::string> attempted_paths_and_errors;\n    auto TryLoadFirmware = [&](const std::string& firmware_directory) {\n        std::string file = firmware_directory + firmware;\n        unique_fd fw_fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));\n        if (fw_fd == -1) {\n            attempted_paths_and_errors.emplace_back(\"firmware: attempted \" + file +\n                                                    \", open failed: \" + strerror(errno));\n            return false;\n        }\n        struct stat sb;\n        if (fstat(fw_fd.get(), &sb) == -1) {\n            attempted_paths_and_errors.emplace_back(\"firmware: attempted \" + file +\n                                                    \", fstat failed: \" + strerror(errno));\n            return false;\n        }\n        LOG(INFO) << \"found \" << file << \" for \" << path;\n        LoadFirmware(firmware, root, fw_fd.get(), sb.st_size, loading_fd.get(), data_fd.get());\n        return true;\n    };\n\n    int booting = IsBooting();\ntry_loading_again:\n    attempted_paths_and_errors.clear();\n    if (ForEachFirmwareDirectory(TryLoadFirmware)) {\n        return;\n    }\n\n    if (booting) {\n        // If we're not fully booted, we may be missing\n        // filesystems needed for firmware, wait and retry.\n        std::this_thread::sleep_for(100ms);\n        booting = IsBooting();\n        goto try_loading_again;\n    }\n\n    LOG(ERROR) << \"firmware: could not find firmware for \" << firmware;\n    for (const auto& message : attempted_paths_and_errors) {\n        LOG(ERROR) << message;\n    }\n\n    // Write \"-1\" as our response to the kernel's firmware request, since we have nothing for it.\n    write(loading_fd.get(), \"-1\", 2);\n}\n\nbool FirmwareHandler::ForEachFirmwareDirectory(\n        std::function<bool(const std::string&)> handler) const {\n    for (const std::string& firmware_directory : firmware_directories_) {\n        if (std::invoke(handler, firmware_directory)) {\n            return true;\n        }\n    }\n\n    glob_t glob_result;\n    glob(\"/apex/*/etc/firmware/\", GLOB_MARK, nullptr, &glob_result);\n    auto free_glob = android::base::make_scope_guard(std::bind(&globfree, &glob_result));\n    for (size_t i = 0; i < glob_result.gl_pathc; i++) {\n        char* apex_firmware_directory = glob_result.gl_pathv[i];\n        // Filter-out /apex/<name>@<ver> paths. The paths are bind-mounted to\n        // /apex/<name> paths, so unless we filter them out, we will look into the\n        // same apex twice.\n        if (strchr(apex_firmware_directory, '@')) {\n            continue;\n        }\n        if (std::invoke(handler, apex_firmware_directory)) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid FirmwareHandler::HandleUevent(const Uevent& uevent) {\n    if (uevent.subsystem != \"firmware\" || uevent.action != \"add\") return;\n\n    // Loading the firmware in a child means we can do that in parallel...\n    auto pid = fork();\n    if (pid == -1) {\n        PLOG(ERROR) << \"could not fork to process firmware event for \" << uevent.firmware;\n    }\n    if (pid == 0) {\n        Timer t;\n        auto firmware = GetFirmwarePath(uevent);\n        ProcessFirmwareEvent(uevent.path, firmware);\n        LOG(INFO) << \"loading \" << uevent.path << \" took \" << t;\n        _exit(EXIT_SUCCESS);\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/firmware_handler.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <grp.h>\n#include <pwd.h>\n\n#include <functional>\n#include <string>\n#include <vector>\n\n#include \"result.h\"\n#include \"uevent.h\"\n#include \"uevent_handler.h\"\n\nnamespace android {\nnamespace init {\n\nstruct ExternalFirmwareHandler {\n    ExternalFirmwareHandler(std::string devpath, uid_t uid, std::string handler_path);\n    ExternalFirmwareHandler(std::string devpath, uid_t uid, gid_t gid, std::string handler_path);\n\n    std::string devpath;\n    uid_t uid;\n    gid_t gid;\n    std::string handler_path;\n\n    std::function<bool(const std::string&)> match;\n};\n\nclass FirmwareHandler : public UeventHandler {\n  public:\n    FirmwareHandler(std::vector<std::string> firmware_directories,\n                    std::vector<ExternalFirmwareHandler> external_firmware_handlers);\n    virtual ~FirmwareHandler() = default;\n\n    void HandleUevent(const Uevent& uevent) override;\n\n  private:\n    friend void FirmwareTestWithExternalHandler(const std::string& test_name,\n                                                bool expect_new_firmware);\n\n    std::string GetFirmwarePath(const Uevent& uevent) const;\n    void ProcessFirmwareEvent(const std::string& path, const std::string& firmware) const;\n    bool ForEachFirmwareDirectory(std::function<bool(const std::string&)> handler) const;\n\n    std::vector<std::string> firmware_directories_;\n    std::vector<ExternalFirmwareHandler> external_firmware_handlers_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/firmware_handler_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"firmware_handler.h\"\n\n#include <stdlib.h>\n#include <iostream>\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n\n#include \"uevent.h\"\n\nusing android::base::GetExecutablePath;\nusing namespace std::literals;\n\nnamespace android {\nnamespace init {\n\nvoid FirmwareTestWithExternalHandler(const std::string& test_name, bool expect_new_firmware) {\n    auto test_path = GetExecutablePath() + \" firmware \" + test_name;\n    auto external_firmware_handler = ExternalFirmwareHandler(\n            \"/devices/led/firmware/test_firmware001.bin\", getuid(), test_path);\n\n    auto firmware_handler = FirmwareHandler({\"/test\"}, {external_firmware_handler});\n\n    auto uevent = Uevent{\n            .path = \"/devices/led/firmware/test_firmware001.bin\",\n            .firmware = \"test_firmware001.bin\",\n    };\n\n    if (expect_new_firmware) {\n        EXPECT_EQ(\"other_firmware001.bin\", firmware_handler.GetFirmwarePath(uevent));\n    } else {\n        EXPECT_EQ(\"test_firmware001.bin\", firmware_handler.GetFirmwarePath(uevent));\n    }\n\n    // Always test the base case that the handler isn't invoked if the devpath doesn't match.\n    auto uevent_different_path = Uevent{\n            .path = \"/devices/led/not/mine\",\n            .firmware = \"test_firmware001.bin\",\n    };\n    EXPECT_EQ(\"test_firmware001.bin\", firmware_handler.GetFirmwarePath(uevent_different_path));\n}\n\nTEST(firmware_handler, HandleChange) {\n    FirmwareTestWithExternalHandler(\"HandleChange\", true);\n}\n\nint HandleChange(int argc, char** argv) {\n    // Assert that the environment is set up correctly.\n    if (getenv(\"DEVPATH\") != \"/devices/led/firmware/test_firmware001.bin\"s) {\n        std::cerr << \"$DEVPATH not set correctly\" << std::endl;\n        return EXIT_FAILURE;\n    }\n    if (getenv(\"FIRMWARE\") != \"test_firmware001.bin\"s) {\n        std::cerr << \"$FIRMWARE not set correctly\" << std::endl;\n        return EXIT_FAILURE;\n    }\n    std::cout << \"other_firmware001.bin\" << std::endl;\n    return 0;\n}\n\nTEST(firmware_handler, HandleAbort) {\n    FirmwareTestWithExternalHandler(\"HandleAbort\", false);\n}\n\nint HandleAbort(int argc, char** argv) {\n    // Since this is an expected failure, disable debuggerd to not generate a tombstone.\n    signal(SIGABRT, SIG_DFL);\n    abort();\n    return 0;\n}\n\nTEST(firmware_handler, HandleFailure) {\n    FirmwareTestWithExternalHandler(\"HandleFailure\", false);\n}\n\nint HandleFailure(int argc, char** argv) {\n    std::cerr << \"Failed\" << std::endl;\n    return EXIT_FAILURE;\n}\n\nTEST(firmware_handler, HandleBadPath) {\n    FirmwareTestWithExternalHandler(\"HandleBadPath\", false);\n}\n\nint HandleBadPath(int argc, char** argv) {\n    std::cout << \"../firmware.bin\";\n    return 0;\n}\n\nTEST(firmware_handler, Matching) {\n    ExternalFirmwareHandler h(\"/dev/path/a.bin\", getuid(), \"/test\");\n    ASSERT_TRUE(h.match(\"/dev/path/a.bin\"));\n    ASSERT_FALSE(h.match(\"/dev/path/a.bi\"));\n\n    h = ExternalFirmwareHandler(\"/dev/path/a.*\", getuid(), \"/test\");\n    ASSERT_TRUE(h.match(\"/dev/path/a.bin\"));\n    ASSERT_TRUE(h.match(\"/dev/path/a.bix\"));\n    ASSERT_FALSE(h.match(\"/dev/path/b.bin\"));\n\n    h = ExternalFirmwareHandler(\"/dev/*/a.bin\", getuid(), \"/test\");\n    ASSERT_TRUE(h.match(\"/dev/path/a.bin\"));\n    ASSERT_TRUE(h.match(\"/dev/other/a.bin\"));\n    ASSERT_FALSE(h.match(\"/dev/other/c.bin\"));\n    ASSERT_FALSE(h.match(\"/dev/path/b.bin\"));\n}\n\n}  // namespace init\n}  // namespace android\n\n// init_test.cpp contains the main entry point for all init tests.\nint FirmwareTestChildMain(int argc, char** argv) {\n    if (argc < 3) {\n        return 1;\n    }\n\n#define RunTest(testname)                           \\\n    if (argv[2] == std::string(#testname)) {        \\\n        return android::init::testname(argc, argv); \\\n    }\n\n    RunTest(HandleChange);\n    RunTest(HandleAbort);\n    RunTest(HandleFailure);\n    RunTest(HandleBadPath);\n\n#undef RunTest\n    return 1;\n}\n"
  },
  {
    "path": "init/first_stage_console.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"first_stage_console.h\"\n\n#include <spawn.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <termios.h>\n\n#include <string>\n#include <thread>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n\nstatic bool KernelConsolePresent(const std::string& cmdline) {\n    size_t pos = 0;\n    while (true) {\n        pos = cmdline.find(\"console=\", pos);\n        if (pos == std::string::npos) return false;\n        if (pos == 0 || cmdline[pos - 1] == ' ') return true;\n        pos++;\n    }\n}\n\nstatic bool SetupConsole() {\n    if (mknod(\"/dev/console\", S_IFCHR | 0600, makedev(5, 1))) {\n        PLOG(ERROR) << \"unable to create /dev/console\";\n        return false;\n    }\n    int fd = -1;\n    int tries = 50;  // should timeout after 5s\n    // The device driver for console may not be ready yet so retry for a while in case of failure.\n    while (tries--) {\n        fd = open(\"/dev/console\", O_RDWR);\n        if (fd != -1) break;\n        std::this_thread::sleep_for(100ms);\n    }\n    if (fd == -1) {\n        PLOG(ERROR) << \"could not open /dev/console\";\n        return false;\n    }\n    ioctl(fd, TIOCSCTTY, 0);\n    dup2(fd, STDIN_FILENO);\n    dup2(fd, STDOUT_FILENO);\n    dup2(fd, STDERR_FILENO);\n    close(fd);\n    return true;\n}\n\nstatic pid_t SpawnImage(const char* file) {\n    const char* argv[] = {file, NULL};\n    const char* envp[] = {NULL};\n\n    char* const* argvp = const_cast<char* const*>(argv);\n    char* const* envpp = const_cast<char* const*>(envp);\n\n    pid_t pid;\n    errno = posix_spawn(&pid, argv[0], NULL, NULL, argvp, envpp);\n    if (!errno) return pid;\n\n    PLOG(ERROR) << \"Failed to spawn '\" << file << \"'\";\n\n    return (pid_t)0;\n}\n\nnamespace android {\nnamespace init {\n\nvoid StartConsole(const std::string& cmdline) {\n    bool console = KernelConsolePresent(cmdline);\n    // Use a simple sigchld handler -- first_stage_console doesn't need to track or log zombies\n    const struct sigaction chld_act {\n        .sa_flags = SA_NOCLDWAIT, .sa_handler = SIG_DFL\n    };\n\n    sigaction(SIGCHLD, &chld_act, nullptr);\n    pid_t pid = fork();\n    if (pid != 0) {\n        wait(NULL);\n        LOG(ERROR) << \"console shell exited\";\n        return;\n    }\n\n    if (console) console = SetupConsole();\n\n    LOG(INFO) << \"Attempting to run /first_stage.sh...\";\n    if (SpawnImage(\"/first_stage.sh\")) {\n        wait(NULL);\n        LOG(INFO) << \"/first_stage.sh exited\";\n    }\n\n    if (console) {\n        if (SpawnImage(\"/system/bin/sh\")) wait(NULL);\n    }\n    _exit(127);\n}\n\nint FirstStageConsole(const std::string& cmdline, const std::string& bootconfig) {\n    auto pos = bootconfig.find(\"androidboot.first_stage_console =\");\n    if (pos != std::string::npos) {\n        int val = 0;\n        if (sscanf(bootconfig.c_str() + pos, \"androidboot.first_stage_console = \\\"%d\\\"\", &val) !=\n            1) {\n            return FirstStageConsoleParam::DISABLED;\n        }\n        if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {\n            return val;\n        }\n    }\n\n    pos = cmdline.find(\"androidboot.first_stage_console=\");\n    if (pos != std::string::npos) {\n        int val = 0;\n        if (sscanf(cmdline.c_str() + pos, \"androidboot.first_stage_console=%d\", &val) != 1) {\n            return FirstStageConsoleParam::DISABLED;\n        }\n        if (val <= FirstStageConsoleParam::MAX_PARAM_VALUE && val >= 0) {\n            return val;\n        }\n    }\n    return FirstStageConsoleParam::DISABLED;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/first_stage_console.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\nnamespace android {\nnamespace init {\n\nenum FirstStageConsoleParam {\n    DISABLED = 0,\n    CONSOLE_ON_FAILURE = 1,\n    IGNORE_FAILURE = 2,\n    MAX_PARAM_VALUE = IGNORE_FAILURE,\n};\n\nvoid StartConsole(const std::string& cmdline);\nint FirstStageConsole(const std::string& cmdline, const std::string& bootconfig);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/first_stage_init.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"first_stage_init.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <paths.h>\n#include <stdlib.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <filesystem>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android/avf_cc_flags.h>\n#include <fs_mgr.h>\n#include <modprobe/modprobe.h>\n#include <private/android_filesystem_config.h>\n\n#include \"debug_ramdisk.h\"\n#include \"first_stage_console.h\"\n#include \"first_stage_mount.h\"\n#include \"reboot_utils.h\"\n#include \"second_stage_resources.h\"\n#include \"snapuserd_transition.h\"\n#include \"switch_root.h\"\n#include \"util.h\"\n\nusing android::base::boot_clock;\n\nusing namespace std::literals;\n\nnamespace fs = std::filesystem;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nenum class BootMode {\n    NORMAL_MODE,\n    RECOVERY_MODE,\n    CHARGER_MODE,\n};\n\nvoid FreeRamdisk(DIR* dir, dev_t dev) {\n    int dfd = dirfd(dir);\n\n    dirent* de = nullptr;\n    while ((de = readdir(dir)) != nullptr) {\n        if (de->d_name == \".\"s || de->d_name == \"..\"s) {\n            continue;\n        }\n\n        bool is_dir = false;\n\n        if (de->d_type == DT_DIR || de->d_type == DT_UNKNOWN) {\n            struct stat info {};\n            if (fstatat(dfd, de->d_name, &info, AT_SYMLINK_NOFOLLOW) != 0) {\n                continue;\n            }\n\n            if (info.st_dev != dev) {\n                continue;\n            }\n\n            if (S_ISDIR(info.st_mode)) {\n                is_dir = true;\n                auto fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);\n                if (fd >= 0) {\n                    auto subdir =\n                            std::unique_ptr<DIR, decltype(&closedir)>{fdopendir(fd), closedir};\n                    if (subdir) {\n                        FreeRamdisk(subdir.get(), dev);\n                    } else {\n                        close(fd);\n                    }\n                }\n            }\n        } else if (de->d_type == DT_REG) {\n            // Do not free snapuserd if we will need the ramdisk copy during the\n            // selinux transition.\n            if (de->d_name == \"snapuserd\"s && IsFirstStageSnapuserdRunning()) {\n                continue;\n            }\n        }\n        unlinkat(dfd, de->d_name, is_dir ? AT_REMOVEDIR : 0);\n    }\n}\n\nbool ForceNormalBoot(const std::string& cmdline, const std::string& bootconfig) {\n    return bootconfig.find(\"androidboot.force_normal_boot = \\\"1\\\"\") != std::string::npos ||\n           cmdline.find(\"androidboot.force_normal_boot=1\") != std::string::npos;\n}\n\nstatic void Copy(const char* src, const char* dst) {\n    if (link(src, dst) == 0) {\n        LOG(INFO) << \"hard linking \" << src << \" to \" << dst << \" succeeded\";\n        return;\n    }\n    PLOG(FATAL) << \"hard linking \" << src << \" to \" << dst << \" failed\";\n}\n\n// Move snapuserd before switching root, so that it is available at the same path\n// after switching root.\nvoid PrepareSwitchRoot() {\n    static constexpr const auto& snapuserd = \"/system/bin/snapuserd\";\n    static constexpr const auto& snapuserd_ramdisk = \"/system/bin/snapuserd_ramdisk\";\n    static constexpr const auto& dst = \"/first_stage_ramdisk/system/bin/snapuserd\";\n\n    if (access(dst, X_OK) == 0) {\n        LOG(INFO) << dst << \" already exists and it can be executed\";\n        return;\n    }\n    auto dst_dir = android::base::Dirname(dst);\n    std::error_code ec;\n    if (access(dst_dir.c_str(), F_OK) != 0) {\n        if (!fs::create_directories(dst_dir, ec)) {\n            LOG(FATAL) << \"Cannot create \" << dst_dir << \": \" << ec.message();\n        }\n    }\n\n    // prefer the generic ramdisk copy of snapuserd, because that's on system side of treble\n    // boundary, and therefore is more likely to be updated along with the Android platform.\n    // The vendor ramdisk copy might be under vendor freeze, or vendor might choose not to update\n    // it.\n    if (access(snapuserd_ramdisk, F_OK) == 0) {\n        LOG(INFO) << \"Using generic ramdisk copy of snapuserd \" << snapuserd_ramdisk;\n        Copy(snapuserd_ramdisk, dst);\n    } else if (access(snapuserd, F_OK) == 0) {\n        LOG(INFO) << \"Using vendor ramdisk copy of snapuserd \" << snapuserd;\n        Copy(snapuserd, dst);\n    }\n}\n\nstd::string GetPageSizeSuffix() {\n    static const size_t page_size = sysconf(_SC_PAGE_SIZE);\n    if (page_size <= 4096) {\n        return \"\";\n    }\n    return android::base::StringPrintf(\"_%zuk\", page_size / 1024);\n}\n\nconstexpr bool EndsWith(const std::string_view str, const std::string_view suffix) {\n    return str.size() >= suffix.size() &&\n           0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);\n}\n\nconstexpr std::string_view GetPageSizeSuffix(std::string_view dirname) {\n    if (EndsWith(dirname, \"_16k\")) {\n        return \"_16k\";\n    }\n    if (EndsWith(dirname, \"_64k\")) {\n        return \"_64k\";\n    }\n    return \"\";\n}\n\n}  // namespace\n\nstd::string GetModuleLoadList(BootMode boot_mode, const std::string& dir_path) {\n    std::string module_load_file;\n\n    switch (boot_mode) {\n        case BootMode::NORMAL_MODE:\n            module_load_file = \"modules.load\";\n            break;\n        case BootMode::RECOVERY_MODE:\n            module_load_file = \"modules.load.recovery\";\n            break;\n        case BootMode::CHARGER_MODE:\n            module_load_file = \"modules.load.charger\";\n            break;\n    }\n\n    if (module_load_file != \"modules.load\") {\n        struct stat fileStat {};\n        std::string load_path = dir_path + \"/\" + module_load_file;\n        // Fall back to modules.load if the other files aren't accessible\n        if (stat(load_path.c_str(), &fileStat)) {\n            module_load_file = \"modules.load\";\n        }\n    }\n\n    return module_load_file;\n}\n\n#define MODULE_BASE_DIR \"/lib/modules\"\nbool LoadKernelModules(BootMode boot_mode, bool want_console, bool want_parallel,\n                       int& modules_loaded) {\n    struct utsname uts {};\n    if (uname(&uts)) {\n        LOG(FATAL) << \"Failed to get kernel version.\";\n    }\n    int major = 0, minor = 0;\n    if (sscanf(uts.release, \"%d.%d\", &major, &minor) != 2) {\n        LOG(FATAL) << \"Failed to parse kernel version \" << uts.release;\n    }\n\n    std::unique_ptr<DIR, decltype(&closedir)> base_dir(opendir(MODULE_BASE_DIR), closedir);\n    if (!base_dir) {\n        LOG(INFO) << \"Unable to open /lib/modules, skipping module loading.\";\n        return true;\n    }\n    dirent* entry = nullptr;\n    std::vector<std::string> module_dirs;\n    const auto page_size_suffix = GetPageSizeSuffix();\n    const std::string release_specific_module_dir = uts.release + page_size_suffix;\n    while ((entry = readdir(base_dir.get()))) {\n        if (entry->d_type != DT_DIR) {\n            continue;\n        }\n        if (entry->d_name == release_specific_module_dir) {\n            LOG(INFO) << \"Release specific kernel module dir \" << release_specific_module_dir\n                      << \" found, loading modules from here with no fallbacks.\";\n            module_dirs.clear();\n            module_dirs.emplace_back(entry->d_name);\n            break;\n        }\n        // Is a directory does not have page size suffix, it does not mean this directory is for 4K\n        // kernels. Certain 16K kernel builds put all modules in /lib/modules/`uname -r` without any\n        // suffix. Therefore, only ignore a directory if it has _16k/_64k suffix and the suffix does\n        // not match system page size.\n        const auto dir_page_size_suffix = GetPageSizeSuffix(entry->d_name);\n        if (!dir_page_size_suffix.empty() && dir_page_size_suffix != page_size_suffix) {\n            continue;\n        }\n        int dir_major = 0, dir_minor = 0;\n        if (sscanf(entry->d_name, \"%d.%d\", &dir_major, &dir_minor) != 2 || dir_major != major ||\n            dir_minor != minor) {\n            continue;\n        }\n        module_dirs.emplace_back(entry->d_name);\n    }\n\n    // Sort the directories so they are iterated over during module loading\n    // in a consistent order. Alphabetical sorting is fine here because the\n    // kernel version at the beginning of the directory name must match the\n    // current kernel version, so the sort only applies to a label that\n    // follows the kernel version, for example /lib/modules/5.4 vs.\n    // /lib/modules/5.4-gki.\n    std::sort(module_dirs.begin(), module_dirs.end());\n\n    for (const auto& module_dir : module_dirs) {\n        std::string dir_path = MODULE_BASE_DIR \"/\";\n        dir_path.append(module_dir);\n        Modprobe m({dir_path}, GetModuleLoadList(boot_mode, dir_path));\n        bool retval = m.LoadListedModules(!want_console);\n        modules_loaded = m.GetModuleCount();\n        if (modules_loaded > 0) {\n            LOG(INFO) << \"Loaded \" << modules_loaded << \" modules from \" << dir_path;\n            return retval;\n        }\n    }\n\n    Modprobe m({MODULE_BASE_DIR}, GetModuleLoadList(boot_mode, MODULE_BASE_DIR));\n    bool retval = (want_parallel) ? m.LoadModulesParallel(std::thread::hardware_concurrency())\n                                  : m.LoadListedModules(!want_console);\n    modules_loaded = m.GetModuleCount();\n    if (modules_loaded > 0) {\n        LOG(INFO) << \"Loaded \" << modules_loaded << \" modules from \" << MODULE_BASE_DIR;\n        return retval;\n    }\n    return true;\n}\n\nstatic bool IsChargerMode(const std::string& cmdline, const std::string& bootconfig) {\n    return bootconfig.find(\"androidboot.mode = \\\"charger\\\"\") != std::string::npos ||\n            cmdline.find(\"androidboot.mode=charger\") != std::string::npos;\n}\n\nstatic BootMode GetBootMode(const std::string& cmdline, const std::string& bootconfig)\n{\n    if (IsChargerMode(cmdline, bootconfig))\n        return BootMode::CHARGER_MODE;\n    else if (IsRecoveryMode() && !ForceNormalBoot(cmdline, bootconfig))\n        return BootMode::RECOVERY_MODE;\n\n    return BootMode::NORMAL_MODE;\n}\n\nstatic void MaybeResumeFromHibernation(const std::string& bootconfig) {\n    std::string hibernationResumeDevice;\n    android::fs_mgr::GetBootconfigFromString(bootconfig, \"androidboot.hibernation_resume_device\",\n                                             &hibernationResumeDevice);\n    if (!hibernationResumeDevice.empty()) {\n        android::base::unique_fd fd(open(\"/sys/power/resume\", O_RDWR | O_CLOEXEC));\n        if (fd >= 0) {\n            if (!android::base::WriteStringToFd(hibernationResumeDevice, fd)) {\n                PLOG(ERROR) << \"Failed to write to /sys/power/resume\";\n            }\n        } else {\n            PLOG(ERROR) << \"Failed to open /sys/power/resume\";\n        }\n    }\n}\n\nstatic std::unique_ptr<FirstStageMount> CreateFirstStageMount(const std::string& cmdline) {\n    auto ret = FirstStageMount::Create(cmdline);\n    if (ret.ok()) {\n        return std::move(*ret);\n    } else {\n        LOG(ERROR) << \"Failed to create FirstStageMount : \" << ret.error();\n        return nullptr;\n    }\n}\n\nint FirstStageMain(int argc, char** argv) {\n    if (REBOOT_BOOTLOADER_ON_PANIC) {\n        InstallRebootSignalHandlers();\n    }\n\n    boot_clock::time_point start_time = boot_clock::now();\n\n    std::vector<std::pair<std::string, int>> errors;\n#define CHECKCALL(x) \\\n    if ((x) != 0) errors.emplace_back(#x \" failed\", errno);\n\n    // Clear the umask.\n    umask(0);\n\n    CHECKCALL(clearenv());\n    CHECKCALL(setenv(\"PATH\", _PATH_DEFPATH, 1));\n    // Get the basic filesystem setup we need put together in the initramdisk\n    // on / and then we'll let the rc file figure out the rest.\n    CHECKCALL(mount(\"tmpfs\", \"/dev\", \"tmpfs\", MS_NOSUID, \"mode=0755\"));\n    CHECKCALL(mkdir(\"/dev/pts\", 0755));\n    CHECKCALL(mkdir(\"/dev/socket\", 0755));\n    CHECKCALL(mkdir(\"/dev/dm-user\", 0755));\n    CHECKCALL(mount(\"devpts\", \"/dev/pts\", \"devpts\", 0, NULL));\n#define MAKE_STR(x) __STRING(x)\n    CHECKCALL(mount(\"proc\", \"/proc\", \"proc\", 0, \"hidepid=2,gid=\" MAKE_STR(AID_READPROC)));\n#undef MAKE_STR\n    // Don't expose the raw commandline to unprivileged processes.\n    CHECKCALL(chmod(\"/proc/cmdline\", 0440));\n    std::string cmdline;\n    android::base::ReadFileToString(\"/proc/cmdline\", &cmdline);\n    // Don't expose the raw bootconfig to unprivileged processes.\n    chmod(\"/proc/bootconfig\", 0440);\n    std::string bootconfig;\n    android::base::ReadFileToString(\"/proc/bootconfig\", &bootconfig);\n    gid_t groups[] = {AID_READPROC};\n    CHECKCALL(setgroups(arraysize(groups), groups));\n    CHECKCALL(mount(\"sysfs\", \"/sys\", \"sysfs\", 0, NULL));\n    CHECKCALL(mount(\"selinuxfs\", \"/sys/fs/selinux\", \"selinuxfs\", 0, NULL));\n\n    CHECKCALL(mknod(\"/dev/kmsg\", S_IFCHR | 0600, makedev(1, 11)));\n\n    if constexpr (WORLD_WRITABLE_KMSG) {\n        CHECKCALL(mknod(\"/dev/kmsg_debug\", S_IFCHR | 0622, makedev(1, 11)));\n    }\n\n    CHECKCALL(mknod(\"/dev/random\", S_IFCHR | 0666, makedev(1, 8)));\n    CHECKCALL(mknod(\"/dev/urandom\", S_IFCHR | 0666, makedev(1, 9)));\n\n    // This is needed for log wrapper, which gets called before ueventd runs.\n    CHECKCALL(mknod(\"/dev/ptmx\", S_IFCHR | 0666, makedev(5, 2)));\n    CHECKCALL(mknod(\"/dev/null\", S_IFCHR | 0666, makedev(1, 3)));\n\n    // These below mounts are done in first stage init so that first stage mount can mount\n    // subdirectories of /mnt/{vendor,product}/.  Other mounts, not required by first stage mount,\n    // should be done in rc files.\n    // Mount staging areas for devices managed by vold\n    // See storage config details at http://source.android.com/devices/storage/\n    CHECKCALL(mount(\"tmpfs\", \"/mnt\", \"tmpfs\", MS_NOEXEC | MS_NOSUID | MS_NODEV,\n                    \"mode=0755,uid=0,gid=1000\"));\n    // /mnt/vendor is used to mount vendor-specific partitions that can not be\n    // part of the vendor partition, e.g. because they are mounted read-write.\n    CHECKCALL(mkdir(\"/mnt/vendor\", 0755));\n    // /mnt/product is used to mount product-specific partitions that can not be\n    // part of the product partition, e.g. because they are mounted read-write.\n    CHECKCALL(mkdir(\"/mnt/product\", 0755));\n\n    // /debug_ramdisk is used to preserve additional files from the debug ramdisk\n    CHECKCALL(mount(\"tmpfs\", \"/debug_ramdisk\", \"tmpfs\", MS_NOEXEC | MS_NOSUID | MS_NODEV,\n                    \"mode=0755,uid=0,gid=0\"));\n\n    // /second_stage_resources is used to preserve files from first to second\n    // stage init\n    CHECKCALL(mount(\"tmpfs\", kSecondStageRes, \"tmpfs\", MS_NOSUID | MS_NODEV,\n                    \"mode=0755,uid=0,gid=0\"));\n\n    if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {\n        CHECKCALL(mount(\"tmpfs\", \"/microdroid_resources\", \"tmpfs\", MS_NOEXEC | MS_NOSUID | MS_NODEV,\n                        \"mode=0750,uid=0,gid=0\"));\n    }\n#undef CHECKCALL\n\n    SetStdioToDevNull(argv);\n    // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually\n    // talk to the outside world...\n    InitKernelLogging(argv);\n\n    if (!errors.empty()) {\n        for (const auto& [error_string, error_errno] : errors) {\n            LOG(ERROR) << error_string << \" \" << strerror(error_errno);\n        }\n        LOG(FATAL) << \"Init encountered errors starting first stage, aborting\";\n    }\n\n    LOG(INFO) << \"init first stage started!\";\n\n    auto old_root_dir = std::unique_ptr<DIR, decltype(&closedir)>{opendir(\"/\"), closedir};\n    if (!old_root_dir) {\n        PLOG(ERROR) << \"Could not opendir(\\\"/\\\"), not freeing ramdisk\";\n    }\n\n    struct stat old_root_info {};\n    if (stat(\"/\", &old_root_info) != 0) {\n        PLOG(ERROR) << \"Could not stat(\\\"/\\\"), not freeing ramdisk\";\n        old_root_dir.reset();\n    }\n\n    auto want_console = ALLOW_FIRST_STAGE_CONSOLE ? FirstStageConsole(cmdline, bootconfig) : 0;\n    auto want_parallel =\n            bootconfig.find(\"androidboot.load_modules_parallel = \\\"true\\\"\") != std::string::npos;\n\n    boot_clock::time_point module_start_time = boot_clock::now();\n    int module_count = 0;\n    BootMode boot_mode = GetBootMode(cmdline, bootconfig);\n    if (!LoadKernelModules(boot_mode, want_console,\n                           want_parallel, module_count)) {\n        if (want_console != FirstStageConsoleParam::DISABLED) {\n            LOG(ERROR) << \"Failed to load kernel modules, starting console\";\n        } else {\n            LOG(FATAL) << \"Failed to load kernel modules\";\n        }\n    }\n    if (module_count > 0) {\n        auto module_elapse_time = std::chrono::duration_cast<std::chrono::milliseconds>(\n                boot_clock::now() - module_start_time);\n        setenv(kEnvInitModuleDurationMs, std::to_string(module_elapse_time.count()).c_str(), 1);\n        LOG(INFO) << \"Loaded \" << module_count << \" kernel modules took \"\n                  << module_elapse_time.count() << \" ms\";\n    }\n\n    MaybeResumeFromHibernation(bootconfig);\n\n    std::unique_ptr<FirstStageMount> fsm;\n\n    bool created_devices = false;\n    if (want_console == FirstStageConsoleParam::CONSOLE_ON_FAILURE) {\n        if (!IsRecoveryMode()) {\n            fsm = CreateFirstStageMount(cmdline);\n            if (fsm) {\n                created_devices = fsm->DoCreateDevices();\n                if (!created_devices) {\n                    LOG(ERROR) << \"Failed to create device nodes early\";\n                }\n            }\n        }\n        StartConsole(cmdline);\n    }\n\n    if (access(kBootImageRamdiskProp, F_OK) == 0) {\n        std::string dest = GetRamdiskPropForSecondStage();\n        std::string dir = android::base::Dirname(dest);\n        std::error_code ec;\n        if (!fs::create_directories(dir, ec) && !!ec) {\n            LOG(FATAL) << \"Can't mkdir \" << dir << \": \" << ec.message();\n        }\n        if (!fs::copy_file(kBootImageRamdiskProp, dest, ec)) {\n            LOG(FATAL) << \"Can't copy \" << kBootImageRamdiskProp << \" to \" << dest << \": \"\n                       << ec.message();\n        }\n        LOG(INFO) << \"Copied ramdisk prop to \" << dest;\n    }\n\n    // If \"/force_debuggable\" is present, the second-stage init will use a userdebug\n    // sepolicy and load adb_debug.prop to allow adb root, if the device is unlocked.\n    if (access(\"/force_debuggable\", F_OK) == 0) {\n        constexpr const char adb_debug_prop_src[] = \"/adb_debug.prop\";\n        constexpr const char userdebug_plat_sepolicy_cil_src[] = \"/userdebug_plat_sepolicy.cil\";\n        std::error_code ec;  // to invoke the overloaded copy_file() that won't throw.\n        if (access(adb_debug_prop_src, F_OK) == 0 &&\n            !fs::copy_file(adb_debug_prop_src, kDebugRamdiskProp, ec)) {\n            LOG(WARNING) << \"Can't copy \" << adb_debug_prop_src << \" to \" << kDebugRamdiskProp\n                         << \": \" << ec.message();\n        }\n        if (access(userdebug_plat_sepolicy_cil_src, F_OK) == 0 &&\n            !fs::copy_file(userdebug_plat_sepolicy_cil_src, kDebugRamdiskSEPolicy, ec)) {\n            LOG(WARNING) << \"Can't copy \" << userdebug_plat_sepolicy_cil_src << \" to \"\n                         << kDebugRamdiskSEPolicy << \": \" << ec.message();\n        }\n        // setenv for second-stage init to read above kDebugRamdisk* files.\n        setenv(\"INIT_FORCE_DEBUGGABLE\", \"true\", 1);\n    }\n\n    if (ForceNormalBoot(cmdline, bootconfig)) {\n        mkdir(\"/first_stage_ramdisk\", 0755);\n        PrepareSwitchRoot();\n        // SwitchRoot() must be called with a mount point as the target, so we bind mount the\n        // target directory to itself here.\n        if (mount(\"/first_stage_ramdisk\", \"/first_stage_ramdisk\", nullptr, MS_BIND, nullptr) != 0) {\n            PLOG(FATAL) << \"Could not bind mount /first_stage_ramdisk to itself\";\n        }\n        SwitchRoot(\"/first_stage_ramdisk\");\n    }\n\n    if (IsRecoveryMode()) {\n        LOG(INFO) << \"First stage mount skipped (recovery mode)\";\n    } else {\n        if (!fsm) {\n            fsm = CreateFirstStageMount(cmdline);\n        }\n        if (!fsm) {\n            LOG(FATAL) << \"FirstStageMount not available\";\n        }\n\n        if (!created_devices && !fsm->DoCreateDevices()) {\n            LOG(FATAL) << \"Failed to create devices required for first stage mount\";\n        }\n\n        if (!fsm->DoFirstStageMount()) {\n            LOG(FATAL) << \"Failed to mount required partitions early ...\";\n        }\n    }\n\n    struct stat new_root_info {};\n    if (stat(\"/\", &new_root_info) != 0) {\n        PLOG(ERROR) << \"Could not stat(\\\"/\\\"), not freeing ramdisk\";\n        old_root_dir.reset();\n    }\n\n    if (old_root_dir && old_root_info.st_dev != new_root_info.st_dev) {\n        FreeRamdisk(old_root_dir.get(), old_root_info.st_dev);\n    }\n\n    SetInitAvbVersionInRecovery();\n\n    setenv(kEnvFirstStageStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(),\n           1);\n\n    const char* path = \"/system/bin/init\";\n    const char* args[] = {path, \"selinux_setup\", nullptr};\n    auto fd = open(\"/dev/kmsg\", O_WRONLY | O_CLOEXEC);\n    dup2(fd, STDOUT_FILENO);\n    dup2(fd, STDERR_FILENO);\n    close(fd);\n    execv(path, const_cast<char**>(args));\n\n    // execv() only returns if an error happened, in which case we\n    // panic and never fall through this conditional.\n    PLOG(FATAL) << \"execv(\\\"\" << path << \"\\\") failed\";\n\n    return 1;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/first_stage_init.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace android {\nnamespace init {\n\nint FirstStageMain(int argc, char** argv);\n\nstatic constexpr char kEnvFirstStageStartedAt[] = \"FIRST_STAGE_STARTED_AT\";\nstatic constexpr char kEnvInitModuleDurationMs[] = \"INIT_MODULE_DURATION_MS\";\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/first_stage_main.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"first_stage_init.h\"\n\nint main(int argc, char** argv) {\n    return android::init::FirstStageMain(argc, argv);\n}\n"
  },
  {
    "path": "init/first_stage_mount.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"first_stage_mount.h\"\n\n#include <signal.h>\n#include <stdlib.h>\n#include <sys/mount.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <filesystem>\n#include <map>\n#include <memory>\n#include <set>\n#include <string>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android/avf_cc_flags.h>\n#include <bootloader_message/bootloader_message.h>\n#include <cutils/android_reboot.h>\n#include <fs_avb/fs_avb.h>\n#include <fs_mgr.h>\n#include <fs_mgr_dm_linear.h>\n#include <fs_mgr_overlayfs.h>\n#include <libfiemap/image_manager.h>\n#include <libgsi/libgsi.h>\n#include <liblp/liblp.h>\n#include <libsnapshot/snapshot.h>\n\n#include \"block_dev_initializer.h\"\n#include \"devices.h\"\n#include \"reboot_utils.h\"\n#include \"result.h\"\n#include \"snapuserd_transition.h\"\n#include \"switch_root.h\"\n#include \"uevent.h\"\n#include \"uevent_listener.h\"\n#include \"util.h\"\n\nusing android::base::ReadFileToString;\nusing android::base::Result;\nusing android::base::Split;\nusing android::base::StringPrintf;\nusing android::base::Timer;\nusing android::fiemap::IImageManager;\nusing android::fs_mgr::AvbHandle;\nusing android::fs_mgr::AvbHandleStatus;\nusing android::fs_mgr::AvbHashtreeResult;\nusing android::fs_mgr::AvbUniquePtr;\nusing android::fs_mgr::Fstab;\nusing android::fs_mgr::FstabEntry;\nusing android::fs_mgr::ReadDefaultFstab;\nusing android::fs_mgr::ReadFstabFromDt;\nusing android::fs_mgr::SkipMountingPartitions;\nusing android::fs_mgr::TransformFstabForDsu;\nusing android::snapshot::SnapshotManager;\n\nusing namespace std::literals;\n\nnamespace android {\nnamespace init {\n\n// Class Declarations\n// ------------------\nclass FirstStageMountVBootV2 : public FirstStageMount {\n  public:\n    friend void SetInitAvbVersionInRecovery();\n\n    FirstStageMountVBootV2(Fstab fstab);\n    virtual ~FirstStageMountVBootV2() = default;\n\n    bool DoCreateDevices() override;\n    bool DoFirstStageMount() override;\n\n  private:\n    bool InitDevices();\n    bool InitRequiredDevices(std::set<std::string> devices);\n    bool CreateLogicalPartitions();\n    bool CreateSnapshotPartitions(SnapshotManager* sm);\n    bool MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,\n                        Fstab::iterator* end = nullptr);\n\n    bool MountPartitions();\n    bool TrySwitchSystemAsRoot();\n    bool IsDmLinearEnabled();\n    void GetSuperDeviceName(std::set<std::string>* devices);\n    bool InitDmLinearBackingDevices(const android::fs_mgr::LpMetadata& metadata);\n    void UseDsuIfPresent();\n    // Reads all fstab.avb_keys from the ramdisk for first-stage mount.\n    void PreloadAvbKeys();\n    // Copies /avb/*.avbpubkey used for DSU from the ramdisk to /metadata for key\n    // revocation check by DSU installation service.\n    void CopyDsuAvbKeys();\n\n    bool GetDmVerityDevices(std::set<std::string>* devices);\n    bool SetUpDmVerity(FstabEntry* fstab_entry);\n\n    void RequestTradeInModeWipeIfNeeded();\n\n    bool InitAvbHandle();\n\n    bool need_dm_verity_;\n    bool dsu_not_on_userdata_ = false;\n    bool use_snapuserd_ = false;\n\n    Fstab fstab_;\n    // The super path is only set after InitDevices, and is invalid before.\n    std::string super_path_;\n    std::string super_partition_name_;\n    BlockDevInitializer block_dev_init_;\n    // Reads all AVB keys before chroot into /system, as they might be used\n    // later when mounting other partitions, e.g., /vendor and /product.\n    std::map<std::string, std::vector<std::string>> preload_avb_key_blobs_;\n\n    std::vector<std::string> vbmeta_partitions_;\n    AvbUniquePtr avb_handle_;\n};\n\n// Static Functions\n// ----------------\nstatic inline bool IsDtVbmetaCompatible(const Fstab& fstab) {\n    if (std::any_of(fstab.begin(), fstab.end(),\n                    [](const auto& entry) { return entry.fs_mgr_flags.avb; })) {\n        return true;\n    }\n    return is_android_dt_value_expected(\"vbmeta/compatible\", \"android,vbmeta\");\n}\n\nstatic Result<Fstab> ReadFirstStageFstabAndroid() {\n    Fstab fstab;\n    if (!ReadFstabFromDt(&fstab)) {\n        if (ReadDefaultFstab(&fstab)) {\n            fstab.erase(std::remove_if(fstab.begin(), fstab.end(),\n                                       [](const auto& entry) {\n                                           return !entry.fs_mgr_flags.first_stage_mount;\n                                       }),\n                        fstab.end());\n        } else {\n            return Error() << \"failed to read default fstab for first stage mount\";\n        }\n    }\n    return fstab;\n}\n\n// Note: this is a temporary solution to avoid blocking devs that depend on /vendor partition in\n// Microdroid. For the proper solution the /vendor fstab should probably be defined in the DT.\n// TODO(b/285855430): refactor this\n// TODO(b/285855436): verify key microdroid-vendor was signed with.\n// TODO(b/285855436): should be mounted on top of dm-verity device.\nstatic Result<Fstab> ReadFirstStageFstabMicrodroid(const std::string& cmdline) {\n    Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        return Error() << \"failed to read fstab\";\n    }\n    if (cmdline.find(\"androidboot.microdroid.mount_vendor=1\") == std::string::npos) {\n        // We weren't asked to mount /vendor partition, filter it out from the fstab.\n        auto predicate = [](const auto& entry) { return entry.mount_point == \"/vendor\"; };\n        fstab.erase(std::remove_if(fstab.begin(), fstab.end(), predicate), fstab.end());\n    }\n    return fstab;\n}\n\nstatic bool GetRootEntry(FstabEntry* root_entry) {\n    Fstab proc_mounts;\n    if (!ReadFstabFromFile(\"/proc/mounts\", &proc_mounts)) {\n        LOG(ERROR) << \"Could not read /proc/mounts and /system not in fstab, /system will not be \"\n                      \"available for overlayfs\";\n        return false;\n    }\n\n    auto entry = std::find_if(proc_mounts.begin(), proc_mounts.end(), [](const auto& entry) {\n        return entry.mount_point == \"/\" && entry.fs_type != \"rootfs\";\n    });\n\n    if (entry == proc_mounts.end()) {\n        LOG(ERROR) << \"Could not get mount point for '/' in /proc/mounts, /system will not be \"\n                      \"available for overlayfs\";\n        return false;\n    }\n\n    *root_entry = std::move(*entry);\n\n    // We don't know if we're avb or not, so we query device mapper as if we are avb.  If we get a\n    // success, then mark as avb, otherwise default to verify.\n    auto& dm = android::dm::DeviceMapper::Instance();\n    if (dm.GetState(\"vroot\") != android::dm::DmDeviceState::INVALID) {\n        root_entry->fs_mgr_flags.avb = true;\n    }\n    return true;\n}\n\nstatic bool IsStandaloneImageRollback(const AvbHandle& builtin_vbmeta,\n                                      const AvbHandle& standalone_vbmeta,\n                                      const FstabEntry& fstab_entry) {\n    std::string old_spl = builtin_vbmeta.GetSecurityPatchLevel(fstab_entry);\n    std::string new_spl = standalone_vbmeta.GetSecurityPatchLevel(fstab_entry);\n\n    bool rollbacked = false;\n    if (old_spl.empty() || new_spl.empty() || new_spl < old_spl) {\n        rollbacked = true;\n    }\n\n    if (rollbacked) {\n        LOG(ERROR) << \"Image rollback detected for \" << fstab_entry.mount_point\n                   << \", SPL switches from '\" << old_spl << \"' to '\" << new_spl << \"'\";\n        if (AvbHandle::IsDeviceUnlocked()) {\n            LOG(INFO) << \"Allowing rollbacked standalone image when the device is unlocked\";\n            return false;\n        }\n    }\n\n    return rollbacked;\n}\n\nResult<std::unique_ptr<FirstStageMount>> FirstStageMount::Create(const std::string& cmdline) {\n    Result<Fstab> fstab;\n    if (IsMicrodroid()) {\n        fstab = ReadFirstStageFstabMicrodroid(cmdline);\n    } else {\n        fstab = ReadFirstStageFstabAndroid();\n    }\n    if (!fstab.ok()) {\n        return fstab.error();\n    }\n\n    return std::make_unique<FirstStageMountVBootV2>(std::move(*fstab));\n}\n\nbool FirstStageMountVBootV2::DoCreateDevices() {\n    if (!InitDevices()) return false;\n\n    // Mount /metadata before creating logical partitions, since we need to\n    // know whether a snapshot merge is in progress.\n    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {\n        return entry.mount_point == \"/metadata\";\n    });\n    if (metadata_partition != fstab_.end()) {\n        if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {\n            // Copies DSU AVB keys from the ramdisk to /metadata.\n            // Must be done before the following TrySwitchSystemAsRoot().\n            // Otherwise, ramdisk will be inaccessible after switching root.\n            CopyDsuAvbKeys();\n        }\n    }\n\n    if (!CreateLogicalPartitions()) return false;\n\n    return true;\n}\n\nbool FirstStageMountVBootV2::DoFirstStageMount() {\n    RequestTradeInModeWipeIfNeeded();\n\n    if (!IsDmLinearEnabled() && fstab_.empty()) {\n        // Nothing to mount.\n        LOG(INFO) << \"First stage mount skipped (missing/incompatible/empty fstab in device tree)\";\n        return true;\n    }\n\n    if (!MountPartitions()) return false;\n\n    return true;\n}\n\n// TODO: should this be in a library in packages/modules/Virtualization first_stage_init links?\nstatic bool IsMicrodroidStrictBoot() {\n    return access(\"/proc/device-tree/chosen/avf,strict-boot\", F_OK) == 0;\n}\n\nbool FirstStageMountVBootV2::InitDevices() {\n    if (!block_dev_init_.InitBootDevicesFromPartUuid()) {\n        return false;\n    }\n\n    std::set<std::string> devices;\n    GetSuperDeviceName(&devices);\n\n    if (!GetDmVerityDevices(&devices)) {\n        return false;\n    }\n    if (!InitRequiredDevices(std::move(devices))) {\n        return false;\n    }\n\n    if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {\n        if (IsMicrodroidStrictBoot()) {\n            if (!block_dev_init_.InitPlatformDevice(\"open-dice0\")) {\n                return false;\n            }\n        }\n    }\n\n    if (IsDmLinearEnabled()) {\n        auto super_symlink = \"/dev/block/by-name/\"s + super_partition_name_;\n        if (!android::base::Realpath(super_symlink, &super_path_)) {\n            PLOG(ERROR) << \"realpath failed: \" << super_symlink;\n            return false;\n        }\n    }\n    return true;\n}\n\nbool FirstStageMountVBootV2::IsDmLinearEnabled() {\n    for (const auto& entry : fstab_) {\n        if (entry.fs_mgr_flags.logical) return true;\n    }\n    return false;\n}\n\nvoid FirstStageMountVBootV2::GetSuperDeviceName(std::set<std::string>* devices) {\n    // Add any additional devices required for dm-linear mappings.\n    if (!IsDmLinearEnabled()) {\n        return;\n    }\n\n    devices->emplace(super_partition_name_);\n}\n\n// Creates devices with uevent->partition_name matching ones in the given set.\n// Found partitions will then be removed from it for the subsequent member\n// function to check which devices are NOT created.\nbool FirstStageMountVBootV2::InitRequiredDevices(std::set<std::string> devices) {\n    if (!block_dev_init_.InitDeviceMapper()) {\n        return false;\n    }\n    if (devices.empty()) {\n        return true;\n    }\n    return block_dev_init_.InitDevices(std::move(devices));\n}\n\nbool FirstStageMountVBootV2::InitDmLinearBackingDevices(\n        const android::fs_mgr::LpMetadata& metadata) {\n    std::set<std::string> devices;\n\n    auto partition_names = android::fs_mgr::GetBlockDevicePartitionNames(metadata);\n    for (const auto& partition_name : partition_names) {\n        // The super partition was found in the earlier pass.\n        if (partition_name == super_partition_name_) {\n            continue;\n        }\n        devices.emplace(partition_name);\n    }\n    if (devices.empty()) {\n        return true;\n    }\n    return InitRequiredDevices(std::move(devices));\n}\n\nbool FirstStageMountVBootV2::CreateLogicalPartitions() {\n    if (!IsDmLinearEnabled()) {\n        return true;\n    }\n    if (super_path_.empty()) {\n        LOG(ERROR) << \"Could not locate logical partition tables in partition \"\n                   << super_partition_name_;\n        return false;\n    }\n\n    if (SnapshotManager::IsSnapshotManagerNeeded()) {\n        auto init_devices = [this](const std::string& device) -> bool {\n            if (android::base::StartsWith(device, \"/dev/block/dm-\")) {\n                return block_dev_init_.InitDmDevice(device);\n            }\n            return block_dev_init_.InitDevices({device});\n        };\n\n        SnapshotManager::MapTempOtaMetadataPartitionIfNeeded(init_devices);\n        auto sm = SnapshotManager::NewForFirstStageMount();\n        if (!sm) {\n            return false;\n        }\n        if (sm->NeedSnapshotsInFirstStageMount()) {\n            return CreateSnapshotPartitions(sm.get());\n        }\n    }\n\n    auto metadata = android::fs_mgr::ReadCurrentMetadata(super_path_);\n    if (!metadata) {\n        LOG(ERROR) << \"Could not read logical partition metadata from \" << super_path_;\n        return false;\n    }\n    if (!InitDmLinearBackingDevices(*metadata.get())) {\n        return false;\n    }\n    return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);\n}\n\nbool FirstStageMountVBootV2::CreateSnapshotPartitions(SnapshotManager* sm) {\n    // When COW images are present for snapshots, they are stored on\n    // the data partition.\n    if (!InitRequiredDevices({\"userdata\"})) {\n        return false;\n    }\n\n    use_snapuserd_ = sm->IsSnapuserdRequired();\n    if (use_snapuserd_) {\n        LaunchFirstStageSnapuserd();\n    }\n\n    sm->SetUeventRegenCallback([this](const std::string& device) -> bool {\n        if (android::base::StartsWith(device, \"/dev/block/dm-\")) {\n            return block_dev_init_.InitDmDevice(device);\n        }\n        if (android::base::StartsWith(device, \"/dev/dm-user/\")) {\n            return block_dev_init_.InitDmUser(android::base::Basename(device));\n        }\n        return block_dev_init_.InitDevices({device});\n    });\n    if (!sm->CreateLogicalAndSnapshotPartitions(super_path_)) {\n        return false;\n    }\n\n    if (use_snapuserd_) {\n        CleanupSnapuserdSocket();\n    }\n    return true;\n}\n\nbool FirstStageMountVBootV2::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,\n                                            Fstab::iterator* end) {\n    // Sets end to begin + 1, so we can just return on failure below.\n    if (end) {\n        *end = begin + 1;\n    }\n\n    if (!fs_mgr_create_canonical_mount_point(begin->mount_point)) {\n        return false;\n    }\n\n    if (begin->fs_mgr_flags.logical) {\n        if (!fs_mgr_update_logical_partition(&(*begin))) {\n            return false;\n        }\n        if (!block_dev_init_.InitDmDevice(begin->blk_device)) {\n            return false;\n        }\n    }\n    if (!SetUpDmVerity(&(*begin))) {\n        PLOG(ERROR) << \"Failed to setup verity for '\" << begin->mount_point << \"'\";\n        return false;\n    }\n\n    bool mounted = (fs_mgr_do_mount_one(*begin) == 0);\n\n    // Try other mounts with the same mount point.\n    Fstab::iterator current = begin + 1;\n    for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) {\n        if (!mounted) {\n            // blk_device is already updated to /dev/dm-<N> by SetUpDmVerity() above.\n            // Copy it from the begin iterator.\n            current->blk_device = begin->blk_device;\n            mounted = (fs_mgr_do_mount_one(*current) == 0);\n        }\n    }\n    if (erase_same_mounts) {\n        current = fstab_.erase(begin, current);\n    }\n    if (end) {\n        *end = current;\n    }\n    return mounted;\n}\n\nvoid FirstStageMountVBootV2::PreloadAvbKeys() {\n    for (const auto& entry : fstab_) {\n        // No need to cache the key content if it's empty, or is already cached.\n        if (entry.avb_keys.empty() || preload_avb_key_blobs_.count(entry.avb_keys)) {\n            continue;\n        }\n\n        // Determines all key paths first.\n        std::vector<std::string> key_paths;\n        if (is_dir(entry.avb_keys.c_str())) {  // fstab_keys might be a dir, e.g., /avb.\n            const char* avb_key_dir = entry.avb_keys.c_str();\n            std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(avb_key_dir), closedir);\n            if (!dir) {\n                LOG(ERROR) << \"Failed to opendir: \" << dir;\n                continue;\n            }\n            // Gets all key pathes under the dir.\n            struct dirent* de;\n            while ((de = readdir(dir.get()))) {\n                if (de->d_type != DT_REG) continue;\n                std::string full_path = StringPrintf(\"%s/%s\", avb_key_dir, de->d_name);\n                key_paths.emplace_back(std::move(full_path));\n            }\n            std::sort(key_paths.begin(), key_paths.end());\n        } else {\n            // avb_keys are key paths separated by \":\", if it's not a dir.\n            key_paths = Split(entry.avb_keys, \":\");\n        }\n\n        // Reads the key content then cache it.\n        std::vector<std::string> key_blobs;\n        for (const auto& path : key_paths) {\n            std::string key_value;\n            if (!ReadFileToString(path, &key_value)) {\n                continue;\n            }\n            key_blobs.emplace_back(std::move(key_value));\n        }\n\n        // Maps entry.avb_keys to actual key blobs.\n        preload_avb_key_blobs_[entry.avb_keys] = std::move(key_blobs);\n    }\n}\n\n// If system is in the fstab then we're not a system-as-root device, and in\n// this case, we mount system first then pivot to it.  From that point on,\n// we are effectively identical to a system-as-root device.\nbool FirstStageMountVBootV2::TrySwitchSystemAsRoot() {\n    UseDsuIfPresent();\n    // Preloading all AVB keys from the ramdisk before switching root to /system.\n    PreloadAvbKeys();\n\n    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {\n        return entry.mount_point == \"/system\";\n    });\n\n    if (system_partition == fstab_.end()) return true;\n\n    if (use_snapuserd_) {\n        SaveRamdiskPathToSnapuserd();\n    }\n\n    if (!MountPartition(system_partition, false /* erase_same_mounts */)) {\n        PLOG(ERROR) << \"Failed to mount /system\";\n        return false;\n    }\n    if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {\n        LOG(ERROR) << \"check_at_most_once forbidden on external media\";\n        return false;\n    }\n\n    SwitchRoot(\"/system\");\n\n    return true;\n}\n\nstatic bool MaybeDeriveMicrodroidVendorDiceNode(Fstab* fstab) {\n    std::optional<std::string> microdroid_vendor_block_dev;\n    for (auto entry = fstab->begin(); entry != fstab->end(); entry++) {\n        if (entry->mount_point == \"/vendor\") {\n            microdroid_vendor_block_dev.emplace(entry->blk_device);\n            break;\n        }\n    }\n    if (!microdroid_vendor_block_dev.has_value()) {\n        LOG(VERBOSE) << \"No microdroid vendor partition to mount\";\n        return true;\n    }\n    // clang-format off\n    const std::array<const char*, 8> args = {\n        \"/system/bin/derive_microdroid_vendor_dice_node\",\n                \"--dice-driver\", \"/dev/open-dice0\",\n                \"--microdroid-vendor-disk-image\", microdroid_vendor_block_dev->data(),\n                \"--output\", \"/microdroid_resources/dice_chain.raw\", nullptr,\n    };\n    // clang-format-on\n    // ForkExecveAndWaitForCompletion calls waitpid to wait for the fork-ed process to finish.\n    // The first_stage_console adds SA_NOCLDWAIT flag to the SIGCHLD handler, which means that\n    // waitpid will always return -ECHLD. Here we re-register a default handler, so that waitpid\n    // works.\n    LOG(INFO) << \"Deriving dice node for microdroid vendor partition\";\n    signal(SIGCHLD, SIG_DFL);\n    if (!ForkExecveAndWaitForCompletion(args[0], (char**)args.data())) {\n        LOG(ERROR) << \"Failed to derive microdroid vendor dice node\";\n        return false;\n    }\n    return true;\n}\n\nbool FirstStageMountVBootV2::MountPartitions() {\n    if (!TrySwitchSystemAsRoot()) return false;\n\n    if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {\n        if (!MaybeDeriveMicrodroidVendorDiceNode(&fstab_)) {\n            return false;\n        }\n    }\n\n    if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;\n\n    for (auto current = fstab_.begin(); current != fstab_.end();) {\n        // We've already mounted /system above.\n        if (current->mount_point == \"/system\") {\n            ++current;\n            continue;\n        }\n\n        // Handle overlayfs entries later.\n        if (current->fs_type == \"overlay\") {\n            ++current;\n            continue;\n        }\n\n        // Skip raw partition entries such as boot, dtbo, etc.\n        // Having emmc fstab entries allows us to probe current->vbmeta_partition\n        // in InitDevices() when they are AVB chained partitions.\n        if (current->fs_type == \"emmc\") {\n            ++current;\n            continue;\n        }\n\n        Fstab::iterator end;\n        if (!MountPartition(current, false /* erase_same_mounts */, &end)) {\n            if (current->fs_mgr_flags.no_fail) {\n                LOG(INFO) << \"Failed to mount \" << current->mount_point\n                          << \", ignoring mount for no_fail partition\";\n            } else if (current->fs_mgr_flags.formattable) {\n                LOG(INFO) << \"Failed to mount \" << current->mount_point\n                          << \", ignoring mount for formattable partition\";\n            } else {\n                PLOG(ERROR) << \"Failed to mount \" << current->mount_point;\n                return false;\n            }\n        }\n        current = end;\n    }\n\n    for (const auto& entry : fstab_) {\n        if (entry.fs_type == \"overlay\") {\n            fs_mgr_mount_overlayfs_fstab_entry(entry);\n        }\n    }\n\n    // If we don't see /system or / in the fstab, then we need to create an root entry for\n    // overlayfs.\n    if (!GetEntryForMountPoint(&fstab_, \"/system\") && !GetEntryForMountPoint(&fstab_, \"/\")) {\n        FstabEntry root_entry;\n        if (GetRootEntry(&root_entry)) {\n            fstab_.emplace_back(std::move(root_entry));\n        }\n    }\n\n    // heads up for instantiating required device(s) for overlayfs logic\n    auto init_devices = [this](std::set<std::string> devices) -> bool {\n        for (auto iter = devices.begin(); iter != devices.end();) {\n            if (android::base::StartsWith(*iter, \"/dev/block/dm-\")) {\n                if (!block_dev_init_.InitDmDevice(*iter)) {\n                    return false;\n                }\n                iter = devices.erase(iter);\n            } else {\n                iter++;\n            }\n        }\n        return InitRequiredDevices(std::move(devices));\n    };\n    MapScratchPartitionIfNeeded(&fstab_, init_devices);\n\n    fs_mgr_overlayfs_mount_all(&fstab_);\n\n    return true;\n}\n\n// Preserves /avb/*.avbpubkey to /metadata/gsi/dsu/avb/, so they can be used for\n// key revocation check by DSU installation service.  Note that failing to\n// copy files to /metadata is NOT fatal, because it is auxiliary to perform\n// public key matching before booting into DSU images on next boot. The actual\n// public key matching will still be done on next boot to DSU.\nvoid FirstStageMountVBootV2::CopyDsuAvbKeys() {\n    std::error_code ec;\n    // Removing existing keys in gsi::kDsuAvbKeyDir as they might be stale.\n    std::filesystem::remove_all(gsi::kDsuAvbKeyDir, ec);\n    if (ec) {\n        LOG(ERROR) << \"Failed to remove directory \" << gsi::kDsuAvbKeyDir << \": \" << ec.message();\n    }\n    // Copy keys from the ramdisk /avb/* to gsi::kDsuAvbKeyDir.\n    static constexpr char kRamdiskAvbKeyDir[] = \"/avb\";\n    std::filesystem::copy(kRamdiskAvbKeyDir, gsi::kDsuAvbKeyDir, ec);\n    if (ec) {\n        LOG(ERROR) << \"Failed to copy \" << kRamdiskAvbKeyDir << \" into \" << gsi::kDsuAvbKeyDir\n                   << \": \" << ec.message();\n    }\n}\n\nvoid FirstStageMountVBootV2::UseDsuIfPresent() {\n    std::string error;\n\n    if (!android::gsi::CanBootIntoGsi(&error)) {\n        LOG(INFO) << \"DSU \" << error << \", proceeding with normal boot\";\n        return;\n    }\n\n    auto init_devices = [this](std::set<std::string> devices) -> bool {\n        if (devices.count(\"userdata\") == 0 || devices.size() > 1) {\n            dsu_not_on_userdata_ = true;\n        }\n        return InitRequiredDevices(std::move(devices));\n    };\n    std::string active_dsu;\n    if (!gsi::GetActiveDsu(&active_dsu)) {\n        LOG(ERROR) << \"Failed to GetActiveDsu\";\n        return;\n    }\n    LOG(INFO) << \"DSU slot: \" << active_dsu;\n    auto images = IImageManager::Open(\"dsu/\" + active_dsu, 0ms);\n    if (!images || !images->MapAllImages(init_devices)) {\n        LOG(ERROR) << \"DSU partition layout could not be instantiated\";\n        return;\n    }\n\n    if (!android::gsi::MarkSystemAsGsi()) {\n        PLOG(ERROR) << \"DSU indicator file could not be written\";\n        return;\n    }\n\n    // Publish the logical partition names for TransformFstabForDsu() and ReadFstabFromFile().\n    const auto dsu_partitions = images->GetAllBackingImages();\n    WriteFile(gsi::kGsiLpNamesFile, android::base::Join(dsu_partitions, \",\"));\n    TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);\n}\n\nFirstStageMountVBootV2::FirstStageMountVBootV2(Fstab fstab)\n    : need_dm_verity_(false), fstab_(std::move(fstab)), avb_handle_(nullptr) {\n    super_partition_name_ = fs_mgr_get_super_partition_name();\n\n    std::string device_tree_vbmeta_parts;\n    read_android_dt_file(\"vbmeta/parts\", &device_tree_vbmeta_parts);\n\n    for (auto&& partition : Split(device_tree_vbmeta_parts, \",\")) {\n        if (!partition.empty()) {\n            vbmeta_partitions_.emplace_back(std::move(partition));\n        }\n    }\n\n    for (const auto& entry : fstab_) {\n        if (!entry.vbmeta_partition.empty()) {\n            vbmeta_partitions_.emplace_back(entry.vbmeta_partition);\n        }\n    }\n\n    if (vbmeta_partitions_.empty()) {\n        LOG(ERROR) << \"Failed to read vbmeta partitions.\";\n    }\n}\n\nbool FirstStageMountVBootV2::GetDmVerityDevices(std::set<std::string>* devices) {\n    need_dm_verity_ = false;\n\n    std::set<std::string> logical_partitions;\n\n    // fstab_rec->blk_device has A/B suffix.\n    for (const auto& fstab_entry : fstab_) {\n        if (fstab_entry.fs_mgr_flags.avb) {\n            need_dm_verity_ = true;\n        }\n        // Skip pseudo filesystems.\n        if (fstab_entry.fs_type == \"overlay\") {\n            continue;\n        }\n        if (fstab_entry.fs_mgr_flags.logical) {\n            // Don't try to find logical partitions via uevent regeneration.\n            logical_partitions.emplace(basename(fstab_entry.blk_device.c_str()));\n        } else {\n            devices->emplace(basename(fstab_entry.blk_device.c_str()));\n        }\n    }\n\n    // Any partitions needed for verifying the partitions used in first stage mount, e.g. vbmeta\n    // must be provided as vbmeta_partitions.\n    if (need_dm_verity_) {\n        if (vbmeta_partitions_.empty()) {\n            LOG(ERROR) << \"Missing vbmeta partitions\";\n            return false;\n        }\n        std::string ab_suffix = fs_mgr_get_slot_suffix();\n        for (const auto& partition : vbmeta_partitions_) {\n            std::string partition_name = partition + ab_suffix;\n            if (logical_partitions.count(partition_name)) {\n                continue;\n            }\n            // devices is of type std::set so it's not an issue to emplace a\n            // partition twice. e.g., /vendor might be in both places:\n            //   - device_tree_vbmeta_parts_ = \"vbmeta,boot,system,vendor\"\n            //   - mount_fstab_recs_: /vendor_a\n            devices->emplace(partition_name);\n        }\n    }\n    return true;\n}\n\nbool IsHashtreeDisabled(const AvbHandle& vbmeta, const std::string& mount_point) {\n    if (vbmeta.status() == AvbHandleStatus::kHashtreeDisabled ||\n        vbmeta.status() == AvbHandleStatus::kVerificationDisabled) {\n        LOG(ERROR) << \"Top-level vbmeta is disabled, skip Hashtree setup for \" << mount_point;\n        return true;  // Returns true to mount the partition directly.\n    }\n    return false;\n}\n\nbool FirstStageMountVBootV2::SetUpDmVerity(FstabEntry* fstab_entry) {\n    AvbHashtreeResult hashtree_result;\n\n    // It's possible for a fstab_entry to have both avb_keys and avb flag.\n    // In this case, try avb_keys first, then fallback to avb flag.\n    if (!fstab_entry->avb_keys.empty()) {\n        if (!InitAvbHandle()) return false;\n        // Checks if hashtree should be disabled from the top-level /vbmeta.\n        if (IsHashtreeDisabled(*avb_handle_, fstab_entry->mount_point)) {\n            return true;\n        }\n        auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(\n                *fstab_entry, preload_avb_key_blobs_[fstab_entry->avb_keys]);\n        if (!avb_standalone_handle) {\n            LOG(ERROR) << \"Failed to load offline vbmeta for \" << fstab_entry->mount_point;\n            // Fallbacks to built-in hashtree if fs_mgr_flags.avb is set.\n            if (!fstab_entry->fs_mgr_flags.avb) return false;\n            LOG(INFO) << \"Fallback to built-in hashtree for \" << fstab_entry->mount_point;\n            hashtree_result =\n                    avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);\n        } else {\n            // Sets up hashtree via the standalone handle.\n            if (IsStandaloneImageRollback(*avb_handle_, *avb_standalone_handle, *fstab_entry)) {\n                return false;\n            }\n            hashtree_result = avb_standalone_handle->SetUpAvbHashtree(\n                    fstab_entry, false /* wait_for_verity_dev */);\n        }\n    } else if (fstab_entry->fs_mgr_flags.avb) {\n        if (!InitAvbHandle()) return false;\n        hashtree_result =\n                avb_handle_->SetUpAvbHashtree(fstab_entry, false /* wait_for_verity_dev */);\n    } else if (!fstab_entry->avb_hashtree_digest.empty()) {\n        // When fstab_entry has neither avb_keys nor avb flag, try using\n        // avb_hashtree_digest.\n        if (!InitAvbHandle()) return false;\n        // Checks if hashtree should be disabled from the top-level /vbmeta.\n        if (IsHashtreeDisabled(*avb_handle_, fstab_entry->mount_point)) {\n            return true;\n        }\n        auto avb_standalone_handle = AvbHandle::LoadAndVerifyVbmeta(*fstab_entry);\n        if (!avb_standalone_handle) {\n            LOG(ERROR) << \"Failed to load vbmeta based on hashtree descriptor root digest for \"\n                       << fstab_entry->mount_point;\n            return false;\n        }\n        hashtree_result = avb_standalone_handle->SetUpAvbHashtree(fstab_entry,\n                                                                  false /* wait_for_verity_dev */);\n    } else {\n        return true;  // No need AVB, returns true to mount the partition directly.\n    }\n\n    switch (hashtree_result) {\n        case AvbHashtreeResult::kDisabled:\n            return true;  // Returns true to mount the partition.\n        case AvbHashtreeResult::kSuccess:\n            // The exact block device name (fstab_rec->blk_device) is changed to\n            // \"/dev/block/dm-XX\". Needs to create it because ueventd isn't started in init\n            // first stage.\n            return block_dev_init_.InitDmDevice(fstab_entry->blk_device);\n        default:\n            return false;\n    }\n}\n\nbool FirstStageMountVBootV2::InitAvbHandle() {\n    if (avb_handle_) return true;  // Returns true if the handle is already initialized.\n\n    avb_handle_ = AvbHandle::Open();\n\n    if (!avb_handle_) {\n        PLOG(ERROR) << \"Failed to open AvbHandle\";\n        return false;\n    }\n    // Sets INIT_AVB_VERSION here for init to set ro.boot.avb_version in the second stage.\n    setenv(\"INIT_AVB_VERSION\", avb_handle_->avb_version().c_str(), 1);\n    return true;\n}\n\nvoid FirstStageMountVBootV2::RequestTradeInModeWipeIfNeeded() {\n    static constexpr const char* kWipeIndicator = \"/metadata/tradeinmode/wipe\";\n    static constexpr size_t kWipeAttempts = 3;\n\n    if (access(kWipeIndicator, R_OK) == -1) {\n        return;\n    }\n\n    // Write a counter to the wipe indicator, to try and prevent boot loops if\n    // recovery fails to wipe data.\n    uint32_t counter = 0;\n    std::string contents;\n    if (ReadFileToString(kWipeIndicator, &contents)) {\n        android::base::ParseUint(contents, &counter);\n        contents = std::to_string(++counter);\n        if (android::base::WriteStringToFile(contents, kWipeIndicator)) {\n            sync();\n        } else {\n            PLOG(ERROR) << \"Failed to update \" << kWipeIndicator;\n        }\n    } else {\n        PLOG(ERROR) << \"Failed to read \" << kWipeIndicator;\n    }\n\n    std::string err;\n    auto misc_device = get_misc_blk_device(&err);\n    if (misc_device.empty()) {\n        LOG(FATAL) << \"Could not find misc device: \" << err;\n    }\n\n    auto misc_name = android::base::Basename(misc_device);\n    if (!block_dev_init_.InitDevices({misc_name})) {\n        LOG(FATAL) << \"Could not find misc device: \" << misc_device;\n    }\n\n    // If we've failed to wipe three times, don't include the wipe command. This\n    // will force us to boot into the recovery menu instead where a manual wipe\n    // can be attempted.\n    std::vector<std::string> options;\n    if (counter <= kWipeAttempts) {\n        options.emplace_back(\"--wipe_data\");\n        options.emplace_back(\"--reason=tradeinmode\");\n    }\n    if (!write_bootloader_message(options, &err)) {\n        LOG(FATAL) << \"Could not issue wipe: \" << err;\n    }\n    RebootSystem(ANDROID_RB_RESTART2, \"recovery\", \"reboot,tradeinmode,wipe\");\n}\n\nvoid SetInitAvbVersionInRecovery() {\n    if (!IsRecoveryMode()) {\n        LOG(INFO) << \"Skipped setting INIT_AVB_VERSION (not in recovery mode)\";\n        return;\n    }\n\n    auto fstab = ReadFirstStageFstabAndroid();\n    if (!fstab.ok()) {\n        LOG(ERROR) << fstab.error();\n        return;\n    }\n\n    if (!IsDtVbmetaCompatible(*fstab)) {\n        LOG(INFO) << \"Skipped setting INIT_AVB_VERSION (not vbmeta compatible)\";\n        return;\n    }\n\n    // Initializes required devices for the subsequent AvbHandle::Open()\n    // to verify AVB metadata on all partitions in the verified chain.\n    // We only set INIT_AVB_VERSION when the AVB verification succeeds, i.e., the\n    // Open() function returns a valid handle.\n    // We don't need to mount partitions here in recovery mode.\n    FirstStageMountVBootV2 avb_first_mount(std::move(*fstab));\n    if (!avb_first_mount.InitDevices()) {\n        LOG(ERROR) << \"Failed to init devices for INIT_AVB_VERSION\";\n        return;\n    }\n\n    AvbUniquePtr avb_handle = AvbHandle::Open();\n    if (!avb_handle) {\n        PLOG(ERROR) << \"Failed to open AvbHandle for INIT_AVB_VERSION\";\n        return;\n    }\n    setenv(\"INIT_AVB_VERSION\", avb_handle->avb_version().c_str(), 1);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/first_stage_mount.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nclass FirstStageMount {\n  public:\n    virtual ~FirstStageMount() = default;\n\n    // The factory method to create a FirstStageMount instance.\n    static Result<std::unique_ptr<FirstStageMount>> Create(const std::string& cmdline);\n    // Creates devices and logical partitions from storage devices\n    virtual bool DoCreateDevices() = 0;\n    // Mounts fstab entries read from device tree.\n    virtual bool DoFirstStageMount() = 0;\n\n  protected:\n    FirstStageMount() = default;\n};\n\nvoid SetInitAvbVersionInRecovery();\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/fscrypt_init_extensions.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"fscrypt_init_extensions.h\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <fts.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <cutils/properties.h>\n#include <cutils/sockets.h>\n#include <fscrypt/fscrypt.h>\n#include <logwrap/logwrap.h>\n\n#define TAG \"fscrypt\"\n\nusing namespace android::fscrypt;\n\n// TODO(b/139378601): use a single central implementation of this.\nstatic void delete_dir_contents(const std::string& dir) {\n    char* const paths[2] = {const_cast<char*>(dir.c_str()), nullptr};\n    FTS* fts = fts_open(paths, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);\n    FTSENT* cur;\n    while ((cur = fts_read(fts)) != nullptr) {\n        if (cur->fts_info == FTS_ERR) {\n            PLOG(ERROR) << \"fts_read\";\n            break;\n        }\n        if (dir == cur->fts_path) {\n            continue;\n        }\n        switch (cur->fts_info) {\n            case FTS_D:\n                break;  // Ignore these\n            case FTS_DP:\n                if (rmdir(cur->fts_path) == -1) {\n                    PLOG(ERROR) << \"rmdir \" << cur->fts_path;\n                }\n                break;\n            default:\n                PLOG(ERROR) << \"FTS unexpected type \" << cur->fts_info << \" at \" << cur->fts_path;\n                if (rmdir(cur->fts_path) != -1) break;\n                // FALLTHRU (for gcc, lint, pcc, etc; and following for clang)\n                FALLTHROUGH_INTENDED;\n            case FTS_F:\n            case FTS_SL:\n            case FTS_SLNONE:\n                if (unlink(cur->fts_path) == -1) {\n                    PLOG(ERROR) << \"unlink \" << cur->fts_path;\n                }\n                break;\n        }\n    }\n\n    if (fts_close(fts) != 0) {\n        PLOG(ERROR) << \"fts_close\";\n    }\n}\n\n// Look up an encryption policy  The policy (key reference\n// and encryption options) to use is read from files that were written by vold.\nstatic bool LookupPolicy(const std::string& ref_basename, EncryptionPolicy* policy) {\n    std::string ref_filename = std::string(\"/data\") + ref_basename;\n    if (!android::base::ReadFileToString(ref_filename, &policy->key_raw_ref)) {\n        LOG(ERROR) << \"Unable to read system policy with name \" << ref_filename;\n        return false;\n    }\n\n    auto options_filename = std::string(\"/data\") + fscrypt_key_mode;\n    std::string options_string;\n    if (!android::base::ReadFileToString(options_filename, &options_string)) {\n        LOG(ERROR) << \"Cannot read encryption options string\";\n        return false;\n    }\n    if (!ParseOptions(options_string, &policy->options)) {\n        LOG(ERROR) << \"Invalid encryption options string: \" << options_string;\n        return false;\n    }\n    return true;\n}\n\nstatic bool EnsurePolicyOrLog(const EncryptionPolicy& policy, const std::string& dir) {\n    if (!EnsurePolicy(policy, dir)) {\n        std::string ref_hex;\n        BytesToHex(policy.key_raw_ref, &ref_hex);\n        LOG(ERROR) << \"Setting \" << ref_hex << \" policy on \" << dir << \" failed!\";\n        return false;\n    }\n    return true;\n}\n\nstatic bool SetPolicyOn(const std::string& ref_basename, const std::string& dir) {\n    EncryptionPolicy policy;\n    if (!LookupPolicy(ref_basename, &policy)) return false;\n    if (!EnsurePolicyOrLog(policy, dir)) return false;\n    return true;\n}\n\nbool FscryptSetDirectoryPolicy(const std::string& ref_basename, FscryptAction action,\n                               const std::string& dir) {\n    if (action == FscryptAction::kNone) {\n        return true;\n    }\n    if (SetPolicyOn(ref_basename, dir) || action == FscryptAction::kAttempt) {\n        return true;\n    }\n    if (action == FscryptAction::kDeleteIfNecessary) {\n        LOG(ERROR) << \"Setting policy failed, deleting: \" << dir;\n        delete_dir_contents(dir);\n        return SetPolicyOn(ref_basename, dir);\n    }\n    return false;\n}\n"
  },
  {
    "path": "init/fscrypt_init_extensions.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\nenum class FscryptAction {\n    kNone,\n    kAttempt,\n    kRequire,\n    kDeleteIfNecessary,\n};\n\nbool FscryptSetDirectoryPolicy(const std::string& ref_basename, FscryptAction action,\n                               const std::string& dir);\n"
  },
  {
    "path": "init/fuzzer/Android.bp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage {\n    default_applicable_licenses: [\"system_core_init_license\"],\n}\n\ncc_defaults {\n    name: \"libinit_fuzzer_defaults\",\n    static_libs: [\n        \"liblmkd_utils\",\n        \"libmodprobe\",\n        \"libprotobuf-cpp-lite\",\n        \"libpropertyinfoparser\",\n        \"libsnapshot_init\",\n        \"libinit\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libfs_mgr\",\n        \"liblog\",\n        \"libprocessgroup\",\n        \"libselinux\",\n    ],\n    header_libs: [\"libinit_headers\"],\n    fuzz_config: {\n        cc: [\n            \"android-media-fuzzing-reports@google.com\",\n        ],\n        componentid: 155276,\n    },\n}\n\ncc_fuzz {\n    name: \"init_parser_fuzzer\",\n    srcs: [\n        \"init_parser_fuzzer.cpp\",\n    ],\n    defaults: [\n        \"libinit_fuzzer_defaults\",\n    ],\n}\n\ncc_fuzz {\n    name: \"init_property_fuzzer\",\n    srcs: [\n        \"init_property_fuzzer.cpp\",\n    ],\n    defaults: [\"libinit_fuzzer_defaults\"],\n}\n\ncc_fuzz {\n    name: \"init_ueventHandler_fuzzer\",\n    srcs: [\n        \"init_ueventHandler_fuzzer.cpp\",\n    ],\n    defaults: [\n        \"libinit_fuzzer_defaults\",\n    ],\n}\n"
  },
  {
    "path": "init/fuzzer/README.md",
    "content": "# Fuzzers for libinit\n\n## Table of contents\n+ [init_parser_fuzzer](#InitParser)\n+ [init_property_fuzzer](#InitProperty)\n+ [init_ueventHandler_fuzzer](#InitUeventHandler)\n\n# <a name=\"InitParser\"></a> Fuzzer for InitParser\n\nInitParser supports the following parameters:\n1. ValidPathNames (parameter name: \"kValidPaths\")\n2. ValidParseInputs (parameter name: \"kValidInputs\")\n\n| Parameter| Valid Values| Configured Value|\n|------------- |-------------| ----- |\n|`kValidPaths`| 0.`/system/etc/init/hw/init.rc`,<br/> 1.`/system/etc/init` |Value obtained from FuzzedDataProvider|\n|`kValidInputs`| 0.`{\"\",\"cpu\", \"10\", \"10\"}`,<br/> 1.`{\"\",\"RLIM_CPU\", \"10\", \"10\"}`,<br/> 2.`{\"\",\"12\", \"unlimited\", \"10\"}`,<br/> 3.`{\"\",\"13\", \"-1\", \"10\"}`,<br/> 4.`{\"\",\"14\", \"10\", \"unlimited\"}`,<br/> 5.`{\"\",\"15\", \"10\", \"-1\"}` |Value obtained from FuzzedDataProvider|\n\n#### Steps to run\n1. Build the fuzzer\n```\n  $ mm -j$(nproc) init_parser_fuzzer\n```\n2. Run on device\n```\n  $ adb sync data\n  $ adb shell /data/fuzz/arm64/init_parser_fuzzer/init_parser_fuzzer\n```\n\n# <a name=\"InitProperty\"></a> Fuzzer for InitProperty\n\nInitProperty supports the following parameters:\n  PropertyType (parameter name: \"PropertyType\")\n\n| Parameter| Valid Values |Configured Value|\n|-------------|----------|----- |\n|`PropertyType`| 0.`STRING`,<br/> 1.`BOOL`,<br/> 2.`INT`,<br/> 3.`UINT`,<br/> 4.`DOUBLE`,<br/> 5.`SIZE`,<br/>6.`ENUM`,<br/>7.`RANDOM`|Value obtained from FuzzedDataProvider|\n\n#### Steps to run\n1. Build the fuzzer\n```\n  $ mm -j$(nproc) init_property_fuzzer\n```\n2. Run on device\n```\n  $ adb sync data\n  $ adb shell /data/fuzz/arm64/init_property_fuzzer/init_property_fuzzer\n```\n\n# <a name=\"InitUeventHandler\"></a> Fuzzer for InitUeventHandler\n\n##### Maximize code coverage\nThe configuration parameters are not hardcoded, but instead selected based on\nincoming data. This ensures more code paths are reached by the fuzzer.\n\nInitUeventHandler supports the following parameters:\n1. Major (parameter name: `major`)\n2. Minor (parameter name: `minor`)\n3. PartitionNum (parameter name: `partition_num`)\n4. Uid (parameter name: `uid`)\n5. Gid (parameter name: `gid`)\n6. Action (parameter name: `action`)\n7. Path (parameter name: `path`)\n8. Subsystem (parameter name: `subsystem`)\n9. PartitionName (parameter name: `partition_name`)\n10. DeviceName (parameter name: `device_name`)\n11. Modalias (parameter name: `modalias`)\n12. DevPath (parameter name: `devPath`)\n13. HandlerPath (parameter name: `handlerPath`)\n\n| Parameter| Valid Values| Configured Value|\n|------------- |-------------| ----- |\n| `major` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|\n| `minor` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|\n| `partition_num ` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|\n| `uid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|\n| `gid` | `UINT32_MIN` to `UINT32_MAX` | Value obtained from FuzzedDataProvider|\n| `action` | `String` | Value obtained from FuzzedDataProvider|\n| `path` | `String` | Value obtained from FuzzedDataProvider|\n| `subsystem` | `String` | Value obtained from FuzzedDataProvider|\n| `partition_name` | `String` | Value obtained from FuzzedDataProvider|\n| `device_name` | `String` | Value obtained from FuzzedDataProvider|\n| `modalias` | `String` | Value obtained from FuzzedDataProvider|\n| `devPath` | `String` | Value obtained from FuzzedDataProvider|\n| `handlerPath` | `String` | Value obtained from FuzzedDataProvider|\n\nThis also ensures that the plugin is always deterministic for any given input.\n\n#### Steps to run\n1. Build the fuzzer\n```\n$ mm -j$(nproc) init_ueventHandler_fuzzer\n```\n2. Run on device\n```\n$ adb sync data\n$ adb shell /data/fuzz/arm64/init_ueventHandler_fuzzer/init_ueventHandler_fuzzer\n```\n"
  },
  {
    "path": "init/fuzzer/init_parser_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fuzzer/FuzzedDataProvider.h>\n#include <import_parser.h>\n#include <rlimit_parser.h>\n\nusing namespace android;\nusing namespace android::init;\n\nconst std::vector<std::string> kValidInputs[] = {\n        {\"\", \"cpu\", \"10\", \"10\"}, {\"\", \"RLIM_CPU\", \"10\", \"10\"},  {\"\", \"12\", \"unlimited\", \"10\"},\n        {\"\", \"13\", \"-1\", \"10\"},  {\"\", \"14\", \"10\", \"unlimited\"}, {\"\", \"15\", \"10\", \"-1\"},\n};\n\nconst std::string kValidPaths[] = {\n        \"/system/etc/init/hw/init.rc\",\n        \"/system/etc/init\",\n};\n\nconst int32_t kMaxBytes = 256;\n\nclass InitParserFuzzer {\n  public:\n    InitParserFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};\n    void Process();\n\n  private:\n    void InvokeParser();\n    void InvokeLimitParser();\n\n    FuzzedDataProvider fdp_;\n};\n\nvoid InitParserFuzzer::InvokeLimitParser() {\n    if (fdp_.ConsumeBool()) {\n        std::vector<std::string> input;\n        input.push_back(\"\");\n        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));\n        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));\n        input.push_back(fdp_.ConsumeRandomLengthString(kMaxBytes));\n        ParseRlimit(input);\n    } else {\n        ParseRlimit(fdp_.PickValueInArray(kValidInputs));\n    }\n}\n\nvoid InitParserFuzzer::InvokeParser() {\n    Parser parser;\n    std::string name = fdp_.ConsumeBool() ? fdp_.ConsumeRandomLengthString(kMaxBytes) : \"import\";\n    parser.AddSectionParser(name, std::make_unique<ImportParser>(&parser));\n    std::string path = fdp_.ConsumeBool() ? fdp_.PickValueInArray(kValidPaths)\n                                          : fdp_.ConsumeRandomLengthString(kMaxBytes);\n    parser.ParseConfig(path);\n    parser.ParseConfigFileInsecure(path, false /* follow_symlinks */);\n}\n\nvoid InitParserFuzzer::Process() {\n    while (fdp_.remaining_bytes()) {\n        auto invoke_parser_fuzzer = fdp_.PickValueInArray<const std::function<void()>>({\n                [&]() { InvokeParser(); },\n                [&]() { InvokeLimitParser(); },\n        });\n        invoke_parser_fuzzer();\n    }\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    InitParserFuzzer init_parser_fuzzer(data, size);\n    init_parser_fuzzer.Process();\n    return 0;\n}\n"
  },
  {
    "path": "init/fuzzer/init_property_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <persistent_properties.h>\n#include <property_type.h>\n#include <sys/stat.h>\n#include <fstream>\n#include \"fuzzer/FuzzedDataProvider.h\"\n\nusing namespace android;\nusing namespace android::init;\nusing android::init::persistent_property_filename;\n\nconst std::string kTempDir = \"/data/local/tmp/\";\nconst std::string kFuzzerPropertyFile = kTempDir + \"persistent_properties\";\nconstexpr int32_t kMaxPropertyLength = 10;\nconst std::string kPrefix = \"persist.\";\nconst std::string kPropertyName = kPrefix + \"sys.timezone\";\nconst std::string kPropertyValue = \"America/Los_Angeles\";\nconst std::string kLegacyPropertyFile = \"/data/property/persist.properties\";\nconst std::string kSizeSuffix[3] = {\"g\", \"k\", \"m\"};\nconstexpr int32_t kMinNumStrings = 1;\nconstexpr int32_t kMaxNumStrings = 10;\n\nenum PropertyType { STRING, BOOL, INT, UINT, DOUBLE, SIZE, ENUM, RANDOM, kMaxValue = RANDOM };\n\nclass InitPropertyFuzzer {\n  public:\n    InitPropertyFuzzer(const uint8_t* data, size_t size) : fdp_(data, size){};\n    void process();\n\n  private:\n    void InvokeCheckType();\n    void InvokeWritePersistentProperty();\n    void RemoveFiles();\n    void CreateFuzzerPropertyFile(const std::string property_file);\n    FuzzedDataProvider fdp_;\n};\n\nvoid InitPropertyFuzzer::InvokeCheckType() {\n    std::string property_type;\n    std::string value;\n    int type = fdp_.ConsumeEnum<PropertyType>();\n    switch (type) {\n        case STRING:\n            value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);\n            property_type = \"string\";\n            break;\n        case BOOL:\n            value = fdp_.ConsumeBool();\n            property_type = \"bool\";\n            break;\n        case INT:\n            value = fdp_.ConsumeIntegral<int>();\n            property_type = \"int\";\n            break;\n        case UINT:\n            value = fdp_.ConsumeIntegral<uint_t>();\n            property_type = \"uint\";\n            break;\n        case DOUBLE:\n            value = fdp_.ConsumeFloatingPoint<double>();\n            property_type = \"double\";\n            break;\n        case SIZE:\n            value = fdp_.ConsumeIntegral<uint_t>();\n            value = value.append(fdp_.PickValueInArray(kSizeSuffix));\n            property_type = \"size\";\n            break;\n        case ENUM:\n            value = fdp_.ConsumeIntegral<uint_t>();\n            property_type = \"enum\";\n            break;\n        case RANDOM:\n            value = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);\n            property_type = fdp_.ConsumeRandomLengthString(kMaxPropertyLength);\n            break;\n    }\n\n    CheckType(property_type, value);\n}\n\nvoid InitPropertyFuzzer::InvokeWritePersistentProperty() {\n    if (fdp_.ConsumeBool()) {\n        WritePersistentProperty(kPropertyName, kPropertyValue);\n    } else {\n        WritePersistentProperty((kPrefix + fdp_.ConsumeRandomLengthString(kMaxPropertyLength)),\n                                fdp_.ConsumeRandomLengthString(kMaxPropertyLength));\n    }\n}\n\nvoid InitPropertyFuzzer::RemoveFiles() {\n    remove(kFuzzerPropertyFile.c_str());\n    remove(kLegacyPropertyFile.c_str());\n}\n\nvoid InitPropertyFuzzer::CreateFuzzerPropertyFile(const std::string property_file) {\n    std::ofstream out;\n    out.open(property_file, std::ios::binary | std::ofstream::trunc);\n    chmod(property_file.c_str(), S_IRWXU);\n    const int32_t numStrings = fdp_.ConsumeIntegralInRange(kMinNumStrings, kMaxNumStrings);\n    for (int32_t i = 0; i < numStrings; ++i) {\n        out << fdp_.ConsumeRandomLengthString(kMaxPropertyLength) << \"\\n\";\n    }\n    out.close();\n}\n\nvoid InitPropertyFuzzer::process() {\n    persistent_property_filename = kFuzzerPropertyFile;\n    /* Property and legacy files are created using createFuzzerPropertyFile() and */\n    /* are used in the below APIs. Hence createFuzzerPropertyFile() is not a part */\n    /* of the lambda construct. */\n    CreateFuzzerPropertyFile(kFuzzerPropertyFile);\n    CreateFuzzerPropertyFile(kLegacyPropertyFile);\n    auto property_type = fdp_.PickValueInArray<const std::function<void()>>({\n            [&]() { InvokeCheckType(); },\n            [&]() { InvokeWritePersistentProperty(); },\n            [&]() { LoadPersistentProperties(); },\n    });\n    property_type();\n    RemoveFiles();\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    InitPropertyFuzzer initPropertyFuzzer(data, size);\n    initPropertyFuzzer.process();\n    return 0;\n}\n"
  },
  {
    "path": "init/fuzzer/init_ueventHandler_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <devices.h>\n#include <firmware_handler.h>\n#include <fuzzer/FuzzedDataProvider.h>\n#include <modalias_handler.h>\n#include <sys/stat.h>\n#include <util.h>\n#include <fstream>\n\nusing namespace android;\nusing namespace android::init;\nconstexpr int32_t kMaxBytes = 100;\nconstexpr int32_t kMaxSize = 1000;\nconstexpr int32_t kMinSize = 1;\n\n/*'HandleUevent' prefixes the path with '/sys' and hence this is required to point\n * to'/data/local/tmp' dir.*/\nconst std::string kPath = \"/../data/local/tmp/\";\nconst std::string kPathPrefix = \"/..\";\n\nvoid MakeFile(FuzzedDataProvider* fdp, std::string s) {\n    std::ofstream out;\n    out.open(s, std::ios::binary | std::ofstream::trunc);\n    for (int32_t idx = 0; idx < fdp->ConsumeIntegralInRange(kMinSize, kMaxSize); ++idx) {\n        out << fdp->ConsumeRandomLengthString(kMaxBytes) << \"\\n\";\n    }\n    out.close();\n}\n\nvoid CreateDir(std::string Directory, FuzzedDataProvider* fdp) {\n    std::string tmp = Directory.substr(kPathPrefix.length());\n    mkdir_recursive(android::base::Dirname(tmp.c_str()),\n                    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);\n    MakeFile(fdp, tmp + \"/data\");\n    MakeFile(fdp, tmp + \"/loading\");\n}\n\nstd::string SelectRandomString(FuzzedDataProvider* fdp, std::string s) {\n    if (fdp->ConsumeBool()) {\n        if (fdp->ConsumeBool()) {\n            return fdp->ConsumeRandomLengthString(kMaxBytes);\n        } else {\n            return s;\n        }\n    }\n    return \"\";\n}\n\nUevent CreateUevent(FuzzedDataProvider* fdp) {\n    Uevent uevent;\n    uevent.action = SelectRandomString(fdp, \"add\");\n    uevent.subsystem = SelectRandomString(fdp, \"firmware\");\n    uevent.path = SelectRandomString(fdp, kPath + fdp->ConsumeRandomLengthString(kMaxBytes));\n    uevent.firmware = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : \"\";\n    uevent.partition_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : \"\";\n    uevent.device_name = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : \"\";\n    uevent.modalias = fdp->ConsumeBool() ? fdp->ConsumeRandomLengthString(kMaxBytes) : \"\";\n    uevent.partition_num = fdp->ConsumeIntegral<int32_t>();\n    uevent.major = fdp->ConsumeIntegral<int32_t>();\n    uevent.minor = fdp->ConsumeIntegral<int32_t>();\n    return uevent;\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider fdp(data, size);\n    while (fdp.remaining_bytes()) {\n        auto invoke_uevent_handler_fuzzer = fdp.PickValueInArray<const std::function<void()>>({\n                [&]() {\n                    std::vector<std::string> modalias_vector;\n                    for (size_t idx = 0;\n                         idx < fdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize); ++idx) {\n                        modalias_vector.push_back(fdp.ConsumeRandomLengthString(kMaxBytes));\n                    }\n                    ModaliasHandler modalias_handler = ModaliasHandler(modalias_vector);\n                    modalias_handler.HandleUevent(CreateUevent(&fdp));\n                },\n                [&]() {\n                    std::vector<ExternalFirmwareHandler> external_handlers;\n                    std::vector<std::string> firmware_directories;\n                    for (size_t idx = 0;\n                         idx < fdp.ConsumeIntegralInRange<size_t>(kMinSize, kMaxSize); ++idx) {\n                        std::string devPath = fdp.ConsumeRandomLengthString(kMaxBytes);\n                        uid_t uid = fdp.ConsumeIntegral<uid_t>();\n                        gid_t gid = fdp.ConsumeIntegral<gid_t>();\n                        std::string handlerPath = fdp.ConsumeRandomLengthString(kMaxBytes);\n                        ExternalFirmwareHandler externalFirmwareHandler =\n                                ExternalFirmwareHandler(devPath, uid, gid, handlerPath);\n                        external_handlers.push_back(externalFirmwareHandler);\n                        firmware_directories.push_back(fdp.ConsumeRandomLengthString(kMaxBytes));\n                    }\n                    FirmwareHandler firmware_handler =\n                            FirmwareHandler(firmware_directories, external_handlers);\n                    Uevent uevent = CreateUevent(&fdp);\n                    if (fdp.ConsumeBool() && uevent.path.size() != 0 &&\n                        uevent.path.find(kPath) == 0) {\n                        CreateDir(uevent.path, &fdp);\n                        firmware_handler.HandleUevent(uevent);\n                        std::string s = uevent.path.substr(kPathPrefix.length());\n                        remove(s.c_str());\n                    } else {\n                        firmware_handler.HandleUevent(uevent);\n                    }\n                },\n        });\n        invoke_uevent_handler_fuzzer();\n    }\n    return 0;\n}\n"
  },
  {
    "path": "init/grab-bootchart.sh",
    "content": "#!/bin/sh\n#\n# This script is used to retrieve a bootchart log generated by init.\n# All options are passed to adb, for better or for worse.\n# See the readme in this directory for more on bootcharting.\n\nTMPDIR=/tmp/android-bootchart\nrm -rf $TMPDIR\nmkdir -p $TMPDIR\n\nLOGROOT=/data/bootchart\nTARBALL=bootchart.tgz\n\nFILES=\"header proc_stat.log proc_ps.log proc_diskstats.log\"\n\nfor f in $FILES; do\n    adb \"${@}\" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null\ndone\n(cd $TMPDIR && tar -czf $TARBALL $FILES)\npybootchartgui ${TMPDIR}/${TARBALL}\nxdg-open ${TARBALL%.tgz}.png\necho \"Clean up ${TMPDIR}/ and ./${TARBALL%.tgz}.png when done\"\n"
  },
  {
    "path": "init/host_builtin_map.py",
    "content": "#!/usr/bin/env python3\n\"\"\"Generates the builtins map to be used by host_init_verifier.\n\nIt copies the builtin function map from builtins.cpp, then replaces do_xxx() functions with the\nequivalent check_xxx() if found in check_builtins.cpp.\n\n\"\"\"\n\nimport re\nimport argparse\n\nparser = argparse.ArgumentParser('host_builtin_map.py')\nparser.add_argument('--builtins', required=True, help='Path to builtins.cpp')\nparser.add_argument('--check_builtins', required=True, help='Path to check_builtins.cpp')\nargs = parser.parse_args()\n\nCHECK_REGEX = re.compile(r'.+check_(\\S+)\\(.+')\ncheck_functions = []\nwith open(args.check_builtins) as check_file:\n  for line in check_file:\n    match = CHECK_REGEX.match(line)\n    if match:\n      check_functions.append(match.group(1))\n\nfunction_map = []\nwith open(args.builtins) as builtins_file:\n  in_function_map = False\n  for line in builtins_file:\n    if '// Builtin-function-map start' in line:\n      in_function_map = True\n    elif '// Builtin-function-map end' in line:\n      in_function_map = False\n    elif in_function_map:\n      function_map.append(line)\n\nDO_REGEX = re.compile(r'.+do_([^\\}]+).+')\nFUNCTION_REGEX = re.compile(r'(do_[^\\}]+)')\nfor line in function_map:\n  match = DO_REGEX.match(line)\n  if match:\n    if match.group(1) in check_functions:\n      line = line.replace('do_', 'check_')\n    else:\n      line = FUNCTION_REGEX.sub('check_stub', line)\n  print(line, end=' ')\n"
  },
  {
    "path": "init/host_import_parser.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"host_import_parser.h\"\n\n#include <android-base/strings.h>\n\nusing android::base::StartsWith;\n\nnamespace android {\nnamespace init {\n\nResult<void> HostImportParser::ParseSection(std::vector<std::string>&& args, const std::string&,\n                                            int) {\n    if (args.size() != 2) {\n        return Error() << \"single argument needed for import\\n\";\n    }\n\n    return {};\n}\n\nResult<void> HostImportParser::ParseLineSection(std::vector<std::string>&&, int) {\n    return Error() << \"Unexpected line found after import statement\";\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/host_import_parser.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"parser.h\"\n\nnamespace android {\nnamespace init {\n\nclass HostImportParser : public SectionParser {\n  public:\n    HostImportParser() {}\n    Result<void> ParseSection(std::vector<std::string>&& args, const std::string&, int) override;\n    Result<void> ParseLineSection(std::vector<std::string>&&, int) override;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/host_init_stubs.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stddef.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n\n#include <optional>\n#include <string>\n\n#include <android-base/properties.h>\n\n// android/api-level.h\n#define __ANDROID_API_P__ 28\n#define __ANDROID_API_Q__ 29\n#define __ANDROID_API_R__ 30\n#define __ANDROID_API_S__ 31\n#define __ANDROID_API_T__ 33\n#define __ANDROID_API_U__ 34\n#define __ANDROID_API_V__ 35\n\n// sys/system_properties.h\n#define PROP_VALUE_MAX 92\n\nnamespace android {\nnamespace init {\n\n// property_service.h\ninline bool CanReadProperty(const std::string&, const std::string&) {\n    return true;\n}\n\n// reboot_utils.h\ninline void SetFatalRebootTarget(const std::optional<std::string>& = std::nullopt) {}\ninline void __attribute__((noreturn)) InitFatalReboot(int signal_number) {\n    abort();\n}\n\n// selabel.h\ninline void SelabelInitialize() {}\ninline bool SelabelLookupFileContext(const std::string&, int, std::string*) {\n    return false;\n}\n\n// selinux.h\ninline int SelinuxGetVendorAndroidVersion() {\n    return 10000;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/host_init_verifier.cpp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <errno.h>\n#include <getopt.h>\n#include <pwd.h>\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <fstream>\n#include <iostream>\n#include <iterator>\n#include <map>\n#include <set>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <generated_android_ids.h>\n#include <hidl/metadata.h>\n#include <property_info_parser/property_info_parser.h>\n#include <property_info_serializer/property_info_serializer.h>\n\n#include \"action.h\"\n#include \"action_manager.h\"\n#include \"action_parser.h\"\n#include \"check_builtins.h\"\n#include \"host_import_parser.h\"\n#include \"host_init_stubs.h\"\n#include \"interface_utils.h\"\n#include \"parser.h\"\n#include \"result.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n#include \"service_parser.h\"\n\nusing namespace std::literals;\n\nusing android::base::EndsWith;\nusing android::base::ParseInt;\nusing android::base::ReadFileToString;\nusing android::base::Split;\nusing android::properties::ParsePropertyInfoFile;\nusing android::properties::PropertyInfoEntry;\n\nstatic std::vector<std::string> passwd_files;\n\n// NOTE: Keep this in sync with the order used by init.cpp LoadBootScripts()\nstatic const std::vector<std::string> partition_search_order =\n        std::vector<std::string>({\"system\", \"system_ext\", \"odm\", \"vendor\", \"product\"});\n\nstatic std::vector<std::pair<std::string, int>> GetVendorPasswd(const std::string& passwd_file) {\n    std::string passwd;\n    if (!ReadFileToString(passwd_file, &passwd)) {\n        return {};\n    }\n\n    std::vector<std::pair<std::string, int>> result;\n    auto passwd_lines = Split(passwd, \"\\n\");\n    for (const auto& line : passwd_lines) {\n        auto split_line = Split(line, \":\");\n        if (split_line.size() < 3) {\n            continue;\n        }\n        int uid = 0;\n        if (!ParseInt(split_line[2], &uid)) {\n            continue;\n        }\n        result.emplace_back(split_line[0], uid);\n    }\n    return result;\n}\n\nstatic std::vector<std::pair<std::string, int>> GetVendorPasswd() {\n    std::vector<std::pair<std::string, int>> result;\n    for (const auto& passwd_file : passwd_files) {\n        auto individual_result = GetVendorPasswd(passwd_file);\n        std::move(individual_result.begin(), individual_result.end(),\n                  std::back_insert_iterator(result));\n    }\n    return result;\n}\n\npasswd* getpwnam(const char* login) {  // NOLINT: implementing bad function.\n    // This isn't thread safe, but that's okay for our purposes.\n    static char static_name[32] = \"\";\n    static char static_dir[32] = \"/\";\n    static char static_shell[32] = \"/system/bin/sh\";\n    static passwd static_passwd = {\n        .pw_name = static_name,\n        .pw_dir = static_dir,\n        .pw_uid = 0,\n        .pw_gid = 0,\n        .pw_shell = static_shell,\n    };\n\n    for (size_t n = 0; n < android_id_count; ++n) {\n        if (!strcmp(android_ids[n].name, login)) {\n            snprintf(static_name, sizeof(static_name), \"%s\", android_ids[n].name);\n            static_passwd.pw_uid = android_ids[n].aid;\n            static_passwd.pw_gid = android_ids[n].aid;\n            return &static_passwd;\n        }\n    }\n\n    static const auto vendor_passwd = GetVendorPasswd();\n\n    for (const auto& [name, uid] : vendor_passwd) {\n        if (name == login) {\n            snprintf(static_name, sizeof(static_name), \"%s\", name.c_str());\n            static_passwd.pw_uid = uid;\n            static_passwd.pw_gid = uid;\n            return &static_passwd;\n        }\n    }\n\n    unsigned int oem_uid;\n    if (sscanf(login, \"oem_%u\", &oem_uid) == 1) {\n        snprintf(static_name, sizeof(static_name), \"%s\", login);\n        static_passwd.pw_uid = oem_uid;\n        static_passwd.pw_gid = oem_uid;\n        return &static_passwd;\n    }\n\n    errno = ENOENT;\n    return nullptr;\n}\n\nnamespace android {\nnamespace init {\n\nvoid PrintUsage() {\n    fprintf(stdout, R\"(usage: host_init_verifier [options]\n\nTests init script(s) for correctness.\n\nGeneric options:\n  -p FILE                     Search this passwd file for users and groups.\n  --property_contexts=FILE    Use this file for property_contexts.\n\nSingle script mode options:\n  [init rc file]              Positional argument; test this init script.\n\nMultiple script mode options:\n  --out_system=DIR            Path to the output product directory for the system partition.\n  --out_system_ext=DIR        Path to the output product directory for the system_ext partition.\n  --out_odm=DIR               Path to the output product directory for the odm partition.\n  --out_vendor=DIR            Path to the output product directory for the vendor partition.\n  --out_product=DIR           Path to the output product directory for the product partition.\n)\");\n}\n\nResult<InterfaceInheritanceHierarchyMap> ReadInterfaceInheritanceHierarchy() {\n    InterfaceInheritanceHierarchyMap result;\n    for (const HidlInterfaceMetadata& iface : HidlInterfaceMetadata::all()) {\n        std::set<FQName> inherited_interfaces;\n        for (const std::string& intf : iface.inherited) {\n            FQName fqname;\n            if (!fqname.setTo(intf)) {\n                return Error() << \"Unable to parse interface '\" << intf << \"'\";\n            }\n            inherited_interfaces.insert(fqname);\n        }\n        FQName fqname;\n        if (!fqname.setTo(iface.name)) {\n            return Error() << \"Unable to parse interface '\" << iface.name << \"'\";\n        }\n        result[fqname] = inherited_interfaces;\n    }\n\n    return result;\n}\n\nvoid HandlePropertyContexts(const std::string& filename,\n                            std::vector<PropertyInfoEntry>* property_infos) {\n    auto file_contents = std::string();\n    if (!ReadFileToString(filename, &file_contents)) {\n        PLOG(ERROR) << \"Could not read properties from '\" << filename << \"'\";\n        exit(EXIT_FAILURE);\n    }\n\n    auto errors = std::vector<std::string>{};\n    ParsePropertyInfoFile(file_contents, true, property_infos, &errors);\n    for (const auto& error : errors) {\n        LOG(ERROR) << \"Could not read line from '\" << filename << \"': \" << error;\n    }\n    if (!errors.empty()) {\n        exit(EXIT_FAILURE);\n    }\n}\n\nint main(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::StdioLogger);\n    android::base::SetMinimumLogSeverity(android::base::ERROR);\n\n    auto property_infos = std::vector<PropertyInfoEntry>();\n    std::map<std::string, std::string> partition_map;\n\n    while (true) {\n        static const char kPropertyContexts[] = \"property-contexts=\";\n        static const struct option long_options[] = {\n                {\"help\", no_argument, nullptr, 'h'},\n                {kPropertyContexts, required_argument, nullptr, 0},\n                {\"out_system\", required_argument, nullptr, 0},\n                {\"out_system_ext\", required_argument, nullptr, 0},\n                {\"out_odm\", required_argument, nullptr, 0},\n                {\"out_vendor\", required_argument, nullptr, 0},\n                {\"out_product\", required_argument, nullptr, 0},\n                {nullptr, 0, nullptr, 0},\n        };\n\n        int option_index;\n        int arg = getopt_long(argc, argv, \"p:\", long_options, &option_index);\n\n        if (arg == -1) {\n            break;\n        }\n\n        switch (arg) {\n            case 0:\n                if (long_options[option_index].name == kPropertyContexts) {\n                    HandlePropertyContexts(optarg, &property_infos);\n                }\n                for (const auto& p : partition_search_order) {\n                    if (long_options[option_index].name == \"out_\" + p) {\n                        if (partition_map.find(p) != partition_map.end()) {\n                            PrintUsage();\n                            return EXIT_FAILURE;\n                        }\n                        partition_map[p] =\n                                EndsWith(optarg, \"/\") ? optarg : std::string(optarg) + \"/\";\n                    }\n                }\n                break;\n            case 'h':\n                PrintUsage();\n                return EXIT_FAILURE;\n            case 'p':\n                passwd_files.emplace_back(optarg);\n                break;\n            default:\n                std::cerr << \"getprop: getopt returned invalid result: \" << arg << std::endl;\n                return EXIT_FAILURE;\n        }\n    }\n\n    argc -= optind;\n    argv += optind;\n\n    // If provided, use the partition map to check multiple init rc files.\n    // Otherwise, check a single init rc file.\n    if ((!partition_map.empty() && argc != 0) || (partition_map.empty() && argc != 1)) {\n        PrintUsage();\n        return EXIT_FAILURE;\n    }\n\n    auto interface_inheritance_hierarchy_map = ReadInterfaceInheritanceHierarchy();\n    if (!interface_inheritance_hierarchy_map.ok()) {\n        LOG(ERROR) << interface_inheritance_hierarchy_map.error();\n        return EXIT_FAILURE;\n    }\n    SetKnownInterfaces(*interface_inheritance_hierarchy_map);\n\n    if (auto result = InitializeHostPropertyInfoArea(property_infos); !result.ok()) {\n        LOG(ERROR) << result.error();\n        return EXIT_FAILURE;\n    }\n\n    if (!partition_map.empty()) {\n        std::vector<std::string> vendor_prefixes;\n        for (const auto& partition : {\"vendor\", \"odm\"}) {\n            if (partition_map.find(partition) != partition_map.end()) {\n                vendor_prefixes.push_back(partition_map.at(partition));\n            }\n        }\n        InitializeHostSubcontext(vendor_prefixes);\n    }\n\n    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();\n    Action::set_function_map(&function_map);\n    ActionManager& am = ActionManager::GetInstance();\n    ServiceList& sl = ServiceList::GetInstance();\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&sl, GetSubcontext()));\n    parser.AddSectionParser(\"on\", std::make_unique<ActionParser>(&am, GetSubcontext()));\n    parser.AddSectionParser(\"import\", std::make_unique<HostImportParser>());\n\n    if (!partition_map.empty()) {\n        for (const auto& p : partition_search_order) {\n            if (partition_map.find(p) != partition_map.end()) {\n                parser.ParseConfig(partition_map.at(p) + \"etc/init\");\n            }\n        }\n    } else {\n        if (!parser.ParseConfigFileInsecure(*argv, true /* follow_symlinks */)) {\n          // Follow symlinks as inputs during build execution in Bazel's\n          // execution root are symlinks, unlike Soong or Make.\n            LOG(ERROR) << \"Failed to open init rc script '\" << *argv << \"'\";\n            return EXIT_FAILURE;\n        }\n    }\n\n    size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands();\n    if (failures > 0) {\n        LOG(ERROR) << \"Failed to parse init scripts with \" << failures << \" error(s).\";\n        return EXIT_FAILURE;\n    }\n\n    for (const auto& service : sl) {\n        if (const auto& result = CheckInterfaceInheritanceHierarchy(\n                    service->interfaces(), *interface_inheritance_hierarchy_map);\n            !result.ok()) {\n            LOG(ERROR) << service->filename() << \": invalid interface in service '\"\n                       << service->name() << \"': \" << result.error();\n            return EXIT_FAILURE;\n        }\n    }\n\n    return EXIT_SUCCESS;\n}\n\n}  // namespace init\n}  // namespace android\n\nint main(int argc, char** argv) {\n    return android::init::main(argc, argv);\n}\n"
  },
  {
    "path": "init/import_parser.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"import_parser.h\"\n\n#include <android-base/logging.h>\n\n#include \"util.h\"\n\nnamespace android {\nnamespace init {\n\nResult<void> ImportParser::ParseSection(std::vector<std::string>&& args,\n                                        const std::string& filename, int line) {\n    if (args.size() != 2) {\n        return Error() << \"single argument needed for import\\n\";\n    }\n\n    auto conf_file = ExpandProps(args[1]);\n    if (!conf_file.ok()) {\n        return Error() << \"Could not expand import: \" << conf_file.error();\n    }\n\n    LOG(INFO) << \"Added '\" << *conf_file << \"' to import list\";\n    if (filename_.empty()) filename_ = filename;\n    imports_.emplace_back(std::move(*conf_file), line);\n    return {};\n}\n\nResult<void> ImportParser::ParseLineSection(std::vector<std::string>&&, int) {\n    return Error() << \"Unexpected line found after import statement\";\n}\n\nvoid ImportParser::EndFile() {\n    auto current_imports = std::move(imports_);\n    imports_.clear();\n    for (const auto& [import, line_num] : current_imports) {\n        parser_->ParseConfig(import);\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/import_parser.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_IMPORT_PARSER_H\n#define _INIT_IMPORT_PARSER_H\n\n#include <string>\n#include <vector>\n\n#include \"parser.h\"\n\nnamespace android {\nnamespace init {\n\nclass ImportParser : public SectionParser {\n  public:\n    ImportParser(Parser* parser) : parser_(parser) {}\n    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,\n                              int line) override;\n    Result<void> ParseLineSection(std::vector<std::string>&&, int) override;\n    void EndFile() override;\n\n  private:\n    Parser* parser_;\n    // Store filename for later error reporting.\n    std::string filename_;\n    // Vector of imports and their line numbers for later error reporting.\n    std::vector<std::pair<std::string, int>> imports_;\n};\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/init.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"init.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <paths.h>\n#include <pthread.h>\n#include <signal.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/eventfd.h>\n#include <sys/mount.h>\n#include <sys/signalfd.h>\n#include <sys/system_properties.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n#include <unistd.h>\n\n#include <filesystem>\n#include <fstream>\n#include <functional>\n#include <iostream>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <optional>\n#include <thread>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/thread_annotations.h>\n#include <fs_avb/fs_avb.h>\n#include <fs_mgr_vendor_overlay.h>\n#include <libavb/libavb.h>\n#include <libgsi/libgsi.h>\n#include <libsnapshot/snapshot.h>\n#include <logwrap/logwrap.h>\n#include <processgroup/processgroup.h>\n#include <processgroup/setup.h>\n#include <selinux/android.h>\n#include <unwindstack/AndroidUnwinder.h>\n\n#include \"action.h\"\n#include \"action_manager.h\"\n#include \"action_parser.h\"\n#include \"apex_init_util.h\"\n#include \"epoll.h\"\n#include \"first_stage_init.h\"\n#include \"first_stage_mount.h\"\n#include \"import_parser.h\"\n#include \"keychords.h\"\n#include \"lmkd_service.h\"\n#include \"mount_handler.h\"\n#include \"mount_namespace.h\"\n#include \"property_service.h\"\n#include \"proto_utils.h\"\n#include \"reboot.h\"\n#include \"reboot_utils.h\"\n#include \"second_stage_resources.h\"\n#include \"security.h\"\n#include \"selabel.h\"\n#include \"selinux.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n#include \"service_parser.h\"\n#include \"sigchld_handler.h\"\n#include \"snapuserd_transition.h\"\n#include \"subcontext.h\"\n#include \"system/core/init/property_service.pb.h\"\n#include \"util.h\"\n\n#ifndef RECOVERY\n#include \"com_android_apex.h\"\n#endif  // RECOVERY\n\nusing namespace std::chrono_literals;\nusing namespace std::string_literals;\n\nusing android::base::boot_clock;\nusing android::base::ConsumePrefix;\nusing android::base::GetProperty;\nusing android::base::GetIntProperty;\nusing android::base::GetBoolProperty;\nusing android::base::ReadFileToString;\nusing android::base::SetProperty;\nusing android::base::StringPrintf;\nusing android::base::Timer;\nusing android::base::Trim;\nusing android::base::unique_fd;\nusing android::fs_mgr::AvbHandle;\nusing android::snapshot::SnapshotManager;\nusing android::base::WaitForProperty;\nusing android::base::WriteStringToFile;\n\nnamespace android {\nnamespace init {\n\nstatic int property_triggers_enabled = 0;\n\nstatic int sigterm_fd = -1;\nstatic int property_fd = -1;\n\nstruct PendingControlMessage {\n    std::string message;\n    std::string name;\n    pid_t pid;\n    int fd;\n};\nstatic std::mutex pending_control_messages_lock;\nstatic std::queue<PendingControlMessage> pending_control_messages;\n\n// Init epolls various FDs to wait for various inputs.  It previously waited on property changes\n// with a blocking socket that contained the information related to the change, however, it was easy\n// to fill that socket and deadlock the system.  Now we use locks to handle the property changes\n// directly in the property thread, however we still must wake the epoll to inform init that there\n// is a change to process, so we use this FD.  It is non-blocking, since we do not care how many\n// times WakeMainInitThread() is called, only that the epoll will wake.\nstatic int wake_main_thread_fd = -1;\nstatic void InstallInitNotifier(Epoll* epoll) {\n    wake_main_thread_fd = eventfd(0, EFD_CLOEXEC);\n    if (wake_main_thread_fd == -1) {\n        PLOG(FATAL) << \"Failed to create eventfd for waking init\";\n    }\n    auto clear_eventfd = [] {\n        uint64_t counter;\n        TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));\n    };\n\n    if (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) {\n        LOG(FATAL) << result.error();\n    }\n}\n\nstatic void WakeMainInitThread() {\n    uint64_t counter = 1;\n    TEMP_FAILURE_RETRY(write(wake_main_thread_fd, &counter, sizeof(counter)));\n}\n\nstatic class PropWaiterState {\n  public:\n    bool StartWaiting(const char* name, const char* value) {\n        auto lock = std::lock_guard{lock_};\n        if (waiting_for_prop_) {\n            return false;\n        }\n        if (GetProperty(name, \"\") != value) {\n            // Current property value is not equal to expected value\n            wait_prop_name_ = name;\n            wait_prop_value_ = value;\n            waiting_for_prop_.reset(new Timer());\n        } else {\n            LOG(INFO) << \"start_waiting_for_property(\\\"\" << name << \"\\\", \\\"\" << value\n                      << \"\\\"): already set\";\n        }\n        return true;\n    }\n\n    void ResetWaitForProp() {\n        auto lock = std::lock_guard{lock_};\n        ResetWaitForPropLocked();\n    }\n\n    void CheckAndResetWait(const std::string& name, const std::string& value) {\n        auto lock = std::lock_guard{lock_};\n        // We always record how long init waited for ueventd to tell us cold boot finished.\n        // If we aren't waiting on this property, it means that ueventd finished before we even\n        // started to wait.\n        if (name == kColdBootDoneProp) {\n            auto time_waited = waiting_for_prop_ ? waiting_for_prop_->duration().count() : 0;\n            std::thread([time_waited] {\n                SetProperty(\"ro.boottime.init.cold_boot_wait\", std::to_string(time_waited));\n            }).detach();\n        }\n\n        if (waiting_for_prop_) {\n            if (wait_prop_name_ == name && wait_prop_value_ == value) {\n                LOG(INFO) << \"Wait for property '\" << wait_prop_name_ << \"=\" << wait_prop_value_\n                          << \"' took \" << *waiting_for_prop_;\n                ResetWaitForPropLocked();\n                WakeMainInitThread();\n            }\n        }\n    }\n\n    // This is not thread safe because it releases the lock when it returns, so the waiting state\n    // may change.  However, we only use this function to prevent running commands in the main\n    // thread loop when we are waiting, so we do not care about false positives; only false\n    // negatives.  StartWaiting() and this function are always called from the same thread, so false\n    // negatives are not possible and therefore we're okay.\n    bool MightBeWaiting() {\n        auto lock = std::lock_guard{lock_};\n        return static_cast<bool>(waiting_for_prop_);\n    }\n\n  private:\n    void ResetWaitForPropLocked() EXCLUSIVE_LOCKS_REQUIRED(lock_) {\n        wait_prop_name_.clear();\n        wait_prop_value_.clear();\n        waiting_for_prop_.reset();\n    }\n\n    std::mutex lock_;\n    GUARDED_BY(lock_) std::unique_ptr<Timer> waiting_for_prop_{nullptr};\n    GUARDED_BY(lock_) std::string wait_prop_name_;\n    GUARDED_BY(lock_) std::string wait_prop_value_;\n\n} prop_waiter_state;\n\nbool start_waiting_for_property(const char* name, const char* value) {\n    return prop_waiter_state.StartWaiting(name, value);\n}\n\nvoid ResetWaitForProp() {\n    prop_waiter_state.ResetWaitForProp();\n}\n\nstatic class ShutdownState {\n  public:\n    void TriggerShutdown(const std::string& command) {\n        // We can't call HandlePowerctlMessage() directly in this function,\n        // because it modifies the contents of the action queue, which can cause the action queue\n        // to get into a bad state if this function is called from a command being executed by the\n        // action queue.  Instead we set this flag and ensure that shutdown happens before the next\n        // command is run in the main init loop.\n        auto lock = std::lock_guard{shutdown_command_lock_};\n        shutdown_command_ = command;\n        do_shutdown_ = true;\n        WakeMainInitThread();\n    }\n\n    std::optional<std::string> CheckShutdown() __attribute__((warn_unused_result)) {\n        auto lock = std::lock_guard{shutdown_command_lock_};\n        if (do_shutdown_ && !IsShuttingDown()) {\n            do_shutdown_ = false;\n            return shutdown_command_;\n        }\n        return {};\n    }\n\n  private:\n    std::mutex shutdown_command_lock_;\n    std::string shutdown_command_ GUARDED_BY(shutdown_command_lock_);\n    bool do_shutdown_ = false;\n} shutdown_state;\n\nvoid DumpState() {\n    ServiceList::GetInstance().DumpState();\n    ActionManager::GetInstance().DumpState();\n}\n\nParser CreateParser(ActionManager& action_manager, ServiceList& service_list) {\n    Parser parser;\n\n    parser.AddSectionParser(\"service\",\n                            std::make_unique<ServiceParser>(&service_list, GetSubcontext()));\n    parser.AddSectionParser(\"on\", std::make_unique<ActionParser>(&action_manager, GetSubcontext()));\n    parser.AddSectionParser(\"import\", std::make_unique<ImportParser>(&parser));\n\n    return parser;\n}\n\n#ifndef RECOVERY\ntemplate <typename T>\nstruct LibXmlErrorHandler {\n    T handler_;\n    template <typename Handler>\n    LibXmlErrorHandler(Handler&& handler) : handler_(std::move(handler)) {\n        xmlSetGenericErrorFunc(nullptr, &ErrorHandler);\n    }\n    ~LibXmlErrorHandler() { xmlSetGenericErrorFunc(nullptr, nullptr); }\n    static void ErrorHandler(void*, const char* msg, ...) {\n        va_list args;\n        va_start(args, msg);\n        char* formatted;\n        if (vasprintf(&formatted, msg, args) >= 0) {\n            LOG(ERROR) << formatted;\n        }\n        free(formatted);\n        va_end(args);\n    }\n};\n\ntemplate <typename Handler>\nLibXmlErrorHandler(Handler&&) -> LibXmlErrorHandler<Handler>;\n#endif  // RECOVERY\n\n// Returns a Parser that accepts scripts from APEX modules. It supports `service` and `on`.\nParser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list) {\n    Parser parser;\n    auto subcontext = GetSubcontext();\n#ifndef RECOVERY\n    if (subcontext) {\n        const auto apex_info_list_file = \"/apex/apex-info-list.xml\";\n        auto error_handler = LibXmlErrorHandler([&](const auto& error_message) {\n            LOG(ERROR) << \"Failed to read \" << apex_info_list_file << \":\" << error_message;\n        });\n        const auto apex_info_list = com::android::apex::readApexInfoList(apex_info_list_file);\n        if (apex_info_list.has_value()) {\n            std::vector<std::string> subcontext_apexes;\n            for (const auto& info : apex_info_list->getApexInfo()) {\n                if (subcontext->PartitionMatchesSubcontext(info.getPartition())) {\n                    subcontext_apexes.push_back(info.getModuleName());\n                }\n            }\n            subcontext->SetApexList(std::move(subcontext_apexes));\n        }\n    }\n#endif  // RECOVERY\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&service_list, subcontext));\n    parser.AddSectionParser(\"on\", std::make_unique<ActionParser>(&action_manager, subcontext));\n\n    return parser;\n}\n\nstatic void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {\n    Parser parser = CreateParser(action_manager, service_list);\n\n    std::string bootscript = GetProperty(\"ro.boot.init_rc\", \"\");\n    if (bootscript.empty()) {\n        parser.ParseConfig(\"/system/etc/init/hw/init.rc\");\n        if (!parser.ParseConfig(\"/system/etc/init\")) {\n            late_import_paths.emplace_back(\"/system/etc/init\");\n        }\n        // late_import is available only in Q and earlier release. As we don't\n        // have system_ext in those versions, skip late_import for system_ext.\n        parser.ParseConfig(\"/system_ext/etc/init\");\n        if (!parser.ParseConfig(\"/vendor/etc/init\")) {\n            late_import_paths.emplace_back(\"/vendor/etc/init\");\n        }\n        if (!parser.ParseConfig(\"/odm/etc/init\")) {\n            late_import_paths.emplace_back(\"/odm/etc/init\");\n        }\n        if (!parser.ParseConfig(\"/product/etc/init\")) {\n            late_import_paths.emplace_back(\"/product/etc/init\");\n        }\n    } else {\n        parser.ParseConfig(bootscript);\n    }\n}\n\nvoid PropertyChanged(const std::string& name, const std::string& value) {\n    // If the property is sys.powerctl, we bypass the event queue and immediately handle it.\n    // This is to ensure that init will always and immediately shutdown/reboot, regardless of\n    // if there are other pending events to process or if init is waiting on an exec service or\n    // waiting on a property.\n    // In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific\n    // commands to be executed.\n    if (name == \"sys.powerctl\") {\n        trigger_shutdown(value);\n    }\n\n    if (property_triggers_enabled) {\n        ActionManager::GetInstance().QueuePropertyChange(name, value);\n        WakeMainInitThread();\n    }\n\n    prop_waiter_state.CheckAndResetWait(name, value);\n}\n\nstatic std::optional<boot_clock::time_point> HandleProcessActions() {\n    std::optional<boot_clock::time_point> next_process_action_time;\n    for (const auto& s : ServiceList::GetInstance()) {\n        if ((s->flags() & SVC_RUNNING) && s->timeout_period()) {\n            auto timeout_time = s->time_started() + *s->timeout_period();\n            if (boot_clock::now() > timeout_time) {\n                s->Timeout();\n            } else {\n                if (!next_process_action_time || timeout_time < *next_process_action_time) {\n                    next_process_action_time = timeout_time;\n                }\n            }\n        }\n\n        if (!(s->flags() & SVC_RESTARTING)) continue;\n\n        auto restart_time = s->time_started() + s->restart_period();\n        if (boot_clock::now() > restart_time) {\n            if (auto result = s->Start(); !result.ok()) {\n                LOG(ERROR) << \"Could not restart process '\" << s->name() << \"': \" << result.error();\n            }\n        } else {\n            if (!next_process_action_time || restart_time < *next_process_action_time) {\n                next_process_action_time = restart_time;\n            }\n        }\n    }\n    return next_process_action_time;\n}\n\nstatic Result<void> DoControlStart(Service* service) {\n    return service->Start();\n}\n\nstatic Result<void> DoControlStop(Service* service) {\n    service->Stop();\n    return {};\n}\n\nstatic Result<void> DoControlRestart(Service* service) {\n    service->Restart();\n    return {};\n}\n\nint StopServicesFromApex(const std::string& apex_name) {\n    auto services = ServiceList::GetInstance().FindServicesByApexName(apex_name);\n    if (services.empty()) {\n        LOG(INFO) << \"No service found for APEX: \" << apex_name;\n        return 0;\n    }\n    std::set<std::string> service_names;\n    for (const auto& service : services) {\n        service_names.emplace(service->name());\n    }\n    constexpr std::chrono::milliseconds kServiceStopTimeout = 10s;\n    int still_running = StopServicesAndLogViolations(service_names, kServiceStopTimeout,\n                        true /*SIGTERM*/);\n    // Send SIGKILL to ones that didn't terminate cleanly.\n    if (still_running > 0) {\n        still_running = StopServicesAndLogViolations(service_names, 0ms, false /*SIGKILL*/);\n    }\n    return still_running;\n}\n\nvoid RemoveServiceAndActionFromApex(const std::string& apex_name) {\n    // Remove services and actions that match apex name\n    ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr<Action>& action) -> bool {\n        if (GetApexNameFromFileName(action->filename()) == apex_name) {\n            return true;\n        }\n        return false;\n    });\n    ServiceList::GetInstance().RemoveServiceIf([&](const std::unique_ptr<Service>& s) -> bool {\n        if (GetApexNameFromFileName(s->filename()) == apex_name) {\n            return true;\n        }\n        return false;\n    });\n}\n\nstatic Result<void> DoUnloadApex(const std::string& apex_name) {\n    if (StopServicesFromApex(apex_name) > 0) {\n        return Error() << \"Unable to stop all service from \" << apex_name;\n    }\n    RemoveServiceAndActionFromApex(apex_name);\n    return {};\n}\n\nstatic Result<void> UpdateApexLinkerConfig(const std::string& apex_name) {\n    // Do not invoke linkerconfig when there's no bin/ in the apex.\n    const std::string bin_path = \"/apex/\" + apex_name + \"/bin\";\n    if (access(bin_path.c_str(), R_OK) != 0) {\n        return {};\n    }\n    const char* linkerconfig_binary = \"/apex/com.android.runtime/bin/linkerconfig\";\n    const char* linkerconfig_target = \"/linkerconfig\";\n    const char* arguments[] = {linkerconfig_binary, \"--target\", linkerconfig_target, \"--apex\",\n                               apex_name.c_str(),   \"--strict\"};\n\n    if (logwrap_fork_execvp(arraysize(arguments), arguments, nullptr, false, LOG_KLOG, false,\n                            nullptr) != 0) {\n        return ErrnoError() << \"failed to execute linkerconfig\";\n    }\n    LOG(INFO) << \"Generated linker configuration for \" << apex_name;\n    return {};\n}\n\nstatic Result<void> DoLoadApex(const std::string& apex_name) {\n    if (auto result = ParseRcScriptsFromApex(apex_name); !result.ok()) {\n        return result.error();\n    }\n\n    if (auto result = UpdateApexLinkerConfig(apex_name); !result.ok()) {\n        return result.error();\n    }\n\n    return {};\n}\n\nenum class ControlTarget {\n    SERVICE,    // function gets called for the named service\n    INTERFACE,  // action gets called for every service that holds this interface\n};\n\nusing ControlMessageFunction = std::function<Result<void>(Service*)>;\n\nstatic const std::map<std::string, ControlMessageFunction, std::less<>>& GetControlMessageMap() {\n    // clang-format off\n    static const std::map<std::string, ControlMessageFunction, std::less<>> control_message_functions = {\n        {\"sigstop_on\",        [](auto* service) { service->set_sigstop(true); return Result<void>{}; }},\n        {\"sigstop_off\",       [](auto* service) { service->set_sigstop(false); return Result<void>{}; }},\n        {\"oneshot_on\",        [](auto* service) { service->set_oneshot(true); return Result<void>{}; }},\n        {\"oneshot_off\",       [](auto* service) { service->set_oneshot(false); return Result<void>{}; }},\n        {\"start\",             DoControlStart},\n        {\"stop\",              DoControlStop},\n        {\"restart\",           DoControlRestart},\n    };\n    // clang-format on\n\n    return control_message_functions;\n}\n\nstatic Result<void> HandleApexControlMessage(std::string_view action, const std::string& name,\n                                             std::string_view message) {\n    if (action == \"load\") {\n        return DoLoadApex(name);\n    } else if (action == \"unload\") {\n        return DoUnloadApex(name);\n    } else {\n        return Error() << \"Unknown control msg '\" << message << \"'\";\n    }\n}\n\nstatic bool HandleControlMessage(std::string_view message, const std::string& name,\n                                 pid_t from_pid) {\n    std::string cmdline_path = StringPrintf(\"proc/%d/cmdline\", from_pid);\n    std::string process_cmdline;\n    if (ReadFileToString(cmdline_path, &process_cmdline)) {\n        std::replace(process_cmdline.begin(), process_cmdline.end(), '\\0', ' ');\n        process_cmdline = Trim(process_cmdline);\n    } else {\n        process_cmdline = \"unknown process\";\n    }\n\n    auto action = message;\n    if (ConsumePrefix(&action, \"apex_\")) {\n        if (auto result = HandleApexControlMessage(action, name, message); !result.ok()) {\n            LOG(ERROR) << \"Control message: Could not ctl.\" << message << \" for '\" << name\n                       << \"' from pid: \" << from_pid << \" (\" << process_cmdline\n                       << \"): \" << result.error();\n            return false;\n        }\n        LOG(INFO) << \"Control message: Processed ctl.\" << message << \" for '\" << name\n                  << \"' from pid: \" << from_pid << \" (\" << process_cmdline << \")\";\n        return true;\n    }\n\n    Service* service = nullptr;\n    if (ConsumePrefix(&action, \"interface_\")) {\n        service = ServiceList::GetInstance().FindInterface(name);\n    } else {\n        service = ServiceList::GetInstance().FindService(name);\n    }\n\n    if (service == nullptr) {\n        LOG(ERROR) << \"Control message: Could not find '\" << name << \"' for ctl.\" << message\n                   << \" from pid: \" << from_pid << \" (\" << process_cmdline << \")\";\n        return false;\n    }\n\n    const auto& map = GetControlMessageMap();\n    const auto it = map.find(action);\n    if (it == map.end()) {\n        LOG(ERROR) << \"Unknown control msg '\" << message << \"'\";\n        return false;\n    }\n    const auto& function = it->second;\n\n    if (auto result = function(service); !result.ok()) {\n        LOG(ERROR) << \"Control message: Could not ctl.\" << message << \" for '\" << name\n                   << \"' from pid: \" << from_pid << \" (\" << process_cmdline\n                   << \"): \" << result.error();\n        return false;\n    }\n\n    LOG(INFO) << \"Control message: Processed ctl.\" << message << \" for '\" << name\n              << \"' from pid: \" << from_pid << \" (\" << process_cmdline << \")\";\n    return true;\n}\n\nbool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd) {\n    auto lock = std::lock_guard{pending_control_messages_lock};\n    if (pending_control_messages.size() > 100) {\n        LOG(ERROR) << \"Too many pending control messages, dropped '\" << message << \"' for '\" << name\n                   << \"' from pid: \" << pid;\n        return false;\n    }\n    pending_control_messages.push({message, name, pid, fd});\n    WakeMainInitThread();\n    return true;\n}\n\nstatic void HandleControlMessages() {\n    auto lock = std::unique_lock{pending_control_messages_lock};\n    // Init historically would only execute handle one property message, including control messages\n    // in each iteration of its main loop.  We retain this behavior here to prevent starvation of\n    // other actions in the main loop.\n    if (!pending_control_messages.empty()) {\n        auto control_message = pending_control_messages.front();\n        pending_control_messages.pop();\n        lock.unlock();\n\n        bool success = HandleControlMessage(control_message.message, control_message.name,\n                                            control_message.pid);\n\n        uint32_t response = success ? PROP_SUCCESS : PROP_ERROR_HANDLE_CONTROL_MESSAGE;\n        if (control_message.fd != -1) {\n            TEMP_FAILURE_RETRY(send(control_message.fd, &response, sizeof(response), 0));\n            close(control_message.fd);\n        }\n        lock.lock();\n    }\n    // If we still have items to process, make sure we wake back up to do so.\n    if (!pending_control_messages.empty()) {\n        WakeMainInitThread();\n    }\n}\n\nstatic Result<void> wait_for_coldboot_done_action(const BuiltinArguments& args) {\n    if (!prop_waiter_state.StartWaiting(kColdBootDoneProp, \"true\")) {\n        LOG(FATAL) << \"Could not wait for '\" << kColdBootDoneProp << \"'\";\n    }\n\n    return {};\n}\n\nstatic Result<void> SetupCgroupsAction(const BuiltinArguments&) {\n    if (!CgroupsAvailable()) {\n        LOG(INFO) << \"Cgroups support in kernel is not enabled\";\n        return {};\n    }\n    if (!CgroupSetup()) {\n        return ErrnoError() << \"Failed to setup cgroups\";\n    }\n\n    return {};\n}\n\nstatic void export_oem_lock_status() {\n    if (!android::base::GetBoolProperty(\"ro.oem_unlock_supported\", false)) {\n        return;\n    }\n    SetProperty(\n            \"ro.boot.flash.locked\",\n            android::base::GetProperty(\"ro.boot.verifiedbootstate\", \"\") == \"orange\" ? \"0\" : \"1\");\n}\n\nstatic Result<void> property_enable_triggers_action(const BuiltinArguments& args) {\n    /* Enable property triggers. */\n    property_triggers_enabled = 1;\n    return {};\n}\n\nstatic Result<void> queue_property_triggers_action(const BuiltinArguments& args) {\n    ActionManager::GetInstance().QueueBuiltinAction(property_enable_triggers_action, \"enable_property_trigger\");\n    ActionManager::GetInstance().QueueAllPropertyActions();\n    return {};\n}\n\n// Set the UDC controller for the ConfigFS USB Gadgets.\n// Read the UDC controller in use from \"/sys/class/udc\".\n// In case of multiple UDC controllers select the first one.\nstatic void SetUsbController() {\n    static auto controller_set = false;\n    if (controller_set) return;\n    std::unique_ptr<DIR, decltype(&closedir)>dir(opendir(\"/sys/class/udc\"), closedir);\n    if (!dir) return;\n\n    dirent* dp;\n    while ((dp = readdir(dir.get())) != nullptr) {\n        if (dp->d_name[0] == '.') continue;\n\n        SetProperty(\"sys.usb.controller\", dp->d_name);\n        controller_set = true;\n        break;\n    }\n}\n\n/// Set ro.kernel.version property to contain the major.minor pair as returned\n/// by uname(2).\nstatic void SetKernelVersion() {\n    struct utsname uts;\n    unsigned int major, minor;\n\n    if ((uname(&uts) != 0) || (sscanf(uts.release, \"%u.%u\", &major, &minor) != 2)) {\n        LOG(ERROR) << \"Could not parse the kernel version from uname\";\n        return;\n    }\n    SetProperty(\"ro.kernel.version\", android::base::StringPrintf(\"%u.%u\", major, minor));\n}\n\nstatic void HandleSigtermSignal(const signalfd_siginfo& siginfo) {\n    if (siginfo.ssi_pid != 0) {\n        // Drop any userspace SIGTERM requests.\n        LOG(DEBUG) << \"Ignoring SIGTERM from pid \" << siginfo.ssi_pid;\n        return;\n    }\n\n    HandlePowerctlMessage(\"shutdown,container\");\n}\n\nstatic void HandleSignalFd(int signal) {\n    signalfd_siginfo siginfo;\n    const int signal_fd = signal == SIGCHLD ? Service::GetSigchldFd() : sigterm_fd;\n    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));\n    if (bytes_read != sizeof(siginfo)) {\n        PLOG(ERROR) << \"Failed to read siginfo from signal_fd\";\n        return;\n    }\n\n    switch (siginfo.ssi_signo) {\n        case SIGCHLD:\n            ReapAnyOutstandingChildren();\n            break;\n        case SIGTERM:\n            HandleSigtermSignal(siginfo);\n            break;\n        default:\n            LOG(ERROR) << \"signal_fd: received unexpected signal \" << siginfo.ssi_signo;\n            break;\n    }\n}\n\nstatic void UnblockSignals() {\n    const struct sigaction act { .sa_handler = SIG_DFL };\n    sigaction(SIGCHLD, &act, nullptr);\n\n    sigset_t mask;\n    sigemptyset(&mask);\n    sigaddset(&mask, SIGCHLD);\n    sigaddset(&mask, SIGTERM);\n\n    if (sigprocmask(SIG_UNBLOCK, &mask, nullptr) == -1) {\n        PLOG(FATAL) << \"failed to unblock signals for PID \" << getpid();\n    }\n}\n\nstatic Result<void> RegisterSignalFd(Epoll* epoll, int signal, int fd) {\n    return epoll->RegisterHandler(\n            fd, [signal]() { HandleSignalFd(signal); }, EPOLLIN | EPOLLPRI);\n}\n\nstatic Result<int> CreateAndRegisterSignalFd(Epoll* epoll, int signal) {\n    sigset_t mask;\n    sigemptyset(&mask);\n    sigaddset(&mask, signal);\n    if (sigprocmask(SIG_BLOCK, &mask, nullptr) == -1) {\n        return ErrnoError() << \"failed to block signal \" << signal;\n    }\n\n    unique_fd signal_fd(signalfd(-1, &mask, SFD_CLOEXEC));\n    if (signal_fd.get() < 0) {\n        return ErrnoError() << \"failed to create signalfd for signal \" << signal;\n    }\n    OR_RETURN(RegisterSignalFd(epoll, signal, signal_fd.get()));\n\n    return signal_fd.release();\n}\n\nstatic void InstallSignalFdHandler(Epoll* epoll) {\n    // Applying SA_NOCLDSTOP to a defaulted SIGCHLD handler prevents the signalfd from receiving\n    // SIGCHLD when a child process stops or continues (b/77867680#comment9).\n    const struct sigaction act { .sa_flags = SA_NOCLDSTOP, .sa_handler = SIG_DFL };\n    sigaction(SIGCHLD, &act, nullptr);\n\n    // Register a handler to unblock signals in the child processes.\n    const int result = pthread_atfork(nullptr, nullptr, &UnblockSignals);\n    if (result != 0) {\n        LOG(FATAL) << \"Failed to register a fork handler: \" << strerror(result);\n    }\n\n    Result<void> cs_result = RegisterSignalFd(epoll, SIGCHLD, Service::GetSigchldFd());\n    if (!cs_result.ok()) {\n        PLOG(FATAL) << cs_result.error();\n    }\n\n    if (!IsRebootCapable()) {\n        Result<int> cs_result = CreateAndRegisterSignalFd(epoll, SIGTERM);\n        if (!cs_result.ok()) {\n            PLOG(FATAL) << cs_result.error();\n        }\n        sigterm_fd = cs_result.value();\n    }\n}\n\nvoid HandleKeychord(const std::vector<int>& keycodes) {\n    // Only handle keychords if adb is enabled.\n    std::string adb_enabled = android::base::GetProperty(\"init.svc.adbd\", \"\");\n    if (adb_enabled != \"running\") {\n        LOG(WARNING) << \"Not starting service for keychord \" << android::base::Join(keycodes, ' ')\n                     << \" because ADB is disabled\";\n        return;\n    }\n\n    auto found = false;\n    for (const auto& service : ServiceList::GetInstance()) {\n        auto svc = service.get();\n        if (svc->keycodes() == keycodes) {\n            found = true;\n            LOG(INFO) << \"Starting service '\" << svc->name() << \"' from keychord \"\n                      << android::base::Join(keycodes, ' ');\n            if (auto result = svc->Start(); !result.ok()) {\n                LOG(ERROR) << \"Could not start service '\" << svc->name() << \"' from keychord \"\n                           << android::base::Join(keycodes, ' ') << \": \" << result.error();\n            }\n        }\n    }\n    if (!found) {\n        LOG(ERROR) << \"Service for keychord \" << android::base::Join(keycodes, ' ') << \" not found\";\n    }\n}\n\nstatic void UmountDebugRamdisk() {\n    if (umount(\"/debug_ramdisk\") != 0) {\n        PLOG(ERROR) << \"Failed to umount /debug_ramdisk\";\n    }\n}\n\nstatic void UmountSecondStageRes() {\n    if (umount(kSecondStageRes) != 0) {\n        PLOG(ERROR) << \"Failed to umount \" << kSecondStageRes;\n    }\n}\n\nstatic void MountExtraFilesystems() {\n#define CHECKCALL(x) \\\n    if ((x) != 0) PLOG(FATAL) << #x \" failed.\";\n\n    // /apex is used to mount APEXes\n    CHECKCALL(mount(\"tmpfs\", \"/apex\", \"tmpfs\", MS_NOEXEC | MS_NOSUID | MS_NODEV,\n                    \"mode=0755,uid=0,gid=0\"));\n\n    if (NeedsTwoMountNamespaces()) {\n        // /bootstrap-apex is used to mount \"bootstrap\" APEXes.\n        CHECKCALL(mount(\"tmpfs\", \"/bootstrap-apex\", \"tmpfs\", MS_NOEXEC | MS_NOSUID | MS_NODEV,\n                        \"mode=0755,uid=0,gid=0\"));\n    }\n\n    // /linkerconfig is used to keep generated linker configuration\n    CHECKCALL(mount(\"tmpfs\", \"/linkerconfig\", \"tmpfs\", MS_NOEXEC | MS_NOSUID | MS_NODEV,\n                    \"mode=0755,uid=0,gid=0\"));\n#undef CHECKCALL\n}\n\nstatic void RecordStageBoottimes(const boot_clock::time_point& second_stage_start_time) {\n    int64_t first_stage_start_time_ns = -1;\n    if (auto first_stage_start_time_str = getenv(kEnvFirstStageStartedAt);\n        first_stage_start_time_str) {\n        SetProperty(\"ro.boottime.init\", first_stage_start_time_str);\n        android::base::ParseInt(first_stage_start_time_str, &first_stage_start_time_ns);\n    }\n    unsetenv(kEnvFirstStageStartedAt);\n\n    int64_t selinux_start_time_ns = -1;\n    if (auto selinux_start_time_str = getenv(kEnvSelinuxStartedAt); selinux_start_time_str) {\n        android::base::ParseInt(selinux_start_time_str, &selinux_start_time_ns);\n    }\n    unsetenv(kEnvSelinuxStartedAt);\n\n    if (selinux_start_time_ns == -1) return;\n    if (first_stage_start_time_ns == -1) return;\n\n    SetProperty(\"ro.boottime.init.first_stage\",\n                std::to_string(selinux_start_time_ns - first_stage_start_time_ns));\n    SetProperty(\"ro.boottime.init.selinux\",\n                std::to_string(second_stage_start_time.time_since_epoch().count() -\n                               selinux_start_time_ns));\n    if (auto init_module_time_str = getenv(kEnvInitModuleDurationMs); init_module_time_str) {\n        SetProperty(\"ro.boottime.init.modules\", init_module_time_str);\n        unsetenv(kEnvInitModuleDurationMs);\n    }\n}\n\nvoid SendLoadPersistentPropertiesMessage() {\n    auto init_message = InitMessage{};\n    init_message.set_load_persistent_properties(true);\n    if (auto result = SendMessage(property_fd, init_message); !result.ok()) {\n        LOG(ERROR) << \"Failed to send load persistent properties message: \" << result.error();\n    }\n}\n\nstatic Result<void> ConnectEarlyStageSnapuserdAction(const BuiltinArguments& args) {\n    auto pid = GetSnapuserdFirstStagePid();\n    if (!pid) {\n        return {};\n    }\n\n    auto info = GetSnapuserdFirstStageInfo();\n    if (auto iter = std::find(info.begin(), info.end(), \"socket\"s); iter == info.end()) {\n        // snapuserd does not support socket handoff, so exit early.\n        return {};\n    }\n\n    // Socket handoff is supported.\n    auto svc = ServiceList::GetInstance().FindService(\"snapuserd\");\n    if (!svc) {\n        LOG(FATAL) << \"Failed to find snapuserd service entry\";\n    }\n\n    svc->SetShutdownCritical();\n    svc->SetStartedInFirstStage(*pid);\n\n    svc = ServiceList::GetInstance().FindService(\"snapuserd_proxy\");\n    if (!svc) {\n        LOG(FATAL) << \"Failed find snapuserd_proxy service entry, merge will never initiate\";\n    }\n    if (!svc->MarkSocketPersistent(\"snapuserd\")) {\n        LOG(FATAL) << \"Could not find snapuserd socket in snapuserd_proxy service entry\";\n    }\n    if (auto result = svc->Start(); !result.ok()) {\n        LOG(FATAL) << \"Could not start snapuserd_proxy: \" << result.error();\n    }\n    return {};\n}\n\nstatic void SecondStageBootMonitor(int timeout_sec) {\n    auto cur_time = boot_clock::now().time_since_epoch();\n    int cur_sec = std::chrono::duration_cast<std::chrono::seconds>(cur_time).count();\n    int extra_sec = timeout_sec <= cur_sec? 0 : timeout_sec - cur_sec;\n    auto boot_timeout = std::chrono::seconds(extra_sec);\n\n    LOG(INFO) << \"Started BootMonitorThread, expiring in \"\n              << timeout_sec\n              << \" seconds from boot-up\";\n\n    if (!WaitForProperty(\"sys.boot_completed\", \"1\", boot_timeout)) {\n        LOG(ERROR) << \"BootMonitorThread: boot didn't complete in \"\n                   << timeout_sec\n                   << \" seconds. Trigger a panic!\";\n\n        // add a short delay for logs to be flushed out.\n        std::this_thread::sleep_for(200ms);\n\n        // trigger a kernel panic\n        WriteStringToFile(\"c\", PROC_SYSRQ);\n    }\n}\n\nstatic void StartSecondStageBootMonitor(int timeout_sec) {\n    std::thread monitor_thread(&SecondStageBootMonitor, timeout_sec);\n    monitor_thread.detach();\n}\n\nint SecondStageMain(int argc, char** argv) {\n    if (REBOOT_BOOTLOADER_ON_PANIC) {\n        InstallRebootSignalHandlers();\n    }\n\n    // No threads should be spin up until signalfd\n    // is registered. If the threads are indeed required,\n    // each of these threads _should_ make sure SIGCHLD signal\n    // is blocked. See b/223076262\n    boot_clock::time_point start_time = boot_clock::now();\n\n    trigger_shutdown = [](const std::string& command) { shutdown_state.TriggerShutdown(command); };\n\n    SetStdioToDevNull(argv);\n    InitKernelLogging(argv);\n    LOG(INFO) << \"init second stage started!\";\n\n    SelinuxSetupKernelLogging();\n\n    // Update $PATH in the case the second stage init is newer than first stage init, where it is\n    // first set.\n    if (setenv(\"PATH\", _PATH_DEFPATH, 1) != 0) {\n        PLOG(FATAL) << \"Could not set $PATH to '\" << _PATH_DEFPATH << \"' in second stage\";\n    }\n\n    // Init should not crash because of a dependence on any other process, therefore we ignore\n    // SIGPIPE and handle EPIPE at the call site directly.  Note that setting a signal to SIG_IGN\n    // is inherited across exec, but custom signal handlers are not.  Since we do not want to\n    // ignore SIGPIPE for child processes, we set a no-op function for the signal handler instead.\n    {\n        struct sigaction action = {.sa_flags = SA_RESTART};\n        action.sa_handler = [](int) {};\n        sigaction(SIGPIPE, &action, nullptr);\n    }\n\n    // Set init and its forked children's oom_adj.\n    if (auto result =\n                WriteFile(\"/proc/1/oom_score_adj\", StringPrintf(\"%d\", DEFAULT_OOM_SCORE_ADJUST));\n        !result.ok()) {\n        LOG(ERROR) << \"Unable to write \" << DEFAULT_OOM_SCORE_ADJUST\n                   << \" to /proc/1/oom_score_adj: \" << result.error();\n    }\n\n    // Indicate that booting is in progress to background fw loaders, etc.\n    close(open(\"/dev/.booting\", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));\n\n    // See if need to load debug props to allow adb root, when the device is unlocked.\n    const char* force_debuggable_env = getenv(\"INIT_FORCE_DEBUGGABLE\");\n    bool load_debug_prop = false;\n    if (force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {\n        load_debug_prop = \"true\"s == force_debuggable_env;\n    }\n    unsetenv(\"INIT_FORCE_DEBUGGABLE\");\n\n    // Umount the debug ramdisk so property service doesn't read .prop files from there, when it\n    // is not meant to.\n    if (!load_debug_prop) {\n        UmountDebugRamdisk();\n    }\n\n    PropertyInit();\n\n    // Umount second stage resources after property service has read the .prop files.\n    UmountSecondStageRes();\n\n    // Umount the debug ramdisk after property service has read the .prop files when it means to.\n    if (load_debug_prop) {\n        UmountDebugRamdisk();\n    }\n\n    // Mount extra filesystems required during second stage init\n    MountExtraFilesystems();\n\n    // Now set up SELinux for second stage.\n    SelabelInitialize();\n    SelinuxRestoreContext();\n\n    Epoll epoll;\n    if (auto result = epoll.Open(); !result.ok()) {\n        PLOG(FATAL) << result.error();\n    }\n\n    // We always reap children before responding to the other pending functions. This is to\n    // prevent a race where other daemons see that a service has exited and ask init to\n    // start it again via ctl.start before init has reaped it.\n    epoll.SetFirstCallback(ReapAnyOutstandingChildren);\n\n    InstallSignalFdHandler(&epoll);\n    InstallInitNotifier(&epoll);\n    StartPropertyService(&property_fd);\n\n    // If boot_timeout property has been set in a debug build, start the boot monitor\n    if (GetBoolProperty(\"ro.debuggable\", false)) {\n        int timeout = GetIntProperty(\"ro.boot.boot_timeout\", 0);\n        if (timeout > 0) {\n            StartSecondStageBootMonitor(timeout);\n        }\n    }\n\n    // Make the time that init stages started available for bootstat to log.\n    RecordStageBoottimes(start_time);\n\n    // Set libavb version for Framework-only OTA match in Treble build.\n    if (const char* avb_version = getenv(\"INIT_AVB_VERSION\"); avb_version != nullptr) {\n        SetProperty(\"ro.boot.avb_version\", avb_version);\n    }\n    unsetenv(\"INIT_AVB_VERSION\");\n\n    fs_mgr_vendor_overlay_mount_all();\n    export_oem_lock_status();\n    MountHandler mount_handler(&epoll);\n    SetUsbController();\n    SetKernelVersion();\n\n    const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();\n    Action::set_function_map(&function_map);\n\n    if (!SetupMountNamespaces()) {\n        PLOG(FATAL) << \"SetupMountNamespaces failed\";\n    }\n\n    InitializeSubcontext();\n\n    ActionManager& am = ActionManager::GetInstance();\n    ServiceList& sm = ServiceList::GetInstance();\n\n    LoadBootScripts(am, sm);\n\n    // Turning this on and letting the INFO logging be discarded adds 0.2s to\n    // Nexus 9 boot time, so it's disabled by default.\n    if (false) DumpState();\n\n    // Make the GSI status available before scripts start running.\n    auto is_running = android::gsi::IsGsiRunning() ? \"1\" : \"0\";\n    SetProperty(gsi::kGsiBootedProp, is_running);\n    auto is_installed = android::gsi::IsGsiInstalled() ? \"1\" : \"0\";\n    SetProperty(gsi::kGsiInstalledProp, is_installed);\n    if (android::gsi::IsGsiRunning()) {\n        std::string dsu_slot;\n        if (android::gsi::GetActiveDsu(&dsu_slot)) {\n            SetProperty(gsi::kDsuSlotProp, dsu_slot);\n        }\n    }\n\n    // This needs to happen before SetKptrRestrictAction, as we are trying to\n    // open /proc/kallsyms while still being allowed to see the full addresses\n    // (since init holds CAP_SYSLOG, and Linux boots with kptr_restrict=0). The\n    // address visibility through the saved fd (more specifically, the backing\n    // open file description) will then be remembered by the kernel for the rest\n    // of its lifetime, even after we raise the kptr_restrict.\n    Service::OpenAndSaveStaticKallsymsFd();\n\n    am.QueueBuiltinAction(SetupCgroupsAction, \"SetupCgroups\");\n    am.QueueBuiltinAction(SetKptrRestrictAction, \"SetKptrRestrict\");\n    am.QueueBuiltinAction(TestPerfEventSelinuxAction, \"TestPerfEventSelinux\");\n    am.QueueEventTrigger(\"early-init\");\n    am.QueueBuiltinAction(ConnectEarlyStageSnapuserdAction, \"ConnectEarlyStageSnapuserd\");\n\n    // Queue an action that waits for coldboot done so we know ueventd has set up all of /dev...\n    am.QueueBuiltinAction(wait_for_coldboot_done_action, \"wait_for_coldboot_done\");\n    // ... so that we can start queuing up actions that require stuff from /dev.\n    am.QueueBuiltinAction(SetMmapRndBitsAction, \"SetMmapRndBits\");\n    Keychords keychords;\n    am.QueueBuiltinAction(\n            [&epoll, &keychords](const BuiltinArguments& args) -> Result<void> {\n                for (const auto& svc : ServiceList::GetInstance()) {\n                    keychords.Register(svc->keycodes());\n                }\n                keychords.Start(&epoll, HandleKeychord);\n                return {};\n            },\n            \"KeychordInit\");\n\n    // Trigger all the boot actions to get us started.\n    am.QueueEventTrigger(\"init\");\n\n    // Don't mount filesystems or start core system services in charger mode.\n    std::string bootmode = GetProperty(\"ro.bootmode\", \"\");\n    if (bootmode == \"charger\") {\n        am.QueueEventTrigger(\"charger\");\n    } else {\n        am.QueueEventTrigger(\"late-init\");\n    }\n\n    // Run all property triggers based on current state of the properties.\n    am.QueueBuiltinAction(queue_property_triggers_action, \"queue_property_triggers\");\n\n    // Restore prio before main loop\n    setpriority(PRIO_PROCESS, 0, 0);\n    while (true) {\n        // By default, sleep until something happens. Do not convert far_future into\n        // std::chrono::milliseconds because that would trigger an overflow. The unit of boot_clock\n        // is 1ns.\n        const boot_clock::time_point far_future = boot_clock::time_point::max();\n        boot_clock::time_point next_action_time = far_future;\n\n        auto shutdown_command = shutdown_state.CheckShutdown();\n        if (shutdown_command) {\n            LOG(INFO) << \"Got shutdown_command '\" << *shutdown_command\n                      << \"' Calling HandlePowerctlMessage()\";\n            HandlePowerctlMessage(*shutdown_command);\n        }\n\n        if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {\n            am.ExecuteOneCommand();\n            // If there's more work to do, wake up again immediately.\n            if (am.HasMoreCommands()) {\n                next_action_time = boot_clock::now();\n            }\n        }\n        // Since the above code examined pending actions, no new actions must be\n        // queued by the code between this line and the Epoll::Wait() call below\n        // without calling WakeMainInitThread().\n        if (!IsShuttingDown()) {\n            auto next_process_action_time = HandleProcessActions();\n\n            // If there's a process that needs restarting, wake up in time for that.\n            if (next_process_action_time) {\n                next_action_time = std::min(next_action_time, *next_process_action_time);\n            }\n        }\n\n        std::optional<std::chrono::milliseconds> epoll_timeout;\n        if (next_action_time != far_future) {\n            epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(\n                    std::max(next_action_time - boot_clock::now(), 0ns));\n        }\n        auto epoll_result = epoll.Wait(epoll_timeout);\n        if (!epoll_result.ok()) {\n            LOG(ERROR) << epoll_result.error();\n        }\n        if (!IsShuttingDown()) {\n            HandleControlMessages();\n            SetUsbController();\n        }\n    }\n\n    return 0;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/init.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <string>\n\n#include \"action.h\"\n#include \"action_manager.h\"\n#include \"parser.h\"\n#include \"service_list.h\"\n\nnamespace android {\nnamespace init {\n\nParser CreateParser(ActionManager& action_manager, ServiceList& service_list);\nParser CreateApexConfigParser(ActionManager& action_manager, ServiceList& service_list);\n\nbool start_waiting_for_property(const char *name, const char *value);\n\nvoid DumpState();\n\nvoid ResetWaitForProp();\n\nvoid SendLoadPersistentPropertiesMessage();\n\nvoid PropertyChanged(const std::string& name, const std::string& value);\nbool QueueControlMessage(const std::string& message, const std::string& name, pid_t pid, int fd);\n\nint SecondStageMain(int argc, char** argv);\n\nint StopServicesFromApex(const std::string& apex_name);\n\nvoid RemoveServiceAndActionFromApex(const std::string& apex_name);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/init_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fstream>\n#include <functional>\n#include <string_view>\n#include <thread>\n#include <type_traits>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android/api-level.h>\n#include <gtest/gtest.h>\n#include <selinux/selinux.h>\n#include <sys/resource.h>\n\n#include \"action.h\"\n#include \"action_manager.h\"\n#include \"action_parser.h\"\n#include \"builtin_arguments.h\"\n#include \"builtins.h\"\n#include \"import_parser.h\"\n#include \"init.h\"\n#include \"keyword_map.h\"\n#include \"parser.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n#include \"service_parser.h\"\n#include \"util.h\"\n\nusing android::base::GetIntProperty;\nusing android::base::GetProperty;\nusing android::base::SetProperty;\nusing android::base::StringPrintf;\nusing android::base::StringReplace;\nusing android::base::WaitForProperty;\nusing namespace std::literals;\n\nnamespace android {\nnamespace init {\n\nusing ActionManagerCommand = std::function<void(ActionManager&)>;\n\nvoid TestInit(const std::string& init_script_file, const BuiltinFunctionMap& test_function_map,\n              const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,\n              ServiceList* service_list) {\n    Action::set_function_map(&test_function_map);\n\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(service_list, nullptr));\n    parser.AddSectionParser(\"on\", std::make_unique<ActionParser>(action_manager, nullptr));\n    parser.AddSectionParser(\"import\", std::make_unique<ImportParser>(&parser));\n\n    ASSERT_TRUE(parser.ParseConfig(init_script_file));\n\n    for (const auto& command : commands) {\n        command(*action_manager);\n    }\n\n    while (action_manager->HasMoreCommands()) {\n        action_manager->ExecuteOneCommand();\n    }\n}\n\nvoid TestInitText(const std::string& init_script, const BuiltinFunctionMap& test_function_map,\n                  const std::vector<ActionManagerCommand>& commands, ActionManager* action_manager,\n                  ServiceList* service_list) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));\n    TestInit(tf.path, test_function_map, commands, action_manager, service_list);\n}\n\nTEST(init, SimpleEventTrigger) {\n    bool expect_true = false;\n    std::string init_script =\n        R\"init(\non boot\npass_test\n)init\";\n\n    auto do_pass_test = [&expect_true](const BuiltinArguments&) {\n        expect_true = true;\n        return Result<void>{};\n    };\n    BuiltinFunctionMap test_function_map = {\n            {\"pass_test\", {0, 0, {false, do_pass_test}}},\n    };\n\n    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger(\"boot\"); };\n    std::vector<ActionManagerCommand> commands{trigger_boot};\n\n    ActionManager action_manager;\n    ServiceList service_list;\n    TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);\n\n    EXPECT_TRUE(expect_true);\n}\n\nTEST(init, WrongEventTrigger) {\n    std::string init_script =\n            R\"init(\non boot:\npass_test\n)init\";\n\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));\n\n    ActionManager am;\n\n    Parser parser;\n    parser.AddSectionParser(\"on\", std::make_unique<ActionParser>(&am, nullptr));\n\n    ASSERT_TRUE(parser.ParseConfig(tf.path));\n    ASSERT_EQ(1u, parser.parse_error_count());\n}\n\nTEST(init, EventTriggerOrder) {\n    std::string init_script =\n        R\"init(\non boot\nexecute_first\n\non boot && property:ro.hardware=*\nexecute_second\n\non boot\nexecute_third\n\n)init\";\n\n    int num_executed = 0;\n    auto do_execute_first = [&num_executed](const BuiltinArguments&) {\n        EXPECT_EQ(0, num_executed++);\n        return Result<void>{};\n    };\n    auto do_execute_second = [&num_executed](const BuiltinArguments&) {\n        EXPECT_EQ(1, num_executed++);\n        return Result<void>{};\n    };\n    auto do_execute_third = [&num_executed](const BuiltinArguments&) {\n        EXPECT_EQ(2, num_executed++);\n        return Result<void>{};\n    };\n\n    BuiltinFunctionMap test_function_map = {\n            {\"execute_first\", {0, 0, {false, do_execute_first}}},\n            {\"execute_second\", {0, 0, {false, do_execute_second}}},\n            {\"execute_third\", {0, 0, {false, do_execute_third}}},\n    };\n\n    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger(\"boot\"); };\n    std::vector<ActionManagerCommand> commands{trigger_boot};\n\n    ActionManager action_manager;\n    ServiceList service_list;\n    TestInitText(init_script, test_function_map, commands, &action_manager, &service_list);\n    EXPECT_EQ(3, num_executed);\n}\n\nTEST(init, OverrideService) {\n    std::string init_script = R\"init(\nservice A something\n    class first\n    user nobody\n\nservice A something\n    class second\n    user nobody\n    override\n\n)init\";\n\n    ActionManager action_manager;\n    ServiceList service_list;\n    TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);\n    ASSERT_EQ(1, std::distance(service_list.begin(), service_list.end()));\n\n    auto service = service_list.begin()->get();\n    ASSERT_NE(nullptr, service);\n    EXPECT_EQ(std::set<std::string>({\"second\"}), service->classnames());\n    EXPECT_EQ(\"A\", service->name());\n    EXPECT_TRUE(service->is_override());\n}\n\nTEST(init, StartConsole) {\n    if (GetProperty(\"ro.build.type\", \"\") == \"user\") {\n        GTEST_SKIP() << \"Must run on userdebug/eng builds. b/262090304\";\n        return;\n    }\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n    std::string init_script = R\"init(\nservice console /system/bin/sh\n    class core\n    console null\n    disabled\n    user root\n    group root shell log readproc\n    seclabel u:r:shell:s0\n    setenv HOSTNAME console\n)init\";\n\n    ActionManager action_manager;\n    ServiceList service_list;\n    TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);\n    ASSERT_EQ(std::distance(service_list.begin(), service_list.end()), 1);\n\n    auto service = service_list.begin()->get();\n    ASSERT_NE(service, nullptr);\n    ASSERT_RESULT_OK(service->Start());\n    const pid_t pid = service->pid();\n    ASSERT_GT(pid, 0);\n    EXPECT_NE(getsid(pid), 0);\n    service->Stop();\n}\n\nstatic std::string GetSecurityContext() {\n    char* ctx;\n    if (getcon(&ctx) == -1) {\n        ADD_FAILURE() << \"Failed to call getcon : \" << strerror(errno);\n    }\n    std::string result = std::string(ctx);\n    freecon(ctx);\n    return result;\n}\n\nvoid TestStartApexServices(const std::vector<std::string>& service_names,\n        const std::string& apex_name) {\n    for (auto const& svc : service_names) {\n        auto service = ServiceList::GetInstance().FindService(svc);\n        ASSERT_NE(nullptr, service);\n        ASSERT_RESULT_OK(service->Start());\n        ASSERT_TRUE(service->IsRunning());\n        LOG(INFO) << \"Service \" << svc << \" is running\";\n        if (!apex_name.empty()) {\n            service->set_filename(\"/apex/\" + apex_name + \"/init_test.rc\");\n        } else {\n            service->set_filename(\"\");\n        }\n    }\n    if (!apex_name.empty()) {\n        auto apex_services = ServiceList::GetInstance().FindServicesByApexName(apex_name);\n        EXPECT_EQ(service_names.size(), apex_services.size());\n    }\n}\n\nvoid TestStopApexServices(const std::vector<std::string>& service_names, bool expect_to_run) {\n    for (auto const& svc : service_names) {\n        auto service = ServiceList::GetInstance().FindService(svc);\n        ASSERT_NE(nullptr, service);\n        EXPECT_EQ(expect_to_run, service->IsRunning());\n    }\n}\n\nvoid TestRemoveApexService(const std::vector<std::string>& service_names, bool exist) {\n    for (auto const& svc : service_names) {\n        auto service = ServiceList::GetInstance().FindService(svc);\n        ASSERT_EQ(exist, service != nullptr);\n    }\n}\n\nvoid InitApexService(const std::string_view& init_template) {\n    std::string init_script = StringReplace(init_template, \"$selabel\",\n                                    GetSecurityContext(), true);\n\n    TestInitText(init_script, BuiltinFunctionMap(), {}, &ActionManager::GetInstance(),\n            &ServiceList::GetInstance());\n}\n\nvoid CleanupApexServices() {\n    std::vector<std::string> names;\n    for (const auto& s : ServiceList::GetInstance()) {\n        names.push_back(s->name());\n    }\n\n    for (const auto& name : names) {\n        auto s = ServiceList::GetInstance().FindService(name);\n        auto pid = s->pid();\n        ServiceList::GetInstance().RemoveService(*s);\n        if (pid > 0) {\n            kill(pid, SIGTERM);\n            kill(pid, SIGKILL);\n        }\n    }\n\n    ActionManager::GetInstance().RemoveActionIf([&](const std::unique_ptr<Action>& s) -> bool {\n        return true;\n    });\n}\n\nvoid TestApexServicesInit(const std::vector<std::string>& apex_services,\n            const std::vector<std::string>& other_apex_services,\n            const std::vector<std::string> non_apex_services) {\n    auto num_svc = apex_services.size() + other_apex_services.size() + non_apex_services.size();\n    ASSERT_EQ(num_svc, ServiceList::GetInstance().size());\n\n    TestStartApexServices(apex_services, \"com.android.apex.test_service\");\n    TestStartApexServices(other_apex_services, \"com.android.other_apex.test_service\");\n    TestStartApexServices(non_apex_services, /*apex_anme=*/ \"\");\n\n    StopServicesFromApex(\"com.android.apex.test_service\");\n    TestStopApexServices(apex_services, /*expect_to_run=*/ false);\n    TestStopApexServices(other_apex_services, /*expect_to_run=*/ true);\n    TestStopApexServices(non_apex_services, /*expect_to_run=*/ true);\n\n    RemoveServiceAndActionFromApex(\"com.android.apex.test_service\");\n    ASSERT_EQ(other_apex_services.size() + non_apex_services.size(),\n        ServiceList::GetInstance().size());\n\n    // TODO(b/244232142): Add test to check if actions are removed\n    TestRemoveApexService(apex_services, /*exist*/ false);\n    TestRemoveApexService(other_apex_services, /*exist*/ true);\n    TestRemoveApexService(non_apex_services, /*exist*/ true);\n\n    CleanupApexServices();\n}\n\nTEST(init, StopServiceByApexName) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n    std::string_view script_template = R\"init(\nservice apex_test_service /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(script_template);\n    TestApexServicesInit({\"apex_test_service\"}, {}, {});\n}\n\nTEST(init, StopMultipleServicesByApexName) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n    std::string_view script_template = R\"init(\nservice apex_test_service_multiple_a /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\nservice apex_test_service_multiple_b /system/bin/id\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(script_template);\n    TestApexServicesInit({\"apex_test_service_multiple_a\",\n            \"apex_test_service_multiple_b\"}, {}, {});\n}\n\nTEST(init, StopServicesFromMultipleApexes) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n    std::string_view apex_script_template = R\"init(\nservice apex_test_service_multi_apex_a /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\nservice apex_test_service_multi_apex_b /system/bin/id\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(apex_script_template);\n\n    std::string_view other_apex_script_template = R\"init(\nservice apex_test_service_multi_apex_c /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(other_apex_script_template);\n\n    TestApexServicesInit({\"apex_test_service_multi_apex_a\",\n            \"apex_test_service_multi_apex_b\"}, {\"apex_test_service_multi_apex_c\"}, {});\n}\n\nTEST(init, StopServicesFromApexAndNonApex) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n    std::string_view apex_script_template = R\"init(\nservice apex_test_service_apex_a /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\nservice apex_test_service_apex_b /system/bin/id\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(apex_script_template);\n\n    std::string_view non_apex_script_template = R\"init(\nservice apex_test_service_non_apex /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(non_apex_script_template);\n\n    TestApexServicesInit({\"apex_test_service_apex_a\",\n            \"apex_test_service_apex_b\"}, {}, {\"apex_test_service_non_apex\"});\n}\n\nTEST(init, StopServicesFromApexMixed) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n    std::string_view script_template = R\"init(\nservice apex_test_service_mixed_a /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(script_template);\n\n    std::string_view other_apex_script_template = R\"init(\nservice apex_test_service_mixed_b /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(other_apex_script_template);\n\n    std::string_view non_apex_script_template = R\"init(\nservice apex_test_service_mixed_c /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n    InitApexService(non_apex_script_template);\n\n    TestApexServicesInit({\"apex_test_service_mixed_a\"},\n            {\"apex_test_service_mixed_b\"}, {\"apex_test_service_mixed_c\"});\n}\n\nTEST(init, EventTriggerOrderMultipleFiles) {\n    // 6 total files, which should have their triggers executed in the following order:\n    // 1: start - original script parsed\n    // 2: first_import - immediately imported by first_script\n    // 3: dir_a - file named 'a.rc' in dir; dir is imported after first_import\n    // 4: a_import - file imported by dir_a\n    // 5: dir_b - file named 'b.rc' in dir\n    // 6: last_import - imported after dir is imported\n\n    TemporaryFile first_import;\n    ASSERT_TRUE(first_import.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(\"on boot\\nexecute 2\", first_import.fd));\n\n    TemporaryFile dir_a_import;\n    ASSERT_TRUE(dir_a_import.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(\"on boot\\nexecute 4\", dir_a_import.fd));\n\n    TemporaryFile last_import;\n    ASSERT_TRUE(last_import.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(\"on boot\\nexecute 6\", last_import.fd));\n\n    TemporaryDir dir;\n    // clang-format off\n    std::string dir_a_script = \"import \" + std::string(dir_a_import.path) + \"\\n\"\n                               \"on boot\\n\"\n                               \"execute 3\";\n    // clang-format on\n    // WriteFile() ensures the right mode is set\n    ASSERT_RESULT_OK(WriteFile(std::string(dir.path) + \"/a.rc\", dir_a_script));\n\n    ASSERT_RESULT_OK(WriteFile(std::string(dir.path) + \"/b.rc\", \"on boot\\nexecute 5\"));\n\n    // clang-format off\n    std::string start_script = \"import \" + std::string(first_import.path) + \"\\n\"\n                               \"import \" + std::string(dir.path) + \"\\n\"\n                               \"import \" + std::string(last_import.path) + \"\\n\"\n                               \"on boot\\n\"\n                               \"execute 1\";\n    // clang-format on\n    TemporaryFile start;\n    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));\n\n    int num_executed = 0;\n    auto execute_command = [&num_executed](const BuiltinArguments& args) {\n        EXPECT_EQ(2U, args.size());\n        EXPECT_EQ(++num_executed, std::stoi(args[1]));\n        return Result<void>{};\n    };\n\n    BuiltinFunctionMap test_function_map = {\n            {\"execute\", {1, 1, {false, execute_command}}},\n    };\n\n    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger(\"boot\"); };\n    std::vector<ActionManagerCommand> commands{trigger_boot};\n\n    ActionManager action_manager;\n    ServiceList service_list;\n    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);\n\n    EXPECT_EQ(6, num_executed);\n}\n\nBuiltinFunctionMap GetTestFunctionMapForLazyLoad(int& num_executed, ActionManager& action_manager) {\n    auto execute_command = [&num_executed](const BuiltinArguments& args) {\n        EXPECT_EQ(2U, args.size());\n        EXPECT_EQ(++num_executed, std::stoi(args[1]));\n        return Result<void>{};\n    };\n    auto load_command = [&action_manager](const BuiltinArguments& args) -> Result<void> {\n        EXPECT_EQ(2U, args.size());\n        Parser parser;\n        parser.AddSectionParser(\"on\", std::make_unique<ActionParser>(&action_manager, nullptr));\n        if (!parser.ParseConfig(args[1])) {\n            return Error() << \"Failed to load\";\n        }\n        return Result<void>{};\n    };\n    auto trigger_command = [&action_manager](const BuiltinArguments& args) {\n        EXPECT_EQ(2U, args.size());\n        LOG(INFO) << \"Queue event trigger: \" << args[1];\n        action_manager.QueueEventTrigger(args[1]);\n        return Result<void>{};\n    };\n    BuiltinFunctionMap test_function_map = {\n            {\"execute\", {1, 1, {false, execute_command}}},\n            {\"load\", {1, 1, {false, load_command}}},\n            {\"trigger\", {1, 1, {false, trigger_command}}},\n    };\n    return test_function_map;\n}\n\nTEST(init, LazilyLoadedActionsCantBeTriggeredByTheSameTrigger) {\n    // \"start\" script loads \"lazy\" script. Even though \"lazy\" scripts\n    // defines \"on boot\" action, it's not executed by the current \"boot\"\n    // event because it's already processed.\n    TemporaryFile lazy;\n    ASSERT_TRUE(lazy.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(\"on boot\\nexecute 2\", lazy.fd));\n\n    TemporaryFile start;\n    // clang-format off\n    std::string start_script = \"on boot\\n\"\n                               \"load \" + std::string(lazy.path) + \"\\n\"\n                               \"execute 1\";\n    // clang-format on\n    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));\n\n    int num_executed = 0;\n    ActionManager action_manager;\n    ServiceList service_list;\n    BuiltinFunctionMap test_function_map =\n            GetTestFunctionMapForLazyLoad(num_executed, action_manager);\n\n    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger(\"boot\"); };\n    std::vector<ActionManagerCommand> commands{trigger_boot};\n    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);\n\n    EXPECT_EQ(1, num_executed);\n}\n\nTEST(init, LazilyLoadedActionsCanBeTriggeredByTheNextTrigger) {\n    // \"start\" script loads \"lazy\" script and then triggers \"next\" event\n    // which executes \"on next\" action loaded by the previous command.\n    TemporaryFile lazy;\n    ASSERT_TRUE(lazy.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(\"on next\\nexecute 2\", lazy.fd));\n\n    TemporaryFile start;\n    // clang-format off\n    std::string start_script = \"on boot\\n\"\n                               \"load \" + std::string(lazy.path) + \"\\n\"\n                               \"execute 1\\n\"\n                               \"trigger next\";\n    // clang-format on\n    ASSERT_TRUE(android::base::WriteStringToFd(start_script, start.fd));\n\n    int num_executed = 0;\n    ActionManager action_manager;\n    ServiceList service_list;\n    BuiltinFunctionMap test_function_map =\n            GetTestFunctionMapForLazyLoad(num_executed, action_manager);\n\n    ActionManagerCommand trigger_boot = [](ActionManager& am) { am.QueueEventTrigger(\"boot\"); };\n    std::vector<ActionManagerCommand> commands{trigger_boot};\n    TestInit(start.path, test_function_map, commands, &action_manager, &service_list);\n\n    EXPECT_EQ(2, num_executed);\n}\n\nTEST(init, RejectsNoUserStartingInV) {\n    std::string init_script =\n            R\"init(\nservice A something\n    class first\n)init\";\n\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));\n\n    ServiceList service_list;\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&service_list, nullptr));\n\n    ASSERT_TRUE(parser.ParseConfig(tf.path));\n\n    if (GetIntProperty(\"ro.vendor.api_level\", 0) > 202404) {\n        ASSERT_EQ(1u, parser.parse_error_count());\n    } else {\n        ASSERT_EQ(0u, parser.parse_error_count());\n    }\n}\n\nTEST(init, RejectsCriticalAndOneshotService) {\n    if (GetIntProperty(\"ro.product.first_api_level\", 10000) < 30) {\n        GTEST_SKIP() << \"Test only valid for devices launching with R or later\";\n    }\n\n    std::string init_script =\n            R\"init(\nservice A something\n  class first\n  user root\n  critical\n  oneshot\n)init\";\n\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(init_script, tf.fd));\n\n    ServiceList service_list;\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&service_list, nullptr));\n\n    ASSERT_TRUE(parser.ParseConfig(tf.path));\n    ASSERT_EQ(1u, parser.parse_error_count());\n}\n\nTEST(init, MemLockLimit) {\n    // Test is enforced only for U+ devices\n    if (android::base::GetIntProperty(\"ro.vendor.api_level\", 0) < __ANDROID_API_U__) {\n        GTEST_SKIP();\n    }\n\n    // Verify we are running memlock at, or under, 64KB\n    const unsigned long max_limit = 65536;\n    struct rlimit curr_limit;\n    ASSERT_EQ(getrlimit(RLIMIT_MEMLOCK, &curr_limit), 0);\n    ASSERT_LE(curr_limit.rlim_cur, max_limit);\n    ASSERT_LE(curr_limit.rlim_max, max_limit);\n}\n\nvoid CloseAllFds() {\n    DIR* dir;\n    struct dirent* ent;\n    int fd;\n\n    if ((dir = opendir(\"/proc/self/fd\"))) {\n        while ((ent = readdir(dir))) {\n            if (sscanf(ent->d_name, \"%d\", &fd) == 1) {\n                close(fd);\n            }\n        }\n        closedir(dir);\n    }\n}\n\npid_t ForkExecvpAsync(const char* argv[]) {\n    pid_t pid = fork();\n    if (pid == 0) {\n        // Child process.\n        CloseAllFds();\n\n        execvp(argv[0], const_cast<char**>(argv));\n        PLOG(ERROR) << \"exec in ForkExecvpAsync init test\";\n        _exit(EXIT_FAILURE);\n    }\n    // Parent process.\n    if (pid == -1) {\n        PLOG(ERROR) << \"fork in ForkExecvpAsync init test\";\n        return -1;\n    }\n    return pid;\n}\n\npid_t TracerPid(pid_t pid) {\n    static constexpr std::string_view prefix{\"TracerPid:\"};\n    std::ifstream is(StringPrintf(\"/proc/%d/status\", pid));\n    std::string line;\n    while (std::getline(is, line)) {\n        if (line.find(prefix) == 0) {\n            return atoi(line.substr(prefix.length()).c_str());\n        }\n    }\n    return -1;\n}\n\nTEST(init, GentleKill) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n    std::string init_script = R\"init(\nservice test_gentle_kill /system/bin/sleep 1000\n    disabled\n    oneshot\n    gentle_kill\n    user root\n    group root\n    seclabel u:r:toolbox:s0\n)init\";\n\n    ActionManager action_manager;\n    ServiceList service_list;\n    TestInitText(init_script, BuiltinFunctionMap(), {}, &action_manager, &service_list);\n    ASSERT_EQ(std::distance(service_list.begin(), service_list.end()), 1);\n\n    auto service = service_list.begin()->get();\n    ASSERT_NE(service, nullptr);\n    ASSERT_RESULT_OK(service->Start());\n    const pid_t pid = service->pid();\n    ASSERT_GT(pid, 0);\n    EXPECT_NE(getsid(pid), 0);\n\n    TemporaryFile logfile;\n    logfile.DoNotRemove();\n    ASSERT_TRUE(logfile.fd != -1);\n\n    std::string pid_str = std::to_string(pid);\n    const char* argv[] = {\"/system/bin/strace\", \"-o\", logfile.path, \"-e\", \"signal\", \"-p\",\n                          pid_str.c_str(),      nullptr};\n    pid_t strace_pid = ForkExecvpAsync(argv);\n\n    // Give strace the chance to connect\n    while (TracerPid(pid) == 0) {\n        std::this_thread::sleep_for(10ms);\n    }\n    service->Stop();\n\n    int status;\n    waitpid(strace_pid, &status, 0);\n\n    std::string logs;\n    android::base::ReadFdToString(logfile.fd, &logs);\n    ASSERT_NE(logs.find(\"killed by SIGTERM\"), std::string::npos);\n}\n\nclass TestCaseLogger : public ::testing::EmptyTestEventListener {\n    void OnTestStart(const ::testing::TestInfo& test_info) override {\n#ifdef __ANDROID__\n        LOG(INFO) << \"===== \" << test_info.test_suite_name() << \"::\" << test_info.name() << \" (\"\n                  << test_info.file() << \":\" << test_info.line() << \")\";\n#else\n        UNUSED(test_info);\n#endif\n    }\n};\n\n}  // namespace init\n}  // namespace android\n\nint SubcontextTestChildMain(int, char**);\nint FirmwareTestChildMain(int, char**);\n\nint main(int argc, char** argv) {\n    if (argc > 1 && !strcmp(argv[1], \"subcontext\")) {\n        return SubcontextTestChildMain(argc, argv);\n    }\n\n    if (argc > 1 && !strcmp(argv[1], \"firmware\")) {\n        return FirmwareTestChildMain(argc, argv);\n    }\n\n    testing::InitGoogleTest(&argc, argv);\n    testing::UnitTest::GetInstance()->listeners().Append(new android::init::TestCaseLogger());\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "init/interface_utils.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"interface_utils.h\"\n\n#include <fstream>\n#include <sstream>\n\n#include <android-base/strings.h>\n#include <hidl-util/FqInstance.h>\n\nusing android::FqInstance;\nusing android::FQName;\nusing android::base::Error;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nstd::string FQNamesToString(const std::set<FQName>& fqnames) {\n    std::set<std::string> fqname_strings;\n    for (const FQName& fqname : fqnames) {\n        fqname_strings.insert(fqname.string());\n    }\n    return android::base::Join(fqname_strings, \" \");\n}\n\nResult<void> CheckInterfaceInheritanceHierarchy(const std::set<FQName>& interfaces,\n                                                const InterfaceInheritanceHierarchyMap& hierarchy) {\n    std::ostringstream error_stream;\n    for (const FQName& intf : interfaces) {\n        if (hierarchy.count(intf) == 0) {\n            error_stream << \"\\nInterface is not in the known set of hidl_interfaces: '\"\n                         << intf.string()\n                         << \"'. Please ensure the interface is spelled correctly and built \"\n                         << \"by a hidl_interface target.\";\n            continue;\n        }\n        const std::set<FQName>& required_interfaces = hierarchy.at(intf);\n        std::set<FQName> diff;\n        std::set_difference(required_interfaces.begin(), required_interfaces.end(),\n                            interfaces.begin(), interfaces.end(),\n                            std::inserter(diff, diff.begin()));\n        if (!diff.empty()) {\n            error_stream << \"\\nInterface '\" << intf.string() << \"' requires its full inheritance \"\n                         << \"hierarchy to be listed in this init_rc file. Missing \"\n                         << \"interfaces: [\" << FQNamesToString(diff) << \"]\";\n        }\n    }\n    const std::string& errors = error_stream.str();\n    if (!errors.empty()) {\n        return Error() << errors;\n    }\n\n    return {};\n}\n\n}  // namespace\n\nResult<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,\n                                                const InterfaceInheritanceHierarchyMap& hierarchy) {\n    std::set<FQName> interface_fqnames;\n    for (const std::string& instance : instances) {\n        // There is insufficient build-time information on AIDL interfaces to check them here\n        // TODO(b/139307527): Rework how services store interfaces to avoid excess string parsing\n        if (base::Split(instance, \"/\")[0] == \"aidl\") {\n            continue;\n        }\n\n        FqInstance fqinstance;\n        if (!fqinstance.setTo(instance)) {\n            return Error() << \"Unable to parse interface instance '\" << instance << \"'\";\n        }\n        interface_fqnames.insert(fqinstance.getFqName());\n    }\n    return CheckInterfaceInheritanceHierarchy(interface_fqnames, hierarchy);\n}\n\nstd::optional<std::set<FQName>> known_interfaces;\n\nvoid SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy) {\n    known_interfaces = std::set<FQName>();\n    for (const auto& [intf, inherited_interfaces] : hierarchy) {\n        known_interfaces->insert(intf);\n    }\n}\n\nResult<void> IsKnownInterface(const std::string& instance) {\n    FqInstance fqinstance;\n    if (!fqinstance.setTo(instance)) {\n        return Error() << \"Unable to parse interface instance '\" << instance << \"'\";\n    }\n    return IsKnownInterface(fqinstance.getFqName());\n}\n\nResult<void> IsKnownInterface(const FQName& intf) {\n    if (!known_interfaces) {\n        return Error() << \"No known interfaces have been loaded.\";\n    }\n    if (known_interfaces->count(intf) == 0) {\n        return Error() << \"Interface is not in the known set of hidl_interfaces: '\" << intf.string()\n                       << \"'. Please ensure the interface is spelled correctly and built \"\n                       << \"by a hidl_interface target.\";\n    }\n    return {};\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/interface_utils.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <set>\n#include <string>\n\n#include <hidl-util/FQName.h>\n\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nusing InterfaceInheritanceHierarchyMap = std::map<android::FQName, std::set<android::FQName>>;\n\n// For the given set of interfaces / interface instances, checks that each\n// interface's hierarchy of inherited interfaces is also included in the given\n// interface set. Uses the provided hierarchy data.\nResult<void> CheckInterfaceInheritanceHierarchy(const std::set<std::string>& instances,\n                                                const InterfaceInheritanceHierarchyMap& hierarchy);\n\n// Saves the set of known interfaces using the provided HIDL interface\n// inheritance hierarchy.\nvoid SetKnownInterfaces(const InterfaceInheritanceHierarchyMap& hierarchy);\n\n// Checks if the provided interface is in the set of known interfaces. Returns\n// an empty Result if present, otherwise an Error.\nResult<void> IsKnownInterface(const std::string& instance);\nResult<void> IsKnownInterface(const FQName& intf);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/interprocess_fifo.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"interprocess_fifo.h\"\n\n#include <android-base/logging.h>\n\n#include <unistd.h>\n\nusing ::android::base::ErrnoError;\nusing ::android::base::Error;\nusing ::android::base::Result;\n\nnamespace android {\nnamespace init {\n\nInterprocessFifo::InterprocessFifo() noexcept : fds_({-1, -1}) {}\n\nInterprocessFifo::InterprocessFifo(InterprocessFifo&& orig) noexcept : fds_({-1, -1}) {\n    std::swap(fds_, orig.fds_);\n}\n\nInterprocessFifo::~InterprocessFifo() noexcept {\n    Close();\n}\n\nvoid InterprocessFifo::CloseFd(int& fd) noexcept {\n    if (fd >= 0) {\n        close(fd);\n        fd = -1;\n    }\n}\n\nvoid InterprocessFifo::CloseReadFd() noexcept {\n    CloseFd(fds_[0]);\n}\n\nvoid InterprocessFifo::CloseWriteFd() noexcept {\n    CloseFd(fds_[1]);\n}\n\nvoid InterprocessFifo::Close() noexcept {\n    CloseReadFd();\n    CloseWriteFd();\n}\n\nResult<void> InterprocessFifo::Initialize() noexcept {\n    if (fds_[0] >= 0) {\n        return Error() << \"already initialized\";\n    }\n    if (pipe(fds_.data()) < 0) {  // NOLINT(android-cloexec-pipe)\n        return ErrnoError() << \"pipe()\";\n    }\n    return {};\n}\n\nResult<uint8_t> InterprocessFifo::Read() noexcept {\n    uint8_t byte;\n    ssize_t count = read(fds_[0], &byte, 1);\n    if (count < 0) {\n        return ErrnoError() << \"read()\";\n    }\n    if (count == 0) {\n        return Error() << \"read() EOF\";\n    }\n    DCHECK_EQ(count, 1);\n    return byte;\n}\n\nResult<void> InterprocessFifo::Write(uint8_t byte) noexcept {\n    ssize_t written = write(fds_[1], &byte, 1);\n    if (written < 0) {\n        return ErrnoError() << \"write()\";\n    }\n    if (written == 0) {\n        return Error() << \"write() EOF\";\n    }\n    DCHECK_EQ(written, 1);\n    return {};\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/interprocess_fifo.h",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <array>\n\n#include <android-base/result.h>\n\nnamespace android {\nnamespace init {\n\n// A FIFO for inter-process communication that uses a Unix pipe internally.\nclass InterprocessFifo {\n  public:\n    template <typename T>\n    using Result = ::android::base::Result<T>;\n\n    InterprocessFifo() noexcept;\n    InterprocessFifo(const InterprocessFifo& orig) noexcept = delete;\n    InterprocessFifo(InterprocessFifo&& orig) noexcept;\n    InterprocessFifo& operator=(const InterprocessFifo& orig) noexcept = delete;\n    InterprocessFifo& operator=(InterprocessFifo&& orig) noexcept = delete;\n    ~InterprocessFifo() noexcept;\n    void CloseReadFd() noexcept;\n    void CloseWriteFd() noexcept;\n    void Close() noexcept;\n    Result<void> Initialize() noexcept;\n    Result<void> Write(uint8_t byte) noexcept;\n    Result<uint8_t> Read() noexcept;\n\n  private:\n    static void CloseFd(int& fd) noexcept;\n\n    std::array<int, 2> fds_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/interprocess_fifo_test.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"interprocess_fifo.h\"\n\n#include <android-base/result-gmock.h>\n#include <gtest/gtest.h>\n\n#define ASSERT_OK(e) ASSERT_THAT(e, Ok())\n#define ASSERT_NOT_OK(e) ASSERT_THAT(e, Not(Ok()))\n\nusing ::android::base::Result;\nusing ::android::base::testing::Ok;\nusing ::testing::Not;\n\nnamespace android {\nnamespace init {\n\nTEST(FifoTest, WriteAndRead) {\n    InterprocessFifo fifo;\n    ASSERT_OK(fifo.Initialize());\n    ASSERT_OK(fifo.Write('a'));\n    ASSERT_OK(fifo.Write('b'));\n    Result<uint8_t> result = fifo.Read();\n    ASSERT_OK(result);\n    EXPECT_EQ(*result, 'a');\n    result = fifo.Read();\n    ASSERT_OK(result);\n    EXPECT_EQ(*result, 'b');\n    InterprocessFifo fifo2 = std::move(fifo);\n    ASSERT_NOT_OK(fifo.Write('c'));\n    ASSERT_NOT_OK(fifo.Read());\n    ASSERT_OK(fifo2.Write('d'));\n    result = fifo2.Read();\n    ASSERT_OK(result);\n    EXPECT_EQ(*result, 'd');\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/keychords.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"keychords.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <linux/input.h>\n#include <sys/cdefs.h>\n#include <sys/inotify.h>\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <functional>\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/logging.h>\n\nnamespace android {\nnamespace init {\n\nKeychords::Keychords() : epoll_(nullptr), inotify_fd_(-1) {}\n\nKeychords::~Keychords() noexcept {\n    if (inotify_fd_ >= 0) {\n        epoll_->UnregisterHandler(inotify_fd_);\n        ::close(inotify_fd_);\n    }\n    while (!registration_.empty()) GeteventCloseDevice(registration_.begin()->first);\n}\n\nKeychords::Mask::Mask(size_t bit) : bits_((bit + sizeof(mask_t) - 1) / sizeof(mask_t), 0) {}\n\nvoid Keychords::Mask::SetBit(size_t bit, bool value) {\n    auto idx = bit / (kBitsPerByte * sizeof(mask_t));\n    if (idx >= bits_.size()) return;\n    if (value) {\n        bits_[idx] |= mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t)));\n    } else {\n        bits_[idx] &= ~(mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));\n    }\n}\n\nbool Keychords::Mask::GetBit(size_t bit) const {\n    auto idx = bit / (kBitsPerByte * sizeof(mask_t));\n    return bits_[idx] & (mask_t(1) << (bit % (kBitsPerByte * sizeof(mask_t))));\n}\n\nsize_t Keychords::Mask::bytesize() const {\n    return bits_.size() * sizeof(mask_t);\n}\n\nvoid* Keychords::Mask::data() {\n    return bits_.data();\n}\n\nsize_t Keychords::Mask::size() const {\n    return bits_.size() * sizeof(mask_t) * kBitsPerByte;\n}\n\nvoid Keychords::Mask::resize(size_t bit) {\n    auto idx = bit / (kBitsPerByte * sizeof(mask_t));\n    if (idx >= bits_.size()) {\n        bits_.resize(idx + 1, 0);\n    }\n}\n\nKeychords::Mask::operator bool() const {\n    for (size_t i = 0; i < bits_.size(); ++i) {\n        if (bits_[i]) return true;\n    }\n    return false;\n}\n\nKeychords::Mask Keychords::Mask::operator&(const Keychords::Mask& rval) const {\n    auto len = std::min(bits_.size(), rval.bits_.size());\n    Keychords::Mask ret;\n    ret.bits_.resize(len);\n    for (size_t i = 0; i < len; ++i) {\n        ret.bits_[i] = bits_[i] & rval.bits_[i];\n    }\n    return ret;\n}\n\nvoid Keychords::Mask::operator|=(const Keychords::Mask& rval) {\n    auto len = rval.bits_.size();\n    bits_.resize(len);\n    for (size_t i = 0; i < len; ++i) {\n        bits_[i] |= rval.bits_[i];\n    }\n}\n\nKeychords::Entry::Entry() : notified(false) {}\n\nvoid Keychords::LambdaCheck() {\n    for (auto& [keycodes, entry] : entries_) {\n        auto found = true;\n        for (auto& code : keycodes) {\n            if (!current_.GetBit(code)) {\n                entry.notified = false;\n                found = false;\n                break;\n            }\n        }\n        if (!found) continue;\n        if (entry.notified) continue;\n        entry.notified = true;\n        handler_(keycodes);\n    }\n}\n\nvoid Keychords::LambdaHandler(int fd) {\n    input_event event;\n    auto res = TEMP_FAILURE_RETRY(::read(fd, &event, sizeof(event)));\n    if ((res != sizeof(event)) || (event.type != EV_KEY)) return;\n    current_.SetBit(event.code, event.value);\n    LambdaCheck();\n}\n\nbool Keychords::GeteventEnable(int fd) {\n    // Make sure it is an event channel, should pass this ioctl call\n    int version;\n    if (::ioctl(fd, EVIOCGVERSION, &version)) return false;\n\n#ifdef EVIOCSMASK\n    static auto EviocsmaskSupported = true;\n    if (EviocsmaskSupported) {\n        Keychords::Mask mask(EV_KEY);\n        mask.SetBit(EV_KEY);\n        input_mask msg = {};\n        msg.type = EV_SYN;\n        msg.codes_size = mask.bytesize();\n        msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());\n        if (::ioctl(fd, EVIOCSMASK, &msg) == -1) {\n            PLOG(WARNING) << \"EVIOCSMASK not supported\";\n            EviocsmaskSupported = false;\n        }\n    }\n#endif\n\n    Keychords::Mask mask;\n    for (auto& [keycodes, entry] : entries_) {\n        for (auto& code : keycodes) {\n            mask.resize(code);\n            mask.SetBit(code);\n        }\n    }\n\n    current_.resize(mask.size());\n    Keychords::Mask available(mask.size());\n    auto res = ::ioctl(fd, EVIOCGBIT(EV_KEY, available.bytesize()), available.data());\n    if (res == -1) return false;\n    if (!(available & mask)) return false;\n\n#ifdef EVIOCSMASK\n    if (EviocsmaskSupported) {\n        input_mask msg = {};\n        msg.type = EV_KEY;\n        msg.codes_size = mask.bytesize();\n        msg.codes_ptr = reinterpret_cast<uintptr_t>(mask.data());\n        ::ioctl(fd, EVIOCSMASK, &msg);\n    }\n#endif\n\n    Keychords::Mask set(mask.size());\n    res = ::ioctl(fd, EVIOCGKEY(res), set.data());\n    if (res > 0) {\n        current_ |= mask & available & set;\n        LambdaCheck();\n    }\n    if (auto result = epoll_->RegisterHandler(fd, [this, fd]() { this->LambdaHandler(fd); });\n        !result.ok()) {\n        LOG(WARNING) << \"Could not register keychord epoll handler: \" << result.error();\n        return false;\n    }\n    return true;\n}\n\nvoid Keychords::GeteventOpenDevice(const std::string& device) {\n    if (registration_.count(device)) return;\n    auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDONLY | O_CLOEXEC));\n    if (fd == -1) {\n        PLOG(ERROR) << \"Can not open \" << device;\n        return;\n    }\n    if (!GeteventEnable(fd)) {\n        ::close(fd);\n    } else {\n        registration_.emplace(device, fd);\n    }\n}\n\nvoid Keychords::GeteventCloseDevice(const std::string& device) {\n    auto it = registration_.find(device);\n    if (it == registration_.end()) return;\n    auto fd = (*it).second;\n    epoll_->UnregisterHandler(fd);\n    registration_.erase(it);\n    ::close(fd);\n}\n\nvoid Keychords::InotifyHandler() {\n    unsigned char buf[512];  // History shows 32-64 bytes typical\n\n    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd_, buf, sizeof(buf)));\n    if (res < 0) {\n        PLOG(WARNING) << \"could not get event\";\n        return;\n    }\n\n    auto event_buf = buf;\n    while (static_cast<size_t>(res) >= sizeof(inotify_event)) {\n        auto event = reinterpret_cast<inotify_event*>(event_buf);\n        auto event_size = sizeof(inotify_event) + event->len;\n        if (static_cast<size_t>(res) < event_size) break;\n        if (event->len) {\n            std::string devname(kDevicePath);\n            devname += '/';\n            devname += event->name;\n            if (event->mask & IN_CREATE) {\n                GeteventOpenDevice(devname);\n            } else {\n                GeteventCloseDevice(devname);\n            }\n        }\n        res -= event_size;\n        event_buf += event_size;\n    }\n}\n\nvoid Keychords::GeteventOpenDevice() {\n    inotify_fd_ = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);\n    if (inotify_fd_ < 0) {\n        PLOG(WARNING) << \"Could not instantiate inotify for \" << kDevicePath;\n    } else if (::inotify_add_watch(inotify_fd_, kDevicePath, IN_DELETE | IN_CREATE | IN_ONLYDIR) <\n               0) {\n        PLOG(WARNING) << \"Could not add watch for \" << kDevicePath;\n        ::close(inotify_fd_);\n        inotify_fd_ = -1;\n    }\n\n    std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);\n    if (device) {\n        dirent* entry;\n        while ((entry = readdir(device.get()))) {\n            if (entry->d_name[0] == '.') continue;\n            std::string devname(kDevicePath);\n            devname += '/';\n            devname += entry->d_name;\n            GeteventOpenDevice(devname);\n        }\n    }\n\n    if (inotify_fd_ >= 0) {\n        if (auto result =\n                    epoll_->RegisterHandler(inotify_fd_, [this]() { this->InotifyHandler(); });\n            !result.ok()) {\n            LOG(WARNING) << \"Could not register keychord epoll handler: \" << result.error();\n        }\n    }\n}\n\nvoid Keychords::Register(const std::vector<int>& keycodes) {\n    if (keycodes.empty()) return;\n    entries_.try_emplace(keycodes, Entry());\n}\n\nvoid Keychords::Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler) {\n    epoll_ = epoll;\n    handler_ = std::move(handler);\n    if (entries_.size()) GeteventOpenDevice();\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/keychords.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_KEYCHORDS_H_\n#define _INIT_KEYCHORDS_H_\n\n#include <functional>\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"epoll.h\"\n\nnamespace android {\nnamespace init {\n\nclass Keychords {\n  public:\n    Keychords();\n    Keychords(const Keychords&) = delete;\n    Keychords(Keychords&&) = delete;\n    Keychords& operator=(const Keychords&) = delete;\n    Keychords& operator=(Keychords&&) = delete;\n    ~Keychords() noexcept;\n\n    void Register(const std::vector<int>& keycodes);\n    void Start(Epoll* epoll, std::function<void(const std::vector<int>&)> handler);\n\n  private:\n    // Bit management\n    class Mask {\n      public:\n        explicit Mask(size_t bit = 0);\n\n        void SetBit(size_t bit, bool value = true);\n        bool GetBit(size_t bit) const;\n\n        size_t bytesize() const;\n        void* data();\n        size_t size() const;\n        void resize(size_t bit);\n\n        operator bool() const;\n        Mask operator&(const Mask& rval) const;\n        void operator|=(const Mask& rval);\n\n      private:\n        typedef unsigned int mask_t;\n        static constexpr size_t kBitsPerByte = 8;\n\n        std::vector<mask_t> bits_;\n    };\n\n    struct Entry {\n        Entry();\n\n        bool notified;\n    };\n\n    static constexpr char kDevicePath[] = \"/dev/input\";\n\n    void LambdaCheck();\n    void LambdaHandler(int fd);\n    void InotifyHandler();\n\n    bool GeteventEnable(int fd);\n    void GeteventOpenDevice(const std::string& device);\n    void GeteventOpenDevice();\n    void GeteventCloseDevice(const std::string& device);\n\n    Epoll* epoll_;\n    std::function<void(const std::vector<int>&)> handler_;\n\n    std::map<std::string, int> registration_;\n\n    std::map<const std::vector<int>, Entry> entries_;\n\n    Mask current_;\n\n    int inotify_fd_;\n};\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/keychords_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"keychords.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <linux/input.h>\n#include <linux/uinput.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <chrono>\n#include <set>\n#include <string>\n#include <vector>\n\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <gtest/gtest.h>\n\n#include \"epoll.h\"\n\nusing namespace std::chrono_literals;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\n// This class is used to inject keys.\nclass EventHandler {\n  public:\n    EventHandler();\n    EventHandler(const EventHandler&) = delete;\n    EventHandler(EventHandler&&) noexcept;\n    EventHandler& operator=(const EventHandler&) = delete;\n    EventHandler& operator=(EventHandler&&) noexcept;\n    ~EventHandler() noexcept;\n\n    bool init();\n\n    bool send(struct input_event& e);\n    bool send(uint16_t type, uint16_t code, uint16_t value);\n    bool send(uint16_t code, bool value);\n\n  private:\n    int fd_;\n};\n\nEventHandler::EventHandler() : fd_(-1) {}\n\nEventHandler::EventHandler(EventHandler&& rval) noexcept : fd_(rval.fd_) {\n    rval.fd_ = -1;\n}\n\nEventHandler& EventHandler::operator=(EventHandler&& rval) noexcept {\n    fd_ = rval.fd_;\n    rval.fd_ = -1;\n    return *this;\n}\n\nEventHandler::~EventHandler() {\n    if (fd_ == -1) return;\n    ::ioctl(fd_, UI_DEV_DESTROY);\n    ::close(fd_);\n}\n\nbool EventHandler::init() {\n    if (fd_ != -1) return true;\n    auto fd = TEMP_FAILURE_RETRY(::open(\"/dev/uinput\", O_WRONLY | O_NONBLOCK | O_CLOEXEC));\n    if (fd == -1) return false;\n    if (::ioctl(fd, UI_SET_EVBIT, EV_KEY) == -1) {\n        ::close(fd);\n        return false;\n    }\n\n    static const struct uinput_user_dev u = {\n        .name = \"com.google.android.init.test\",\n        .id.bustype = BUS_VIRTUAL,\n        .id.vendor = 0x1AE0,   // Google\n        .id.product = 0x494E,  // IN\n        .id.version = 1,\n    };\n    if (TEMP_FAILURE_RETRY(::write(fd, &u, sizeof(u))) != sizeof(u)) {\n        ::close(fd);\n        return false;\n    }\n\n    // all keys\n    for (uint16_t i = 0; i < KEY_MAX; ++i) {\n        if (::ioctl(fd, UI_SET_KEYBIT, i) == -1) {\n            ::close(fd);\n            return false;\n        }\n    }\n    if (::ioctl(fd, UI_DEV_CREATE) == -1) {\n        ::close(fd);\n        return false;\n    }\n    fd_ = fd;\n    return true;\n}\n\nbool EventHandler::send(struct input_event& e) {\n    gettimeofday(&e.time, nullptr);\n    return TEMP_FAILURE_RETRY(::write(fd_, &e, sizeof(e))) == sizeof(e);\n}\n\nbool EventHandler::send(uint16_t type, uint16_t code, uint16_t value) {\n    struct input_event e = {.type = type, .code = code, .value = value};\n    return send(e);\n}\n\nbool EventHandler::send(uint16_t code, bool value) {\n    return (code < KEY_MAX) && init() && send(EV_KEY, code, value) && send(EV_SYN, SYN_REPORT, 0);\n}\n\nstd::string InitFds(const char* prefix, pid_t pid = getpid()) {\n    std::string ret;\n\n    std::string init_fds(\"/proc/\");\n    init_fds += std::to_string(pid) + \"/fd\";\n    std::unique_ptr<DIR, decltype(&closedir)> fds(opendir(init_fds.c_str()), closedir);\n    if (!fds) return ret;\n\n    dirent* entry;\n    while ((entry = readdir(fds.get()))) {\n        if (entry->d_name[0] == '.') continue;\n        std::string devname = init_fds + '/' + entry->d_name;\n        char buf[256];\n        auto retval = readlink(devname.c_str(), buf, sizeof(buf) - 1);\n        if ((retval < 0) || (size_t(retval) >= (sizeof(buf) - 1))) continue;\n        buf[retval] = '\\0';\n        if (!android::base::StartsWith(buf, prefix)) continue;\n        if (ret.size() != 0) ret += \",\";\n        ret += buf;\n    }\n    return ret;\n}\n\nstd::string InitInputFds() {\n    return InitFds(\"/dev/input/\");\n}\n\nstd::string InitInotifyFds() {\n    return InitFds(\"anon_inode:inotify\");\n}\n\n// NB: caller (this series of tests, or conversely the service parser in init)\n// is responsible for validation, sorting and uniqueness of the chords, so no\n// fuzzing is advised.\n\nconst std::vector<int> escape_chord = {KEY_ESC};\nconst std::vector<int> triple1_chord = {KEY_BACKSPACE, KEY_VOLUMEDOWN, KEY_VOLUMEUP};\nconst std::vector<int> triple2_chord = {KEY_VOLUMEDOWN, KEY_VOLUMEUP, KEY_BACK};\n\nconst std::vector<std::vector<int>> empty_chords;\nconst std::vector<std::vector<int>> chords = {\n        escape_chord,\n        triple1_chord,\n        triple2_chord,\n};\n\nclass TestFrame {\n  public:\n    TestFrame(const std::vector<std::vector<int>>& chords, EventHandler* ev = nullptr);\n\n    void RelaxForMs(std::chrono::milliseconds wait = 1ms);\n\n    void SetChord(int key, bool value = true);\n    void SetChords(const std::vector<int>& chord, bool value = true);\n    void ClrChord(int key);\n    void ClrChords(const std::vector<int>& chord);\n\n    bool IsOnlyChord(const std::vector<int>& chord) const;\n    bool IsNoChord() const;\n    bool IsChord(const std::vector<int>& chord) const;\n    void WaitForChord(const std::vector<int>& chord);\n\n    std::string Format() const;\n\n  private:\n    static std::string Format(const std::vector<std::vector<int>>& chords);\n\n    Epoll epoll_;\n    Keychords keychords_;\n    std::vector<std::vector<int>> keycodes_;\n    EventHandler* ev_;\n};\n\nTestFrame::TestFrame(const std::vector<std::vector<int>>& chords, EventHandler* ev) : ev_(ev) {\n    if (!epoll_.Open().ok()) return;\n    for (const auto& keycodes : chords) keychords_.Register(keycodes);\n    keychords_.Start(&epoll_, [this](const std::vector<int>& keycodes) {\n        this->keycodes_.emplace_back(keycodes);\n    });\n}\n\nvoid TestFrame::RelaxForMs(std::chrono::milliseconds wait) {\n    auto epoll_result = epoll_.Wait(wait);\n    ASSERT_RESULT_OK(epoll_result);\n}\n\nvoid TestFrame::SetChord(int key, bool value) {\n    ASSERT_TRUE(!!ev_);\n    RelaxForMs();\n    EXPECT_TRUE(ev_->send(key, value));\n}\n\nvoid TestFrame::SetChords(const std::vector<int>& chord, bool value) {\n    ASSERT_TRUE(!!ev_);\n    for (auto& key : chord) SetChord(key, value);\n    RelaxForMs();\n}\n\nvoid TestFrame::ClrChord(int key) {\n    ASSERT_TRUE(!!ev_);\n    SetChord(key, false);\n}\n\nvoid TestFrame::ClrChords(const std::vector<int>& chord) {\n    ASSERT_TRUE(!!ev_);\n    SetChords(chord, false);\n}\n\nbool TestFrame::IsOnlyChord(const std::vector<int>& chord) const {\n    auto ret = false;\n    for (const auto& keycode : keycodes_) {\n        if (keycode != chord) return false;\n        ret = true;\n    }\n    return ret;\n}\n\nbool TestFrame::IsNoChord() const {\n    return keycodes_.empty();\n}\n\nbool TestFrame::IsChord(const std::vector<int>& chord) const {\n    for (const auto& keycode : keycodes_) {\n        if (keycode == chord) return true;\n    }\n    return false;\n}\n\nvoid TestFrame::WaitForChord(const std::vector<int>& chord) {\n    for (int retry = 1000; retry && !IsChord(chord); --retry) RelaxForMs();\n}\n\nstd::string TestFrame::Format(const std::vector<std::vector<int>>& chords) {\n    std::string ret(\"{\");\n    if (!chords.empty()) {\n        ret += android::base::Join(chords.front(), ' ');\n        for (auto it = std::next(chords.begin()); it != chords.end(); ++it) {\n            ret += ',';\n            ret += android::base::Join(*it, ' ');\n        }\n    }\n    return ret + '}';\n}\n\nstd::string TestFrame::Format() const {\n    return Format(keycodes_);\n}\n\n}  // namespace\n\nTEST(keychords, not_instantiated) {\n    TestFrame test_frame(empty_chords);\n    EXPECT_TRUE(InitInotifyFds().size() == 0);\n}\n\nTEST(keychords, instantiated) {\n    // Test if a valid set of chords results in proper instantiation of the\n    // underlying mechanisms for /dev/input/ attachment.\n    TestFrame test_frame(chords);\n    EXPECT_TRUE(InitInotifyFds().size() != 0);\n}\n\nTEST(keychords, init_inotify) {\n    std::string before(InitInputFds());\n\n    TestFrame test_frame(chords);\n\n    EventHandler ev;\n    EXPECT_TRUE(ev.init());\n\n    for (int retry = 1000; retry && before == InitInputFds(); --retry) test_frame.RelaxForMs();\n    std::string after(InitInputFds());\n    EXPECT_NE(before, after);\n}\n\nTEST(keychords, key) {\n    EventHandler ev;\n    EXPECT_TRUE(ev.init());\n    TestFrame test_frame(chords, &ev);\n\n    test_frame.SetChords(escape_chord);\n    test_frame.WaitForChord(escape_chord);\n    test_frame.ClrChords(escape_chord);\n    EXPECT_TRUE(test_frame.IsOnlyChord(escape_chord))\n        << \"expected only \" << android::base::Join(escape_chord, ' ') << \" got \"\n        << test_frame.Format();\n}\n\nTEST(keychords, keys_in_series) {\n    EventHandler ev;\n    EXPECT_TRUE(ev.init());\n    TestFrame test_frame(chords, &ev);\n\n    for (auto& key : triple1_chord) {\n        test_frame.SetChord(key);\n        test_frame.ClrChord(key);\n    }\n    test_frame.WaitForChord(triple1_chord);\n    EXPECT_TRUE(test_frame.IsNoChord()) << \"expected nothing got \" << test_frame.Format();\n}\n\nTEST(keychords, keys_in_parallel) {\n    EventHandler ev;\n    EXPECT_TRUE(ev.init());\n    TestFrame test_frame(chords, &ev);\n\n    test_frame.SetChords(triple2_chord);\n    test_frame.WaitForChord(triple2_chord);\n    test_frame.ClrChords(triple2_chord);\n    EXPECT_TRUE(test_frame.IsOnlyChord(triple2_chord))\n        << \"expected only \" << android::base::Join(triple2_chord, ' ') << \" got \"\n        << test_frame.Format();\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/keyword_map.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <string>\n#include <vector>\n\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\n// Every init builtin, init service option, and ueventd option has a minimum and maximum number of\n// arguments.  These must be checked both at run time for safety and also at build time for\n// correctness in host_init_verifier.  Instead of copying and pasting the boiler plate code that\n// does this check into each function, it is abstracted in KeywordMap<>.  This class maps keywords\n// to functions and checks that the number of arguments provided falls in the correct range or\n// returns an error otherwise.\n\n// Value is the return value of Find(), which is typically either a single function or a struct with\n// additional information.\ntemplate <typename Value>\nclass KeywordMap {\n  public:\n    struct MapValue {\n        size_t min_args;\n        size_t max_args;\n        Value value;\n    };\n\n    KeywordMap() {}\n    KeywordMap(std::initializer_list<std::pair<const std::string, MapValue>> init) : map_(init) {}\n\n    Result<Value> Find(const std::vector<std::string>& args) const {\n        if (args.empty()) return Error() << \"Keyword needed, but not provided\";\n\n        auto& keyword = args[0];\n        auto num_args = args.size() - 1;\n\n        auto result_it = map_.find(keyword);\n        if (result_it == map_.end()) {\n            return Errorf(\"Invalid keyword '{}'\", keyword);\n        }\n\n        auto result = result_it->second;\n\n        auto min_args = result.min_args;\n        auto max_args = result.max_args;\n        if (min_args == max_args && num_args != min_args) {\n            return Errorf(\"{} requires {} argument{}\", keyword, min_args,\n                          (min_args > 1 || min_args == 0) ? \"s\" : \"\");\n        }\n\n        if (num_args < min_args || num_args > max_args) {\n            if (max_args == std::numeric_limits<decltype(max_args)>::max()) {\n                return Errorf(\"{} requires at least {} argument{}\", keyword, min_args,\n                              min_args > 1 ? \"s\" : \"\");\n            } else {\n                return Errorf(\"{} requires between {} and {} arguments\", keyword, min_args,\n                              max_args);\n            }\n        }\n\n        return result.value;\n    }\n\n  private:\n    std::map<std::string, MapValue> map_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/libprefetch/prefetch/Android.bp",
    "content": "//\n// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_library_rlib {\n    name: \"libprefetch_rs\",\n    crate_name: \"prefetch_rs\",\n    srcs: [\"src/lib.rs\"],\n    rustlibs: [\n        \"libandroid_logger\",\n        \"libargh\",\n        \"libchrono\",\n        \"libcrc32fast\",\n        \"libcsv\",\n        \"liblibc\",\n        \"liblog_rust\",\n        \"liblru_cache\",\n        \"libnix\",\n        \"librand\",\n        \"librayon\",\n        \"libregex\",\n        \"libserde_cbor\",\n        \"libserde_json\",\n        \"libserde\",\n        \"libthiserror\",\n        \"libwalkdir\",\n        \"librustutils\",\n    ],\n    prefer_rlib: true,\n    features: [\n        \"derive\",\n        \"error-context\",\n        \"help\",\n        \"std\",\n        \"usage\",\n        \"use_argh\",\n    ],\n}\n\nrust_binary {\n    name: \"prefetch\",\n    crate_name: \"prefetch\",\n    srcs: [\"src/main.rs\"],\n    rustlibs: [\n        \"libprefetch_rs\",\n        \"liblog_rust\",\n        \"libandroid_logger\",\n    ],\n    prefer_rlib: true,\n    features: [\n        \"default\",\n        \"derive\",\n        \"error-context\",\n        \"help\",\n        \"std\",\n        \"usage\",\n        \"use_argh\",\n    ],\n    init_rc: [\n        \"prefetch.rc\",\n    ],\n}\n\n// TODO: Add rust_test to enable unit testing - b/378554334\n"
  },
  {
    "path": "init/libprefetch/prefetch/Cargo.toml",
    "content": "[package]\nname = \"prefetch\"\nversion = \"0.1.0\"\nedition = \"2018\"\ndefault-run = \"prefetch\"\n\n[lib]\nname = \"prefetch_rs\"\npath = \"src/lib.rs\"\n\n[[bin]]\nname = \"prefetch\"\npath = \"src/main.rs\"\n\n[features]\ndefault = [\"use_argh\"]\nuse_argh = [\"argh\"]\n\n[dependencies]\nargh = { version = \"0.1.10\", optional = true }\nchrono = { version = \"=0.4.19\", features = [\"serde\"] }\ncrc32fast = \"1.2.1\"\ncsv = \"=1.1.6\"\nlibc = \"0.2.82\"\nlog = \"=0.4.14\"\nlru-cache = \"0.1.2\"\nmemchr = \"=2.3.4\"\nnix = {version = \"0.28\", features = [\"fs\", \"time\", \"feature\", \"mman\", \"uio\"]}\nproc-macro2 = \"=1.0.26\"\nquote = \"=1.0.9\"\nrand = \"0.8.3\"\nrayon = \"=1.5.0\"\nrayon-core = \"=1.9.0\"\nregex = \"1.4.5\"\nserde = { version = \"*\", features = [\"derive\"] }\nserde_cbor = \"0.11.2\"\nserde_derive = \"=1.0.123\"\nserde_json = \"=1.0.62\"\nthiserror = \"=1.0.24\"\nthiserror-impl = \"1.0.24\"\nwalkdir = \"2.3.2\"\n\n# crates required for android builds\n[target.'cfg(target_os = \"android\")'.dependencies]\nandroid_logger = \"0.10.1\"\n\n# crates not present in android builds\n[target.'cfg(not(target_os = \"android\"))'.dependencies]\nbincode = \"=0.9.0\"\nenv_logger = \"=0.8.4\"\ntempfile = \"2.2.0\"\n"
  },
  {
    "path": "init/libprefetch/prefetch/OWNERS",
    "content": "akailash@google.com\nauradkar@google.com\ntakayas@google.com\n"
  },
  {
    "path": "init/libprefetch/prefetch/prefetch.rc",
    "content": "# Reads data from disk in advance and populates page cache\n# to speed up subsequent disk access.\n#\n# Record:\n#   start by `start prefetch_record` at appropriate timing.\n#   stop by setting `prefetch_boot.record_stop` to 1.\n#   set --duration to only capture for a certain duration instead.\n#\n# Replay:\n#   start by `start prefetch_replay` at appropriate timing.\n#   it will depend on several files generated from record.\n#\n#   replay is I/O intensive. make sure you pick appropriate\n#   timing to run each, so that you can maximize the page cache\n#   hit for subsequent disk access.\n#\n# Example:\n#   on early-init && property:ro.prefetch_boot.enabled=true\n#     start prefetch_replay\n#\n#   on init && property:ro.prefetch_boot.enabled=true\n#     start prefetch_record\n#\n#   on property:sys.boot_completed=1 && property:ro.prefetch_boot.enabled=true\n#     setprop prefetch_boot.record_stop 1\n\nservice prefetch_record /system/bin/prefetch record --duration ${ro.prefetch_boot.duration_s:-0}\n    user root\n    group root system\n    disabled\n    oneshot\n\nservice prefetch_replay /system/bin/prefetch replay --io-depth ${ro.prefetch_boot.io_depth:-2} --max-fds ${ro.prefetch_boot.max_fds:-1024}\n    user root\n    group root system\n    disabled\n    oneshot\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/arch/android.rs",
    "content": "use crate::Error;\nuse crate::RecordArgs;\nuse log::warn;\nuse std::fs::File;\nuse std::fs::OpenOptions;\nuse std::io::Write;\nuse std::path::Path;\nuse std::time::Duration;\n\nuse rustutils::system_properties::error::PropertyWatcherError;\nuse rustutils::system_properties::PropertyWatcher;\n\nconst PREFETCH_RECORD_PROPERTY_STOP: &str = \"prefetch_boot.record_stop\";\n\nfn is_prefetch_enabled() -> Result<bool, Error> {\n    rustutils::system_properties::read_bool(\"ro.prefetch_boot.enabled\", false).map_err(|e| {\n        Error::Custom { error: format!(\"Failed to read ro.prefetch_boot.enabled: {}\", e) }\n    })\n}\n\nfn wait_for_property_true(\n    property_name: &str,\n    timeout: Option<Duration>,\n) -> Result<(), PropertyWatcherError> {\n    let mut prop = PropertyWatcher::new(property_name)?;\n    prop.wait_for_value(\"1\", timeout)?;\n    Ok(())\n}\n\n/// Wait for record to stop\npub fn wait_for_record_stop() {\n    wait_for_property_true(PREFETCH_RECORD_PROPERTY_STOP, None).unwrap_or_else(|e| {\n        warn!(\"failed to wait for {} with error: {}\", PREFETCH_RECORD_PROPERTY_STOP, e)\n    });\n}\n\n/// Checks if we can perform replay phase.\n/// Ensure that the pack file exists and is up-to-date, returns false otherwise.\npub fn can_perform_replay(pack_path: &Path, fingerprint_path: &Path) -> Result<bool, Error> {\n    if !is_prefetch_enabled()? {\n        return Ok(false);\n    }\n\n    if !pack_path.exists() || !fingerprint_path.exists() {\n        return Ok(false);\n    }\n\n    let saved_fingerprint = std::fs::read_to_string(fingerprint_path)?;\n\n    let current_device_fingerprint = rustutils::system_properties::read(\"ro.build.fingerprint\")\n        .map_err(|e| Error::Custom {\n            error: format!(\"Failed to read ro.build.fingerprint: {}\", e),\n        })?;\n\n    Ok(current_device_fingerprint.is_some_and(|fp| fp == saved_fingerprint.trim()))\n}\n\n/// Checks if we can perform record phase.\n/// Ensure that following conditions hold:\n///   - File specified in ready_path exists. otherwise, create a new file and return false.\n///   - can_perform_replay is false.\npub fn ensure_record_is_ready(\n    ready_path: &Path,\n    pack_path: &Path,\n    fingerprint_path: &Path,\n) -> Result<bool, Error> {\n    if !is_prefetch_enabled()? {\n        return Ok(false);\n    }\n\n    if !ready_path.exists() {\n        File::create(ready_path)\n            .map_err(|_| Error::Custom { error: \"File Creation failed\".to_string() })?;\n\n        return Ok(false);\n    }\n\n    let can_replay = can_perform_replay(pack_path, fingerprint_path)?;\n    Ok(!can_replay)\n}\n\n/// Write build finger print to associate prefetch pack file\npub fn write_build_fingerprint(args: &RecordArgs) -> Result<(), Error> {\n    let mut build_fingerprint_file = OpenOptions::new()\n        .write(true)\n        .create(true)\n        .truncate(true)\n        .open(&args.build_fingerprint_path)\n        .map_err(|source| Error::Create {\n            source,\n            path: args.build_fingerprint_path.to_str().unwrap().to_owned(),\n        })?;\n\n    let device_build_fingerprint =\n        rustutils::system_properties::read(\"ro.build.fingerprint\").unwrap_or_default();\n    let device_build_fingerprint = device_build_fingerprint.unwrap_or_default();\n\n    build_fingerprint_file.write_all(device_build_fingerprint.as_bytes())?;\n    build_fingerprint_file.sync_all()?;\n\n    Ok(())\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/args/args_argh.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse std::{option::Option, path::PathBuf, result::Result::Ok, str::FromStr};\n\nuse argh::FromArgs;\nuse serde::Deserialize;\n\nuse crate::args::DEFAULT_EXIT_ON_ERROR;\nuse crate::args::DEFAULT_IO_DEPTH;\nuse crate::args::DEFAULT_MAX_FDS;\nuse crate::Error;\n\n/// prefetch-rs\n#[derive(Eq, PartialEq, Debug, Default, FromArgs)]\npub struct MainArgs {\n    /// subcommands\n    #[argh(subcommand)]\n    pub nested: SubCommands,\n}\n\n/// Sub commands for prefetch functions\n#[derive(Eq, PartialEq, Debug, FromArgs)]\n#[argh(subcommand)]\npub enum SubCommands {\n    /// Records prefetch data.\n    Record(RecordArgs),\n    /// Replays from prefetch data\n    Replay(ReplayArgs),\n    /// Dump prefetch data in human readable format\n    Dump(DumpArgs),\n}\n\n#[cfg(target_os = \"android\")]\nfn default_ready_path() -> PathBuf {\n    PathBuf::from(\"/metadata/prefetch/prefetch_ready\")\n}\n\n#[cfg(target_os = \"android\")]\nfn default_build_finger_print_path() -> PathBuf {\n    PathBuf::from(\"/metadata/prefetch/build_finger_print\")\n}\n\nimpl Default for SubCommands {\n    fn default() -> Self {\n        Self::Dump(DumpArgs::default())\n    }\n}\n\nfn default_path() -> PathBuf {\n    PathBuf::from(\"/metadata/prefetch/prefetch.pack\")\n}\n\nfn parse_tracing_instance(value: &str) -> Result<Option<String>, String> {\n    Ok(Some(value.to_string()))\n}\n\n#[derive(Eq, PartialEq, Debug, Default, FromArgs)]\n/// Records prefect data.\n#[argh(subcommand, name = \"record\")]\npub struct RecordArgs {\n    /// duration in seconds to record the data\n    ///\n    /// On Android, if duration count is set to zero, recording\n    /// will continue until the property sys.boot_completed = 1.\n    #[argh(option)]\n    pub duration: u16,\n\n    /// file path where the records will be written to\n    ///\n    /// A new file is created at the given path. If the path exists, it\n    /// will be overwritten\n    #[argh(option, default = \"default_path()\")]\n    pub path: PathBuf,\n\n    /// when set an intermediate file will be created that provides more information\n    /// about collected data.\n    #[argh(option, default = \"false\")]\n    pub debug: bool,\n\n    /// file path where the intermediate file will be written to\n    ///\n    /// A new file is created at the given path. Errors out if the file\n    /// already exists.\n    #[argh(option)]\n    pub int_path: Option<PathBuf>,\n\n    /// size of the trace buffer which holds trace events. We need larger\n    /// buffer on a system that has faster disks or has large number of events\n    /// enabled. Defaults to TRACE_BUFFER_SIZE_KIB KiB.\n    #[argh(option, long = \"trace-buffer-size\")]\n    pub trace_buffer_size_kib: Option<u64>,\n\n    /// trace subsystem to use. \"mem\" subsystem is set by default.\n    #[argh(option, default = \"Default::default()\")]\n    pub tracing_subsystem: TracerType,\n\n    /// if true enables all the needed trace events. And at the end it restores\n    /// the values of those events.\n    /// If false, assumes that user has setup the needed trace events.\n    #[argh(option, default = \"true\")]\n    pub setup_tracing: bool,\n\n    /// if specified, works on a tracing instance (like /sys/kernel/tracing/instance/my_instance)\n    /// rather than using on shared global instance (i.e. /sys/kernel/tracing).\"\n    #[argh(\n        option,\n        default = \"Some(\\\"prefetch\\\".to_string())\",\n        from_str_fn(parse_tracing_instance)\n    )]\n    pub tracing_instance: Option<String>,\n\n    #[cfg(target_os = \"android\")]\n    /// store build_finger_print to tie the pack format\n    #[argh(option, default = \"default_build_finger_print_path()\")]\n    pub build_fingerprint_path: PathBuf,\n\n    #[cfg(target_os = \"android\")]\n    /// file path to check if prefetch_ready is present.\n    ///\n    /// A new file is created at the given path if it's not present.\n    #[argh(option, default = \"default_ready_path()\")]\n    pub ready_path: PathBuf,\n}\n\n/// Type of tracing subsystem to use.\n#[derive(Deserialize, Clone, Eq, PartialEq, Debug)]\npub enum TracerType {\n    /// mem tracing subsystem relies on when a file's in-memory page gets added to the fs cache.\n    Mem,\n}\n\nimpl FromStr for TracerType {\n    type Err = Error;\n    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n        Ok(match s.to_lowercase().as_str() {\n            \"mem\" => Self::Mem,\n            _ => {\n                return Err(Error::InvalidArgs {\n                    arg_name: \"tracing_subsystem\".to_owned(),\n                    arg_value: s.to_owned(),\n                    error: \"unknown value\".to_owned(),\n                })\n            }\n        })\n    }\n}\n\nimpl Default for TracerType {\n    fn default() -> Self {\n        Self::Mem\n    }\n}\n\n#[derive(Eq, PartialEq, Debug, Default, FromArgs)]\n/// Prefetch data from the recorded file.\n#[argh(subcommand, name = \"replay\")]\npub struct ReplayArgs {\n    /// file path from where the records will be read\n    #[argh(option, default = \"default_path()\")]\n    pub path: PathBuf,\n\n    /// IO depth. Number of IO that can go in parallel.\n    #[argh(option, long = \"io-depth\", default = \"DEFAULT_IO_DEPTH\")]\n    pub io_depth: u16,\n\n    /// max number of open fds to cache\n    #[argh(option, arg_name = \"max-fds\", default = \"DEFAULT_MAX_FDS\")]\n    pub max_fds: u16,\n\n    /// if true, command exits on encountering any error.\n    ///\n    /// This defaults to false as there is not harm prefetching if we encounter\n    /// non-fatal errors.\n    #[argh(option, default = \"DEFAULT_EXIT_ON_ERROR\")]\n    pub exit_on_error: bool,\n\n    /// file path from where the prefetch config file will be read\n    #[argh(option, default = \"PathBuf::new()\")]\n    pub config_path: PathBuf,\n\n    #[cfg(target_os = \"android\")]\n    /// store build_finger_print to tie the pack format\n    #[argh(option, default = \"default_build_finger_print_path()\")]\n    pub build_fingerprint_path: PathBuf,\n}\n\n/// dump records file in given format\n#[derive(Eq, PartialEq, Debug, Default, FromArgs)]\n#[argh(subcommand, name = \"dump\")]\npub struct DumpArgs {\n    /// file path from where the records will be read\n    #[argh(option)]\n    pub path: PathBuf,\n    /// output format. One of json or csv.\n    /// Note: In csv format, few fields are excluded from the output.\n    #[argh(option)]\n    pub format: OutputFormat,\n}\n\n#[derive(Deserialize, Eq, PartialEq, Debug)]\npub enum OutputFormat {\n    Json,\n    Csv,\n}\n\nimpl FromStr for OutputFormat {\n    type Err = Error;\n    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {\n        Ok(match s.to_lowercase().as_str() {\n            \"csv\" => Self::Csv,\n            \"json\" => Self::Json,\n            _ => {\n                return Err(Error::InvalidArgs {\n                    arg_name: \"format\".to_owned(),\n                    arg_value: s.to_owned(),\n                    error: \"unknown value\".to_owned(),\n                })\n            }\n        })\n    }\n}\n\nimpl Default for OutputFormat {\n    fn default() -> Self {\n        Self::Json\n    }\n}\n\n/// Build args struct from command line arguments\npub fn args_from_env() -> MainArgs {\n    argh::from_env()\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/args.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npub(crate) static DEFAULT_IO_DEPTH: u16 = 2;\npub(crate) static DEFAULT_MAX_FDS: u16 = 128;\npub(crate) static DEFAULT_EXIT_ON_ERROR: bool = false;\n\nmod args_argh;\nuse args_argh as args_internal;\n\nuse std::path::Path;\nuse std::path::PathBuf;\nuse std::process::exit;\n\npub use args_internal::OutputFormat;\npub use args_internal::ReplayArgs;\npub use args_internal::TracerType;\npub use args_internal::{DumpArgs, MainArgs, RecordArgs, SubCommands};\nuse serde::Deserialize;\nuse serde::Serialize;\n\nuse crate::Error;\nuse log::error;\n\n// Deserialized form of the config file\n#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]\npub struct ConfigFile {\n    // Files to be excluded in prefetch. These files might have been\n    // added in the record file while recording,but we do not want to\n    // replay these files. These can be two types of files:\n    // 1) installation-specific files (e.g. files in /data) and\n    // 2) large files which we do not want to load in replay (e.g. APK files).\n    pub files_to_exclude_regex: Vec<String>,\n    // Files that are not in the record file, but need to be loaded during replay\n    pub additional_replay_files: Vec<String>,\n}\n\nfn verify_and_fix(args: &mut MainArgs) -> Result<(), Error> {\n    match &mut args.nested {\n        SubCommands::Record(arg) => {\n            if arg.debug && arg.int_path.is_none() {\n                arg.int_path = Some(PathBuf::from(format!(\"{}.int\", arg.path.to_str().unwrap())));\n            }\n\n            if let Some(p) = &arg.int_path {\n                ensure_path_doesnt_exist(p)?;\n            }\n        }\n        SubCommands::Replay(arg) => {\n            ensure_path_exists(&arg.path)?;\n            if !arg.config_path.as_os_str().is_empty() {\n                ensure_path_exists(&arg.config_path)?;\n            }\n        }\n        SubCommands::Dump(arg) => {\n            ensure_path_exists(&arg.path)?;\n        }\n    }\n    Ok(())\n}\n\n/// Returns error if the given path at `p` exist.\npub(crate) fn ensure_path_doesnt_exist(p: &Path) -> Result<(), Error> {\n    if p.exists() {\n        Err(Error::InvalidArgs {\n            arg_name: \"path\".to_string(),\n            arg_value: p.display().to_string(),\n            error: \"Path already exists\".to_string(),\n        })\n    } else {\n        Ok(())\n    }\n}\n\n/// Returns error if the given path at `p` doesn't exist.\npub(crate) fn ensure_path_exists(p: &Path) -> Result<(), Error> {\n    if p.is_file() {\n        Ok(())\n    } else {\n        Err(Error::InvalidArgs {\n            arg_name: \"path\".to_string(),\n            arg_value: p.display().to_string(),\n            error: \"Path does not exist\".to_string(),\n        })\n    }\n}\n\n/// Builds `MainArgs` from command line arguments. On error prints error/help message\n/// and exits.\npub fn args_from_env() -> MainArgs {\n    let mut args = args_internal::args_from_env();\n    if let Err(e) = verify_and_fix(&mut args) {\n        error!(\"failed to verify args: {}\", e);\n        exit(1);\n    }\n    args\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/error.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse thiserror::Error;\n\nuse crate::{format::FileId, InodeInfo};\n\n/// Enumerates all possible errors returned by this library.\n#[derive(Debug, Error)]\npub enum Error {\n    /// Represents a failure to open a file.\n    #[error(\"Open error: {path}: {source}\")]\n    Open {\n        /// The IO error\n        source: std::io::Error,\n        /// Path on which the operation failed.\n        path: String,\n    },\n\n    /// Represents a failure to create a file.\n    #[error(\"Create error. {path} {source}\")]\n    Create {\n        /// The IO error\n        source: std::io::Error,\n        /// Path on which the operation failed.\n        path: String,\n    },\n\n    /// Represents a failure to read trace file.\n    #[error(\"Read error. {error}\")]\n    Read {\n        /// Detailed error message.\n        error: String,\n    },\n\n    /// Represents a failure to write to a file.\n    #[error(\"Write error. {source}\")]\n    Write {\n        /// The IO error\n        source: std::io::Error,\n\n        /// file path\n        path: String,\n    },\n\n    /// Represents a failure to delete a file.\n    #[error(\"Delete error. {path} {source}\")]\n    Delete {\n        /// The IO error\n        source: std::io::Error,\n        /// Path on which the operation failed.\n        path: String,\n    },\n\n    /// Represents a failure to stat a file.\n    #[error(\"Stat error. {path} {source}\")]\n    Stat {\n        /// The IO error\n        source: std::io::Error,\n        /// Path on which the operation failed.\n        path: String,\n    },\n\n    /// Represents a failure to stat a file.\n    #[error(\"clone failed. {id} {source}\")]\n    FileClone {\n        /// The IO error\n        source: std::io::Error,\n        /// File id for which we could not clone the file.\n        id: FileId,\n    },\n\n    /// Represents a failure to mmap a file.\n    #[error(\"mmap failed. {path} {error}\")]\n    Mmap {\n        /// Detailed error message.\n        error: String,\n        /// Path on which the operation failed.\n        path: String,\n    },\n\n    /// Represents a failure to munmap a file.\n    #[error(\"munmap failed. {length} {error}\")]\n    Munmap {\n        /// Detailed error message.\n        error: String,\n        /// Size of file which this munmap failed\n        length: usize,\n    },\n\n    /// Represents all other cases of `std::io::Error`.\n    ///\n    #[error(transparent)]\n    IoError(\n        /// The IO error\n        #[from]\n        std::io::Error,\n    ),\n\n    /// Represents a failure to map FileId to path\n    ///\n    #[error(\"Failed to map id to path: {id}\")]\n    IdNoFound {\n        /// File id for which path lookup failed.\n        id: FileId,\n    },\n\n    /// Indicates that the file is skipped for prefetching\n    /// because it is in the exclude files list.\n    ///\n    #[error(\"Skipped prefetching file from path: {path}\")]\n    SkipPrefetch {\n        /// Path to file for which prefetching is skipped.\n        path: String,\n    },\n\n    /// Represents spurious InodeInfo or missing Record.\n    ///\n    #[error(\n        \"Stale inode(s) info found.\\n\\\n            missing_file_ids: {missing_file_ids:#?}\\n\\\n            stale_inodes: {stale_inodes:#?} \\n\\\n            missing_paths:{missing_paths:#?}\"\n    )]\n    StaleInode {\n        /// FileIds for which InodeInfo is missing.\n        missing_file_ids: Vec<FileId>,\n\n        /// InodeInfos for which no records exist.\n        stale_inodes: Vec<InodeInfo>,\n\n        /// InodeInfos in which no paths were found.\n        missing_paths: Vec<InodeInfo>,\n    },\n\n    /// Represents a failure to serialize records file.\n    #[error(\"Serialize error: {error}\")]\n    Serialize {\n        /// Detailed error message.\n        error: String,\n    },\n\n    /// Represents a failure to deserialize records file.\n    #[error(\"Deserialize error: {error}\")]\n    Deserialize {\n        /// Detailed error message.\n        error: String,\n    },\n\n    /// Represents a failure from thread pool.\n    #[error(\"Thread pool error: {error}\")]\n    ThreadPool {\n        /// Detailed error message.\n        error: String,\n    },\n\n    /// Represents a failure to setup file.\n    #[error(\"Failed to setup prefetch: {error}\")]\n    Custom {\n        /// Detailed error message.\n        error: String,\n    },\n\n    /// Represents a failure to parse args.\n    #[error(\"Failed to parse arg:{arg_name} value:{arg_value} error:{error}\")]\n    InvalidArgs {\n        /// Arg name.\n        arg_name: String,\n\n        /// Arg value.\n        arg_value: String,\n\n        /// Detailed error message.\n        error: String,\n    },\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/format.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse std::cmp::{max, min};\nuse std::collections::{BTreeMap, HashMap, HashSet};\nuse std::fmt;\nuse std::fmt::Display;\nuse std::fs::{File, Metadata, OpenOptions};\nuse std::hash::Hash;\nuse std::io::Write;\nuse std::ops::{Deref, DerefMut};\nuse std::os::unix::fs::MetadataExt;\nuse std::time::SystemTime;\n\nuse crc32fast::Hasher;\nuse log::debug;\nuse regex::Regex;\nuse serde::Deserializer;\nuse serde::Serialize;\nuse serde::{Deserialize, Serializer};\n\nuse crate::error::Error;\n\nstatic MAGIC_UUID: [u8; 16] = [\n    0x10, 0x54, 0x3c, 0xb8, 0x60, 0xdb, 0x49, 0x45, 0xa1, 0xd5, 0xde, 0xa7, 0xd2, 0x3b, 0x05, 0x49,\n];\nstatic MAJOR_VERSION: u16 = 0;\nstatic MINOR_VERSION: u16 = 1;\n\n/// Represents inode number which is unique within a filesystem.\npub(crate) type InodeNumber = u64;\n\n/// Represents device number which is unique for given block device.\npub(crate) type DeviceNumber = u64;\n\n/// Convenience name for string that represents a path.\npub(crate) type PathString = String;\n\n/// Represents unique file id across filesystems.\n#[derive(Clone, Debug, Deserialize, Eq, Hash, Default, PartialEq, PartialOrd, Ord, Serialize)]\npub struct FileId(pub u64);\n\nimpl Display for FileId {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        self.0.fmt(f)\n    }\n}\n\nfn serialize_hashmap<S, K: Ord + Serialize + Clone, V: Serialize + Clone>(\n    value: &HashMap<K, V>,\n    serializer: S,\n) -> Result<S::Ok, S::Error>\nwhere\n    S: Serializer,\n{\n    let mut btree = BTreeMap::new();\n    for (k, v) in value {\n        btree.insert(k.clone(), v.clone());\n    }\n    btree.serialize(serializer)\n}\n\n#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]\npub(crate) struct SerializableHashMap<\n    K: Ord + Serialize + Clone + Hash + PartialEq,\n    V: Serialize + Clone,\n> {\n    #[serde(serialize_with = \"serialize_hashmap\")]\n    pub map: HashMap<K, V>,\n}\n\nimpl<K, V> Deref for SerializableHashMap<K, V>\nwhere\n    K: Ord + Serialize + Clone + Hash + PartialEq,\n    V: Serialize + Clone,\n{\n    type Target = HashMap<K, V>;\n    fn deref(&self) -> &Self::Target {\n        &self.map\n    }\n}\n\nimpl<K, V> DerefMut for SerializableHashMap<K, V>\nwhere\n    K: Ord + Serialize + Clone + Hash + PartialEq,\n    V: Serialize + Clone,\n{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.map\n    }\n}\n\n/// The InodeInfo is unique per (device, inode) combination. It is\n/// used to verify that we are prefetching a file for which we generated\n/// the records for.\n/// `Record` refers to this information with a unique `FileId`.\n#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]\npub struct InodeInfo {\n    // Inode number of the file.\n    pub(crate) inode_number: InodeNumber,\n\n    // File size in bytes.\n    pub(crate) file_size: u64,\n\n    // Helps to get to a file from a Record. The field is used to get to the file\n    // that needs to be prefetched.\n    //\n    // This struct is built by getting data from trace lines and querying filesystem\n    // for other fields about the file/inode.\n    //\n    // One instance per file to be prefetched. A file/inode can have multiple paths.\n    // We store multiple paths so that we can still get to it if some of the\n    // paths get deleted.\n    //\n    // See comments for `Record`.\n    #[serde(deserialize_with = \"check_inode_info_paths\")]\n    pub(crate) paths: Vec<PathString>,\n\n    // Block device number on which the file is located.\n    pub(crate) device_number: DeviceNumber,\n}\n\nimpl InodeInfo {\n    /// Returns InodeInfo.\n    pub fn new(\n        inode_number: InodeNumber,\n        file_size: u64,\n        paths: Vec<String>,\n        device_number: DeviceNumber,\n    ) -> Self {\n        Self { inode_number, file_size, paths, device_number }\n    }\n}\n\n// Helps us check block alignment.\n//\n// A records file can have multiple FsInfos.\n#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]\npub struct FsInfo {\n    // This is filesystem block size and is not underlying device's block size\n    pub(crate) block_size: u64,\n}\n\n/// Prefetch record.\n/// Each record translates to one filesystem `read()` request.\n///\n/// Tracer builds `Record` by parsing trace lines or by querying filesystem.\n///\n/// Multiple `Record`s can belong to a single InodeInfo. For example if there were two\n/// reads for file `/data/my.apk` which is assigned FileId 10 at offsets 0 and 8k of length\n/// 1 byte each then we will have two `Records` in `RecordsFile` that look like\n/// `Record {file_id: 10, offset: 0, length: 1, timestamp: t1}`\n/// `Record {file_id: 10, offset: 8192, length: 1, timestamp: t2}`\n#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]\npub struct Record {\n    /// Points to the file that should be fetched./ file_id is unique per `InodeInfo`\n    /// in a `RecordsFile`\n    pub file_id: FileId,\n\n    /// start offset to fetch data from. This is FsInfo.block_size aligned.\n    pub offset: u64,\n\n    /// length of the read. This is generally rounded up to Fs.Info.block_size\n    /// except when the rounding up crosses `InodeInfo.file_size`\n    pub length: u64,\n\n    /// Timestamp in nanoseconds since the start when the data was loaded.\n    pub timestamp: u64,\n}\n\nimpl Record {\n    /// Returns a new record if two records belong to same file and overlap.\n    fn overlaps(&self, other: &Self) -> Option<Self> {\n        if self.file_id == other.file_id {\n            let self_start = self.offset;\n            let self_end = self.offset + self.length;\n            let other_start = other.offset;\n            let other_end = other.offset + other.length;\n\n            if (self_start <= other_end) && (self_end >= other_start) {\n                let offset = min(self_start, other_start);\n                let length = max(self_end, other_end) - offset;\n                return Some(Self {\n                    file_id: self.file_id.clone(),\n                    offset,\n                    length,\n                    timestamp: min(self.timestamp, other.timestamp),\n                });\n            }\n        }\n        None\n    }\n}\n\nfn group_record_by_file_id(records: Vec<Record>) -> Vec<Record> {\n    let mut map: HashMap<FileId, BTreeMap<u64, Record>> = HashMap::new();\n\n    for record in &records {\n        let recs = map.entry(record.file_id.clone()).or_default();\n        recs.entry(record.offset).or_insert_with(|| record.clone());\n    }\n\n    let mut grouped = vec![];\n    for record in &records {\n        if let Some(inode) = map.get(&record.file_id) {\n            for rec in inode.values() {\n                grouped.push(rec.clone());\n            }\n        }\n        let _ = map.remove(&record.file_id);\n    }\n\n    grouped\n}\n\n/// When records are coalesced, because their file ids match and IO offsets overlap, the least\n/// timestamp of the coalesced records is retained.\npub(crate) fn coalesce_records(records: Vec<Record>, group_by_file_id: bool) -> Vec<Record> {\n    let records = if group_by_file_id { group_record_by_file_id(records) } else { records };\n\n    let mut coalesced = vec![];\n    let mut current: Option<Record> = None;\n    for r in records {\n        current = match current {\n            None => Some(r),\n            Some(c) => {\n                let merged = c.overlaps(&r);\n                match merged {\n                    None => {\n                        coalesced.push(c);\n                        Some(r)\n                    }\n                    Some(m) => Some(m),\n                }\n            }\n        }\n    }\n    if let Some(r) = current {\n        coalesced.push(r);\n    }\n    coalesced\n}\n\n// Records file header.\n#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]\npub struct Header {\n    /// magic number as uuid to identify the header/format.\n    #[serde(deserialize_with = \"check_magic\")]\n    magic: [u8; 16],\n\n    // major version number.\n    #[serde(deserialize_with = \"check_major_number\")]\n    major_number: u16,\n\n    // minor version number.\n    #[serde(deserialize_with = \"check_minor_number\")]\n    minor_number: u16,\n\n    /// timestamp when the records file was generated.\n    date: SystemTime,\n\n    /// Checksum of the `RecordsFile` with `digest` being empty vector.\n    digest: u32,\n}\n\nfn check_version_number<'de, D>(\n    deserializer: D,\n    expected: u16,\n    version_type: &str,\n) -> Result<u16, D::Error>\nwhere\n    D: Deserializer<'de>,\n{\n    let found = u16::deserialize(deserializer)?;\n    if expected != found {\n        return Err(serde::de::Error::custom(format!(\n            \"Failed to parse {} version. Expected: {} Found: {}\",\n            version_type, expected, found\n        )));\n    }\n    Ok(found)\n}\n\nfn check_major_number<'de, D>(deserializer: D) -> Result<u16, D::Error>\nwhere\n    D: Deserializer<'de>,\n{\n    check_version_number(deserializer, MAJOR_VERSION, \"major\")\n}\n\nfn check_minor_number<'de, D>(deserializer: D) -> Result<u16, D::Error>\nwhere\n    D: Deserializer<'de>,\n{\n    check_version_number(deserializer, MINOR_VERSION, \"minor\")\n}\n\nfn check_magic<'de, D>(deserializer: D) -> Result<[u8; 16], D::Error>\nwhere\n    D: Deserializer<'de>,\n{\n    let found: [u8; 16] = <[u8; 16]>::deserialize(deserializer)?;\n    if found != MAGIC_UUID {\n        return Err(serde::de::Error::custom(format!(\n            \"Failed to parse magic number. Expected: {:?} Found: {:?}\",\n            MAGIC_UUID, found\n        )));\n    }\n    Ok(found)\n}\n\nfn check_inode_info_paths<'de, D>(deserializer: D) -> Result<Vec<PathString>, D::Error>\nwhere\n    D: Deserializer<'de>,\n{\n    let parsed: Vec<PathString> = Vec::deserialize(deserializer)?;\n    if parsed.is_empty() {\n        return Err(serde::de::Error::custom(\"No paths found for in InodeInfo\"));\n    }\n    Ok(parsed)\n}\n\n// Helper inner struct of RecordsFile meant to verify checksum.\n#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]\npub(crate) struct RecordsFileInner {\n    // One instance per mounted block device.\n    pub(crate) filesystems: SerializableHashMap<DeviceNumber, FsInfo>,\n\n    /// Helps to get to a file path from a given `FileId`.\n    /// One instance per file to be prefetched.\n    pub(crate) inode_map: SerializableHashMap<FileId, InodeInfo>,\n\n    /// Helps to get to a file and offset to be replayed..\n    ///\n    // The records are chronologically arranged meaning the data that\n    // needs first is at the beginning of the vector and the data that\n    // needs last is at the end.\n    //\n    // One instance per part of the file that needs to be prefetched.\n    pub records: Vec<Record>,\n}\n\n/// Deserialized form of records file.\n#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq)]\n#[serde(remote = \"Self\")]\npub struct RecordsFile {\n    /// Helps the prefetch tool to parse rest of the file\n    pub header: Header,\n\n    /// Helps the prefetch tool to verify checksum.\n    pub(crate) inner: RecordsFileInner,\n}\n\nimpl RecordsFile {\n    /// Given file id, looks up path of the file and returns open File handle.\n    pub fn open_file(&self, id: FileId, exclude_files_regex: &[Regex]) -> Result<File, Error> {\n        if let Some(inode) = self.inner.inode_map.get(&id) {\n            let path = inode.paths.first().unwrap();\n\n            for regex in exclude_files_regex {\n                if regex.is_match(path) {\n                    return Err(Error::SkipPrefetch { path: path.to_owned() });\n                }\n            }\n            debug!(\"Opening {} file {}\", id.0, path);\n            OpenOptions::new()\n                .read(true)\n                .write(false)\n                .open(path)\n                .map_err(|source| Error::Open { source, path: path.to_owned() })\n        } else {\n            Err(Error::IdNoFound { id })\n        }\n    }\n\n    /// Inserts given record in RecordsFile\n    pub fn insert_record(&mut self, records: Record) {\n        self.inner.records.push(records);\n    }\n\n    /// Inserts given InodeInfo into in RecordsFile.\n    pub fn insert_or_update_inode_info(&mut self, id: FileId, info: InodeInfo) {\n        if let Some(inode) = self.inner.inode_map.get_mut(&id) {\n            if let Some(first_path) = info.paths.first() {\n                inode.paths.push(first_path.clone());\n            }\n        } else {\n            self.inner.inode_map.insert(id, info);\n        }\n    }\n\n    /// Verifies the integrity of records file.\n    ///\n    /// check saves us from serializing a improperly built record file or replaying an inconsistent\n    /// `RecordFile`.\n    ///\n    /// Note: check only works on the `RecordsFile` and doesn't access filesystem. We limit the\n    /// scope so that we avoid issuing filesystem operations(directory lookup, stats) twice - once\n    /// during check and once during replaying.\n    pub fn check(&self) -> Result<(), Error> {\n        let mut unique_files = HashSet::new();\n        let mut missing_file_ids = vec![];\n\n        for record in &self.inner.records {\n            if !self.inner.inode_map.contains_key(&record.file_id) {\n                missing_file_ids.push(record.file_id.clone());\n            }\n            unique_files.insert(record.file_id.clone());\n        }\n\n        let mut stale_inodes = vec![];\n        let mut missing_paths = vec![];\n        for (file_id, inode_info) in &self.inner.inode_map.map {\n            if inode_info.paths.is_empty() {\n                missing_paths.push(inode_info.clone());\n            }\n            if !unique_files.contains(file_id) {\n                stale_inodes.push(inode_info.clone());\n            }\n        }\n\n        if !stale_inodes.is_empty() || !missing_paths.is_empty() || !missing_file_ids.is_empty() {\n            return Err(Error::StaleInode { stale_inodes, missing_paths, missing_file_ids });\n        }\n\n        Ok(())\n    }\n\n    /// Builds InodeInfo from args and inserts inode info in RecordsFile.\n    pub fn insert_or_update_inode(&mut self, id: FileId, stat: &Metadata, path: PathString) {\n        self.insert_or_update_inode_info(\n            id,\n            InodeInfo {\n                inode_number: stat.ino(),\n                file_size: stat.len(),\n                paths: vec![path],\n                device_number: stat.dev(),\n            },\n        )\n    }\n\n    /// Serialize records in the form of csv.\n    pub fn serialize_records_to_csv(&self, writer: &mut dyn Write) -> Result<(), Error> {\n        let mut wtr = csv::Writer::from_writer(writer);\n\n        #[derive(Serialize)]\n        struct TempRecord<'a> {\n            timestamp: u64,\n            file: &'a PathString,\n            offset: u64,\n            length: u64,\n            file_size: u64,\n        }\n\n        for record in &self.inner.records {\n            if let Some(inode_info) = self.inner.inode_map.get(&record.file_id) {\n                let mut inode_info = inode_info.clone();\n                inode_info.paths.sort();\n\n                if let Some(first_path) = inode_info.paths.first().cloned() {\n                    // Clone the &String inside Option\n                    let record = TempRecord {\n                        timestamp: record.timestamp,\n                        file: &first_path, // Now you have &String\n                        offset: record.offset,\n                        length: record.length,\n                        file_size: inode_info.file_size,\n                    };\n                    wtr.serialize(&record)\n                        .map_err(|e| Error::Serialize { error: e.to_string() })?;\n                }\n            }\n        }\n        wtr.flush()?;\n        Ok(())\n    }\n\n    fn compute_digest(&mut self) -> Result<u32, Error> {\n        self.header.digest = Default::default();\n        let serialized = serde_cbor::to_vec(self)\n            .map_err(|source| Error::Serialize { error: source.to_string() })?;\n\n        let mut hasher = Hasher::new();\n        hasher.update(&serialized);\n\n        Ok(hasher.finalize())\n    }\n\n    /// Convenience wrapper around serialize that adds checksum/digest to the file\n    /// to verify file consistency during replay/deserialize.\n    pub fn add_checksum_and_serialize(&mut self) -> Result<Vec<u8>, Error> {\n        self.header.digest = self.compute_digest()?;\n\n        serde_cbor::to_vec(self).map_err(|source| Error::Serialize { error: source.to_string() })\n    }\n}\n\nimpl Default for Header {\n    fn default() -> Self {\n        Self {\n            major_number: MAJOR_VERSION,\n            minor_number: MINOR_VERSION,\n            date: SystemTime::now(),\n            digest: 0,\n            magic: MAGIC_UUID,\n        }\n    }\n}\n\n// Wrapper around deserialize to check any inconsistencies in the file format.\nimpl<'de> Deserialize<'de> for RecordsFile {\n    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>\n    where\n        D: Deserializer<'de>,\n    {\n        let rf = Self::deserialize(deserializer)?;\n\n        rf.check().map_err(|e| {\n            serde::de::Error::custom(format!(\"failed to validate records file: {}\", e))\n        })?;\n\n        let mut zero_digest = rf.clone();\n        zero_digest.header.digest = 0;\n        let digest =\n            zero_digest.compute_digest().map_err(|e| serde::de::Error::custom(format!(\"{}\", e)))?;\n\n        if digest != rf.header.digest {\n            return Err(serde::de::Error::custom(format!(\n                \"file consistency check failed. Expected: {}. Found: {}\",\n                digest, rf.header.digest\n            )));\n        }\n\n        Ok(rf)\n    }\n}\n\n// Wrapper around serialize to check any inconsistencies in the file format before serializing\nimpl Serialize for RecordsFile {\n    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>\n    where\n        S: Serializer,\n    {\n        self.check().map(|_| self).map_err(|e| {\n            serde::ser::Error::custom(format!(\"failed to validate records file: {}\", e))\n        })?;\n        Self::serialize(self, serializer)\n    }\n}\n\n#[cfg(test)]\npub mod tests {\n\n    use std::assert_eq;\n\n    use super::*;\n\n    #[test]\n    fn test_major_version_mismatch() {\n        let mut rf = RecordsFile::default();\n\n        rf.header.major_number += 1;\n\n        let serialized: Result<RecordsFile, serde_cbor::Error> =\n            serde_cbor::from_slice(&serde_cbor::to_vec(&rf).unwrap());\n\n        assert_eq!(\n            serialized.unwrap_err().to_string(),\n            format!(\n                \"Failed to parse major version. Expected: {} Found: {}\",\n                MAJOR_VERSION,\n                MAJOR_VERSION + 1\n            )\n        );\n    }\n\n    #[test]\n    fn test_minor_version_mismatch() {\n        let mut rf = RecordsFile::default();\n\n        rf.header.minor_number += 1;\n\n        let serialized: Result<RecordsFile, serde_cbor::Error> =\n            serde_cbor::from_slice(&serde_cbor::to_vec(&rf).unwrap());\n\n        assert_eq!(\n            serialized.unwrap_err().to_string(),\n            format!(\n                \"Failed to parse minor version. Expected: {} Found: {}\",\n                MINOR_VERSION,\n                MINOR_VERSION + 1\n            )\n        );\n    }\n\n    #[test]\n    fn deserialize_inode_info_without_path() {\n        let inode = InodeInfo { inode_number: 1, file_size: 10, paths: vec![], device_number: 1 };\n        let serialized = serde_cbor::to_vec(&inode).unwrap();\n        let deserialized: Result<InodeInfo, serde_cbor::Error> =\n            serde_cbor::from_slice(&serialized);\n        assert_eq!(\n            deserialized.unwrap_err().to_string(),\n            \"No paths found for in InodeInfo\".to_owned()\n        );\n    }\n    #[test]\n    fn test_serialize_records_to_csv() {\n        let mut rf = RecordsFile::default();\n        let file_count = 4;\n        for i in 0..file_count {\n            rf.insert_or_update_inode_info(\n                FileId(i),\n                InodeInfo {\n                    inode_number: i,\n                    file_size: i * 10,\n                    paths: vec![format!(\"/hello/{}\", i)],\n                    device_number: i + 10,\n                },\n            )\n        }\n        for i in 0..10 {\n            rf.insert_record(Record {\n                file_id: FileId(i % file_count),\n                offset: i * 3,\n                length: i + 4,\n                timestamp: i * file_count,\n            });\n        }\n\n        let mut buf = vec![];\n        rf.serialize_records_to_csv(&mut buf).unwrap();\n\n        let data = String::from_utf8(buf).unwrap();\n        assert_eq!(\n            data,\n            \"timestamp,file,offset,length,file_size\\n\\\n            0,/hello/0,0,4,0\\n\\\n            4,/hello/1,3,5,10\\n\\\n            8,/hello/2,6,6,20\\n\\\n            12,/hello/3,9,7,30\\n\\\n            16,/hello/0,12,8,0\\n\\\n            20,/hello/1,15,9,10\\n\\\n            24,/hello/2,18,10,20\\n\\\n            28,/hello/3,21,11,30\\n\\\n            32,/hello/0,24,12,0\\n\\\n            36,/hello/1,27,13,10\\n\"\n        );\n    }\n\n    fn new_record(file: u64, offset: u64, length: u64, timestamp: u64) -> Record {\n        Record { file_id: FileId(file), offset, length, timestamp }\n    }\n\n    #[test]\n    fn test_coalesced_without_group() {\n        let non_coalescable_same_inode =\n            vec![new_record(1, 2, 3, 4), new_record(1, 6, 3, 5), new_record(1, 10, 3, 6)];\n        assert_eq!(\n            coalesce_records(non_coalescable_same_inode.clone(), false),\n            non_coalescable_same_inode\n        );\n\n        let non_coalescable_different_inode =\n            vec![new_record(1, 2, 3, 4), new_record(2, 5, 3, 5), new_record(3, 8, 3, 6)];\n        assert_eq!(\n            coalesce_records(non_coalescable_different_inode.clone(), false),\n            non_coalescable_different_inode\n        );\n\n        let some_coalesced =\n            vec![new_record(1, 2, 3, 4), new_record(1, 5, 3, 5), new_record(3, 8, 3, 6)];\n        assert_eq!(\n            coalesce_records(some_coalesced, false),\n            vec![new_record(1, 2, 6, 4), new_record(3, 8, 3, 6),]\n        );\n\n        let coalesced_into_one =\n            vec![new_record(1, 2, 3, 4), new_record(1, 5, 3, 5), new_record(1, 8, 3, 6)];\n        assert_eq!(coalesce_records(coalesced_into_one, false), vec![new_record(1, 2, 9, 4)]);\n\n        let no_grouping_or_coalescing =\n            vec![new_record(1, 2, 3, 4), new_record(3, 8, 3, 5), new_record(1, 5, 3, 6)];\n        assert_eq!(\n            coalesce_records(no_grouping_or_coalescing, false),\n            vec![new_record(1, 2, 3, 4), new_record(3, 8, 3, 5), new_record(1, 5, 3, 6),]\n        );\n    }\n\n    #[test]\n    fn test_coalesced_with_grouping() {\n        let non_coalescable_same_inode =\n            vec![new_record(1, 2, 3, 4), new_record(1, 6, 3, 5), new_record(1, 10, 3, 6)];\n        assert_eq!(\n            coalesce_records(non_coalescable_same_inode.clone(), true),\n            non_coalescable_same_inode\n        );\n\n        let non_coalescable_different_inode =\n            vec![new_record(1, 2, 3, 4), new_record(2, 5, 3, 5), new_record(3, 8, 3, 6)];\n        assert_eq!(\n            coalesce_records(non_coalescable_different_inode.clone(), true),\n            non_coalescable_different_inode\n        );\n\n        let some_coalesced =\n            vec![new_record(1, 2, 3, 4), new_record(1, 5, 3, 5), new_record(3, 8, 3, 6)];\n        assert_eq!(\n            coalesce_records(some_coalesced, true),\n            vec![new_record(1, 2, 6, 4), new_record(3, 8, 3, 6),]\n        );\n\n        let coalesced_into_one =\n            vec![new_record(1, 2, 3, 4), new_record(1, 5, 3, 5), new_record(1, 8, 3, 6)];\n        assert_eq!(coalesce_records(coalesced_into_one, true), vec![new_record(1, 2, 9, 4)]);\n\n        let some_grouped_coalesced =\n            vec![new_record(1, 2, 3, 4), new_record(3, 8, 3, 5), new_record(1, 5, 3, 6)];\n        assert_eq!(\n            coalesce_records(some_grouped_coalesced, true),\n            vec![new_record(1, 2, 6, 4), new_record(3, 8, 3, 5),]\n        );\n    }\n\n    #[test]\n    fn check_missing_records() {\n        let mut rf = RecordsFile::default();\n        rf.inner.inode_map.insert(\n            FileId(0),\n            InodeInfo {\n                inode_number: 0,\n                file_size: 1,\n                paths: vec![\"hello\".to_owned()],\n                device_number: 2,\n            },\n        );\n        rf.insert_record(Record { file_id: FileId(0), offset: 10, length: 20, timestamp: 30 });\n\n        rf.inner.inode_map.insert(\n            FileId(1),\n            InodeInfo {\n                inode_number: 1,\n                file_size: 2,\n                paths: vec![\"world\".to_owned()],\n                device_number: 3,\n            },\n        );\n        let e = rf.check().unwrap_err();\n        assert_eq!(\n            e.to_string(),\n            \"Stale inode(s) info found.\\n\\\n                missing_file_ids: []\\n\\\n                stale_inodes: [\\n    \\\n                    InodeInfo {\\n        \\\n                        inode_number: 1,\\n        \\\n                        file_size: 2,\\n        \\\n                        paths: [\\n            \\\"world\\\",\\n        ],\\n        \\\n                        device_number: 3,\\n    },\\n] \\n\\\n                missing_paths:[]\"\n        );\n    }\n\n    #[test]\n    fn check_missing_file() {\n        let mut rf = RecordsFile::default();\n        rf.inner.inode_map.insert(\n            FileId(0),\n            InodeInfo {\n                inode_number: 0,\n                file_size: 1,\n                paths: vec![\"hello\".to_owned()],\n                device_number: 2,\n            },\n        );\n        rf.insert_record(Record { file_id: FileId(0), offset: 10, length: 20, timestamp: 30 });\n        rf.insert_record(Record { file_id: FileId(1), offset: 10, length: 20, timestamp: 30 });\n\n        let e = rf.check().unwrap_err();\n        assert_eq!(\n            e.to_string(),\n            \"Stale inode(s) info found.\\n\\\n                missing_file_ids: [\\n    \\\n                    FileId(\\n        1,\\n    ),\\n]\\n\\\n                stale_inodes: [] \\n\\\n                missing_paths:[]\"\n        );\n    }\n\n    #[test]\n    fn check_missing_paths() {\n        let mut rf = RecordsFile::default();\n        rf.inner.inode_map.insert(\n            FileId(0),\n            InodeInfo { inode_number: 0, file_size: 1, paths: vec![], device_number: 2 },\n        );\n        rf.insert_record(Record { file_id: FileId(0), offset: 10, length: 20, timestamp: 30 });\n\n        let e = rf.check().unwrap_err();\n        assert_eq!(\n            e.to_string(),\n            \"Stale inode(s) info found.\\n\\\n                missing_file_ids: []\\n\\\n                stale_inodes: [] \\n\\\n                missing_paths:[\\n    \\\n                    InodeInfo {\\n        \\\n                        inode_number: 0,\\n        \\\n                        file_size: 1,\\n        \\\n                        paths: [],\\n        \\\n                        device_number: 2,\\n    },\\n]\"\n        );\n    }\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/lib.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! A library to prefetch files on the file system to optimize startup times\n//!\n\nmod args;\nmod error;\nmod format;\nmod replay;\nmod tracer;\n#[cfg(target_os = \"android\")]\nmod arch {\n    pub mod android;\n}\n\nuse std::fs::File;\nuse std::fs::OpenOptions;\nuse std::io;\nuse std::io::Write;\nuse std::os::unix::fs::PermissionsExt;\nuse std::string::ToString;\nuse std::thread;\nuse std::time::Duration;\n\n#[cfg(target_os = \"android\")]\nuse log::Level;\n#[cfg(target_os = \"linux\")]\nuse log::LevelFilter;\n\npub use args::args_from_env;\nuse args::OutputFormat;\npub use args::ReplayArgs;\npub use args::{DumpArgs, MainArgs, RecordArgs, SubCommands};\npub use error::Error;\npub use format::FileId;\npub use format::InodeInfo;\npub use format::Record;\npub use format::RecordsFile;\nuse log::info;\npub use replay::Replay;\npub use tracer::nanoseconds_since_boot;\n\n#[cfg(target_os = \"android\")]\npub use arch::android::*;\n\n/// Records prefetch data for the given configuration\npub fn record(args: &RecordArgs) -> Result<(), Error> {\n    #[cfg(target_os = \"android\")]\n    if !ensure_record_is_ready(&args.ready_path, &args.path, &args.build_fingerprint_path)? {\n        info!(\"Cannot perform record -- skipping\");\n        return Ok(());\n    }\n\n    info!(\"Starting record.\");\n    let (mut tracer, exit_tx) = tracer::Tracer::create(\n        args.trace_buffer_size_kib,\n        args.tracing_subsystem.clone(),\n        args.tracing_instance.clone(),\n        args.setup_tracing,\n    )?;\n    let duration = Duration::from_secs(args.duration as u64);\n\n    let thd = thread::spawn(move || {\n        if !duration.is_zero() {\n            info!(\"Record start - waiting for duration: {:?}\", duration);\n            thread::sleep(duration);\n        } else {\n            #[cfg(target_os = \"android\")]\n            wait_for_record_stop();\n        }\n\n        info!(\"Prefetch record exiting\");\n        // We want to unwrap here on failure to send this signal. Otherwise\n        // tracer will continue generating huge records data.\n        exit_tx.send(()).unwrap();\n    });\n\n    let mut rf = tracer.trace(args.int_path.as_ref())?;\n    thd.join()\n        .map_err(|_| Error::ThreadPool { error: \"Failed to join timeout thread\".to_string() })?;\n\n    let mut out_file =\n        OpenOptions::new().write(true).create(true).truncate(true).open(&args.path).map_err(\n            |source| Error::Create { source, path: args.path.to_str().unwrap().to_owned() },\n        )?;\n\n    std::fs::set_permissions(&args.path, std::fs::Permissions::from_mode(0o644))\n        .map_err(|source| Error::Create { source, path: args.path.to_str().unwrap().to_owned() })?;\n\n    // Write the record file\n    out_file\n        .write_all(&rf.add_checksum_and_serialize()?)\n        .map_err(|source| Error::Write { path: args.path.to_str().unwrap().to_owned(), source })?;\n    out_file.sync_all()?;\n\n    // Write build-finger-print file\n    #[cfg(target_os = \"android\")]\n    write_build_fingerprint(args)?;\n\n    Ok(())\n}\n\n/// Replays prefetch data for the given configuration\npub fn replay(args: &ReplayArgs) -> Result<(), Error> {\n    #[cfg(target_os = \"android\")]\n    if !can_perform_replay(&args.path, &args.build_fingerprint_path)? {\n        info!(\"Cannot perform replay -- exiting.\");\n        return Ok(());\n    }\n\n    info!(\"Starting replay.\");\n    let replay = Replay::new(args)?;\n    replay.replay()\n}\n\n/// Dumps prefetch data in the human readable form\npub fn dump(args: &DumpArgs) -> Result<(), Error> {\n    let reader = File::open(&args.path)\n        .map_err(|source| Error::Open { source, path: args.path.to_str().unwrap().to_string() })?;\n    let rf: RecordsFile =\n        serde_cbor::from_reader(reader).map_err(|e| Error::Deserialize { error: e.to_string() })?;\n    match args.format {\n        OutputFormat::Json => println!(\n            \"{:#}\",\n            serde_json::to_string_pretty(&rf)\n                .map_err(|e| Error::Serialize { error: e.to_string() })?\n        ),\n        OutputFormat::Csv => rf.serialize_records_to_csv(&mut io::stdout())?,\n    }\n    Ok(())\n}\n\n/// An alias of android_logger::Level to use log level across android and linux.\n#[cfg(target_os = \"android\")]\npub type LogLevel = Level;\n\n/// An alias of log::LevelFilter to use log level across android and linux.\n#[cfg(not(target_os = \"android\"))]\npub type LogLevel = LevelFilter;\n\n/// Convenience logging initializer that is shared between the prefetch tool and c wrapper library\n#[cfg(target_os = \"android\")]\npub fn init_logging(_level: LogLevel) {\n    android_logger::init_once(\n        android_logger::Config::default().with_max_level(log::LevelFilter::Info).format(\n            |f, record| {\n                write!(\n                    f,\n                    \"{} prefetch_rs: {}:{} {}: {}\",\n                    nanoseconds_since_boot(),\n                    record.file().unwrap_or(\"unknown_file\"),\n                    record.line().unwrap_or(0),\n                    record.level(),\n                    record.args()\n                )\n            },\n        ),\n    )\n}\n\n/// Convenience logging initializer that is shared between the prefetch tool and c wrapper library\n#[cfg(target_os = \"linux\")]\npub fn init_logging(level: LogLevel) {\n    let mut builder = env_logger::Builder::from_default_env();\n\n    builder\n        .filter(None, level)\n        .format(|buf, record| {\n            writeln!(\n                buf,\n                \"{} prefetch_rs: {}:{} {}: {}\",\n                nanoseconds_since_boot(),\n                record.file().unwrap_or(\"unknown_file\"),\n                record.line().unwrap_or(0),\n                record.level(),\n                record.args()\n            )\n        })\n        .init();\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/main.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! A utility wrapper around libprefetch that allows to record, replay and dump\n//! prefetch data.\n\nuse log::error;\n\nuse prefetch_rs::args_from_env;\nuse prefetch_rs::dump;\nuse prefetch_rs::init_logging;\nuse prefetch_rs::record;\nuse prefetch_rs::replay;\nuse prefetch_rs::LogLevel;\nuse prefetch_rs::MainArgs;\nuse prefetch_rs::SubCommands;\n\nfn main() {\n    init_logging(LogLevel::Debug);\n    let args: MainArgs = args_from_env();\n    let ret = match &args.nested {\n        SubCommands::Record(args) => record(args),\n        SubCommands::Replay(args) => replay(args),\n        SubCommands::Dump(args) => dump(args),\n    };\n\n    if let Err(err) = ret {\n        error!(\"{:?} command failed: {:?}\", args, err);\n    }\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/replay.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nuse std::clone::Clone;\nuse std::convert::TryInto;\nuse std::fmt::Display;\nuse std::mem::replace;\nuse std::os::unix::io::AsRawFd;\nuse std::sync::Arc;\nuse std::sync::Mutex;\nuse std::sync::RwLock;\nuse std::thread;\n\nuse log::debug;\nuse log::error;\nuse log::warn;\nuse lru_cache::LruCache;\nuse nix::errno::Errno;\nuse nix::fcntl::posix_fadvise;\nuse regex::Regex;\n\nuse crate::args::ConfigFile;\nuse crate::format::Record;\nuse crate::format::{FileId, RecordsFile};\nuse crate::Error;\nuse crate::ReplayArgs;\nuse libc::{c_void, off64_t, pread64};\nuse std::fs::File;\n\nconst READ_SZ: usize = 1024 * 1024;\n\nstruct ScopedLog<T: Display + Sized> {\n    msg: T,\n    thd_id: usize,\n}\n\nfn scoped_log<T: Display + Sized>(ctx: usize, msg: T) -> ScopedLog<T> {\n    let thd_id = ctx;\n    debug!(\"{} {} start\", thd_id, msg);\n    ScopedLog { msg, thd_id }\n}\n\nimpl<T: Display> Drop for ScopedLog<T> {\n    fn drop(&mut self) {\n        debug!(\"{} {} end\", self.thd_id, self.msg);\n    }\n}\n\nfn readahead(\n    id: usize,\n    file: Arc<File>,\n    record: &Record,\n    buffer: &mut [u8; READ_SZ],\n) -> Result<(), Error> {\n    debug!(\"readahead {:?}\", record);\n    let _dbg = scoped_log(id, \"readahead\");\n\n    let mut current_offset: off64_t = record\n        .offset\n        .try_into()\n        .map_err(|_| Error::Read { error: \"Failed to convert offset\".to_string() })?;\n    let mut remaining_data: usize = record\n        .length\n        .try_into()\n        .map_err(|_| Error::Read { error: \"Failed to convert length\".to_string() })?;\n\n    while remaining_data > 0 {\n        let read_size = std::cmp::min(READ_SZ, remaining_data);\n\n        // SAFETY: This is safe because\n        // - the file is known to exist and opened\n        // - buffer is allocated upfront and is guaranteed by the fact it comes from a mutable slice reference.\n        // - read_size is guaranteed not to exceed length of the buffer.\n        let bytes_read = unsafe {\n            pread64(file.as_raw_fd(), buffer.as_mut_ptr() as *mut c_void, read_size, current_offset)\n        };\n\n        if bytes_read == -1 {\n            return Err(Error::Read { error: format!(\"readahead failed: {}\", Errno::last_raw()) });\n        }\n\n        if bytes_read == 0 {\n            break; // End of file reached\n        }\n\n        current_offset += bytes_read as off64_t;\n        remaining_data -= bytes_read as usize;\n    }\n\n    // TODO: Try readahead() syscall or async I/O\n    Ok(())\n}\n\nfn worker_internal(\n    id: usize,\n    state: Arc<Mutex<SharedState>>,\n    records_file: Arc<RwLock<RecordsFile>>,\n    exit_on_error: bool,\n    exclude_files_regex: Vec<Regex>,\n    buffer: &mut [u8],\n) -> Result<(), Error> {\n    loop {\n        let index = {\n            let mut state = state.lock().unwrap();\n            if state.result.is_err() {\n                return Ok(());\n            }\n            state.next_record()\n        };\n\n        let record = {\n            let rf = records_file.read().unwrap();\n            if index >= rf.inner.records.len() {\n                return Ok(());\n            }\n            rf.inner.records.get(index).unwrap().clone()\n        };\n\n        let _dbg = scoped_log(id, \"record_replay\");\n\n        let file = state.lock().unwrap().fds.get_mut(&record.file_id).map(|f| f.clone());\n\n        let file = match file {\n            Some(file) => file,\n            None => {\n                let file = Arc::new({\n                    let file = records_file\n                        .read()\n                        .unwrap()\n                        .open_file(record.file_id.clone(), &exclude_files_regex);\n                    if let Err(e) = file {\n                        if exit_on_error {\n                            return Err(e);\n                        } else {\n                            match e {\n                                Error::SkipPrefetch { path } => {\n                                    debug!(\"Skipping file during replay: {}\", path);\n                                }\n                                _ => error!(\n                                    \"Failed to open file id: {} with {}\",\n                                    record.file_id.clone(),\n                                    e.to_string()\n                                ),\n                            }\n                            continue;\n                        }\n                    }\n\n                    let file = file.unwrap();\n                    // We do not want the filesystem be intelligent and prefetch more than what this\n                    // code is reading. So turn off prefetch.\n\n                    if let Err(e) = posix_fadvise(\n                        file.as_raw_fd(),\n                        0,\n                        0,\n                        nix::fcntl::PosixFadviseAdvice::POSIX_FADV_RANDOM,\n                    ) {\n                        warn!(\n                            \"Failed to turn off filesystem read ahead for file id: {} with {}\",\n                            record.file_id.clone(),\n                            e.to_string()\n                        );\n                    }\n                    file\n                });\n                let cache_file = file.clone();\n                state.lock().unwrap().fds.insert(record.file_id.clone(), cache_file);\n                file\n            }\n        };\n        if let Err(e) = readahead(id, file, &record, buffer.try_into().unwrap()) {\n            if exit_on_error {\n                return Err(e);\n            } else {\n                error!(\n                    \"readahead failed on file id: {} with: {}\",\n                    record.file_id.clone(),\n                    e.to_string()\n                );\n                continue;\n            }\n        }\n    }\n}\n\nfn worker(\n    id: usize,\n    state: Arc<Mutex<SharedState>>,\n    records_file: Arc<RwLock<RecordsFile>>,\n    exit_on_error: bool,\n    exclude_files_regex: Vec<Regex>,\n    buffer: &mut [u8],\n) {\n    let _dbg = scoped_log(id, \"read_loop\");\n    let result = worker_internal(\n        id,\n        state.clone(),\n        records_file,\n        exit_on_error,\n        exclude_files_regex,\n        buffer,\n    );\n    if result.is_err() {\n        error!(\"worker failed with {:?}\", result);\n        let mut state = state.lock().unwrap();\n        if state.result.is_ok() {\n            state.result = result;\n        }\n    }\n}\n\n#[derive(Debug)]\npub struct SharedState {\n    fds: LruCache<FileId, Arc<File>>,\n    records_index: usize,\n    result: Result<(), Error>,\n}\n\nimpl SharedState {\n    fn next_record(&mut self) -> usize {\n        let ret = self.records_index;\n        self.records_index += 1;\n        ret\n    }\n}\n\n/// Runtime, in-memory, representation of records file structure.\n#[derive(Debug)]\npub struct Replay {\n    records_file: Arc<RwLock<RecordsFile>>,\n    io_depth: u16,\n    exit_on_error: bool,\n    state: Arc<Mutex<SharedState>>,\n    exclude_files_regex: Vec<Regex>,\n}\n\nimpl Replay {\n    /// Creates Replay from input `args`.\n    pub fn new(args: &ReplayArgs) -> Result<Self, Error> {\n        let _dbg = scoped_log(1, \"new\");\n        let reader: File = File::open(&args.path).map_err(|source| Error::Open {\n            source,\n            path: args.path.to_str().unwrap().to_owned(),\n        })?;\n        let rf: RecordsFile = serde_cbor::from_reader(reader)\n            .map_err(|error| Error::Deserialize { error: error.to_string() })?;\n\n        let mut exclude_files_regex: Vec<Regex> = Vec::new();\n        // The path to the configuration file is optional in the command.\n        // If the path is provided, the configuration file will be read.\n        if !&args.config_path.as_os_str().is_empty() {\n            let config_reader = File::open(&args.config_path).map_err(|source| Error::Open {\n                source,\n                path: args.path.to_str().unwrap().to_owned(),\n            })?;\n            let cf: ConfigFile = serde_json::from_reader(config_reader)\n                .map_err(|error| Error::Deserialize { error: error.to_string() })?;\n\n            for file_to_exclude in &cf.files_to_exclude_regex {\n                exclude_files_regex.push(Regex::new(file_to_exclude).unwrap());\n            }\n        }\n\n        Ok(Self {\n            records_file: Arc::new(RwLock::new(rf)),\n            io_depth: args.io_depth,\n            exit_on_error: args.exit_on_error,\n            state: Arc::new(Mutex::new(SharedState {\n                fds: LruCache::new(args.max_fds.into()),\n                records_index: 0,\n                result: Ok(()),\n            })),\n            exclude_files_regex,\n        })\n    }\n\n    /// Replay records.\n    pub fn replay(self) -> Result<(), Error> {\n        let _dbg = scoped_log(1, \"replay\");\n        let mut threads = vec![];\n        for i in 0..self.io_depth {\n            let i_clone = i as usize;\n            let state = self.state.clone();\n            let records_file = self.records_file.clone();\n            let exit_on_error = self.exit_on_error;\n            let exclude_files_regex = self.exclude_files_regex.clone();\n\n            let mut buffer = Box::new([0u8; READ_SZ]);\n\n            threads.push(thread::Builder::new().spawn(move || {\n                worker(\n                    i_clone,\n                    state,\n                    records_file,\n                    exit_on_error,\n                    exclude_files_regex,\n                    buffer.as_mut_slice(),\n                )\n            }));\n        }\n        for thread in threads {\n            thread.unwrap().join().unwrap();\n        }\n        replace(&mut self.state.lock().unwrap().result, Ok(()))\n    }\n}\n\n// WARNING: flaky tests.\n// In these tests we create files, invalidate their caches and then replay.\n// Verify that after reply the same portions of data is in memory.\n//\n// Since these tests to rely on presence or absence of data in cache, the\n// files used by the tests should not be in tmp filesystem. So we use relative\n// path as target directory. There is no guarantee that this target directory\n// is not on temp filesystem but chances are better than using target directory\n// in tempfs.\n//\n// Tests can be flaky if the system under tests is running low on memory. The\n// tests create file using O_DIRECT so that no data is left in file cache.\n// Though this is sufficient to avoid caching, but other processes reading these\n// files(like anti-virus) or some other system processes might change the state\n// of the cache. Or it may happen that the filesystem evicts the file before\n// we verify that read ahead worked as intended.\n#[cfg(test)]\npub mod tests {\n    use std::{\n        assert,\n        io::Write,\n        ops::Range,\n        path::{Path, PathBuf},\n        time::Duration,\n    };\n\n    use crate::format::DeviceNumber;\n    use crate::format::FsInfo;\n    use crate::format::InodeNumber;\n    use crate::nanoseconds_since_boot;\n    use nix::sys::mman::MapFlags;\n    use nix::sys::mman::ProtFlags;\n    use serde::Deserialize;\n    use serde::Serialize;\n    use std::collections::HashMap;\n    use std::fs::OpenOptions;\n    use std::num::NonZeroUsize;\n    use std::os::fd::AsFd;\n    use std::os::unix::fs::symlink;\n    use std::os::unix::fs::MetadataExt;\n    use std::ptr::NonNull;\n    use tempfile::NamedTempFile;\n\n    use super::*;\n    use crate::tracer::{\n        page_size,\n        tests::{copy_uncached_files_and_record_from, setup_test_dir},\n    };\n\n    static MB: u64 = 1024 * 1024;\n    static KB: u64 = 1024;\n\n    fn random_write(file: &mut NamedTempFile, base: u64) -> Range<u64> {\n        let start: u64 = base + (rand::random::<u64>() % (base / 2)) as u64;\n        let len: u64 = rand::random::<u64>() % (32 * KB);\n        let buf = vec![5; len as usize];\n        nix::sys::uio::pwrite(file.as_fd(), &buf, start as i64).unwrap();\n        start..(start + len)\n    }\n\n    pub(crate) fn create_file(\n        path: Option<&Path>,\n        align: Option<u64>,\n    ) -> (NamedTempFile, Vec<Range<u64>>) {\n        let mut file = if let Some(path) = path {\n            NamedTempFile::new_in(path).unwrap()\n        } else {\n            NamedTempFile::new().unwrap()\n        };\n        let range1 = random_write(&mut file, 32 * KB);\n        let range2 = random_write(&mut file, 128 * KB);\n        let range3 = random_write(&mut file, 4 * MB);\n        if let Some(align) = align {\n            let orig_size = file.metadata().unwrap().len();\n            let aligned_size = orig_size + (align - (orig_size % align));\n            file.set_len(aligned_size).unwrap();\n        }\n        (file, vec![range1, range2, range3])\n    }\n\n    pub(crate) fn generate_cached_files_and_record(\n        path: Option<&Path>,\n        create_symlink: bool,\n        align: Option<u64>,\n    ) -> (RecordsFile, Vec<(NamedTempFile, Vec<Range<u64>>)>) {\n        let file1 = create_file(path, align);\n        let file2 = create_file(path, align);\n        let file3 = create_file(path, align);\n\n        let mut f: RecordsFileBuilder = Default::default();\n        f.add_file(file1.0.path().to_str().unwrap());\n        f.add_file(file2.0.path().to_str().unwrap());\n        f.add_file(file3.0.path().to_str().unwrap());\n        if create_symlink {\n            let symlink_path = format!(\"{}-symlink\", file1.0.path().to_str().unwrap());\n            symlink(file1.0.path().file_name().unwrap(), &symlink_path).unwrap();\n\n            f.add_file(&symlink_path);\n        }\n        let rf = f.build().unwrap();\n        (rf, vec![file1, file2, file3])\n    }\n\n    /// RecordsFileBuilder is primarily used for testing purpose. This\n    /// is a thin wrapper around \"Record\". This gives the ability\n    /// to test Records functionality. The flow of this test is as follows:\n    ///\n    /// 1: generate_cached_files_and_record -> This will create temporary files of different length\n    /// and builds the \"RecordFile\" format.\n    /// 2: For each of the file path create, a \"RecordsFile\" is generated.\n    ///    a: mmap the file based on the length.\n    ///    b: call mincore() to get the residency of pages in memory for the given\n    ///    length.\n    ///    c: Iterate over the buffer of pages returned by mincore(). If a page\n    ///    is not resident in RAM, construct the \"Record\" structure.\n    /// 3: build() function will finally return a constructed Prefetch Record which\n    /// contains all the \"Record\" structure required for \"Replay\".\n    #[derive(Debug, Default, Deserialize, Serialize)]\n    pub struct RecordsFileBuilder {\n        // Temporarily holds paths of all files opened by other processes.\n        pub(crate) paths: HashMap<String, FileId>,\n\n        // Read inode numbers\n        inode_numbers: HashMap<(DeviceNumber, InodeNumber), FileId>,\n    }\n\n    impl RecordsFileBuilder {\n        pub fn add_file(&mut self, path: &str) {\n            if self.paths.contains_key(path) {\n                return;\n            }\n\n            self.paths.insert(path.to_owned(), FileId(self.paths.len() as u64));\n        }\n\n        pub fn build(&mut self) -> Result<RecordsFile, Error> {\n            let mut rf = RecordsFile::default();\n            for (path, mut id) in self.paths.drain() {\n                let stat = Path::new(&path)\n                    .metadata()\n                    .map_err(|source| Error::Stat { source, path: path.clone() })?;\n\n                rf.inner\n                    .filesystems\n                    .entry(stat.dev())\n                    .or_insert(FsInfo { block_size: stat.blksize() });\n\n                if let Some(orig_id) = self.inode_numbers.get(&(stat.dev(), stat.ino())) {\n                    let inode = rf.inner.inode_map.get_mut(orig_id).unwrap();\n                    inode.paths.push(path.clone());\n\n                    // There may be multiple paths for the file so from those path we may have multiple\n                    // ids. Override the id.\n                    id = orig_id.clone();\n                } else {\n                    self.inode_numbers.insert((stat.dev(), stat.ino()), id.clone());\n                    rf.insert_or_update_inode(id.clone(), &stat, path.clone());\n                }\n                if let Some(mmap) = Mmap::create(&path, id)? {\n                    mmap.get_records(&mut rf.inner.records)?;\n                }\n            }\n            Ok(rf)\n        }\n    }\n\n    #[derive(Debug)]\n    pub(crate) struct Mmap {\n        map_addr: *mut c_void,\n        length: usize,\n        #[allow(dead_code)]\n        file: File,\n        file_id: FileId,\n    }\n\n    impl Mmap {\n        pub fn create(path: &str, file_id: FileId) -> Result<Option<Self>, Error> {\n            let file = OpenOptions::new()\n                .read(true)\n                .write(false)\n                .open(path)\n                .map_err(|source| Error::Open { source, path: path.to_owned() })?;\n\n            let length = file\n                .metadata()\n                .map_err(|source| Error::Stat { source, path: path.to_owned() })?\n                .len() as usize;\n\n            if length == 0 {\n                return Ok(None);\n            }\n\n            // SAFETY: This is safe because\n            // - the length is checked for zero\n            // - offset is set to 0\n            let map_addr = unsafe {\n                nix::sys::mman::mmap(\n                    None,\n                    NonZeroUsize::new(length).unwrap(),\n                    ProtFlags::PROT_READ,\n                    MapFlags::MAP_SHARED,\n                    file.as_fd(),\n                    0,\n                )\n                .map_err(|source| Error::Mmap {\n                    error: source.to_string(),\n                    path: path.to_owned(),\n                })?\n            };\n\n            Ok(Some(Self { map_addr: map_addr.as_ptr(), length, file, file_id }))\n        }\n\n        /// Construct the \"Record\" file based on pages resident in RAM.\n        pub(crate) fn get_records(&self, records: &mut Vec<Record>) -> Result<(), Error> {\n            let page_size = page_size()?;\n            let page_count = (self.length + page_size - 1) / page_size;\n            let mut buf: Vec<u8> = vec![0_u8; page_count];\n            // SAFETY: This is safe because\n            // - the file is mapped\n            // - buf points to a valid and sufficiently large memory region with the\n            //   requirement of (length+PAGE_SIZE-1) / PAGE_SIZE bytes\n            let ret = unsafe { libc::mincore(self.map_addr, self.length, buf.as_mut_ptr()) };\n            if ret < 0 {\n                return Err(Error::Custom {\n                    error: format!(\"failed to query resident pages: {}\", Errno::last_raw()),\n                });\n            }\n            let mut i = 0;\n\n            let mut offset_length: Option<(u64, u64)> = None;\n            for (index, resident) in buf.iter().enumerate() {\n                if *resident != 0 {\n                    if let Some((_, length)) = &mut offset_length {\n                        *length += page_size as u64;\n                    } else {\n                        offset_length = Some((index as u64 * page_size as u64, page_size as u64));\n                    }\n                } else if let Some((offset, length)) = offset_length {\n                    i += 1;\n                    records.push(Record {\n                        file_id: self.file_id.clone(),\n                        offset,\n                        length,\n                        timestamp: nanoseconds_since_boot(),\n                    });\n\n                    offset_length = None;\n                }\n            }\n\n            if let Some((offset, length)) = offset_length {\n                i += 1;\n                records.push(Record {\n                    file_id: self.file_id.clone(),\n                    offset,\n                    length,\n                    timestamp: nanoseconds_since_boot(),\n                });\n            }\n            debug!(\"records found: {} for {:?}\", i, self);\n\n            Ok(())\n        }\n    }\n\n    impl Drop for Mmap {\n        fn drop(&mut self) {\n            // SAFETY: This is safe because\n            // - addr is mapped and is multiple of page_size\n            let ret = unsafe {\n                nix::sys::mman::munmap(NonNull::new(self.map_addr).unwrap(), self.length)\n            };\n            if let Err(e) = ret {\n                error!(\n                    \"failed to munmap {:p} {} with {}\",\n                    self.map_addr,\n                    self.length,\n                    e.to_string()\n                );\n            }\n        }\n    }\n\n    // Please see comment above RecordsFileBuilder.\n    fn rebuild_records_file(files: &[(PathBuf, Vec<Range<u64>>)]) -> RecordsFile {\n        // Validate that caches are dropped\n        let mut f: RecordsFileBuilder = Default::default();\n        for (path, _) in files {\n            f.add_file(path.to_str().unwrap());\n        }\n        f.build().unwrap()\n    }\n\n    fn ensure_files_not_cached(files: &mut [(PathBuf, Vec<Range<u64>>)]) {\n        assert!(rebuild_records_file(files).inner.records.is_empty());\n    }\n\n    fn has_record(records: &[Record], key: &Record) -> bool {\n        for r in records {\n            if r.offset == key.offset && r.length == key.length {\n                return true;\n            }\n        }\n        false\n    }\n\n    fn compare_records(old: &[Record], new: &[Record]) {\n        for key in new {\n            if !has_record(old, key) {\n                panic!(\"Failed to file {:?} in {:?}\", key, old);\n            }\n        }\n    }\n\n    fn create_test_config_file(files_to_exclude_regex: Vec<String>) -> String {\n        let cfg = ConfigFile { files_to_exclude_regex, ..Default::default() };\n        serde_json::to_string(&cfg).unwrap()\n    }\n\n    // TODO: Split this into individual tests for better readability.\n    // b/378554334\n    fn test_replay_internal(\n        create_symlink: bool,\n        exit_on_error: bool,\n        inject_error: bool,\n        exclude_all_files: bool,\n        empty_exclude_file_list: bool,\n    ) {\n        let page_size = page_size().unwrap() as u64;\n        let test_base_dir = setup_test_dir();\n        let (rf, mut files) =\n            generate_cached_files_and_record(None, create_symlink, Some(page_size));\n\n        // Here \"uncached_files\" emulate the files after reboot when none of those files data is in cache.\n        let (mut uncached_rf, mut uncached_files) =\n            copy_uncached_files_and_record_from(Path::new(&test_base_dir), &mut files, &rf);\n\n        // Injects error(s) in the form of invalid filename\n        if inject_error {\n            if let Some(v) = uncached_rf.inner.inode_map.values_mut().next() {\n                for path in &mut v.paths {\n                    path.push('-');\n                }\n            }\n        }\n\n        let mut file = NamedTempFile::new().unwrap();\n        file.write_all(&uncached_rf.add_checksum_and_serialize().unwrap()).unwrap();\n        let mut config_file = NamedTempFile::new().unwrap();\n\n        let mut files_to_exclude: Vec<String> = Vec::new();\n        if exclude_all_files {\n            // Exclude files from replay by adding them in config\n            for v in uncached_rf.inner.inode_map.values_mut() {\n                for path in &mut v.paths {\n                    files_to_exclude.push(path.to_string())\n                }\n            }\n        } else if empty_exclude_file_list {\n            files_to_exclude.extend(vec![]);\n        } else {\n            // Exclude file1 and file2 during replay\n            files_to_exclude.extend(vec![\"file1\".to_owned(), \"file2\".to_owned()]);\n        }\n\n        // Create a config json to exclude files during replay\n        let config_file_contents = create_test_config_file(files_to_exclude);\n        config_file.write_all(config_file_contents.as_bytes()).unwrap();\n\n        ensure_files_not_cached(&mut uncached_files);\n\n        let replay = Replay::new(&ReplayArgs {\n            path: file.path().to_owned(),\n            io_depth: 32,\n            max_fds: 128,\n            exit_on_error,\n            config_path: config_file.path().to_owned(),\n        })\n        .unwrap();\n\n        let result = replay.replay();\n        // Sleep a bit so that readaheads are complete.\n        thread::sleep(Duration::from_secs(1));\n\n        if exit_on_error && inject_error {\n            result.expect_err(\"Failure was expected\");\n        } else if exclude_all_files {\n            let new_rf = rebuild_records_file(&uncached_files);\n            assert!(new_rf.inner.records.is_empty());\n        } else {\n            result.unwrap();\n\n            // At this point, we have prefetched data for uncached file bringing same set of\n            // data in memory as the original cached files.\n            // If we record prefetch data for new files, we should get same records files\n            // (offset and lengths) except that the file names should be different.\n            // This block verifies it.\n            // Note: `new_rf` is for uncached_files. But, [un]fortunately, those \"uncached_files\"\n            // are now cached after we replayed the records.\n            let new_rf = rebuild_records_file(&uncached_files);\n            assert!(!new_rf.inner.records.is_empty());\n            assert_eq!(rf.inner.inode_map.len(), new_rf.inner.inode_map.len());\n            assert_eq!(rf.inner.records.len(), new_rf.inner.records.len());\n            compare_records(&rf.inner.records, &new_rf.inner.records);\n        }\n    }\n\n    #[test]\n    fn test_replay() {\n        test_replay_internal(true, false, false, false, false);\n    }\n\n    #[test]\n    fn test_replay_strict() {\n        test_replay_internal(true, true, false, false, false);\n    }\n\n    #[test]\n    fn test_replay_no_symlink() {\n        test_replay_internal(false, false, false, false, false);\n    }\n\n    #[test]\n    fn test_replay_no_symlink_strict() {\n        test_replay_internal(false, true, false, false, false);\n    }\n\n    #[test]\n    fn test_replay_fails_on_error() {\n        test_replay_internal(true, true, true, false, false);\n    }\n\n    #[test]\n    fn test_replay_exclude_all_files() {\n        test_replay_internal(true, false, false, true, false);\n    }\n\n    #[test]\n    fn test_replay_empty_exclude_files_list() {\n        test_replay_internal(true, false, false, false, true);\n    }\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/tracer/mem.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! See top level documentation for `crate::tracer`.\n\nuse std::collections::hash_map::Iter;\nuse std::fs::symlink_metadata;\nuse std::io::{ErrorKind, Write};\nuse std::iter::Iterator;\nuse std::mem::take;\nuse std::os::unix::fs::MetadataExt;\nuse std::{\n    collections::{HashMap, HashSet},\n    fs::read_to_string,\n    option::Option,\n    path::{Path, PathBuf},\n};\n\nuse log::{debug, error, info, warn};\nuse regex::Regex;\nuse serde::Deserialize;\nuse serde::Serialize;\nuse walkdir::{DirEntry, WalkDir};\n\nuse crate::format::{coalesce_records, FsInfo};\nuse crate::tracer::{page_size, TracerConfigs};\nuse crate::{\n    format::{DeviceNumber, InodeNumber},\n    tracer::{TraceSubsystem, EXCLUDE_PATHS},\n    Error, FileId, Record, RecordsFile,\n};\n\nstatic MOUNTINFO_PATH: &str = \"/proc/self/mountinfo\";\n\n// Trace events to enable\n// Paths are relative to trace mount point\nstatic TRACE_EVENTS: &[&str] =\n    &[\"events/filemap/mm_filemap_add_to_page_cache/enable\", \"tracing_on\"];\n\n// Filesystem types to ignore\nstatic EXCLUDED_FILESYSTEM_TYPES: &[&str] = &[\n    \"binder\",\n    \"bpf\",\n    \"cgroup\",\n    \"cgroup2\",\n    \"configfs\",\n    \"devpts\",\n    \"fuse\", // No emulated storage\n    \"fusectl\",\n    \"proc\",\n    \"pstore\",\n    \"selinuxfs\",\n    \"sysfs\",\n    \"tmpfs\", // Check for apex mount points\n    \"tracefs\",\n    \"functionfs\", // adb, fastboot\n    \"f2fs\",       // Skip /data mounts\n];\n\n#[cfg(target_os = \"linux\")]\ntype MajorMinorType = u32;\n#[cfg(target_os = \"android\")]\ntype MajorMinorType = i32;\n\n// TODO(b/302056482): Once we uprev nix crate, we can use the function exported by the crate.\nfn major(dev: DeviceNumber) -> MajorMinorType {\n    (((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)) as MajorMinorType\n}\n\n// TODO(b/302056482): Once we uprev nix crate, we can use the function exported by the crate.\nfn minor(dev: DeviceNumber) -> MajorMinorType {\n    (((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)) as MajorMinorType\n}\n\n// TODO(b/302056482): Once we uprev nix crate, we can use the function exported by the crate.\nfn makedev(major: MajorMinorType, minor: MajorMinorType) -> DeviceNumber {\n    let major = major as DeviceNumber;\n    let minor = minor as DeviceNumber;\n    ((major & 0xffff_f000) << 32)\n        | ((major & 0x0000_0fff) << 8)\n        | ((minor & 0xffff_ff00) << 12)\n        | (minor & 0x0000_00ff)\n}\n\nfn build_device_number(major: &str, minor: &str) -> Result<DeviceNumber, Error> {\n    Ok(makedev(\n        major.parse::<MajorMinorType>().map_err(|e| Error::Custom {\n            error: format!(\"Failed to parse major number from {} with {}\", major, e),\n        })?,\n        minor.parse::<MajorMinorType>().map_err(|e| Error::Custom {\n            error: format!(\"Failed to parse major number from {} with {}\", major, e),\n        })?,\n    ))\n}\n\n// Returns timestamp in nanoseconds\nfn build_timestamp(seconds: &str, microseconds: &str) -> Result<u64, Error> {\n    let seconds = seconds.parse::<u64>().map_err(|e| Error::Custom {\n        error: format!(\"Failed to parse seconds from {} with {}\", seconds, e),\n    })?;\n    let microseconds = microseconds.parse::<u64>().map_err(|e| Error::Custom {\n        error: format!(\"Failed to parse microseconds from {} with {}\", seconds, e),\n    })?;\n    Ok((seconds * 1_000_000_000) + (microseconds * 1_000))\n}\n\n#[cfg(not(target_os = \"android\"))]\nfn is_highly_privileged_path(_path: &Path) -> bool {\n    false\n}\n\n#[cfg(target_os = \"android\")]\nfn is_highly_privileged_path(path: &Path) -> bool {\n    // Following directories contain a mix of files with and without access to stat/read.\n    // We do not completely exclude these directories as there is still a lot of\n    // file we can issue readahead on. Some of the files on which readahead fails include\n    // - /system/bin/run-as\n    // - /data/data/com.android.storagemanager\n    // - /system/apex/com.android.art/bin/dex2oat32\n    // - /data/user/0/com.android.systemui\n    //\n    // - TODO: /system/apex: Apex files in read-only partition may be read during boot.\n    // However, some files may not have access. Double check the record files\n    // to filter out the exact path.\n    let privileged_paths = [\n        \"/data/data\",\n        \"/data/user/0\",\n        \"/data/user_de/0\",\n        \"/system/bin/\",\n        \"/system/etc/selinux/\",\n        \"/system/system_ext/etc/selinux/\",\n        \"/system/product/etc/selinux/\",\n        \"/system/vendor/etc/selinux/\",\n        \"/system_ext/etc/selinux/\",\n        \"/product/etc/selinux/\",\n        \"/vendor/etc/selinux/\",\n        \"/system/xbin\",\n        \"/system/etc/\",\n        \"/data/\",\n        \"/postinstall/\",\n        \"/mnt/\",\n        \"/metadata/\",\n    ];\n    for privileged in privileged_paths {\n        if path.to_str().unwrap().starts_with(privileged) {\n            return true;\n        }\n    }\n    false\n}\n\nenum DeviceState {\n    Include((DeviceNumber, PathBuf)),\n    Exclude(DeviceNumber),\n}\n\n/// Utility struct that helps to include and exclude devices and mount points that need and don't\n/// need prefetching.\n#[derive(Debug, Deserialize, Serialize)]\nstruct MountInfo {\n    // Map of device number to mount points\n    included_devices: HashMap<DeviceNumber, PathBuf>,\n\n    // Devices that we don't want to prefetch - like devices backing tempfs and sysfs\n    excluded_devices: HashSet<DeviceNumber>,\n}\n\nimpl MountInfo {\n    // Parses file at `path` to build `Self`.`\n    fn create(path: &str) -> Result<Self, Error> {\n        let buf = read_to_string(path)\n            .map_err(|e| Error::Read { error: format!(\"Reading {} failed with: {}\", path, e) })?;\n        Self::with_buf(&buf)\n    }\n\n    // Parses string in `buf` to build `Self`.\n    fn with_buf(buf: &str) -> Result<Self, Error> {\n        let regex = Self::get_regex()?;\n        let mut included_devices: HashMap<DeviceNumber, PathBuf> = HashMap::new();\n        let mut excluded_devices = HashSet::new();\n        let excluded_filesystem_types: HashSet<String> =\n            EXCLUDED_FILESYSTEM_TYPES.iter().map(|s| String::from(*s)).collect();\n        for line in buf.lines() {\n            if let Some(state) = Self::parse_line(&regex, &excluded_filesystem_types, line)? {\n                match state {\n                    DeviceState::Include((device, path)) => {\n                        included_devices.insert(device, path);\n                    }\n                    DeviceState::Exclude(device) => {\n                        excluded_devices.insert(device);\n                    }\n                }\n            }\n        }\n\n        Ok(Self { included_devices, excluded_devices })\n    }\n\n    fn parse_line(\n        re: &Regex,\n        excluded_filesystem_types: &HashSet<String>,\n        line: &str,\n    ) -> Result<Option<DeviceState>, Error> {\n        let caps = match re.captures(line) {\n            Some(caps) => caps,\n            None => {\n                return Ok(None);\n            }\n        };\n        if &caps[\"relative_path\"] != \"/\" {\n            return Ok(None);\n        }\n\n        let mount_point = &caps[\"mount_point\"];\n        let mnt_pnt_with_slash = format!(\"{}/\", mount_point);\n        let device_number = build_device_number(&caps[\"major\"], &caps[\"minor\"])?;\n        let fs_type = &caps[\"fs_type\"];\n\n        if excluded_filesystem_types.contains(fs_type) {\n            info!(\n                \"excluding fs type: {} for {} mount-point {} slash {}\",\n                fs_type, line, mount_point, mnt_pnt_with_slash\n            );\n            return Ok(Some(DeviceState::Exclude(device_number)));\n        }\n\n        for excluded in EXCLUDE_PATHS {\n            if mnt_pnt_with_slash.starts_with(excluded) {\n                info!(\n                    \"exclude-paths fs type: {} for {} mount-point {} slash {}\",\n                    fs_type, line, mount_point, mnt_pnt_with_slash\n                );\n                return Ok(Some(DeviceState::Exclude(device_number)));\n            }\n        }\n\n        Ok(Some(DeviceState::Include((device_number, PathBuf::from(mount_point)))))\n    }\n\n    fn get_regex() -> Result<Regex, Error> {\n        Regex::new(concat!(\n            r\"^\\s*(?P<id_unknown1>\\S+)\",\n            r\"\\s+(?P<id_unknown2>\\S+)\",\n            r\"\\s+(?P<major>[0-9]+):(?P<minor>[0-9]+)\",\n            r\"\\s+(?P<relative_path>\\S+)\",\n            r\"\\s+(?P<mount_point>\\S+)\",\n            r\"\\s+(?P<mount_opt>\\S+)\",\n            r\"\\s+(?P<shared>\\S+)\",\n            r\"\\s+\\S+\",\n            r\"\\s+(?P<fs_type>\\S+)\",\n            r\"\\s+(?P<device_path>\\S+)\"\n        ))\n        .map_err(|e| Error::Custom {\n            error: format!(\"create regex for parsing mountinfo failed with: {}\", e),\n        })\n    }\n\n    fn is_excluded(&self, device: &DeviceNumber) -> bool {\n        self.excluded_devices.contains(device)\n    }\n\n    fn get_included(&self) -> Iter<DeviceNumber, PathBuf> {\n        self.included_devices.iter()\n    }\n}\n\n#[derive(Default, PartialEq, Debug, Eq, Hash)]\nstruct TraceLineInfo {\n    device: DeviceNumber,\n    inode: InodeNumber,\n    offset: u64,\n    timestamp: u64,\n}\n\nimpl TraceLineInfo {\n    pub fn from_trace_line(re: &Regex, line: &str) -> Result<Option<Self>, Error> {\n        let caps = match re.captures(line) {\n            Some(caps) => caps,\n            None => return Ok(None),\n        };\n        let major = &caps[\"major\"];\n        let minor = &caps[\"minor\"];\n        let ino = &caps[\"ino\"];\n        let offset = &caps[\"offset\"];\n        let timestamp = build_timestamp(&caps[\"seconds\"], &caps[\"microseconds\"])?;\n        Ok(Some(TraceLineInfo {\n            device: build_device_number(major, minor)?,\n            inode: u64::from_str_radix(ino, 16).map_err(|e| Error::Custom {\n                error: format!(\"failed parsing inode: {} : {}\", ino, e),\n            })?,\n            offset: offset.parse::<u64>().map_err(|e| Error::Custom {\n                error: format!(\"failed parsing offset: {} : {}\", offset, e),\n            })?,\n            timestamp,\n        }))\n    }\n\n    #[cfg(test)]\n    pub fn from_fields(\n        major: MajorMinorType,\n        minor: MajorMinorType,\n        inode: u64,\n        offset: u64,\n        timestamp: u64,\n    ) -> Self {\n        Self { device: makedev(major, minor), inode, offset, timestamp }\n    }\n\n    // Convenience function to create regex. Used once per life of `record` but multiple times in\n    // case of tests.\n    pub fn get_trace_line_regex() -> Result<Regex, Error> {\n        // `page=[hex]` entry exists in 5.x kernel format but not in 6.x.\n        // Conversely, `order=[digit]` entry exists in 6.x kernel format but not in 5.x.\n        Regex::new(concat!(\n            r\"^\\s+(?P<cmd_pid>\\S+)\",\n            r\"\\s+(?P<cpu>\\S+)\",\n            r\"\\s+(?P<irq_stuff>\\S+)\",\n            r\"\\s+(?P<seconds>[0-9]+)\\.(?P<microseconds>[0-9]+):\",\n            r\"\\s+mm_filemap_add_to_page_cache:\",\n            r\"\\s+dev\\s+(?P<major>[0-9]+):(?P<minor>[0-9]+)\",\n            r\"\\s+ino\\s+(?P<ino>\\S+)\",\n            r\"(?:\\s+(?P<page>page=\\S+))?\",\n            r\"\\s+(?P<pfn>\\S+)\",\n            r\"\\s+ofs=(?P<offset>[0-9]+)\",\n            r\"(?:\\s+(?P<order>\\S+))?\"\n        ))\n        .map_err(|e| Error::Custom {\n            error: format!(\"create regex for tracing failed with: {}\", e),\n        })\n    }\n}\n\n#[derive(Debug, Serialize, Deserialize)]\nstruct MissingFile {\n    major_no: MajorMinorType,\n    minor_no: MajorMinorType,\n    inode: InodeNumber,\n    records: Vec<Record>,\n}\n\n#[derive(Debug, Default, Deserialize, Serialize)]\nstruct DebugInfo {\n    // Check all inodes for which paths don't exists. These are the files which\n    // * got deleted before we got to them\n    // * are filesystem internal files that fs access only via inode numbers.\n    missing_files: HashMap<FileId, MissingFile>,\n\n    // Number of bytes read that belongs to directory type inodes.\n    directory_read_bytes: u64,\n\n    // Number of bytes read from files for which we could not find a path in\n    // the filesystems.\n    missing_path_bytes: u64,\n\n    // Paths for which the current process doesn't have read permission.\n    privileged_paths: Vec<PathBuf>,\n}\n\n#[derive(Debug, Serialize)]\npub(crate) struct MemTraceSubsystem {\n    device_inode_map: HashMap<DeviceNumber, HashMap<InodeNumber, FileId>>,\n    // Count of all InodeNumber held by `device_inode_map`. This is handy to assign unique\n    // FileId.\n    inode_count: u64,\n\n    // `Record`s built from parsing read trace lines.\n    records: Vec<Record>,\n\n    // Regex to parse lines from trace_pipe.\n    #[serde(skip_serializing)]\n    regex: Regex,\n\n    // Mounted devices/filesystems either at the time of parsing trace file or at the time\n    // of building RecordsFile from parsed lines.\n    mount_info: MountInfo,\n\n    // A copy of TracerConfigs\n    tracer_configs: Option<TracerConfigs>,\n\n    // system page size stored to avoid frequent syscall to get the page size.\n    page_size: u64,\n\n    // The fields of the debug_info are populated when build_records_file is called (after lines\n    // are parsed from the trace file/pipe).\n    debug_info: DebugInfo,\n}\n\nimpl MemTraceSubsystem {\n    pub fn update_configs(configs: &mut TracerConfigs) {\n        for path in EXCLUDE_PATHS {\n            configs.excluded_paths.push(path.to_owned().to_string());\n        }\n\n        for event in TRACE_EVENTS {\n            configs.trace_events.push(event.to_owned().to_string());\n        }\n        configs.mountinfo_path = Some(MOUNTINFO_PATH.to_string());\n    }\n\n    pub fn create_with_configs(tracer_configs: TracerConfigs) -> Result<Self, Error> {\n        static INITIAL_RECORDS_CAPACITY: usize = 100_000;\n        debug!(\"TracerConfig: {:#?}\", tracer_configs);\n\n        let regex = TraceLineInfo::get_trace_line_regex()?;\n        let mount_info = MountInfo::create(tracer_configs.mountinfo_path.as_ref().unwrap())?;\n        debug!(\"mountinfo: {:#?}\", mount_info);\n\n        Ok(Self {\n            device_inode_map: HashMap::new(),\n            inode_count: 0,\n            // For one product of android, we see around 50k records. To avoid a lot allocations\n            // and copying of records, we create a vec of this size.\n            //\n            // We do this to reduces chances of losing data, however unlikely, coming over\n            // `trace_pipe`.\n            //\n            // Note: Once we are done reading trace lines, we are less pedantic about allocations\n            // and mem copies.\n            records: Vec::with_capacity(INITIAL_RECORDS_CAPACITY),\n            regex,\n            mount_info,\n            tracer_configs: Some(tracer_configs),\n            page_size: page_size()? as u64,\n            debug_info: DebugInfo {\n                missing_files: HashMap::new(),\n                directory_read_bytes: 0,\n                missing_path_bytes: 0,\n                privileged_paths: vec![],\n            },\n        })\n    }\n\n    fn new_file_id(&mut self) -> FileId {\n        let id = self.inode_count;\n        self.inode_count += 1;\n        FileId(id)\n    }\n\n    fn get_trace_info(&self, line: &str) -> Result<Option<TraceLineInfo>, Error> {\n        TraceLineInfo::from_trace_line(&self.regex, line)\n    }\n\n    // Returns true if the file or directory is on a device which is excluded from walking.\n    // If the path was excluded because the current process doesn't have privileged to read it,\n    // the path gets added to `privileged` list.\n    fn is_excluded(&self, entry: &DirEntry, device: u64, privileged: &mut Vec<PathBuf>) -> bool {\n        // We skip paths that are reside on excluded devices here. This is ok because a\n        // non-excluded mount point will have a separate entry in MountInfo. For example\n        // - `/` has ext4\n        // - `/tmp` has tempfs\n        // - `/tmp/mnt` has ext4 that we are interested in.\n        // MountInfo will have three entries - `/`, `/tmp/` and `/tmp/mnt`. Skipping walking\n        // `/tmp` while walking `/` is ok as next `mount_info.get_included()` will return\n        // `/tmp/mnt` path.\n        //\n        //\n        // We skip links here as they can refer to mount points across\n        // filesystems. If that path is valid and access are valid, then\n        // we should have entry by the file's <device, inode> pair.\n        //\n        //\n        // We skip devices that don't match current walking device because we eventually\n        // walk other devices.\n        match symlink_metadata(entry.path()) {\n            Ok(lstat) => {\n                if self.mount_info.is_excluded(&lstat.dev())\n                    || lstat.dev() != device\n                    || lstat.file_type().is_symlink()\n                {\n                    return true;\n                }\n            }\n            Err(e) => {\n                error!(\"stat on {} failed with {}\", entry.path().to_str().unwrap(), e);\n\n                // We treat EACCES special because on some platforms, like android, process needs to\n                // have very special set of permissions to access some inodes.\n                // We ignore errors in such cases *after* making an effort to get to them.\n                if e.kind() == ErrorKind::PermissionDenied\n                    && is_highly_privileged_path(entry.path())\n                {\n                    privileged.push(entry.path().to_owned());\n                    return true;\n                }\n            }\n        }\n\n        // On error, we return false because if lstat has failed, it will fail following operations\n        // including stat.\n        false\n    }\n}\n\nimpl TraceSubsystem for MemTraceSubsystem {\n    fn add_line(&mut self, line: &str) -> Result<(), Error> {\n        if let Some(info) = self.get_trace_info(line)? {\n            if self.mount_info.is_excluded(&info.device) {\n                return Ok(());\n            }\n\n            self.device_inode_map.entry(info.device).or_default();\n\n            let file_id = if let Some(id) =\n                self.device_inode_map.get_mut(&info.device).unwrap().get(&info.inode)\n            {\n                id.clone()\n            } else {\n                self.new_file_id()\n            };\n            self.device_inode_map\n                .get_mut(&info.device)\n                .unwrap()\n                .insert(info.inode, file_id.clone());\n\n            self.records.push(Record {\n                file_id,\n                offset: info.offset,\n                length: self.page_size,\n                timestamp: info.timestamp,\n            });\n        }\n\n        Ok(())\n    }\n\n    fn build_records_file(&mut self) -> Result<RecordsFile, Error> {\n        // reset debug_info in case build_records_file was called twice.\n        self.debug_info = DebugInfo::default();\n        let mut rf = RecordsFile::default();\n        let mut directories = HashSet::new();\n\n        // TODO(b/302194377): We are holding all privileged_paths in this variable and then\n        // transferring it to `self.debug_info.privileged_paths` later. We can avoid this step\n        // if we directly update `self.debug_info.privileged_paths`. To do so, we need to refactor\n        // code to make borrow not complain at several places - ex. immutably borrowing\n        // `self.mount_info` in outer loop and then mutably borrowing\n        // `self.debug_info.privileged_paths`.\n        let mut privileged_paths = vec![];\n\n        // Reload mount_info. When we created mount_info for the first time, maybe\n        // the system was in early boot phase. Reload the mount_info so as to get\n        // current/new mount points.\n        if let Some(tracer_config) = &self.tracer_configs {\n            self.mount_info = MountInfo::create(tracer_config.mountinfo_path.as_ref().unwrap())?;\n            debug!(\"reloaded mountinfo: {:#?}\", self.mount_info);\n        }\n\n        for (device, root_path) in self.mount_info.get_included() {\n            let inode_map = if let Some(map) = self.device_inode_map.get(device) {\n                map\n            } else {\n                continue;\n            };\n\n            if inode_map.is_empty() {\n                return Err(Error::Custom {\n                    error: format!(\"Unexpected empty records for {:?}\", root_path),\n                });\n            }\n\n            let mut block_size = 0;\n            let walker = WalkDir::new(root_path).into_iter();\n\n            for entry in\n                walker.filter_entry(|e| !self.is_excluded(e, *device, &mut privileged_paths))\n            {\n                let path = match entry {\n                    Ok(entry) => entry.path().to_owned(),\n                    Err(e) => {\n                        error!(\"walking directory failed: {} {}\", root_path.to_str().unwrap(), e);\n                        continue;\n                    }\n                };\n\n                let stat = match path.metadata() {\n                    Ok(stat) => stat,\n                    Err(e) => {\n                        error!(\"stat on {} failed with {}\", path.to_str().unwrap(), e);\n                        continue;\n                    }\n                };\n\n                block_size = stat.blksize();\n\n                let file_id = if let Some(id) = inode_map.get(&stat.ino()) {\n                    id.clone()\n                } else {\n                    continue;\n                };\n\n                // We cannot issue a normal readahead on directories. So we skip those records that\n                // belong to directories.\n                if stat.file_type().is_dir() {\n                    info!(\n                        \"skipping directory readahead record for file_id:{file_id} ino:{} path:{} \",\n                        stat.ino(),\n                        path.to_str().unwrap()\n                    );\n                    directories.insert(file_id.clone());\n                    continue;\n                }\n\n                rf.insert_or_update_inode(file_id, &stat, path.to_str().unwrap().to_owned());\n            }\n\n            rf.inner.filesystems.insert(*device, FsInfo { block_size });\n        }\n\n        self.debug_info.privileged_paths.append(&mut privileged_paths);\n\n        for (device, inode_map) in &self.device_inode_map {\n            for (inode, file_id) in inode_map {\n                if !rf.inner.inode_map.contains_key(file_id) {\n                    let major_no: MajorMinorType = major(*device);\n                    let minor_no: MajorMinorType = minor(*device);\n                    self.debug_info.missing_files.insert(\n                        file_id.clone(),\n                        MissingFile { major_no, minor_no, inode: *inode, records: vec![] },\n                    );\n                }\n            }\n        }\n\n        // Remove all records that belong to directories or for which we did not find paths.\n        let mut records = vec![];\n        for record in take(&mut self.records) {\n            if directories.contains(&record.file_id) {\n                self.debug_info.directory_read_bytes += record.length;\n            } else if let Some(missing_file) =\n                self.debug_info.missing_files.get_mut(&record.file_id)\n            {\n                self.debug_info.missing_path_bytes += record.length;\n                missing_file.records.push(record);\n            } else {\n                records.push(record);\n            }\n        }\n\n        warn!(\n            \"Recorded {} bytes worth of data read from directories\",\n            self.debug_info.directory_read_bytes\n        );\n        warn!(\n            \"Recorded {} bytes worth of data read from files that don't have paths\",\n            self.debug_info.missing_path_bytes\n        );\n\n        rf.inner.records = coalesce_records(records, true);\n\n        Ok(rf)\n    }\n\n    fn serialize(&self, write: &mut dyn Write) -> Result<(), Error> {\n        write\n            .write_all(\n                &serde_json::to_vec(&self)\n                    .map_err(|e| Error::Serialize { error: e.to_string() })?,\n            )\n            .map_err(|source| Error::Write { path: \"intermediate file\".to_owned(), source })\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use nix::sys::stat::{major, minor};\n    use std::assert_eq;\n    use std::path::Path;\n\n    use crate::tracer::tests::{copy_uncached_files_and_record_from, setup_test_dir};\n\n    use crate::replay::tests::generate_cached_files_and_record;\n\n    use super::*;\n\n    static TRACE_BUFFER: &str = concat!(\n        // kernel 5.x\n        \" Settingide-502  [001] ....   484.360292: mm_filemap_add_to_page_CACHE: dev 254:6 ino cf1 page=68d477 pfn=59833 ofs=32768\\n\",\n        \" Settingide-502  [001] ....   484.360311: mm_filemap_add_to_page_cache: dev 254:6 ino cf1 page=759458 pfn=59827 ofs=57344\\n\",\n        \" BOX_ENTDED-3071 [001] ....   485.276715: mm_filemap_add_to_pag_ecache: dev 254:6 ino 1 page=00cc1c pfn=81748 ofs=13574144\\n\",\n        \" BOX_ENTDED-3071 [001] ....   485.276990: mm_filemap_add_to_page_cache: dev 254:6 ino cf2 page=36540b pfn=60952 ofs=0\\n\",\n        \" .gms.peent-843  [001] ....   485.545516: mm_filemap_add_to_page_cache: dev 254:6 ino 1 page=002e8b pfn=58928 ofs=13578240\\n\",\n        \" .gms.peent-843  [001] ....   485.545820: mm_filemap_add_to_page_cache: dev 254:6 ino cf3 page=6233ce pfn=58108 ofs=0\\n\",\n        \"      an.bg-459  [001] ....   494.029396: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=c5b5c7 pfn=373933 ofs=1310720\\n\",\n        \"      an.bg-459  [001] ....   494.029398: mm_filemap_add_to_page_cache: dev 254:3 ino 7cf page=b8b9ec pfn=410074 ofs=1314816\\n\",\n\n        // kernel 6.x\n        \" logcat-686     [006] ..... 148216.040320: mm_filemap_add_to_page_CACHE: dev 254:85 ino 3f15 pfn=0x213bc2 ofs=528384 order=0\\n\",\n        \" logcat-686     [001] ..... 148217.776227: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x21d306 ofs=532480 order=0\\n\",\n        \" logcat-686     [003] ..... 148219.044389: mm_filemap_add_to_pag_ecache: dev 254:85 ino 3f15 pfn=0x224b8d ofs=536576 order=0\\n\",\n        \" logcat-686     [001] ..... 148220.780964: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x1bfe0a ofs=540672 order=0\\n\",\n        \" logcat-686     [001] ..... 148223.046560: mm_filemap_add_to_page_cache: dev 254:85 ino 3f15 pfn=0x1f3d29 ofs=544768 order=0\",\n    );\n\n    fn sample_mem_traces() -> (String, Vec<Option<TraceLineInfo>>) {\n        (\n            TRACE_BUFFER.to_owned(),\n            vec![\n                // 5.x\n                None,\n                Some(TraceLineInfo::from_fields(254, 6, 0xcf1, 57344, 484360311000)),\n                None,\n                Some(TraceLineInfo::from_fields(254, 6, 0xcf2, 0, 485276990000)),\n                Some(TraceLineInfo::from_fields(254, 6, 0x1, 13578240, 485545516000)),\n                Some(TraceLineInfo::from_fields(254, 6, 0xcf3, 0, 485545820000)),\n                Some(TraceLineInfo::from_fields(254, 3, 0x7cf, 1310720, 494029396000)),\n                Some(TraceLineInfo::from_fields(254, 3, 0x7cf, 1314816, 494029398000)),\n                // 6.x\n                None,\n                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 532480, 148217776227000)),\n                None,\n                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 540672, 148220780964000)),\n                Some(TraceLineInfo::from_fields(254, 85, 0x3f15, 544768, 148223046560000)),\n            ],\n        )\n    }\n\n    #[test]\n    fn test_parse_trace_line() {\n        let (buf, res) = sample_mem_traces();\n        let re = TraceLineInfo::get_trace_line_regex().unwrap();\n        for (index, line) in buf.lines().enumerate() {\n            let found = TraceLineInfo::from_trace_line(&re, line).unwrap();\n            let expected = res.get(index).unwrap();\n            assert_eq!(found.is_some(), expected.is_some());\n            if found.is_some() {\n                assert_eq!(found.unwrap(), *expected.as_ref().unwrap());\n            }\n        }\n    }\n\n    #[test]\n    fn test_add_line() {\n        let test_base_dir = setup_test_dir();\n        let (rf, mut files) =\n            generate_cached_files_and_record(None, true, Some(page_size().unwrap() as u64));\n        let (_uncached_rf, uncached_files) =\n            copy_uncached_files_and_record_from(Path::new(&test_base_dir), &mut files, &rf);\n        let mut mount_include = HashMap::new();\n\n        let included_dev = uncached_files.get(0).unwrap().0.metadata().unwrap().dev();\n        let included_inode1 = uncached_files.get(0).unwrap().0.metadata().unwrap().ino();\n        let included_inode2 = uncached_files.get(1).unwrap().0.metadata().unwrap().ino();\n        let included_major = major(included_dev);\n        let included_minor = minor(included_dev);\n        mount_include.insert(included_dev, std::fs::canonicalize(test_base_dir).unwrap());\n        let mut mount_exclude = HashSet::new();\n        mount_exclude.insert(0);\n\n        let mut mem_tracer = MemTraceSubsystem {\n            device_inode_map: HashMap::new(),\n            inode_count: 0,\n            records: vec![],\n            regex: TraceLineInfo::get_trace_line_regex().unwrap(),\n            mount_info: MountInfo {\n                included_devices: mount_include,\n                excluded_devices: mount_exclude,\n            },\n            tracer_configs: None,\n            page_size: page_size().unwrap() as u64,\n            debug_info: DebugInfo {\n                missing_files: HashMap::new(),\n                directory_read_bytes: 0,\n                missing_path_bytes: 0,\n                privileged_paths: vec![],\n            },\n        };\n\n        let pg_size = page_size().unwrap();\n        // Format is major, minor, inode, offset\n        let inputs = vec![\n            (0, 0, 2, 10), // to be excluded. bad device.\n            (included_major, included_minor, included_inode1, 0),\n            (included_major, included_minor, included_inode1, 3 * pg_size),\n            // duplicate read\n            (included_major, included_minor, included_inode1, 3 * pg_size),\n            (0, 0, included_inode1, 10), // to be excluded. bad device.\n            (included_major, included_minor, included_inode1, 2 * pg_size), // contiguous\n            // non-contiguous\n            (included_major, included_minor, included_inode1, 12 * pg_size),\n            // same offset different inode\n            (included_major, included_minor, included_inode2, 3 * pg_size),\n            // Contiguous offset different inode\n            (included_major, included_minor, included_inode2, pg_size),\n        ];\n\n        for (i, (major, minor, inode, offset)) in inputs.iter().enumerate() {\n            // used to timestamp the log line.\n            let seconds = i;\n            // used to timestamp the log line.\n            let microseconds = i;\n            for operation in &[\"mm_filemap_add_to_page_cache\", \"some_other_operation\"] {\n                let line = format!(\n                    \" BOX_ENTRY_ADDED-3071    [001] ....   {}.{}: {}: \\\n                    dev {}:{} ino {:x} page=00000000f936540b pfn=60952 ofs={}\",\n                    seconds, microseconds, operation, major, minor, inode, offset\n                );\n                mem_tracer.add_line(&line).unwrap();\n            }\n        }\n        assert_eq!(mem_tracer.records.len(), 7);\n        assert_eq!(mem_tracer.device_inode_map.len(), 1);\n        assert_eq!(mem_tracer.device_inode_map.get(&included_dev).unwrap().len(), 2);\n        assert!(mem_tracer\n            .device_inode_map\n            .get(&included_dev)\n            .unwrap()\n            .contains_key(&included_inode1));\n        assert!(mem_tracer\n            .device_inode_map\n            .get(&included_dev)\n            .unwrap()\n            .contains_key(&included_inode2));\n    }\n\n    fn new_record(file: u64, offset: u64, length: u64, timestamp: u64) -> Record {\n        Record { file_id: FileId(file), offset, length, timestamp }\n    }\n\n    #[test]\n    fn test_get_records_file() {\n        let test_base_dir = setup_test_dir();\n        let (rf, mut files) =\n            generate_cached_files_and_record(None, true, Some(page_size().unwrap() as u64));\n        let (_uncached_rf, uncached_files) =\n            copy_uncached_files_and_record_from(Path::new(&test_base_dir), &mut files, &rf);\n        let mut mount_include = HashMap::new();\n\n        let included_dev = uncached_files.get(0).unwrap().0.metadata().unwrap().dev();\n        let included_inode1 = uncached_files.get(0).unwrap().0.metadata().unwrap().ino();\n        let included_inode2 = uncached_files.get(1).unwrap().0.metadata().unwrap().ino();\n        let included_major = major(included_dev);\n        let included_minor = minor(included_dev);\n        mount_include.insert(included_dev, std::fs::canonicalize(test_base_dir).unwrap());\n        let mut mount_exclude = HashSet::new();\n        mount_exclude.insert(0);\n\n        let mut mem_tracer = MemTraceSubsystem {\n            device_inode_map: HashMap::new(),\n            inode_count: 0,\n            records: vec![],\n            regex: TraceLineInfo::get_trace_line_regex().unwrap(),\n            mount_info: MountInfo {\n                included_devices: mount_include,\n                excluded_devices: mount_exclude,\n            },\n            tracer_configs: None,\n            page_size: page_size().unwrap() as u64,\n            debug_info: DebugInfo {\n                missing_files: HashMap::new(),\n                directory_read_bytes: 0,\n                missing_path_bytes: 0,\n                privileged_paths: vec![],\n            },\n        };\n\n        let pg_size = page_size().unwrap() as u64;\n        // Format is major, minor, inode, offset\n        let inputs = vec![\n            (0, 0, 2, 10), // to be excluded. bad device.\n            (included_major, included_minor, included_inode1, 0),\n            (included_major, included_minor, included_inode1, 3 * pg_size),\n            // duplicate read\n            (included_major, included_minor, included_inode1, 3 * pg_size),\n            (0, 0, included_inode1, 10), // to be excluded. bad device.\n            (included_major, included_minor, included_inode1, 2 * pg_size), // contiguous\n            // non-contiguous\n            (included_major, included_minor, included_inode1, 12 * pg_size),\n            // same offset different inode\n            (included_major, included_minor, included_inode2, 3 * pg_size),\n            // Contiguous offset different inode\n            (included_major, included_minor, included_inode2, pg_size),\n        ];\n\n        for (i, (major, minor, inode, offset)) in inputs.iter().enumerate() {\n            // used to timestamp the log line.\n            let seconds = i;\n            // used to timestamp the log line.\n            let microseconds = i;\n            for operation in &[\"mm_filemap_add_to_page_cache\", \"some_other_operation\"] {\n                let line = format!(\n                    \" BOX_ENTRY_ADDED-3071    [001] ....   {}.{}: {}: \\\n                    dev {}:{} ino {:x} page=00000000f936540b pfn=60952 ofs={}\",\n                    seconds, microseconds, operation, major, minor, inode, offset\n                );\n                mem_tracer.add_line(&line).unwrap();\n            }\n        }\n        let rf = mem_tracer.build_records_file().unwrap();\n        assert_eq!(\n            rf.inner.records,\n            vec![\n                new_record(0, 0, pg_size, 1000001000),\n                new_record(0, 2 * pg_size, 2 * pg_size, 2000002000),\n                new_record(0, 12 * pg_size, pg_size, 6000006000),\n                new_record(1, pg_size, pg_size, 8000008000),\n                new_record(1, 3 * pg_size, pg_size, 7000007000),\n            ]\n        );\n    }\n}\n"
  },
  {
    "path": "init/libprefetch/prefetch/src/tracer/mod.rs",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! Tracer supports collecting information based off of two different tracing\n//! subsystems within `/sys/kernel/tracing`.\n//!\n//! ## Mem\n//! Mem is preferred tracer.\n//! ### Phase 1:\n//! This phase relies on a trace event at\n//! \"events/filemap/mm_filemap_add_to_page_cache\". When enabled, the event logs\n//! a message that contains device id, inode number, offset of the page that is\n//! being read. The tracer makes a note of this.\n//!\n//! ### Phase 2:\n//! When the recording of events is done, tracer all get mount points for which\n//! device id is recorded. Once it knows the mount points, it looks up file\n//! paths for the inode numbers that it records. The paths, offset and lengths\n//! are then stored in records file.\n//!\n//! Phase 2 is very IO intensive as entire filesystem is walked to find paths\n//! for different inodes.\n//!\npub(crate) mod mem;\n\nuse std::{\n    boxed::Box,\n    collections::HashSet,\n    fs::{create_dir, read_to_string, rename, File, OpenOptions},\n    io::{BufRead, BufReader, Read, Write},\n    path::{Path, PathBuf},\n    string::ToString,\n    sync::mpsc::{self, Receiver, Sender},\n};\n\nuse log::{error, info};\nuse nix::time::ClockId;\nuse serde::Deserialize;\nuse serde::Serialize;\n\nuse crate::error::Error;\nuse crate::{args::TracerType, format::RecordsFile};\nuse mem::MemTraceSubsystem;\n\npub(crate) static EXCLUDE_PATHS: &[&str] =\n    &[\"/dev/\", \"/proc/\", \"/sys/\", \"/tmp/\", \"/run/\", \"/config/\", \"/mnt/\", \"/storage/\"];\n\n/// During record phase, prefetch may modify files under `/sys/kernel/tracing/` to\n/// - change trace buffer size so that we don't lose trace events\n/// - enable a few trace events\n/// - enable tracing\n///\n///  The old values are restored at the end of record.\n#[derive(Debug, Serialize, Deserialize)]\npub(crate) struct TraceEventFile {\n    path: PathBuf,\n    restore_value: Option<String>,\n}\n\nimpl TraceEventFile {\n    fn open_and_write(path: &Path, value: &str) -> Result<(), Error> {\n        let mut f = OpenOptions::new()\n            .write(true)\n            .read(true)\n            .open(path)\n            .map_err(|e| Error::Open { source: e, path: path.to_str().unwrap().to_string() })?;\n        f.write_all(value.as_bytes())\n            .map_err(|e| Error::Write { path: path.to_str().unwrap().to_owned(), source: e })\n    }\n\n    pub fn write(path: PathBuf, value: &str) -> Result<Self, Error> {\n        let restore_value = read_to_string(&path).map_err(|s| Error::Read {\n            error: format!(\"Reading {} failed:{}\", path.to_str().unwrap(), s),\n        })?;\n\n        Self::open_and_write(&path, value)?;\n\n        info!(\n            \"Changed contents of {} from {:?} to {}\",\n            path.to_str().unwrap(),\n            restore_value,\n            value\n        );\n        Ok(Self { path, restore_value: Some(restore_value) })\n    }\n\n    pub fn enable(path: PathBuf) -> Result<Self, Error> {\n        Self::write(path, \"1\")\n    }\n\n    pub fn restore(&self) -> Result<(), Error> {\n        if let Some(restore_value) = &self.restore_value {\n            Self::open_and_write(&self.path, restore_value)\n        } else {\n            Ok(())\n        }\n    }\n}\n\nimpl Drop for TraceEventFile {\n    fn drop(&mut self) {\n        if let Err(ret) = self.restore() {\n            error!(\n                \"Failed to restore state of file {:?} with value: {:?}. Error: {}\",\n                self.path,\n                self.restore_value,\n                ret.to_string()\n            );\n        }\n    }\n}\n\n#[derive(Debug, Deserialize, Serialize)]\npub(crate) struct TracerConfigs {\n    pub excluded_paths: Vec<String>,\n    pub buffer_size_file_path: String,\n    pub trace_base_path: PathBuf,\n    pub trace_events: Vec<String>,\n    pub mountinfo_path: Option<String>,\n    pub trace_operations: HashSet<String>,\n    // We never read back these fields. The only use for holding these around is to restore state at\n    // the end of run.\n    #[allow(dead_code)]\n    trace_files: Vec<TraceEventFile>,\n}\n\nimpl TracerConfigs {\n    pub fn new(\n        kb_buffer_size: Option<u64>,\n        setup_tracing: bool,\n        tracer_type: TracerType,\n        trace_mount_point: Option<String>,\n        tracing_instance: Option<String>,\n    ) -> Result<Self, Error> {\n        static TRACE_MOUNT_POINT: &str = \"/sys/kernel/tracing\";\n\n        // Trace buffer size file relative to trace mount point\n        static TRACE_BUFFER_SIZE_FILE: &str = \"buffer_size_kb\";\n\n        let trace_mount_point = trace_mount_point.unwrap_or_else(|| TRACE_MOUNT_POINT.to_owned());\n        let trace_base_path = if let Some(instance) = tracing_instance {\n            Path::new(&trace_mount_point).join(\"instances\").join(instance)\n        } else {\n            Path::new(&trace_mount_point).to_owned()\n        };\n\n        if setup_tracing && !trace_base_path.exists() {\n            create_dir(&trace_base_path).map_err(|e| Error::Create {\n                source: e,\n                path: trace_base_path.to_str().unwrap().to_owned(),\n            })?;\n        }\n\n        if !trace_base_path.exists() {\n            return Err(Error::Custom {\n                error: format!(\n                    \"trace mount point doesn't exist: {}\",\n                    trace_base_path.to_str().unwrap().to_owned()\n                ),\n            });\n        }\n\n        let mut configs = TracerConfigs {\n            excluded_paths: vec![],\n            buffer_size_file_path: TRACE_BUFFER_SIZE_FILE.to_owned(),\n            trace_base_path,\n            trace_events: vec![],\n            mountinfo_path: None,\n            trace_operations: HashSet::new(),\n            trace_files: vec![],\n        };\n\n        match tracer_type {\n            TracerType::Mem => MemTraceSubsystem::update_configs(&mut configs),\n        }\n\n        if setup_tracing {\n            let trace_base_dir = Path::new(&configs.trace_base_path);\n            if let Some(kb_buffer_size) = kb_buffer_size {\n                configs.trace_files.push(TraceEventFile::write(\n                    trace_base_dir.join(&configs.buffer_size_file_path),\n                    &kb_buffer_size.to_string(),\n                )?);\n            }\n            for path in &configs.trace_events {\n                configs.trace_files.push(TraceEventFile::enable(trace_base_dir.join(path))?);\n            }\n        }\n\n        Ok(configs)\n    }\n}\n\n/// Returns time, in nanoseconds, since boot\npub fn nanoseconds_since_boot() -> u64 {\n    if let Ok(t) = nix::time::clock_gettime(ClockId::CLOCK_MONOTONIC) {\n        //((t.tv_sec() * 1_000_000_000) + t.tv_nsec()) as u64\n        (1 + t.tv_nsec()) as u64\n    } else {\n        0\n    }\n}\n\npub(crate) trait TraceSubsystem {\n    /// This routine is called whenever there is a new line available to be parsed.\n    /// The impl potentially want to parse the line and retain the data in memory.\n    /// Implementors are not expected to do heavy lifting tasks, like IO, in this context.\n    fn add_line(&mut self, line: &str) -> Result<(), Error>;\n\n    /// Generates a records file from all the collected data.\n    /// From this context, the implementors might process data by issuing queries to filesystems.\n    fn build_records_file(&mut self) -> Result<RecordsFile, Error>;\n\n    /// This helps us serialize internat state of tracing subsystem during record phase.\n    /// This allows us to get raw data for analysis of read pattern and debugging in situations\n    /// when we might not have access to system yet(ex. early boot phase) .\n    fn serialize(&self, writer: &mut dyn Write) -> Result<(), Error>;\n}\n\n/// Returns page size in bytes\npub(crate) fn page_size() -> Result<usize, Error> {\n    Ok(nix::unistd::sysconf(nix::unistd::SysconfVar::PAGE_SIZE)\n        .map_err(|e| Error::Custom { error: format!(\"failed to query page size: {}\", e) })?\n        .ok_or(Error::Custom { error: \"failed to query page size: None returned\".to_string() })?\n        as usize)\n}\n\npub struct Tracer {\n    // Open handle to static trace buffer file which is usually located at\n    // `/sys/kernel/tracing/trace`.\n    // See comment on top of `trace` function.\n    trace_file: BufReader<File>,\n\n    // Open handle to trace pipe which is usually located at\n    // `/sys/kernel/tracing/trace_pipe`.\n    // See comment on top of `trace` function.\n    trace_pipe: BufReader<File>,\n\n    // Signal to exit the infinite loop in `trace()`\n    exit_rx: Receiver<()>,\n\n    // tracing subsystem that actually parses trace lines and builds records.\n    tracing_subsystem: Box<dyn TraceSubsystem + Send>,\n}\n\nimpl Tracer {\n    pub fn create(\n        kb_buffer_size: Option<u64>,\n        tracer_type: TracerType,\n        tracing_instance: Option<String>,\n        setup_tracing: bool,\n    ) -> Result<(Self, Sender<()>), Error> {\n        /// Trace pipe path relative to trace mount point\n        static TRACE_PIPE_PATH: &str = \"trace_pipe\";\n\n        /// Trace file path relative to trace mount point\n        static TRACE_FILE_PATH: &str = \"trace\";\n\n        let configs = TracerConfigs::new(\n            kb_buffer_size,\n            setup_tracing,\n            tracer_type.clone(),\n            None,\n            tracing_instance,\n        )?;\n\n        let pipe_path = Path::new(&configs.trace_base_path).join(TRACE_PIPE_PATH);\n        let trace_pipe = File::open(&pipe_path)\n            .map_err(|e| Error::Open { source: e, path: pipe_path.to_str().unwrap().to_owned() })?;\n\n        let file_path = Path::new(&configs.trace_base_path).join(TRACE_FILE_PATH);\n        let trace_file = File::open(&file_path)\n            .map_err(|e| Error::Open { source: e, path: file_path.to_str().unwrap().to_owned() })?;\n        let tracer: Box<dyn TraceSubsystem + Send> = match tracer_type {\n            TracerType::Mem => Box::new(MemTraceSubsystem::create_with_configs(configs)?),\n        };\n\n        Self::create_with_config(trace_file, trace_pipe, tracer)\n    }\n\n    fn create_with_config(\n        file: File,\n        pipe: File,\n        tracer: Box<dyn TraceSubsystem + Send>,\n    ) -> Result<(Self, Sender<()>), Error> {\n        let (exit_tx, exit_rx) = mpsc::channel();\n        let trace_pipe = BufReader::new(pipe);\n        let trace_file = BufReader::new(file);\n\n        Ok((Self { trace_file, trace_pipe, exit_rx, tracing_subsystem: tracer }, exit_tx))\n    }\n\n    fn save_intermediate_state(&self, intermediate_file: Option<&PathBuf>) -> Result<(), Error> {\n        if let Some(int_path) = intermediate_file {\n            let mut tmp_file = int_path.clone();\n            tmp_file.set_extension(\"int.tmp\");\n            let mut out_file = File::create(&tmp_file).map_err(|source| Error::Create {\n                source,\n                path: int_path.to_str().unwrap().to_owned(),\n            })?;\n            self.tracing_subsystem.serialize(&mut out_file)?;\n            rename(&tmp_file, int_path).map_err(|e| Error::Custom {\n                error: format!(\n                    \"rename file from{} to:{} failed with {}\",\n                    tmp_file.to_str().unwrap(),\n                    int_path.to_str().unwrap(),\n                    e\n                ),\n            })?;\n        }\n        Ok(())\n    }\n\n    /// This routine parses all the events since last reset of trace buffer.\n    ///\n    /// The linux tracing subsystem exposes two interfaces to get trace events from\n    /// 1. a file - usually at `/sys/kernel/tracing/trace`\n    /// 2. a pipe - usually at `/sys/kernel/tracing/trace_pipe`\n    ///\n    /// The file is *sort of* ring buffer which works off of `buffer_size_kb` sized buffer.\n    /// Relying on it is not very efficient as we end up getting a lot of duplicates.\n    ///\n    /// The pipe only contains line traces. Any trace events that occurred before opening\n    /// of this file are lost.\n    ///\n    /// IMPORTANT: The moment we start reading from the pipe, the events in the file\n    /// disappear/reset. So we should read file entirely before we start reading the pipe.\n    pub fn trace(&mut self, intermediate_file: Option<&PathBuf>) -> Result<RecordsFile, Error> {\n        let mut buf = String::new();\n        self.trace_file\n            .read_to_string(&mut buf)\n            .map_err(|e| Error::Read { error: format!(\"failed to read trace file: {}\", e) })?;\n\n        for line in buf.lines() {\n            let trimmed = line.trim_end();\n            self.tracing_subsystem.add_line(trimmed)?;\n        }\n\n        // The logic here is to block on trace_pipe forever. We break out of loop only when we read\n        // a line from the pipe *and* we have received an event on exit_rx.\n        // This logic works because the system will have one or more read syscalls and also we,\n        // at the moment, use prefetch on build systems and not in production to generate records\n        // file.\n        //\n        // TODO(b/302045304): async read trace_pipe.\n        while self.exit_rx.try_recv().is_err() {\n            let mut line = String::new();\n            let len = self\n                .trace_pipe\n                .read_line(&mut line)\n                .map_err(|e| Error::Read { error: e.to_string() })?;\n            let trimmed = line.trim_end();\n            if len == 0 {\n                // We should never read zero length line or reach EOF of the pipe.\n                return Err(Error::Read {\n                    error: \"read zero length line from trace_pipe\".to_string(),\n                });\n            }\n            self.tracing_subsystem.add_line(trimmed)?;\n        }\n\n        // We are here because the above loop exited normally. Traced lines are stored in `Self`.\n        // Build `RecordsFile` from processing data from read lines above.\n        self.save_intermediate_state(intermediate_file)?;\n        let rf = self.tracing_subsystem.build_records_file()?;\n        self.save_intermediate_state(intermediate_file)?;\n        Ok(rf)\n    }\n}\n\n#[cfg(test)]\npub(crate) mod tests {\n    use crate::RecordsFile;\n\n    use std::alloc::Layout;\n    use std::borrow::ToOwned;\n    use std::convert::TryInto;\n    use std::fs::{create_dir_all, OpenOptions};\n    use std::io::Read;\n    use std::io::Seek;\n    use std::io::Write;\n    use std::ops::Range;\n    use std::os::linux::fs::MetadataExt;\n    use std::os::unix::fs::symlink;\n    use std::os::unix::prelude::OpenOptionsExt;\n    use std::path::Path;\n    use std::thread;\n    use std::time::Duration;\n    use std::{assert_eq, env};\n\n    use libc::O_DIRECT;\n    use nix::sys::stat::{major, minor};\n    use nix::unistd::pipe;\n    use rand::distributions::Alphanumeric;\n    use rand::Rng;\n    use tempfile::NamedTempFile;\n\n    use super::*;\n    use crate::replay::tests::generate_cached_files_and_record;\n    use std::ops::{Deref, DerefMut};\n\n    #[test]\n    fn trace_event_file_enable_and_restore() {\n        let mut file = NamedTempFile::new().unwrap();\n        let _ = file.write(\"0\".as_bytes()).unwrap();\n        {\n            let _e = TraceEventFile::enable(file.path().to_owned()).unwrap();\n            assert_eq!(read_to_string(file.path()).unwrap(), \"1\");\n        }\n        assert_eq!(read_to_string(file.path()).unwrap(), \"0\");\n    }\n\n    #[test]\n    fn trace_event_file_write_and_restore() {\n        let mut file = NamedTempFile::new().unwrap();\n        let _ = file.write(\"hello\".as_bytes()).unwrap();\n        {\n            let _e = TraceEventFile::write(file.path().to_owned(), \"world\").unwrap();\n            assert_eq!(read_to_string(file.path()).unwrap(), \"world\");\n        }\n        assert_eq!(read_to_string(file.path()).unwrap(), \"hello\");\n    }\n\n    fn setup_trace_mount_point(\n        create_mount_point: bool,\n        create_instances: bool,\n        instance_name: Option<String>,\n    ) -> PathBuf {\n        assert!(\n            create_mount_point || !create_instances,\n            \"cannot create instances without creating mount point\"\n        );\n\n        let mount_point = env::temp_dir().join(\n            rand::thread_rng()\n                .sample_iter(&Alphanumeric)\n                .take(10)\n                .map(char::from)\n                .collect::<String>(),\n        );\n\n        let mut base_path = Path::new(&mount_point).to_owned();\n        if create_mount_point {\n            create_dir(&mount_point).unwrap();\n        }\n\n        if create_instances {\n            base_path = base_path.join(\"instances\");\n            if let Some(instance_name) = &instance_name {\n                base_path = base_path.join(instance_name)\n            }\n            create_dir_all(&base_path).unwrap();\n        }\n\n        if create_mount_point || create_instances {\n            std::fs::write(&base_path.join(\"buffer_size_kb\"), \"100\").unwrap();\n            std::fs::write(&base_path.join(\"tracing_on\"), \"0\").unwrap();\n            std::fs::write(&base_path.join(\"trace\"), \"0\").unwrap();\n            std::fs::write(&base_path.join(\"trace_pipe\"), \"0\").unwrap();\n\n            for event in [\n                \"events/fs/do_sys_open\",\n                \"events/fs/open_exec\",\n                \"events/fs/uselib\",\n                \"events/filemap/mm_filemap_add_to_page_cache\",\n            ] {\n                let event_path = base_path.join(event);\n                std::fs::create_dir_all(&event_path).unwrap();\n                std::fs::write(&event_path.join(\"enable\"), \"0\").unwrap();\n            }\n        }\n        mount_point\n    }\n\n    #[test]\n    fn test_configs_no_setup() {\n        let mount_point = setup_trace_mount_point(true, true, None);\n        let _configs = TracerConfigs::new(\n            Some(10),\n            false,\n            TracerType::Mem,\n            Some(mount_point.to_str().unwrap().to_owned()),\n            None,\n        )\n        .unwrap();\n    }\n\n    #[test]\n    fn test_configs_no_setup_no_mount_point() {\n        let mount_point = setup_trace_mount_point(false, false, None);\n        assert_eq!(\n            TracerConfigs::new(\n                Some(10),\n                false,\n                TracerType::Mem,\n                Some(mount_point.to_str().unwrap().to_owned()),\n                None,\n            )\n            .unwrap_err()\n            .to_string(),\n            format!(\n                \"Failed to setup prefetch: trace mount point doesn't exist: {}\",\n                mount_point.to_str().unwrap()\n            )\n        );\n    }\n\n    #[test]\n    fn test_configs_no_setup_no_instances() {\n        let mount_point = setup_trace_mount_point(true, false, None);\n        assert_eq!(\n            TracerConfigs::new(\n                Some(10),\n                false,\n                TracerType::Mem,\n                Some(mount_point.to_str().unwrap().to_owned()),\n                Some(\"my_instance\".to_owned()),\n            )\n            .unwrap_err()\n            .to_string(),\n            format!(\n                \"Failed to setup prefetch: trace mount point doesn't exist: {}/instances/my_instance\",\n                mount_point.to_str().unwrap()\n            )\n        );\n    }\n\n    #[test]\n    fn test_configs_setup_without_instances() {\n        let mount_point = setup_trace_mount_point(true, false, None);\n        assert!(TracerConfigs::new(\n            Some(10),\n            true,\n            TracerType::Mem,\n            Some(mount_point.to_str().unwrap().to_owned()),\n            None\n        )\n        .is_ok());\n    }\n\n    #[test]\n    fn test_configs_setup_with_instances() {\n        let mount_point = setup_trace_mount_point(true, true, Some(\"my_instance\".to_owned()));\n        assert!(TracerConfigs::new(\n            Some(10),\n            true,\n            TracerType::Mem,\n            Some(mount_point.to_str().unwrap().to_owned()),\n            Some(\"my_instance\".to_owned())\n        )\n        .is_ok())\n    }\n\n    pub(crate) fn setup_test_dir() -> PathBuf {\n        let test_base_dir: String = rand::thread_rng()\n            .sample_iter(&rand::distributions::Alphanumeric)\n            .take(7)\n            .map(char::from)\n            .collect();\n        let test_base_dir = format!(\n            \"{}/test/{}\",\n            std::fs::read_link(\"/proc/self/exe\").unwrap().parent().unwrap().to_str().unwrap(),\n            test_base_dir\n        );\n        std::fs::create_dir_all(&test_base_dir).unwrap();\n        PathBuf::from(test_base_dir)\n    }\n\n    fn modify_records_file(rf: &RecordsFile, target: &str) -> RecordsFile {\n        let mut modified_rf = rf.clone();\n\n        for inode in modified_rf.inner.inode_map.values_mut() {\n            let new_paths: Vec<String> = inode\n                .paths\n                .iter()\n                .map(|s| {\n                    let parent = Path::new(s).parent().unwrap().to_str().unwrap();\n                    s.replace(parent, target)\n                })\n                .collect();\n\n            inode.paths = new_paths;\n        }\n\n        modified_rf\n    }\n\n    struct AlignedBuffer {\n        ptr: *mut u8,\n        len: usize,\n        layout: Layout,\n    }\n\n    impl AlignedBuffer {\n        fn new(size: usize, alignment: usize) -> Result<Self, Error> {\n            if size == 0 {\n                return Err(Error::Custom { error: \"cannot allocate zero bytes\".to_string() });\n            }\n\n            let layout = Layout::from_size_align(size, alignment).unwrap();\n            // SAFETY:\n            // - `size` is a valid non-zero positive integer representing the desired buffer size.\n            // - The layout is checked for validity using `.unwrap()`.\n            let ptr = unsafe { std::alloc::alloc(layout) };\n            if ptr.is_null() {\n                return Err(Error::Custom { error: format!(\"alloc failed: size: {}\", size) });\n            }\n            Ok(AlignedBuffer { ptr, len: size, layout })\n        }\n    }\n\n    impl Deref for AlignedBuffer {\n        type Target = [u8];\n        // SAFETY:\n        // - self.ptr is a valid pointer obtained from a successful allocation in the new() method.\n        // - self.len is a valid length used for allocation in the new() method.\n        fn deref(&self) -> &Self::Target {\n            unsafe { std::slice::from_raw_parts(self.ptr, self.len) }\n        }\n    }\n\n    impl DerefMut for AlignedBuffer {\n        // SAFETY:\n        // - self.ptr is a valid pointer obtained from a successful allocation in the new() method.\n        // - self.len is a valid length used for allocation in the new() method.\n        fn deref_mut(&mut self) -> &mut Self::Target {\n            unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) }\n        }\n    }\n\n    impl Drop for AlignedBuffer {\n        fn drop(&mut self) {\n            // SAFETY:\n            //  - self.ptr is a valid pointer obtained from a successful allocation in the new() method.\n            //  - self.layout is the Layout used to allocate the memory.\n            unsafe {\n                std::alloc::dealloc(self.ptr, self.layout);\n            }\n        }\n    }\n\n    // Copies `files` into directory pointed by `base`.\n    //\n    // The newly created file's data is potentially uncached - i.e. the new\n    // files are opened in O_DIRECT.\n    //\n    // WARNING: Though this function makes an attempt to copy into uncached files\n    // but it cannot guarantee as other processes in the system may access the\n    // files. This may lead to flaky tests or unexpected results.\n    pub(crate) fn copy_uncached_files_and_record_from(\n        base: &Path,\n        files: &mut [(NamedTempFile, Vec<Range<u64>>)],\n        rf: &RecordsFile,\n    ) -> (RecordsFile, Vec<(PathBuf, Vec<Range<u64>>)>) {\n        let mut new_files = vec![];\n        for (in_file, ranges) in files {\n            let out_path = base.join(in_file.path().file_name().unwrap());\n            let mut out_file = OpenOptions::new()\n                .read(true)\n                .write(true)\n                .custom_flags(O_DIRECT)\n                .create_new(true)\n                .open(&out_path)\n                .expect(\"Can't open\");\n            let page_size = page_size().unwrap() as u64;\n            let in_file_size = in_file.metadata().unwrap().len();\n            assert_eq!(\n                in_file_size % page_size,\n                0,\n                \"we create files that are aligned to page size\"\n            );\n            let out_file_size = in_file_size;\n            let mut buf =\n                AlignedBuffer::new(out_file_size.try_into().unwrap(), page_size as usize).unwrap();\n            let _ = in_file.read(&mut *buf).unwrap();\n            out_file.write_all(&*buf).unwrap();\n\n            new_files.push((out_path, ranges.clone()));\n        }\n\n        for inode in rf.inner.inode_map.values() {\n            for path in &inode.paths {\n                let in_path = Path::new(&path);\n                let out_path = base.join(in_path.file_name().unwrap());\n                if !out_path.exists() {\n                    let orig_file =\n                        out_path.file_name().unwrap().to_str().unwrap().replace(\"-symlink\", \"\");\n                    symlink(orig_file, out_path.to_str().unwrap()).unwrap();\n                    new_files.push((out_path.to_owned(), vec![]));\n                }\n            }\n        }\n        let modified_rf = modify_records_file(rf, base.to_str().unwrap());\n        (modified_rf, new_files)\n    }\n\n    // Generates mem trace string from given args. Sometimes injects lines that are of no importance\n    fn mem_generate_trace_line_for_open(path: &Path, time: u16, _op: Option<&str>) -> Vec<String> {\n        let op = \"mm_filemap_add_to_page_cache\";\n        let stat = path.metadata().unwrap();\n        let major_no = major(stat.st_dev());\n        let minor_no = minor(stat.st_dev());\n        let inode_number = stat.st_ino();\n\n        vec![\n            // unknown operation\n            format!(\n                \" SettingsProvide-502     [001] ....   {}.{}: {}: dev {}:{} ino {:x} \\\n                    page=000000008b759458 pfn=59827 ofs=0\",\n                time,\n                (time * 100) + time,\n                \"unknown_operation\",\n                major_no,\n                minor_no,\n                inode_number,\n            ),\n            // invalid/relative inode\n            format!(\n                \" SettingsProvide-502     [001] ....   {}.{}: {}: dev {}:{} ino {:x} \\\n                    page=000000008b759458 pfn=59827 ofs=0\",\n                time,\n                (time * 100) + time,\n                \"unknown_operation\",\n                major_no,\n                minor_no,\n                inode_number + 100,\n            ),\n            // good one\n            format!(\n                \" BOX_ENTRY_ADDED-3071    [001] ....   {}.{}: {}: dev {}:{} ino {:x} \\\n                    page=00000000f936540b pfn=60952 ofs={}\",\n                time,\n                (time * 100) + time,\n                op,\n                major_no,\n                minor_no,\n                inode_number,\n                0\n            ),\n            // good one\n            format!(\n                \" BOX_ENTRY_ADDED-3071    [001] ....   {}.{}: {}: dev {}:{} ino {:x} \\\n                    page=00000000f936540b pfn=60952 ofs={}\",\n                time,\n                (time * 100) + time,\n                op,\n                major_no,\n                minor_no,\n                inode_number,\n                10_000,\n            ),\n            // good one\n            format!(\n                \" BOX_ENTRY_ADDED-3071    [001] ....   {}.{}: {}: dev {}:{} ino {:x} \\\n                    page=00000000f936540b pfn=60952 ofs={}\",\n                time,\n                (time * 100) + time,\n                op,\n                major_no,\n                minor_no,\n                inode_number,\n                100_000,\n            ),\n            // good one\n            format!(\n                \" BOX_ENTRY_ADDED-3071    [001] ....   {}.{}: {}: dev {}:{} ino {:x} \\\n                    page=00000000f936540b pfn=60952 ofs={}\",\n                time,\n                (time * 100) + time,\n                op,\n                major_no,\n                minor_no,\n                inode_number,\n                1_000_000,\n            ),\n            // invalid operation case\n            format!(\n                \" SettingsProvide-502     [001] ....   {}.{}: {}: dev {}:{} ino {:x} \\\n                    page=000000008b759458 pfn=59827 ofs=0\",\n                time,\n                (time * 100) + time,\n                op.to_uppercase(),\n                major_no,\n                minor_no,\n                inode_number,\n            ),\n        ]\n    }\n\n    fn generate_trace_line_for_open(\n        tracing_type: TracerType,\n        path: &Path,\n        time: u16,\n        op: Option<&str>,\n    ) -> Vec<String> {\n        match tracing_type {\n            TracerType::Mem => mem_generate_trace_line_for_open(path, time, op),\n        }\n    }\n\n    // Generates a fake mountinfo file with bunch of fake mount point and\n    // fakes given path as a mount point.\n    fn create_fake_mountinfo_for(path: &Path) -> NamedTempFile {\n        let stat = path.metadata().unwrap();\n        let major_no = major(stat.st_dev());\n        let minor_no = minor(stat.st_dev());\n        let mut mountinfo_path = NamedTempFile::new().unwrap();\n        mountinfo_path\n            .write_all(\n                \"16 15 0:17 / /dev/pts rw,relatime shared:3 - devpts devpts \\\n                     rw,seclabel,mode=600,ptmxmode=000\\n\"\n                    .as_bytes(),\n            )\n            .unwrap();\n        mountinfo_path\n            .write_all(\n                \"17 26 0:18 / /proc rw,relatime shared:4 - proc proc rw,gid=3009,hidepid=\\\n                    invisible\\n\"\n                    .as_bytes(),\n            )\n            .unwrap();\n        mountinfo_path\n            .write_all(\n                format!(\n                    \"26 24 {}:{} / {} ro,nodev,noatime shared:1 - ext4 /dev/block/dm-3 ro,\\\n                    seclabel,errors=panic\\n\",\n                    major_no,\n                    minor_no,\n                    path.to_str().unwrap(),\n                )\n                .as_bytes(),\n            )\n            .unwrap();\n\n        mountinfo_path\n    }\n\n    static RECORD_PER_FILE: usize = 4;\n\n    fn create_tracer(\n        base_dir: &Path,\n        t: TracerType,\n    ) -> (Box<dyn TraceSubsystem + Send>, Vec<NamedTempFile>) {\n        let kb_buffer_size = Some(8388608);\n        let trace_mount_point = setup_test_dir();\n        let mut buffer_size_file = NamedTempFile::new_in(&trace_mount_point).unwrap();\n        buffer_size_file\n            .write_all(format!(\"{}\", kb_buffer_size.as_ref().unwrap()).as_bytes())\n            .unwrap();\n\n        let buffer_size_file_path = buffer_size_file.path().to_str().unwrap().to_string();\n        let mut config = TracerConfigs::new(\n            kb_buffer_size,\n            false,\n            t.clone(),\n            Some(trace_mount_point.to_str().unwrap().to_string()),\n            None,\n        )\n        .unwrap();\n        let mut tempfiles = vec![buffer_size_file];\n        (\n            match t {\n                TracerType::Mem => {\n                    let mountinfo_path =\n                        create_fake_mountinfo_for(&base_dir.canonicalize().unwrap());\n                    config.trace_events = vec![];\n                    config.buffer_size_file_path = buffer_size_file_path;\n                    config.mountinfo_path =\n                        Some(mountinfo_path.path().to_str().unwrap().to_string());\n                    tempfiles.push(mountinfo_path);\n                    Box::new(MemTraceSubsystem::create_with_configs(config).unwrap())\n                }\n            },\n            tempfiles,\n        )\n    }\n\n    fn test_trace_of_type(tracing_type: TracerType) {\n        let test_base_dir = setup_test_dir();\n        let (_rf, files) = generate_cached_files_and_record(\n            Some(&test_base_dir),\n            true,\n            Some(page_size().unwrap() as u64),\n        );\n\n        let mut file = NamedTempFile::new().unwrap();\n        let (reader_fd, writer_fd) = pipe().unwrap();\n        let reader = File::from(reader_fd);\n        let mut writer = File::from(writer_fd);\n\n        let (tracer, _temp_files) = create_tracer(&test_base_dir, tracing_type.clone());\n\n        let mut files_iter = files.iter();\n\n        for line in generate_trace_line_for_open(\n            tracing_type.clone(),\n            files_iter.next().unwrap().0.path(),\n            5,\n            None,\n        ) {\n            writeln!(file, \"{}\", line).unwrap();\n        }\n        file.sync_all().unwrap();\n        file.seek(std::io::SeekFrom::Start(0)).unwrap();\n\n        let (mut tracer, exit_evt) =\n            Tracer::create_with_config(file.reopen().unwrap(), reader, tracer).unwrap();\n\n        let thd = thread::spawn(move || tracer.trace(None));\n\n        for (index, file) in files_iter.enumerate() {\n            for line in generate_trace_line_for_open(tracing_type.clone(), file.0.path(), 10, None)\n            {\n                writeln!(&mut writer, \"{}\", line).unwrap();\n            }\n            if index == 0 {\n                // This sleep emulates delay in data arriving over a pipe. This shouldn't cause\n                // flakes in virtualized environment.\n                thread::sleep(Duration::from_secs(1));\n            }\n        }\n\n        thread::sleep(Duration::from_millis(100));\n        exit_evt.send(()).unwrap();\n        writeln!(&mut writer, \"line\").unwrap();\n\n        let tracer_rf = thd.join().unwrap().unwrap();\n\n        let mut found_count = 0;\n        for file in &files {\n            let mut found = false;\n            'inner: for inode in tracer_rf.inner.inode_map.values() {\n                for found_path in &inode.paths {\n                    if found_path == file.0.path().canonicalize().unwrap().to_str().unwrap() {\n                        found = true;\n                        break 'inner;\n                    }\n                }\n            }\n            if found {\n                found_count += 1;\n            } else {\n                println!(\"missing {:?}\", file.0.path());\n            }\n        }\n        assert_eq!(found_count, files.len());\n        assert_eq!(tracer_rf.inner.records.len(), files.len() * RECORD_PER_FILE);\n    }\n\n    #[test]\n    fn test_trace_mem() {\n        test_trace_of_type(TracerType::Mem)\n    }\n}\n"
  },
  {
    "path": "init/lmkd_service.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"lmkd_service.h\"\n\n#include <errno.h>\n\n#include <android-base/logging.h>\n#include <liblmkd_utils.h>\n\n#include \"service_list.h\"\n\nnamespace android {\nnamespace init {\n\nenum LmkdRegistrationResult {\n    LMKD_REG_SUCCESS,\n    LMKD_CONN_FAILED,\n    LMKD_REG_FAILED,\n};\n\nstatic int lmkd_socket = -1;\n\nstatic LmkdRegistrationResult RegisterProcess(uid_t uid, pid_t pid, int oom_score_adjust) {\n    // connect to lmkd if not already connected\n    if (lmkd_socket < 0) {\n        lmkd_socket = lmkd_connect();\n        if (lmkd_socket < 0) {\n            return LMKD_CONN_FAILED;\n        }\n    }\n\n    // register service with lmkd\n    struct lmk_procprio params;\n    params.pid = pid;\n    params.uid = uid;\n    params.oomadj = oom_score_adjust;\n    params.ptype = PROC_TYPE_SERVICE;\n    if (lmkd_register_proc(lmkd_socket, &params) != 0) {\n        // data transfer failed, reset the connection\n        close(lmkd_socket);\n        lmkd_socket = -1;\n        return LMKD_REG_FAILED;\n    }\n\n    return LMKD_REG_SUCCESS;\n}\n\nstatic bool UnregisterProcess(pid_t pid) {\n    if (lmkd_socket < 0) {\n        // no connection or it was lost, no need to unregister\n        return false;\n    }\n\n    // unregister service\n    struct lmk_procremove params;\n    params.pid = pid;\n    if (lmkd_unregister_proc(lmkd_socket, &params) != 0) {\n        // data transfer failed, reset the connection\n        close(lmkd_socket);\n        lmkd_socket = -1;\n        return false;\n    }\n\n    return true;\n}\n\nstatic void RegisterServices(pid_t exclude_pid) {\n    for (const auto& service : ServiceList::GetInstance()) {\n        auto svc = service.get();\n        if (svc->oom_score_adjust() != DEFAULT_OOM_SCORE_ADJUST) {\n            // skip if process is excluded or not yet forked (pid==0)\n            if (svc->pid() == exclude_pid || svc->pid() == 0) {\n                continue;\n            }\n            if (RegisterProcess(svc->uid(), svc->pid(), svc->oom_score_adjust()) !=\n                LMKD_REG_SUCCESS) {\n                // a failure here resets the connection, will retry during next registration\n                break;\n            }\n        }\n    }\n}\n\nvoid LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust) {\n    bool new_connection = lmkd_socket == -1;\n    LmkdRegistrationResult result;\n\n    result = RegisterProcess(uid, pid, oom_score_adjust);\n    if (result == LMKD_REG_FAILED) {\n        // retry one time if connection to lmkd was lost\n        result = RegisterProcess(uid, pid, oom_score_adjust);\n        new_connection = result == LMKD_REG_SUCCESS;\n    }\n    switch (result) {\n        case LMKD_REG_SUCCESS:\n            // register existing services once new connection is established\n            if (new_connection) {\n                RegisterServices(pid);\n            }\n            break;\n        case LMKD_CONN_FAILED:\n            PLOG(ERROR) << \"lmkd connection failed when \" << name << \" process got started\";\n            break;\n        case LMKD_REG_FAILED:\n            PLOG(ERROR) << \"lmkd failed to register \" << name << \" process\";\n            break;\n    }\n}\n\nvoid LmkdUnregister(const std::string& name, pid_t pid) {\n    if (!UnregisterProcess(pid)) {\n        PLOG(ERROR) << \"lmkd failed to unregister \" << name << \" process\";\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/lmkd_service.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <string>\n\nnamespace android {\nnamespace init {\n\nstatic const int MIN_OOM_SCORE_ADJUST = -1000;\nstatic const int MAX_OOM_SCORE_ADJUST = 1000;\n// service with default score is unkillable\nstatic const int DEFAULT_OOM_SCORE_ADJUST = MIN_OOM_SCORE_ADJUST;\n\n#if defined(__ANDROID__)\n\nvoid LmkdRegister(const std::string& name, uid_t uid, pid_t pid, int oom_score_adjust);\nvoid LmkdUnregister(const std::string& name, pid_t pid);\n\n#else  // defined(__ANDROID__)\n\nstatic inline void LmkdRegister(const std::string&, uid_t, pid_t, int) {}\nstatic inline void LmkdUnregister(const std::string&, pid_t) {}\n\n#endif  // defined(__ANDROID__)\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/main.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"builtins.h\"\n#include \"first_stage_init.h\"\n#include \"init.h\"\n#include \"selinux.h\"\n#include \"subcontext.h\"\n#include \"ueventd.h\"\n\n#include <android-base/logging.h>\n\n#if __has_feature(address_sanitizer)\n#include <sanitizer/asan_interface.h>\n#elif __has_feature(hwaddress_sanitizer)\n#include <sanitizer/hwasan_interface.h>\n#endif\n\n#if __has_feature(address_sanitizer) || __has_feature(hwaddress_sanitizer)\n// Load asan.options if it exists since these are not yet in the environment.\n// Always ensure detect_container_overflow=0 as there are false positives with this check.\n// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds.\nextern \"C\" const char* __asan_default_options() {\n    return \"include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1\";\n}\n\n__attribute__((no_sanitize(\"address\", \"memory\", \"thread\", \"undefined\"))) extern \"C\" void\n__sanitizer_report_error_summary(const char* summary) {\n    LOG(ERROR) << \"Init (error summary): \" << summary;\n}\n\n__attribute__((no_sanitize(\"address\", \"memory\", \"thread\", \"undefined\"))) static void\nAsanReportCallback(const char* str) {\n    LOG(ERROR) << \"Init: \" << str;\n}\n#endif\n\nusing namespace android::init;\n\nint main(int argc, char** argv) {\n#if __has_feature(address_sanitizer)\n    __asan_set_error_report_callback(AsanReportCallback);\n#elif __has_feature(hwaddress_sanitizer)\n    __hwasan_set_error_report_callback(AsanReportCallback);\n#endif\n    // Boost prio which will be restored later\n    setpriority(PRIO_PROCESS, 0, -20);\n    if (!strcmp(basename(argv[0]), \"ueventd\")) {\n        return ueventd_main(argc, argv);\n    }\n\n    if (argc > 1) {\n        if (!strcmp(argv[1], \"subcontext\")) {\n            android::base::InitLogging(argv, &android::base::KernelLogger);\n            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();\n\n            return SubcontextMain(argc, argv, &function_map);\n        }\n\n        if (!strcmp(argv[1], \"selinux_setup\")) {\n            return SetupSelinux(argv);\n        }\n\n        if (!strcmp(argv[1], \"second_stage\")) {\n            return SecondStageMain(argc, argv);\n        }\n    }\n\n    return FirstStageMain(argc, argv);\n}\n"
  },
  {
    "path": "init/modalias_handler.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"modalias_handler.h\"\n\n#include <string>\n#include <vector>\n\n#include <modprobe/modprobe.h>\n\nnamespace android {\nnamespace init {\n\nModaliasHandler::ModaliasHandler(const std::vector<std::string>& base_paths)\n    : modprobe_(base_paths) {}\n\nvoid ModaliasHandler::HandleUevent(const Uevent& uevent) {\n    if (uevent.modalias.empty()) return;\n    modprobe_.LoadWithAliases(uevent.modalias, true);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/modalias_handler.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include <modprobe/modprobe.h>\n\n#include \"uevent.h\"\n#include \"uevent_handler.h\"\n\nnamespace android {\nnamespace init {\n\nclass ModaliasHandler : public UeventHandler {\n  public:\n    ModaliasHandler(const std::vector<std::string>&);\n    virtual ~ModaliasHandler() = default;\n\n    void HandleUevent(const Uevent& uevent) override;\n\n  private:\n    Modprobe modprobe_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/mount_handler.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mount_handler.h\"\n\n#include <ctype.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/epoll.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <filesystem>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n#include <fstab/fstab.h>\n#include <libdm/dm.h>\n\n#include \"epoll.h\"\n\nusing android::base::Basename;\nusing android::base::StringPrintf;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nMountHandlerEntry ParseMount(const std::string& line) {\n    auto fields = android::base::Split(line, \" \");\n    while (fields.size() < 3) fields.emplace_back(\"\");\n    if (fields[0] == \"/dev/root\") {\n        auto& dm = dm::DeviceMapper::Instance();\n        std::string path;\n        if (dm.GetDmDevicePathByName(\"system\", &path) || dm.GetDmDevicePathByName(\"vroot\", &path)) {\n            fields[0] = path;\n        } else if (android::fs_mgr::Fstab fstab; android::fs_mgr::ReadDefaultFstab(&fstab)) {\n            auto entry = GetEntryForMountPoint(&fstab, \"/\");\n            if (entry || (entry = GetEntryForMountPoint(&fstab, \"/system\"))) {\n                fields[0] = entry->blk_device;\n            }\n        }\n    }\n    if (android::base::StartsWith(fields[0], \"/dev/\")) {\n        if (std::string link; android::base::Readlink(fields[0], &link)) {\n            fields[0] = link;\n        }\n    }\n    return MountHandlerEntry(fields[0], fields[1], fields[2]);\n}\n\n// return sda25 for dm-4, sda25 for sda25, or mmcblk0p24 for mmcblk0p24\nstd::string GetDiskPart(std::string blockdev) {\n    if (blockdev.find('/') != std::string::npos) return {};\n\n    while (android::base::StartsWith(blockdev, \"dm-\")) {\n        auto& dm = dm::DeviceMapper::Instance();\n        std::optional<std::string> parent = dm.GetParentBlockDeviceByPath(\"/dev/block/\" + blockdev);\n        if (parent) {\n            blockdev = android::base::Basename(*parent);\n        } else {\n            return {};\n        }\n    }\n    return blockdev;\n}\n\n// return sda for sda25, or mmcblk0 for mmcblk0p24\nstd::string GetRootDisk(std::string blockdev) {\n    if (blockdev.empty()) return {};\n    if (blockdev.find('/') != std::string::npos) return {};\n\n    std::error_code ec;\n    for (const auto& entry : std::filesystem::directory_iterator(\"/sys/block\", ec)) {\n        const std::string path = entry.path().string();\n        if (std::filesystem::exists(StringPrintf(\"%s/%s\", path.c_str(), blockdev.c_str()))) {\n            return Basename(path);\n        }\n    }\n    return {};\n}\n\nvoid SetMountProperty(const MountHandlerEntry& entry, bool add) {\n    static constexpr char devblock[] = \"/dev/block/\";\n    if (!android::base::StartsWith(entry.blk_device, devblock)) return;\n    auto target = entry.blk_device.substr(strlen(devblock));\n    std::string diskpart, rootdisk;\n    if (add) {\n        diskpart = GetDiskPart(target);\n        rootdisk = GetRootDisk(diskpart);\n\n        struct stat sb;\n        if (stat(entry.mount_point.c_str(), &sb) || !S_ISDIR(sb.st_mode)) rootdisk = \"\";\n        // Clear the noise associated with loopback and APEX.\n        if (android::base::StartsWith(target, \"loop\")) rootdisk = \"\";\n        if (android::base::StartsWith(entry.mount_point, \"/apex/\")) rootdisk = \"\";\n    }\n    auto mount_prop = entry.mount_point;\n    if (mount_prop == \"/\") mount_prop = \"/root\";\n    std::replace(mount_prop.begin(), mount_prop.end(), '/', '.');\n    auto blk_mount_prop = \"dev.mnt.blk\" + mount_prop;\n    auto dev_mount_prop = \"dev.mnt.dev\" + mount_prop;\n    auto rootdisk_mount_prop = \"dev.mnt.rootdisk\" + mount_prop;\n    // Set property even if its rootdisk does not change to trigger 'on property:'\n    // handling, except for clearing non-existent or already clear property.\n    // Goal is reduction of empty properties and associated triggers.\n    if (rootdisk.empty() && android::base::GetProperty(blk_mount_prop, \"\").empty()) return;\n\n    if (rootdisk.empty()) {\n        android::base::SetProperty(blk_mount_prop, \"\");\n        android::base::SetProperty(dev_mount_prop, \"\");\n        android::base::SetProperty(rootdisk_mount_prop, \"\");\n        return;\n    }\n\n    // 1. dm-N\n    //  dev.mnt.dev.data = dm-N\n    //  dev.mnt.blk.data = sdaN or mmcblk0pN\n    //  dev.mnt.rootdisk.data = sda or mmcblk0\n    //\n    // 2. sdaN or mmcblk0pN\n    //  dev.mnt.dev.data = sdaN or mmcblk0pN\n    //  dev.mnt.blk.data = sdaN or mmcblk0pN\n    //  dev.mnt.rootdisk.data = sda or mmcblk0\n    android::base::SetProperty(dev_mount_prop, target);\n    android::base::SetProperty(blk_mount_prop, diskpart);\n    android::base::SetProperty(rootdisk_mount_prop, rootdisk);\n}\n\n}  // namespace\n\nMountHandlerEntry::MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,\n                                     const std::string& fs_type)\n    : blk_device(blk_device), mount_point(mount_point), fs_type(fs_type) {}\n\nbool MountHandlerEntry::operator<(const MountHandlerEntry& r) const {\n    if (blk_device < r.blk_device) return true;\n    if (blk_device > r.blk_device) return false;\n    if (mount_point < r.mount_point) return true;\n    if (mount_point > r.mount_point) return false;\n    return fs_type < r.fs_type;\n}\n\nMountHandler::MountHandler(Epoll* epoll) : epoll_(epoll), fp_(fopen(\"/proc/mounts\", \"re\"), fclose) {\n    if (!fp_) PLOG(FATAL) << \"Could not open /proc/mounts\";\n    auto result = epoll->RegisterHandler(\n            fileno(fp_.get()), [this]() { this->MountHandlerFunction(); }, EPOLLERR | EPOLLPRI);\n    if (!result.ok()) LOG(FATAL) << result.error();\n}\n\nMountHandler::~MountHandler() {\n    if (fp_) epoll_->UnregisterHandler(fileno(fp_.get()));\n}\n\nvoid MountHandler::MountHandlerFunction() {\n    rewind(fp_.get());\n    std::vector<MountHandlerEntry> touched;\n    auto untouched = mounts_;\n    char* buf = nullptr;\n    size_t len = 0;\n    while (getline(&buf, &len, fp_.get()) != -1) {\n        auto buf_string = std::string(buf);\n        if (buf_string.find(\"/emulated\") != std::string::npos) {\n            continue;\n        }\n        auto entry = ParseMount(buf_string);\n        auto match = untouched.find(entry);\n        if (match == untouched.end()) {\n            touched.emplace_back(std::move(entry));\n        } else {\n            untouched.erase(match);\n        }\n    }\n    free(buf);\n    for (auto& entry : untouched) {\n        SetMountProperty(entry, false);\n        mounts_.erase(entry);\n    }\n    for (auto& entry : touched) {\n        SetMountProperty(entry, true);\n        mounts_.emplace(std::move(entry));\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/mount_handler.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdio.h>\n\n#include <memory>\n#include <set>\n#include <string>\n\n#include \"epoll.h\"\n\nnamespace android {\nnamespace init {\n\nstruct MountHandlerEntry {\n    MountHandlerEntry(const std::string& blk_device, const std::string& mount_point,\n                      const std::string& fs_type);\n\n    bool operator<(const MountHandlerEntry& r) const;\n\n    const std::string blk_device;\n    const std::string mount_point;\n    const std::string fs_type;\n};\n\nclass MountHandler {\n  public:\n    explicit MountHandler(Epoll* epoll);\n    MountHandler(const MountHandler&) = delete;\n    MountHandler(MountHandler&&) = delete;\n    MountHandler& operator=(const MountHandler&) = delete;\n    MountHandler& operator=(MountHandler&&) = delete;\n    ~MountHandler();\n\n  private:\n    void MountHandlerFunction();\n\n    Epoll* epoll_;\n    std::unique_ptr<FILE, decltype(&fclose)> fp_;\n    std::set<MountHandlerEntry> mounts_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/mount_namespace.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"mount_namespace.h\"\n\n#include <sys/mount.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/result.h>\n#include <android-base/unique_fd.h>\n\n#include \"util.h\"\n\nnamespace android {\nnamespace init {\nnamespace {\n\nstatic bool BindMount(const std::string& source, const std::string& mount_point) {\n    if (mount(source.c_str(), mount_point.c_str(), nullptr, MS_BIND | MS_REC, nullptr) == -1) {\n        PLOG(ERROR) << \"Failed to bind mount \" << source;\n        return false;\n    }\n    return true;\n}\n\nstatic bool ChangeMount(const std::string& mount_point, unsigned long mountflags) {\n    if (mount(nullptr, mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {\n        PLOG(ERROR) << \"Failed to remount \" << mount_point << \" as \" << std::hex << mountflags;\n        return false;\n    }\n    return true;\n}\n\nstatic int OpenMountNamespace() {\n    int fd = open(\"/proc/self/ns/mnt\", O_RDONLY | O_CLOEXEC);\n    if (fd < 0) {\n        PLOG(ERROR) << \"Cannot open fd for current mount namespace\";\n    }\n    return fd;\n}\n\nstatic std::string GetMountNamespaceId() {\n    std::string ret;\n    if (!android::base::Readlink(\"/proc/self/ns/mnt\", &ret)) {\n        PLOG(ERROR) << \"Failed to read namespace ID\";\n        return \"\";\n    }\n    return ret;\n}\n\nstatic android::base::unique_fd bootstrap_ns_fd;\nstatic android::base::unique_fd default_ns_fd;\n\nstatic std::string bootstrap_ns_id;\nstatic std::string default_ns_id;\n\n}  // namespace\n\n// In case we have two sets of APEXes (non-updatable, updatable), we need two separate mount\n// namespaces.\nbool NeedsTwoMountNamespaces() {\n    if (IsRecoveryMode()) return false;\n    // In microdroid, there's only one set of APEXes in built-in directories include block devices.\n    if (IsMicrodroid()) return false;\n    return true;\n}\n\nbool SetupMountNamespaces() {\n    // Set the propagation type of / as shared so that any mounting event (e.g.\n    // /data) is by default visible to all processes. When private mounting is\n    // needed for /foo/bar, then we will make /foo/bar as a mount point (by\n    // bind-mounting by to itself) and set the propagation type of the mount\n    // point to private.\n    if (!ChangeMount(\"/\", MS_SHARED | MS_REC)) return false;\n\n    // /apex is a private mountpoint to give different sets of APEXes for\n    // the bootstrap and default mount namespaces. The processes running with\n    // the bootstrap namespace get APEXes from the read-only partition.\n    if (!(ChangeMount(\"/apex\", MS_PRIVATE))) return false;\n\n    // /linkerconfig is a private mountpoint to give a different linker configuration\n    // based on the mount namespace. Subdirectory will be bind-mounted based on current mount\n    // namespace\n    if (!(ChangeMount(\"/linkerconfig\", MS_PRIVATE))) return false;\n\n    // The two mount namespaces present challenges for scoped storage, because\n    // vold, which is responsible for most of the mounting, lives in the\n    // bootstrap mount namespace, whereas most other daemons and all apps live\n    // in the default namespace.  Scoped storage has a need for a\n    // /mnt/installer view that is a slave bind mount of /mnt/user - in other\n    // words, all mounts under /mnt/user should automatically show up under\n    // /mnt/installer. However, additional mounts done under /mnt/installer\n    // should not propagate back to /mnt/user. In a single mount namespace\n    // this is easy to achieve, by simply marking the /mnt/installer a slave\n    // bind mount. Unfortunately, if /mnt/installer is only created and\n    // bind mounted after the two namespaces are created below, we end up\n    // with the following situation:\n    // /mnt/user and /mnt/installer share the same peer group in both the\n    // bootstrap and default namespaces. Marking /mnt/installer slave in either\n    // namespace means that it won't propagate events to the /mnt/installer in\n    // the other namespace, which is still something we require - vold is the\n    // one doing the mounting under /mnt/installer, and those mounts should\n    // show up in the default namespace as well.\n    //\n    // The simplest solution is to do the bind mount before the two namespaces\n    // are created: the effect is that in both namespaces, /mnt/installer is a\n    // slave to the /mnt/user mount, and at the same time /mnt/installer in the\n    // bootstrap namespace shares a peer group with /mnt/installer in the\n    // default namespace.\n    // /mnt/androidwritable is similar to /mnt/installer but serves for\n    // MOUNT_EXTERNAL_ANDROID_WRITABLE apps.\n    if (!mkdir_recursive(\"/mnt/user\", 0755)) return false;\n    if (!mkdir_recursive(\"/mnt/installer\", 0755)) return false;\n    if (!mkdir_recursive(\"/mnt/androidwritable\", 0755)) return false;\n    if (!(BindMount(\"/mnt/user\", \"/mnt/installer\"))) return false;\n    if (!(BindMount(\"/mnt/user\", \"/mnt/androidwritable\"))) return false;\n    // First, make /mnt/installer and /mnt/androidwritable a slave bind mount\n    if (!(ChangeMount(\"/mnt/installer\", MS_SLAVE))) return false;\n    if (!(ChangeMount(\"/mnt/androidwritable\", MS_SLAVE))) return false;\n    // Then, make it shared again - effectively creating a new peer group, that\n    // will be inherited by new mount namespaces.\n    if (!(ChangeMount(\"/mnt/installer\", MS_SHARED))) return false;\n    if (!(ChangeMount(\"/mnt/androidwritable\", MS_SHARED))) return false;\n\n    bootstrap_ns_fd.reset(OpenMountNamespace());\n    bootstrap_ns_id = GetMountNamespaceId();\n\n    // When APEXes are updatable (e.g. not-flattened), we create separate mount\n    // namespaces for processes that are started before and after the APEX is\n    // activated by apexd. In the namespace for pre-apexd processes, small\n    // number of essential APEXes (e.g. com.android.runtime) are activated.\n    // In the namespace for post-apexd processes, all APEXes are activated.\n    bool success = true;\n    if (NeedsTwoMountNamespaces()) {\n        // Creating a new namespace by cloning, saving, and switching back to\n        // the original namespace.\n        if (unshare(CLONE_NEWNS) == -1) {\n            PLOG(ERROR) << \"Cannot create mount namespace\";\n            return false;\n        }\n        default_ns_fd.reset(OpenMountNamespace());\n        default_ns_id = GetMountNamespaceId();\n\n        if (setns(bootstrap_ns_fd.get(), CLONE_NEWNS) == -1) {\n            PLOG(ERROR) << \"Cannot switch back to bootstrap mount namespace\";\n            return false;\n        }\n\n        // Some components (e.g. servicemanager) need to access bootstrap\n        // APEXes from the default mount namespace. To achieve that, we bind-mount\n        // /apex to /bootstrap-apex in the bootstrap mount namespace. Since /bootstrap-apex\n        // is \"shared\", the mounts are visible in the default mount namespace as well.\n        //\n        // The end result will look like:\n        //   in the bootstrap mount namespace:\n        //     /apex  (== /bootstrap-apex)\n        //       {bootstrap APEXes from the read-only partition}\n        //\n        //   in the default mount namespace:\n        //     /bootstrap-apex\n        //       {bootstrap APEXes from the read-only partition}\n        //     /apex\n        //       {APEXes, can be from /data partition}\n        if (!(BindMount(\"/bootstrap-apex\", \"/apex\"))) return false;\n    } else {\n        // Otherwise, default == bootstrap\n        default_ns_fd.reset(OpenMountNamespace());\n        default_ns_id = GetMountNamespaceId();\n    }\n\n    LOG(INFO) << \"SetupMountNamespaces done\";\n    return success;\n}\n\n// Switch the mount namespace of the current process from bootstrap to default OR from default to\n// bootstrap. If the current mount namespace is neither bootstrap nor default, keep it that way.\nResult<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace) {\n    if (IsRecoveryMode()) {\n        // we don't have multiple namespaces in recovery mode or if apex is not updatable\n        return {};\n    }\n\n    const std::string current_namespace_id = GetMountNamespaceId();\n    MountNamespace current_mount_namespace;\n    if (current_namespace_id == bootstrap_ns_id) {\n        current_mount_namespace = NS_BOOTSTRAP;\n    } else if (current_namespace_id == default_ns_id) {\n        current_mount_namespace = NS_DEFAULT;\n    } else {\n        // services with `namespace mnt` start in its own mount namespace. So we need to keep it.\n        return {};\n    }\n\n    // We're already in the target mount namespace.\n    if (current_mount_namespace == target_mount_namespace) {\n        return {};\n    }\n\n    const auto& ns_fd = target_mount_namespace == NS_BOOTSTRAP ? bootstrap_ns_fd : default_ns_fd;\n    const auto& ns_name = target_mount_namespace == NS_BOOTSTRAP ? \"bootstrap\" : \"default\";\n    if (ns_fd.get() != -1) {\n        if (setns(ns_fd.get(), CLONE_NEWNS) == -1) {\n            return ErrnoError() << \"Failed to switch to \" << ns_name << \" mount namespace.\";\n        }\n    }\n    return {};\n}\n\nbase::Result<MountNamespace> GetCurrentMountNamespace() {\n    std::string current_namespace_id = GetMountNamespaceId();\n    if (current_namespace_id == \"\") {\n        return Error() << \"Failed to get current mount namespace ID\";\n    }\n\n    if (current_namespace_id == bootstrap_ns_id) {\n        return NS_BOOTSTRAP;\n    } else if (current_namespace_id == default_ns_id) {\n        return NS_DEFAULT;\n    }\n\n    return Error() << \"Failed to find current mount namespace\";\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/mount_namespace.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android-base/result.h>\n\nnamespace android {\nnamespace init {\n\nenum MountNamespace { NS_BOOTSTRAP, NS_DEFAULT };\n\nbool SetupMountNamespaces();\n\nbase::Result<void> SwitchToMountNamespaceIfNeeded(MountNamespace target_mount_namespace);\n\nbase::Result<MountNamespace> GetCurrentMountNamespace();\n\nbool NeedsTwoMountNamespaces();\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/noop_builtins.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Note that parser will perform arity checks only.\n\n#include <android-base/result.h>\n\n#include \"builtin_arguments.h\"\n#include \"builtins.h\"\n\nnamespace android::init {\n\nstatic base::Result<void> check_stub(const BuiltinArguments&) {\n    return {};\n}\n\n#include \"noop_builtin_function_map.h\"\n\n}  // namespace android::init\n"
  },
  {
    "path": "init/oneshot_on_test.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include <chrono>\n\n#include <android-base/properties.h>\n\nusing android::base::GetProperty;\nusing android::base::SetProperty;\nusing android::base::WaitForProperty;\nusing namespace std::literals;\n\nTEST(init, oneshot_on) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n\n    // Bootanim shouldn't be running once the device has booted.\n    ASSERT_EQ(\"stopped\", GetProperty(\"init.svc.bootanim\", \"\"));\n\n    SetProperty(\"ctl.oneshot_off\", \"bootanim\");\n    SetProperty(\"ctl.start\", \"bootanim\");\n\n    // Bootanim exits quickly when the device is fully booted, so check that it goes back to the\n    // 'restarting' state that non-oneshot services enter once they've restarted.\n    EXPECT_TRUE(WaitForProperty(\"init.svc.bootanim\", \"restarting\", 10s))\n            << \"Value is: \" << GetProperty(\"init.svc.bootanim\", \"\");\n\n    SetProperty(\"ctl.oneshot_on\", \"bootanim\");\n    SetProperty(\"ctl.start\", \"bootanim\");\n\n    // Now that oneshot is enabled again, bootanim should transition into the 'stopped' state.\n    EXPECT_TRUE(WaitForProperty(\"init.svc.bootanim\", \"stopped\", 10s))\n            << \"Value is: \" << GetProperty(\"init.svc.bootanim\", \"\");\n}\n"
  },
  {
    "path": "init/parser/tokenizer.cpp",
    "content": "// Copyright (C) 2015 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"tokenizer.h\"\n\nnamespace init {\n\nTokenizer::Tokenizer(const std::string& data)\n    : data_(data), eof_(false), pos_(0), tok_start_(0) {\n  current_.type = TOK_START;\n\n  if (data.size() > 0) {\n    cur_char_ = data[0];\n  } else {\n    eof_ = true;\n    cur_char_ = '\\0';\n  }\n}\n\nTokenizer::~Tokenizer() {}\n\nconst Tokenizer::Token& Tokenizer::current() {\n  return current_;\n}\n\nbool Tokenizer::Next() {\n  while (!eof_) {\n    AdvWhiteSpace();\n\n    // Check for comments.\n    if (cur_char_ == '#') {\n      AdvChar();\n      // Skip rest of line\n      while (!eof_ && cur_char_ != '\\n') {\n        AdvChar();\n      }\n    }\n\n    if (eof_) {\n      break;\n    }\n\n    if (cur_char_ == '\\0') {\n      AdvChar();\n    } else if (cur_char_ == '\\n') {\n      current_.type = TOK_NEWLINE;\n      current_.text.clear();\n      AdvChar();\n      return true;\n    } else if (cur_char_ == '\\\\') {\n      AdvChar();  // skip backslash\n      // This is line continuation so\n      // do not generated TOK_NEWLINE at\n      // the next \\n.\n      AdvUntil('\\n');\n      AdvChar();  // skip \\n\n    } else if (cur_char_ == '\\\"') {\n      AdvChar();\n      StartText();\n      // Grab everything until the next quote.\n      AdvUntil('\\\"');\n      EndText();\n      AdvChar();  // skip quote.\n      return true;\n    } else {\n      StartText();\n      AdvText();\n      EndText();\n      return true;\n    }\n  }\n  current_.type = TOK_END;\n  current_.text.clear();\n  return false;\n}\n\nvoid Tokenizer::AdvChar() {\n  pos_++;\n  if (pos_ < data_.size()) {\n    cur_char_ = data_[pos_];\n  } else {\n    eof_ = true;\n    cur_char_ = '\\0';\n  }\n}\n\nvoid Tokenizer::AdvWhiteSpace() {\n  while (cur_char_ == '\\t' || cur_char_ == '\\r' || cur_char_ == ' ') {\n    AdvChar();\n  }\n}\n\nvoid Tokenizer::AdvUntil(char x) {\n  while (!eof_ && cur_char_ != x) {\n    AdvChar();\n  }\n}\n\nvoid Tokenizer::AdvText() {\n  while (cur_char_ != '\\t' && cur_char_ != '\\r' && cur_char_ != '\\0' &&\n         cur_char_ != ' ' && cur_char_ != '\\n' && cur_char_ != '#') {\n    AdvChar();\n  }\n}\n\nvoid Tokenizer::StartText() {\n  current_.text.clear();\n  tok_start_ = pos_;\n  current_.type = TOK_TEXT;\n}\n\nvoid Tokenizer::EndText() {\n  if (pos_ != tok_start_) {\n    current_.text.append(data_, tok_start_, pos_ - tok_start_);\n  }\n}\n\n}  // namespace init"
  },
  {
    "path": "init/parser/tokenizer.h",
    "content": "// Copyright (C) 2015 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#ifndef _INIT_PARSER_TOKENIZER_H\n#define _INIT_PARSER_TOKENIZER_H\n\n#include <string>\n\nnamespace init {\n\n// Used to tokenize a std::string.\n// Call Next() to advance through each token until it returns false,\n// indicating there are no more tokens left in the string.\n// The current token can be accessed with current(), which returns\n// a Token.\n// Supported tokens are:\n// TOK_START - Next() has yet to be called\n// TOK_END - At the end of string\n// TOK_NEWLINE - The end of a line denoted by \\n.\n// TOK_TEXT - A word.\n// Comments are denoted with '#' and the tokenizer will ignore\n// the rest of the line.\n// Double quotes can be used to insert whitespace into words.\n// A backslash at the end of a line denotes continuation and\n// a TOK_NEWLINE will not be generated for that line.\nclass Tokenizer {\n public:\n  explicit Tokenizer(const std::string& data);\n  ~Tokenizer();\n\n  enum TokenType { TOK_START, TOK_END, TOK_NEWLINE, TOK_TEXT };\n  struct Token {\n    TokenType type;\n    std::string text;\n  };\n\n  // Returns the curret token.\n  const Token& current();\n\n  // Move to the next token, returns false at the end of input.\n  bool Next();\n\n private:\n  void GetData();\n  void AdvChar();\n  void AdvText();\n  void AdvUntil(char x);\n  void AdvWhiteSpace();\n  void StartText();\n  void EndText();\n\n  const std::string& data_;\n  Token current_;\n\n  bool eof_;\n  size_t pos_;\n  char cur_char_;\n  size_t tok_start_;\n};\n\n}  // namespace init\n\n#endif\n"
  },
  {
    "path": "init/parser/tokenizer_test.cpp",
    "content": "// Copyright (C) 2015 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"tokenizer.h\"\n\n#include <errno.h>\n#include <gtest/gtest.h>\n\n#include <string>\n\nnamespace init {\n\n#define SETUP_TEST(test_data)  \\\n  std::string data(test_data); \\\n  Tokenizer tokenizer(data);   \\\n  ASSERT_EQ(Tokenizer::TOK_START, tokenizer.current().type)\n\n#define ASSERT_TEXT_TOKEN(test_text)              \\\n  ASSERT_TRUE(tokenizer.Next());                  \\\n  ASSERT_EQ(test_text, tokenizer.current().text); \\\n  ASSERT_EQ(Tokenizer::TOK_TEXT, tokenizer.current().type)\n\n#define ASSERT_NEWLINE_TOKEN()   \\\n  ASSERT_TRUE(tokenizer.Next()); \\\n  ASSERT_EQ(Tokenizer::TOK_NEWLINE, tokenizer.current().type)\n\nTEST(Tokenizer, Empty) {\n  SETUP_TEST(\"\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, Simple) {\n  SETUP_TEST(\"test\");\n  ASSERT_TEXT_TOKEN(\"test\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, LeadingWhiteSpace) {\n  SETUP_TEST(\" \\t  \\r  test\");\n  ASSERT_TEXT_TOKEN(\"test\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, TrailingWhiteSpace) {\n  SETUP_TEST(\"test \\t  \\r  \");\n  ASSERT_TEXT_TOKEN(\"test\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, WhiteSpace) {\n  SETUP_TEST(\" \\t  \\r  test \\t  \\r  \");\n  ASSERT_TEXT_TOKEN(\"test\");\n\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, TwoTokens) {\n  SETUP_TEST(\"  foo   bar \");\n  ASSERT_TEXT_TOKEN(\"foo\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, MultiToken) {\n  SETUP_TEST(\"one two three four five\");\n  ASSERT_TEXT_TOKEN(\"one\");\n  ASSERT_TEXT_TOKEN(\"two\");\n  ASSERT_TEXT_TOKEN(\"three\");\n  ASSERT_TEXT_TOKEN(\"four\");\n  ASSERT_TEXT_TOKEN(\"five\");\n\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, NewLine) {\n  SETUP_TEST(\"\\n\");\n  ASSERT_NEWLINE_TOKEN();\n\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, TextNewLine) {\n  SETUP_TEST(\"test\\n\");\n  ASSERT_TEXT_TOKEN(\"test\");\n  ASSERT_NEWLINE_TOKEN();\n\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, MultiTextNewLine) {\n  SETUP_TEST(\"one\\ntwo\\nthree\\n\");\n  ASSERT_TEXT_TOKEN(\"one\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_TEXT_TOKEN(\"two\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_TEXT_TOKEN(\"three\");\n  ASSERT_NEWLINE_TOKEN();\n\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, MultiTextNewLineNoLineEnding) {\n  SETUP_TEST(\"one\\ntwo\\nthree\");\n  ASSERT_TEXT_TOKEN(\"one\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_TEXT_TOKEN(\"two\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_TEXT_TOKEN(\"three\");\n\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, Comment) {\n  SETUP_TEST(\"#test\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, CommentWhiteSpace) {\n  SETUP_TEST(\" \\t  \\r  #test \\t  \\r  \");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, CommentNewLine) {\n  SETUP_TEST(\" #test   \\n\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, CommentTwoNewLine) {\n  SETUP_TEST(\" #test   \\n#test\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, CommentWithText) {\n  SETUP_TEST(\"foo bar #test\");\n  ASSERT_TEXT_TOKEN(\"foo\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, CommentWithTextNoSpace) {\n  SETUP_TEST(\"foo bar#test\");\n  ASSERT_TEXT_TOKEN(\"foo\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, CommentWithTextLineFeed) {\n  SETUP_TEST(\"foo bar #test\\n\");\n  ASSERT_TEXT_TOKEN(\"foo\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, CommentWithMultiTextLineFeed) {\n  SETUP_TEST(\"#blah\\nfoo bar #test\\n#blah\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_TEXT_TOKEN(\"foo\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_NEWLINE_TOKEN();\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, SimpleEscaped) {\n  SETUP_TEST(\"fo\\\\no bar\");\n  ASSERT_TEXT_TOKEN(\"fo\\\\no\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, EscapedLineContNoLineFeed) {\n  SETUP_TEST(\"fo\\\\no bar \\\\ hello\");\n  ASSERT_TEXT_TOKEN(\"fo\\\\no\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, EscapedLineContLineFeed) {\n  SETUP_TEST(\"fo\\\\no bar \\\\ hello\\n\");\n  ASSERT_TEXT_TOKEN(\"fo\\\\no\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, EscapedLineCont) {\n  SETUP_TEST(\"fo\\\\no bar \\\\\\ntest\");\n  ASSERT_TEXT_TOKEN(\"fo\\\\no\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_TEXT_TOKEN(\"test\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, EscapedLineContWithBadChars) {\n  SETUP_TEST(\"fo\\\\no bar \\\\bad bad bad\\ntest\");\n  ASSERT_TEXT_TOKEN(\"fo\\\\no\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_TEXT_TOKEN(\"test\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, SimpleQuotes) {\n  SETUP_TEST(\"foo \\\"single token\\\" bar\");\n  ASSERT_TEXT_TOKEN(\"foo\");\n  ASSERT_TEXT_TOKEN(\"single token\");\n  ASSERT_TEXT_TOKEN(\"bar\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\nTEST(Tokenizer, BadQuotes) {\n  SETUP_TEST(\"foo \\\"single token\");\n  ASSERT_TEXT_TOKEN(\"foo\");\n  ASSERT_TEXT_TOKEN(\"single token\");\n  ASSERT_FALSE(tokenizer.Next());\n}\n\n}  // namespace init\n"
  },
  {
    "path": "init/parser.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"parser.h\"\n\n#include <dirent.h>\n\n#include <map>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n\n#include \"tokenizer.h\"\n#include \"util.h\"\n\nnamespace android {\nnamespace init {\n\nParser::Parser() {}\n\nvoid Parser::AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser) {\n    section_parsers_[name] = std::move(parser);\n}\n\nvoid Parser::AddSingleLineParser(const std::string& prefix, LineCallback callback) {\n    line_callbacks_.emplace_back(prefix, std::move(callback));\n}\n\nvoid Parser::ParseData(const std::string& filename, std::string* data) {\n    data->push_back('\\n');\n    data->push_back('\\0');\n\n    parse_state state;\n    state.line = 0;\n    state.ptr = data->data();\n    state.nexttoken = 0;\n\n    SectionParser* section_parser = nullptr;\n    int section_start_line = -1;\n    std::vector<std::string> args;\n\n    // If we encounter a bad section start, there is no valid parser object to parse the subsequent\n    // sections, so we must suppress errors until the next valid section is found.\n    bool bad_section_found = false;\n\n    auto end_section = [&] {\n        bad_section_found = false;\n        if (section_parser == nullptr) return;\n\n        if (auto result = section_parser->EndSection(); !result.ok()) {\n            parse_error_count_++;\n            LOG(ERROR) << filename << \": \" << section_start_line << \": \" << result.error();\n        }\n\n        section_parser = nullptr;\n        section_start_line = -1;\n    };\n\n    for (;;) {\n        switch (next_token(&state)) {\n            case T_EOF:\n                end_section();\n\n                for (const auto& [section_name, section_parser] : section_parsers_) {\n                    section_parser->EndFile();\n                }\n\n                return;\n            case T_NEWLINE: {\n                state.line++;\n                if (args.empty()) break;\n                // If we have a line matching a prefix we recognize, call its callback and unset any\n                // current section parsers.  This is meant for /sys/ and /dev/ line entries for\n                // uevent.\n                auto line_callback = std::find_if(\n                    line_callbacks_.begin(), line_callbacks_.end(),\n                    [&args](const auto& c) { return android::base::StartsWith(args[0], c.first); });\n                if (line_callback != line_callbacks_.end()) {\n                    end_section();\n\n                    if (auto result = line_callback->second(std::move(args)); !result.ok()) {\n                        parse_error_count_++;\n                        LOG(ERROR) << filename << \": \" << state.line << \": \" << result.error();\n                    }\n                } else if (section_parsers_.count(args[0])) {\n                    end_section();\n                    section_parser = section_parsers_[args[0]].get();\n                    section_start_line = state.line;\n                    if (auto result =\n                                section_parser->ParseSection(std::move(args), filename, state.line);\n                        !result.ok()) {\n                        parse_error_count_++;\n                        LOG(ERROR) << filename << \": \" << state.line << \": \" << result.error();\n                        section_parser = nullptr;\n                        bad_section_found = true;\n                    }\n                } else if (section_parser) {\n                    if (auto result = section_parser->ParseLineSection(std::move(args), state.line);\n                        !result.ok()) {\n                        parse_error_count_++;\n                        LOG(ERROR) << filename << \": \" << state.line << \": \" << result.error();\n                    }\n                } else if (!bad_section_found) {\n                    parse_error_count_++;\n                    LOG(ERROR) << filename << \": \" << state.line\n                               << \": Invalid section keyword found\";\n                }\n                args.clear();\n                break;\n            }\n            case T_TEXT:\n                args.emplace_back(state.text);\n                break;\n        }\n    }\n}\n\nbool Parser::ParseConfigFileInsecure(const std::string& path, bool follow_symlinks = false) {\n    std::string config_contents;\n    if (!android::base::ReadFileToString(path, &config_contents, follow_symlinks)) {\n        return false;\n    }\n\n    ParseData(path, &config_contents);\n    return true;\n}\n\nResult<void> Parser::ParseConfigFile(const std::string& path) {\n    LOG(INFO) << \"Parsing file \" << path << \"...\";\n    android::base::Timer t;\n    auto config_contents = ReadFile(path);\n    if (!config_contents.ok()) {\n        return Error() << \"Unable to read config file '\" << path\n                       << \"': \" << config_contents.error();\n    }\n\n    ParseData(path, &config_contents.value());\n\n    LOG(VERBOSE) << \"(Parsing \" << path << \" took \" << t << \".)\";\n    return {};\n}\n\nbool Parser::ParseConfigDir(const std::string& path) {\n    LOG(INFO) << \"Parsing directory \" << path << \"...\";\n    std::unique_ptr<DIR, decltype(&closedir)> config_dir(opendir(path.c_str()), closedir);\n    if (!config_dir) {\n        PLOG(INFO) << \"Could not import directory '\" << path << \"'\";\n        return false;\n    }\n    dirent* current_file;\n    std::vector<std::string> files;\n    while ((current_file = readdir(config_dir.get()))) {\n        // Ignore directories and only process regular files.\n        if (current_file->d_type == DT_REG) {\n            std::string current_path =\n                android::base::StringPrintf(\"%s/%s\", path.c_str(), current_file->d_name);\n            files.emplace_back(current_path);\n        }\n    }\n    // Sort first so we load files in a consistent order (bug 31996208)\n    std::sort(files.begin(), files.end());\n    for (const auto& file : files) {\n        if (auto result = ParseConfigFile(file); !result.ok()) {\n            LOG(ERROR) << \"could not import file '\" << file << \"': \" << result.error();\n        }\n    }\n    return true;\n}\n\nbool Parser::ParseConfig(const std::string& path) {\n    if (is_dir(path.c_str())) {\n        return ParseConfigDir(path);\n    }\n    auto result = ParseConfigFile(path);\n    if (!result.ok()) {\n        LOG(INFO) << result.error();\n    }\n    return result.ok();\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/parser.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_PARSER_H_\n#define _INIT_PARSER_H_\n\n#include <map>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"result.h\"\n\n//  SectionParser is an interface that can parse a given 'section' in init.\n//\n//  You can implement up to 4 functions below, with ParseSection being mandatory. The first two\n//  functions return Result<void> indicating if they have an error. It will be reported along\n//  with the filename and line number of where the error occurred.\n//\n//  1) ParseSection\n//    This function is called when a section is first encountered.\n//\n//  2) ParseLineSection\n//    This function is called on each subsequent line until the next section is encountered.\n//\n//  3) EndSection\n//    This function is called either when a new section is found or at the end of the file.\n//    It indicates that parsing of the current section is complete and any relevant objects should\n//    be committed.\n//\n//  4) EndFile\n//    This function is called at the end of the file.\n//    It indicates that the parsing has completed and any relevant objects should be committed.\n\nnamespace android {\nnamespace init {\n\nclass SectionParser {\n  public:\n    virtual ~SectionParser() {}\n    virtual Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,\n                                      int line) = 0;\n    virtual Result<void> ParseLineSection(std::vector<std::string>&&, int) { return {}; };\n    virtual Result<void> EndSection() { return {}; };\n    virtual void EndFile(){};\n};\n\nclass Parser {\n  public:\n    //  LineCallback is the type for callbacks that can parse a line starting with a given prefix.\n    //\n    //  They take the form of bool Callback(std::vector<std::string>&& args, std::string* err)\n    //\n    //  Similar to ParseSection() and ParseLineSection(), this function returns bool with false\n    //  indicating a failure and has an std::string* err parameter into which an error string can\n    //  be written.\n    using LineCallback = std::function<Result<void>(std::vector<std::string>&&)>;\n\n    Parser();\n\n    bool ParseConfig(const std::string& path);\n    Result<void> ParseConfigFile(const std::string& path);\n    void AddSectionParser(const std::string& name, std::unique_ptr<SectionParser> parser);\n    void AddSingleLineParser(const std::string& prefix, LineCallback callback);\n\n    // Host init verifier check file permissions.\n    bool ParseConfigFileInsecure(const std::string& path, bool follow_symlinks);\n\n    size_t parse_error_count() const { return parse_error_count_; }\n\n  private:\n    void ParseData(const std::string& filename, std::string* data);\n    bool ParseConfigDir(const std::string& path);\n\n    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;\n    std::vector<std::pair<std::string, LineCallback>> line_callbacks_;\n    size_t parse_error_count_ = 0;\n};\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/perfboot.py",
    "content": "#!/usr/bin/env python3\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Record the event logs during boot and output them to a file.\n\nThis script repeats the record of each event log during Android boot specified\ntimes. By default, interval between measurements is adjusted in such a way that\nCPUs are cooled down sufficiently to avoid boot time slowdown caused by CPU\nthermal throttling. The result is output in a tab-separated value format.\n\nExamples:\n\nRepeat measurements 10 times. Interval between iterations is adjusted based on\nCPU temperature of the device.\n\n$ ./perfboot.py --iterations=10\n\nRepeat measurements 20 times. 60 seconds interval is taken between each\niteration.\n\n$ ./perfboot.py --iterations=20 --interval=60\n\nRepeat measurements 20 times, show verbose output, output the result to\ndata.tsv, and read event tags from eventtags.txt.\n\n$ ./perfboot.py --iterations=30 -v --output=data.tsv --tags=eventtags.txt\n\"\"\"\n\nimport argparse\nimport atexit\nimport io\nimport glob\nimport inspect\nimport logging\nimport math\nimport os\nimport re\nimport subprocess\nimport sys\nimport threading\nimport time\n\nsys.path.append(os.path.dirname(os.path.dirname(__file__)))\nimport adb\n\n# The default event tags to record.\n_DEFAULT_EVENT_TAGS = [\n    'boot_progress_start',\n    'boot_progress_preload_start',\n    'boot_progress_preload_end',\n    'boot_progress_system_run',\n    'boot_progress_pms_start',\n    'boot_progress_pms_system_scan_start',\n    'boot_progress_pms_data_scan_start',\n    'boot_progress_pms_scan_end',\n    'boot_progress_pms_ready',\n    'boot_progress_ams_ready',\n    'boot_progress_enable_screen',\n    'sf_stop_bootanim',\n    'wm_boot_animation_done',\n]\n\n\nclass IntervalAdjuster(object):\n    \"\"\"A helper class to take suffficient interval between iterations.\"\"\"\n\n    # CPU temperature values per product used to decide interval\n    _CPU_COOL_DOWN_THRESHOLDS = {\n        'flo': 40,\n        'flounder': 40000,\n        'razor': 40,\n        'volantis': 40000,\n    }\n    # The interval between CPU temperature checks\n    _CPU_COOL_DOWN_WAIT_INTERVAL = 10\n    # The wait time used when the value of _CPU_COOL_DOWN_THRESHOLDS for\n    # the product is not defined.\n    _CPU_COOL_DOWN_WAIT_TIME_DEFAULT = 120\n\n    def __init__(self, interval, device):\n        self._interval = interval\n        self._device = device\n        self._temp_paths = device.shell(\n            ['ls', '/sys/class/thermal/thermal_zone*/temp'])[0].splitlines()\n        self._product = device.get_prop('ro.build.product')\n        self._waited = False\n\n    def wait(self):\n        \"\"\"Waits certain amount of time for CPUs cool-down.\"\"\"\n        if self._interval is None:\n            self._wait_cpu_cool_down(self._product, self._temp_paths)\n        else:\n            if self._waited:\n                print('Waiting for %d seconds' % self._interval)\n                time.sleep(self._interval)\n        self._waited = True\n\n    def _get_cpu_temp(self, threshold):\n        max_temp = 0\n        for temp_path in self._temp_paths:\n            temp = int(self._device.shell(['cat', temp_path])[0].rstrip())\n            max_temp = max(max_temp, temp)\n            if temp >= threshold:\n                return temp\n        return max_temp\n\n    def _wait_cpu_cool_down(self, product, temp_paths):\n        threshold = IntervalAdjuster._CPU_COOL_DOWN_THRESHOLDS.get(\n            self._product)\n        if threshold is None:\n            print('No CPU temperature threshold is set for ' + self._product)\n            print(('Just wait %d seconds' %\n                   IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT))\n            time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_TIME_DEFAULT)\n            return\n        while True:\n            temp = self._get_cpu_temp(threshold)\n            if temp < threshold:\n                logging.info('Current CPU temperature %s' % temp)\n                return\n            print('Waiting until CPU temperature (%d) falls below %d' % (\n                temp, threshold))\n            time.sleep(IntervalAdjuster._CPU_COOL_DOWN_WAIT_INTERVAL)\n\n\nclass WatchdogTimer(object):\n    \"\"\"A timer that makes is_timedout() return true in |timeout| seconds.\"\"\"\n    def __init__(self, timeout):\n        self._timedout = False\n\n        def notify_timeout():\n            self._timedout = True\n        self._timer = threading.Timer(timeout, notify_timeout)\n        self._timer.daemon = True\n        self._timer.start()\n\n    def is_timedout(self):\n        return self._timedout\n\n    def cancel(self):\n        self._timer.cancel()\n\n\ndef readlines_unbuffered(proc):\n    \"\"\"Read lines from |proc|'s standard out without buffering.\"\"\"\n    while True:\n        buf = []\n        c = proc.stdout.read(1)\n        if c == '' and proc.poll() is not None:\n            break\n        while c != '\\n':\n            if c == '' and proc.poll() is not None:\n                break\n            buf.append(c)\n            c = proc.stdout.read(1)\n        yield ''.join(buf)\n\n\ndef disable_dropbox(device):\n    \"\"\"Removes the files created by Dropbox and avoids creating the files.\"\"\"\n    device.root()\n    device.wait()\n    device.shell(['rm', '-rf', '/system/data/dropbox'])\n    original_dropbox_max_files = device.shell(\n        ['settings', 'get', 'global', 'dropbox_max_files'])[0].rstrip()\n    device.shell(['settings', 'put', 'global', 'dropbox_max_files', '0'])\n    return original_dropbox_max_files\n\n\ndef restore_dropbox(device, original_dropbox_max_files):\n    \"\"\"Restores the dropbox_max_files setting.\"\"\"\n    device.root()\n    device.wait()\n    if original_dropbox_max_files == 'null':\n        device.shell(['settings', 'delete', 'global', 'dropbox_max_files'])\n    else:\n        device.shell(['settings', 'put', 'global', 'dropbox_max_files',\n                      original_dropbox_max_files])\n\n\ndef init_perf(device, output, record_list, tags):\n    device.wait()\n    debuggable = device.get_prop('ro.debuggable')\n    original_dropbox_max_files = None\n    if debuggable == '1':\n        # Workaround for Dropbox issue (http://b/20890386).\n        original_dropbox_max_files = disable_dropbox(device)\n\n    def cleanup():\n        try:\n            if record_list:\n                print_summary(record_list, tags[-1])\n                output_results(output, record_list, tags)\n            if original_dropbox_max_files is not None:\n                restore_dropbox(device, original_dropbox_max_files)\n        except (subprocess.CalledProcessError, RuntimeError):\n            pass\n    atexit.register(cleanup)\n\n\ndef check_dm_verity_settings(device):\n    device.wait()\n    for partition in ['system', 'vendor']:\n        verity_mode = device.get_prop('partition.%s.verified' % partition)\n        if verity_mode is None:\n            logging.warning('dm-verity is not enabled for /%s. Did you run '\n                            'adb disable-verity? That may skew the result.',\n                            partition)\n\n\ndef read_event_tags(tags_file):\n    \"\"\"Reads event tags from |tags_file|.\"\"\"\n    if not tags_file:\n        return _DEFAULT_EVENT_TAGS\n    tags = []\n    with open(tags_file) as f:\n        for line in f:\n            if '#' in line:\n                line = line[:line.find('#')]\n            line = line.strip()\n            if line:\n                tags.append(line)\n    return tags\n\n\ndef make_event_tags_re(tags):\n    \"\"\"Makes a regular expression object that matches event logs of |tags|.\"\"\"\n    return re.compile(r'(?P<pid>[0-9]+) +[0-9]+ I (?P<tag>%s): (?P<time>\\d+)' %\n                      '|'.join(tags))\n\n\ndef filter_event_tags(tags, device):\n    \"\"\"Drop unknown tags not listed in device's event-log-tags file.\"\"\"\n    device.wait()\n    supported_tags = set()\n    for l in device.shell(\n        ['cat', '/system/etc/event-log-tags'])[0].splitlines():\n        tokens = l.split(' ')\n        if len(tokens) >= 2:\n            supported_tags.add(tokens[1])\n    filtered = []\n    for tag in tags:\n        if tag in supported_tags:\n            filtered.append(tag)\n        else:\n            logging.warning('Unknown tag \\'%s\\'. Ignoring...', tag)\n    return filtered\n\n\ndef get_values(record, tag):\n    \"\"\"Gets values that matches |tag| from |record|.\"\"\"\n    keys = [key for key in list(record.keys()) if key[0] == tag]\n    return [record[k] for k in sorted(keys)]\n\n\ndef get_last_value(record, tag):\n    \"\"\"Gets the last value that matches |tag| from |record|.\"\"\"\n    values = get_values(record, tag)\n    if not values:\n        return 0\n    return values[-1]\n\n\ndef output_results(filename, record_list, tags):\n    \"\"\"Outputs |record_list| into |filename| in a TSV format.\"\"\"\n    # First, count the number of the values of each tag.\n    # This is for dealing with events that occur multiple times.\n    # For instance, boot_progress_preload_start and boot_progress_preload_end\n    # are recorded twice on 64-bit system. One is for 64-bit zygote process\n    # and the other is for 32-bit zygote process.\n    values_counter = {}\n    for record in record_list:\n        for tag in tags:\n            # Some record might lack values for some tags due to unanticipated\n            # problems (e.g. timeout), so take the maximum count among all the\n            # record.\n            values_counter[tag] = max(values_counter.get(tag, 1),\n                                      len(get_values(record, tag)))\n\n    # Then creates labels for the data. If there are multiple values for one\n    # tag, labels for these values are numbered except the first one as\n    # follows:\n    #\n    # event_tag event_tag2 event_tag3\n    #\n    # The corresponding values are sorted in an ascending order of PID.\n    labels = []\n    for tag in tags:\n        for i in range(1, values_counter[tag] + 1):\n            labels.append('%s%s' % (tag, '' if i == 1 else str(i)))\n\n    # Finally write the data into the file.\n    with open(filename, 'w') as f:\n        f.write('\\t'.join(labels) + '\\n')\n        for record in record_list:\n            line = io.StringIO()\n            invalid_line = False\n            for i, tag in enumerate(tags):\n                if i != 0:\n                    line.write('\\t')\n                values = get_values(record, tag)\n                if len(values) < values_counter[tag]:\n                    invalid_line = True\n                    # Fill invalid record with 0\n                    values += [0] * (values_counter[tag] - len(values))\n                line.write('\\t'.join(str(t) for t in values))\n            if invalid_line:\n                logging.error('Invalid record found: ' + line.getvalue())\n            line.write('\\n')\n            f.write(line.getvalue())\n    print(('Wrote: ' + filename))\n\n\ndef median(data):\n    \"\"\"Calculates the median value from |data|.\"\"\"\n    data = sorted(data)\n    n = len(data)\n    if n % 2 == 1:\n        return data[n / 2]\n    else:\n        n2 = n / 2\n        return (data[n2 - 1] + data[n2]) / 2.0\n\n\ndef mean(data):\n    \"\"\"Calculates the mean value from |data|.\"\"\"\n    return float(sum(data)) / len(data)\n\n\ndef stddev(data):\n    \"\"\"Calculates the standard deviation value from |value|.\"\"\"\n    m = mean(data)\n    return math.sqrt(sum((x - m) ** 2 for x in data) / len(data))\n\n\ndef print_summary(record_list, end_tag):\n    \"\"\"Prints the summary of |record_list|.\"\"\"\n    # Filter out invalid data.\n    end_times = [get_last_value(record, end_tag) for record in record_list\n                 if get_last_value(record, end_tag) != 0]\n    print(('mean:', int(round(mean(end_times))), 'ms'))\n    print(('median:', int(round(median(end_times))), 'ms'))\n    print(('standard deviation:', int(round(stddev(end_times))), 'ms'))\n\n\ndef do_iteration(device, interval_adjuster, event_tags_re, end_tag):\n    \"\"\"Measures the boot time once.\"\"\"\n    device.wait()\n    interval_adjuster.wait()\n    device.reboot()\n    print('Rebooted the device, waiting for tag', end_tag)\n    record = {}\n    booted = False\n    while not booted:\n        device.wait()\n        # Stop the iteration if it does not finish within 120 seconds.\n        timeout = 120\n        t = WatchdogTimer(timeout)\n        p = subprocess.Popen(\n                ['adb', 'logcat', '-b', 'events', '-v', 'threadtime'],\n                stdout=subprocess.PIPE)\n        for line in readlines_unbuffered(p):\n            if t.is_timedout():\n                print('*** Timed out ***')\n                return record\n            m = event_tags_re.search(line)\n            if not m:\n                continue\n            tag = m.group('tag')\n            event_time = int(m.group('time'))\n            pid = m.group('pid')\n            record[(tag, pid)] = event_time\n            print(('Event log recorded: %s (%s) - %d ms' % (\n                tag, pid, event_time)))\n            if tag == end_tag:\n                booted = True\n                t.cancel()\n                break\n    return record\n\n\ndef parse_args():\n    \"\"\"Parses the command line arguments.\"\"\"\n    parser = argparse.ArgumentParser(\n        description=inspect.getdoc(sys.modules[__name__]),\n        formatter_class=argparse.RawDescriptionHelpFormatter)\n    parser.add_argument('--iterations', type=int, default=5,\n                        help='Number of times to repeat boot measurements.')\n    parser.add_argument('--interval', type=int,\n                        help=('Duration between iterations. If this is not '\n                              'set explicitly, durations are determined '\n                              'adaptively based on CPUs temperature.'))\n    parser.add_argument('-o', '--output', help='File name of output data.')\n    parser.add_argument('-v', '--verbose', action='store_true',\n                        help='Show verbose output.')\n    parser.add_argument('-s', '--serial', default=os.getenv('ANDROID_SERIAL'),\n                        help='Adb device serial number.')\n    parser.add_argument('-t', '--tags', help='Specify the filename from which '\n                        'event tags are read. Every line contains one event '\n                        'tag and the last event tag is used to detect that '\n                        'the device has finished booting unless --end-tag is '\n                        'specified.')\n    parser.add_argument('--end-tag', help='An event tag on which the script '\n                        'stops measuring the boot time.')\n    parser.add_argument('--apk-dir', help='Specify the directory which contains '\n                        'APK files to be installed before measuring boot time.')\n    return parser.parse_args()\n\n\ndef install_apks(device, apk_dir):\n    for apk in glob.glob(os.path.join(apk_dir, '*.apk')):\n        print('Installing: ' + apk)\n        device.install(apk, replace=True)\n\n\ndef main():\n    args = parse_args()\n    if args.verbose:\n        logging.getLogger().setLevel(logging.INFO)\n\n    device = adb.get_device(args.serial)\n\n    if not args.output:\n        device.wait()\n        args.output = 'perf-%s-%s.tsv' % (\n            device.get_prop('ro.build.flavor'),\n            device.get_prop('ro.build.version.incremental'))\n    check_dm_verity_settings(device)\n\n    if args.apk_dir:\n        install_apks(device, args.apk_dir)\n\n    record_list = []\n    event_tags = filter_event_tags(read_event_tags(args.tags), device)\n    end_tag = args.end_tag or event_tags[-1]\n    if end_tag not in event_tags:\n        sys.exit('%s is not a valid tag.' % end_tag)\n    event_tags = event_tags[0 : event_tags.index(end_tag) + 1]\n    init_perf(device, args.output, record_list, event_tags)\n    interval_adjuster = IntervalAdjuster(args.interval, device)\n    event_tags_re = make_event_tags_re(event_tags)\n\n    for i in range(args.iterations):\n        print('Run #%d ' % i)\n        record = do_iteration(\n            device, interval_adjuster, event_tags_re, end_tag)\n        record_list.append(record)\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "init/persistent_properties.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"persistent_properties.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <sys/system_properties.h>\n#include <sys/types.h>\n\n#include <memory>\n#include <unordered_map>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"util.h\"\n\nusing android::base::Dirname;\nusing android::base::ReadFdToString;\nusing android::base::StartsWith;\nusing android::base::unique_fd;\nusing android::base::WriteStringToFd;\n\nnamespace android {\nnamespace init {\n\nstd::string persistent_property_filename = \"/data/property/persistent_properties\";\n\nnamespace {\n\nconstexpr const char kLegacyPersistentPropertyDir[] = \"/data/property\";\n\nvoid AddPersistentProperty(const std::string& name, const std::string& value,\n                           PersistentProperties* persistent_properties) {\n    auto persistent_property_record = persistent_properties->add_properties();\n    persistent_property_record->set_name(name);\n    persistent_property_record->set_value(value);\n}\n\nResult<PersistentProperties> LoadLegacyPersistentProperties() {\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);\n    if (!dir) {\n        return ErrnoError() << \"Unable to open persistent property directory \\\"\"\n                            << kLegacyPersistentPropertyDir << \"\\\"\";\n    }\n\n    PersistentProperties persistent_properties;\n    dirent* entry;\n    while ((entry = readdir(dir.get())) != nullptr) {\n        if (!StartsWith(entry->d_name, \"persist.\")) {\n            continue;\n        }\n        if (entry->d_type != DT_REG) {\n            continue;\n        }\n\n        unique_fd fd(openat(dirfd(dir.get()), entry->d_name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));\n        if (fd == -1) {\n            PLOG(ERROR) << \"Unable to open persistent property file \\\"\" << entry->d_name << \"\\\"\";\n            continue;\n        }\n\n        struct stat sb;\n        if (fstat(fd.get(), &sb) == -1) {\n            PLOG(ERROR) << \"fstat on property file \\\"\" << entry->d_name << \"\\\" failed\";\n            continue;\n        }\n\n        // File must not be accessible to others, be owned by root/root, and\n        // not be a hard link to any other file.\n        if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) || sb.st_uid != 0 || sb.st_gid != 0 ||\n            sb.st_nlink != 1) {\n            PLOG(ERROR) << \"skipping insecure property file \" << entry->d_name\n                        << \" (uid=\" << sb.st_uid << \" gid=\" << sb.st_gid << \" nlink=\" << sb.st_nlink\n                        << \" mode=\" << std::oct << sb.st_mode << \")\";\n            continue;\n        }\n\n        std::string value;\n        if (ReadFdToString(fd, &value)) {\n            AddPersistentProperty(entry->d_name, value, &persistent_properties);\n        } else {\n            PLOG(ERROR) << \"Unable to read persistent property file \" << entry->d_name;\n        }\n    }\n    return persistent_properties;\n}\n\nvoid RemoveLegacyPersistentPropertyFiles() {\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(kLegacyPersistentPropertyDir), closedir);\n    if (!dir) {\n        PLOG(ERROR) << \"Unable to open persistent property directory \\\"\"\n                    << kLegacyPersistentPropertyDir << \"\\\"\";\n        return;\n    }\n\n    dirent* entry;\n    while ((entry = readdir(dir.get())) != nullptr) {\n        if (!StartsWith(entry->d_name, \"persist.\")) {\n            continue;\n        }\n        if (entry->d_type != DT_REG) {\n            continue;\n        }\n        unlinkat(dirfd(dir.get()), entry->d_name, 0);\n    }\n}\n\nResult<std::string> ReadPersistentPropertyFile() {\n    const std::string temp_filename = persistent_property_filename + \".tmp\";\n    if (access(temp_filename.c_str(), F_OK) == 0) {\n        LOG(INFO)\n            << \"Found temporary property file while attempting to persistent system properties\"\n               \" a previous persistent property write may have failed\";\n        unlink(temp_filename.c_str());\n    }\n    auto file_contents = ReadFile(persistent_property_filename);\n    if (!file_contents.ok()) {\n        return Error() << \"Unable to read persistent property file: \" << file_contents.error();\n    }\n    return *file_contents;\n}\n\nResult<PersistentProperties> ParsePersistentPropertyFile(const std::string& file_contents) {\n    PersistentProperties persistent_properties;\n    if (!persistent_properties.ParseFromString(file_contents)) {\n        return Error() << \"Unable to parse persistent property file: Could not parse protobuf\";\n    }\n    for (auto& prop : persistent_properties.properties()) {\n        if (!StartsWith(prop.name(), \"persist.\") && !StartsWith(prop.name(), \"next_boot.\")) {\n            return Error() << \"Unable to load persistent property file: property '\" << prop.name()\n                           << \"' doesn't start with 'persist.' or 'next_boot.'\";\n        }\n    }\n    return persistent_properties;\n}\n\n}  // namespace\n\nResult<PersistentProperties> LoadPersistentPropertyFile() {\n    auto file_contents = ReadPersistentPropertyFile();\n    if (!file_contents.ok()) return file_contents.error();\n\n    auto persistent_properties = ParsePersistentPropertyFile(*file_contents);\n    if (!persistent_properties.ok()) {\n        // If the file cannot be parsed in either format, then we don't have any recovery\n        // mechanisms, so we delete it to allow for future writes to take place successfully.\n        unlink(persistent_property_filename.c_str());\n    }\n    return persistent_properties;\n}\n\nResult<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) {\n    const std::string temp_filename = persistent_property_filename + \".tmp\";\n    unique_fd fd(TEMP_FAILURE_RETRY(\n        open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));\n    if (fd == -1) {\n        return ErrnoError() << \"Could not open temporary properties file\";\n    }\n    std::string serialized_string;\n    if (!persistent_properties.SerializeToString(&serialized_string)) {\n        return Error() << \"Unable to serialize properties\";\n    }\n    if (!WriteStringToFd(serialized_string, fd)) {\n        return ErrnoError() << \"Unable to write file contents\";\n    }\n    fsync(fd.get());\n    fd.reset();\n\n    if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) {\n        int saved_errno = errno;\n        unlink(temp_filename.c_str());\n        return Error(saved_errno) << \"Unable to rename persistent property file\";\n    }\n\n    // rename() is atomic with regards to the kernel's filesystem buffers, but the parent\n    // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage.\n    // Note in this case, that the source and destination directories are the same, so only one\n    // fsync() is required.\n    auto dir = Dirname(persistent_property_filename);\n    auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)};\n    if (dir_fd < 0) {\n        return ErrnoError() << \"Unable to open persistent properties directory for fsync()\";\n    }\n    fsync(dir_fd.get());\n\n    return {};\n}\n\nPersistentProperties LoadPersistentPropertiesFromMemory() {\n    PersistentProperties persistent_properties;\n    __system_property_foreach(\n            [](const prop_info* pi, void* cookie) {\n                __system_property_read_callback(\n                        pi,\n                        [](void* cookie, const char* name, const char* value, unsigned serial) {\n                            if (StartsWith(name, \"persist.\")) {\n                                auto properties = reinterpret_cast<PersistentProperties*>(cookie);\n                                AddPersistentProperty(name, value, properties);\n                            }\n                        },\n                        cookie);\n            },\n            &persistent_properties);\n    return persistent_properties;\n}\n\n// Persistent properties are not written often, so we rather not keep any data in memory and read\n// then rewrite the persistent property file for each update.\nvoid WritePersistentProperty(const std::string& name, const std::string& value) {\n    auto persistent_properties = LoadPersistentPropertyFile();\n\n    if (!persistent_properties.ok()) {\n        LOG(ERROR) << \"Recovering persistent properties from memory: \"\n                   << persistent_properties.error();\n        persistent_properties = LoadPersistentPropertiesFromMemory();\n    }\n    auto it = std::find_if(persistent_properties->mutable_properties()->begin(),\n                           persistent_properties->mutable_properties()->end(),\n                           [&name](const auto& record) { return record.name() == name; });\n    if (it != persistent_properties->mutable_properties()->end()) {\n        if (it->value() == value) {\n            return;\n        }\n        it->set_name(name);\n        it->set_value(value);\n    } else {\n        AddPersistentProperty(name, value, &persistent_properties.value());\n    }\n\n    if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) {\n        LOG(ERROR) << \"Could not store persistent property: \" << result.error();\n    }\n}\n\nPersistentProperties LoadPersistentProperties() {\n    auto persistent_properties = LoadPersistentPropertyFile();\n\n    if (!persistent_properties.ok()) {\n        LOG(ERROR) << \"Could not load single persistent property file, trying legacy directory: \"\n                   << persistent_properties.error();\n        persistent_properties = LoadLegacyPersistentProperties();\n        if (!persistent_properties.ok()) {\n            LOG(ERROR) << \"Unable to load legacy persistent properties: \"\n                       << persistent_properties.error();\n            return {};\n        }\n        if (auto result = WritePersistentPropertyFile(*persistent_properties); result.ok()) {\n            RemoveLegacyPersistentPropertyFiles();\n        } else {\n            LOG(ERROR) << \"Unable to write single persistent property file: \" << result.error();\n            // Fall through so that we still set the properties that we've read.\n        }\n    }\n\n    // loop over to find all staged props\n    auto const staged_prefix = std::string_view(\"next_boot.\");\n    auto staged_props = std::unordered_map<std::string, std::string>();\n    for (const auto& property_record : persistent_properties->properties()) {\n        auto const& prop_name = property_record.name();\n        auto const& prop_value = property_record.value();\n        if (StartsWith(prop_name, staged_prefix)) {\n            auto actual_prop_name = prop_name.substr(staged_prefix.size());\n            staged_props[actual_prop_name] = prop_value;\n        }\n    }\n\n    if (staged_props.empty()) {\n        return *persistent_properties;\n    }\n\n    // if has staging, apply staging and perserve the original prop order\n    PersistentProperties updated_persistent_properties;\n    for (const auto& property_record : persistent_properties->properties()) {\n        auto const& prop_name = property_record.name();\n        auto const& prop_value = property_record.value();\n\n        // don't include staged props anymore\n        if (StartsWith(prop_name, staged_prefix)) {\n            continue;\n        }\n\n        auto iter = staged_props.find(prop_name);\n        if (iter != staged_props.end()) {\n            AddPersistentProperty(prop_name, iter->second, &updated_persistent_properties);\n            staged_props.erase(iter);\n        } else {\n            AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);\n        }\n    }\n\n    // add any additional staged props\n    for (auto const& [prop_name, prop_value] : staged_props) {\n        AddPersistentProperty(prop_name, prop_value, &updated_persistent_properties);\n    }\n\n    // write current updated persist prop file\n    auto result = WritePersistentPropertyFile(updated_persistent_properties);\n    if (!result.ok()) {\n        LOG(ERROR) << \"Could not store persistent property: \" << result.error();\n    }\n\n    return updated_persistent_properties;\n}\n\n\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/persistent_properties.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_PERSISTENT_PROPERTIES_H\n#define _INIT_PERSISTENT_PROPERTIES_H\n\n#include <string>\n\n#include \"result.h\"\n#include \"system/core/init/persistent_properties.pb.h\"\n\nnamespace android {\nnamespace init {\n\nPersistentProperties LoadPersistentProperties();\nvoid WritePersistentProperty(const std::string& name, const std::string& value);\nPersistentProperties LoadPersistentPropertiesFromMemory();\n\n// Exposed only for testing\nResult<PersistentProperties> LoadPersistentPropertyFile();\nResult<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties);\nextern std::string persistent_property_filename;\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/persistent_properties.proto",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nsyntax = \"proto2\";\noption optimize_for = LITE_RUNTIME;\n\nmessage PersistentProperties {\n    message PersistentPropertyRecord {\n        optional string name = 1;\n        optional string value = 2;\n    }\n\n    repeated PersistentPropertyRecord properties = 1;\n}\n"
  },
  {
    "path": "init/persistent_properties_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"persistent_properties.h\"\n\n#include <errno.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <vector>\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n\n#include \"util.h\"\n\nusing namespace std::string_literals;\n\nnamespace android {\nnamespace init {\n\nPersistentProperties VectorToPersistentProperties(\n    const std::vector<std::pair<std::string, std::string>>& input_properties) {\n    PersistentProperties persistent_properties;\n\n    for (const auto& [name, value] : input_properties) {\n        auto persistent_property_record = persistent_properties.add_properties();\n        persistent_property_record->set_name(name);\n        persistent_property_record->set_value(value);\n    }\n\n    return persistent_properties;\n}\n\nvoid CheckPropertiesEqual(std::vector<std::pair<std::string, std::string>> expected,\n                          const PersistentProperties& persistent_properties) {\n    for (const auto& persistent_property_record : persistent_properties.properties()) {\n        auto it = std::find_if(expected.begin(), expected.end(),\n                               [persistent_property_record](const auto& entry) {\n                                   return entry.first == persistent_property_record.name() &&\n                                          entry.second == persistent_property_record.value();\n                               });\n        ASSERT_TRUE(it != expected.end())\n            << \"Found unexpected property (\" << persistent_property_record.name() << \", \"\n            << persistent_property_record.value() << \")\";\n        expected.erase(it);\n    }\n    auto joiner = [](const std::vector<std::pair<std::string, std::string>>& vector) {\n        std::string result;\n        for (const auto& [name, value] : vector) {\n            result += \" (\" + name + \", \" + value + \")\";\n        }\n        return result;\n    };\n    EXPECT_TRUE(expected.empty()) << \"Did not find expected properties:\" << joiner(expected);\n}\n\nTEST(persistent_properties, EndToEnd) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    persistent_property_filename = tf.path;\n\n    std::vector<std::pair<std::string, std::string>> persistent_properties = {\n        {\"persist.sys.locale\", \"en-US\"},\n        {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n        {\"persist.test.empty.value\", \"\"},\n        {\"persist.test.new.line\", \"abc\\n\\n\\nabc\"},\n        {\"persist.test.numbers\", \"1234567890\"},\n        {\"persist.test.non.ascii\", \"\\x00\\x01\\x02\\xFF\\xFE\\xFD\\x7F\\x8F\\x9F\"},\n        // We don't currently allow for non-ascii names for system properties, but this is a policy\n        // decision, not a technical limitation.\n        {\"persist.\\x00\\x01\\x02\\xFF\\xFE\\xFD\\x7F\\x8F\\x9F\", \"non-ascii-name\"},\n    };\n\n    ASSERT_RESULT_OK(\n            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));\n\n    auto read_back_properties = LoadPersistentProperties();\n    CheckPropertiesEqual(persistent_properties, read_back_properties);\n}\n\nTEST(persistent_properties, AddProperty) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    persistent_property_filename = tf.path;\n\n    std::vector<std::pair<std::string, std::string>> persistent_properties = {\n        {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n    };\n    ASSERT_RESULT_OK(\n            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));\n\n    WritePersistentProperty(\"persist.sys.locale\", \"pt-BR\");\n\n    std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {\n        {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n        {\"persist.sys.locale\", \"pt-BR\"},\n    };\n\n    auto read_back_properties = LoadPersistentProperties();\n    CheckPropertiesEqual(persistent_properties_expected, read_back_properties);\n}\n\nTEST(persistent_properties, UpdateProperty) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    persistent_property_filename = tf.path;\n\n    std::vector<std::pair<std::string, std::string>> persistent_properties = {\n        {\"persist.sys.locale\", \"en-US\"},\n        {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n    };\n    ASSERT_RESULT_OK(\n            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));\n\n    WritePersistentProperty(\"persist.sys.locale\", \"pt-BR\");\n\n    std::vector<std::pair<std::string, std::string>> persistent_properties_expected = {\n        {\"persist.sys.locale\", \"pt-BR\"},\n        {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n    };\n\n    auto read_back_properties = LoadPersistentProperties();\n    CheckPropertiesEqual(persistent_properties_expected, read_back_properties);\n}\n\nTEST(persistent_properties, UpdatePropertyBadParse) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    persistent_property_filename = tf.path;\n\n    ASSERT_RESULT_OK(WriteFile(tf.path, \"ab\"));\n\n    WritePersistentProperty(\"persist.sys.locale\", \"pt-BR\");\n\n    auto read_back_properties = LoadPersistentProperties();\n    EXPECT_GT(read_back_properties.properties().size(), 0);\n\n    auto it =\n        std::find_if(read_back_properties.properties().begin(),\n                     read_back_properties.properties().end(), [](const auto& entry) {\n                         return entry.name() == \"persist.sys.locale\" && entry.value() == \"pt-BR\";\n                     });\n    EXPECT_FALSE(it == read_back_properties.properties().end());\n}\n\nTEST(persistent_properties, NopUpdateDoesntWriteFile) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    persistent_property_filename = tf.path;\n\n    auto last_modified = [&tf]() -> time_t {\n        struct stat buf;\n        EXPECT_EQ(fstat(tf.fd, &buf), 0);\n        return buf.st_mtime;\n    };\n\n    std::vector<std::pair<std::string, std::string>> persistent_properties = {\n            {\"persist.sys.locale\", \"en-US\"},\n            {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n    };\n    ASSERT_RESULT_OK(\n            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));\n\n    time_t t = last_modified();\n    sleep(2);\n    WritePersistentProperty(\"persist.sys.locale\", \"en-US\");\n    // Ensure that the file was not modified\n    ASSERT_EQ(last_modified(), t);\n}\n\nTEST(persistent_properties, RejectNonPersistProperty) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    persistent_property_filename = tf.path;\n\n    WritePersistentProperty(\"notpersist.sys.locale\", \"pt-BR\");\n\n    auto read_back_properties = LoadPersistentProperties();\n    EXPECT_EQ(read_back_properties.properties().size(), 0);\n\n    WritePersistentProperty(\"persist.sys.locale\", \"pt-BR\");\n\n    read_back_properties = LoadPersistentProperties();\n    EXPECT_GT(read_back_properties.properties().size(), 0);\n\n    auto it = std::find_if(read_back_properties.properties().begin(),\n                           read_back_properties.properties().end(), [](const auto& entry) {\n                               return entry.name() == \"persist.sys.locale\" &&\n                                      entry.value() == \"pt-BR\";\n                           });\n    EXPECT_FALSE(it == read_back_properties.properties().end());\n}\n\nTEST(persistent_properties, StagedPersistProperty) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    persistent_property_filename = tf.path;\n\n    std::vector<std::pair<std::string, std::string>> persistent_properties = {\n        {\"persist.sys.locale\", \"en-US\"},\n        {\"next_boot.persist.test.numbers\", \"54321\"},\n        {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n        {\"persist.test.numbers\", \"12345\"},\n        {\"next_boot.persist.test.extra\", \"abc\"},\n    };\n\n    ASSERT_RESULT_OK(\n            WritePersistentPropertyFile(VectorToPersistentProperties(persistent_properties)));\n\n    std::vector<std::pair<std::string, std::string>> expected_persistent_properties = {\n        {\"persist.sys.locale\", \"en-US\"},\n        {\"persist.sys.timezone\", \"America/Los_Angeles\"},\n        {\"persist.test.numbers\", \"54321\"},\n        {\"persist.test.extra\", \"abc\"},\n    };\n\n    // lock down that staged props are applied\n    auto first_read_back_properties = LoadPersistentProperties();\n    CheckPropertiesEqual(expected_persistent_properties, first_read_back_properties);\n\n    // lock down that other props are not overwritten\n    auto second_read_back_properties = LoadPersistentProperties();\n    CheckPropertiesEqual(expected_persistent_properties, second_read_back_properties);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/property_service.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"property_service.h\"\n\n#include <android/api-level.h>\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <netinet/in.h>\n#include <stdarg.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/poll.h>\n#include <sys/select.h>\n#include <sys/system_properties.h>\n#include <sys/types.h>\n#include <sys/un.h>\n#include <unistd.h>\n#include <wchar.h>\n\n#include <map>\n#include <memory>\n#include <mutex>\n#include <optional>\n#include <queue>\n#include <string_view>\n#include <thread>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/result.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <fs_mgr.h>\n#include <private/android_filesystem_config.h>\n#include <property_info_parser/property_info_parser.h>\n#include <property_info_serializer/property_info_serializer.h>\n#include <selinux/android.h>\n#include <selinux/label.h>\n#include <selinux/selinux.h>\n#include <vendorsupport/api_level.h>\n\n#include \"debug_ramdisk.h\"\n#include \"epoll.h\"\n#include \"init.h\"\n#include \"persistent_properties.h\"\n#include \"property_type.h\"\n#include \"proto_utils.h\"\n#include \"second_stage_resources.h\"\n#include \"selinux.h\"\n#include \"subcontext.h\"\n#include \"system/core/init/property_service.pb.h\"\n#include \"util.h\"\n\nstatic constexpr char APPCOMPAT_OVERRIDE_PROP_FOLDERNAME[] =\n        \"/dev/__properties__/appcompat_override\";\nstatic constexpr char APPCOMPAT_OVERRIDE_PROP_TREE_FILE[] =\n        \"/dev/__properties__/appcompat_override/property_info\";\nusing namespace std::literals;\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::GetIntProperty;\nusing android::base::GetProperty;\nusing android::base::ParseInt;\nusing android::base::ReadFileToString;\nusing android::base::Result;\nusing android::base::Split;\nusing android::base::StartsWith;\nusing android::base::StringPrintf;\nusing android::base::Timer;\nusing android::base::Trim;\nusing android::base::unique_fd;\nusing android::base::WriteStringToFile;\nusing android::properties::BuildTrie;\nusing android::properties::ParsePropertyInfoFile;\nusing android::properties::PropertyInfoAreaFile;\nusing android::properties::PropertyInfoEntry;\n\nnamespace android {\nnamespace init {\n\nconstexpr auto FINGERPRINT_PROP = \"ro.build.fingerprint\";\nconstexpr auto LEGACY_FINGERPRINT_PROP = \"ro.build.legacy.fingerprint\";\nconstexpr auto ID_PROP = \"ro.build.id\";\nconstexpr auto LEGACY_ID_PROP = \"ro.build.legacy.id\";\nconstexpr auto VBMETA_DIGEST_PROP = \"ro.boot.vbmeta.digest\";\nconstexpr auto DIGEST_SIZE_USED = 8;\n\nstatic bool persistent_properties_loaded = false;\n\nstatic int from_init_socket = -1;\nstatic int init_socket = -1;\nstatic bool accept_messages = false;\nstatic std::mutex accept_messages_lock;\nstatic std::mutex selinux_check_access_lock;\nstatic std::thread property_service_thread;\nstatic std::thread property_service_for_system_thread;\n\nstatic PropertyInfoAreaFile property_info_area;\n\nstruct PropertyAuditData {\n    const ucred* cr;\n    const char* name;\n};\n\nstatic int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {\n    auto* d = reinterpret_cast<PropertyAuditData*>(data);\n\n    if (!d || !d->name || !d->cr) {\n        LOG(ERROR) << \"AuditCallback invoked with null data arguments!\";\n        return 0;\n    }\n\n    snprintf(buf, len, \"property=%s pid=%d uid=%d gid=%d\", d->name, d->cr->pid, d->cr->uid,\n             d->cr->gid);\n    return 0;\n}\n\nvoid StartSendingMessages() {\n    auto lock = std::lock_guard{accept_messages_lock};\n    accept_messages = true;\n}\n\nvoid StopSendingMessages() {\n    auto lock = std::lock_guard{accept_messages_lock};\n    accept_messages = false;\n}\n\nbool CanReadProperty(const std::string& source_context, const std::string& name) {\n    const char* target_context = nullptr;\n    property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);\n\n    PropertyAuditData audit_data;\n\n    audit_data.name = name.c_str();\n\n    ucred cr = {.pid = 0, .uid = 0, .gid = 0};\n    audit_data.cr = &cr;\n\n    auto lock = std::lock_guard{selinux_check_access_lock};\n    return selinux_check_access(source_context.c_str(), target_context, \"file\", \"read\",\n                                &audit_data) == 0;\n}\n\nstatic bool CheckMacPerms(const std::string& name, const char* target_context,\n                          const char* source_context, const ucred& cr) {\n    if (!target_context || !source_context) {\n        return false;\n    }\n\n    PropertyAuditData audit_data;\n\n    audit_data.name = name.c_str();\n    audit_data.cr = &cr;\n\n    auto lock = std::lock_guard{selinux_check_access_lock};\n    return selinux_check_access(source_context, target_context, \"property_service\", \"set\",\n                                &audit_data) == 0;\n}\n\nvoid NotifyPropertyChange(const std::string& name, const std::string& value) {\n    // If init hasn't started its main loop, then it won't be handling property changed messages\n    // anyway, so there's no need to try to send them.\n    auto lock = std::lock_guard{accept_messages_lock};\n    if (accept_messages) {\n        PropertyChanged(name, value);\n    }\n}\n\nclass AsyncRestorecon {\n  public:\n    void TriggerRestorecon(const std::string& path) {\n        auto guard = std::lock_guard{mutex_};\n        paths_.emplace(path);\n\n        if (!thread_started_) {\n            thread_started_ = true;\n            std::thread{&AsyncRestorecon::ThreadFunction, this}.detach();\n        }\n    }\n\n  private:\n    void ThreadFunction() {\n        auto lock = std::unique_lock{mutex_};\n\n        while (!paths_.empty()) {\n            auto path = paths_.front();\n            paths_.pop();\n\n            lock.unlock();\n            if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {\n                LOG(ERROR) << \"Asynchronous restorecon of '\" << path << \"' failed'\";\n            }\n            android::base::SetProperty(kRestoreconProperty, path);\n            lock.lock();\n        }\n\n        thread_started_ = false;\n    }\n\n    std::mutex mutex_;\n    std::queue<std::string> paths_;\n    bool thread_started_ = false;\n};\n\nclass SocketConnection {\n  public:\n    SocketConnection() = default;\n    SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}\n    SocketConnection(SocketConnection&&) = default;\n\n    bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {\n        return RecvFully(value, sizeof(*value), timeout_ms);\n    }\n\n    bool RecvChars(char* chars, size_t size, uint32_t* timeout_ms) {\n        return RecvFully(chars, size, timeout_ms);\n    }\n\n    bool RecvString(std::string* value, uint32_t* timeout_ms) {\n        uint32_t len = 0;\n        if (!RecvUint32(&len, timeout_ms)) {\n            return false;\n        }\n\n        if (len == 0) {\n            *value = \"\";\n            return true;\n        }\n\n        // http://b/35166374: don't allow init to make arbitrarily large allocations.\n        if (len > 0xffff) {\n            LOG(ERROR) << \"sys_prop: RecvString asked to read huge string: \" << len;\n            errno = ENOMEM;\n            return false;\n        }\n\n        std::vector<char> chars(len);\n        if (!RecvChars(&chars[0], len, timeout_ms)) {\n            return false;\n        }\n\n        *value = std::string(&chars[0], len);\n        return true;\n    }\n\n    bool SendUint32(uint32_t value) {\n        if (!socket_.ok()) {\n            return true;\n        }\n        int result = TEMP_FAILURE_RETRY(send(socket_.get(), &value, sizeof(value), 0));\n        return result == sizeof(value);\n    }\n\n    bool GetSourceContext(std::string* source_context) const {\n        char* c_source_context = nullptr;\n        if (getpeercon(socket_.get(), &c_source_context) != 0) {\n            return false;\n        }\n        *source_context = c_source_context;\n        freecon(c_source_context);\n        return true;\n    }\n\n    [[nodiscard]] int Release() { return socket_.release(); }\n\n    const ucred& cred() { return cred_; }\n\n    SocketConnection& operator=(SocketConnection&&) = default;\n\n  private:\n    bool PollIn(uint32_t* timeout_ms) {\n        struct pollfd ufd = {\n                .fd = socket_.get(),\n                .events = POLLIN,\n        };\n        while (*timeout_ms > 0) {\n            auto start_time = std::chrono::steady_clock::now();\n            int nr = poll(&ufd, 1, *timeout_ms);\n            auto now = std::chrono::steady_clock::now();\n            auto time_elapsed =\n                std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);\n            uint64_t millis = time_elapsed.count();\n            *timeout_ms = (millis > *timeout_ms) ? 0 : *timeout_ms - millis;\n\n            if (nr > 0) {\n                return true;\n            }\n\n            if (nr == 0) {\n                // Timeout\n                break;\n            }\n\n            if (nr < 0 && errno != EINTR) {\n                PLOG(ERROR) << \"sys_prop: error waiting for uid \" << cred_.uid\n                            << \" to send property message\";\n                return false;\n            } else {  // errno == EINTR\n                // Timer rounds milliseconds down in case of EINTR we want it to be rounded up\n                // to avoid slowing init down by causing EINTR with under millisecond timeout.\n                if (*timeout_ms > 0) {\n                    --(*timeout_ms);\n                }\n            }\n        }\n\n        LOG(ERROR) << \"sys_prop: timeout waiting for uid \" << cred_.uid\n                   << \" to send property message.\";\n        return false;\n    }\n\n    bool RecvFully(void* data_ptr, size_t size, uint32_t* timeout_ms) {\n        size_t bytes_left = size;\n        char* data = static_cast<char*>(data_ptr);\n        while (*timeout_ms > 0 && bytes_left > 0) {\n            if (!PollIn(timeout_ms)) {\n                return false;\n            }\n\n            int result = TEMP_FAILURE_RETRY(recv(socket_.get(), data, bytes_left, MSG_DONTWAIT));\n            if (result <= 0) {\n                PLOG(ERROR) << \"sys_prop: recv error\";\n                return false;\n            }\n\n            bytes_left -= result;\n            data += result;\n        }\n\n        if (bytes_left != 0) {\n            LOG(ERROR) << \"sys_prop: recv data is not properly obtained.\";\n        }\n\n        return bytes_left == 0;\n    }\n\n    unique_fd socket_;\n    ucred cred_;\n\n    DISALLOW_COPY_AND_ASSIGN(SocketConnection);\n};\n\nclass PersistWriteThread {\n  public:\n    PersistWriteThread();\n    void Write(std::string name, std::string value, SocketConnection socket);\n\n  private:\n    void Work();\n\n  private:\n    std::thread thread_;\n    std::mutex mutex_;\n    std::condition_variable cv_;\n    std::deque<std::tuple<std::string, std::string, SocketConnection>> work_;\n};\n\nstatic std::unique_ptr<PersistWriteThread> persist_write_thread;\n\nstatic std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value,\n                                           SocketConnection* socket, std::string* error) {\n    size_t valuelen = value.size();\n\n    if (!IsLegalPropertyName(name)) {\n        *error = \"Illegal property name\";\n        return {PROP_ERROR_INVALID_NAME};\n    }\n\n    if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {\n        *error = result.error().message();\n        return {PROP_ERROR_INVALID_VALUE};\n    }\n\n    if (name == \"sys.powerctl\") {\n        // No action here - NotifyPropertyChange will trigger the appropriate action, and since this\n        // can come to the second thread, we mustn't call out to the __system_property_* functions\n        // which support multiple readers but only one mutator.\n    } else {\n        prop_info* pi = (prop_info*)__system_property_find(name.c_str());\n        if (pi != nullptr) {\n            // ro.* properties are actually \"write-once\".\n            if (StartsWith(name, \"ro.\")) {\n                *error = \"Read-only property was already set\";\n                return {PROP_ERROR_READ_ONLY_PROPERTY};\n            }\n\n            __system_property_update(pi, value.c_str(), valuelen);\n        } else {\n            int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);\n            if (rc < 0) {\n                *error = \"__system_property_add failed\";\n                return {PROP_ERROR_SET_FAILED};\n            }\n        }\n\n        // Don't write properties to disk until after we have read all default\n        // properties to prevent them from being overwritten by default values.\n        bool need_persist = StartsWith(name, \"persist.\") || StartsWith(name, \"next_boot.\");\n        if (socket && persistent_properties_loaded && need_persist) {\n            if (persist_write_thread) {\n                persist_write_thread->Write(name, value, std::move(*socket));\n                return {};\n            }\n            WritePersistentProperty(name, value);\n        }\n    }\n\n    NotifyPropertyChange(name, value);\n    return {PROP_SUCCESS};\n}\n\n// Helper for PropertySet, for the case where no socket is used, and therefore an asynchronous\n// return is not possible.\nstatic uint32_t PropertySetNoSocket(const std::string& name, const std::string& value,\n                                    std::string* error) {\n    auto ret = PropertySet(name, value, nullptr, error);\n    CHECK(ret.has_value());\n    return *ret;\n}\n\nstatic uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,\n                                   SocketConnection* socket, std::string* error) {\n    auto lock = std::lock_guard{accept_messages_lock};\n    if (!accept_messages) {\n        // If we're already shutting down and you're asking us to stop something,\n        // just say we did (https://issuetracker.google.com/336223505).\n        if (msg == \"stop\") return PROP_SUCCESS;\n\n        *error = \"Received control message after shutdown, ignoring\";\n        return PROP_ERROR_HANDLE_CONTROL_MESSAGE;\n    }\n\n    // We must release the fd before sending it to init, otherwise there will be a race with init.\n    // If init calls close() before Release(), then fdsan will see the wrong tag and abort().\n    int fd = -1;\n    if (socket != nullptr && SelinuxGetVendorAndroidVersion() > __ANDROID_API_Q__) {\n        fd = socket->Release();\n    }\n\n    bool queue_success = QueueControlMessage(msg, name, pid, fd);\n    if (!queue_success && fd != -1) {\n        uint32_t response = PROP_ERROR_HANDLE_CONTROL_MESSAGE;\n        TEMP_FAILURE_RETRY(send(fd, &response, sizeof(response), 0));\n        close(fd);\n    }\n\n    return PROP_SUCCESS;\n}\n\nbool CheckControlPropertyPerms(const std::string& name, const std::string& value,\n                               const std::string& source_context, const ucred& cr) {\n    // We check the legacy method first but these properties are dontaudit, so we only log an audit\n    // if the newer method fails as well.  We only do this with the legacy ctl. properties.\n    if (name == \"ctl.start\" || name == \"ctl.stop\" || name == \"ctl.restart\") {\n        // The legacy permissions model is that ctl. properties have their name ctl.<action> and\n        // their value is the name of the service to apply that action to.  Permissions for these\n        // actions are based on the service, so we must create a fake name of ctl.<service> to\n        // check permissions.\n        auto control_string_legacy = \"ctl.\" + value;\n        const char* target_context_legacy = nullptr;\n        const char* type_legacy = nullptr;\n        property_info_area->GetPropertyInfo(control_string_legacy.c_str(), &target_context_legacy,\n                                            &type_legacy);\n\n        if (CheckMacPerms(control_string_legacy, target_context_legacy, source_context.c_str(), cr)) {\n            return true;\n        }\n    }\n\n    auto control_string_full = name + \"$\" + value;\n    const char* target_context_full = nullptr;\n    const char* type_full = nullptr;\n    property_info_area->GetPropertyInfo(control_string_full.c_str(), &target_context_full,\n                                        &type_full);\n\n    return CheckMacPerms(control_string_full, target_context_full, source_context.c_str(), cr);\n}\n\n// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.\nuint32_t CheckPermissions(const std::string& name, const std::string& value,\n                          const std::string& source_context, const ucred& cr, std::string* error) {\n    if (!IsLegalPropertyName(name)) {\n        *error = \"Illegal property name\";\n        return PROP_ERROR_INVALID_NAME;\n    }\n\n    if (StartsWith(name, \"ctl.\")) {\n        if (!CheckControlPropertyPerms(name, value, source_context, cr)) {\n            *error = StringPrintf(\"Invalid permissions to perform '%s' on '%s'\", name.c_str() + 4,\n                                  value.c_str());\n            return PROP_ERROR_HANDLE_CONTROL_MESSAGE;\n        }\n\n        return PROP_SUCCESS;\n    }\n\n    const char* target_context = nullptr;\n    const char* type = nullptr;\n    property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);\n\n    if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {\n        *error = \"SELinux permission check failed\";\n        return PROP_ERROR_PERMISSION_DENIED;\n    }\n\n    if (!CheckType(type, value)) {\n        *error = StringPrintf(\"Property type check failed, value doesn't match expected type '%s'\",\n                              (type ?: \"(null)\"));\n        return PROP_ERROR_INVALID_VALUE;\n    }\n\n    return PROP_SUCCESS;\n}\n\n// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*, or std::nullopt\n// if asynchronous.\nstd::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value,\n                                          const std::string& source_context, const ucred& cr,\n                                          SocketConnection* socket, std::string* error) {\n    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {\n        return {ret};\n    }\n\n    if (StartsWith(name, \"ctl.\")) {\n        return {SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error)};\n    }\n\n    // sys.powerctl is a special property that is used to make the device reboot.  We want to log\n    // any process that sets this property to be able to accurately blame the cause of a shutdown.\n    if (name == \"sys.powerctl\") {\n        std::string cmdline_path = StringPrintf(\"proc/%d/cmdline\", cr.pid);\n        std::string process_cmdline;\n        std::string process_log_string;\n        if (ReadFileToString(cmdline_path, &process_cmdline)) {\n            // Since cmdline is null deliminated, .c_str() conveniently gives us just the process\n            // path.\n            process_log_string = StringPrintf(\" (%s)\", process_cmdline.c_str());\n        }\n        LOG(INFO) << \"Received sys.powerctl='\" << value << \"' from pid: \" << cr.pid\n                  << process_log_string;\n        if (value == \"reboot,userspace\") {\n            *error = \"Userspace reboot is deprecated.\";\n            return {PROP_ERROR_INVALID_VALUE};\n        }\n    }\n\n    // If a process other than init is writing a non-empty value, it means that process is\n    // requesting that init performs a restorecon operation on the path specified by 'value'.\n    // We use a thread to do this restorecon operation to prevent holding up init, as it may take\n    // a long time to complete.\n    if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {\n        static AsyncRestorecon async_restorecon;\n        async_restorecon.TriggerRestorecon(value);\n        return {PROP_SUCCESS};\n    }\n\n    return PropertySet(name, value, socket, error);\n}\n\n// Helper for HandlePropertySet, for the case where no socket is used, and\n// therefore an asynchronous return is not possible.\nuint32_t HandlePropertySetNoSocket(const std::string& name, const std::string& value,\n                                   const std::string& source_context, const ucred& cr,\n                                   std::string* error) {\n    auto ret = HandlePropertySet(name, value, source_context, cr, nullptr, error);\n    CHECK(ret.has_value());\n    return *ret;\n}\n\nstatic void handle_property_set_fd(int fd) {\n    static constexpr uint32_t kDefaultSocketTimeout = 5000; /* ms */\n\n    int s = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC);\n    if (s == -1) {\n        return;\n    }\n\n    ucred cr;\n    socklen_t cr_size = sizeof(cr);\n    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {\n        close(s);\n        PLOG(ERROR) << \"sys_prop: unable to get SO_PEERCRED\";\n        return;\n    }\n\n    SocketConnection socket(s, cr);\n    uint32_t timeout_ms = kDefaultSocketTimeout;\n\n    uint32_t cmd = 0;\n    if (!socket.RecvUint32(&cmd, &timeout_ms)) {\n        PLOG(ERROR) << \"sys_prop: error while reading command from the socket\";\n        socket.SendUint32(PROP_ERROR_READ_CMD);\n        return;\n    }\n\n    switch (cmd) {\n    case PROP_MSG_SETPROP: {\n        char prop_name[PROP_NAME_MAX];\n        char prop_value[PROP_VALUE_MAX];\n\n        if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||\n            !socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {\n          PLOG(ERROR) << \"sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket\";\n          return;\n        }\n\n        prop_name[PROP_NAME_MAX-1] = 0;\n        prop_value[PROP_VALUE_MAX-1] = 0;\n\n        std::string source_context;\n        if (!socket.GetSourceContext(&source_context)) {\n            PLOG(ERROR) << \"Unable to set property '\" << prop_name << \"': getpeercon() failed\";\n            return;\n        }\n\n        const auto& cr = socket.cred();\n        std::string error;\n        auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error);\n        if (result != PROP_SUCCESS) {\n            LOG(ERROR) << \"Unable to set property '\" << prop_name << \"' from uid:\" << cr.uid\n                       << \" gid:\" << cr.gid << \" pid:\" << cr.pid << \": \" << error;\n        }\n\n        break;\n      }\n\n    case PROP_MSG_SETPROP2: {\n        std::string name;\n        std::string value;\n        if (!socket.RecvString(&name, &timeout_ms) ||\n            !socket.RecvString(&value, &timeout_ms)) {\n          PLOG(ERROR) << \"sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket\";\n          socket.SendUint32(PROP_ERROR_READ_DATA);\n          return;\n        }\n\n        std::string source_context;\n        if (!socket.GetSourceContext(&source_context)) {\n            PLOG(ERROR) << \"Unable to set property '\" << name << \"': getpeercon() failed\";\n            socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);\n            return;\n        }\n\n        // HandlePropertySet takes ownership of the socket if the set is handled asynchronously.\n        const auto& cr = socket.cred();\n        std::string error;\n        auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);\n        if (!result) {\n            // Result will be sent after completion.\n            return;\n        }\n        if (*result != PROP_SUCCESS) {\n            LOG(ERROR) << \"Unable to set property '\" << name << \"' from uid:\" << cr.uid\n                       << \" gid:\" << cr.gid << \" pid:\" << cr.pid << \": \" << error;\n        }\n        socket.SendUint32(*result);\n        break;\n      }\n\n    default:\n        LOG(ERROR) << \"sys_prop: invalid command \" << cmd;\n        socket.SendUint32(PROP_ERROR_INVALID_CMD);\n        break;\n    }\n}\n\nuint32_t InitPropertySet(const std::string& name, const std::string& value) {\n    ucred cr = {.pid = 1, .uid = 0, .gid = 0};\n    std::string error;\n    auto result = HandlePropertySetNoSocket(name, value, kInitContext, cr, &error);\n    if (result != PROP_SUCCESS) {\n        LOG(ERROR) << \"Init cannot set '\" << name << \"' to '\" << value << \"': \" << error;\n    }\n\n    return result;\n}\n\nstatic Result<void> load_properties_from_file(const char*, const char*,\n                                              std::map<std::string, std::string>*);\n\n/*\n * Filter is used to decide which properties to load: NULL loads all keys,\n * \"ro.foo.*\" is a prefix match, and \"ro.foo.bar\" is an exact match.\n */\nstatic void LoadProperties(char* data, const char* filter, const char* filename,\n                           std::map<std::string, std::string>* properties) {\n    char *key, *value, *eol, *sol, *tmp, *fn;\n    size_t flen = 0;\n\n    static constexpr const char* const kVendorPathPrefixes[4] = {\n            \"/vendor\",\n            \"/odm\",\n            \"/vendor_dlkm\",\n            \"/odm_dlkm\",\n    };\n\n    const char* context = kInitContext;\n    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {\n        for (const auto& vendor_path_prefix : kVendorPathPrefixes) {\n            if (StartsWith(filename, vendor_path_prefix)) {\n                context = kVendorContext;\n            }\n        }\n    }\n\n    if (filter) {\n        flen = strlen(filter);\n    }\n\n    sol = data;\n    while ((eol = strchr(sol, '\\n'))) {\n        key = sol;\n        *eol++ = 0;\n        sol = eol;\n\n        while (isspace(*key)) key++;\n        if (*key == '#') continue;\n\n        tmp = eol - 2;\n        while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;\n\n        if (!strncmp(key, \"import \", 7) && flen == 0) {\n            fn = key + 7;\n            while (isspace(*fn)) fn++;\n\n            key = strchr(fn, ' ');\n            if (key) {\n                *key++ = 0;\n                while (isspace(*key)) key++;\n            }\n\n            std::string raw_filename(fn);\n            auto expanded_filename = ExpandProps(raw_filename);\n\n            if (!expanded_filename.ok()) {\n                LOG(ERROR) << \"Could not expand filename ': \" << expanded_filename.error();\n                continue;\n            }\n\n            if (auto res = load_properties_from_file(expanded_filename->c_str(), key, properties);\n                !res.ok()) {\n                LOG(WARNING) << res.error();\n            }\n        } else {\n            value = strchr(key, '=');\n            if (!value) continue;\n            *value++ = 0;\n\n            tmp = value - 2;\n            while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;\n\n            while (isspace(*value)) value++;\n\n            if (flen > 0) {\n                if (filter[flen - 1] == '*') {\n                    if (strncmp(key, filter, flen - 1) != 0) continue;\n                } else {\n                    if (strcmp(key, filter) != 0) continue;\n                }\n            }\n\n            if (StartsWith(key, \"ctl.\") || key == \"sys.powerctl\"s ||\n                std::string{key} == kRestoreconProperty) {\n                LOG(ERROR) << \"Ignoring disallowed property '\" << key\n                           << \"' with special meaning in prop file '\" << filename << \"'\";\n                continue;\n            }\n\n            ucred cr = {.pid = 1, .uid = 0, .gid = 0};\n            std::string error;\n            if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {\n                auto it = properties->find(key);\n                if (it == properties->end()) {\n                    (*properties)[key] = value;\n                } else if (it->second != value) {\n                    LOG(WARNING) << \"Overriding previous property '\" << key << \"':'\" << it->second\n                                 << \"' with new value '\" << value << \"'\";\n                    it->second = value;\n                }\n            } else {\n                LOG(ERROR) << \"Do not have permissions to set '\" << key << \"' to '\" << value\n                           << \"' in property file '\" << filename << \"': \" << error;\n            }\n        }\n    }\n}\n\n// Filter is used to decide which properties to load: NULL loads all keys,\n// \"ro.foo.*\" is a prefix match, and \"ro.foo.bar\" is an exact match.\nstatic Result<void> load_properties_from_file(const char* filename, const char* filter,\n                                              std::map<std::string, std::string>* properties) {\n    Timer t;\n    auto file_contents = ReadFile(filename);\n    if (!file_contents.ok()) {\n        return Error() << \"Couldn't load property file '\" << filename\n                       << \"': \" << file_contents.error();\n    }\n    file_contents->push_back('\\n');\n\n    LoadProperties(file_contents->data(), filter, filename, properties);\n    LOG(VERBOSE) << \"(Loading properties from \" << filename << \" took \" << t << \".)\";\n    return {};\n}\n\nstatic void LoadPropertiesFromSecondStageRes(std::map<std::string, std::string>* properties) {\n    std::string prop = GetRamdiskPropForSecondStage();\n    if (access(prop.c_str(), R_OK) != 0) {\n        CHECK(errno == ENOENT) << \"Cannot access \" << prop << \": \" << strerror(errno);\n        return;\n    }\n    if (auto res = load_properties_from_file(prop.c_str(), nullptr, properties); !res.ok()) {\n        LOG(WARNING) << res.error();\n    }\n}\n\n// persist.sys.usb.config values can't be combined on build-time when property\n// files are split into each partition.\n// So we need to apply the same rule of build/make/tools/post_process_props.py\n// on runtime.\nstatic void update_sys_usb_config() {\n    bool is_debuggable = android::base::GetBoolProperty(\"ro.debuggable\", false);\n    std::string config = android::base::GetProperty(\"persist.sys.usb.config\", \"\");\n    // b/150130503, add (config == \"none\") condition here to prevent appending\n    // \",adb\" if \"none\" is explicitly defined in default prop.\n    if (config.empty() || config == \"none\") {\n        InitPropertySet(\"persist.sys.usb.config\", is_debuggable ? \"adb\" : \"none\");\n    } else if (is_debuggable && config.find(\"adb\") == std::string::npos &&\n               config.length() + 4 < PROP_VALUE_MAX) {\n        config.append(\",adb\");\n        InitPropertySet(\"persist.sys.usb.config\", config);\n    }\n}\n\nstatic void load_override_properties() {\n    if (ALLOW_LOCAL_PROP_OVERRIDE) {\n        std::map<std::string, std::string> properties;\n        load_properties_from_file(\"/data/local.prop\", nullptr, &properties);\n        for (const auto& [name, value] : properties) {\n            std::string error;\n            if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) {\n                LOG(ERROR) << \"Could not set '\" << name << \"' to '\" << value\n                           << \"' in /data/local.prop: \" << error;\n            }\n        }\n    }\n}\n\n// If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly\n// set, derive them from ro.product.${partition}.* properties\nstatic void property_initialize_ro_product_props() {\n    const char* RO_PRODUCT_PROPS_PREFIX = \"ro.product.\";\n    const char* RO_PRODUCT_PROPS[] = {\n            \"brand\", \"device\", \"manufacturer\", \"model\", \"name\",\n    };\n    const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {\n            \"odm\", \"product\", \"system_ext\", \"system\", \"vendor\",\n    };\n    const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = \"product,odm,vendor,system_ext,system\";\n    const std::string EMPTY = \"\";\n\n    std::string ro_product_props_source_order =\n            GetProperty(\"ro.product.property_source_order\", EMPTY);\n\n    if (!ro_product_props_source_order.empty()) {\n        // Verify that all specified sources are valid\n        for (const auto& source : Split(ro_product_props_source_order, \",\")) {\n            // Verify that the specified source is valid\n            bool is_allowed_source = false;\n            for (const auto& allowed_source : RO_PRODUCT_PROPS_ALLOWED_SOURCES) {\n                if (source == allowed_source) {\n                    is_allowed_source = true;\n                    break;\n                }\n            }\n            if (!is_allowed_source) {\n                LOG(ERROR) << \"Found unexpected source in ro.product.property_source_order; \"\n                              \"using the default property source order\";\n                ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;\n                break;\n            }\n        }\n    } else {\n        ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;\n    }\n\n    for (const auto& ro_product_prop : RO_PRODUCT_PROPS) {\n        std::string base_prop(RO_PRODUCT_PROPS_PREFIX);\n        base_prop += ro_product_prop;\n\n        std::string base_prop_val = GetProperty(base_prop, EMPTY);\n        if (!base_prop_val.empty()) {\n            continue;\n        }\n\n        for (const auto& source : Split(ro_product_props_source_order, \",\")) {\n            std::string target_prop(RO_PRODUCT_PROPS_PREFIX);\n            target_prop += source;\n            target_prop += '.';\n            target_prop += ro_product_prop;\n\n            std::string target_prop_val = GetProperty(target_prop, EMPTY);\n            if (!target_prop_val.empty()) {\n                LOG(INFO) << \"Setting product property \" << base_prop << \" to '\" << target_prop_val\n                          << \"' (from \" << target_prop << \")\";\n                std::string error;\n                auto res = PropertySetNoSocket(base_prop, target_prop_val, &error);\n                if (res != PROP_SUCCESS) {\n                    LOG(ERROR) << \"Error setting product property \" << base_prop << \": err=\" << res\n                               << \" (\" << error << \")\";\n                }\n                break;\n            }\n        }\n    }\n}\n\nstatic void property_initialize_build_id() {\n    std::string build_id = GetProperty(ID_PROP, \"\");\n    if (!build_id.empty()) {\n        return;\n    }\n\n    std::string legacy_build_id = GetProperty(LEGACY_ID_PROP, \"\");\n    std::string vbmeta_digest = GetProperty(VBMETA_DIGEST_PROP, \"\");\n    if (vbmeta_digest.size() < DIGEST_SIZE_USED) {\n        LOG(ERROR) << \"vbmeta digest size too small \" << vbmeta_digest;\n        // Still try to set the id field in the unexpected case.\n        build_id = legacy_build_id;\n    } else {\n        // Derive the ro.build.id by appending the vbmeta digest to the base value.\n        build_id = legacy_build_id + \".\" + vbmeta_digest.substr(0, DIGEST_SIZE_USED);\n    }\n\n    std::string error;\n    auto res = PropertySetNoSocket(ID_PROP, build_id, &error);\n    if (res != PROP_SUCCESS) {\n        LOG(ERROR) << \"Failed to set \" << ID_PROP << \" to \" << build_id;\n    }\n}\n\nstatic std::string ConstructBuildFingerprint(bool legacy) {\n    const std::string UNKNOWN = \"unknown\";\n    std::string build_fingerprint = GetProperty(\"ro.product.brand\", UNKNOWN);\n    build_fingerprint += '/';\n    build_fingerprint += GetProperty(\"ro.product.name\", UNKNOWN);\n\n    // should be set in /product/etc/build.prop\n    // when we have a dev option device, and we've switched the kernel to 16kb mode\n    // we use the same system image, but we've switched out the kernel, so make it\n    // visible at a high level\n    bool has16KbDevOption =\n            android::base::GetBoolProperty(\"ro.product.build.16k_page.enabled\", false);\n    if (has16KbDevOption && getpagesize() == 16384) {\n        build_fingerprint += \"_16kb\";\n    }\n\n    build_fingerprint += '/';\n    build_fingerprint += GetProperty(\"ro.product.device\", UNKNOWN);\n    build_fingerprint += ':';\n    build_fingerprint += GetProperty(\"ro.build.version.release_or_codename\", UNKNOWN);\n    build_fingerprint += '/';\n\n    std::string build_id =\n            legacy ? GetProperty(LEGACY_ID_PROP, UNKNOWN) : GetProperty(ID_PROP, UNKNOWN);\n    build_fingerprint += build_id;\n    build_fingerprint += '/';\n    build_fingerprint += GetProperty(\"ro.build.version.incremental\", UNKNOWN);\n    build_fingerprint += ':';\n    build_fingerprint += GetProperty(\"ro.build.type\", UNKNOWN);\n    build_fingerprint += '/';\n    build_fingerprint += GetProperty(\"ro.build.tags\", UNKNOWN);\n\n    return build_fingerprint;\n}\n\n// Derive the legacy build fingerprint if we overwrite the build id at runtime.\nstatic void property_derive_legacy_build_fingerprint() {\n    std::string legacy_build_fingerprint = GetProperty(LEGACY_FINGERPRINT_PROP, \"\");\n    if (!legacy_build_fingerprint.empty()) {\n        return;\n    }\n\n    // The device doesn't have a legacy build id, skipping the legacy fingerprint.\n    std::string legacy_build_id = GetProperty(LEGACY_ID_PROP, \"\");\n    if (legacy_build_id.empty()) {\n        return;\n    }\n\n    legacy_build_fingerprint = ConstructBuildFingerprint(true /* legacy fingerprint */);\n    LOG(INFO) << \"Setting property '\" << LEGACY_FINGERPRINT_PROP << \"' to '\"\n              << legacy_build_fingerprint << \"'\";\n\n    std::string error;\n    auto res = PropertySetNoSocket(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error);\n    if (res != PROP_SUCCESS) {\n        LOG(ERROR) << \"Error setting property '\" << LEGACY_FINGERPRINT_PROP << \"': err=\" << res\n                   << \" (\" << error << \")\";\n    }\n}\n\n// If the ro.build.fingerprint property has not been set, derive it from constituent pieces\nstatic void property_derive_build_fingerprint() {\n    std::string build_fingerprint = GetProperty(\"ro.build.fingerprint\", \"\");\n    if (!build_fingerprint.empty()) {\n        return;\n    }\n\n    build_fingerprint = ConstructBuildFingerprint(false /* legacy fingerprint */);\n    LOG(INFO) << \"Setting property '\" << FINGERPRINT_PROP << \"' to '\" << build_fingerprint << \"'\";\n\n    std::string error;\n    auto res = PropertySetNoSocket(FINGERPRINT_PROP, build_fingerprint, &error);\n    if (res != PROP_SUCCESS) {\n        LOG(ERROR) << \"Error setting property '\" << FINGERPRINT_PROP << \"': err=\" << res << \" (\"\n                   << error << \")\";\n    }\n}\n\n// If the ro.product.cpu.abilist* properties have not been explicitly\n// set, derive them from ro.${partition}.product.cpu.abilist* properties.\nstatic void property_initialize_ro_cpu_abilist() {\n    // From high to low priority.\n    const char* kAbilistSources[] = {\n            \"product\",\n            \"odm\",\n            \"vendor\",\n            \"system\",\n    };\n    const std::string EMPTY = \"\";\n    const char* kAbilistProp = \"ro.product.cpu.abilist\";\n    const char* kAbilist32Prop = \"ro.product.cpu.abilist32\";\n    const char* kAbilist64Prop = \"ro.product.cpu.abilist64\";\n\n    // If the properties are defined explicitly, just use them.\n    if (GetProperty(kAbilistProp, EMPTY) != EMPTY) {\n        return;\n    }\n\n    // Find the first source defining these properties by order.\n    std::string abilist32_prop_val;\n    std::string abilist64_prop_val;\n    for (const auto& source : kAbilistSources) {\n        const auto abilist32_prop = std::string(\"ro.\") + source + \".product.cpu.abilist32\";\n        const auto abilist64_prop = std::string(\"ro.\") + source + \".product.cpu.abilist64\";\n        abilist32_prop_val = GetProperty(abilist32_prop, EMPTY);\n        abilist64_prop_val = GetProperty(abilist64_prop, EMPTY);\n        // The properties could be empty on 32-bit-only or 64-bit-only devices,\n        // but we cannot identify a property is empty or undefined by GetProperty().\n        // So, we assume both of these 2 properties are empty as undefined.\n        if (abilist32_prop_val != EMPTY || abilist64_prop_val != EMPTY) {\n            break;\n        }\n    }\n\n    // Merge ABI lists for ro.product.cpu.abilist\n    auto abilist_prop_val = abilist64_prop_val;\n    if (abilist32_prop_val != EMPTY) {\n        if (abilist_prop_val != EMPTY) {\n            abilist_prop_val += \",\";\n        }\n        abilist_prop_val += abilist32_prop_val;\n    }\n\n    // Set these properties\n    const std::pair<const char*, const std::string&> set_prop_list[] = {\n            {kAbilistProp, abilist_prop_val},\n            {kAbilist32Prop, abilist32_prop_val},\n            {kAbilist64Prop, abilist64_prop_val},\n    };\n    for (const auto& [prop, prop_val] : set_prop_list) {\n        LOG(INFO) << \"Setting property '\" << prop << \"' to '\" << prop_val << \"'\";\n\n        std::string error;\n        auto res = PropertySetNoSocket(prop, prop_val, &error);\n        if (res != PROP_SUCCESS) {\n            LOG(ERROR) << \"Error setting property '\" << prop << \"': err=\" << res << \" (\" << error\n                       << \")\";\n        }\n    }\n}\n\nstatic void property_initialize_ro_vendor_api_level() {\n    // ro.vendor.api_level shows the api_level that the vendor images (vendor, odm, ...) are\n    // required to support.\n    constexpr auto VENDOR_API_LEVEL_PROP = \"ro.vendor.api_level\";\n\n    if (__system_property_find(VENDOR_API_LEVEL_PROP) != nullptr) {\n        // The device already have ro.vendor.api_level in its vendor/build.prop.\n        // Skip initializing the ro.vendor.api_level property.\n        return;\n    }\n\n    auto vendor_api_level = GetIntProperty(\"ro.board.first_api_level\", __ANDROID_VENDOR_API_MAX__);\n    if (vendor_api_level != __ANDROID_VENDOR_API_MAX__) {\n        // Update the vendor_api_level with \"ro.board.api_level\" only if both \"ro.board.api_level\"\n        // and \"ro.board.first_api_level\" are defined.\n        vendor_api_level = GetIntProperty(\"ro.board.api_level\", vendor_api_level);\n    }\n\n    auto product_first_api_level =\n            GetIntProperty(\"ro.product.first_api_level\", __ANDROID_API_FUTURE__);\n    if (product_first_api_level == __ANDROID_API_FUTURE__) {\n        // Fallback to \"ro.build.version.sdk\" if the \"ro.product.first_api_level\" is not defined.\n        product_first_api_level = GetIntProperty(\"ro.build.version.sdk\", __ANDROID_API_FUTURE__);\n    }\n\n    vendor_api_level =\n            std::min(AVendorSupport_getVendorApiLevelOf(product_first_api_level), vendor_api_level);\n\n    if (vendor_api_level < 0) {\n        LOG(ERROR) << \"Unexpected vendor api level for \" << VENDOR_API_LEVEL_PROP << \". Check \"\n                   << \"ro.product.first_api_level and ro.build.version.sdk.\";\n        vendor_api_level = __ANDROID_VENDOR_API_MAX__;\n    }\n\n    std::string error;\n    auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(vendor_api_level), &error);\n    if (res != PROP_SUCCESS) {\n        LOG(ERROR) << \"Failed to set \" << VENDOR_API_LEVEL_PROP << \" with \" << vendor_api_level\n                   << \": \" << error << \"(\" << res << \")\";\n    }\n}\n\nvoid PropertyLoadBootDefaults() {\n    // We read the properties and their values into a map, in order to always allow properties\n    // loaded in the later property files to override the properties in loaded in the earlier\n    // property files, regardless of if they are \"ro.\" properties or not.\n    std::map<std::string, std::string> properties;\n\n    if (IsRecoveryMode()) {\n        if (auto res = load_properties_from_file(\"/prop.default\", nullptr, &properties);\n            !res.ok()) {\n            LOG(ERROR) << res.error();\n        }\n    }\n\n    // /<part>/etc/build.prop is the canonical location of the build-time properties since S.\n    // Falling back to /<part>/defalt.prop and /<part>/build.prop only when legacy path has to\n    // be supported, which is controlled by the support_legacy_path_until argument.\n    const auto load_properties_from_partition = [&properties](const std::string& partition,\n                                                              int support_legacy_path_until) {\n        auto path = \"/\" + partition + \"/etc/build.prop\";\n        if (load_properties_from_file(path.c_str(), nullptr, &properties).ok()) {\n            return;\n        }\n        // To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a\n        // separate map. Then by comparing its value with legacy_version, we know that if the\n        // partition is old enough so that we need to respect the legacy paths.\n        std::map<std::string, std::string> temp;\n        auto legacy_path1 = \"/\" + partition + \"/default.prop\";\n        auto legacy_path2 = \"/\" + partition + \"/build.prop\";\n        load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);\n        load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);\n        bool support_legacy_path = false;\n        auto version_prop_name = \"ro.\" + partition + \".build.version.sdk\";\n        auto it = temp.find(version_prop_name);\n        if (it == temp.end()) {\n            // This is embarassing. Without the prop, we can't determine how old the partition is.\n            // Let's be conservative by assuming it is very very old.\n            support_legacy_path = true;\n        } else if (int value;\n                   ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {\n            support_legacy_path = true;\n        }\n        if (support_legacy_path) {\n            // We don't update temp into properties directly as it might skip any (future) logic\n            // for resolving duplicates implemented in load_properties_from_file.  Instead, read\n            // the files again into the properties map.\n            load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);\n            load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);\n        } else {\n            LOG(FATAL) << legacy_path1 << \" and \" << legacy_path2 << \" were not loaded \"\n                       << \"because \" << version_prop_name << \"(\" << it->second << \") is newer \"\n                       << \"than \" << support_legacy_path_until;\n        }\n    };\n\n    // Order matters here. The more the partition is specific to a product, the higher its\n    // precedence is.\n    LoadPropertiesFromSecondStageRes(&properties);\n\n    // system should have build.prop, unlike the other partitions\n    if (auto res = load_properties_from_file(\"/system/build.prop\", nullptr, &properties);\n        !res.ok()) {\n        LOG(WARNING) << res.error();\n    }\n\n    load_properties_from_partition(\"system_ext\", /* support_legacy_path_until */ 30);\n    load_properties_from_file(\"/system_dlkm/etc/build.prop\", nullptr, &properties);\n    // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are\n    // all updated.\n    // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {\n    load_properties_from_file(\"/vendor/default.prop\", nullptr, &properties);\n    // }\n    load_properties_from_file(\"/vendor/build.prop\", nullptr, &properties);\n    load_properties_from_file(\"/vendor_dlkm/etc/build.prop\", nullptr, &properties);\n    load_properties_from_file(\"/odm_dlkm/etc/build.prop\", nullptr, &properties);\n    load_properties_from_partition(\"odm\", /* support_legacy_path_until */ 28);\n    load_properties_from_partition(\"product\", /* support_legacy_path_until */ 30);\n\n    if (access(kDebugRamdiskProp, R_OK) == 0) {\n        LOG(INFO) << \"Loading \" << kDebugRamdiskProp;\n        if (auto res = load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);\n            !res.ok()) {\n            LOG(WARNING) << res.error();\n        }\n    }\n\n    for (const auto& [name, value] : properties) {\n        std::string error;\n        if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) {\n            LOG(ERROR) << \"Could not set '\" << name << \"' to '\" << value\n                       << \"' while loading .prop files\" << error;\n        }\n    }\n\n    property_initialize_ro_product_props();\n    property_initialize_build_id();\n    property_derive_build_fingerprint();\n    property_derive_legacy_build_fingerprint();\n    property_initialize_ro_cpu_abilist();\n    property_initialize_ro_vendor_api_level();\n\n    update_sys_usb_config();\n}\n\nvoid PropertyLoadDerivedDefaults() {\n    const char* PAGE_PROP = \"ro.boot.hardware.cpu.pagesize\";\n    if (GetProperty(PAGE_PROP, \"\").empty()) {\n        std::string error;\n        if (PropertySetNoSocket(PAGE_PROP, std::to_string(getpagesize()), &error) != PROP_SUCCESS) {\n            LOG(ERROR) << \"Could not set '\" << PAGE_PROP << \"' because: \" << error;\n        }\n    }\n}\n\nbool LoadPropertyInfoFromFile(const std::string& filename,\n                              std::vector<PropertyInfoEntry>* property_infos) {\n    auto file_contents = std::string();\n    if (!ReadFileToString(filename, &file_contents)) {\n        PLOG(ERROR) << \"Could not read properties from '\" << filename << \"'\";\n        return false;\n    }\n\n    auto errors = std::vector<std::string>{};\n    bool require_prefix_or_exact = SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__;\n    ParsePropertyInfoFile(file_contents, require_prefix_or_exact, property_infos, &errors);\n    // Individual parsing errors are reported but do not cause a failed boot, which is what\n    // returning false would do here.\n    for (const auto& error : errors) {\n        LOG(ERROR) << \"Could not read line from '\" << filename << \"': \" << error;\n    }\n\n    return true;\n}\n\nvoid CreateSerializedPropertyInfo() {\n    auto property_infos = std::vector<PropertyInfoEntry>();\n    if (access(\"/system/etc/selinux/plat_property_contexts\", R_OK) != -1) {\n        if (!LoadPropertyInfoFromFile(\"/system/etc/selinux/plat_property_contexts\",\n                                      &property_infos)) {\n            return;\n        }\n        // Don't check for failure here, since we don't always have all of these partitions.\n        // E.g. In case of recovery, the vendor partition will not have mounted and we\n        // still need the system / platform properties to function.\n        if (access(\"/system_ext/etc/selinux/system_ext_property_contexts\", R_OK) != -1) {\n            LoadPropertyInfoFromFile(\"/system_ext/etc/selinux/system_ext_property_contexts\",\n                                     &property_infos);\n        }\n        if (access(\"/vendor/etc/selinux/vendor_property_contexts\", R_OK) != -1) {\n            LoadPropertyInfoFromFile(\"/vendor/etc/selinux/vendor_property_contexts\",\n                                     &property_infos);\n        }\n        if (access(\"/product/etc/selinux/product_property_contexts\", R_OK) != -1) {\n            LoadPropertyInfoFromFile(\"/product/etc/selinux/product_property_contexts\",\n                                     &property_infos);\n        }\n        if (access(\"/odm/etc/selinux/odm_property_contexts\", R_OK) != -1) {\n            LoadPropertyInfoFromFile(\"/odm/etc/selinux/odm_property_contexts\", &property_infos);\n        }\n    } else {\n        if (!LoadPropertyInfoFromFile(\"/plat_property_contexts\", &property_infos)) {\n            return;\n        }\n        LoadPropertyInfoFromFile(\"/system_ext_property_contexts\", &property_infos);\n        LoadPropertyInfoFromFile(\"/vendor_property_contexts\", &property_infos);\n        LoadPropertyInfoFromFile(\"/product_property_contexts\", &property_infos);\n        LoadPropertyInfoFromFile(\"/odm_property_contexts\", &property_infos);\n    }\n\n    auto serialized_contexts = std::string();\n    auto error = std::string();\n    if (!BuildTrie(property_infos, \"u:object_r:default_prop:s0\", \"string\", &serialized_contexts,\n                   &error)) {\n        LOG(ERROR) << \"Unable to serialize property contexts: \" << error;\n        return;\n    }\n\n    if (!WriteStringToFile(serialized_contexts, PROP_TREE_FILE, 0444, 0, 0, false)) {\n        PLOG(ERROR) << \"Unable to write serialized property infos to file\";\n    }\n    selinux_android_restorecon(PROP_TREE_FILE, 0);\n\n#ifdef WRITE_APPCOMPAT_OVERRIDE_SYSTEM_PROPERTIES\n    mkdir(APPCOMPAT_OVERRIDE_PROP_FOLDERNAME, S_IRWXU | S_IXGRP | S_IXOTH);\n    if (!WriteStringToFile(serialized_contexts, APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0444, 0, 0,\n                           false)) {\n        PLOG(ERROR) << \"Unable to write appcompat override property infos to file\";\n    }\n    selinux_android_restorecon(APPCOMPAT_OVERRIDE_PROP_TREE_FILE, 0);\n#endif\n}\n\nstatic void ExportKernelBootProps() {\n    constexpr const char* UNSET = \"\";\n    struct {\n        const char* src_prop;\n        const char* dst_prop;\n        const char* default_value;\n    } prop_map[] = {\n            // clang-format off\n        { \"ro.boot.serialno\",   \"ro.serialno\",   UNSET, },\n        { \"ro.boot.mode\",       \"ro.bootmode\",   \"unknown\", },\n        { \"ro.boot.baseband\",   \"ro.baseband\",   \"unknown\", },\n        { \"ro.boot.bootloader\", \"ro.bootloader\", \"unknown\", },\n        { \"ro.boot.hardware\",   \"ro.hardware\",   \"unknown\", },\n        { \"ro.boot.revision\",   \"ro.revision\",   \"0\", },\n            // clang-format on\n    };\n    for (const auto& prop : prop_map) {\n        std::string value = GetProperty(prop.src_prop, prop.default_value);\n        if (value != UNSET) InitPropertySet(prop.dst_prop, value);\n    }\n}\n\nstatic void ProcessKernelDt() {\n    if (!is_android_dt_value_expected(\"compatible\", \"android,firmware\")) {\n        return;\n    }\n\n    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(android::fs_mgr::GetAndroidDtDir().c_str()),\n                                            closedir);\n    if (!dir) return;\n\n    std::string dt_file;\n    struct dirent* dp;\n    while ((dp = readdir(dir.get())) != NULL) {\n        if (dp->d_type != DT_REG || !strcmp(dp->d_name, \"compatible\") ||\n            !strcmp(dp->d_name, \"name\")) {\n            continue;\n        }\n\n        std::string file_name = android::fs_mgr::GetAndroidDtDir() + dp->d_name;\n\n        android::base::ReadFileToString(file_name, &dt_file);\n        std::replace(dt_file.begin(), dt_file.end(), ',', '.');\n\n        InitPropertySet(\"ro.boot.\"s + dp->d_name, dt_file);\n    }\n}\n\nconstexpr auto ANDROIDBOOT_PREFIX = \"androidboot.\"sv;\n\nstatic void ProcessKernelCmdline() {\n    android::fs_mgr::ImportKernelCmdline([&](const std::string& key, const std::string& value) {\n        if (StartsWith(key, ANDROIDBOOT_PREFIX)) {\n            InitPropertySet(\"ro.boot.\" + key.substr(ANDROIDBOOT_PREFIX.size()), value);\n        }\n    });\n}\n\n\nstatic void ProcessBootconfig() {\n    android::fs_mgr::ImportBootconfig([&](const std::string& key, const std::string& value) {\n        if (StartsWith(key, ANDROIDBOOT_PREFIX)) {\n            InitPropertySet(\"ro.boot.\" + key.substr(ANDROIDBOOT_PREFIX.size()), value);\n        }\n    });\n}\n\nvoid PropertyInit() {\n    selinux_callback cb;\n    cb.func_audit = PropertyAuditCallback;\n    selinux_set_callback(SELINUX_CB_AUDIT, cb);\n\n    mkdir(\"/dev/__properties__\", S_IRWXU | S_IXGRP | S_IXOTH);\n    CreateSerializedPropertyInfo();\n    if (__system_property_area_init()) {\n        LOG(FATAL) << \"Failed to initialize property area\";\n    }\n    if (!property_info_area.LoadDefaultPath()) {\n        LOG(FATAL) << \"Failed to load serialized property info file\";\n    }\n\n    // If arguments are passed both on the command line and in DT,\n    // properties set in DT always have priority over the command-line ones.\n    ProcessKernelDt();\n    ProcessKernelCmdline();\n    ProcessBootconfig();\n\n    // Propagate the kernel variables to internal variables\n    // used by init as well as the current required properties.\n    ExportKernelBootProps();\n\n    PropertyLoadBootDefaults();\n    PropertyLoadDerivedDefaults();\n}\n\nstatic void HandleInitSocket() {\n    auto message = ReadMessage(init_socket);\n    if (!message.ok()) {\n        LOG(ERROR) << \"Could not read message from init_dedicated_recv_socket: \" << message.error();\n        return;\n    }\n\n    auto init_message = InitMessage{};\n    if (!init_message.ParseFromString(*message)) {\n        LOG(ERROR) << \"Could not parse message from init\";\n        return;\n    }\n\n    switch (init_message.msg_case()) {\n        case InitMessage::kLoadPersistentProperties: {\n            load_override_properties();\n\n            auto persistent_properties = LoadPersistentProperties();\n            for (const auto& property_record : persistent_properties.properties()) {\n                auto const& prop_name = property_record.name();\n                auto const& prop_value = property_record.value();\n                InitPropertySet(prop_name, prop_value);\n            }\n\n            // Apply debug ramdisk special settings after persistent properties are loaded.\n            if (android::base::GetBoolProperty(\"ro.force.debuggable\", false)) {\n                // Always enable usb adb if device is booted with debug ramdisk.\n                update_sys_usb_config();\n            }\n            InitPropertySet(\"ro.persistent_properties.ready\", \"true\");\n            persistent_properties_loaded = true;\n            break;\n        }\n        default:\n            LOG(ERROR) << \"Unknown message type from init: \" << init_message.msg_case();\n    }\n}\n\nstatic void PropertyServiceThread(int fd, bool listen_init) {\n    Epoll epoll;\n    if (auto result = epoll.Open(); !result.ok()) {\n        LOG(FATAL) << result.error();\n    }\n\n    if (auto result = epoll.RegisterHandler(fd, std::bind(handle_property_set_fd, fd));\n        !result.ok()) {\n        LOG(FATAL) << result.error();\n    }\n\n    if (listen_init) {\n        if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {\n            LOG(FATAL) << result.error();\n        }\n    }\n\n    while (true) {\n        auto epoll_result = epoll.Wait(std::nullopt);\n        if (!epoll_result.ok()) {\n            LOG(ERROR) << epoll_result.error();\n        }\n    }\n}\n\nPersistWriteThread::PersistWriteThread() {\n    auto new_thread = std::thread([this]() -> void { Work(); });\n    thread_.swap(new_thread);\n}\n\nvoid PersistWriteThread::Work() {\n    while (true) {\n        std::tuple<std::string, std::string, SocketConnection> item;\n\n        // Grab the next item within the lock.\n        {\n            std::unique_lock<std::mutex> lock(mutex_);\n\n            while (work_.empty()) {\n                cv_.wait(lock);\n            }\n\n            item = std::move(work_.front());\n            work_.pop_front();\n        }\n\n        // Perform write/fsync outside the lock.\n        WritePersistentProperty(std::get<0>(item), std::get<1>(item));\n        NotifyPropertyChange(std::get<0>(item), std::get<1>(item));\n\n        SocketConnection& socket = std::get<2>(item);\n        socket.SendUint32(PROP_SUCCESS);\n    }\n}\n\nvoid PersistWriteThread::Write(std::string name, std::string value, SocketConnection socket) {\n    {\n        std::unique_lock<std::mutex> lock(mutex_);\n        work_.emplace_back(std::move(name), std::move(value), std::move(socket));\n    }\n    cv_.notify_all();\n}\n\nvoid StartThread(const char* name, int mode, int gid, std::thread& t, bool listen_init) {\n    int fd = -1;\n    if (auto result = CreateSocket(name, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,\n                                   /*passcred=*/false, /*should_listen=*/false, mode, /*uid=*/0,\n                                   /*gid=*/gid, /*socketcon=*/{});\n        result.ok()) {\n        fd = *result;\n    } else {\n        LOG(FATAL) << \"start_property_service socket creation failed: \" << result.error();\n    }\n\n    listen(fd, 8);\n\n    auto new_thread = std::thread(PropertyServiceThread, fd, listen_init);\n    t.swap(new_thread);\n}\n\nvoid StartPropertyService(int* epoll_socket) {\n    InitPropertySet(\"ro.property_service.version\", \"2\");\n\n    int sockets[2];\n    if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {\n        PLOG(FATAL) << \"Failed to socketpair() between property_service and init\";\n    }\n    *epoll_socket = from_init_socket = sockets[0];\n    init_socket = sockets[1];\n    StartSendingMessages();\n\n    StartThread(PROP_SERVICE_FOR_SYSTEM_NAME, 0660, AID_SYSTEM, property_service_for_system_thread,\n                true);\n    StartThread(PROP_SERVICE_NAME, 0666, 0, property_service_thread, false);\n\n    auto async_persist_writes =\n            android::base::GetBoolProperty(\"ro.property_service.async_persist_writes\", false);\n\n    if (async_persist_writes) {\n        persist_write_thread = std::make_unique<PersistWriteThread>();\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/property_service.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/socket.h>\n\n#include <condition_variable>\n#include <deque>\n#include <mutex>\n#include <string>\n#include <thread>\n\n#include \"epoll.h\"\n\nnamespace android {\nnamespace init {\n\nstatic constexpr const char kRestoreconProperty[] = \"selinux.restorecon_recursive\";\n\nbool CanReadProperty(const std::string& source_context, const std::string& name);\n\nvoid PropertyInit();\nvoid StartPropertyService(int* epoll_socket);\n\nvoid StartSendingMessages();\nvoid StopSendingMessages();\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/property_service.proto",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nsyntax = \"proto2\";\noption optimize_for = LITE_RUNTIME;\n\nmessage PropertyMessage {\n    message ControlMessage {\n        optional string msg = 1;\n        optional string name = 2;\n        optional int32 pid = 3;\n        optional int32 fd = 4;\n    }\n\n    message ChangedMessage {\n        optional string name = 1;\n        optional string value = 2;\n    }\n\n    oneof msg {\n        ControlMessage control_message = 1;\n        ChangedMessage changed_message = 2;\n    };\n}\n\nmessage InitMessage {\n    oneof msg {\n        bool load_persistent_properties = 1;\n        bool stop_sending_messages = 2;\n        bool start_sending_messages = 3;\n    };\n}\n"
  },
  {
    "path": "init/property_service_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <sys/socket.h>\n#include <sys/system_properties.h>\n#include <sys/un.h>\n\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <gtest/gtest.h>\n\nusing android::base::GetProperty;\nusing android::base::SetProperty;\n\nnamespace android {\nnamespace init {\n\nTEST(property_service, very_long_name_35166374) {\n  // Connect to the property service directly...\n  int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);\n  ASSERT_NE(fd, -1);\n\n  static const char* property_service_socket = \"/dev/socket/\" PROP_SERVICE_NAME;\n  sockaddr_un addr = {};\n  addr.sun_family = AF_LOCAL;\n  strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));\n\n  socklen_t addr_len = strlen(property_service_socket) + offsetof(sockaddr_un, sun_path) + 1;\n  ASSERT_NE(connect(fd, reinterpret_cast<sockaddr*>(&addr), addr_len), -1);\n\n  // ...so we can send it a malformed request.\n  uint32_t msg = PROP_MSG_SETPROP2;\n  uint32_t size = 0xffffffff;\n\n  ASSERT_EQ(static_cast<ssize_t>(sizeof(msg)), send(fd, &msg, sizeof(msg), 0));\n  ASSERT_EQ(static_cast<ssize_t>(sizeof(size)), send(fd, &size, sizeof(size), 0));\n  uint32_t result = 0;\n  ASSERT_EQ(static_cast<ssize_t>(sizeof(result)),\n            TEMP_FAILURE_RETRY(recv(fd, &result, sizeof(result), MSG_WAITALL)));\n  EXPECT_EQ(static_cast<uint32_t>(PROP_ERROR_READ_DATA), result);\n  ASSERT_EQ(0, close(fd));\n}\n\nTEST(property_service, non_utf8_value) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n\n    ASSERT_TRUE(SetProperty(\"property_service_utf8_test\", \"base_success\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\x80\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\xC2\\x01\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\xE0\\xFF\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\xE0\\xA0\\xFF\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\xF0\\x01\\xFF\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\xF0\\x90\\xFF\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\xF0\\x90\\x80\\xFF\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"\\xF0\\x90\\x80\"));\n    EXPECT_FALSE(SetProperty(\"property_service_utf8_test\", \"ab\\xF0\\x90\\x80\\x80qe\\xF0\\x90\\x80\"));\n    EXPECT_TRUE(SetProperty(\"property_service_utf8_test\", \"\\xF0\\x90\\x80\\x80\"));\n}\n\nTEST(property_service, userspace_reboot_not_supported) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n    EXPECT_FALSE(SetProperty(\"sys.powerctl\", \"reboot,userspace\"));\n}\n\nTEST(property_service, check_fingerprint_with_legacy_build_id) {\n    std::string legacy_build_id = GetProperty(\"ro.build.legacy.id\", \"\");\n    if (legacy_build_id.empty()) {\n        GTEST_SKIP() << \"Skipping test, legacy build id isn't set.\";\n    }\n\n    std::string vbmeta_digest = GetProperty(\"ro.boot.vbmeta.digest\", \"\");\n    ASSERT_GE(vbmeta_digest.size(), 8u);\n    std::string build_id = GetProperty(\"ro.build.id\", \"\");\n    // Check that the build id is constructed with the prefix of vbmeta digest\n    std::string expected_build_id = legacy_build_id + \".\" + vbmeta_digest.substr(0, 8);\n    ASSERT_EQ(expected_build_id, build_id);\n    // Check that the fingerprint is constructed with the expected format.\n    std::string fingerprint = GetProperty(\"ro.build.fingerprint\", \"\");\n    std::vector<std::string> fingerprint_fields = {\n            GetProperty(\"ro.product.brand\", \"\"),\n            \"/\",\n            GetProperty(\"ro.product.name\", \"\"),\n            \"/\",\n            GetProperty(\"ro.product.device\", \"\"),\n            \":\",\n            GetProperty(\"ro.build.version.release_or_codename\", \"\"),\n            \"/\",\n            expected_build_id,\n            \"/\",\n            GetProperty(\"ro.build.version.incremental\", \"\"),\n            \":\",\n            GetProperty(\"ro.build.type\", \"\"),\n            \"/\",\n            GetProperty(\"ro.build.tags\", \"\")};\n\n    ASSERT_EQ(android::base::Join(fingerprint_fields, \"\"), fingerprint);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/property_type.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"property_type.h\"\n\n#include <android-base/parsedouble.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n\nusing android::base::ParseDouble;\nusing android::base::ParseInt;\nusing android::base::ParseUint;\nusing android::base::Split;\n\nnamespace android {\nnamespace init {\n\nbool CheckType(const std::string& type_string, const std::string& value) {\n    // Always allow clearing a property such that the default value when it is not set takes over.\n    if (value.empty()) {\n        return true;\n    }\n\n    auto type_strings = Split(type_string, \" \");\n    if (type_strings.empty()) {\n        return false;\n    }\n    auto type = type_strings[0];\n\n    if (type == \"string\") {\n        return true;\n    }\n    if (type == \"bool\") {\n        return value == \"true\" || value == \"false\" || value == \"1\" || value == \"0\";\n    }\n    if (type == \"int\") {\n        int64_t parsed;\n        return ParseInt(value, &parsed);\n    }\n    if (type == \"uint\") {\n        uint64_t parsed;\n        if (value.empty() || value.front() == '-') {\n            return false;\n        }\n        return ParseUint(value, &parsed);\n    }\n    if (type == \"double\") {\n        double parsed;\n        return ParseDouble(value.c_str(), &parsed);\n    }\n    if (type == \"size\") {\n        auto it = value.begin();\n        while (it != value.end() && isdigit(*it)) {\n            it++;\n        }\n        if (it == value.begin() || it == value.end() || (*it != 'g' && *it != 'k' && *it != 'm')) {\n            return false;\n        }\n        it++;\n        return it == value.end();\n    }\n    if (type == \"enum\") {\n        for (auto it = std::next(type_strings.begin()); it != type_strings.end(); ++it) {\n            if (*it == value) {\n                return true;\n            }\n        }\n    }\n    return false;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/property_type.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_PROPERTY_TYPE_H\n#define _INIT_PROPERTY_TYPE_H\n\n#include <string>\n\nnamespace android {\nnamespace init {\n\nbool CheckType(const std::string& type_string, const std::string& value);\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/property_type_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"property_type.h\"\n\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace init {\n\nTEST(property_type, CheckType_string) {\n    EXPECT_TRUE(CheckType(\"string\", \"\"));\n    EXPECT_TRUE(CheckType(\"string\", \"-234\"));\n    EXPECT_TRUE(CheckType(\"string\", \"234\"));\n    EXPECT_TRUE(CheckType(\"string\", \"true\"));\n    EXPECT_TRUE(CheckType(\"string\", \"false\"));\n    EXPECT_TRUE(CheckType(\"string\", \"45645634563456345634563456\"));\n    EXPECT_TRUE(CheckType(\"string\", \"some other string\"));\n}\n\nTEST(property_type, CheckType_int) {\n    EXPECT_TRUE(CheckType(\"int\", \"\"));\n    EXPECT_FALSE(CheckType(\"int\", \"abc\"));\n    EXPECT_FALSE(CheckType(\"int\", \"-abc\"));\n    EXPECT_TRUE(CheckType(\"int\", \"0\"));\n    EXPECT_TRUE(CheckType(\"int\", std::to_string(std::numeric_limits<int64_t>::min())));\n    EXPECT_TRUE(CheckType(\"int\", std::to_string(std::numeric_limits<int64_t>::max())));\n    EXPECT_TRUE(CheckType(\"int\", \"123\"));\n    EXPECT_TRUE(CheckType(\"int\", \"-123\"));\n}\n\nTEST(property_type, CheckType_uint) {\n    EXPECT_TRUE(CheckType(\"uint\", \"\"));\n    EXPECT_FALSE(CheckType(\"uint\", \"abc\"));\n    EXPECT_FALSE(CheckType(\"uint\", \"-abc\"));\n    EXPECT_TRUE(CheckType(\"uint\", \"0\"));\n    EXPECT_TRUE(CheckType(\"uint\", std::to_string(std::numeric_limits<uint64_t>::max())));\n    EXPECT_TRUE(CheckType(\"uint\", \"123\"));\n    EXPECT_FALSE(CheckType(\"uint\", \"-123\"));\n}\n\nTEST(property_type, CheckType_double) {\n    EXPECT_TRUE(CheckType(\"double\", \"\"));\n    EXPECT_FALSE(CheckType(\"double\", \"abc\"));\n    EXPECT_FALSE(CheckType(\"double\", \"-abc\"));\n    EXPECT_TRUE(CheckType(\"double\", \"0.0\"));\n    EXPECT_TRUE(CheckType(\"double\", std::to_string(std::numeric_limits<double>::min())));\n    EXPECT_TRUE(CheckType(\"double\", std::to_string(std::numeric_limits<double>::max())));\n    EXPECT_TRUE(CheckType(\"double\", \"123.1\"));\n    EXPECT_TRUE(CheckType(\"double\", \"-123.1\"));\n}\n\nTEST(property_type, CheckType_size) {\n    EXPECT_TRUE(CheckType(\"size\", \"\"));\n    EXPECT_FALSE(CheckType(\"size\", \"ab\"));\n    EXPECT_FALSE(CheckType(\"size\", \"abcd\"));\n    EXPECT_FALSE(CheckType(\"size\", \"0\"));\n\n    EXPECT_TRUE(CheckType(\"size\", \"512g\"));\n    EXPECT_TRUE(CheckType(\"size\", \"512k\"));\n    EXPECT_TRUE(CheckType(\"size\", \"512m\"));\n\n    EXPECT_FALSE(CheckType(\"size\", \"512gggg\"));\n    EXPECT_FALSE(CheckType(\"size\", \"512mgk\"));\n    EXPECT_FALSE(CheckType(\"size\", \"g\"));\n    EXPECT_FALSE(CheckType(\"size\", \"m\"));\n}\n\nTEST(property_type, CheckType_enum) {\n    EXPECT_TRUE(CheckType(\"enum abc\", \"\"));\n    EXPECT_FALSE(CheckType(\"enum abc\", \"ab\"));\n    EXPECT_FALSE(CheckType(\"enum abc\", \"abcd\"));\n    EXPECT_FALSE(CheckType(\"enum 123 456 789\", \"0\"));\n\n    EXPECT_TRUE(CheckType(\"enum abc\", \"abc\"));\n    EXPECT_TRUE(CheckType(\"enum 123 456 789\", \"123\"));\n    EXPECT_TRUE(CheckType(\"enum 123 456 789\", \"456\"));\n    EXPECT_TRUE(CheckType(\"enum 123 456 789\", \"789\"));\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/proto_utils.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/socket.h>\n#include <unistd.h>\n\n#include <string>\n\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nconstexpr size_t kBufferSize = 4096;\n\ninline Result<std::string> ReadMessage(int socket) {\n    char buffer[kBufferSize] = {};\n    auto result = TEMP_FAILURE_RETRY(recv(socket, buffer, sizeof(buffer), 0));\n    if (result == 0) {\n        return Error();\n    } else if (result < 0) {\n        return ErrnoError();\n    }\n    return std::string(buffer, result);\n}\n\ntemplate <typename T>\nResult<void> SendMessage(int socket, const T& message) {\n    std::string message_string;\n    if (!message.SerializeToString(&message_string)) {\n        return Error() << \"Unable to serialize message\";\n    }\n\n    if (message_string.size() > kBufferSize) {\n        return Error() << \"Serialized message too long to send\";\n    }\n\n    if (auto result =\n                TEMP_FAILURE_RETRY(send(socket, message_string.c_str(), message_string.size(), 0));\n        result != static_cast<long>(message_string.size())) {\n        return ErrnoError() << \"send() failed to send message contents\";\n    }\n    return {};\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/reboot.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"reboot.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <linux/ext4.h>\n#include <linux/f2fs.h>\n#include <linux/fs.h>\n#include <linux/loop.h>\n#include <mntent.h>\n#include <semaphore.h>\n#include <stdlib.h>\n#include <sys/cdefs.h>\n#include <sys/ioctl.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/swap.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n\n#include <chrono>\n#include <memory>\n#include <set>\n#include <thread>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <bootloader_message/bootloader_message.h>\n#include <cutils/android_reboot.h>\n#include <fs_mgr.h>\n#include <libsnapshot/snapshot.h>\n#include <logwrap/logwrap.h>\n#include <private/android_filesystem_config.h>\n#include <selinux/selinux.h>\n\n#include \"action.h\"\n#include \"action_manager.h\"\n#include \"builtin_arguments.h\"\n#include \"init.h\"\n#include \"mount_namespace.h\"\n#include \"property_service.h\"\n#include \"reboot_utils.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n#include \"sigchld_handler.h\"\n#include \"util.h\"\n\nusing namespace std::literals;\n\nusing android::base::boot_clock;\nusing android::base::GetBoolProperty;\nusing android::base::GetUintProperty;\nusing android::base::SetProperty;\nusing android::base::Split;\nusing android::base::Timer;\nusing android::base::unique_fd;\nusing android::base::WaitForProperty;\nusing android::base::WriteStringToFile;\n\nnamespace android {\nnamespace init {\n\nstatic bool shutting_down = false;\n\nstatic const std::set<std::string> kDebuggingServices{\"tombstoned\", \"logd\", \"adbd\", \"console\"};\n\nstatic void PersistRebootReason(const char* reason, bool write_to_property) {\n    if (write_to_property) {\n        SetProperty(LAST_REBOOT_REASON_PROPERTY, reason);\n    }\n    auto fd = unique_fd(TEMP_FAILURE_RETRY(open(\n            LAST_REBOOT_REASON_FILE, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY, 0666)));\n    if (!fd.ok()) {\n        PLOG(ERROR) << \"Could not open '\" << LAST_REBOOT_REASON_FILE\n                    << \"' to persist reboot reason\";\n        return;\n    }\n    WriteStringToFd(reason, fd);\n    fsync(fd.get());\n}\n\n// represents umount status during reboot / shutdown.\nenum UmountStat {\n    /* umount succeeded. */\n    UMOUNT_STAT_SUCCESS = 0,\n    /* umount was not run. */\n    UMOUNT_STAT_SKIPPED = 1,\n    /* umount failed with timeout. */\n    UMOUNT_STAT_TIMEOUT = 2,\n    /* could not run due to error */\n    UMOUNT_STAT_ERROR = 3,\n    /* not used by init but reserved for other part to use this to represent the\n       the state where umount status before reboot is not found / available. */\n    UMOUNT_STAT_NOT_AVAILABLE = 4,\n};\n\n// Utility for struct mntent\nclass MountEntry {\n  public:\n    explicit MountEntry(const mntent& entry)\n        : mnt_fsname_(entry.mnt_fsname),\n          mnt_dir_(entry.mnt_dir),\n          mnt_type_(entry.mnt_type),\n          mnt_opts_(entry.mnt_opts) {}\n\n    bool Umount(bool force) {\n        LOG(INFO) << \"Unmounting \" << mnt_fsname_ << \":\" << mnt_dir_ << \" opts \" << mnt_opts_;\n        int r = umount2(mnt_dir_.c_str(), force ? MNT_FORCE : 0);\n        if (r == 0) {\n            LOG(INFO) << \"Umounted \" << mnt_fsname_ << \":\" << mnt_dir_ << \" opts \" << mnt_opts_;\n            return true;\n        } else {\n            PLOG(WARNING) << \"Cannot umount \" << mnt_fsname_ << \":\" << mnt_dir_ << \" opts \"\n                          << mnt_opts_;\n            return false;\n        }\n    }\n\n    void DoFsck() {\n        int st;\n        if (IsF2Fs()) {\n            const char* f2fs_argv[] = {\n                    \"/system/bin/fsck.f2fs\",\n                    \"-a\",\n                    mnt_fsname_.c_str(),\n            };\n            logwrap_fork_execvp(arraysize(f2fs_argv), f2fs_argv, &st, false, LOG_KLOG, true,\n                                nullptr);\n        } else if (IsExt4()) {\n            const char* ext4_argv[] = {\n                    \"/system/bin/e2fsck\",\n                    \"-y\",\n                    mnt_fsname_.c_str(),\n            };\n            logwrap_fork_execvp(arraysize(ext4_argv), ext4_argv, &st, false, LOG_KLOG, true,\n                                nullptr);\n        }\n    }\n\n    static bool IsBlockDevice(const struct mntent& mntent) {\n        return android::base::StartsWith(mntent.mnt_fsname, \"/dev/block\");\n    }\n\n    static bool IsEmulatedDevice(const struct mntent& mntent) {\n        return android::base::StartsWith(mntent.mnt_fsname, \"/data/\");\n    }\n\n  private:\n    bool IsF2Fs() const { return mnt_type_ == \"f2fs\"; }\n\n    bool IsExt4() const { return mnt_type_ == \"ext4\"; }\n\n    std::string mnt_fsname_;\n    std::string mnt_dir_;\n    std::string mnt_type_;\n    std::string mnt_opts_;\n};\n\n// Turn off backlight while we are performing power down cleanup activities.\nstatic void TurnOffBacklight() {\n    Service* service = ServiceList::GetInstance().FindService(\"blank_screen\");\n    if (service == nullptr) {\n        LOG(WARNING) << \"cannot find blank_screen in TurnOffBacklight\";\n        return;\n    }\n    if (auto result = service->Start(); !result.ok()) {\n        LOG(WARNING) << \"Could not start blank_screen service: \" << result.error();\n    }\n}\n\nstatic Result<void> CallVdc(const std::string& system, const std::string& cmd) {\n    LOG(INFO) << \"Calling /system/bin/vdc \" << system << \" \" << cmd;\n    const char* vdc_argv[] = {\"/system/bin/vdc\", system.c_str(), cmd.c_str()};\n    int status;\n    if (logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG, true,\n                            nullptr) != 0) {\n        return ErrnoError() << \"Failed to call '/system/bin/vdc \" << system << \" \" << cmd << \"'\";\n    }\n    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {\n        return {};\n    }\n    return Error() << \"'/system/bin/vdc \" << system << \" \" << cmd << \"' failed : \" << status;\n}\n\nstatic void LogShutdownTime(UmountStat stat, Timer* t) {\n    LOG(WARNING) << \"powerctl_shutdown_time_ms:\" << std::to_string(t->duration().count()) << \":\"\n                 << stat;\n}\n\nstatic bool IsDataMounted(const std::string& fstype) {\n    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent(\"/proc/mounts\", \"re\"), endmntent);\n    if (fp == nullptr) {\n        PLOG(ERROR) << \"Failed to open /proc/mounts\";\n        return false;\n    }\n    mntent* mentry;\n    while ((mentry = getmntent(fp.get())) != nullptr) {\n        if (mentry->mnt_dir == \"/data\"s) {\n            return fstype == \"*\" || mentry->mnt_type == fstype;\n        }\n    }\n    return false;\n}\n\n// Find all read+write block devices and emulated devices in /proc/mounts and add them to\n// the correpsponding list.\nstatic bool FindPartitionsToUmount(std::vector<MountEntry>* block_dev_partitions,\n                                   std::vector<MountEntry>* emulated_partitions, bool dump) {\n    std::unique_ptr<std::FILE, int (*)(std::FILE*)> fp(setmntent(\"/proc/mounts\", \"re\"), endmntent);\n    if (fp == nullptr) {\n        PLOG(ERROR) << \"Failed to open /proc/mounts\";\n        return false;\n    }\n    mntent* mentry;\n    while ((mentry = getmntent(fp.get())) != nullptr) {\n        if (dump) {\n            LOG(INFO) << \"mount entry \" << mentry->mnt_fsname << \":\" << mentry->mnt_dir << \" opts \"\n                      << mentry->mnt_opts << \" type \" << mentry->mnt_type;\n        } else if (MountEntry::IsBlockDevice(*mentry) && hasmntopt(mentry, \"rw\")) {\n            std::string mount_dir(mentry->mnt_dir);\n            // These are R/O partitions changed to R/W after adb remount.\n            // Do not umount them as shutdown critical services may rely on them.\n            if (mount_dir != \"/\" && mount_dir != \"/system\" && mount_dir != \"/vendor\" &&\n                mount_dir != \"/oem\") {\n                block_dev_partitions->emplace(block_dev_partitions->begin(), *mentry);\n            }\n        } else if (MountEntry::IsEmulatedDevice(*mentry)) {\n            emulated_partitions->emplace(emulated_partitions->begin(), *mentry);\n        }\n    }\n    return true;\n}\n\nstatic void DumpUmountDebuggingInfo() {\n    int status;\n    if (!security_getenforce()) {\n        LOG(INFO) << \"Run lsof\";\n        const char* lsof_argv[] = {\"/system/bin/lsof\"};\n        logwrap_fork_execvp(arraysize(lsof_argv), lsof_argv, &status, false, LOG_KLOG, true,\n                            nullptr);\n    }\n    FindPartitionsToUmount(nullptr, nullptr, true);\n    // dump current CPU stack traces and uninterruptible tasks\n    WriteStringToFile(\"l\", PROC_SYSRQ);\n    WriteStringToFile(\"w\", PROC_SYSRQ);\n}\n\nstatic UmountStat UmountPartitions(std::chrono::milliseconds timeout) {\n    // Terminate (SIGTERM) the services before unmounting partitions.\n    // If the processes block the signal, then partitions will eventually fail\n    // to unmount and then we fallback to SIGKILL the services.\n    //\n    // Hence, give the services a chance for a graceful shutdown before sending SIGKILL.\n    for (const auto& s : ServiceList::GetInstance()) {\n        if (s->IsShutdownCritical()) {\n            LOG(INFO) << \"Shutdown service: \" << s->name();\n            s->Terminate();\n        }\n    }\n    ReapAnyOutstandingChildren();\n\n    Timer t;\n    /* data partition needs all pending writes to be completed and all emulated partitions\n     * umounted.If the current waiting is not good enough, give\n     * up and leave it to e2fsck after reboot to fix it.\n     */\n    while (true) {\n        std::vector<MountEntry> block_devices;\n        std::vector<MountEntry> emulated_devices;\n        if (!FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {\n            return UMOUNT_STAT_ERROR;\n        }\n        if (block_devices.size() == 0) {\n            return UMOUNT_STAT_SUCCESS;\n        }\n        bool unmount_done = true;\n        if (emulated_devices.size() > 0) {\n            for (auto& entry : emulated_devices) {\n                if (!entry.Umount(false)) unmount_done = false;\n            }\n            if (unmount_done) {\n                sync();\n            }\n        }\n        for (auto& entry : block_devices) {\n            if (!entry.Umount(timeout == 0ms)) unmount_done = false;\n        }\n        if (unmount_done) {\n            return UMOUNT_STAT_SUCCESS;\n        }\n        if ((timeout < t.duration())) {  // try umount at least once\n            return UMOUNT_STAT_TIMEOUT;\n        }\n        std::this_thread::sleep_for(100ms);\n    }\n}\n\nstatic void KillAllProcesses() {\n    WriteStringToFile(\"i\", PROC_SYSRQ);\n}\n\n// Create reboot/shutdwon monitor thread\nvoid RebootMonitorThread(unsigned int cmd, const std::string& reboot_target,\n                         sem_t* reboot_semaphore, std::chrono::milliseconds shutdown_timeout,\n                         bool* reboot_monitor_run) {\n    unsigned int remaining_shutdown_time = 0;\n\n    // 300 seconds more than the timeout passed to the thread as there is a final Umount pass\n    // after the timeout is reached.\n    constexpr unsigned int shutdown_watchdog_timeout_default = 300;\n    auto shutdown_watchdog_timeout = android::base::GetUintProperty(\n            \"ro.build.shutdown.watchdog.timeout\", shutdown_watchdog_timeout_default);\n    remaining_shutdown_time = shutdown_watchdog_timeout + shutdown_timeout.count() / 1000;\n\n    while (*reboot_monitor_run == true) {\n        if (TEMP_FAILURE_RETRY(sem_wait(reboot_semaphore)) == -1) {\n            LOG(ERROR) << \"sem_wait failed and exit RebootMonitorThread()\";\n            return;\n        }\n\n        timespec shutdown_timeout_timespec;\n        if (clock_gettime(CLOCK_MONOTONIC, &shutdown_timeout_timespec) == -1) {\n            LOG(ERROR) << \"clock_gettime() fail! exit RebootMonitorThread()\";\n            return;\n        }\n\n        // If there are some remaining shutdown time left from previous round, we use\n        // remaining time here.\n        shutdown_timeout_timespec.tv_sec += remaining_shutdown_time;\n\n        LOG(INFO) << \"shutdown_timeout_timespec.tv_sec: \" << shutdown_timeout_timespec.tv_sec;\n\n        int sem_return = 0;\n        while ((sem_return = sem_timedwait_monotonic_np(reboot_semaphore,\n                                                        &shutdown_timeout_timespec)) == -1 &&\n               errno == EINTR) {\n        }\n\n        if (sem_return == -1) {\n            LOG(ERROR) << \"Reboot thread timed out\";\n\n            if (android::base::GetBoolProperty(\"ro.debuggable\", false) == true) {\n                if (false) {\n                    // SEPolicy will block debuggerd from running and this is intentional.\n                    // But these lines are left to be enabled during debugging.\n                    LOG(INFO) << \"Try to dump init process call trace:\";\n                    const char* vdc_argv[] = {\"/system/bin/debuggerd\", \"-b\", \"1\"};\n                    int status;\n                    logwrap_fork_execvp(arraysize(vdc_argv), vdc_argv, &status, false, LOG_KLOG,\n                                        true, nullptr);\n                }\n                LOG(INFO) << \"Show stack for all active CPU:\";\n                WriteStringToFile(\"l\", PROC_SYSRQ);\n\n                LOG(INFO) << \"Show tasks that are in disk sleep(uninterruptable sleep), which are \"\n                             \"like \"\n                             \"blocked in mutex or hardware register access:\";\n                WriteStringToFile(\"w\", PROC_SYSRQ);\n            }\n\n            // In shutdown case,notify kernel to sync and umount fs to read-only before shutdown.\n            if (cmd == ANDROID_RB_POWEROFF || cmd == ANDROID_RB_THERMOFF) {\n                WriteStringToFile(\"s\", PROC_SYSRQ);\n\n                WriteStringToFile(\"u\", PROC_SYSRQ);\n\n                RebootSystem(cmd, reboot_target);\n            }\n\n            LOG(ERROR) << \"Trigger crash at last!\";\n            WriteStringToFile(\"c\", PROC_SYSRQ);\n        } else {\n            timespec current_time_timespec;\n\n            if (clock_gettime(CLOCK_MONOTONIC, &current_time_timespec) == -1) {\n                LOG(ERROR) << \"clock_gettime() fail! exit RebootMonitorThread()\";\n                return;\n            }\n\n            remaining_shutdown_time =\n                    shutdown_timeout_timespec.tv_sec - current_time_timespec.tv_sec;\n\n            LOG(INFO) << \"remaining_shutdown_time: \" << remaining_shutdown_time;\n        }\n    }\n}\n\nstatic bool UmountDynamicPartitions(const std::vector<std::string>& dynamic_partitions) {\n    bool ret = true;\n    for (auto device : dynamic_partitions) {\n        // Cannot unmount /system\n        if (device == \"/system\") {\n            continue;\n        }\n        int r = umount2(device.c_str(), MNT_FORCE);\n        if (r == 0) {\n            LOG(INFO) << \"Umounted success: \" << device;\n        } else {\n            PLOG(WARNING) << \"Cannot umount: \" << device;\n            ret = false;\n        }\n    }\n    return ret;\n}\n\n/* Try umounting all emulated file systems R/W block device cfile systems.\n * This will just try umount and give it up if it fails.\n * For fs like ext4, this is ok as file system will be marked as unclean shutdown\n * and necessary check can be done at the next reboot.\n * For safer shutdown, caller needs to make sure that\n * all processes / emulated partition for the target fs are all cleaned-up.\n *\n * return true when umount was successful. false when timed out.\n */\nstatic UmountStat TryUmountAndFsck(unsigned int cmd, bool run_fsck,\n                                   std::chrono::milliseconds timeout, sem_t* reboot_semaphore) {\n    Timer t;\n    std::vector<MountEntry> block_devices;\n    std::vector<MountEntry> emulated_devices;\n    std::vector<std::string> dynamic_partitions;\n\n    if (run_fsck && !FindPartitionsToUmount(&block_devices, &emulated_devices, false)) {\n        return UMOUNT_STAT_ERROR;\n    }\n    auto sm = snapshot::SnapshotManager::New();\n    bool ota_update_in_progress = false;\n    if (sm->IsUserspaceSnapshotUpdateInProgress(dynamic_partitions)) {\n        LOG(INFO) << \"OTA update in progress. Pause snapshot merge\";\n        if (!sm->PauseSnapshotMerge()) {\n            LOG(ERROR) << \"Snapshot-merge pause failed\";\n        }\n        ota_update_in_progress = true;\n    }\n    UmountStat stat = UmountPartitions(timeout - t.duration());\n    if (stat != UMOUNT_STAT_SUCCESS) {\n        LOG(INFO) << \"umount timeout, last resort, kill all and try\";\n        if (DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();\n        // Since umount timedout, we will try to kill all processes\n        // and do one more attempt to umount the partitions.\n        //\n        // However, if OTA update is in progress, we don't want\n        // to kill the snapuserd daemon as the daemon will\n        // be serving I/O requests. Killing the daemon will\n        // end up with I/O failures. If the update is in progress,\n        // we will just return the umount failure status immediately.\n        // This is ok, given the fact that killing the processes\n        // and doing an umount is just a last effort. We are\n        // still not doing fsck when all processes are killed.\n        //\n        if (ota_update_in_progress) {\n            bool umount_dynamic_partitions = UmountDynamicPartitions(dynamic_partitions);\n            LOG(INFO) << \"Sending SIGTERM to all process\";\n            // Send SIGTERM to all processes except init\n            WriteStringToFile(\"e\", PROC_SYSRQ);\n            // Wait for processes to terminate\n            std::this_thread::sleep_for(1s);\n            // Try one more attempt to umount other partitions which failed\n            // earlier\n            if (!umount_dynamic_partitions) {\n                UmountDynamicPartitions(dynamic_partitions);\n            }\n            return stat;\n        }\n        KillAllProcesses();\n        // even if it succeeds, still it is timeout and do not run fsck with all processes killed\n        UmountStat st = UmountPartitions(0ms);\n        if ((st != UMOUNT_STAT_SUCCESS) && DUMP_ON_UMOUNT_FAILURE) DumpUmountDebuggingInfo();\n    }\n\n    if (stat == UMOUNT_STAT_SUCCESS && run_fsck) {\n        LOG(INFO) << \"Pause reboot monitor thread before fsck\";\n        sem_post(reboot_semaphore);\n\n        // fsck part is excluded from timeout check. It only runs for user initiated shutdown\n        // and should not affect reboot time.\n        for (auto& entry : block_devices) {\n            entry.DoFsck();\n        }\n\n        LOG(INFO) << \"Resume reboot monitor thread after fsck\";\n        sem_post(reboot_semaphore);\n    }\n    return stat;\n}\n\n// zram is able to use backing device on top of a loopback device.\n// In order to unmount /data successfully, we have to kill the loopback device first\n#define ZRAM_DEVICE       \"/dev/block/zram0\"\n#define ZRAM_RESET        \"/sys/block/zram0/reset\"\n#define ZRAM_BACK_DEV     \"/sys/block/zram0/backing_dev\"\n#define ZRAM_INITSTATE    \"/sys/block/zram0/initstate\"\nstatic Result<void> KillZramBackingDevice() {\n    std::string zram_initstate;\n    if (!android::base::ReadFileToString(ZRAM_INITSTATE, &zram_initstate)) {\n        return ErrnoError() << \"Failed to read \" << ZRAM_INITSTATE;\n    }\n\n    zram_initstate.erase(zram_initstate.length() - 1);\n    if (zram_initstate == \"0\") {\n        LOG(INFO) << \"Zram has not been swapped on\";\n        return {};\n    }\n\n    if (access(ZRAM_BACK_DEV, F_OK) != 0 && errno == ENOENT) {\n        LOG(INFO) << \"No zram backing device configured\";\n        return {};\n    }\n    std::string backing_dev;\n    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) {\n        return ErrnoError() << \"Failed to read \" << ZRAM_BACK_DEV;\n    }\n\n    android::base::Trim(backing_dev);\n\n    if (android::base::StartsWith(backing_dev, \"none\")) {\n        LOG(INFO) << \"No zram backing device configured\";\n        return {};\n    }\n\n    // shutdown zram handle\n    Timer swap_timer;\n    LOG(INFO) << \"swapoff() start...\";\n    if (swapoff(ZRAM_DEVICE) == -1) {\n        return ErrnoError() << \"zram_backing_dev: swapoff (\" << backing_dev << \")\"\n                            << \" failed\";\n    }\n    LOG(INFO) << \"swapoff() took \" << swap_timer;\n\n    if (!WriteStringToFile(\"1\", ZRAM_RESET)) {\n        return Error() << \"zram_backing_dev: reset (\" << backing_dev << \")\"\n                       << \" failed\";\n    }\n\n    if (!android::base::ReadFileToString(ZRAM_BACK_DEV, &backing_dev)) {\n        return ErrnoError() << \"Failed to read \" << ZRAM_BACK_DEV;\n    }\n\n    android::base::Trim(backing_dev);\n\n    if (!android::base::StartsWith(backing_dev, \"/dev/block/loop\")) {\n        LOG(INFO) << backing_dev << \" is not a loop device. Exiting early\";\n        return {};\n    }\n\n    // clear loopback device\n    unique_fd loop(TEMP_FAILURE_RETRY(open(backing_dev.c_str(), O_RDWR | O_CLOEXEC)));\n    if (loop.get() < 0) {\n        return ErrnoError() << \"zram_backing_dev: open(\" << backing_dev << \")\"\n                            << \" failed\";\n    }\n\n    if (ioctl(loop.get(), LOOP_CLR_FD, 0) < 0) {\n        return ErrnoError() << \"zram_backing_dev: loop_clear (\" << backing_dev << \")\"\n                            << \" failed\";\n    }\n    LOG(INFO) << \"zram_backing_dev: `\" << backing_dev << \"` is cleared successfully.\";\n    return {};\n}\n\n// Stops given services, waits for them to be stopped for |timeout| ms.\n// If terminate is true, then SIGTERM is sent to services, otherwise SIGKILL is sent.\n// Note that services are stopped in order given by |ServiceList::services_in_shutdown_order|\n// function.\nstatic void StopServices(const std::set<std::string>& services, std::chrono::milliseconds timeout,\n                         bool terminate) {\n    LOG(INFO) << \"Stopping \" << services.size() << \" services by sending \"\n              << (terminate ? \"SIGTERM\" : \"SIGKILL\");\n    std::vector<pid_t> pids;\n    pids.reserve(services.size());\n    for (const auto& s : ServiceList::GetInstance().services_in_shutdown_order()) {\n        if (services.count(s->name()) == 0) {\n            continue;\n        }\n        if (s->pid() > 0) {\n            pids.push_back(s->pid());\n        }\n        if (terminate) {\n            s->Terminate();\n        } else {\n            s->Stop();\n        }\n    }\n    if (timeout > 0ms) {\n        WaitToBeReaped(Service::GetSigchldFd(), pids, timeout);\n    } else {\n        // Even if we don't to wait for services to stop, we still optimistically reap zombies.\n        ReapAnyOutstandingChildren();\n    }\n}\n\n// Like StopServices, but also logs all the services that failed to stop after the provided timeout.\n// Returns number of violators.\nint StopServicesAndLogViolations(const std::set<std::string>& services,\n                                 std::chrono::milliseconds timeout, bool terminate) {\n    StopServices(services, timeout, terminate);\n    int still_running = 0;\n    for (const auto& s : ServiceList::GetInstance()) {\n        if (s->IsRunning() && services.count(s->name())) {\n            LOG(ERROR) << \"[service-misbehaving] : service '\" << s->name() << \"' is still running \"\n                       << timeout.count() << \"ms after receiving \"\n                       << (terminate ? \"SIGTERM\" : \"SIGKILL\");\n            still_running++;\n        }\n    }\n    return still_running;\n}\n\nstatic Result<void> UnmountAllApexes() {\n    // don't need to unmount because apexd doesn't use /data in Microdroid\n    if (IsMicrodroid()) {\n        return {};\n    }\n\n    const char* args[] = {\"/system/bin/apexd\", \"--unmount-all\"};\n    int status;\n    if (logwrap_fork_execvp(arraysize(args), args, &status, false, LOG_KLOG, true, nullptr) != 0) {\n        return ErrnoError() << \"Failed to call '/system/bin/apexd --unmount-all'\";\n    }\n    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {\n        return {};\n    }\n    return Error() << \"'/system/bin/apexd --unmount-all' failed : \" << status;\n}\n\n//* Reboot / shutdown the system.\n// cmd ANDROID_RB_* as defined in android_reboot.h\n// reason Reason string like \"reboot\", \"shutdown,userrequested\"\n// reboot_target Reboot target string like \"bootloader\". Otherwise, it should be an empty string.\n// run_fsck Whether to run fsck after umount is done.\n//\nstatic void DoReboot(unsigned int cmd, const std::string& reason, const std::string& reboot_target,\n                     bool run_fsck) {\n    Timer t;\n    LOG(INFO) << \"Reboot start, reason: \" << reason << \", reboot_target: \" << reboot_target;\n\n    bool is_thermal_shutdown = cmd == ANDROID_RB_THERMOFF;\n\n    auto shutdown_timeout = 0ms;\n    if (!SHUTDOWN_ZERO_TIMEOUT) {\n        constexpr unsigned int shutdown_timeout_default = 6;\n        constexpr unsigned int max_thermal_shutdown_timeout = 3;\n        auto shutdown_timeout_final = android::base::GetUintProperty(\"ro.build.shutdown_timeout\",\n                                                                     shutdown_timeout_default);\n        if (is_thermal_shutdown && shutdown_timeout_final > max_thermal_shutdown_timeout) {\n            shutdown_timeout_final = max_thermal_shutdown_timeout;\n        }\n        shutdown_timeout = std::chrono::seconds(shutdown_timeout_final);\n    }\n    LOG(INFO) << \"Shutdown timeout: \" << shutdown_timeout.count() << \" ms\";\n\n    sem_t reboot_semaphore;\n    if (sem_init(&reboot_semaphore, false, 0) == -1) {\n        // These should never fail, but if they do, skip the graceful reboot and reboot immediately.\n        LOG(ERROR) << \"sem_init() fail and RebootSystem() return!\";\n        RebootSystem(cmd, reboot_target, reason);\n    }\n\n    // Start a thread to monitor init shutdown process\n    LOG(INFO) << \"Create reboot monitor thread.\";\n    bool reboot_monitor_run = true;\n    std::thread reboot_monitor_thread(&RebootMonitorThread, cmd, reboot_target, &reboot_semaphore,\n                                      shutdown_timeout, &reboot_monitor_run);\n    reboot_monitor_thread.detach();\n\n    // Start reboot monitor thread\n    sem_post(&reboot_semaphore);\n\n    // Ensure last reboot reason is reduced to canonical\n    // alias reported in bootloader or system boot reason.\n    size_t skip = 0;\n    std::vector<std::string> reasons = Split(reason, \",\");\n    if (reasons.size() >= 2 && reasons[0] == \"reboot\" &&\n        (reasons[1] == \"recovery\" || reasons[1] == \"bootloader\" || reasons[1] == \"cold\" ||\n         reasons[1] == \"hard\" || reasons[1] == \"warm\")) {\n        skip = strlen(\"reboot,\");\n    }\n    PersistRebootReason(reason.c_str() + skip, true);\n\n    // If /data isn't mounted then we can skip the extra reboot steps below, since we don't need to\n    // worry about unmounting it.\n    if (!IsDataMounted(\"*\")) {\n        sync();\n        RebootSystem(cmd, reboot_target, reason);\n        abort();\n    }\n\n    bool do_shutdown_animation = GetBoolProperty(\"ro.init.shutdown_animation\", false);\n    // watchdogd is a vendor specific component but should be alive to complete shutdown safely.\n    const std::set<std::string> to_starts{\"watchdogd\"};\n    std::set<std::string> stop_first;\n    for (const auto& s : ServiceList::GetInstance()) {\n        if (kDebuggingServices.count(s->name())) {\n            // keep debugging tools until non critical ones are all gone.\n            s->SetShutdownCritical();\n        } else if (to_starts.count(s->name())) {\n            if (auto result = s->Start(); !result.ok()) {\n                LOG(ERROR) << \"Could not start shutdown 'to_start' service '\" << s->name()\n                           << \"': \" << result.error();\n            }\n            s->SetShutdownCritical();\n        } else if (do_shutdown_animation && s->classnames().count(\"animation\") > 0) {\n            // Need these for shutdown animations.\n        } else if (s->IsShutdownCritical()) {\n            // Start shutdown critical service if not started.\n            if (auto result = s->Start(); !result.ok()) {\n                LOG(ERROR) << \"Could not start shutdown critical service '\" << s->name()\n                           << \"': \" << result.error();\n            }\n        } else {\n            stop_first.insert(s->name());\n        }\n    }\n\n    // remaining operations (specifically fsck) may take a substantial duration\n    if (!do_shutdown_animation && (cmd == ANDROID_RB_POWEROFF || is_thermal_shutdown)) {\n        TurnOffBacklight();\n    }\n\n    Service* boot_anim = ServiceList::GetInstance().FindService(\"bootanim\");\n    Service* surface_flinger = ServiceList::GetInstance().FindService(\"surfaceflinger\");\n    if (boot_anim != nullptr && surface_flinger != nullptr && surface_flinger->IsRunning()) {\n\n        if (do_shutdown_animation) {\n            SetProperty(\"service.bootanim.exit\", \"0\");\n            SetProperty(\"service.bootanim.progress\", \"0\");\n            // Could be in the middle of animation. Stop and start so that it can pick\n            // up the right mode.\n            boot_anim->Stop();\n        }\n\n        for (const auto& service : ServiceList::GetInstance()) {\n            if (service->classnames().count(\"animation\") == 0) {\n                continue;\n            }\n\n            // start all animation classes if stopped.\n            if (do_shutdown_animation) {\n                service->Start();\n            }\n            service->SetShutdownCritical();  // will not check animation class separately\n        }\n\n        if (do_shutdown_animation) {\n            boot_anim->Start();\n            surface_flinger->SetShutdownCritical();\n            boot_anim->SetShutdownCritical();\n        }\n    }\n\n    // optional shutdown step\n    // 1. terminate all services except shutdown critical ones. wait for delay to finish\n    if (shutdown_timeout > 0ms) {\n        StopServicesAndLogViolations(stop_first, shutdown_timeout / 2, true /* SIGTERM */);\n    }\n    // Send SIGKILL to ones that didn't terminate cleanly.\n    StopServicesAndLogViolations(stop_first, 0ms, false /* SIGKILL */);\n    SubcontextTerminate();\n    // Reap subcontext pids.\n    ReapAnyOutstandingChildren();\n\n    // 3. send volume abort_fuse and volume shutdown to vold\n    Service* vold_service = ServiceList::GetInstance().FindService(\"vold\");\n    if (vold_service != nullptr && vold_service->IsRunning()) {\n        // Manually abort FUSE connections, since the FUSE daemon is already dead\n        // at this point, and unmounting it might hang.\n        CallVdc(\"volume\", \"abort_fuse\");\n        CallVdc(\"volume\", \"shutdown\");\n        vold_service->Stop();\n    } else {\n        LOG(INFO) << \"vold not running, skipping vold shutdown\";\n    }\n    // logcat stopped here\n    StopServices(kDebuggingServices, 0ms, false /* SIGKILL */);\n    // 4. sync, try umount, and optionally run fsck for user shutdown\n    {\n        Timer sync_timer;\n        LOG(INFO) << \"sync() before umount...\";\n        sync();\n        LOG(INFO) << \"sync() before umount took\" << sync_timer;\n    }\n    // 5. drop caches and disable zram backing device, if exist\n    KillZramBackingDevice();\n\n    LOG(INFO) << \"Ready to unmount apexes. So far shutdown sequence took \" << t;\n    // 6. unmount active apexes, otherwise they might prevent clean unmount of /data.\n    if (auto ret = UnmountAllApexes(); !ret.ok()) {\n        LOG(ERROR) << ret.error();\n    }\n    UmountStat stat =\n            TryUmountAndFsck(cmd, run_fsck, shutdown_timeout - t.duration(), &reboot_semaphore);\n    // Follow what linux shutdown is doing: one more sync with little bit delay\n    {\n        Timer sync_timer;\n        LOG(INFO) << \"sync() after umount...\";\n        sync();\n        LOG(INFO) << \"sync() after umount took\" << sync_timer;\n    }\n    if (!is_thermal_shutdown) std::this_thread::sleep_for(100ms);\n    LogShutdownTime(stat, &t);\n\n    // Send signal to terminate reboot monitor thread.\n    reboot_monitor_run = false;\n    sem_post(&reboot_semaphore);\n\n    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.\n    if (IsDataMounted(\"f2fs\")) {\n        uint32_t flag = F2FS_GOING_DOWN_FULLSYNC;\n        unique_fd fd(TEMP_FAILURE_RETRY(open(\"/data\", O_RDONLY)));\n        LOG(INFO) << \"Invoking F2FS_IOC_SHUTDOWN during shutdown\";\n        int ret = ioctl(fd.get(), F2FS_IOC_SHUTDOWN, &flag);\n        if (ret) {\n            PLOG(ERROR) << \"Shutdown /data: \";\n        } else {\n            LOG(INFO) << \"Shutdown /data\";\n        }\n    } else if (IsDataMounted(\"ext4\")) {\n        uint32_t flag = EXT4_GOING_FLAGS_DEFAULT;\n        unique_fd fd(TEMP_FAILURE_RETRY(open(\"/data\", O_RDONLY)));\n        int ret = ioctl(fd.get(), EXT4_IOC_SHUTDOWN, &flag);\n        if (ret) {\n            PLOG(ERROR) << \"Shutdown /data: \";\n        } else {\n            LOG(INFO) << \"Shutdown /data\";\n        }\n    }\n    RebootSystem(cmd, reboot_target, reason);\n    abort();\n}\n\nstatic void EnterShutdown() {\n    LOG(INFO) << \"Entering shutdown mode\";\n    shutting_down = true;\n    // Skip wait for prop if it is in progress\n    ResetWaitForProp();\n    // Clear EXEC flag if there is one pending\n    for (const auto& s : ServiceList::GetInstance()) {\n        s->UnSetExec();\n    }\n}\n\n/**\n * Check if \"command\" field is set in bootloader message.\n *\n * If \"command\" field is broken (contains non-printable characters prior to\n * terminating zero), it will be zeroed.\n *\n * @param[in,out] boot Bootloader message (BCB) structure\n * @return true if \"command\" field is already set, and false if it's empty\n */\nstatic bool CommandIsPresent(bootloader_message* boot) {\n    if (boot->command[0] == '\\0')\n        return false;\n\n    for (size_t i = 0; i < arraysize(boot->command); ++i) {\n        if (boot->command[i] == '\\0')\n            return true;\n        if (!isprint(boot->command[i]))\n            break;\n    }\n\n    memset(boot->command, 0, sizeof(boot->command));\n    return false;\n}\n\nvoid HandlePowerctlMessage(const std::string& command) {\n    unsigned int cmd = 0;\n    std::vector<std::string> cmd_params = Split(command, \",\");\n    std::string reboot_target = \"\";\n    bool run_fsck = false;\n    bool command_invalid = false;\n\n    if (cmd_params[0] == \"shutdown\") {\n        cmd = ANDROID_RB_POWEROFF;\n        if (cmd_params.size() >= 2) {\n            if (cmd_params[1] == \"userrequested\") {\n                // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.\n                // Run fsck once the file system is remounted in read-only mode.\n                run_fsck = true;\n            } else if (cmd_params[1] == \"thermal\") {\n                // Turn off sources of heat immediately.\n                TurnOffBacklight();\n                // run_fsck is false to avoid delay\n                cmd = ANDROID_RB_THERMOFF;\n            }\n        }\n    } else if (cmd_params[0] == \"reboot\") {\n        cmd = ANDROID_RB_RESTART2;\n        if (cmd_params.size() >= 2) {\n            reboot_target = cmd_params[1];\n            if (reboot_target == \"userspace\") {\n                LOG(ERROR) << \"Userspace reboot is deprecated.\";\n                return;\n            }\n            // adb reboot fastboot should boot into bootloader for devices not\n            // supporting logical partitions.\n            if (reboot_target == \"fastboot\" &&\n                !android::base::GetBoolProperty(\"ro.boot.dynamic_partitions\", false)) {\n                reboot_target = \"bootloader\";\n            }\n            // When rebooting to the bootloader notify the bootloader writing\n            // also the BCB.\n            if (reboot_target == \"bootloader\") {\n                std::string err;\n                if (!write_reboot_bootloader(&err)) {\n                    LOG(ERROR) << \"reboot-bootloader: Error writing \"\n                                  \"bootloader_message: \"\n                               << err;\n                }\n            } else if (reboot_target == \"recovery\") {\n                bootloader_message boot = {};\n                if (std::string err; !read_bootloader_message(&boot, &err)) {\n                    LOG(ERROR) << \"Failed to read bootloader message: \" << err;\n                }\n                // Update the boot command field if it's empty, and preserve\n                // the other arguments in the bootloader message.\n                if (!CommandIsPresent(&boot)) {\n                    strlcpy(boot.command, \"boot-recovery\", sizeof(boot.command));\n                    if (std::string err; !write_bootloader_message(boot, &err)) {\n                        LOG(ERROR) << \"Failed to set bootloader message: \" << err;\n                        return;\n                    }\n                }\n            } else if (std::find(cmd_params.begin(), cmd_params.end(), \"quiescent\")\n                    != cmd_params.end()) { // Quiescent can be either subreason or details.\n                bootloader_message boot = {};\n                if (std::string err; !read_bootloader_message(&boot, &err)) {\n                    LOG(ERROR) << \"Failed to read bootloader message: \" << err;\n                }\n                // Update the boot command field if it's empty, and preserve\n                // the other arguments in the bootloader message.\n                if (!CommandIsPresent(&boot)) {\n                    strlcpy(boot.command, \"boot-quiescent\", sizeof(boot.command));\n                    if (std::string err; !write_bootloader_message(boot, &err)) {\n                        LOG(ERROR) << \"Failed to set bootloader message: \" << err;\n                        return;\n                    }\n                }\n            } else if (reboot_target == \"sideload\" || reboot_target == \"sideload-auto-reboot\" ||\n                       reboot_target == \"fastboot\") {\n                std::string arg = reboot_target == \"sideload-auto-reboot\" ? \"sideload_auto_reboot\"\n                                                                          : reboot_target;\n                const std::vector<std::string> options = {\n                        \"--\" + arg,\n                };\n                std::string err;\n                if (!write_bootloader_message(options, &err)) {\n                    LOG(ERROR) << \"Failed to set bootloader message: \" << err;\n                    return;\n                }\n                reboot_target = \"recovery\";\n            }\n\n            // If there are additional parameter, pass them along\n            for (size_t i = 2; (cmd_params.size() > i) && cmd_params[i].size(); ++i) {\n                reboot_target += \",\" + cmd_params[i];\n            }\n        }\n    } else {\n        command_invalid = true;\n    }\n    if (command_invalid) {\n        LOG(ERROR) << \"powerctl: unrecognized command '\" << command << \"'\";\n        return;\n    }\n\n    // We do not want to process any messages (queue'ing triggers, shutdown messages, control\n    // messages, etc) from properties during reboot.\n    StopSendingMessages();\n\n    LOG(INFO) << \"Clear action queue and start shutdown trigger\";\n    ActionManager::GetInstance().ClearQueue();\n    // Queue shutdown trigger first\n    ActionManager::GetInstance().QueueEventTrigger(\"shutdown\");\n    // Queue built-in shutdown_done\n    auto shutdown_handler = [cmd, command, reboot_target, run_fsck](const BuiltinArguments&) {\n        DoReboot(cmd, command, reboot_target, run_fsck);\n        return Result<void>{};\n    };\n    ActionManager::GetInstance().QueueBuiltinAction(shutdown_handler, \"shutdown_done\");\n\n    EnterShutdown();\n}\n\nbool IsShuttingDown() {\n    return shutting_down;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/reboot.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_REBOOT_H\n#define _INIT_REBOOT_H\n\n#include <chrono>\n#include <set>\n#include <string>\n\nnamespace android {\nnamespace init {\n\n// Like StopServices, but also logs all the services that failed to stop after the provided timeout.\n// Returns number of violators.\nint StopServicesAndLogViolations(const std::set<std::string>& services,\n                                 std::chrono::milliseconds timeout, bool terminate);\n// Parses and handles a setprop sys.powerctl message.\nvoid HandlePowerctlMessage(const std::string& command);\n\nbool IsShuttingDown();\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/reboot_test.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"reboot.h\"\n\n#include <errno.h>\n#include <unistd.h>\n\n#include <memory>\n#include <string_view>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <gtest/gtest.h>\n#include <selinux/selinux.h>\n\n#include \"builtin_arguments.h\"\n#include \"builtins.h\"\n#include \"parser.h\"\n#include \"service_list.h\"\n#include \"service_parser.h\"\n#include \"subcontext.h\"\n#include \"util.h\"\n\nusing namespace std::literals;\n\nusing android::base::GetProperty;\nusing android::base::Join;\nusing android::base::SetProperty;\nusing android::base::Split;\nusing android::base::StringReplace;\nusing android::base::WaitForProperty;\nusing android::base::WriteStringToFd;\n\nnamespace android {\nnamespace init {\n\nclass RebootTest : public ::testing::Test {\n  public:\n    RebootTest() {\n        std::vector<std::string> names = GetServiceNames();\n        if (!names.empty()) {\n            ADD_FAILURE() << \"Expected empty ServiceList but found: [\" << Join(names, ',') << \"]\";\n        }\n    }\n\n    ~RebootTest() {\n        std::vector<std::string> names = GetServiceNames();\n        for (const auto& name : names) {\n            auto s = ServiceList::GetInstance().FindService(name);\n            auto pid = s->pid();\n            ServiceList::GetInstance().RemoveService(*s);\n            if (pid > 0) {\n                kill(pid, SIGTERM);\n                kill(pid, SIGKILL);\n            }\n        }\n    }\n\n  private:\n    std::vector<std::string> GetServiceNames() const {\n        std::vector<std::string> names;\n        for (const auto& s : ServiceList::GetInstance()) {\n            names.push_back(s->name());\n        }\n        return names;\n    }\n};\n\nstd::string GetSecurityContext() {\n    char* ctx;\n    if (getcon(&ctx) == -1) {\n        ADD_FAILURE() << \"Failed to call getcon : \" << strerror(errno);\n    }\n    std::string result = std::string(ctx);\n    freecon(ctx);\n    return result;\n}\n\nvoid AddTestService(const std::string& name) {\n    static constexpr std::string_view kScriptTemplate = R\"init(\nservice $name /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n\n    std::string script = StringReplace(StringReplace(kScriptTemplate, \"$name\", name, false),\n                                       \"$selabel\", GetSecurityContext(), false);\n    ServiceList& service_list = ServiceList::GetInstance();\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&service_list, nullptr));\n\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    ASSERT_TRUE(WriteStringToFd(script, tf.fd));\n    ASSERT_TRUE(parser.ParseConfig(tf.path));\n}\n\nTEST_F(RebootTest, StopServicesSIGTERM) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n\n    AddTestService(\"A\");\n    AddTestService(\"B\");\n\n    auto service_a = ServiceList::GetInstance().FindService(\"A\");\n    ASSERT_NE(nullptr, service_a);\n    auto service_b = ServiceList::GetInstance().FindService(\"B\");\n    ASSERT_NE(nullptr, service_b);\n\n    ASSERT_RESULT_OK(service_a->Start());\n    ASSERT_TRUE(service_a->IsRunning());\n    ASSERT_RESULT_OK(service_b->Start());\n    ASSERT_TRUE(service_b->IsRunning());\n\n    std::unique_ptr<Service> oneshot_service;\n    {\n        auto result = Service::MakeTemporaryOneshotService(\n                {\"exec\", GetSecurityContext(), \"--\", \"/system/bin/yes\"});\n        ASSERT_RESULT_OK(result);\n        oneshot_service = std::move(*result);\n    }\n    std::string oneshot_service_name = oneshot_service->name();\n    oneshot_service->Start();\n    ASSERT_TRUE(oneshot_service->IsRunning());\n    ServiceList::GetInstance().AddService(std::move(oneshot_service));\n\n    EXPECT_EQ(0, StopServicesAndLogViolations({\"A\", \"B\", oneshot_service_name}, 10s,\n                                              /* terminate= */ true));\n    EXPECT_FALSE(service_a->IsRunning());\n    EXPECT_FALSE(service_b->IsRunning());\n    // Oneshot services are deleted from the ServiceList after they are destroyed.\n    auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);\n    EXPECT_EQ(nullptr, oneshot_service_after_stop);\n}\n\nTEST_F(RebootTest, StopServicesSIGKILL) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n\n    AddTestService(\"A\");\n    AddTestService(\"B\");\n\n    auto service_a = ServiceList::GetInstance().FindService(\"A\");\n    ASSERT_NE(nullptr, service_a);\n    auto service_b = ServiceList::GetInstance().FindService(\"B\");\n    ASSERT_NE(nullptr, service_b);\n\n    ASSERT_RESULT_OK(service_a->Start());\n    ASSERT_TRUE(service_a->IsRunning());\n    ASSERT_RESULT_OK(service_b->Start());\n    ASSERT_TRUE(service_b->IsRunning());\n\n    std::unique_ptr<Service> oneshot_service;\n    {\n        auto result = Service::MakeTemporaryOneshotService(\n                {\"exec\", GetSecurityContext(), \"--\", \"/system/bin/yes\"});\n        ASSERT_RESULT_OK(result);\n        oneshot_service = std::move(*result);\n    }\n    std::string oneshot_service_name = oneshot_service->name();\n    oneshot_service->Start();\n    ASSERT_TRUE(oneshot_service->IsRunning());\n    ServiceList::GetInstance().AddService(std::move(oneshot_service));\n\n    EXPECT_EQ(0, StopServicesAndLogViolations({\"A\", \"B\", oneshot_service_name}, 10s,\n                                              /* terminate= */ false));\n    EXPECT_FALSE(service_a->IsRunning());\n    EXPECT_FALSE(service_b->IsRunning());\n    // Oneshot services are deleted from the ServiceList after they are destroyed.\n    auto oneshot_service_after_stop = ServiceList::GetInstance().FindService(oneshot_service_name);\n    EXPECT_EQ(nullptr, oneshot_service_after_stop);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/reboot_utils.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/capability.h>\n#include <sys/reboot.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n\n#include <optional>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <cutils/android_reboot.h>\n#include <fs_mgr.h>\n#include <unwindstack/AndroidUnwinder.h>\n\n#include \"capabilities.h\"\n#include \"reboot_utils.h\"\n#include \"util.h\"\n\nnamespace android {\nnamespace init {\n\nstatic std::string init_fatal_reboot_target = \"bootloader\";\nstatic bool init_fatal_panic = false;\n\n// this needs to read the /proc/* files directly because it is called before\n// ro.boot.* properties are initialized\nvoid SetFatalRebootTarget(const std::optional<std::string>& reboot_target) {\n    std::string cmdline;\n    android::base::ReadFileToString(\"/proc/cmdline\", &cmdline);\n    cmdline = android::base::Trim(cmdline);\n\n    const std::string kInitFatalPanicParamString = \"androidboot.init_fatal_panic\";\n    if (cmdline.find(kInitFatalPanicParamString) == std::string::npos) {\n        std::string value;\n        init_fatal_panic = (android::fs_mgr::GetBootconfig(kInitFatalPanicParamString, &value) &&\n                            value == \"true\");\n    } else {\n        const std::string kInitFatalPanicString = kInitFatalPanicParamString + \"=true\";\n        init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;\n    }\n\n    if (reboot_target) {\n        init_fatal_reboot_target = *reboot_target;\n        return;\n    }\n\n    const std::string kRebootTargetString = \"androidboot.init_fatal_reboot_target\";\n    auto start_pos = cmdline.find(kRebootTargetString);\n    if (start_pos == std::string::npos) {\n        android::fs_mgr::GetBootconfig(kRebootTargetString, &init_fatal_reboot_target);\n        // We already default to bootloader if no setting is provided.\n    } else {\n        const std::string kRebootTargetStringPattern = kRebootTargetString + \"=\";\n        start_pos += sizeof(kRebootTargetStringPattern) - 1;\n\n        auto end_pos = cmdline.find(' ', start_pos);\n        // if end_pos isn't found, then we've run off the end, but this is okay as this is the last\n        // entry, and -1 is a valid size for string::substr();\n        auto size = end_pos == std::string::npos ? -1 : end_pos - start_pos;\n        init_fatal_reboot_target = cmdline.substr(start_pos, size);\n    }\n}\n\nbool IsRebootCapable() {\n    if (!CAP_IS_SUPPORTED(CAP_SYS_BOOT)) {\n        PLOG(WARNING) << \"CAP_SYS_BOOT is not supported\";\n        return true;\n    }\n\n    ScopedCaps caps(cap_get_proc());\n    if (!caps) {\n        PLOG(WARNING) << \"cap_get_proc() failed\";\n        return true;\n    }\n\n    cap_flag_value_t value = CAP_SET;\n    if (cap_get_flag(caps.get(), CAP_SYS_BOOT, CAP_EFFECTIVE, &value) != 0) {\n        PLOG(WARNING) << \"cap_get_flag(CAP_SYS_BOOT, EFFECTIVE) failed\";\n        return true;\n    }\n    return value == CAP_SET;\n}\n\nvoid __attribute__((noreturn))\nRebootSystem(unsigned int cmd, const std::string& rebootTarget, const std::string& reboot_reason) {\n    LOG(INFO) << \"Reboot ending, jumping to kernel\";\n\n    if (!IsRebootCapable()) {\n        // On systems where init does not have the capability of rebooting the\n        // device, just exit cleanly.\n        exit(0);\n    }\n\n    switch (cmd) {\n        case ANDROID_RB_POWEROFF:\n            reboot(RB_POWER_OFF);\n            break;\n\n        case ANDROID_RB_RESTART2:\n            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,\n                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());\n            break;\n\n        case ANDROID_RB_THERMOFF:\n            if (android::base::GetBoolProperty(\"ro.thermal_warmreset\", false)) {\n                std::string reason = \"shutdown,thermal\";\n                if (!reboot_reason.empty()) reason = reboot_reason;\n\n                LOG(INFO) << \"Try to trigger a warm reset for thermal shutdown\";\n                syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,\n                        LINUX_REBOOT_CMD_RESTART2, reason.c_str());\n            } else {\n                reboot(RB_POWER_OFF);\n            }\n            break;\n    }\n    // In normal case, reboot should not return.\n    PLOG(ERROR) << \"reboot call returned\";\n    abort();\n}\n\nvoid __attribute__((noreturn)) InitFatalReboot(int signal_number) {\n    auto pid = fork();\n\n    if (pid == -1) {\n        // Couldn't fork, don't even try to backtrace, just reboot.\n        RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);\n    } else if (pid == 0) {\n        // Fork a child for safety, since we always want to shut down if something goes wrong, but\n        // its worth trying to get the backtrace, even in the signal handler, since typically it\n        // does work despite not being async-signal-safe.\n        sleep(5);\n        RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);\n    }\n\n    // In the parent, let's try to get a backtrace then shutdown.\n    LOG(ERROR) << __FUNCTION__ << \": signal \" << signal_number;\n    unwindstack::AndroidLocalUnwinder unwinder;\n    unwindstack::AndroidUnwinderData data;\n    if (!unwinder.Unwind(data)) {\n        LOG(ERROR) << __FUNCTION__ << \": Failed to unwind callstack: \" << data.GetErrorString();\n    }\n    for (const auto& frame : data.frames) {\n        LOG(ERROR) << unwinder.FormatFrame(frame);\n    }\n    if (init_fatal_panic) {\n        LOG(ERROR) << __FUNCTION__ << \": Trigger crash\";\n        android::base::WriteStringToFile(\"c\", PROC_SYSRQ);\n        LOG(ERROR) << __FUNCTION__ << \": Sys-Rq failed to crash the system; fallback to exit().\";\n        _exit(signal_number);\n    }\n    RebootSystem(ANDROID_RB_RESTART2, init_fatal_reboot_target);\n}\n\nvoid InstallRebootSignalHandlers() {\n    // Instead of panic'ing the kernel as is the default behavior when init crashes,\n    // we prefer to reboot to bootloader on development builds, as this will prevent\n    // boot looping bad configurations and allow both developers and test farms to easily\n    // recover.\n    struct sigaction action;\n    memset(&action, 0, sizeof(action));\n    sigfillset(&action.sa_mask);\n    action.sa_handler = [](int signal) {\n        // These signal handlers are also caught for processes forked from init, however we do not\n        // want them to trigger reboot, so we directly call _exit() for children processes here.\n        if (getpid() != 1) {\n            _exit(signal);\n        }\n\n        // Calling DoReboot() or LOG(FATAL) is not a good option as this is a signal handler.\n        // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option\n        // and probably good enough given this is already an error case and only enabled for\n        // development builds.\n        InitFatalReboot(signal);\n    };\n    action.sa_flags = SA_RESTART;\n    sigaction(SIGABRT, &action, nullptr);\n    sigaction(SIGBUS, &action, nullptr);\n    sigaction(SIGFPE, &action, nullptr);\n    sigaction(SIGILL, &action, nullptr);\n    sigaction(SIGSEGV, &action, nullptr);\n#if defined(SIGSTKFLT)\n    sigaction(SIGSTKFLT, &action, nullptr);\n#endif\n    sigaction(SIGSYS, &action, nullptr);\n    sigaction(SIGTRAP, &action, nullptr);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/reboot_utils.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <optional>\n#include <string>\n\n#define PROC_SYSRQ \"/proc/sysrq-trigger\"\n\nnamespace android {\nnamespace init {\n\nvoid SetFatalRebootTarget(const std::optional<std::string>& reboot_target = std::nullopt);\n// Determines whether the system is capable of rebooting. This is conservative,\n// so if any of the attempts to determine this fail, it will still return true.\nbool IsRebootCapable();\n// This is a wrapper around the actual reboot calls.\nvoid __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target,\n                                            const std::string& reboot_reason = \"\");\nvoid __attribute__((noreturn)) InitFatalReboot(int signal_number);\nvoid InstallRebootSignalHandlers();\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/result.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// The implementation of this file has moved to android-base.  This file remains since historically,\n// these classes were a part of init.\n\n#include <android-base/result.h>\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\nusing android::base::ResultError;\n"
  },
  {
    "path": "init/rlimit_parser.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"rlimit_parser.h\"\n\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n\nusing android::base::EqualsIgnoreCase;\nusing android::base::ParseInt;\nusing android::base::ParseUint;\nusing android::base::StartsWith;\n\nnamespace android {\nnamespace init {\n\n// Builtins and service definitions both have their arguments start at 1 and finish at 3.\nResult<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args) {\n    static const std::vector<std::pair<const char*, int>> text_to_resources = {\n            {\"cpu\", RLIMIT_CPU},           {\"fsize\", RLIMIT_FSIZE},\n            {\"data\", RLIMIT_DATA},         {\"stack\", RLIMIT_STACK},\n            {\"core\", RLIMIT_CORE},         {\"rss\", RLIMIT_RSS},\n            {\"nproc\", RLIMIT_NPROC},       {\"nofile\", RLIMIT_NOFILE},\n            {\"memlock\", RLIMIT_MEMLOCK},   {\"as\", RLIMIT_AS},\n            {\"locks\", RLIMIT_LOCKS},       {\"sigpending\", RLIMIT_SIGPENDING},\n            {\"msgqueue\", RLIMIT_MSGQUEUE}, {\"nice\", RLIMIT_NICE},\n            {\"rtprio\", RLIMIT_RTPRIO},     {\"rttime\", RLIMIT_RTTIME},\n    };\n\n    int resource;\n\n    if (ParseInt(args[1], &resource)) {\n        if (resource >= RLIM_NLIMITS) {\n            return Error() << \"Resource '\" << args[1] << \"' over the maximum resource value '\"\n                           << RLIM_NLIMITS << \"'\";\n        } else if (resource < 0) {\n            return Error() << \"Resource '\" << args[1] << \"' below the minimum resource value '0'\";\n        }\n    } else {\n        std::string resource_string;\n        if (StartsWith(args[1], \"RLIM_\")) {\n            resource_string = args[1].substr(5);\n        } else if (StartsWith(args[1], \"RLIMIT_\")) {\n            resource_string = args[1].substr(7);\n        } else {\n            resource_string = args[1];\n        }\n\n        auto it = std::find_if(text_to_resources.begin(), text_to_resources.end(),\n                               [&resource_string](const auto& entry) {\n                                   return EqualsIgnoreCase(resource_string, entry.first);\n                               });\n        if (it == text_to_resources.end()) {\n            return Error() << \"Could not parse resource '\" << args[1] << \"'\";\n        }\n\n        resource = it->second;\n    }\n\n    rlimit limit;\n    if (args[2] == \"-1\" || args[2] == \"unlimited\") {\n        limit.rlim_cur = RLIM_INFINITY;\n    } else if (!ParseUint(args[2], &limit.rlim_cur)) {\n        return Error() << \"Could not parse soft limit '\" << args[2] << \"'\";\n    }\n\n    if (args[3] == \"-1\" || args[3] == \"unlimited\") {\n        limit.rlim_max = RLIM_INFINITY;\n    } else if (!ParseUint(args[3], &limit.rlim_max)) {\n        return Error() << \"Could not parse hard limit '\" << args[3] << \"'\";\n    }\n\n    return std::pair{resource, limit};\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/rlimit_parser.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_RLIMIT_PARSER_H\n#define _INIT_RLIMIT_PARSER_H\n\n#include <sys/resource.h>\n\n#include <string>\n#include <vector>\n\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nResult<std::pair<int, rlimit>> ParseRlimit(const std::vector<std::string>& args);\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/rlimit_parser_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"rlimit_parser.h\"\n\n#include <iostream>\n\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace init {\n\nvoid TestRlimitSuccess(std::vector<std::string> input,\n                       const std::pair<int, rlimit>& expected_result) {\n    input.emplace(input.begin(), \"\");\n    ASSERT_EQ(4U, input.size());\n    auto result = ParseRlimit(input);\n\n    ASSERT_TRUE(result.ok()) << \"input: \" << input[1];\n    const auto& [resource, rlimit] = *result;\n    const auto& [expected_resource, expected_rlimit] = expected_result;\n    EXPECT_EQ(expected_resource, resource);\n    EXPECT_EQ(expected_rlimit.rlim_cur, rlimit.rlim_cur);\n    EXPECT_EQ(expected_rlimit.rlim_max, rlimit.rlim_max);\n}\n\nvoid TestRlimitFailure(std::vector<std::string> input, const std::string& expected_result) {\n    input.emplace(input.begin(), \"\");\n    ASSERT_EQ(4U, input.size());\n    auto result = ParseRlimit(input);\n\n    ASSERT_FALSE(result.ok()) << \"input: \" << input[1];\n    EXPECT_EQ(expected_result, result.error().message());\n    EXPECT_EQ(0, result.error().code());\n}\n\nTEST(rlimit, RlimitSuccess) {\n    const std::vector<std::pair<std::vector<std::string>, std::pair<int, rlimit>>>\n            inputs_and_results = {\n                    {{\"cpu\", \"10\", \"10\"}, {0, {10, 10}}},\n                    {{\"fsize\", \"10\", \"10\"}, {1, {10, 10}}},\n                    {{\"data\", \"10\", \"10\"}, {2, {10, 10}}},\n                    {{\"stack\", \"10\", \"10\"}, {3, {10, 10}}},\n                    {{\"core\", \"10\", \"10\"}, {4, {10, 10}}},\n                    {{\"rss\", \"10\", \"10\"}, {5, {10, 10}}},\n                    {{\"nproc\", \"10\", \"10\"}, {6, {10, 10}}},\n                    {{\"nofile\", \"10\", \"10\"}, {7, {10, 10}}},\n                    {{\"memlock\", \"10\", \"10\"}, {8, {10, 10}}},\n                    {{\"as\", \"10\", \"10\"}, {9, {10, 10}}},\n                    {{\"locks\", \"10\", \"10\"}, {10, {10, 10}}},\n                    {{\"sigpending\", \"10\", \"10\"}, {11, {10, 10}}},\n                    {{\"msgqueue\", \"10\", \"10\"}, {12, {10, 10}}},\n                    {{\"nice\", \"10\", \"10\"}, {13, {10, 10}}},\n                    {{\"rtprio\", \"10\", \"10\"}, {14, {10, 10}}},\n                    {{\"rttime\", \"10\", \"10\"}, {15, {10, 10}}},\n\n                    // For some reason, we spelled these wrong.\n                    {{\"RLIM_CPU\", \"10\", \"10\"}, {0, {10, 10}}},\n                    {{\"RLIM_FSIZE\", \"10\", \"10\"}, {1, {10, 10}}},\n                    {{\"RLIM_DATA\", \"10\", \"10\"}, {2, {10, 10}}},\n                    {{\"RLIM_STACK\", \"10\", \"10\"}, {3, {10, 10}}},\n                    {{\"RLIM_CORE\", \"10\", \"10\"}, {4, {10, 10}}},\n                    {{\"RLIM_RSS\", \"10\", \"10\"}, {5, {10, 10}}},\n                    {{\"RLIM_NPROC\", \"10\", \"10\"}, {6, {10, 10}}},\n                    {{\"RLIM_NOFILE\", \"10\", \"10\"}, {7, {10, 10}}},\n                    {{\"RLIM_MEMLOCK\", \"10\", \"10\"}, {8, {10, 10}}},\n                    {{\"RLIM_AS\", \"10\", \"10\"}, {9, {10, 10}}},\n                    {{\"RLIM_LOCKS\", \"10\", \"10\"}, {10, {10, 10}}},\n                    {{\"RLIM_SIGPENDING\", \"10\", \"10\"}, {11, {10, 10}}},\n                    {{\"RLIM_MSGQUEUE\", \"10\", \"10\"}, {12, {10, 10}}},\n                    {{\"RLIM_NICE\", \"10\", \"10\"}, {13, {10, 10}}},\n                    {{\"RLIM_RTPRIO\", \"10\", \"10\"}, {14, {10, 10}}},\n                    {{\"RLIM_RTTIME\", \"10\", \"10\"}, {15, {10, 10}}},\n\n                    // These are the correct spellings.\n                    {{\"RLIMIT_CPU\", \"10\", \"10\"}, {0, {10, 10}}},\n                    {{\"RLIMIT_FSIZE\", \"10\", \"10\"}, {1, {10, 10}}},\n                    {{\"RLIMIT_DATA\", \"10\", \"10\"}, {2, {10, 10}}},\n                    {{\"RLIMIT_STACK\", \"10\", \"10\"}, {3, {10, 10}}},\n                    {{\"RLIMIT_CORE\", \"10\", \"10\"}, {4, {10, 10}}},\n                    {{\"RLIMIT_RSS\", \"10\", \"10\"}, {5, {10, 10}}},\n                    {{\"RLIMIT_NPROC\", \"10\", \"10\"}, {6, {10, 10}}},\n                    {{\"RLIMIT_NOFILE\", \"10\", \"10\"}, {7, {10, 10}}},\n                    {{\"RLIMIT_MEMLOCK\", \"10\", \"10\"}, {8, {10, 10}}},\n                    {{\"RLIMIT_AS\", \"10\", \"10\"}, {9, {10, 10}}},\n                    {{\"RLIMIT_LOCKS\", \"10\", \"10\"}, {10, {10, 10}}},\n                    {{\"RLIMIT_SIGPENDING\", \"10\", \"10\"}, {11, {10, 10}}},\n                    {{\"RLIMIT_MSGQUEUE\", \"10\", \"10\"}, {12, {10, 10}}},\n                    {{\"RLIMIT_NICE\", \"10\", \"10\"}, {13, {10, 10}}},\n                    {{\"RLIMIT_RTPRIO\", \"10\", \"10\"}, {14, {10, 10}}},\n                    {{\"RLIMIT_RTTIME\", \"10\", \"10\"}, {15, {10, 10}}},\n\n                    {{\"0\", \"10\", \"10\"}, {0, {10, 10}}},\n                    {{\"1\", \"10\", \"10\"}, {1, {10, 10}}},\n                    {{\"2\", \"10\", \"10\"}, {2, {10, 10}}},\n                    {{\"3\", \"10\", \"10\"}, {3, {10, 10}}},\n                    {{\"4\", \"10\", \"10\"}, {4, {10, 10}}},\n                    {{\"5\", \"10\", \"10\"}, {5, {10, 10}}},\n                    {{\"6\", \"10\", \"10\"}, {6, {10, 10}}},\n                    {{\"7\", \"10\", \"10\"}, {7, {10, 10}}},\n                    {{\"8\", \"10\", \"10\"}, {8, {10, 10}}},\n                    {{\"9\", \"10\", \"10\"}, {9, {10, 10}}},\n                    {{\"10\", \"10\", \"10\"}, {10, {10, 10}}},\n                    {{\"11\", \"10\", \"10\"}, {11, {10, 10}}},\n                    {{\"12\", \"unlimited\", \"10\"}, {12, {RLIM_INFINITY, 10}}},\n                    {{\"13\", \"-1\", \"10\"}, {13, {RLIM_INFINITY, 10}}},\n                    {{\"14\", \"10\", \"unlimited\"}, {14, {10, RLIM_INFINITY}}},\n                    {{\"15\", \"10\", \"-1\"}, {15, {10, RLIM_INFINITY}}},\n            };\n\n    for (const auto& [input, expected_result] : inputs_and_results) {\n        TestRlimitSuccess(input, expected_result);\n    }\n}\n\nTEST(rlimit, RlimitFailure) {\n    const std::vector<std::pair<std::vector<std::string>, std::string>> inputs_and_results = {\n            {{\"-4\", \"10\", \"10\"}, \"Resource '-4' below the minimum resource value '0'\"},\n            {{\"100\", \"10\", \"10\"}, \"Resource '100' over the maximum resource value '16'\"},\n            {{\"bad_string\", \"10\", \"10\"}, \"Could not parse resource 'bad_string'\"},\n            {{\"RLIM_\", \"10\", \"10\"}, \"Could not parse resource 'RLIM_'\"},\n            {{\"RLIMIT_\", \"10\", \"10\"}, \"Could not parse resource 'RLIMIT_'\"},\n            {{\"cpu\", \"abc\", \"10\"}, \"Could not parse soft limit 'abc'\"},\n            {{\"cpu\", \"10\", \"abc\"}, \"Could not parse hard limit 'abc'\"},\n            {{\"cpu\", \"unlimit\", \"10\"}, \"Could not parse soft limit 'unlimit'\"},\n            {{\"cpu\", \"10\", \"unlimit\"}, \"Could not parse hard limit 'unlimit'\"},\n            {{\"cpu\", \"-2\", \"10\"}, \"Could not parse soft limit '-2'\"},\n            {{\"cpu\", \"10\", \"-2\"}, \"Could not parse hard limit '-2'\"},\n    };\n\n    for (const auto& [input, expected_result] : inputs_and_results) {\n        TestRlimitFailure(input, expected_result);\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/second_stage_resources.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\nnamespace android {\nnamespace init {\n\nconstexpr const char kSecondStageRes[] = \"/second_stage_resources\";\nconstexpr const char kBootImageRamdiskProp[] = \"/system/etc/ramdisk/build.prop\";\n\ninline std::string GetRamdiskPropForSecondStage() {\n    return std::string(kSecondStageRes) + kBootImageRamdiskProp;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/security.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"security.h\"\n#include \"util.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/perf_event.h>\n#include <math.h>\n#include <selinux/selinux.h>\n#include <sys/ioctl.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n\n#include <fstream>\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/unique_fd.h>\n\nusing android::base::unique_fd;\nusing android::base::SetProperty;\n\nnamespace android {\nnamespace init {\n\nstatic bool SetHighestAvailableOptionValue(const std::string& path, int min, int max) {\n    std::ifstream inf(path, std::fstream::in);\n    if (!inf) {\n        LOG(ERROR) << \"Cannot open for reading: \" << path;\n        return false;\n    }\n\n    int current = max;\n    while (current >= min) {\n        // try to write out new value\n        std::string str_val = std::to_string(current);\n        std::ofstream of(path, std::fstream::out);\n        if (!of) {\n            LOG(ERROR) << \"Cannot open for writing: \" << path;\n            return false;\n        }\n        of << str_val << std::endl;\n        of.close();\n\n        // check to make sure it was recorded\n        inf.seekg(0);\n        std::string str_rec;\n        inf >> str_rec;\n        if (str_val.compare(str_rec) == 0) {\n            break;\n        }\n        current--;\n    }\n    inf.close();\n\n    if (current < min) {\n        LOG(ERROR) << \"Unable to set minimum option value \" << min << \" in \" << path;\n        return false;\n    }\n    return true;\n}\n\n#define MMAP_RND_PATH \"/proc/sys/vm/mmap_rnd_bits\"\n#define MMAP_RND_COMPAT_PATH \"/proc/sys/vm/mmap_rnd_compat_bits\"\n\nstatic bool SetMmapRndBitsMin(int start, int min, bool compat) {\n    std::string path;\n    if (compat) {\n        path = MMAP_RND_COMPAT_PATH;\n    } else {\n        path = MMAP_RND_PATH;\n    }\n\n    return SetHighestAvailableOptionValue(path, min, start);\n}\n\n// Set /proc/sys/vm/mmap_rnd_bits and potentially\n// /proc/sys/vm/mmap_rnd_compat_bits to the maximum supported values.\n// Returns an error if unable to set these to an acceptable value.\n//\n// To support this sysctl, the following upstream commits are needed:\n//\n// d07e22597d1d mm: mmap: add new /proc tunable for mmap_base ASLR\n// e0c25d958f78 arm: mm: support ARCH_MMAP_RND_BITS\n// 8f0d3aa9de57 arm64: mm: support ARCH_MMAP_RND_BITS\n// 9e08f57d684a x86: mm: support ARCH_MMAP_RND_BITS\n// ec9ee4acd97c drivers: char: random: add get_random_long()\n// 5ef11c35ce86 mm: ASLR: use get_random_long()\nResult<void> SetMmapRndBitsAction(const BuiltinArguments&) {\n// values are arch-dependent\n#if defined(USER_MODE_LINUX)\n    // uml does not support mmap_rnd_bits\n    return {};\n#elif defined(__aarch64__)\n    // arm64 supports 14 - 33 rnd bits depending on page size and ARM64_VA_BITS.\n    // The kernel (6.5) still defaults to 39 va bits for 4KiB pages, so shipping\n    // devices are only getting 24 bits of randomness in practice.\n    if (SetMmapRndBitsMin(33, 24, false) && (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {\n        return {};\n    }\n#elif defined(__riscv)\n    // riscv64 supports 24 rnd bits with Sv39, and starting with the 6.9 kernel,\n    // 33 bits with Sv48 and Sv57.\n    if (SetMmapRndBitsMin(33, 24, false)) {\n        return {};\n    }\n#elif defined(__x86_64__)\n    // x86_64 supports 28 - 32 rnd bits, but Android wants to ensure that the\n    // theoretical maximum of 32 bits is always supported and used; except in\n    // the case of the x86 page size emulator which supports a maximum\n    // of 30 bits for 16k page size, or 28 bits for 64k page size.\n    int max_bits = 32 - (static_cast<int>(log2(getpagesize())) - 12);\n    if (SetMmapRndBitsMin(max_bits, max_bits, false) &&\n        (!Has32BitAbi() || SetMmapRndBitsMin(16, 16, true))) {\n        return {};\n    }\n#elif defined(__arm__) || defined(__i386__)\n    // check to see if we're running on 64-bit kernel\n    bool h64 = !access(MMAP_RND_COMPAT_PATH, F_OK);\n    // supported 32-bit architecture must have 16 bits set\n    if (SetMmapRndBitsMin(16, 16, h64)) {\n        return {};\n    }\n#else\n    LOG(ERROR) << \"Unknown architecture\";\n#endif\n\n    LOG(FATAL) << \"Unable to set adequate mmap entropy value!\";\n    return Error();\n}\n\n#define KPTR_RESTRICT_PATH \"/proc/sys/kernel/kptr_restrict\"\n#define KPTR_RESTRICT_MINVALUE 2\n#define KPTR_RESTRICT_MAXVALUE 4\n\n// Set kptr_restrict to the highest available level.\n//\n// Aborts if unable to set this to an acceptable value.\nResult<void> SetKptrRestrictAction(const BuiltinArguments&) {\n    std::string path = KPTR_RESTRICT_PATH;\n\n    if (!SetHighestAvailableOptionValue(path, KPTR_RESTRICT_MINVALUE, KPTR_RESTRICT_MAXVALUE)) {\n        LOG(FATAL) << \"Unable to set adequate kptr_restrict value!\";\n        return Error();\n    }\n    return {};\n}\n\n// Test for whether the kernel has SELinux hooks for the perf_event_open()\n// syscall. If the hooks are present, we can stop using the other permission\n// mechanism (perf_event_paranoid sysctl), and use only the SELinux policy to\n// control access to the syscall. The hooks are expected on all Android R\n// release kernels, but might be absent on devices that upgrade while keeping an\n// older kernel.\n//\n// There is no direct/synchronous way of finding out that a syscall failed due\n// to SELinux. Therefore we test for a combination of a success and a failure\n// that are explained by the platform's SELinux policy for the \"init\" domain:\n// * cpu-scoped perf_event is allowed\n// * ioctl() on the event fd is disallowed with EACCES\n//\n// Since init has CAP_SYS_ADMIN, these tests are not affected by the system-wide\n// perf_event_paranoid sysctl.\n//\n// If the SELinux hooks are detected, a special sysprop\n// (sys.init.perf_lsm_hooks) is set, which translates to a modification of\n// perf_event_paranoid (through init.rc sysprop actions).\n//\n// TODO(b/137092007): this entire test can be removed once the platform stops\n// supporting kernels that precede the perf_event_open hooks (Android common\n// kernels 4.4 and 4.9).\nResult<void> TestPerfEventSelinuxAction(const BuiltinArguments&) {\n    // Special case: for *development devices* that boot with permissive\n    // SELinux, treat the LSM hooks as present for the effect of lowering the\n    // perf_event_paranoid sysctl. The sysprop is reused for pragmatic reasons,\n    // as there no existing way for init rules to check for permissive boot at\n    // the time of writing.\n    if (ALLOW_PERMISSIVE_SELINUX) {\n        if (!security_getenforce()) {\n            LOG(INFO) << \"Permissive SELinux boot, forcing sys.init.perf_lsm_hooks to 1.\";\n            SetProperty(\"sys.init.perf_lsm_hooks\", \"1\");\n            return {};\n        }\n    }\n\n    // Use a trivial event that will be configured, but not started.\n    struct perf_event_attr pe = {\n            .type = PERF_TYPE_SOFTWARE,\n            .size = sizeof(struct perf_event_attr),\n            .config = PERF_COUNT_SW_TASK_CLOCK,\n            .disabled = 1,\n            .exclude_kernel = 1,\n    };\n\n    // Open the above event targeting cpu 0. (EINTR not possible.)\n    unique_fd fd(static_cast<int>(syscall(__NR_perf_event_open, &pe, /*pid=*/-1,\n                                          /*cpu=*/0,\n                                          /*group_fd=*/-1, /*flags=*/0)));\n    if (fd == -1) {\n        PLOG(ERROR) << \"Unexpected perf_event_open error\";\n        return {};\n    }\n\n    int ioctl_ret = ioctl(fd.get(), PERF_EVENT_IOC_RESET);\n    if (ioctl_ret != -1) {\n        // Success implies that the kernel doesn't have the hooks.\n        return {};\n    } else if (errno != EACCES) {\n        PLOG(ERROR) << \"Unexpected perf_event ioctl error\";\n        return {};\n    }\n\n    // Conclude that the SELinux hooks are present.\n    SetProperty(\"sys.init.perf_lsm_hooks\", \"1\");\n    return {};\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/security.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_SECURITY_H\n#define _INIT_SECURITY_H\n\n#include <string>\n#include <vector>\n\n#include \"builtin_arguments.h\"\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\nResult<void> SetMmapRndBitsAction(const BuiltinArguments&);\nResult<void> SetKptrRestrictAction(const BuiltinArguments&);\nResult<void> TestPerfEventSelinuxAction(const BuiltinArguments&);\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/selabel.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"selabel.h\"\n\n#include <selinux/android.h>\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nselabel_handle* sehandle = nullptr;\n}\n\n// selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache\n// its value.  selinux_android_restorecon() also needs an sehandle for file context look up.  It\n// will create and store its own copy, but selinux_android_set_sehandle() can be used to provide\n// one, thus eliminating an extra call to selinux_android_file_context_handle().\nvoid SelabelInitialize() {\n    sehandle = selinux_android_file_context_handle();\n    selinux_android_set_sehandle(sehandle);\n}\n\n// A C++ wrapper around selabel_lookup() using the cached sehandle.\n// If sehandle is null, this returns success with an empty context.\nbool SelabelLookupFileContext(const std::string& key, int type, std::string* result) {\n    result->clear();\n\n    if (!sehandle) return true;\n\n    char* context;\n    if (selabel_lookup(sehandle, &context, key.c_str(), type) != 0) {\n        return false;\n    }\n    *result = context;\n    free(context);\n    return true;\n}\n\n// A C++ wrapper around selabel_lookup_best_match() using the cached sehandle.\n// If sehandle is null, this returns success with an empty context.\nbool SelabelLookupFileContextBestMatch(const std::string& key,\n                                       const std::vector<std::string>& aliases, int type,\n                                       std::string* result) {\n    result->clear();\n\n    if (!sehandle) return true;\n\n    std::vector<const char*> c_aliases;\n    for (const auto& alias : aliases) {\n        c_aliases.emplace_back(alias.c_str());\n    }\n    c_aliases.emplace_back(nullptr);\n\n    char* context;\n    if (selabel_lookup_best_match(sehandle, &context, key.c_str(), &c_aliases[0], type) != 0) {\n        return false;\n    }\n    *result = context;\n    free(context);\n    return true;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/selabel.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\nnamespace android {\nnamespace init {\n\nvoid SelabelInitialize();\nbool SelabelLookupFileContext(const std::string& key, int type, std::string* result);\nbool SelabelLookupFileContextBestMatch(const std::string& key,\n                                       const std::vector<std::string>& aliases, int type,\n                                       std::string* result);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/selinux.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// This file contains the functions that initialize SELinux during boot as well as helper functions\n// for SELinux operation for init.\n\n// When the system boots, there is no SEPolicy present and init is running in the kernel domain.\n// Init loads the SEPolicy from the file system, restores the context of /system/bin/init based on\n// this SEPolicy, and finally exec()'s itself to run in the proper domain.\n\n// The SEPolicy on Android comes in two variants: monolithic and split.\n\n// The monolithic policy variant is for legacy non-treble devices that contain a single SEPolicy\n// file located at /sepolicy and is directly loaded into the kernel SELinux subsystem.\n\n// The split policy is for supporting treble devices.  It splits the SEPolicy across files on\n// /system/etc/selinux (the 'plat' portion of the policy) and /vendor/etc/selinux (the 'vendor'\n// portion of the policy).  This is necessary to allow the system image to be updated independently\n// of the vendor image, while maintaining contributions from both partitions in the SEPolicy.  This\n// is especially important for VTS testing, where the SEPolicy on the Google System Image may not be\n// identical to the system image shipped on a vendor's device.\n\n// The split SEPolicy is loaded as described below:\n// 1) There is a precompiled SEPolicy located at either /vendor/etc/selinux/precompiled_sepolicy or\n//    /odm/etc/selinux/precompiled_sepolicy if odm parition is present.  Stored along with this file\n//    are the sha256 hashes of the parts of the SEPolicy on /system, /system_ext and /product that\n//    were used to compile this precompiled policy.  The system partition contains a similar sha256\n//    of the parts of the SEPolicy that it currently contains.  Symmetrically, system_ext and\n//    product paritition contain sha256 hashes of their SEPolicy.  The init loads this\n//    precompiled_sepolicy directly if and only if the hashes along with the precompiled SEPolicy on\n//    /vendor or /odm match the hashes for system, system_ext and product SEPolicy, respectively.\n// 2) If these hashes do not match, then either /system or /system_ext or /product (or some of them)\n//    have been updated out of sync with /vendor (or /odm if it is present) and the init needs to\n//    compile the SEPolicy.  /system contains the SEPolicy compiler, secilc, and it is used by the\n//    OpenSplitPolicy() function below to compile the SEPolicy to a temp directory and load it.\n//    That function contains even more documentation with the specific implementation details of how\n//    the SEPolicy is compiled if needed.\n\n#include \"selinux.h\"\n\n#include <android/api-level.h>\n#include <fcntl.h>\n#include <linux/audit.h>\n#include <linux/netlink.h>\n#include <stdlib.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/result.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <android/avf_cc_flags.h>\n#include <fs_avb/fs_avb.h>\n#include <fs_mgr.h>\n#include <fs_mgr_overlayfs.h>\n#include <genfslabelsversion.h>\n#include <libgsi/libgsi.h>\n#include <libsnapshot/snapshot.h>\n#include <selinux/android.h>\n\n#include \"block_dev_initializer.h\"\n#include \"debug_ramdisk.h\"\n#include \"reboot_utils.h\"\n#include \"second_stage_resources.h\"\n#include \"snapuserd_transition.h\"\n#include \"util.h\"\n\nusing namespace std::string_literals;\n\nusing android::base::ParseInt;\nusing android::base::Timer;\nusing android::base::unique_fd;\nusing android::fs_mgr::AvbHandle;\nusing android::snapshot::SnapshotManager;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nenum EnforcingStatus { SELINUX_PERMISSIVE, SELINUX_ENFORCING };\n\nEnforcingStatus StatusFromProperty() {\n    std::string value;\n    if (android::fs_mgr::GetKernelCmdline(\"androidboot.selinux\", &value) && value == \"permissive\") {\n        return SELINUX_PERMISSIVE;\n    }\n    if (android::fs_mgr::GetBootconfig(\"androidboot.selinux\", &value) && value == \"permissive\") {\n        return SELINUX_PERMISSIVE;\n    }\n    return SELINUX_ENFORCING;\n}\n\nbool IsEnforcing() {\n    if (ALLOW_PERMISSIVE_SELINUX) {\n        return StatusFromProperty() == SELINUX_ENFORCING;\n    }\n    return true;\n}\n\nbool ReadFirstLine(const char* file, std::string* line) {\n    line->clear();\n\n    std::string contents;\n    if (!android::base::ReadFileToString(file, &contents, true /* follow symlinks */)) {\n        return false;\n    }\n    std::istringstream in(contents);\n    std::getline(in, *line);\n    return true;\n}\n\nResult<std::string> FindPrecompiledSplitPolicy() {\n    std::string precompiled_sepolicy;\n    // If there is an odm partition, precompiled_sepolicy will be in\n    // odm/etc/selinux. Otherwise it will be in vendor/etc/selinux.\n    static constexpr const char vendor_precompiled_sepolicy[] =\n            \"/vendor/etc/selinux/precompiled_sepolicy\";\n    static constexpr const char odm_precompiled_sepolicy[] =\n            \"/odm/etc/selinux/precompiled_sepolicy\";\n    if (access(odm_precompiled_sepolicy, R_OK) == 0) {\n        precompiled_sepolicy = odm_precompiled_sepolicy;\n    } else if (access(vendor_precompiled_sepolicy, R_OK) == 0) {\n        precompiled_sepolicy = vendor_precompiled_sepolicy;\n    } else {\n        return ErrnoError() << \"No precompiled sepolicy at \" << vendor_precompiled_sepolicy;\n    }\n\n    // Use precompiled sepolicy only when all corresponding hashes are equal.\n    std::vector<std::pair<std::string, std::string>> sepolicy_hashes{\n            {\"/system/etc/selinux/plat_sepolicy_and_mapping.sha256\",\n             precompiled_sepolicy + \".plat_sepolicy_and_mapping.sha256\"},\n            {\"/system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256\",\n             precompiled_sepolicy + \".system_ext_sepolicy_and_mapping.sha256\"},\n            {\"/product/etc/selinux/product_sepolicy_and_mapping.sha256\",\n             precompiled_sepolicy + \".product_sepolicy_and_mapping.sha256\"},\n    };\n\n    for (const auto& [actual_id_path, precompiled_id_path] : sepolicy_hashes) {\n        // Both of them should exist or both of them shouldn't exist.\n        if (access(actual_id_path.c_str(), R_OK) != 0) {\n            if (access(precompiled_id_path.c_str(), R_OK) == 0) {\n                return Error() << precompiled_id_path << \" exists but \" << actual_id_path\n                               << \" doesn't\";\n            }\n            continue;\n        }\n\n        std::string actual_id;\n        if (!ReadFirstLine(actual_id_path.c_str(), &actual_id)) {\n            return ErrnoError() << \"Failed to read \" << actual_id_path;\n        }\n\n        std::string precompiled_id;\n        if (!ReadFirstLine(precompiled_id_path.c_str(), &precompiled_id)) {\n            return ErrnoError() << \"Failed to read \" << precompiled_id_path;\n        }\n\n        if (actual_id.empty() || actual_id != precompiled_id) {\n            return Error() << actual_id_path << \" and \" << precompiled_id_path << \" differ\";\n        }\n    }\n\n    return precompiled_sepolicy;\n}\n\nbool GetVendorMappingVersion(std::string* plat_vers) {\n    if (!ReadFirstLine(\"/vendor/etc/selinux/plat_sepolicy_vers.txt\", plat_vers)) {\n        PLOG(ERROR) << \"Failed to read /vendor/etc/selinux/plat_sepolicy_vers.txt\";\n        return false;\n    }\n    if (plat_vers->empty()) {\n        LOG(ERROR) << \"No version present in plat_sepolicy_vers.txt\";\n        return false;\n    }\n    return true;\n}\n\nconstexpr const char plat_policy_cil_file[] = \"/system/etc/selinux/plat_sepolicy.cil\";\n\nbool IsSplitPolicyDevice() {\n    return access(plat_policy_cil_file, R_OK) != -1;\n}\n\nstd::optional<const char*> GetUserdebugPlatformPolicyFile() {\n    // See if we need to load userdebug_plat_sepolicy.cil instead of plat_sepolicy.cil.\n    const char* force_debuggable_env = getenv(\"INIT_FORCE_DEBUGGABLE\");\n    if (force_debuggable_env && \"true\"s == force_debuggable_env && AvbHandle::IsDeviceUnlocked()) {\n        const std::vector<const char*> debug_policy_candidates = {\n#if INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT == 1\n            \"/system_ext/etc/selinux/userdebug_plat_sepolicy.cil\",\n#endif\n            kDebugRamdiskSEPolicy,\n        };\n        for (const char* debug_policy : debug_policy_candidates) {\n            if (access(debug_policy, F_OK) == 0) {\n                return debug_policy;\n            }\n        }\n    }\n    return std::nullopt;\n}\n\nstruct PolicyFile {\n    unique_fd fd;\n    std::string path;\n};\n\nbool OpenSplitPolicy(PolicyFile* policy_file) {\n    // IMPLEMENTATION NOTE: Split policy consists of three or more CIL files:\n    // * platform -- policy needed due to logic contained in the system image,\n    // * vendor -- policy needed due to logic contained in the vendor image,\n    // * mapping -- mapping policy which helps preserve forward-compatibility of non-platform policy\n    //   with newer versions of platform policy.\n    // * (optional) policy needed due to logic on product, system_ext, or odm images.\n    // secilc is invoked to compile the above three policy files into a single monolithic policy\n    // file. This file is then loaded into the kernel.\n\n    const auto userdebug_plat_sepolicy = GetUserdebugPlatformPolicyFile();\n    const bool use_userdebug_policy = userdebug_plat_sepolicy.has_value();\n    if (use_userdebug_policy) {\n        LOG(INFO) << \"Using userdebug system sepolicy \" << *userdebug_plat_sepolicy;\n    }\n\n    // Load precompiled policy from vendor image, if a matching policy is found there. The policy\n    // must match the platform policy on the system image.\n    // use_userdebug_policy requires compiling sepolicy with userdebug_plat_sepolicy.cil.\n    // Thus it cannot use the precompiled policy from vendor image.\n    if (!use_userdebug_policy) {\n        if (auto res = FindPrecompiledSplitPolicy(); res.ok()) {\n            unique_fd fd(open(res->c_str(), O_RDONLY | O_CLOEXEC | O_BINARY));\n            if (fd != -1) {\n                policy_file->fd = std::move(fd);\n                policy_file->path = std::move(*res);\n                return true;\n            }\n        } else {\n            LOG(INFO) << res.error();\n        }\n    }\n    // No suitable precompiled policy could be loaded\n\n    LOG(INFO) << \"Compiling SELinux policy\";\n\n    // We store the output of the compilation on /dev because this is the most convenient tmpfs\n    // storage mount available this early in the boot sequence.\n    char compiled_sepolicy[] = \"/dev/sepolicy.XXXXXX\";\n    unique_fd compiled_sepolicy_fd(mkostemp(compiled_sepolicy, O_CLOEXEC));\n    if (compiled_sepolicy_fd < 0) {\n        PLOG(ERROR) << \"Failed to create temporary file \" << compiled_sepolicy;\n        return false;\n    }\n\n    // Determine which mapping file to include\n    std::string vend_plat_vers;\n    if (!GetVendorMappingVersion(&vend_plat_vers)) {\n        return false;\n    }\n    std::string plat_mapping_file(\"/system/etc/selinux/mapping/\" + vend_plat_vers + \".cil\");\n\n    std::string plat_compat_cil_file(\"/system/etc/selinux/mapping/\" + vend_plat_vers +\n                                     \".compat.cil\");\n    if (access(plat_compat_cil_file.c_str(), F_OK) == -1) {\n        plat_compat_cil_file.clear();\n    }\n\n    std::string system_ext_policy_cil_file(\"/system_ext/etc/selinux/system_ext_sepolicy.cil\");\n    if (access(system_ext_policy_cil_file.c_str(), F_OK) == -1) {\n        system_ext_policy_cil_file.clear();\n    }\n\n    std::string system_ext_mapping_file(\"/system_ext/etc/selinux/mapping/\" + vend_plat_vers +\n                                        \".cil\");\n    if (access(system_ext_mapping_file.c_str(), F_OK) == -1) {\n        system_ext_mapping_file.clear();\n    }\n\n    std::string system_ext_compat_cil_file(\"/system_ext/etc/selinux/mapping/\" + vend_plat_vers +\n                                           \".compat.cil\");\n    if (access(system_ext_compat_cil_file.c_str(), F_OK) == -1) {\n        system_ext_compat_cil_file.clear();\n    }\n\n    std::string product_policy_cil_file(\"/product/etc/selinux/product_sepolicy.cil\");\n    if (access(product_policy_cil_file.c_str(), F_OK) == -1) {\n        product_policy_cil_file.clear();\n    }\n\n    std::string product_mapping_file(\"/product/etc/selinux/mapping/\" + vend_plat_vers + \".cil\");\n    if (access(product_mapping_file.c_str(), F_OK) == -1) {\n        product_mapping_file.clear();\n    }\n\n    std::string vendor_policy_cil_file(\"/vendor/etc/selinux/vendor_sepolicy.cil\");\n    if (access(vendor_policy_cil_file.c_str(), F_OK) == -1) {\n        LOG(ERROR) << \"Missing \" << vendor_policy_cil_file;\n        return false;\n    }\n\n    std::string plat_pub_versioned_cil_file(\"/vendor/etc/selinux/plat_pub_versioned.cil\");\n    if (access(plat_pub_versioned_cil_file.c_str(), F_OK) == -1) {\n        LOG(ERROR) << \"Missing \" << plat_pub_versioned_cil_file;\n        return false;\n    }\n\n    // odm_sepolicy.cil is default but optional.\n    std::string odm_policy_cil_file(\"/odm/etc/selinux/odm_sepolicy.cil\");\n    if (access(odm_policy_cil_file.c_str(), F_OK) == -1) {\n        odm_policy_cil_file.clear();\n    }\n    const std::string version_as_string = std::to_string(SEPOLICY_VERSION);\n\n    std::vector<std::string> genfs_cil_files;\n\n    int vendor_genfs_version = get_genfs_labels_version();\n    std::string genfs_cil_file =\n            std::format(\"/system/etc/selinux/plat_sepolicy_genfs_{}.cil\", vendor_genfs_version);\n    if (access(genfs_cil_file.c_str(), F_OK) != 0) {\n        LOG(INFO) << \"Missing \" << genfs_cil_file << \"; skipping\";\n        genfs_cil_file.clear();\n    } else {\n        LOG(INFO) << \"Using \" << genfs_cil_file << \" for genfs labels\";\n    }\n\n    // clang-format off\n    std::vector<const char*> compile_args {\n        \"/system/bin/secilc\",\n        use_userdebug_policy ? *userdebug_plat_sepolicy : plat_policy_cil_file,\n        \"-m\", \"-M\", \"true\", \"-G\", \"-N\",\n        \"-c\", version_as_string.c_str(),\n        plat_mapping_file.c_str(),\n        \"-o\", compiled_sepolicy,\n        // We don't care about file_contexts output by the compiler\n        \"-f\", \"/sys/fs/selinux/null\",  // /dev/null is not yet available\n    };\n    // clang-format on\n\n    if (!plat_compat_cil_file.empty()) {\n        compile_args.push_back(plat_compat_cil_file.c_str());\n    }\n    if (!system_ext_policy_cil_file.empty()) {\n        compile_args.push_back(system_ext_policy_cil_file.c_str());\n    }\n    if (!system_ext_mapping_file.empty()) {\n        compile_args.push_back(system_ext_mapping_file.c_str());\n    }\n    if (!system_ext_compat_cil_file.empty()) {\n        compile_args.push_back(system_ext_compat_cil_file.c_str());\n    }\n    if (!product_policy_cil_file.empty()) {\n        compile_args.push_back(product_policy_cil_file.c_str());\n    }\n    if (!product_mapping_file.empty()) {\n        compile_args.push_back(product_mapping_file.c_str());\n    }\n    if (!plat_pub_versioned_cil_file.empty()) {\n        compile_args.push_back(plat_pub_versioned_cil_file.c_str());\n    }\n    if (!vendor_policy_cil_file.empty()) {\n        compile_args.push_back(vendor_policy_cil_file.c_str());\n    }\n    if (!odm_policy_cil_file.empty()) {\n        compile_args.push_back(odm_policy_cil_file.c_str());\n    }\n    if (!genfs_cil_file.empty()) {\n        compile_args.push_back(genfs_cil_file.c_str());\n    }\n    compile_args.push_back(nullptr);\n\n    if (!ForkExecveAndWaitForCompletion(compile_args[0], (char**)compile_args.data())) {\n        unlink(compiled_sepolicy);\n        return false;\n    }\n    unlink(compiled_sepolicy);\n\n    policy_file->fd = std::move(compiled_sepolicy_fd);\n    policy_file->path = compiled_sepolicy;\n    return true;\n}\n\nbool OpenMonolithicPolicy(PolicyFile* policy_file) {\n    static constexpr char kSepolicyFile[] = \"/sepolicy\";\n\n    LOG(INFO) << \"Opening SELinux policy from monolithic file \" << kSepolicyFile;\n    policy_file->fd.reset(open(kSepolicyFile, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));\n    if (policy_file->fd < 0) {\n        PLOG(ERROR) << \"Failed to open monolithic SELinux policy\";\n        return false;\n    }\n    policy_file->path = kSepolicyFile;\n    return true;\n}\n\nvoid ReadPolicy(std::string* policy) {\n    PolicyFile policy_file;\n\n    bool ok = IsSplitPolicyDevice() ? OpenSplitPolicy(&policy_file)\n                                    : OpenMonolithicPolicy(&policy_file);\n    if (!ok) {\n        LOG(FATAL) << \"Unable to open SELinux policy\";\n    }\n\n    if (!android::base::ReadFdToString(policy_file.fd, policy)) {\n        PLOG(FATAL) << \"Failed to read policy file: \" << policy_file.path;\n    }\n}\n\nvoid SelinuxSetEnforcement() {\n    bool kernel_enforcing = (security_getenforce() == 1);\n    bool is_enforcing = IsEnforcing();\n    if (kernel_enforcing != is_enforcing) {\n        if (security_setenforce(is_enforcing)) {\n            PLOG(FATAL) << \"security_setenforce(\" << (is_enforcing ? \"true\" : \"false\")\n                        << \") failed\";\n        }\n    }\n}\n\nconstexpr size_t kKlogMessageSize = 1024;\n\nvoid SelinuxAvcLog(char* buf) {\n    struct NetlinkMessage {\n        nlmsghdr hdr;\n        char buf[kKlogMessageSize];\n    } request = {};\n\n    request.hdr.nlmsg_flags = NLM_F_REQUEST;\n    request.hdr.nlmsg_type = AUDIT_USER_AVC;\n    request.hdr.nlmsg_len = sizeof(request);\n    strlcpy(request.buf, buf, sizeof(request.buf));\n\n    auto fd = unique_fd{socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT)};\n    if (!fd.ok()) {\n        return;\n    }\n\n    TEMP_FAILURE_RETRY(send(fd.get(), &request, sizeof(request), 0));\n}\n\nint RestoreconIfExists(const char* path, unsigned int flags) {\n    if (access(path, F_OK) != 0 && errno == ENOENT) {\n        // Avoid error message for path that is expected to not always exist.\n        return 0;\n    }\n    return selinux_android_restorecon(path, flags);\n}\n\n}  // namespace\n\nvoid SelinuxRestoreContext() {\n    LOG(INFO) << \"Running restorecon...\";\n    selinux_android_restorecon(\"/dev\", 0);\n    selinux_android_restorecon(\"/dev/console\", 0);\n    selinux_android_restorecon(\"/dev/kmsg\", 0);\n    if constexpr (WORLD_WRITABLE_KMSG) {\n        selinux_android_restorecon(\"/dev/kmsg_debug\", 0);\n    }\n    selinux_android_restorecon(\"/dev/null\", 0);\n    selinux_android_restorecon(\"/dev/ptmx\", 0);\n    selinux_android_restorecon(\"/dev/socket\", 0);\n    selinux_android_restorecon(\"/dev/random\", 0);\n    selinux_android_restorecon(\"/dev/urandom\", 0);\n    selinux_android_restorecon(\"/dev/__properties__\", 0);\n\n    selinux_android_restorecon(\"/dev/block\", SELINUX_ANDROID_RESTORECON_RECURSE);\n    selinux_android_restorecon(\"/dev/dm-user\", SELINUX_ANDROID_RESTORECON_RECURSE);\n    selinux_android_restorecon(\"/dev/device-mapper\", 0);\n\n    selinux_android_restorecon(\"/apex\", 0);\n    selinux_android_restorecon(\"/bootstrap-apex\", 0);\n    selinux_android_restorecon(\"/linkerconfig\", 0);\n\n    // adb remount, snapshot-based updates, and DSUs all create files during\n    // first-stage init.\n    RestoreconIfExists(SnapshotManager::GetGlobalRollbackIndicatorPath().c_str(), 0);\n    RestoreconIfExists(\"/metadata/gsi\",\n                       SELINUX_ANDROID_RESTORECON_RECURSE | SELINUX_ANDROID_RESTORECON_SKIP_SEHASH);\n}\n\nint SelinuxKlogCallback(int type, const char* fmt, ...) {\n    android::base::LogSeverity severity = android::base::ERROR;\n    if (type == SELINUX_WARNING) {\n        severity = android::base::WARNING;\n    } else if (type == SELINUX_INFO) {\n        severity = android::base::INFO;\n    }\n    char buf[kKlogMessageSize];\n    va_list ap;\n    va_start(ap, fmt);\n    int length_written = vsnprintf(buf, sizeof(buf), fmt, ap);\n    va_end(ap);\n    if (length_written <= 0) {\n        return 0;\n    }\n\n    // libselinux log messages usually contain a new line character, while\n    // Android LOG() does not expect it. Remove it to avoid empty lines in\n    // the log buffers.\n    size_t str_len = strlen(buf);\n    if (buf[str_len - 1] == '\\n') {\n        buf[str_len - 1] = '\\0';\n    }\n\n    if (type == SELINUX_AVC) {\n        SelinuxAvcLog(buf);\n    } else {\n        android::base::KernelLogger(android::base::MAIN, severity, \"selinux\", nullptr, 0, buf);\n    }\n    return 0;\n}\n\nvoid SelinuxSetupKernelLogging() {\n    selinux_callback cb;\n    cb.func_log = SelinuxKlogCallback;\n    selinux_set_callback(SELINUX_CB_LOG, cb);\n}\n\nint SelinuxGetVendorAndroidVersion() {\n    if (IsMicrodroid()) {\n        // As of now Microdroid doesn't have any vendor code.\n        return __ANDROID_API_FUTURE__;\n    }\n    static int vendor_android_version = [] {\n        if (!IsSplitPolicyDevice()) {\n            // If this device does not split sepolicy files, it's not a Treble device and therefore,\n            // we assume it's always on the latest platform.\n            return __ANDROID_API_FUTURE__;\n        }\n\n        std::string version;\n        if (!GetVendorMappingVersion(&version)) {\n            LOG(FATAL) << \"Could not read vendor SELinux version\";\n        }\n\n        int major_version;\n        std::string major_version_str(version, 0, version.find('.'));\n        if (!ParseInt(major_version_str, &major_version)) {\n            PLOG(FATAL) << \"Failed to parse the vendor sepolicy major version \"\n                        << major_version_str;\n        }\n\n        return major_version;\n    }();\n    return vendor_android_version;\n}\n\n// This is for R system.img/system_ext.img to work on old vendor.img as system_ext.img\n// is introduced in R. We mount system_ext in second stage init because the first-stage\n// init in boot.img won't be updated in the system-only OTA scenario.\nvoid MountMissingSystemPartitions() {\n    android::fs_mgr::Fstab fstab;\n    if (!ReadDefaultFstab(&fstab)) {\n        LOG(ERROR) << \"Could not read default fstab\";\n    }\n\n    android::fs_mgr::Fstab mounts;\n    if (!ReadFstabFromFile(\"/proc/mounts\", &mounts)) {\n        LOG(ERROR) << \"Could not read /proc/mounts\";\n    }\n\n    static const std::vector<std::string> kPartitionNames = {\"system_ext\", \"product\"};\n\n    android::fs_mgr::Fstab extra_fstab;\n    for (const auto& name : kPartitionNames) {\n        if (GetEntryForMountPoint(&mounts, \"/\"s + name)) {\n            // The partition is already mounted.\n            continue;\n        }\n\n        auto system_entries = GetEntriesForMountPoint(&fstab, \"/system\");\n        for (auto& system_entry : system_entries) {\n            if (!system_entry) {\n                LOG(ERROR) << \"Could not find mount entry for /system\";\n                break;\n            }\n            if (!system_entry->fs_mgr_flags.logical) {\n                LOG(INFO) << \"Skipping mount of \" << name << \", system is not dynamic.\";\n                break;\n            }\n\n            auto entry = *system_entry;\n            auto partition_name = name + fs_mgr_get_slot_suffix();\n            auto replace_name = \"system\"s + fs_mgr_get_slot_suffix();\n\n            entry.mount_point = \"/\"s + name;\n            entry.blk_device =\n                android::base::StringReplace(entry.blk_device, replace_name, partition_name, false);\n            if (!fs_mgr_update_logical_partition(&entry)) {\n                LOG(ERROR) << \"Could not update logical partition\";\n                continue;\n            }\n\n            extra_fstab.emplace_back(std::move(entry));\n        }\n    }\n\n    SkipMountingPartitions(&extra_fstab, true /* verbose */);\n    if (extra_fstab.empty()) {\n        return;\n    }\n\n    BlockDevInitializer block_dev_init;\n    for (auto& entry : extra_fstab) {\n        if (access(entry.blk_device.c_str(), F_OK) != 0) {\n            auto block_dev = android::base::Basename(entry.blk_device);\n            if (!block_dev_init.InitDmDevice(block_dev)) {\n                LOG(ERROR) << \"Failed to find device-mapper node: \" << block_dev;\n                continue;\n            }\n        }\n        if (fs_mgr_do_mount_one(entry)) {\n            LOG(ERROR) << \"Could not mount \" << entry.mount_point;\n        }\n    }\n}\n\nstatic void LoadSelinuxPolicy(std::string& policy) {\n    LOG(INFO) << \"Loading SELinux policy\";\n\n    set_selinuxmnt(\"/sys/fs/selinux\");\n    if (security_load_policy(policy.data(), policy.size()) < 0) {\n        PLOG(FATAL) << \"SELinux:  Could not load policy\";\n    }\n}\n\n// Encapsulates steps to load SELinux policy in Microdroid.\n// So far the process is very straightforward - just load the precompiled policy from /system.\nvoid LoadSelinuxPolicyMicrodroid() {\n    constexpr const char kMicrodroidPrecompiledSepolicy[] =\n            \"/system/etc/selinux/microdroid_precompiled_sepolicy\";\n\n    LOG(INFO) << \"Opening SELinux policy from \" << kMicrodroidPrecompiledSepolicy;\n    unique_fd policy_fd(open(kMicrodroidPrecompiledSepolicy, O_RDONLY | O_CLOEXEC | O_NOFOLLOW));\n    if (policy_fd < 0) {\n        PLOG(FATAL) << \"Failed to open \" << kMicrodroidPrecompiledSepolicy;\n    }\n\n    std::string policy;\n    if (!android::base::ReadFdToString(policy_fd, &policy)) {\n        PLOG(FATAL) << \"Failed to read policy file: \" << kMicrodroidPrecompiledSepolicy;\n    }\n\n    LoadSelinuxPolicy(policy);\n}\n\n// The SELinux setup process is carefully orchestrated around snapuserd. Policy\n// must be loaded off dynamic partitions, and during an OTA, those partitions\n// cannot be read without snapuserd. But, with kernel-privileged snapuserd\n// running, loading the policy will immediately trigger audits.\n//\n// We use a five-step process to address this:\n//  (1) Read the policy into a string, with snapuserd running.\n//  (2) Rewrite the snapshot device-mapper tables, to generate new dm-user\n//      devices and to flush I/O.\n//  (3) Kill snapuserd, which no longer has any dm-user devices to attach to.\n//  (4) Load the sepolicy and issue critical restorecons in /dev, carefully\n//      avoiding anything that would read from /system.\n//  (5) Re-launch snapuserd and attach it to the dm-user devices from step (2).\n//\n// After this sequence, it is safe to enable enforcing mode and continue booting.\nvoid LoadSelinuxPolicyAndroid() {\n    MountMissingSystemPartitions();\n\n    LOG(INFO) << \"Opening SELinux policy\";\n\n    // Read the policy before potentially killing snapuserd.\n    std::string policy;\n    ReadPolicy(&policy);\n\n    auto snapuserd_helper = SnapuserdSelinuxHelper::CreateIfNeeded();\n    if (snapuserd_helper) {\n        // Kill the old snapused to avoid audit messages. After this we cannot read from /system\n        // (or other dynamic partitions) until we call FinishTransition().\n        snapuserd_helper->StartTransition();\n    }\n\n    LoadSelinuxPolicy(policy);\n\n    if (snapuserd_helper) {\n        // Before enforcing, finish the pending snapuserd transition.\n        snapuserd_helper->FinishTransition();\n        snapuserd_helper = nullptr;\n    }\n}\n\n#ifdef ALLOW_REMOUNT_OVERLAYS\nvoid SetupOverlays() {\n    if (android::fs_mgr::use_override_creds) return;\n\n    bool has_overlays = false;\n    std::string contents;\n    auto result = android::base::ReadFileToString(\"/proc/mounts\", &contents, true);\n\n    auto lines = android::base::Split(contents, \"\\n\");\n    for (auto const& line : lines)\n        if (android::base::StartsWith(line, \"overlay\")) {\n            has_overlays = true;\n            break;\n        }\n\n    if (!has_overlays) return;\n\n    // After adb remount, we mount all r/o volumes with overlayfs to allow writing.\n    // However, since overlayfs performs its file operations in the context of the\n    // mounting process, this will not work as is - init is in the kernel domain in\n    // first stage, which has very limited permissions.\n\n    // In order to fix this, we need to unmount remount all these volumes from a process\n    // with sufficient privileges to be able to perform these operations. The\n    // overlay_remounter domain has those privileges on debuggable devices.\n    // We will call overlay_remounter which will do the unmounts/mounts.\n    // But for that to work, the volumes must not be busy, so we need to copy\n    // overlay_remounter from system to a ramdisk and run it from there.\n\n    const char* kOverlayRemounter = \"overlay_remounter\";\n    auto or_src = std::filesystem::path(\"/system/xbin/\") / kOverlayRemounter;\n    auto or_dest = std::filesystem::path(kSecondStageRes) / kOverlayRemounter;\n    std::error_code ec;\n    std::filesystem::copy(or_src, or_dest, ec);\n    if (ec) {\n        LOG(FATAL) << \"Failed to copy \" << or_src << \" to \" << or_dest << \" \" << ec.message();\n    }\n\n    if (selinux_android_restorecon(or_dest.c_str(), 0) == -1) {\n        PLOG(FATAL) << \"restorecon of \" << or_dest << \" failed\";\n    }\n    auto dest = unique_fd(open(or_dest.c_str(), O_RDONLY | O_CLOEXEC));\n    if (dest.get() == -1) {\n        PLOG(FATAL) << \"Failed to reopen \" << or_dest;\n    }\n    if (unlink(or_dest.c_str()) == -1) {\n        PLOG(FATAL) << \"Failed to unlink \" << or_dest;\n    }\n    const char* args[] = {or_dest.c_str(), nullptr};\n    fexecve(dest.get(), const_cast<char**>(args), environ);\n\n    // execv() only returns if an error happened, in which case we\n    // panic and never return from this function.\n    PLOG(FATAL) << \"execv(\\\"\" << or_dest << \"\\\") failed\";\n}\n#else\nvoid SetupOverlays() {}\n#endif\n\nint SetupSelinux(char** argv) {\n    SetStdioToDevNull(argv);\n    InitKernelLogging(argv);\n\n    if (REBOOT_BOOTLOADER_ON_PANIC) {\n        InstallRebootSignalHandlers();\n    }\n\n    boot_clock::time_point start_time = boot_clock::now();\n\n    SelinuxSetupKernelLogging();\n\n    // TODO(b/287206497): refactor into different headers to only include what we need.\n    if (IsMicrodroid()) {\n        LoadSelinuxPolicyMicrodroid();\n    } else {\n        LoadSelinuxPolicyAndroid();\n    }\n\n    SelinuxSetEnforcement();\n\n    if (IsMicrodroid() && android::virtualization::IsOpenDiceChangesFlagEnabled()) {\n        // We run restorecon of /microdroid_resources while we are still in kernel context to avoid\n        // granting init `tmpfs:file relabelfrom` capability.\n        const int flags = SELINUX_ANDROID_RESTORECON_RECURSE;\n        if (selinux_android_restorecon(\"/microdroid_resources\", flags) == -1) {\n            PLOG(FATAL) << \"restorecon of /microdroid_resources failed\";\n        }\n    }\n\n    // We're in the kernel domain and want to transition to the init domain.  File systems that\n    // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here,\n    // but other file systems do.  In particular, this is needed for ramdisks such as the\n    // recovery image for A/B devices.\n    if (selinux_android_restorecon(\"/system/bin/init\", 0) == -1) {\n        PLOG(FATAL) << \"restorecon failed of /system/bin/init failed\";\n    }\n\n    setenv(kEnvSelinuxStartedAt, std::to_string(start_time.time_since_epoch().count()).c_str(), 1);\n\n    // SetupOverlays does not return if overlays exist, instead it execs overlay_remounter\n    // which then execs second stage init\n    SetupOverlays();\n\n    const char* path = \"/system/bin/init\";\n    const char* args[] = {path, \"second_stage\", nullptr};\n    execv(path, const_cast<char**>(args));\n\n    // execv() only returns if an error happened, in which case we\n    // panic and never return from this function.\n    PLOG(FATAL) << \"execv(\\\"\" << path << \"\\\") failed\";\n\n    return 1;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/selinux.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace android {\nnamespace init {\n\n// Initialize SELinux, then exec init to run in the init SELinux context.\nint SetupSelinux(char** argv);\n\n// Restore the proper security context to files and directories on ramdisk, and\n// those that were created before initial sepolicy load.\n// This must happen before /dev is populated by ueventd.\nvoid SelinuxRestoreContext();\n\n// Set up SELinux logging to be written to kmsg, to match init's logging.\nvoid SelinuxSetupKernelLogging();\n\n// Return the Android API level with which the vendor SEPolicy was compiled.\n// Used for version checks such as whether or not vendor_init should be used.\nint SelinuxGetVendorAndroidVersion();\n\nstatic constexpr char kEnvSelinuxStartedAt[] = \"SELINUX_STARTED_AT\";\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"service.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <linux/securebits.h>\n#include <sched.h>\n#include <sys/prctl.h>\n#include <sys/stat.h>\n#include <sys/time.h>\n#include <termios.h>\n#include <unistd.h>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <cutils/android_get_control_file.h>\n#include <cutils/sockets.h>\n#include <processgroup/processgroup.h>\n#include <selinux/selinux.h>\n#include <sys/signalfd.h>\n\n#include <string>\n\n#include \"interprocess_fifo.h\"\n#include \"lmkd_service.h\"\n#include \"service_list.h\"\n#include \"util.h\"\n\n#if defined(__BIONIC__)\n#include <bionic/reserved_signals.h>\n#endif\n\n#ifdef INIT_FULL_SOURCES\n#include <android/api-level.h>\n\n#include \"mount_namespace.h\"\n#include \"reboot_utils.h\"\n#include \"selinux.h\"\n#else\n#include \"host_init_stubs.h\"\n#endif\n\nusing android::base::boot_clock;\nusing android::base::GetBoolProperty;\nusing android::base::GetIntProperty;\nusing android::base::GetProperty;\nusing android::base::Join;\nusing android::base::make_scope_guard;\nusing android::base::SetProperty;\nusing android::base::StartsWith;\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\nusing android::base::WriteStringToFile;\n\nnamespace android {\nnamespace init {\n\nstatic Result<std::string> ComputeContextFromExecutable(const std::string& service_path) {\n    std::string computed_context;\n\n    char* raw_con = nullptr;\n    char* raw_filecon = nullptr;\n\n    if (getcon(&raw_con) == -1) {\n        return Error() << \"Could not get security context\";\n    }\n    std::unique_ptr<char, decltype(&freecon)> mycon(raw_con, freecon);\n\n    if (getfilecon(service_path.c_str(), &raw_filecon) == -1) {\n        return Error() << \"Could not get file context\";\n    }\n    std::unique_ptr<char, decltype(&freecon)> filecon(raw_filecon, freecon);\n\n    char* new_con = nullptr;\n    int rc = security_compute_create(mycon.get(), filecon.get(),\n                                     string_to_security_class(\"process\"), &new_con);\n    if (rc == 0) {\n        computed_context = new_con;\n        free(new_con);\n    }\n    if (rc == 0 && computed_context == mycon.get()) {\n        return Error() << \"File \" << service_path << \"(labeled \\\"\" << filecon.get()\n                       << \"\\\") has incorrect label or no domain transition from \" << mycon.get()\n                       << \" to another SELinux domain defined. Have you configured your \"\n                          \"service correctly? https://source.android.com/security/selinux/\"\n                          \"device-policy#label_new_services_and_address_denials. Note: this \"\n                          \"error shows up even in permissive mode in order to make auditing \"\n                          \"denials possible.\";\n    }\n    if (rc < 0) {\n        return Error() << \"Could not get process context\";\n    }\n    return computed_context;\n}\n\nstatic bool ExpandArgsAndExecv(const std::vector<std::string>& args, bool sigstop) {\n    std::vector<std::string> expanded_args;\n    std::vector<char*> c_strings;\n\n    expanded_args.resize(args.size());\n    c_strings.push_back(const_cast<char*>(args[0].data()));\n    for (std::size_t i = 1; i < args.size(); ++i) {\n        auto expanded_arg = ExpandProps(args[i]);\n        if (!expanded_arg.ok()) {\n            LOG(FATAL) << args[0] << \": cannot expand arguments': \" << expanded_arg.error();\n        }\n        expanded_args[i] = *expanded_arg;\n        c_strings.push_back(expanded_args[i].data());\n    }\n    c_strings.push_back(nullptr);\n\n    if (sigstop) {\n        kill(getpid(), SIGSTOP);\n    }\n\n    return execv(c_strings[0], c_strings.data()) == 0;\n}\n\nunsigned long Service::next_start_order_ = 1;\nbool Service::is_exec_service_running_ = false;\n\nService::Service(const std::string& name, Subcontext* subcontext_for_restart_commands,\n                 const std::string& filename, const std::vector<std::string>& args)\n    : Service(name, 0, std::nullopt, 0, {}, 0, \"\", subcontext_for_restart_commands, filename,\n              args) {}\n\nService::Service(const std::string& name, unsigned flags, std::optional<uid_t> uid, gid_t gid,\n                 const std::vector<gid_t>& supp_gids, int namespace_flags,\n                 const std::string& seclabel, Subcontext* subcontext_for_restart_commands,\n                 const std::string& filename, const std::vector<std::string>& args)\n    : name_(name),\n      classnames_({\"default\"}),\n      flags_(flags),\n      pid_(0),\n      crash_count_(0),\n      proc_attr_{.ioprio_class = IoSchedClass_NONE,\n                 .ioprio_pri = 0,\n                 .parsed_uid = uid,\n                 .gid = gid,\n                 .supp_gids = supp_gids,\n                 .priority = 0},\n      namespaces_{.flags = namespace_flags},\n      seclabel_(seclabel),\n      subcontext_(subcontext_for_restart_commands),\n      onrestart_(false, subcontext_for_restart_commands, \"<Service '\" + name + \"' onrestart>\", 0,\n                 \"onrestart\", {}),\n      oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST),\n      start_order_(0),\n      args_(args),\n      filename_(filename) {}\n\nvoid Service::NotifyStateChange(const std::string& new_state) const {\n    if ((flags_ & SVC_TEMPORARY) != 0) {\n        // Services created by 'exec' are temporary and don't have properties tracking their state.\n        return;\n    }\n\n    std::string prop_name = \"init.svc.\" + name_;\n    SetProperty(prop_name, new_state);\n\n    if (new_state == \"running\") {\n        uint64_t start_ns = time_started_.time_since_epoch().count();\n        std::string boottime_property = \"ro.boottime.\" + name_;\n        if (GetProperty(boottime_property, \"\").empty()) {\n            SetProperty(boottime_property, std::to_string(start_ns));\n        }\n    }\n\n    // init.svc_debug_pid.* properties are only for tests, and should not be used\n    // on device for security checks.\n    std::string pid_property = \"init.svc_debug_pid.\" + name_;\n    if (new_state == \"running\") {\n        SetProperty(pid_property, std::to_string(pid_));\n    } else if (new_state == \"stopped\") {\n        SetProperty(pid_property, \"\");\n    }\n}\n\nvoid Service::KillProcessGroup(int signal) {\n    // Always attempt the process kill if process is still running.\n    // Cgroup clean up routines are idempotent. It's safe to call\n    // killProcessGroup repeatedly. During shutdown, `init` will\n    // call this function to send SIGTERM/SIGKILL to all processes.\n    // These signals must be sent for a successful shutdown.\n    if (!process_cgroup_empty_ || IsRunning()) {\n        LOG(INFO) << \"Sending signal \" << signal << \" to service '\" << name_ << \"' (pid \" << pid_\n                  << \") process group...\";\n        int r;\n        if (signal == SIGTERM) {\n            r = killProcessGroupOnce(uid(), pid_, signal);\n        } else {\n            r = killProcessGroup(uid(), pid_, signal);\n        }\n\n        if (r == 0) process_cgroup_empty_ = true;\n    }\n\n    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {\n        LmkdUnregister(name_, pid_);\n    }\n}\n\nvoid Service::SetProcessAttributesAndCaps(InterprocessFifo setsid_finished) {\n    // Keep capabilites on uid change.\n    if (capabilities_ && uid()) {\n        // If Android is running in a container, some securebits might already\n        // be locked, so don't change those.\n        unsigned long securebits = prctl(PR_GET_SECUREBITS);\n        if (securebits == -1UL) {\n            PLOG(FATAL) << \"prctl(PR_GET_SECUREBITS) failed for \" << name_;\n        }\n        securebits |= SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED;\n        if (prctl(PR_SET_SECUREBITS, securebits) != 0) {\n            PLOG(FATAL) << \"prctl(PR_SET_SECUREBITS) failed for \" << name_;\n        }\n    }\n\n    if (auto result = SetProcessAttributes(proc_attr_, std::move(setsid_finished)); !result.ok()) {\n        LOG(FATAL) << \"cannot set attribute for \" << name_ << \": \" << result.error();\n    }\n\n    if (!seclabel_.empty()) {\n        if (setexeccon(seclabel_.c_str()) < 0) {\n            PLOG(FATAL) << \"cannot setexeccon('\" << seclabel_ << \"') for \" << name_;\n        }\n    }\n\n    if (capabilities_) {\n        if (!SetCapsForExec(*capabilities_)) {\n            LOG(FATAL) << \"cannot set capabilities for \" << name_;\n        }\n    } else if (uid()) {\n        // Inheritable caps can be non-zero when running in a container.\n        if (!DropInheritableCaps()) {\n            LOG(FATAL) << \"cannot drop inheritable caps for \" << name_;\n        }\n    }\n}\n\nvoid Service::Reap(const siginfo_t& siginfo) {\n    if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {\n        KillProcessGroup(SIGKILL);\n    } else {\n        // Legacy behavior from ~2007 until Android R: this else branch did not exist and we did not\n        // kill the process group in this case.\n        if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {\n            // The new behavior in Android R is to kill these process groups in all cases.  The\n            // 'true' parameter instructions KillProcessGroup() to report a warning message where it\n            // detects a difference in behavior has occurred.\n            KillProcessGroup(SIGKILL);\n        }\n    }\n\n    // Remove any socket resources we may have created.\n    for (const auto& socket : sockets_) {\n        if (socket.persist) {\n            continue;\n        }\n        auto path = ANDROID_SOCKET_DIR \"/\" + socket.name;\n        unlink(path.c_str());\n    }\n\n    for (const auto& f : reap_callbacks_) {\n        f(siginfo);\n    }\n\n    if ((siginfo.si_code != CLD_EXITED || siginfo.si_status != 0) && on_failure_reboot_target_) {\n        LOG(ERROR) << \"Service \" << name_\n                   << \" has 'reboot_on_failure' option and failed, shutting down system.\";\n        trigger_shutdown(*on_failure_reboot_target_);\n    }\n\n    if (flags_ & SVC_EXEC) UnSetExec();\n\n    if (name_ == \"zygote\" || name_ == \"zygote64\") {\n        removeAllEmptyProcessGroups();\n    }\n\n    if (flags_ & SVC_TEMPORARY) return;\n\n    pid_ = 0;\n    flags_ &= (~SVC_RUNNING);\n    start_order_ = 0;\n    was_last_exit_ok_ = siginfo.si_code == CLD_EXITED && siginfo.si_status == 0;\n\n    // Oneshot processes go into the disabled state on exit,\n    // except when manually restarted.\n    if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) {\n        flags_ |= SVC_DISABLED;\n    }\n\n    // Disabled and reset processes do not get restarted automatically.\n    if (flags_ & (SVC_DISABLED | SVC_RESET))  {\n        NotifyStateChange(\"stopped\");\n        return;\n    }\n\n#if INIT_FULL_SOURCES\n    static bool is_apex_updatable = true;\n#else\n    static bool is_apex_updatable = false;\n#endif\n    const bool use_default_mount_ns =\n            mount_namespace_.has_value() && *mount_namespace_ == NS_DEFAULT;\n    const bool is_process_updatable = use_default_mount_ns && is_apex_updatable;\n\n#if defined(__BIONIC__) && defined(SEGV_MTEAERR)\n    // As a precaution, we only upgrade a service once per reboot, to limit\n    // the potential impact.\n    //\n    // BIONIC_SIGNAL_ART_PROFILER is a magic value used by deuggerd to signal\n    // that the process crashed with SIGSEGV and SEGV_MTEAERR. This signal will\n    // never be seen otherwise in a crash, because it always gets handled by the\n    // profiling signal handlers in bionic. See also\n    // debuggerd/handler/debuggerd_handler.cpp.\n    bool should_upgrade_mte = siginfo.si_code != CLD_EXITED &&\n                              siginfo.si_status == BIONIC_SIGNAL_ART_PROFILER && !upgraded_mte_;\n\n    if (should_upgrade_mte) {\n        constexpr int kDefaultUpgradeSecs = 60;\n        int secs = GetIntProperty(\"persist.device_config.memory_safety_native.upgrade_secs.default\",\n                                  kDefaultUpgradeSecs);\n        secs = GetIntProperty(\n                \"persist.device_config.memory_safety_native.upgrade_secs.service.\" + name_, secs);\n        if (secs > 0) {\n            LOG(INFO) << \"Upgrading service \" << name_ << \" to sync MTE for \" << secs << \" seconds\";\n            once_environment_vars_.emplace_back(\"BIONIC_MEMTAG_UPGRADE_SECS\", std::to_string(secs));\n            upgraded_mte_ = true;\n        } else {\n            LOG(INFO) << \"Not upgrading service \" << name_ << \" to sync MTE due to device config\";\n        }\n    }\n#endif\n\n    // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,\n    // reboot into bootloader or set crashing property\n    boot_clock::time_point now = boot_clock::now();\n    constexpr const char native_watchdog_reboot_time[] = \"persist.init.svc.last_fatal_reboot_epoch\";\n    uint64_t throttle_window =\n            std::chrono::duration_cast<std::chrono::seconds>(std::chrono::hours(24)).count();\n    if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART) &&\n        !was_last_exit_ok_) {\n        bool boot_completed = GetBoolProperty(\"sys.boot_completed\", false);\n        if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {\n            if (++crash_count_ > 4) {\n                auto exit_reason =\n                        boot_completed\n                                ? \"in \" + std::to_string(fatal_crash_window_.count()) + \" minutes\"\n                                : \"before boot completed\";\n                if (flags_ & SVC_CRITICAL) {\n                    if (!GetBoolProperty(\"init.svc_debug.no_fatal.\" + name_, false)) {\n                        uint64_t epoch_time =\n                                std::chrono::duration_cast<std::chrono::seconds>(\n                                        std::chrono::system_clock::now().time_since_epoch())\n                                        .count();\n                        // Do not reboot again If it was already initiated in the last 24hrs\n                        if (epoch_time - GetIntProperty(native_watchdog_reboot_time, 0) >\n                            throttle_window) {\n                            SetProperty(native_watchdog_reboot_time, std::to_string(epoch_time));\n                            // Aborts into `fatal_reboot_target_'.\n                            SetFatalRebootTarget(fatal_reboot_target_);\n                            LOG(FATAL) << \"critical process '\" << name_ << \"' exited 4 times \"\n                                       << exit_reason;\n                        } else {\n                            LOG(INFO) << \"Reboot already performed in last 24hrs because of crash.\";\n                        }\n                    }\n                } else {\n                    LOG(ERROR) << \"process with updatable components '\" << name_\n                               << \"' exited 4 times \" << exit_reason;\n                    // Notifies update_verifier and apexd\n                    SetProperty(\"sys.init.updatable_crashing_process_name\", name_);\n                    SetProperty(\"sys.init.updatable_crashing\", \"1\");\n                }\n            }\n        } else {\n            time_crashed_ = now;\n            crash_count_ = 1;\n        }\n    }\n\n    flags_ &= (~SVC_RESTART);\n    flags_ |= SVC_RESTARTING;\n\n    // Execute all onrestart commands for this service.\n    onrestart_.ExecuteAllCommands();\n\n    NotifyStateChange(\"restarting\");\n    return;\n}\n\nvoid Service::DumpState() const {\n    LOG(INFO) << \"service \" << name_;\n    LOG(INFO) << \"  class '\" << Join(classnames_, \" \") << \"'\";\n    LOG(INFO) << \"  exec \" << Join(args_, \" \");\n    for (const auto& socket : sockets_) {\n        LOG(INFO) << \"  socket \" << socket.name;\n    }\n    for (const auto& file : files_) {\n        LOG(INFO) << \"  file \" << file.name;\n    }\n}\n\n\nResult<void> Service::ExecStart() {\n    auto reboot_on_failure = make_scope_guard([this] {\n        if (on_failure_reboot_target_) {\n            trigger_shutdown(*on_failure_reboot_target_);\n        }\n    });\n\n    if (is_updatable() && !IsDefaultMountNamespaceReady()) {\n        // Don't delay the service for ExecStart() as the semantic is that\n        // the caller might depend on the side effect of the execution.\n        return Error() << \"Cannot start an updatable service '\" << name_\n                       << \"' before configs from APEXes are all loaded\";\n    }\n\n    flags_ |= SVC_ONESHOT;\n\n    if (auto result = Start(); !result.ok()) {\n        return result;\n    }\n\n    flags_ |= SVC_EXEC;\n    is_exec_service_running_ = true;\n\n    LOG(INFO) << \"SVC_EXEC service '\" << name_ << \"' pid \" << pid_ << \" (uid \" << uid() << \" gid \"\n              << proc_attr_.gid << \"+\" << proc_attr_.supp_gids.size() << \" context \"\n              << (!seclabel_.empty() ? seclabel_ : \"default\") << \") started; waiting...\";\n\n    reboot_on_failure.Disable();\n    return {};\n}\n\nResult<void> Service::CheckConsole() {\n    if (!(flags_ & SVC_CONSOLE)) {\n        return {};\n    }\n\n    // On newer kernels, /dev/console will always exist because\n    // \"console=ttynull\" is hard-coded in CONFIG_CMDLINE. This new boot\n    // property should be set via \"androidboot.serialconsole=0\" to explicitly\n    // disable services requiring the console. For older kernels and boot\n    // images, not setting this at all will fall back to the old behavior\n    if (GetProperty(\"ro.boot.serialconsole\", \"\") == \"0\") {\n        flags_ |= SVC_DISABLED;\n        return {};\n    }\n\n    if (proc_attr_.console.empty()) {\n        proc_attr_.console = \"/dev/\" + GetProperty(\"ro.boot.console\", \"console\");\n    }\n\n    // Make sure that open call succeeds to ensure a console driver is\n    // properly registered for the device node\n    int console_fd = open(proc_attr_.console.c_str(), O_RDWR | O_CLOEXEC);\n    if (console_fd < 0) {\n        flags_ |= SVC_DISABLED;\n        return ErrnoError() << \"Couldn't open console '\" << proc_attr_.console << \"'\";\n    }\n    close(console_fd);\n    return {};\n}\n\n// Configures the memory cgroup properties for the service.\nvoid Service::ConfigureMemcg() {\n    if (swappiness_ != -1) {\n        if (!setProcessGroupSwappiness(uid(), pid_, swappiness_)) {\n            PLOG(ERROR) << \"setProcessGroupSwappiness failed\";\n        }\n    }\n\n    if (soft_limit_in_bytes_ != -1) {\n        if (!setProcessGroupSoftLimit(uid(), pid_, soft_limit_in_bytes_)) {\n            PLOG(ERROR) << \"setProcessGroupSoftLimit failed\";\n        }\n    }\n\n    size_t computed_limit_in_bytes = limit_in_bytes_;\n    if (limit_percent_ != -1) {\n        long page_size = sysconf(_SC_PAGESIZE);\n        long num_pages = sysconf(_SC_PHYS_PAGES);\n        if (page_size > 0 && num_pages > 0) {\n            size_t max_mem = SIZE_MAX;\n            if (size_t(num_pages) < SIZE_MAX / size_t(page_size)) {\n                max_mem = size_t(num_pages) * size_t(page_size);\n            }\n            computed_limit_in_bytes =\n                    std::min(computed_limit_in_bytes, max_mem / 100 * limit_percent_);\n        }\n    }\n\n    if (!limit_property_.empty()) {\n        // This ends up overwriting computed_limit_in_bytes but only if the\n        // property is defined.\n        computed_limit_in_bytes =\n                android::base::GetUintProperty(limit_property_, computed_limit_in_bytes, SIZE_MAX);\n    }\n\n    if (computed_limit_in_bytes != size_t(-1)) {\n        if (!setProcessGroupLimit(uid(), pid_, computed_limit_in_bytes)) {\n            PLOG(ERROR) << \"setProcessGroupLimit failed\";\n        }\n    }\n}\n\n// Enters namespaces, sets environment variables, writes PID files and runs the service executable.\nvoid Service::RunService(const std::vector<Descriptor>& descriptors,\n                         InterprocessFifo cgroups_activated, InterprocessFifo setsid_finished) {\n    if (auto result = EnterNamespaces(namespaces_, name_, mount_namespace_); !result.ok()) {\n        LOG(FATAL) << \"Service '\" << name_ << \"' failed to set up namespaces: \" << result.error();\n    }\n\n    for (const auto& [key, value] : once_environment_vars_) {\n        setenv(key.c_str(), value.c_str(), 1);\n    }\n    for (const auto& [key, value] : environment_vars_) {\n        setenv(key.c_str(), value.c_str(), 1);\n    }\n\n    for (const auto& descriptor : descriptors) {\n        descriptor.Publish();\n    }\n\n    if (auto result = WritePidToFiles(&writepid_files_); !result.ok()) {\n        LOG(ERROR) << \"failed to write pid to files: \" << result.error();\n    }\n\n    // Wait until the cgroups have been created and until the cgroup controllers have been\n    // activated.\n    Result<uint8_t> byte = cgroups_activated.Read();\n    if (!byte.ok()) {\n        LOG(ERROR) << name_ << \": failed to read from notification channel: \" << byte.error();\n    }\n    cgroups_activated.Close();\n    if (*byte != kCgroupsActivated) {\n        LOG(FATAL) << \"Service '\" << name_  << \"' failed to start due to a fatal error\";\n        _exit(EXIT_FAILURE);\n    }\n\n    if (task_profiles_.size() > 0) {\n        bool succeeded = SelinuxGetVendorAndroidVersion() < __ANDROID_API_U__\n                                 ?\n                                 // Compatibility mode: apply the task profiles to the current\n                                 // thread.\n                                 SetTaskProfiles(getpid(), task_profiles_)\n                                 :\n                                 // Apply the task profiles to the current process.\n                                 SetProcessProfiles(getuid(), getpid(), task_profiles_);\n        if (!succeeded) {\n            LOG(ERROR) << \"failed to set task profiles\";\n        }\n    }\n\n    // As requested, set our gid, supplemental gids, uid, context, and\n    // priority. Aborts on failure.\n    SetProcessAttributesAndCaps(std::move(setsid_finished));\n\n    if (!ExpandArgsAndExecv(args_, sigstop_)) {\n        PLOG(ERROR) << \"cannot execv('\" << args_[0]\n                    << \"'). See the 'Debugging init' section of init's README.md for tips\";\n    }\n}\n\nResult<void> Service::Start() {\n    auto reboot_on_failure = make_scope_guard([this] {\n        if (on_failure_reboot_target_) {\n            trigger_shutdown(*on_failure_reboot_target_);\n        }\n    });\n\n    if (is_updatable() && !IsDefaultMountNamespaceReady()) {\n        ServiceList::GetInstance().DelayService(*this);\n        return Error() << \"Cannot start an updatable service '\" << name_\n                       << \"' before configs from APEXes are all loaded. \"\n                       << \"Queued for execution.\";\n    }\n\n    bool disabled = (flags_ & (SVC_DISABLED | SVC_RESET));\n    ResetFlagsForStart();\n\n    // Running processes require no additional work --- if they're in the\n    // process of exiting, we've ensured that they will immediately restart\n    // on exit, unless they are ONESHOT. For ONESHOT service, if it's in\n    // stopping status, we just set SVC_RESTART flag so it will get restarted\n    // in Reap().\n    if (flags_ & SVC_RUNNING) {\n        if ((flags_ & SVC_ONESHOT) && disabled) {\n            flags_ |= SVC_RESTART;\n        }\n\n        LOG(INFO) << \"service '\" << name_\n                  << \"' requested start, but it is already running (flags: \" << flags_ << \")\";\n\n        // It is not an error to try to start a service that is already running.\n        reboot_on_failure.Disable();\n        return {};\n    }\n\n    // cgroups_activated is used for communication from the parent to the child\n    // while setsid_finished is used for communication from the child process to\n    // the parent process. These two communication channels are separate because\n    // combining these into a single communication channel would introduce a\n    // race between the Write() calls by the parent and by the child.\n    InterprocessFifo cgroups_activated, setsid_finished;\n    OR_RETURN(cgroups_activated.Initialize());\n    OR_RETURN(setsid_finished.Initialize());\n\n    if (Result<void> result = CheckConsole(); !result.ok()) {\n        return result;\n    }\n\n    struct stat sb;\n    if (stat(args_[0].c_str(), &sb) == -1) {\n        flags_ |= SVC_DISABLED;\n        return ErrnoError() << \"Cannot find '\" << args_[0] << \"'\";\n    }\n\n    std::string scon;\n    if (!seclabel_.empty()) {\n        scon = seclabel_;\n    } else {\n        auto result = ComputeContextFromExecutable(args_[0]);\n        if (!result.ok()) {\n            return result.error();\n        }\n        scon = *result;\n    }\n\n    if (!mount_namespace_.has_value()) {\n        // remember from which mount namespace the service should start\n        SetMountNamespace();\n    }\n\n    LOG(INFO) << \"starting service '\" << name_ << \"'...\";\n\n    std::vector<Descriptor> descriptors;\n    for (const auto& socket : sockets_) {\n        if (auto result = socket.Create(scon); result.ok()) {\n            descriptors.emplace_back(std::move(*result));\n        } else {\n            LOG(INFO) << \"Could not create socket '\" << socket.name << \"': \" << result.error();\n        }\n    }\n\n    for (const auto& file : files_) {\n        if (auto result = file.Create(); result.ok()) {\n            descriptors.emplace_back(std::move(*result));\n        } else {\n            LOG(INFO) << \"Could not open file '\" << file.name << \"': \" << result.error();\n        }\n    }\n\n    if (shared_kallsyms_file_) {\n        if (auto result = CreateSharedKallsymsFd(); result.ok()) {\n            descriptors.emplace_back(std::move(*result));\n        } else {\n            LOG(INFO) << \"Could not obtain a copy of /proc/kallsyms: \" << result.error();\n        }\n    }\n\n    pid_t pid = -1;\n    if (namespaces_.flags) {\n        pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);\n    } else {\n        pid = fork();\n    }\n\n    if (pid == 0) {\n        umask(077);\n        cgroups_activated.CloseWriteFd();\n        setsid_finished.CloseReadFd();\n        RunService(descriptors, std::move(cgroups_activated), std::move(setsid_finished));\n        _exit(127);\n    } else {\n        cgroups_activated.CloseReadFd();\n        setsid_finished.CloseWriteFd();\n    }\n\n    if (pid < 0) {\n        pid_ = 0;\n        return ErrnoError() << \"Failed to fork\";\n    }\n\n    once_environment_vars_.clear();\n\n    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {\n        std::string oom_str = std::to_string(oom_score_adjust_);\n        std::string oom_file = StringPrintf(\"/proc/%d/oom_score_adj\", pid);\n        if (!WriteStringToFile(oom_str, oom_file)) {\n            PLOG(ERROR) << \"couldn't write oom_score_adj\";\n        }\n    }\n\n    time_started_ = boot_clock::now();\n    pid_ = pid;\n    flags_ |= SVC_RUNNING;\n    start_order_ = next_start_order_++;\n    process_cgroup_empty_ = false;\n\n    if (CgroupsAvailable()) {\n        bool use_memcg = swappiness_ != -1 || soft_limit_in_bytes_ != -1 || limit_in_bytes_ != -1 ||\n                         limit_percent_ != -1 || !limit_property_.empty();\n        errno = -createProcessGroup(uid(), pid_, use_memcg);\n        if (errno != 0) {\n            Result<void> result = cgroups_activated.Write(kActivatingCgroupsFailed);\n            if (!result.ok()) {\n                return Error() << \"Sending notification failed: \" << result.error();\n            }\n            return Error() << \"createProcessGroup(\" << uid() << \", \" << pid_ << \", \" << use_memcg\n                           << \") failed for service '\" << name_ << \"': \" << strerror(errno);\n        }\n\n        // When the blkio controller is mounted in the v1 hierarchy, NormalIoPriority is\n        // the default (/dev/blkio). When the blkio controller is mounted in the v2 hierarchy, the\n        // NormalIoPriority profile has to be applied explicitly.\n        SetProcessProfiles(uid(), pid_, {\"NormalIoPriority\"});\n\n        if (use_memcg) {\n            ConfigureMemcg();\n        }\n    }\n\n    if (oom_score_adjust_ != DEFAULT_OOM_SCORE_ADJUST) {\n        LmkdRegister(name_, uid(), pid_, oom_score_adjust_);\n    }\n\n    if (Result<void> result = cgroups_activated.Write(kCgroupsActivated); !result.ok()) {\n        return Error() << \"Sending cgroups activated notification failed: \" << result.error();\n    }\n\n    cgroups_activated.Close();\n\n    // Call setpgid() from the parent process to make sure that this call has\n    // finished before the parent process calls kill(-pgid, ...).\n    if (!RequiresConsole(proc_attr_)) {\n        if (setpgid(pid, pid) < 0) {\n            switch (errno) {\n                case EACCES:  // Child has already performed setpgid() followed by execve().\n                case ESRCH:   // Child process no longer exists.\n                    break;\n                default:\n                    PLOG(ERROR) << \"setpgid() from parent failed\";\n            }\n        }\n    } else {\n        // The Read() call below will return an error if the child is killed.\n        if (Result<uint8_t> result = setsid_finished.Read();\n            !result.ok() || *result != kSetSidFinished) {\n            if (!result.ok()) {\n                return Error() << \"Waiting for setsid() failed: \" << result.error();\n            } else {\n                return Error() << \"Waiting for setsid() failed: \" << static_cast<uint32_t>(*result)\n                               << \" <> \" << static_cast<uint32_t>(kSetSidFinished);\n            }\n        }\n    }\n\n    setsid_finished.Close();\n\n    NotifyStateChange(\"running\");\n    reboot_on_failure.Disable();\n\n    LOG(INFO) << \"... started service '\" << name_ << \"' has pid \" << pid_;\n\n    return {};\n}\n\n// Set mount namespace for the service.\n// The reason why remember the mount namespace:\n//   If this service is started before APEXes and corresponding linker configuration\n//   get available, mark it as pre-apexd one. Note that this marking is\n//   permanent. So for example, if the service is re-launched (e.g., due\n//   to crash), it is still recognized as pre-apexd... for consistency.\nvoid Service::SetMountNamespace() {\n    // APEXd is always started in the \"current\" namespace because it is the process to set up\n    // the current namespace. So, leave mount_namespace_ as empty.\n    if (args_[0] == \"/system/bin/apexd\") {\n        return;\n    }\n    // Services in the following list start in the \"default\" mount namespace.\n    // Note that they should use bootstrap bionic if they start before APEXes are ready.\n    static const std::set<std::string> kUseDefaultMountNamespace = {\n            \"ueventd\",           // load firmwares from APEXes\n            \"hwservicemanager\",  // load VINTF fragments from APEXes\n            \"servicemanager\",    // load VINTF fragments from APEXes\n    };\n    if (kUseDefaultMountNamespace.find(name_) != kUseDefaultMountNamespace.end()) {\n        mount_namespace_ = NS_DEFAULT;\n        return;\n    }\n    // Use the \"default\" mount namespace only if it's ready\n    mount_namespace_ = IsDefaultMountNamespaceReady() ? NS_DEFAULT : NS_BOOTSTRAP;\n}\n\nstatic int ThreadCount() {\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(\"/proc/self/task\"), closedir);\n    if (!dir) {\n        return -1;\n    }\n\n    int count = 0;\n    dirent* entry;\n    while ((entry = readdir(dir.get())) != nullptr) {\n        if (entry->d_name[0] != '.') {\n            count++;\n        }\n    }\n    return count;\n}\n\n// Must be called BEFORE any threads are created. See also the sigprocmask() man page.\nunique_fd Service::CreateSigchldFd() {\n    CHECK_EQ(ThreadCount(), 1);\n    sigset_t mask;\n    sigemptyset(&mask);\n    sigaddset(&mask, SIGCHLD);\n    if (sigprocmask(SIG_BLOCK, &mask, nullptr) < 0) {\n        PLOG(FATAL) << \"Failed to block SIGCHLD\";\n    }\n\n    return unique_fd(signalfd(-1, &mask, SFD_CLOEXEC));\n}\n\nvoid Service::OpenAndSaveStaticKallsymsFd() {\n    Result<Descriptor> result = CreateSharedKallsymsFd();\n    if (!result.ok()) {\n      LOG(ERROR) << result.error();\n    }\n}\n\n// This function is designed to be called in two situations:\n// 1) early during second_stage init, to open and save the shared fd as a\n//    static (see OpenAndSaveStaticKallsymsFd).\n// 2) whenever a service requesting a copy of the fd is being started, at which\n//    point it will get a duplicated copy of the static fd.\nResult<Descriptor> Service::CreateSharedKallsymsFd() {\n    static constexpr char kallsyms_path[] = \"/proc/kallsyms\";\n    static int static_fd = open(kallsyms_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);\n    if (static_fd < 0) {\n        return ErrnoError() << \"failed to open \" << kallsyms_path;\n    }\n\n    unique_fd fd{fcntl(static_fd, F_DUPFD_CLOEXEC, /*min_fd=*/3)};\n    if (fd < 0) {\n        return ErrnoError() << \"failed fcntl(F_DUPFD_CLOEXEC)\";\n    }\n\n    // Use the same environment variable as if the service specified\n    // \"file /proc/kallsyms r\".\n    return Descriptor(std::string(ANDROID_FILE_ENV_PREFIX) + kallsyms_path, std::move(fd));\n}\n\nvoid Service::SetStartedInFirstStage(pid_t pid) {\n    LOG(INFO) << \"adding first-stage service '\" << name_ << \"'...\";\n\n    time_started_ = boot_clock::now();  // not accurate, but doesn't matter here\n    pid_ = pid;\n    flags_ |= SVC_RUNNING;\n    start_order_ = next_start_order_++;\n\n    NotifyStateChange(\"running\");\n}\n\nvoid Service::ResetFlagsForStart() {\n    // Starting a service removes it from the disabled or reset state and\n    // immediately takes it out of the restarting state if it was in there.\n    flags_ &= ~(SVC_DISABLED | SVC_RESTARTING | SVC_RESET | SVC_RESTART | SVC_DISABLED_START);\n}\n\nResult<void> Service::StartIfNotDisabled() {\n    if (!(flags_ & SVC_DISABLED)) {\n        return Start();\n    } else {\n        flags_ |= SVC_DISABLED_START;\n    }\n    return {};\n}\n\nResult<void> Service::Enable() {\n    flags_ &= ~(SVC_DISABLED | SVC_RC_DISABLED);\n    if (flags_ & SVC_DISABLED_START) {\n        return Start();\n    }\n    return {};\n}\n\nvoid Service::Reset() {\n    StopOrReset(SVC_RESET);\n}\n\nvoid Service::Stop() {\n    StopOrReset(SVC_DISABLED);\n}\n\nvoid Service::Terminate() {\n    flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);\n    flags_ |= SVC_DISABLED;\n    if (pid_) {\n        KillProcessGroup(SIGTERM);\n        NotifyStateChange(\"stopping\");\n    }\n}\n\nvoid Service::Timeout() {\n    // All process state flags will be taken care of in Reap(), we really just want to kill the\n    // process here when it times out.  Oneshot processes will transition to be disabled, and\n    // all other processes will transition to be restarting.\n    LOG(INFO) << \"Service '\" << name_ << \"' expired its timeout of \" << timeout_period_->count()\n              << \" seconds and will now be killed\";\n    if (pid_) {\n        KillProcessGroup(SIGKILL);\n        NotifyStateChange(\"stopping\");\n    }\n}\n\nvoid Service::Restart() {\n    if (flags_ & SVC_RUNNING) {\n        /* Stop, wait, then start the service. */\n        StopOrReset(SVC_RESTART);\n    } else if (!(flags_ & SVC_RESTARTING)) {\n        /* Just start the service since it's not running. */\n        if (auto result = Start(); !result.ok()) {\n            LOG(ERROR) << \"Could not restart '\" << name_ << \"': \" << result.error();\n        }\n    } /* else: Service is restarting anyways. */\n}\n\n// The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART.\nvoid Service::StopOrReset(int how) {\n    // The service is still SVC_RUNNING until its process exits, but if it has\n    // already exited it shoudn't attempt a restart yet.\n    flags_ &= ~(SVC_RESTARTING | SVC_DISABLED_START);\n\n    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {\n        // An illegal flag: default to SVC_DISABLED.\n        LOG(ERROR) << \"service '\" << name_ << \"' requested unknown flag \" << how\n                   << \", defaulting to disabling it.\";\n        how = SVC_DISABLED;\n    }\n\n    // If the service has not yet started, prevent it from auto-starting with its class.\n    if (how == SVC_RESET) {\n        flags_ |= (flags_ & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;\n    } else {\n        flags_ |= how;\n    }\n    // Make sure it's in right status when a restart immediately follow a\n    // stop/reset or vice versa.\n    if (how == SVC_RESTART) {\n        flags_ &= (~(SVC_DISABLED | SVC_RESET));\n    } else {\n        flags_ &= (~SVC_RESTART);\n    }\n\n    if (pid_) {\n        if (flags_ & SVC_GENTLE_KILL) {\n            KillProcessGroup(SIGTERM);\n            if (!process_cgroup_empty()) std::this_thread::sleep_for(200ms);\n        }\n        KillProcessGroup(SIGKILL);\n        NotifyStateChange(\"stopping\");\n    } else {\n        NotifyStateChange(\"stopped\");\n    }\n}\n\nResult<std::unique_ptr<Service>> Service::MakeTemporaryOneshotService(\n        const std::vector<std::string>& args) {\n    // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...\n    // SECLABEL can be a - to denote default\n    std::size_t command_arg = 1;\n    for (std::size_t i = 1; i < args.size(); ++i) {\n        if (args[i] == \"--\") {\n            command_arg = i + 1;\n            break;\n        }\n    }\n    if (command_arg > 4 + NR_SVC_SUPP_GIDS) {\n        return Error() << \"exec called with too many supplementary group ids\";\n    }\n\n    if (command_arg >= args.size()) {\n        return Error() << \"exec called without command\";\n    }\n    std::vector<std::string> str_args(args.begin() + command_arg, args.end());\n\n    static size_t exec_count = 0;\n    exec_count++;\n    std::string name = \"exec \" + std::to_string(exec_count) + \" (\" + Join(str_args, \" \") + \")\";\n\n    unsigned flags = SVC_ONESHOT | SVC_TEMPORARY;\n    unsigned namespace_flags = 0;\n\n    std::string seclabel = \"\";\n    if (command_arg > 2 && args[1] != \"-\") {\n        seclabel = args[1];\n    }\n    Result<uid_t> uid = 0;\n    if (command_arg > 3) {\n        uid = DecodeUid(args[2]);\n        if (!uid.ok()) {\n            return Error() << \"Unable to decode UID for '\" << args[2] << \"': \" << uid.error();\n        }\n    }\n    Result<gid_t> gid = 0;\n    std::vector<gid_t> supp_gids;\n    if (command_arg > 4) {\n        gid = DecodeUid(args[3]);\n        if (!gid.ok()) {\n            return Error() << \"Unable to decode GID for '\" << args[3] << \"': \" << gid.error();\n        }\n        std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;\n        for (size_t i = 0; i < nr_supp_gids; ++i) {\n            auto supp_gid = DecodeUid(args[4 + i]);\n            if (!supp_gid.ok()) {\n                return Error() << \"Unable to decode GID for '\" << args[4 + i]\n                               << \"': \" << supp_gid.error();\n            }\n            supp_gids.push_back(*supp_gid);\n        }\n    }\n\n    return std::make_unique<Service>(name, flags, *uid, *gid, supp_gids, namespace_flags, seclabel,\n                                     nullptr, /*filename=*/\"\", str_args);\n}\n\n// This is used for snapuserd_proxy, which hands off a socket to snapuserd. It's\n// a special case to support the daemon launched in first-stage init. The persist\n// feature is not part of the init language and is only used here.\nbool Service::MarkSocketPersistent(const std::string& socket_name) {\n    for (auto& socket : sockets_) {\n        if (socket.name == socket_name) {\n            socket.persist = true;\n            return true;\n        }\n    }\n    return false;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <signal.h>\n#include <sys/types.h>\n\n#include <algorithm>\n#include <chrono>\n#include <memory>\n#include <optional>\n#include <set>\n#include <string>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <cutils/iosched_policy.h>\n\n#include \"action.h\"\n#include \"capabilities.h\"\n#include \"interprocess_fifo.h\"\n#include \"keyword_map.h\"\n#include \"mount_namespace.h\"\n#include \"parser.h\"\n#include \"service_utils.h\"\n#include \"subcontext.h\"\n\n#define SVC_DISABLED 0x001        // do not autostart with class\n#define SVC_ONESHOT 0x002         // do not restart on exit\n#define SVC_RUNNING 0x004         // currently active\n#define SVC_RESTARTING 0x008      // waiting to restart\n#define SVC_CONSOLE 0x010         // requires console\n#define SVC_CRITICAL 0x020        // will reboot into bootloader if keeps crashing\n#define SVC_RESET 0x040           // Use when stopping a process,\n                                  // but not disabling so it can be restarted with its class.\n#define SVC_RC_DISABLED 0x080     // Remember if the disabled flag was set in the rc script.\n#define SVC_RESTART 0x100         // Use to safely restart (stop, wait, start) a service.\n#define SVC_DISABLED_START 0x200  // A start was requested but it was disabled at the time.\n#define SVC_EXEC 0x400  // This service was started by either 'exec' or 'exec_start' and stops\n                        // init from processing more commands until it completes\n\n#define SVC_SHUTDOWN_CRITICAL 0x800  // This service is critical for shutdown and\n                                     // should not be killed during shutdown\n#define SVC_TEMPORARY 0x1000  // This service was started by 'exec' and should be removed from the\n                              // service list once it is reaped.\n#define SVC_GENTLE_KILL 0x2000  // This service should be stopped with SIGTERM instead of SIGKILL\n                                // Will still be SIGKILLed after timeout period of 200 ms\n\n#define NR_SVC_SUPP_GIDS 32    // thirty two supplementary groups\n\nnamespace android {\nnamespace init {\n\nclass Service {\n    friend class ServiceParser;\n\n  public:\n    Service(const std::string& name, Subcontext* subcontext_for_restart_commands,\n            const std::string& filename, const std::vector<std::string>& args);\n\n    Service(const std::string& name, unsigned flags, std::optional<uid_t> uid, gid_t gid,\n            const std::vector<gid_t>& supp_gids, int namespace_flags, const std::string& seclabel,\n            Subcontext* subcontext_for_restart_commands, const std::string& filename,\n            const std::vector<std::string>& args);\n    Service(const Service&) = delete;\n    void operator=(const Service&) = delete;\n\n    static Result<std::unique_ptr<Service>> MakeTemporaryOneshotService(\n            const std::vector<std::string>& args);\n\n    bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }\n    bool IsEnabled() { return (flags_ & SVC_DISABLED) == 0; }\n    Result<void> ExecStart();\n    Result<void> Start();\n    Result<void> StartIfNotDisabled();\n    Result<void> Enable();\n    void Reset();\n    void Stop();\n    void Terminate();\n    void Timeout();\n    void Restart();\n    void Reap(const siginfo_t& siginfo);\n    void DumpState() const;\n    void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }\n    bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }\n    void UnSetExec() {\n        is_exec_service_running_ = false;\n        flags_ &= ~SVC_EXEC;\n    }\n    void AddReapCallback(std::function<void(const siginfo_t& siginfo)> callback) {\n        reap_callbacks_.emplace_back(std::move(callback));\n    }\n    void SetStartedInFirstStage(pid_t pid);\n    bool MarkSocketPersistent(const std::string& socket_name);\n    size_t CheckAllCommands() const { return onrestart_.CheckAllCommands(); }\n\n    static bool is_exec_service_running() { return is_exec_service_running_; }\n\n    const std::string& name() const { return name_; }\n    const std::set<std::string>& classnames() const { return classnames_; }\n    unsigned flags() const { return flags_; }\n    pid_t pid() const { return pid_; }\n    android::base::boot_clock::time_point time_started() const { return time_started_; }\n    int crash_count() const { return crash_count_; }\n    int was_last_exit_ok() const { return was_last_exit_ok_; }\n    uid_t uid() const { return proc_attr_.uid(); }\n    gid_t gid() const { return proc_attr_.gid; }\n    int namespace_flags() const { return namespaces_.flags; }\n    const std::vector<gid_t>& supp_gids() const { return proc_attr_.supp_gids; }\n    const std::string& seclabel() const { return seclabel_; }\n    const std::vector<int>& keycodes() const { return keycodes_; }\n    IoSchedClass ioprio_class() const { return proc_attr_.ioprio_class; }\n    int ioprio_pri() const { return proc_attr_.ioprio_pri; }\n    const std::set<std::string>& interfaces() const { return interfaces_; }\n    int priority() const { return proc_attr_.priority; }\n    int oom_score_adjust() const { return oom_score_adjust_; }\n    bool is_override() const { return override_; }\n    bool process_cgroup_empty() const { return process_cgroup_empty_; }\n    unsigned long start_order() const { return start_order_; }\n    void set_sigstop(bool value) { sigstop_ = value; }\n    std::chrono::seconds restart_period() const {\n        // If the service exited abnormally or due to timeout, late limit the restart even if\n        // restart_period is set to a very short value.\n        // If not, i.e. restart after a deliberate and successful exit, respect the period.\n        if (!was_last_exit_ok_) {\n            return std::max(restart_period_, default_restart_period_);\n        }\n        return restart_period_;\n    }\n    std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }\n    const std::vector<std::string>& args() const { return args_; }\n    bool is_updatable() const { return updatable_; }\n    void set_oneshot(bool value) {\n        if (value) {\n            flags_ |= SVC_ONESHOT;\n        } else {\n            flags_ &= ~SVC_ONESHOT;\n        }\n    }\n    const Subcontext* subcontext() const { return subcontext_; }\n    const std::string& filename() const { return filename_; }\n    void set_filename(const std::string& name) { filename_ = name; }\n    static int GetSigchldFd() {\n        static int sigchld_fd = CreateSigchldFd().release();\n        return sigchld_fd;\n    }\n    static void OpenAndSaveStaticKallsymsFd();\n\n  private:\n    void NotifyStateChange(const std::string& new_state) const;\n    void StopOrReset(int how);\n    void KillProcessGroup(int signal);\n    void SetProcessAttributesAndCaps(InterprocessFifo setsid_finished);\n    void ResetFlagsForStart();\n    Result<void> CheckConsole();\n    void ConfigureMemcg();\n    void RunService(const std::vector<Descriptor>& descriptors, InterprocessFifo cgroups_activated,\n                    InterprocessFifo setsid_finished);\n    void SetMountNamespace();\n    static ::android::base::unique_fd CreateSigchldFd();\n    static Result<Descriptor> CreateSharedKallsymsFd();\n\n    static unsigned long next_start_order_;\n    static bool is_exec_service_running_;\n\n    const std::string name_;\n    std::set<std::string> classnames_;\n\n    unsigned flags_;\n    pid_t pid_;\n    android::base::boot_clock::time_point time_started_;  // time of last start\n    android::base::boot_clock::time_point time_crashed_;  // first crash within inspection window\n    int crash_count_;                     // number of times crashed within window\n    bool upgraded_mte_ = false;           // whether we upgraded async MTE -> sync MTE before\n    std::chrono::minutes fatal_crash_window_ = 4min;  // fatal() when more than 4 crashes in it\n    std::optional<std::string> fatal_reboot_target_;  // reboot target of fatal handler\n    bool was_last_exit_ok_ =\n            true;  // true if the service never exited, or exited with status code 0\n    bool shared_kallsyms_file_ = false; // pass the service a pre-opened fd to /proc/kallsyms\n\n    std::optional<CapSet> capabilities_;\n    ProcessAttributes proc_attr_;\n    NamespaceInfo namespaces_;\n\n    std::string seclabel_;\n\n    std::vector<SocketDescriptor> sockets_;\n    std::vector<FileDescriptor> files_;\n    std::vector<std::pair<std::string, std::string>> environment_vars_;\n    // Environment variables that only get applied to the next run.\n    std::vector<std::pair<std::string, std::string>> once_environment_vars_;\n\n    const Subcontext* const subcontext_;\n    Action onrestart_;  // Commands to execute on restart.\n\n    std::vector<std::string> writepid_files_;\n\n    std::vector<std::string> task_profiles_;\n\n    std::set<std::string> interfaces_;  // e.g. some.package.foo@1.0::IBaz/instance-name\n\n    // keycodes for triggering this service via /dev/input/input*\n    std::vector<int> keycodes_;\n\n    int oom_score_adjust_;\n\n    int swappiness_ = -1;\n    int soft_limit_in_bytes_ = -1;\n\n    int limit_in_bytes_ = -1;\n    int limit_percent_ = -1;\n    std::string limit_property_;\n\n    bool process_cgroup_empty_ = false;\n\n    bool override_ = false;\n\n    unsigned long start_order_;\n\n    bool sigstop_ = false;\n\n    const std::chrono::seconds default_restart_period_ = 5s;\n    std::chrono::seconds restart_period_ = default_restart_period_;\n    std::optional<std::chrono::seconds> timeout_period_;\n\n    bool updatable_ = false;\n\n    const std::vector<std::string> args_;\n\n    std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;\n\n    std::optional<MountNamespace> mount_namespace_;\n\n    std::optional<std::string> on_failure_reboot_target_;\n\n    std::string filename_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service_list.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"service_list.h\"\n\n#include <android-base/logging.h>\n\nnamespace android {\nnamespace init {\n\nServiceList::ServiceList() {}\n\nServiceList& ServiceList::GetInstance() {\n    static ServiceList* instance = new ServiceList;\n    return *instance;\n}\n\nsize_t ServiceList::CheckAllCommands() {\n    size_t failures = 0;\n    for (const auto& service : services_) {\n        failures += service->CheckAllCommands();\n    }\n    return failures;\n}\n\nvoid ServiceList::AddService(std::unique_ptr<Service> service) {\n    services_.emplace_back(std::move(service));\n}\n\n// Shutdown services in the opposite order that they were started.\nconst std::vector<Service*> ServiceList::services_in_shutdown_order() const {\n    std::vector<Service*> shutdown_services;\n    for (const auto& service : services_) {\n        if (service->start_order() > 0) shutdown_services.emplace_back(service.get());\n    }\n    std::sort(shutdown_services.begin(), shutdown_services.end(),\n              [](const auto& a, const auto& b) { return a->start_order() > b->start_order(); });\n    return shutdown_services;\n}\n\nvoid ServiceList::RemoveService(const Service& svc) {\n    auto svc_it = std::find_if(\n            services_.begin(), services_.end(),\n            [&svc](const std::unique_ptr<Service>& s) { return svc.name() == s->name(); });\n    if (svc_it == services_.end()) {\n        return;\n    }\n\n    services_.erase(svc_it);\n}\n\nvoid ServiceList::DumpState() const {\n    for (const auto& s : services_) {\n        s->DumpState();\n    }\n}\n\nvoid ServiceList::StartDelayedServices() {\n    for (const auto& name : delayed_service_names_) {\n        Service* service = FindService(name);\n        if (service == nullptr) {\n            LOG(ERROR) << \"delayed service '\" << name << \"' could not be found.\";\n            continue;\n        }\n        if (auto result = service->Start(); !result.ok()) {\n            LOG(ERROR) << result.error().message();\n        }\n    }\n    delayed_service_names_.clear();\n}\n\nvoid ServiceList::DelayService(const Service& service) {\n    if (IsDefaultMountNamespaceReady()) {\n        LOG(ERROR) << \"Cannot delay the start of service '\" << service.name()\n                   << \"' because all services are already updated. Ignoring.\";\n        return;\n    }\n    delayed_service_names_.emplace_back(service.name());\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service_list.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <iterator>\n#include <memory>\n#include <vector>\n\n#include <android-base/logging.h>\n\n#include \"service.h\"\n#include \"util.h\"\n\nnamespace android {\nnamespace init {\n\nclass ServiceList {\n  public:\n    static ServiceList& GetInstance();\n\n    // Exposed for testing\n    ServiceList();\n    size_t CheckAllCommands();\n\n    void AddService(std::unique_ptr<Service> service);\n    void RemoveService(const Service& svc);\n    template <class UnaryPredicate>\n    void RemoveServiceIf(UnaryPredicate predicate) {\n        services_.erase(std::remove_if(services_.begin(), services_.end(), predicate),\n                        services_.end());\n    }\n\n    template <typename T, typename F = decltype(&Service::name)>\n    Service* FindService(T value, F function = &Service::name) const {\n        auto svc = std::find_if(services_.begin(), services_.end(),\n                                [&function, &value](const std::unique_ptr<Service>& s) {\n                                    return std::invoke(function, s) == value;\n                                });\n        if (svc != services_.end()) {\n            return svc->get();\n        }\n        return nullptr;\n    }\n\n    std::vector<Service*> FindServicesByApexName(const std::string& apex_name) const {\n        CHECK(!apex_name.empty()) << \"APEX name cannot be empty\";\n        std::vector<Service*> matches;\n        for (const auto& svc : services_) {\n            if (GetApexNameFromFileName(svc->filename()) == apex_name) {\n                matches.emplace_back(svc.get());\n            }\n        }\n        return matches;\n    }\n\n    Service* FindInterface(const std::string& interface_name) {\n        for (const auto& svc : services_) {\n            if (svc->interfaces().count(interface_name) > 0) {\n                return svc.get();\n            }\n        }\n\n        return nullptr;\n    }\n\n    void DumpState() const;\n\n    auto begin() const { return services_.begin(); }\n    auto end() const { return services_.end(); }\n    const std::vector<Service*> services_in_shutdown_order() const;\n\n    void DelayService(const Service& service);\n    void StartDelayedServices();\n\n    auto size() const { return services_.size(); }\n\n  private:\n    std::vector<std::unique_ptr<Service>> services_;\n\n    std::vector<std::string> delayed_service_names_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service_parser.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"service_parser.h\"\n\n#include <linux/input.h>\n#include <stdlib.h>\n#include <sys/socket.h>\n\n#include <algorithm>\n#include <sstream>\n\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <processgroup/processgroup.h>\n#include <system/thread_defs.h>\n\n#include \"lmkd_service.h\"\n#include \"rlimit_parser.h\"\n#include \"service_utils.h\"\n#include \"util.h\"\n\n#ifdef INIT_FULL_SOURCES\n#include <android/api-level.h>\n#include <sys/system_properties.h>\n\n#include \"selinux.h\"\n#else\n#include \"host_init_stubs.h\"\n#endif\n\nusing android::base::ParseInt;\nusing android::base::Split;\nusing android::base::StartsWith;\n\nnamespace android {\nnamespace init {\n\n#ifdef INIT_FULL_SOURCES\n// on full sources, we have better information on device to\n// make this decision\nconstexpr bool kAlwaysErrorUserRoot = false;\n#else\nconstexpr uint64_t kBuildShippingApiLevel = BUILD_SHIPPING_API_LEVEL + 0 /* +0 if empty */;\n// on partial sources, the host build, we don't have the specific\n// vendor API level, but we can enforce things based on the\n// shipping API level.\nconstexpr bool kAlwaysErrorUserRoot = kBuildShippingApiLevel > __ANDROID_API_V__;\n#endif\n\nResult<void> ServiceParser::ParseCapabilities(std::vector<std::string>&& args) {\n    service_->capabilities_ = 0;\n\n    if (!CapAmbientSupported()) {\n        return Error()\n               << \"capabilities requested but the kernel does not support ambient capabilities\";\n    }\n\n    unsigned int last_valid_cap = GetLastValidCap();\n    if (last_valid_cap >= service_->capabilities_->size()) {\n        LOG(WARNING) << \"last valid run-time capability is larger than CAP_LAST_CAP\";\n    }\n\n    for (size_t i = 1; i < args.size(); i++) {\n        const std::string& arg = args[i];\n        int res = LookupCap(arg);\n        if (res < 0) {\n            return Errorf(\"invalid capability '{}'\", arg);\n        }\n        unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.\n        if (cap > last_valid_cap) {\n            return Errorf(\"capability '{}' not supported by the kernel\", arg);\n        }\n        (*service_->capabilities_)[cap] = true;\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseClass(std::vector<std::string>&& args) {\n    service_->classnames_ = std::set<std::string>(args.begin() + 1, args.end());\n    return {};\n}\n\nResult<void> ServiceParser::ParseConsole(std::vector<std::string>&& args) {\n    if (service_->proc_attr_.stdio_to_kmsg) {\n        return Error() << \"'console' and 'stdio_to_kmsg' are mutually exclusive\";\n    }\n    service_->flags_ |= SVC_CONSOLE;\n    service_->proc_attr_.console = args.size() > 1 ? \"/dev/\" + args[1] : \"\";\n    return {};\n}\n\nResult<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {\n    std::optional<std::string> fatal_reboot_target;\n    std::optional<std::chrono::minutes> fatal_crash_window;\n\n    for (auto it = args.begin() + 1; it != args.end(); ++it) {\n        auto arg = android::base::Split(*it, \"=\");\n        if (arg.size() != 2) {\n            return Error() << \"critical: Argument '\" << *it << \"' is not supported\";\n        } else if (arg[0] == \"target\") {\n            fatal_reboot_target = arg[1];\n        } else if (arg[0] == \"window\") {\n            int minutes;\n            auto window = ExpandProps(arg[1]);\n            if (!window.ok()) {\n                return Error() << \"critical: Could not expand argument ': \" << arg[1];\n            }\n            if (*window == \"off\") {\n                return {};\n            }\n            if (!ParseInt(*window, &minutes, 0)) {\n                return Error() << \"critical: 'fatal_crash_window' must be an integer > 0\";\n            }\n            fatal_crash_window = std::chrono::minutes(minutes);\n        } else {\n            return Error() << \"critical: Argument '\" << *it << \"' is not supported\";\n        }\n    }\n\n    if (fatal_reboot_target) {\n        service_->fatal_reboot_target_ = *fatal_reboot_target;\n    }\n    if (fatal_crash_window) {\n        service_->fatal_crash_window_ = *fatal_crash_window;\n    }\n    service_->flags_ |= SVC_CRITICAL;\n    return {};\n}\n\nResult<void> ServiceParser::ParseDisabled(std::vector<std::string>&& args) {\n    service_->flags_ |= SVC_DISABLED;\n    service_->flags_ |= SVC_RC_DISABLED;\n    return {};\n}\n\nResult<void> ServiceParser::ParseEnterNamespace(std::vector<std::string>&& args) {\n    if (args[1] != \"net\") {\n        return Error() << \"Init only supports entering network namespaces\";\n    }\n    if (!service_->namespaces_.namespaces_to_enter.empty()) {\n        return Error() << \"Only one network namespace may be entered\";\n    }\n    // Network namespaces require that /sys is remounted, otherwise the old adapters will still be\n    // present. Therefore, they also require mount namespaces.\n    service_->namespaces_.flags |= CLONE_NEWNS;\n    service_->namespaces_.namespaces_to_enter.emplace_back(CLONE_NEWNET, std::move(args[2]));\n    return {};\n}\n\nResult<void> ServiceParser::ParseGentleKill(std::vector<std::string>&& args) {\n    service_->flags_ |= SVC_GENTLE_KILL;\n    return {};\n}\n\nResult<void> ServiceParser::ParseGroup(std::vector<std::string>&& args) {\n    auto gid = DecodeUid(args[1]);\n    if (!gid.ok()) {\n        return Error() << \"Unable to decode GID for '\" << args[1] << \"': \" << gid.error();\n    }\n    service_->proc_attr_.gid = *gid;\n\n    for (std::size_t n = 2; n < args.size(); n++) {\n        gid = DecodeUid(args[n]);\n        if (!gid.ok()) {\n            return Error() << \"Unable to decode GID for '\" << args[n] << \"': \" << gid.error();\n        }\n        service_->proc_attr_.supp_gids.emplace_back(*gid);\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParsePriority(std::vector<std::string>&& args) {\n    service_->proc_attr_.priority = 0;\n    if (!ParseInt(args[1], &service_->proc_attr_.priority,\n                  static_cast<int>(ANDROID_PRIORITY_HIGHEST),  // highest is negative\n                  static_cast<int>(ANDROID_PRIORITY_LOWEST))) {\n        return Errorf(\"process priority value must be range {} - {}\",\n                      static_cast<int>(ANDROID_PRIORITY_HIGHEST),\n                      static_cast<int>(ANDROID_PRIORITY_LOWEST));\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseInterface(std::vector<std::string>&& args) {\n    const std::string& interface_name = args[1];\n    const std::string& instance_name = args[2];\n    const std::string fullname = interface_name + \"/\" + instance_name;\n\n    for (const auto& svc : *service_list_) {\n        if (svc->interfaces().count(fullname) > 0 && !service_->is_override()) {\n            return Error() << \"Interface '\" << fullname << \"' redefined in \" << service_->name()\n                           << \" but is already defined by \" << svc->name();\n        }\n    }\n\n    service_->interfaces_.insert(fullname);\n\n    return {};\n}\n\nResult<void> ServiceParser::ParseIoprio(std::vector<std::string>&& args) {\n    if (!ParseInt(args[2], &service_->proc_attr_.ioprio_pri, 0, 7)) {\n        return Error() << \"priority value must be range 0 - 7\";\n    }\n\n    if (args[1] == \"rt\") {\n        service_->proc_attr_.ioprio_class = IoSchedClass_RT;\n    } else if (args[1] == \"be\") {\n        service_->proc_attr_.ioprio_class = IoSchedClass_BE;\n    } else if (args[1] == \"idle\") {\n        service_->proc_attr_.ioprio_class = IoSchedClass_IDLE;\n    } else {\n        return Error() << \"ioprio option usage: ioprio <rt|be|idle> <0-7>\";\n    }\n\n    return {};\n}\n\nResult<void> ServiceParser::ParseKeycodes(std::vector<std::string>&& args) {\n    auto it = args.begin() + 1;\n    if (args.size() == 2 && StartsWith(args[1], \"$\")) {\n        auto expanded = ExpandProps(args[1]);\n        if (!expanded.ok()) {\n            return expanded.error();\n        }\n\n        // If the property is not set, it defaults to none, in which case there are no keycodes\n        // for this service.\n        if (*expanded == \"none\") {\n            return {};\n        }\n\n        args = Split(*expanded, \",\");\n        it = args.begin();\n    }\n\n    for (; it != args.end(); ++it) {\n        int code;\n        if (ParseInt(*it, &code, 0, KEY_MAX)) {\n            for (auto& key : service_->keycodes_) {\n                if (key == code) return Error() << \"duplicate keycode: \" << *it;\n            }\n            service_->keycodes_.insert(\n                    std::upper_bound(service_->keycodes_.begin(), service_->keycodes_.end(), code),\n                    code);\n        } else {\n            return Error() << \"invalid keycode: \" << *it;\n        }\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseOneshot(std::vector<std::string>&& args) {\n    service_->flags_ |= SVC_ONESHOT;\n    return {};\n}\n\nResult<void> ServiceParser::ParseOnrestart(std::vector<std::string>&& args) {\n    args.erase(args.begin());\n    int line = service_->onrestart_.NumCommands() + 1;\n    if (auto result = service_->onrestart_.AddCommand(std::move(args), line); !result.ok()) {\n        return Error() << \"cannot add Onrestart command: \" << result.error();\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseNamespace(std::vector<std::string>&& args) {\n    for (size_t i = 1; i < args.size(); i++) {\n        if (args[i] == \"pid\") {\n            service_->namespaces_.flags |= CLONE_NEWPID;\n            // PID namespaces require mount namespaces.\n            service_->namespaces_.flags |= CLONE_NEWNS;\n        } else if (args[i] == \"mnt\") {\n            service_->namespaces_.flags |= CLONE_NEWNS;\n        } else {\n            return Error() << \"namespace must be 'pid' or 'mnt'\";\n        }\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseOomScoreAdjust(std::vector<std::string>&& args) {\n    if (!ParseInt(args[1], &service_->oom_score_adjust_, MIN_OOM_SCORE_ADJUST,\n                  MAX_OOM_SCORE_ADJUST)) {\n        return Error() << \"oom_score_adjust value must be in range \" << MIN_OOM_SCORE_ADJUST\n                       << \" - +\" << MAX_OOM_SCORE_ADJUST;\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseOverride(std::vector<std::string>&& args) {\n    service_->override_ = true;\n    return {};\n}\n\nResult<void> ServiceParser::ParseSharedKallsyms(std::vector<std::string>&& args) {\n    service_->shared_kallsyms_file_ = true;\n    return {};\n}\n\nResult<void> ServiceParser::ParseMemcgSwappiness(std::vector<std::string>&& args) {\n    LOG(WARNING) << \"memcg.swappiness is unsupported with memcg v2 and will be deprecated\";\n    if (!ParseInt(args[1], &service_->swappiness_, 0)) {\n        return Error() << \"swappiness value must be equal or greater than 0\";\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseMemcgLimitInBytes(std::vector<std::string>&& args) {\n    if (!ParseInt(args[1], &service_->limit_in_bytes_, 0)) {\n        return Error() << \"limit_in_bytes value must be equal or greater than 0\";\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseMemcgLimitPercent(std::vector<std::string>&& args) {\n    if (!ParseInt(args[1], &service_->limit_percent_, 0)) {\n        return Error() << \"limit_percent value must be equal or greater than 0\";\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseMemcgLimitProperty(std::vector<std::string>&& args) {\n    service_->limit_property_ = std::move(args[1]);\n    return {};\n}\n\nResult<void> ServiceParser::ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args) {\n    if (!ParseInt(args[1], &service_->soft_limit_in_bytes_, 0)) {\n        return Error() << \"soft_limit_in_bytes value must be equal or greater than 0\";\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseProcessRlimit(std::vector<std::string>&& args) {\n    auto rlimit = ParseRlimit(args);\n    if (!rlimit.ok()) return rlimit.error();\n\n    service_->proc_attr_.rlimits.emplace_back(*rlimit);\n    return {};\n}\n\nResult<void> ServiceParser::ParseRebootOnFailure(std::vector<std::string>&& args) {\n    if (service_->on_failure_reboot_target_) {\n        return Error() << \"Only one reboot_on_failure command may be specified\";\n    }\n    if (!StartsWith(args[1], \"shutdown\") && !StartsWith(args[1], \"reboot\")) {\n        return Error()\n               << \"reboot_on_failure commands must begin with either 'shutdown' or 'reboot'\";\n    }\n    service_->on_failure_reboot_target_ = std::move(args[1]);\n    return {};\n}\n\nResult<void> ServiceParser::ParseRestartPeriod(std::vector<std::string>&& args) {\n    int period;\n    if (!ParseInt(args[1], &period, 0)) {\n        return Error() << \"restart_period value must be an integer >= 0\";\n    }\n    service_->restart_period_ = std::chrono::seconds(period);\n    return {};\n}\n\nResult<void> ServiceParser::ParseSeclabel(std::vector<std::string>&& args) {\n    service_->seclabel_ = std::move(args[1]);\n    return {};\n}\n\nResult<void> ServiceParser::ParseSigstop(std::vector<std::string>&& args) {\n    service_->sigstop_ = true;\n    return {};\n}\n\nResult<void> ServiceParser::ParseSetenv(std::vector<std::string>&& args) {\n    service_->environment_vars_.emplace_back(std::move(args[1]), std::move(args[2]));\n    return {};\n}\n\nResult<void> ServiceParser::ParseShutdown(std::vector<std::string>&& args) {\n    if (args[1] == \"critical\") {\n        service_->flags_ |= SVC_SHUTDOWN_CRITICAL;\n        return {};\n    }\n    return Error() << \"Invalid shutdown option\";\n}\n\nResult<void> ServiceParser::ParseTaskProfiles(std::vector<std::string>&& args) {\n    args.erase(args.begin());\n    if (service_->task_profiles_.empty()) {\n        service_->task_profiles_ = std::move(args);\n    } else {\n        // Some task profiles might have been added during writepid conversions\n        service_->task_profiles_.insert(service_->task_profiles_.end(),\n                                        std::make_move_iterator(args.begin()),\n                                        std::make_move_iterator(args.end()));\n        args.clear();\n    }\n    return {};\n}\n\nResult<void> ServiceParser::ParseTimeoutPeriod(std::vector<std::string>&& args) {\n    int period;\n    if (!ParseInt(args[1], &period, 1)) {\n        return Error() << \"timeout_period value must be an integer >= 1\";\n    }\n    service_->timeout_period_ = std::chrono::seconds(period);\n    return {};\n}\n\n// name type perm [ uid gid context ]\nResult<void> ServiceParser::ParseSocket(std::vector<std::string>&& args) {\n    SocketDescriptor socket;\n    socket.name = std::move(args[1]);\n\n    auto types = Split(args[2], \"+\");\n    if (types[0] == \"stream\") {\n        socket.type = SOCK_STREAM;\n    } else if (types[0] == \"dgram\") {\n        socket.type = SOCK_DGRAM;\n    } else if (types[0] == \"seqpacket\") {\n        socket.type = SOCK_SEQPACKET;\n    } else {\n        return Error() << \"socket type must be 'dgram', 'stream' or 'seqpacket', got '\" << types[0]\n                       << \"' instead.\";\n    }\n\n    for (size_t i = 1; i < types.size(); i++) {\n        if (types[i] == \"passcred\") {\n            socket.passcred = true;\n        } else if (types[i] == \"listen\") {\n            socket.listen = true;\n        } else {\n            return Error() << \"Unknown socket type decoration '\" << types[i]\n                           << \"'. Known values are ['passcred', 'listen']\";\n        }\n    }\n\n    errno = 0;\n    char* end = nullptr;\n    socket.perm = strtol(args[3].c_str(), &end, 8);\n    if (errno != 0) {\n        return ErrnoError() << \"Unable to parse permissions '\" << args[3] << \"'\";\n    }\n    if (end == args[3].c_str() || *end != '\\0') {\n        errno = EINVAL;\n        return ErrnoError() << \"Unable to parse permissions '\" << args[3] << \"'\";\n    }\n\n    if (args.size() > 4) {\n        auto uid = DecodeUid(args[4]);\n        if (!uid.ok()) {\n            return Error() << \"Unable to find UID for '\" << args[4] << \"': \" << uid.error();\n        }\n        socket.uid = *uid;\n    }\n\n    if (args.size() > 5) {\n        auto gid = DecodeUid(args[5]);\n        if (!gid.ok()) {\n            return Error() << \"Unable to find GID for '\" << args[5] << \"': \" << gid.error();\n        }\n        socket.gid = *gid;\n    }\n\n    socket.context = args.size() > 6 ? args[6] : \"\";\n\n    auto old = std::find_if(service_->sockets_.begin(), service_->sockets_.end(),\n                            [&socket](const auto& other) { return socket.name == other.name; });\n\n    if (old != service_->sockets_.end()) {\n        return Error() << \"duplicate socket descriptor '\" << socket.name << \"'\";\n    }\n\n    service_->sockets_.emplace_back(std::move(socket));\n\n    return {};\n}\n\nResult<void> ServiceParser::ParseStdioToKmsg(std::vector<std::string>&& args) {\n    if (service_->flags_ & SVC_CONSOLE) {\n        return Error() << \"'stdio_to_kmsg' and 'console' are mutually exclusive\";\n    }\n    service_->proc_attr_.stdio_to_kmsg = true;\n    return {};\n}\n\n// name type\nResult<void> ServiceParser::ParseFile(std::vector<std::string>&& args) {\n    if (args[2] != \"r\" && args[2] != \"w\" && args[2] != \"rw\") {\n        return Error() << \"file type must be 'r', 'w' or 'rw'\";\n    }\n\n    FileDescriptor file;\n    file.type = args[2];\n\n    auto file_name = ExpandProps(args[1]);\n    if (!file_name.ok()) {\n        return Error() << \"Could not expand file path ': \" << file_name.error();\n    }\n    file.name = *file_name;\n    if (file.name[0] != '/' || file.name.find(\"../\") != std::string::npos) {\n        return Error() << \"file name must not be relative\";\n    }\n\n    auto old = std::find_if(service_->files_.begin(), service_->files_.end(),\n                            [&file](const auto& other) { return other.name == file.name; });\n\n    if (old != service_->files_.end()) {\n        return Error() << \"duplicate file descriptor '\" << file.name << \"'\";\n    }\n\n    service_->files_.emplace_back(std::move(file));\n\n    return {};\n}\n\nResult<void> ServiceParser::ParseUser(std::vector<std::string>&& args) {\n    auto uid = DecodeUid(args[1]);\n    if (!uid.ok()) {\n        return Error() << \"Unable to find UID for '\" << args[1] << \"': \" << uid.error();\n    }\n    service_->proc_attr_.parsed_uid = *uid;\n    return {};\n}\n\n// Convert legacy paths used to migrate processes between cgroups using writepid command.\n// We can't get these paths from TaskProfiles because profile definitions are changing\n// when we migrate to cgroups v2 while these hardcoded paths stay the same.\nstatic std::optional<const std::string> ConvertTaskFileToProfile(const std::string& file) {\n    static const std::map<const std::string, const std::string> map = {\n            {\"/dev/cpuset/camera-daemon/tasks\", \"CameraServiceCapacity\"},\n            {\"/dev/cpuset/foreground/tasks\", \"ProcessCapacityHigh\"},\n            {\"/dev/cpuset/system-background/tasks\", \"ServiceCapacityLow\"},\n            {\"/dev/blkio/background/tasks\", \"LowIoPriority\"},\n    };\n    auto iter = map.find(file);\n    return iter == map.end() ? std::nullopt : std::make_optional<const std::string>(iter->second);\n}\n\nResult<void> ServiceParser::ParseWritepid(std::vector<std::string>&& args) {\n    args.erase(args.begin());\n    // Convert any cgroup writes into appropriate task_profiles\n    for (auto iter = args.begin(); iter != args.end();) {\n        auto task_profile = ConvertTaskFileToProfile(*iter);\n        if (task_profile) {\n            LOG(WARNING) << \"'writepid \" << *iter << \"' is converted into 'task_profiles \"\n                         << task_profile.value() << \"' for service \" << service_->name();\n            service_->task_profiles_.push_back(task_profile.value());\n            iter = args.erase(iter);\n        } else {\n            ++iter;\n        }\n    }\n    service_->writepid_files_ = std::move(args);\n    return {};\n}\n\nResult<void> ServiceParser::ParseUpdatable(std::vector<std::string>&& args) {\n    service_->updatable_ = true;\n    return {};\n}\n\nconst KeywordMap<ServiceParser::OptionParser>& ServiceParser::GetParserMap() const {\n    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();\n    // clang-format off\n    static const KeywordMap<ServiceParser::OptionParser> parser_map = {\n        {\"capabilities\",            {0,     kMax, &ServiceParser::ParseCapabilities}},\n        {\"class\",                   {1,     kMax, &ServiceParser::ParseClass}},\n        {\"console\",                 {0,     1,    &ServiceParser::ParseConsole}},\n        {\"critical\",                {0,     2,    &ServiceParser::ParseCritical}},\n        {\"disabled\",                {0,     0,    &ServiceParser::ParseDisabled}},\n        {\"enter_namespace\",         {2,     2,    &ServiceParser::ParseEnterNamespace}},\n        {\"file\",                    {2,     2,    &ServiceParser::ParseFile}},\n        {\"gentle_kill\",             {0,     0,    &ServiceParser::ParseGentleKill}},\n        {\"group\",                   {1,     NR_SVC_SUPP_GIDS + 1, &ServiceParser::ParseGroup}},\n        {\"interface\",               {2,     2,    &ServiceParser::ParseInterface}},\n        {\"ioprio\",                  {2,     2,    &ServiceParser::ParseIoprio}},\n        {\"keycodes\",                {1,     kMax, &ServiceParser::ParseKeycodes}},\n        {\"memcg.limit_in_bytes\",    {1,     1,    &ServiceParser::ParseMemcgLimitInBytes}},\n        {\"memcg.limit_percent\",     {1,     1,    &ServiceParser::ParseMemcgLimitPercent}},\n        {\"memcg.limit_property\",    {1,     1,    &ServiceParser::ParseMemcgLimitProperty}},\n        {\"memcg.soft_limit_in_bytes\",\n                                    {1,     1,    &ServiceParser::ParseMemcgSoftLimitInBytes}},\n        {\"memcg.swappiness\",        {1,     1,    &ServiceParser::ParseMemcgSwappiness}},\n        {\"namespace\",               {1,     2,    &ServiceParser::ParseNamespace}},\n        {\"oneshot\",                 {0,     0,    &ServiceParser::ParseOneshot}},\n        {\"onrestart\",               {1,     kMax, &ServiceParser::ParseOnrestart}},\n        {\"oom_score_adjust\",        {1,     1,    &ServiceParser::ParseOomScoreAdjust}},\n        {\"override\",                {0,     0,    &ServiceParser::ParseOverride}},\n        {\"priority\",                {1,     1,    &ServiceParser::ParsePriority}},\n        {\"reboot_on_failure\",       {1,     1,    &ServiceParser::ParseRebootOnFailure}},\n        {\"restart_period\",          {1,     1,    &ServiceParser::ParseRestartPeriod}},\n        {\"rlimit\",                  {3,     3,    &ServiceParser::ParseProcessRlimit}},\n        {\"seclabel\",                {1,     1,    &ServiceParser::ParseSeclabel}},\n        {\"setenv\",                  {2,     2,    &ServiceParser::ParseSetenv}},\n        {\"shared_kallsyms\",         {0,     0,    &ServiceParser::ParseSharedKallsyms}},\n        {\"shutdown\",                {1,     1,    &ServiceParser::ParseShutdown}},\n        {\"sigstop\",                 {0,     0,    &ServiceParser::ParseSigstop}},\n        {\"socket\",                  {3,     6,    &ServiceParser::ParseSocket}},\n        {\"stdio_to_kmsg\",           {0,     0,    &ServiceParser::ParseStdioToKmsg}},\n        {\"task_profiles\",           {1,     kMax, &ServiceParser::ParseTaskProfiles}},\n        {\"timeout_period\",          {1,     1,    &ServiceParser::ParseTimeoutPeriod}},\n        {\"updatable\",               {0,     0,    &ServiceParser::ParseUpdatable}},\n        {\"user\",                    {1,     1,    &ServiceParser::ParseUser}},\n        {\"writepid\",                {1,     kMax, &ServiceParser::ParseWritepid}},\n    };\n    // clang-format on\n    return parser_map;\n}\n\nResult<void> ServiceParser::ParseSection(std::vector<std::string>&& args,\n                                         const std::string& filename, int line) {\n    if (args.size() < 3) {\n        return Error() << \"services must have a name and a program\";\n    }\n\n    const std::string& name = args[1];\n    if (!IsValidName(name)) {\n        return Error() << \"invalid service name '\" << name << \"'\";\n    }\n\n    filename_ = filename;\n\n    Subcontext* restart_action_subcontext = nullptr;\n    if (subcontext_ && subcontext_->PathMatchesSubcontext(filename)) {\n        restart_action_subcontext = subcontext_;\n    }\n\n    std::vector<std::string> str_args(args.begin() + 2, args.end());\n\n    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_P__) {\n        if (str_args[0] == \"/sbin/watchdogd\") {\n            str_args[0] = \"/system/bin/watchdogd\";\n        }\n    }\n    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {\n        if (str_args[0] == \"/charger\") {\n            str_args[0] = \"/system/bin/charger\";\n        }\n    }\n\n    service_ = std::make_unique<Service>(name, restart_action_subcontext, filename, str_args);\n    return {};\n}\n\nResult<void> ServiceParser::ParseLineSection(std::vector<std::string>&& args, int line) {\n    if (!service_) {\n        return {};\n    }\n\n    auto parser = GetParserMap().Find(args);\n\n    if (!parser.ok()) return parser.error();\n\n    return std::invoke(*parser, this, std::move(args));\n}\n\nResult<void> ServiceParser::EndSection() {\n    if (!service_) {\n        return {};\n    }\n\n    if (service_->proc_attr_.parsed_uid == std::nullopt) {\n        if (kAlwaysErrorUserRoot ||\n            android::base::GetIntProperty(\"ro.vendor.api_level\", 0) > 202404) {\n            return Error() << \"No user specified for service '\" << service_->name()\n                           << \"', so it would have been root.\";\n        } else {\n            LOG(WARNING) << \"No user specified for service '\" << service_->name()\n                         << \"', so it is root.\";\n        }\n    }\n\n    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {\n        if ((service_->flags() & SVC_CRITICAL) != 0 && (service_->flags() & SVC_ONESHOT) != 0) {\n            return Error() << \"service '\" << service_->name()\n                           << \"' can't be both critical and oneshot\";\n        }\n    }\n\n    Service* old_service = service_list_->FindService(service_->name());\n    if (old_service) {\n        if (!service_->is_override()) {\n            return Error() << \"ignored duplicate definition of service '\" << service_->name()\n                           << \"'\";\n        }\n\n        if (StartsWith(filename_, \"/apex/\") && !old_service->is_updatable()) {\n            return Error() << \"cannot update a non-updatable service '\" << service_->name()\n                           << \"' with a config in APEX\";\n        }\n\n        std::string context = service_->subcontext() ? service_->subcontext()->context() : \"\";\n        std::string old_context =\n                old_service->subcontext() ? old_service->subcontext()->context() : \"\";\n        if (context != old_context) {\n            return Error() << \"service '\" << service_->name() << \"' overrides another service \"\n                           << \"across the treble boundary.\";\n        }\n\n        service_list_->RemoveService(*old_service);\n        old_service = nullptr;\n    }\n\n    service_list_->AddService(std::move(service_));\n\n    return {};\n}\n\nbool ServiceParser::IsValidName(const std::string& name) const {\n    // Property names can be any length, but may only contain certain characters.\n    // Property values can contain any characters, but may only be a certain length.\n    // (The latter restriction is needed because `start` and `stop` work by writing\n    // the service name to the \"ctl.start\" and \"ctl.stop\" properties.)\n    return IsLegalPropertyName(\"init.svc.\" + name) && name.size() <= PROP_VALUE_MAX;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service_parser.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <vector>\n\n#include \"parser.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n#include \"subcontext.h\"\n\nnamespace android {\nnamespace init {\n\nclass ServiceParser : public SectionParser {\n  public:\n    ServiceParser(ServiceList* service_list, Subcontext* subcontext)\n        : service_list_(service_list), subcontext_(subcontext), service_(nullptr) {}\n    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,\n                              int line) override;\n    Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;\n    Result<void> EndSection() override;\n    void EndFile() override { filename_ = \"\"; }\n\n  private:\n    using OptionParser = Result<void> (ServiceParser::*)(std::vector<std::string>&& args);\n    const KeywordMap<ServiceParser::OptionParser>& GetParserMap() const;\n\n    Result<void> ParseCapabilities(std::vector<std::string>&& args);\n    Result<void> ParseClass(std::vector<std::string>&& args);\n    Result<void> ParseConsole(std::vector<std::string>&& args);\n    Result<void> ParseCritical(std::vector<std::string>&& args);\n    Result<void> ParseDisabled(std::vector<std::string>&& args);\n    Result<void> ParseEnterNamespace(std::vector<std::string>&& args);\n    Result<void> ParseGroup(std::vector<std::string>&& args);\n    Result<void> ParseGentleKill(std::vector<std::string>&& args);\n    Result<void> ParsePriority(std::vector<std::string>&& args);\n    Result<void> ParseInterface(std::vector<std::string>&& args);\n    Result<void> ParseIoprio(std::vector<std::string>&& args);\n    Result<void> ParseKeycodes(std::vector<std::string>&& args);\n    Result<void> ParseOneshot(std::vector<std::string>&& args);\n    Result<void> ParseOnrestart(std::vector<std::string>&& args);\n    Result<void> ParseOomScoreAdjust(std::vector<std::string>&& args);\n    Result<void> ParseOverride(std::vector<std::string>&& args);\n    Result<void> ParseMemcgLimitInBytes(std::vector<std::string>&& args);\n    Result<void> ParseMemcgLimitPercent(std::vector<std::string>&& args);\n    Result<void> ParseMemcgLimitProperty(std::vector<std::string>&& args);\n    Result<void> ParseMemcgSoftLimitInBytes(std::vector<std::string>&& args);\n    Result<void> ParseMemcgSwappiness(std::vector<std::string>&& args);\n    Result<void> ParseNamespace(std::vector<std::string>&& args);\n    Result<void> ParseProcessRlimit(std::vector<std::string>&& args);\n    Result<void> ParseRebootOnFailure(std::vector<std::string>&& args);\n    Result<void> ParseRestartPeriod(std::vector<std::string>&& args);\n    Result<void> ParseSeclabel(std::vector<std::string>&& args);\n    Result<void> ParseSetenv(std::vector<std::string>&& args);\n    Result<void> ParseSharedKallsyms(std::vector<std::string>&& args);\n    Result<void> ParseShutdown(std::vector<std::string>&& args);\n    Result<void> ParseSigstop(std::vector<std::string>&& args);\n    Result<void> ParseSocket(std::vector<std::string>&& args);\n    Result<void> ParseStdioToKmsg(std::vector<std::string>&& args);\n    Result<void> ParseTaskProfiles(std::vector<std::string>&& args);\n    Result<void> ParseTimeoutPeriod(std::vector<std::string>&& args);\n    Result<void> ParseFile(std::vector<std::string>&& args);\n    Result<void> ParseUser(std::vector<std::string>&& args);\n    Result<void> ParseWritepid(std::vector<std::string>&& args);\n    Result<void> ParseUpdatable(std::vector<std::string>&& args);\n\n    bool IsValidName(const std::string& name) const;\n\n    ServiceList* service_list_;\n    Subcontext* subcontext_;\n    std::unique_ptr<Service> service_;\n    std::string filename_;\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"service.h\"\n\n#include <algorithm>\n#include <fstream>\n#include <memory>\n#include <type_traits>\n#include <vector>\n\n#include <gtest/gtest.h>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <fstab/fstab.h>\n#include <selinux/selinux.h>\n#include <sys/signalfd.h>\n#include \"lmkd_service.h\"\n#include \"reboot.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n#include \"service_parser.h\"\n#include \"util.h\"\n\nusing ::android::base::ReadFileToString;\nusing ::android::base::StringPrintf;\nusing ::android::base::StringReplace;\nusing ::android::base::unique_fd;\nusing ::android::base::WriteStringToFd;\nusing ::android::base::WriteStringToFile;\n\nnamespace android {\nnamespace init {\n\nstatic std::string GetSecurityContext() {\n    char* ctx;\n    if (getcon(&ctx) == -1) {\n        ADD_FAILURE() << \"Failed to call getcon : \" << strerror(errno);\n    }\n    std::string result{ctx};\n    freecon(ctx);\n    return result;\n}\n\nTEST(service, pod_initialized) {\n    constexpr auto memory_size = sizeof(Service);\n    alignas(alignof(Service)) unsigned char old_memory[memory_size];\n\n    for (std::size_t i = 0; i < memory_size; ++i) {\n        old_memory[i] = 0xFF;\n    }\n\n    std::vector<std::string> dummy_args{\"/bin/test\"};\n    Service* service_in_old_memory =\n        new (old_memory) Service(\"test_old_memory\", nullptr, /*filename=*/\"\", dummy_args);\n\n    EXPECT_EQ(0U, service_in_old_memory->flags());\n    EXPECT_EQ(0, service_in_old_memory->pid());\n    EXPECT_EQ(0, service_in_old_memory->crash_count());\n    EXPECT_EQ(0U, service_in_old_memory->uid());\n    EXPECT_EQ(0U, service_in_old_memory->gid());\n    EXPECT_EQ(0, service_in_old_memory->namespace_flags());\n    EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory->ioprio_class());\n    EXPECT_EQ(0, service_in_old_memory->ioprio_pri());\n    EXPECT_EQ(0, service_in_old_memory->priority());\n    EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory->oom_score_adjust());\n    EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());\n\n    for (std::size_t i = 0; i < memory_size; ++i) {\n        old_memory[i] = 0xFF;\n    }\n\n    Service* service_in_old_memory2 = new (old_memory) Service(\n            \"test_old_memory\", 0U, 0U, 0U, std::vector<gid_t>(), 0U, \"\",\n            nullptr, /*filename=*/\"\", dummy_args);\n\n    EXPECT_EQ(0U, service_in_old_memory2->flags());\n    EXPECT_EQ(0, service_in_old_memory2->pid());\n    EXPECT_EQ(0, service_in_old_memory2->crash_count());\n    EXPECT_EQ(0U, service_in_old_memory2->uid());\n    EXPECT_EQ(0U, service_in_old_memory2->gid());\n    EXPECT_EQ(0, service_in_old_memory2->namespace_flags());\n    EXPECT_EQ(IoSchedClass_NONE, service_in_old_memory2->ioprio_class());\n    EXPECT_EQ(0, service_in_old_memory2->ioprio_pri());\n    EXPECT_EQ(0, service_in_old_memory2->priority());\n    EXPECT_EQ(DEFAULT_OOM_SCORE_ADJUST, service_in_old_memory2->oom_score_adjust());\n    EXPECT_FALSE(service_in_old_memory->process_cgroup_empty());\n}\n\nTEST(service, make_temporary_oneshot_service_invalid_syntax) {\n    std::vector<std::string> args;\n    // Nothing.\n    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());\n\n    // No arguments to 'exec'.\n    args.push_back(\"exec\");\n    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());\n\n    // No command in \"exec --\".\n    args.push_back(\"--\");\n    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());\n}\n\nTEST(service, make_temporary_oneshot_service_too_many_supplementary_gids) {\n    std::vector<std::string> args;\n    args.push_back(\"exec\");\n    args.push_back(\"seclabel\");\n    args.push_back(\"root\");  // uid.\n    args.push_back(\"root\");  // gid.\n    for (int i = 0; i < NR_SVC_SUPP_GIDS; ++i) {\n        args.push_back(\"root\");  // Supplementary gid.\n    }\n    args.push_back(\"--\");\n    args.push_back(\"/system/bin/id\");\n    ASSERT_FALSE(Service::MakeTemporaryOneshotService(args).ok());\n}\n\nstatic void Test_make_temporary_oneshot_service(bool dash_dash, bool seclabel, bool uid, bool gid,\n                                                bool supplementary_gids) {\n    std::vector<std::string> args;\n    args.push_back(\"exec\");\n    if (seclabel) {\n        args.push_back(\"u:r:su:s0\");  // seclabel\n        if (uid) {\n            args.push_back(\"log\");  // uid\n            if (gid) {\n                args.push_back(\"shell\");  // gid\n                if (supplementary_gids) {\n                    args.push_back(\"system\");  // supplementary gid 0\n                    args.push_back(\"adb\");     // supplementary gid 1\n                }\n            }\n        }\n    }\n    if (dash_dash) {\n        args.push_back(\"--\");\n    }\n    args.push_back(\"/system/bin/toybox\");\n    args.push_back(\"id\");\n    auto service_ret = Service::MakeTemporaryOneshotService(args);\n    ASSERT_RESULT_OK(service_ret);\n    auto svc = std::move(*service_ret);\n\n    if (seclabel) {\n        ASSERT_EQ(\"u:r:su:s0\", svc->seclabel());\n    } else {\n        ASSERT_EQ(\"\", svc->seclabel());\n    }\n    if (uid) {\n        auto decoded_uid = DecodeUid(\"log\");\n        ASSERT_RESULT_OK(decoded_uid);\n        ASSERT_EQ(*decoded_uid, svc->uid());\n    } else {\n        ASSERT_EQ(0U, svc->uid());\n    }\n    if (gid) {\n        auto decoded_uid = DecodeUid(\"shell\");\n        ASSERT_RESULT_OK(decoded_uid);\n        ASSERT_EQ(*decoded_uid, svc->gid());\n    } else {\n        ASSERT_EQ(0U, svc->gid());\n    }\n    if (supplementary_gids) {\n        ASSERT_EQ(2U, svc->supp_gids().size());\n\n        auto decoded_uid = DecodeUid(\"system\");\n        ASSERT_RESULT_OK(decoded_uid);\n        ASSERT_EQ(*decoded_uid, svc->supp_gids()[0]);\n\n        decoded_uid = DecodeUid(\"adb\");\n        ASSERT_RESULT_OK(decoded_uid);\n        ASSERT_EQ(*decoded_uid, svc->supp_gids()[1]);\n    } else {\n        ASSERT_EQ(0U, svc->supp_gids().size());\n    }\n\n    ASSERT_EQ(static_cast<std::size_t>(2), svc->args().size());\n    ASSERT_EQ(\"/system/bin/toybox\", svc->args()[0]);\n    ASSERT_EQ(\"id\", svc->args()[1]);\n}\n\nTEST(service, make_temporary_oneshot_service_with_everything) {\n    Test_make_temporary_oneshot_service(true, true, true, true, true);\n}\n\nTEST(service, make_temporary_oneshot_service_with_seclabel_uid_gid) {\n    Test_make_temporary_oneshot_service(true, true, true, true, false);\n}\n\nTEST(service, make_temporary_oneshot_service_with_seclabel_uid) {\n    Test_make_temporary_oneshot_service(true, true, true, false, false);\n}\n\nTEST(service, make_temporary_oneshot_service_with_seclabel) {\n    Test_make_temporary_oneshot_service(true, true, false, false, false);\n}\n\nTEST(service, make_temporary_oneshot_service_with_just_command) {\n    Test_make_temporary_oneshot_service(true, false, false, false, false);\n}\n\nTEST(service, make_temporary_oneshot_service_with_just_command_no_dash) {\n    Test_make_temporary_oneshot_service(false, false, false, false, false);\n}\n\n// Returns the path in the v2 cgroup hierarchy for a given process in the format /uid_%d/pid_%d.\nstatic std::string CgroupPath(pid_t pid) {\n    std::string cgroup_path = StringPrintf(\"/proc/%d/cgroup\", pid);\n    std::ifstream is(cgroup_path, std::ios::in);\n    std::string line;\n    while (std::getline(is, line)) {\n        if (line.substr(0, 3) == \"0::\") {\n            return line.substr(3);\n        }\n    }\n    return {};\n}\n\nclass ServiceStopTest : public testing::TestWithParam<bool> {};\n\n// Before November 2023, processes that were migrated to another v2 cgroup were ignored by\n// Service::Stop() if their uid_%d/pid_%d cgroup directory got removed. This test, if run with the\n// parameter set to 'true', verifies that such services are stopped.\nTEST_P(ServiceStopTest, stop) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n\n    static constexpr std::string_view kServiceName = \"ServiceA\";\n    static constexpr std::string_view kScriptTemplate = R\"init(\nservice $name /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n)init\";\n\n    std::string script = StringReplace(StringReplace(kScriptTemplate, \"$name\", kServiceName, false),\n                                       \"$selabel\", GetSecurityContext(), false);\n    ServiceList& service_list = ServiceList::GetInstance();\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&service_list, nullptr));\n\n    TemporaryFile tf;\n    ASSERT_GE(tf.fd, 0);\n    ASSERT_TRUE(WriteStringToFd(script, tf.fd));\n    ASSERT_TRUE(parser.ParseConfig(tf.path));\n\n    Service* const service = ServiceList::GetInstance().FindService(kServiceName);\n    ASSERT_NE(service, nullptr);\n    ASSERT_RESULT_OK(service->Start());\n    ASSERT_TRUE(service->IsRunning());\n    if (GetParam()) {\n        const pid_t pid = service->pid();\n        const std::string cgroup_path = CgroupPath(pid);\n        EXPECT_NE(cgroup_path, \"\");\n        EXPECT_NE(cgroup_path, \"/\");\n        const std::string pid_str = std::to_string(pid);\n        EXPECT_TRUE(WriteStringToFile(pid_str, \"/sys/fs/cgroup/cgroup.procs\"));\n        EXPECT_EQ(CgroupPath(pid), \"/\");\n        EXPECT_EQ(rmdir((\"/sys/fs/cgroup\" + cgroup_path).c_str()), 0);\n    }\n    EXPECT_EQ(0, StopServicesAndLogViolations({service->name()}, 10s, /*terminate=*/true));\n    ServiceList::GetInstance().RemoveService(*service);\n}\n\nINSTANTIATE_TEST_SUITE_P(service, ServiceStopTest, testing::Values(false, true));\n\n// Entering a network namespace requires remounting sysfs to update contents of\n// /sys/class/net whose contents depend on the network namespace of the process\n// that mounted it rather than the effective network namespace of the reading\n// process.\n//\n// A side effect of the remounting is unmounting all filesystems mounted under\n// /sys, like tracefs. Verify that init doesn't leave them unmounted by\n// accident.\nTEST(service, enter_namespace_net_preserves_mounts) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Must be run as root.\";\n        return;\n    }\n\n    struct ScopedNetNs {\n        std::string name;\n        ScopedNetNs(std::string n) : name(n) {\n            EXPECT_EQ(system((\"/system/bin/ip netns add \" + name).c_str()), 0);\n        }\n        ~ScopedNetNs() { EXPECT_EQ(system((\"/system/bin/ip netns delete \" + name).c_str()), 0); }\n    };\n    const ScopedNetNs netns(\"test_ns\");\n\n    static constexpr std::string_view kServiceName = \"ServiceA\";\n    static constexpr std::string_view kScriptTemplate = R\"init(\nservice $name /system/bin/yes\n    user shell\n    group shell\n    seclabel $selabel\n    enter_namespace net /mnt/run/$ns_name\n)init\";\n\n    std::string script = StringReplace(kScriptTemplate, \"$name\", kServiceName, false);\n    script = StringReplace(script, \"$selabel\", GetSecurityContext(), false);\n    script = StringReplace(script, \"$ns_name\", netns.name, false);\n\n    ServiceList& service_list = ServiceList::GetInstance();\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&service_list, nullptr));\n\n    TemporaryFile tf;\n    ASSERT_GE(tf.fd, 0);\n    ASSERT_TRUE(WriteStringToFd(script, tf.fd));\n    ASSERT_TRUE(parser.ParseConfig(tf.path));\n\n    Service* const service = ServiceList::GetInstance().FindService(kServiceName);\n    ASSERT_NE(service, nullptr);\n    ASSERT_RESULT_OK(service->Start());\n    ASSERT_TRUE(service->IsRunning());\n\n    android::fs_mgr::Fstab root_mounts;\n    ASSERT_TRUE(ReadFstabFromFile(\"/proc/mounts\", &root_mounts));\n\n    android::fs_mgr::Fstab ns_mounts;\n    ASSERT_TRUE(ReadFstabFromFile(StringReplace(\"/proc/$pid/mounts\", \"$pid\",\n                                                std::to_string(service->pid()), /*all=*/false),\n                                  &ns_mounts));\n\n    for (const auto& expected_mount : root_mounts) {\n        auto it = std::find_if(ns_mounts.begin(), ns_mounts.end(), [&](const auto& ns_mount) {\n            return ns_mount.mount_point == expected_mount.mount_point;\n        });\n        EXPECT_TRUE(it != ns_mounts.end()) << StringPrintf(\n                \"entering network namespace unmounted %s\", expected_mount.mount_point.c_str());\n    }\n\n    ServiceList::GetInstance().RemoveService(*service);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service_utils.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"service_utils.h\"\n\n#include <fcntl.h>\n#include <grp.h>\n#include <sys/mount.h>\n#include <sys/prctl.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <map>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <cutils/android_get_control_file.h>\n#include <cutils/sockets.h>\n#include <fstab/fstab.h>\n#include <processgroup/processgroup.h>\n\n#include \"mount_namespace.h\"\n#include \"util.h\"\n\nusing android::base::GetProperty;\nusing android::base::StartsWith;\nusing android::base::StringPrintf;\nusing android::base::unique_fd;\nusing android::base::WriteStringToFile;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nResult<void> EnterNamespace(int nstype, const char* path) {\n    auto fd = unique_fd{open(path, O_RDONLY | O_CLOEXEC)};\n    if (fd == -1) {\n        return ErrnoError() << \"Could not open namespace at \" << path;\n    }\n    if (setns(fd.get(), nstype) == -1) {\n        return ErrnoError() << \"Could not setns() namespace at \" << path;\n    }\n    return {};\n}\n\nResult<void> SetUpMountNamespace(bool remount_proc, bool remount_sys) {\n    constexpr unsigned int kSafeFlags = MS_NODEV | MS_NOEXEC | MS_NOSUID;\n\n    // Recursively remount / as MS_SLAVE like zygote does so that\n    // unmounting and mounting /proc doesn't interfere with the parent\n    // namespace's /proc mount. This will also prevent any other\n    // mounts/unmounts initiated by the service from interfering with the\n    // parent namespace but will still allow mount events from the parent\n    // namespace to propagate to the child.\n    if (mount(\"rootfs\", \"/\", nullptr, (MS_SLAVE | MS_REC), nullptr) == -1) {\n        return ErrnoError() << \"Could not remount(/) recursively as MS_SLAVE\";\n    }\n\n    // umount() then mount() /proc and/or /sys\n    // Note that it is not sufficient to mount with MS_REMOUNT.\n    if (remount_proc) {\n        if (umount(\"/proc\") == -1) {\n            return ErrnoError() << \"Could not umount(/proc)\";\n        }\n        if (mount(\"\", \"/proc\", \"proc\", kSafeFlags, \"\") == -1) {\n            return ErrnoError() << \"Could not mount(/proc)\";\n        }\n    }\n    if (remount_sys) {\n        android::fs_mgr::Fstab mounts;\n        if (!ReadFstabFromFile(\"/proc/mounts\", &mounts)) {\n            LOG(ERROR) << \"Could not read /proc/mounts\";\n        }\n        if (umount2(\"/sys\", MNT_DETACH) == -1) {\n            return ErrnoError() << \"Could not umount(/sys)\";\n        }\n        if (mount(\"sysfs\", \"/sys\", \"sysfs\", kSafeFlags, \"\") == -1) {\n            return ErrnoError() << \"Could not mount(/sys)\";\n        }\n        // Unmounting /sys also unmounts all nested mounts like tracefs.\n        //\n        // Look up the filesystems that were mounted under /sys before we wiped\n        // it and attempt to restore them.\n        for (const auto& entry : mounts) {\n            // Never mount /sys/kernel/debug/tracing. This is the *one* mount\n            // that is special within Linux kernel: for backward compatibility\n            // tracefs gets auto-mounted there whenever one mounts debugfs [1].\n            //\n            // Attempting to mount the filesystem here will cause SELinux\n            // denials, because unlike *all other* filesystems in Android, it's\n            // not init who mounted it so there's no policy that would allow it.\n            //\n            // [1] https://lore.kernel.org/lkml/20150204143755.694479564@goodmis.org/\n            if (entry.mount_point.starts_with(\"/sys/\") &&\n                entry.mount_point != \"/sys/kernel/debug/tracing\") {\n                if (mount(entry.blk_device.c_str(), entry.mount_point.c_str(),\n                          entry.fs_type.c_str(), entry.flags, \"\")) {\n                    LOG(WARNING) << \"Could not mount(\" << entry.mount_point\n                                 << \") after switching netns: \" << ErrnoError().str();\n                }\n            }\n        }\n    }\n    return {};\n}\n\nResult<void> SetUpPidNamespace(const char* name) {\n    if (prctl(PR_SET_NAME, name) == -1) {\n        return ErrnoError() << \"Could not set name\";\n    }\n\n    pid_t child_pid = fork();\n    if (child_pid == -1) {\n        return ErrnoError() << \"Could not fork init inside the PID namespace\";\n    }\n\n    if (child_pid > 0) {\n        // So that we exit with the right status.\n        static int init_exitstatus = 0;\n        signal(SIGTERM, [](int) { _exit(init_exitstatus); });\n\n        pid_t waited_pid;\n        int status;\n        while ((waited_pid = wait(&status)) > 0) {\n            // This loop will end when there are no processes left inside the\n            // PID namespace or when the init process inside the PID namespace\n            // gets a signal.\n            if (waited_pid == child_pid) {\n                init_exitstatus = status;\n            }\n        }\n        if (!WIFEXITED(init_exitstatus)) {\n            _exit(EXIT_FAILURE);\n        }\n        _exit(WEXITSTATUS(init_exitstatus));\n    }\n    return {};\n}\n\nvoid SetupStdio(bool stdio_to_kmsg) {\n    auto fd = unique_fd{open(\"/dev/null\", O_RDWR | O_CLOEXEC)};\n    dup2(fd.get(), STDIN_FILENO);\n    if (stdio_to_kmsg) {\n        fd.reset(open(\"/dev/kmsg_debug\", O_WRONLY | O_CLOEXEC));\n        if (fd == -1) fd.reset(open(\"/dev/null\", O_WRONLY | O_CLOEXEC));\n    }\n    dup2(fd.get(), STDOUT_FILENO);\n    dup2(fd.get(), STDERR_FILENO);\n}\n\nvoid OpenConsole(const std::string& console) {\n    auto fd = unique_fd{open(console.c_str(), O_RDWR | O_CLOEXEC)};\n    if (fd == -1) fd.reset(open(\"/dev/null\", O_RDWR | O_CLOEXEC));\n    ioctl(fd.get(), TIOCSCTTY, 0);\n    dup2(fd.get(), 0);\n    dup2(fd.get(), 1);\n    dup2(fd.get(), 2);\n}\n\n}  // namespace\n\nvoid Descriptor::Publish() const {\n    auto published_name = name_;\n\n    for (auto& c : published_name) {\n        c = isalnum(c) ? c : '_';\n    }\n\n    int fd = fd_.get();\n    // For safety, the FD is created as CLOEXEC, so that must be removed before publishing.\n    auto fd_flags = fcntl(fd, F_GETFD);\n    fd_flags &= ~FD_CLOEXEC;\n    if (fcntl(fd, F_SETFD, fd_flags) != 0) {\n        PLOG(ERROR) << \"Failed to remove CLOEXEC from '\" << published_name << \"'\";\n    }\n\n    std::string val = std::to_string(fd);\n    setenv(published_name.c_str(), val.c_str(), 1);\n}\n\nResult<Descriptor> SocketDescriptor::Create(const std::string& global_context) const {\n    const auto& socket_context = context.empty() ? global_context : context;\n    auto result = CreateSocket(name, type | SOCK_CLOEXEC, passcred, listen, perm, uid, gid,\n                               socket_context);\n    if (!result.ok()) {\n        return result.error();\n    }\n\n    return Descriptor(ANDROID_SOCKET_ENV_PREFIX + name, unique_fd(*result));\n}\n\nResult<Descriptor> FileDescriptor::Create() const {\n    int flags = (type == \"r\") ? O_RDONLY : (type == \"w\") ? O_WRONLY : O_RDWR;\n\n    // Make sure we do not block on open (eg: devices can chose to block on carrier detect).  Our\n    // intention is never to delay launch of a service for such a condition.  The service can\n    // perform its own blocking on carrier detect.\n    unique_fd fd(TEMP_FAILURE_RETRY(open(name.c_str(), flags | O_NONBLOCK | O_CLOEXEC)));\n\n    if (fd < 0) {\n        return ErrnoError() << \"Failed to open file '\" << name << \"'\";\n    }\n\n    // Fixup as we set O_NONBLOCK for open, the intent for fd is to block reads.\n    fcntl(fd.get(), F_SETFL, flags);\n\n    return Descriptor(ANDROID_FILE_ENV_PREFIX + name, std::move(fd));\n}\n\nResult<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,\n                             std::optional<MountNamespace> override_mount_namespace) {\n    for (const auto& [nstype, path] : info.namespaces_to_enter) {\n        if (auto result = EnterNamespace(nstype, path.c_str()); !result.ok()) {\n            return result;\n        }\n    }\n\n#if defined(__ANDROID__)\n    if (override_mount_namespace.has_value()) {\n        if (auto result = SwitchToMountNamespaceIfNeeded(override_mount_namespace.value());\n            !result.ok()) {\n            return result;\n        }\n    }\n#endif\n\n    if (info.flags & CLONE_NEWNS) {\n        bool remount_proc = info.flags & CLONE_NEWPID;\n        bool remount_sys =\n                std::any_of(info.namespaces_to_enter.begin(), info.namespaces_to_enter.end(),\n                            [](const auto& entry) { return entry.first == CLONE_NEWNET; });\n        if (auto result = SetUpMountNamespace(remount_proc, remount_sys); !result.ok()) {\n            return result;\n        }\n    }\n\n    if (info.flags & CLONE_NEWPID) {\n        // This will fork again to run an init process inside the PID namespace.\n        if (auto result = SetUpPidNamespace(name.c_str()); !result.ok()) {\n            return result;\n        }\n    }\n\n    return {};\n}\n\nResult<void> SetProcessAttributes(const ProcessAttributes& attr, InterprocessFifo setsid_finished) {\n    if (attr.ioprio_class != IoSchedClass_NONE) {\n        if (android_set_ioprio(getpid(), attr.ioprio_class, attr.ioprio_pri)) {\n            PLOG(ERROR) << \"failed to set pid \" << getpid() << \" ioprio=\" << attr.ioprio_class\n                        << \",\" << attr.ioprio_pri;\n        }\n    }\n\n    if (RequiresConsole(attr)) {\n        setsid();\n        setsid_finished.Write(kSetSidFinished);\n        setsid_finished.Close();\n        OpenConsole(attr.console);\n    } else {\n        // Without PID namespaces, this call duplicates the setpgid() call from\n        // the parent process. With PID namespaces, this setpgid() call sets the\n        // process group ID for a child of the init process in the PID\n        // namespace.\n        if (setpgid(0, 0) == -1) {\n            return ErrnoError() << \"setpgid failed\";\n        }\n        SetupStdio(attr.stdio_to_kmsg);\n    }\n\n    for (const auto& rlimit : attr.rlimits) {\n        if (setrlimit(rlimit.first, &rlimit.second) == -1) {\n            return ErrnoErrorf(\"setrlimit({}, {{rlim_cur={}, rlim_max={}}}) failed\", rlimit.first,\n                               rlimit.second.rlim_cur, rlimit.second.rlim_max);\n        }\n    }\n\n    if (attr.gid) {\n        if (setgid(attr.gid) != 0) {\n            return ErrnoError() << \"setgid failed\";\n        }\n    }\n    if (setgroups(attr.supp_gids.size(), const_cast<gid_t*>(&attr.supp_gids[0])) != 0) {\n        return ErrnoError() << \"setgroups failed\";\n    }\n    if (attr.uid()) {\n        if (setuid(attr.uid()) != 0) {\n            return ErrnoError() << \"setuid failed\";\n        }\n    }\n\n    if (attr.priority != 0) {\n        if (setpriority(PRIO_PROCESS, 0, attr.priority) != 0) {\n            return ErrnoError() << \"setpriority failed\";\n        }\n    }\n    return {};\n}\n\nResult<void> WritePidToFiles(std::vector<std::string>* files) {\n    if (files->empty()) {\n        // No files to write pid to, exit early.\n        return {};\n    }\n\n    if (!CgroupsAvailable()) {\n        return Error() << \"cgroups are not available\";\n    }\n\n    // See if there were \"writepid\" instructions to write to files under cpuset path.\n    std::string cpuset_path;\n    if (CgroupGetControllerPath(\"cpuset\", &cpuset_path)) {\n        auto cpuset_predicate = [&cpuset_path](const std::string& path) {\n            return StartsWith(path, cpuset_path + \"/\");\n        };\n        auto iter = std::find_if(files->begin(), files->end(), cpuset_predicate);\n        if (iter == files->end()) {\n            // There were no \"writepid\" instructions for cpusets, check if the system default\n            // cpuset is specified to be used for the process.\n            std::string default_cpuset = GetProperty(\"ro.cpuset.default\", \"\");\n            if (!default_cpuset.empty()) {\n                // Make sure the cpuset name starts and ends with '/'.\n                // A single '/' means the 'root' cpuset.\n                if (default_cpuset.front() != '/') {\n                    default_cpuset.insert(0, 1, '/');\n                }\n                if (default_cpuset.back() != '/') {\n                    default_cpuset.push_back('/');\n                }\n                files->push_back(\n                        StringPrintf(\"%s%stasks\", cpuset_path.c_str(), default_cpuset.c_str()));\n            }\n        }\n    } else {\n        LOG(ERROR) << \"cpuset cgroup controller is not mounted!\";\n    }\n\n    // Issue a warning whenever writepid is being used with a cgroup. This can't be done during\n    // command parsing because cgroups might not be configured at the time or parsing.\n    for (const auto& file : *files) {\n        if (CgroupGetControllerFromPath(file, nullptr)) {\n            LOG(WARNING) << \"writepid usage with cgroups path '\" << file\n                         << \"' is obsolete, please use task_profiles!\";\n        }\n    }\n\n    std::string pid_str = std::to_string(getpid());\n    for (const auto& file : *files) {\n        if (!WriteStringToFile(pid_str, file)) {\n            return ErrnoError() << \"couldn't write \" << pid_str << \" to \" << file;\n        }\n    }\n    return {};\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/service_utils.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/resource.h>\n#include <sys/types.h>\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <cutils/iosched_policy.h>\n\n#include \"interprocess_fifo.h\"\n#include \"mount_namespace.h\"\n#include \"result.h\"\n\nnamespace android {\nnamespace init {\n\n// Constants used by Service::Start() for communication between parent and child.\nenum ServiceCode : uint8_t {\n    kActivatingCgroupsFailed,\n    kCgroupsActivated,\n    kSetSidFinished,\n};\n\nclass Descriptor {\n  public:\n    Descriptor(const std::string& name, android::base::unique_fd fd)\n        : name_(name), fd_(std::move(fd)){};\n\n    // Publish() unsets FD_CLOEXEC from the FD and publishes its name via setenv().  It should be\n    // called when starting a service after fork() and before exec().\n    void Publish() const;\n\n  private:\n    std::string name_;\n    android::base::unique_fd fd_;\n};\n\nstruct SocketDescriptor {\n    std::string name;\n    int type = 0;\n    uid_t uid = 0;\n    gid_t gid = 0;\n    int perm = 0;\n    std::string context;\n    bool passcred = false;\n    bool listen = false;\n    bool persist = false;\n\n    // Create() creates the named unix domain socket in /dev/socket and returns a Descriptor object.\n    // It should be called when starting a service, before calling fork(), such that the socket is\n    // synchronously created before starting any other services, which may depend on it.\n    Result<Descriptor> Create(const std::string& global_context) const;\n};\n\nstruct FileDescriptor {\n    std::string name;\n    std::string type;\n\n    Result<Descriptor> Create() const;\n};\n\nstruct NamespaceInfo {\n    int flags;\n    // Pair of namespace type, path to name.\n    std::vector<std::pair<int, std::string>> namespaces_to_enter;\n};\nResult<void> EnterNamespaces(const NamespaceInfo& info, const std::string& name,\n                             std::optional<MountNamespace> override_mount_namespace);\n\nstruct ProcessAttributes {\n    std::string console;\n    IoSchedClass ioprio_class;\n    int ioprio_pri;\n    std::vector<std::pair<int, rlimit>> rlimits;\n    std::optional<uid_t> parsed_uid;\n    gid_t gid;\n    std::vector<gid_t> supp_gids;\n    int priority;\n    bool stdio_to_kmsg;\n\n    uid_t uid() const { return parsed_uid.value_or(0); }\n};\n\ninline bool RequiresConsole(const ProcessAttributes& attr) {\n    return !attr.console.empty();\n}\n\nResult<void> SetProcessAttributes(const ProcessAttributes& attr, InterprocessFifo setsid_finished);\n\nResult<void> WritePidToFiles(std::vector<std::string>* files);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/sigchld_handler.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"sigchld_handler.h\"\n\n#include <signal.h>\n#include <string.h>\n#include <sys/signalfd.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/scopeguard.h>\n#include <android-base/stringprintf.h>\n\n#include <thread>\n\n#include \"epoll.h\"\n#include \"init.h\"\n#include \"service.h\"\n#include \"service_list.h\"\n\nusing android::base::boot_clock;\nusing android::base::make_scope_guard;\nusing android::base::ReadFileToString;\nusing android::base::StringPrintf;\nusing android::base::Timer;\n\nnamespace android {\nnamespace init {\n\nstatic pid_t ReapOneProcess() {\n    siginfo_t siginfo = {};\n    // This returns a zombie pid or informs us that there are no zombies left to be reaped.\n    // It does NOT reap the pid; that is done below.\n    if (TEMP_FAILURE_RETRY(waitid(P_ALL, 0, &siginfo, WEXITED | WNOHANG | WNOWAIT)) != 0) {\n        PLOG(ERROR) << \"waitid failed\";\n        return 0;\n    }\n\n    const pid_t pid = siginfo.si_pid;\n    if (pid == 0) {\n        DCHECK_EQ(siginfo.si_signo, 0);\n        return 0;\n    }\n\n    DCHECK_EQ(siginfo.si_signo, SIGCHLD);\n\n    // At this point we know we have a zombie pid, so we use this scopeguard to reap the pid\n    // whenever the function returns from this point forward.\n    // We do NOT want to reap the zombie earlier as in Service::Reap(), we kill(-pid, ...) and we\n    // want the pid to remain valid throughout that (and potentially future) usages.\n    auto reaper = make_scope_guard([pid] { TEMP_FAILURE_RETRY(waitpid(pid, nullptr, WNOHANG)); });\n\n    std::string name;\n    std::string wait_string;\n    Service* service = nullptr;\n\n    if (SubcontextChildReap(pid)) {\n        name = \"Subcontext\";\n    } else {\n        service = ServiceList::GetInstance().FindService(pid, &Service::pid);\n\n        if (service) {\n            name = StringPrintf(\"Service '%s' (pid %d)\", service->name().c_str(), pid);\n            if (service->flags() & SVC_EXEC) {\n                auto exec_duration = boot_clock::now() - service->time_started();\n                auto exec_duration_ms =\n                    std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration).count();\n                wait_string = StringPrintf(\" waiting took %f seconds\", exec_duration_ms / 1000.0f);\n            } else if (service->flags() & SVC_ONESHOT) {\n                auto exec_duration = boot_clock::now() - service->time_started();\n                auto exec_duration_ms =\n                        std::chrono::duration_cast<std::chrono::milliseconds>(exec_duration)\n                                .count();\n                wait_string = StringPrintf(\" oneshot service took %f seconds in background\",\n                                           exec_duration_ms / 1000.0f);\n            }\n        } else {\n            name = StringPrintf(\"Untracked pid %d\", pid);\n        }\n    }\n\n    if (siginfo.si_code == CLD_EXITED) {\n        LOG(INFO) << name << \" exited with status \" << siginfo.si_status << wait_string;\n    } else {\n        LOG(INFO) << name << \" received signal \" << siginfo.si_status << wait_string;\n    }\n\n    if (!service) {\n        LOG(INFO) << name << \" did not have an associated service entry and will not be reaped\";\n        return pid;\n    }\n\n    service->Reap(siginfo);\n\n    if (service->flags() & SVC_TEMPORARY) {\n        ServiceList::GetInstance().RemoveService(*service);\n    }\n\n    return pid;\n}\n\nstd::set<pid_t> ReapAnyOutstandingChildren() {\n    std::set<pid_t> reaped_pids;\n    for (;;) {\n        const pid_t pid = ReapOneProcess();\n        if (pid <= 0) {\n            return reaped_pids;\n        }\n        reaped_pids.emplace(pid);\n    }\n}\n\nstatic void ReapAndRemove(std::vector<pid_t>& alive_pids) {\n    for (auto pid : ReapAnyOutstandingChildren()) {\n        const auto it = std::find(alive_pids.begin(), alive_pids.end(), pid);\n        if (it != alive_pids.end()) {\n            alive_pids.erase(it);\n        }\n    }\n}\n\nstatic void HandleSignal(int signal_fd) {\n    signalfd_siginfo siginfo;\n    ssize_t bytes_read = TEMP_FAILURE_RETRY(read(signal_fd, &siginfo, sizeof(siginfo)));\n    if (bytes_read != sizeof(siginfo)) {\n        LOG(WARNING) << \"Unexpected: \" << __func__ << \" read \" << bytes_read << \" bytes instead of \"\n                     << sizeof(siginfo);\n    }\n}\n\nvoid WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,\n                    std::chrono::milliseconds timeout) {\n    Timer t;\n    Epoll epoll;\n    if (sigchld_fd >= 0) {\n        if (auto result = epoll.Open(); result.ok()) {\n            result =\n                    epoll.RegisterHandler(sigchld_fd, [sigchld_fd]() { HandleSignal(sigchld_fd); });\n            if (!result.ok()) {\n                LOG(WARNING) << __func__\n                             << \" RegisterHandler() failed. Falling back to sleep_for(): \"\n                             << result.error();\n                sigchld_fd = -1;\n            }\n        } else {\n            LOG(WARNING) << __func__ << \" Epoll::Open() failed. Falling back to sleep_for(): \"\n                         << result.error();\n            sigchld_fd = -1;\n        }\n    }\n    std::vector<pid_t> alive_pids(pids);\n    ReapAndRemove(alive_pids);\n    while (!alive_pids.empty() && t.duration() < timeout) {\n        if (sigchld_fd >= 0) {\n            auto result = epoll.Wait(std::max(timeout - t.duration(), 0ms));\n            if (result.ok()) {\n                ReapAndRemove(alive_pids);\n                continue;\n            } else {\n                LOG(WARNING) << \"Epoll::Wait() failed \" << result.error();\n            }\n        }\n        std::this_thread::sleep_for(50ms);\n        ReapAndRemove(alive_pids);\n    }\n    LOG(INFO) << \"Waiting for \" << pids.size() << \" pids to be reaped took \" << t << \" with \"\n              << alive_pids.size() << \" of them still running\";\n    for (pid_t pid : alive_pids) {\n        std::string status = \"(no-such-pid)\";\n        ReadFileToString(StringPrintf(\"/proc/%d/status\", pid), &status);\n        LOG(INFO) << \"Still running: \" << pid << '\\n' << status;\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/sigchld_handler.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_SIGCHLD_HANDLER_H_\n#define _INIT_SIGCHLD_HANDLER_H_\n\n#include <chrono>\n#include <set>\n#include <vector>\n\nnamespace android {\nnamespace init {\n\nstd::set<pid_t> ReapAnyOutstandingChildren();\n\nvoid WaitToBeReaped(int sigchld_fd, const std::vector<pid_t>& pids,\n                    std::chrono::milliseconds timeout);\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/snapuserd_transition.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"snapuserd_transition.h\"\n\n#include <sys/mman.h>\n#include <sys/socket.h>\n#include <sys/syscall.h>\n#include <sys/xattr.h>\n#include <unistd.h>\n\n#include <filesystem>\n#include <string>\n#include <string_view>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <cutils/sockets.h>\n#include <fs_avb/fs_avb.h>\n#include <libsnapshot/snapshot.h>\n#include <private/android_filesystem_config.h>\n#include <procinfo/process_map.h>\n#include <selinux/android.h>\n#include <snapuserd/snapuserd_client.h>\n\n#include \"block_dev_initializer.h\"\n#include \"lmkd_service.h\"\n#include \"service_utils.h\"\n#include \"util.h\"\n\nnamespace android {\nnamespace init {\n\nusing namespace std::string_literals;\n\nusing android::base::unique_fd;\nusing android::snapshot::SnapshotManager;\nusing android::snapshot::SnapuserdClient;\n\nstatic constexpr char kSnapuserdPath[] = \"/system/bin/snapuserd\";\nstatic constexpr char kSnapuserdFirstStagePidVar[] = \"FIRST_STAGE_SNAPUSERD_PID\";\nstatic constexpr char kSnapuserdFirstStageFdVar[] = \"FIRST_STAGE_SNAPUSERD_FD\";\nstatic constexpr char kSnapuserdFirstStageInfoVar[] = \"FIRST_STAGE_SNAPUSERD_INFO\";\nstatic constexpr char kSnapuserdLabel[] = \"u:object_r:snapuserd_exec:s0\";\nstatic constexpr char kSnapuserdSocketLabel[] = \"u:object_r:snapuserd_socket:s0\";\n\nvoid LaunchFirstStageSnapuserd() {\n    SocketDescriptor socket_desc;\n    socket_desc.name = android::snapshot::kSnapuserdSocket;\n    socket_desc.type = SOCK_STREAM;\n    socket_desc.perm = 0660;\n    socket_desc.uid = AID_SYSTEM;\n    socket_desc.gid = AID_SYSTEM;\n\n    // We specify a label here even though it technically is not needed. During\n    // first_stage_mount there is no sepolicy loaded. Once sepolicy is loaded,\n    // we bypass the socket entirely.\n    auto socket = socket_desc.Create(kSnapuserdSocketLabel);\n    if (!socket.ok()) {\n        LOG(FATAL) << \"Could not create snapuserd socket: \" << socket.error();\n    }\n\n    pid_t pid = fork();\n    if (pid < 0) {\n        PLOG(FATAL) << \"Cannot launch snapuserd; fork failed\";\n    }\n    if (pid == 0) {\n        socket->Publish();\n\n        char arg0[] = \"/system/bin/snapuserd\";\n        char arg1[] = \"-user_snapshot\";\n        char* const argv[] = {arg0, arg1, nullptr};\n        if (execv(arg0, argv) < 0) {\n            PLOG(FATAL) << \"Cannot launch snapuserd; execv failed\";\n        }\n        _exit(127);\n    }\n\n    auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 10s);\n    if (!client) {\n        LOG(FATAL) << \"Could not connect to first-stage snapuserd\";\n    }\n    if (client->SupportsSecondStageSocketHandoff()) {\n        setenv(kSnapuserdFirstStageInfoVar, \"socket\", 1);\n        auto sm = SnapshotManager::NewForFirstStageMount();\n        if (!sm->MarkSnapuserdFromSystem()) {\n            LOG(ERROR) << \"Failed to update MarkSnapuserdFromSystem\";\n        }\n    }\n\n    setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);\n\n    if (!client->RemoveTransitionedDaemonIndicator()) {\n        LOG(ERROR) << \"RemoveTransitionedDaemonIndicator failed\";\n    }\n\n    LOG(INFO) << \"Relaunched snapuserd with pid: \" << pid;\n}\n\nstd::optional<pid_t> GetSnapuserdFirstStagePid() {\n    const char* pid_str = getenv(kSnapuserdFirstStagePidVar);\n    if (!pid_str) {\n        return {};\n    }\n\n    int pid = 0;\n    if (!android::base::ParseInt(pid_str, &pid)) {\n        LOG(FATAL) << \"Could not parse pid in environment, \" << kSnapuserdFirstStagePidVar << \"=\"\n                   << pid_str;\n    }\n    return {pid};\n}\n\nstatic void RelabelLink(const std::string& link) {\n    selinux_android_restorecon(link.c_str(), 0);\n\n    std::string path;\n    if (android::base::Readlink(link, &path)) {\n        selinux_android_restorecon(path.c_str(), 0);\n    }\n}\n\nstatic void RelabelDeviceMapper() {\n    selinux_android_restorecon(\"/dev/device-mapper\", 0);\n\n    std::error_code ec;\n    for (auto& iter : std::filesystem::directory_iterator(\"/dev/block\", ec)) {\n        const auto& path = iter.path();\n        if (android::base::StartsWith(path.string(), \"/dev/block/dm-\")) {\n            selinux_android_restorecon(path.string().c_str(), 0);\n        }\n    }\n}\n\nstatic std::optional<int> GetRamdiskSnapuserdFd() {\n    const char* fd_str = getenv(kSnapuserdFirstStageFdVar);\n    if (!fd_str) {\n        return {};\n    }\n\n    int fd;\n    if (!android::base::ParseInt(fd_str, &fd)) {\n        LOG(FATAL) << \"Could not parse fd in environment, \" << kSnapuserdFirstStageFdVar << \"=\"\n                   << fd_str;\n    }\n    return {fd};\n}\n\nvoid RestoreconRamdiskSnapuserd(int fd) {\n    if (fsetxattr(fd, XATTR_NAME_SELINUX, kSnapuserdLabel, strlen(kSnapuserdLabel) + 1, 0) < 0) {\n        PLOG(FATAL) << \"fsetxattr snapuserd failed\";\n    }\n}\n\nSnapuserdSelinuxHelper::SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid)\n    : sm_(std::move(sm)), old_pid_(old_pid) {\n    // Only dm-user device names change during transitions, so the other\n    // devices are expected to be present.\n    sm_->SetUeventRegenCallback([this](const std::string& device) -> bool {\n        if (android::base::StartsWith(device, \"/dev/dm-user/\")) {\n            return block_dev_init_.InitDmUser(android::base::Basename(device));\n        }\n        return true;\n    });\n}\n\nstatic void LockAllSystemPages() {\n    bool ok = true;\n    auto callback = [&](const android::procinfo::MapInfo& map) -> void {\n        if (!ok || android::base::StartsWith(map.name, \"/dev/\") ||\n            !android::base::StartsWith(map.name, \"/\")) {\n            return;\n        }\n        auto start = reinterpret_cast<const void*>(map.start);\n        uint64_t len = android::procinfo::MappedFileSize(map);\n        if (!len) {\n            return;\n        }\n\n        if (mlock(start, len) < 0) {\n            PLOG(ERROR) << \"\\\"\" << map.name << \"\\\": mlock(\" << start << \", \" << len\n                        << \") failed: pgoff = \" << map.pgoff;\n            ok = false;\n        }\n    };\n\n    if (!android::procinfo::ReadProcessMaps(getpid(), callback) || !ok) {\n        LOG(FATAL) << \"Could not process /proc/\" << getpid() << \"/maps file for init\";\n    }\n}\n\nvoid SnapuserdSelinuxHelper::StartTransition() {\n    LOG(INFO) << \"Starting SELinux transition of snapuserd\";\n\n    // The restorecon path reads from /system etc, so make sure any reads have\n    // been cached before proceeding.\n    auto handle = selinux_android_file_context_handle();\n    if (!handle) {\n        LOG(FATAL) << \"Could not create SELinux file context handle\";\n    }\n    selinux_android_set_sehandle(handle);\n\n    // We cannot access /system after the transition, so make sure init is\n    // pinned in memory.\n    LockAllSystemPages();\n\n    argv_.emplace_back(\"snapuserd\");\n    argv_.emplace_back(\"-no_socket\");\n    if (!sm_->PrepareSnapuserdArgsForSelinux(&argv_)) {\n        LOG(FATAL) << \"Could not perform selinux transition\";\n    }\n}\n\nvoid SnapuserdSelinuxHelper::FinishTransition() {\n    RelabelLink(\"/dev/block/by-name/super\");\n    RelabelDeviceMapper();\n\n    selinux_android_restorecon(\"/dev/null\", 0);\n    selinux_android_restorecon(\"/dev/urandom\", 0);\n    selinux_android_restorecon(\"/dev/kmsg\", 0);\n    selinux_android_restorecon(\"/dev/dm-user\", SELINUX_ANDROID_RESTORECON_RECURSE);\n\n    RelaunchFirstStageSnapuserd();\n\n    if (munlockall() < 0) {\n        PLOG(ERROR) << \"munlockall failed\";\n    }\n}\n\n/*\n * Before starting init second stage, we will wait\n * for snapuserd daemon to be up and running; bionic libc\n * may read /system/etc/selinux/plat_property_contexts file\n * before invoking main() function. This will happen if\n * init initializes property during second stage. Any access\n * to /system without snapuserd daemon will lead to a deadlock.\n *\n * Thus, we do a simple probe by reading system partition. This\n * read will eventually be serviced by daemon confirming that\n * daemon is up and running. Furthermore, we are still in the kernel\n * domain and sepolicy has not been enforced yet. Thus, access\n * to these device mapper block devices are ok even though\n * we may see audit logs.\n */\nbool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {\n    // Wait for the daemon to be fully up. Daemon will write to path\n    // /metadata/ota/daemon-alive-indicator only when all the threads\n    // are ready and attached to dm-user.\n    //\n    // This check will fail for GRF devices with vendor on Android S.\n    // snapuserd binary from Android S won't be able to communicate\n    // and hence, we will fallback and issue I/O to verify\n    // the presence of daemon.\n    auto client = std::make_unique<SnapuserdClient>();\n    if (!client->IsTransitionedDaemonReady()) {\n        LOG(ERROR) << \"IsTransitionedDaemonReady failed\";\n    }\n\n    std::string dev = \"/dev/block/mapper/system\"s + fs_mgr_get_slot_suffix();\n    android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));\n    if (fd < 0) {\n        PLOG(ERROR) << \"open \" << dev << \" failed\";\n        return false;\n    }\n\n    void* addr;\n    ssize_t page_size = getpagesize();\n    if (posix_memalign(&addr, page_size, page_size) < 0) {\n        PLOG(ERROR) << \"posix_memalign with page size \" << page_size;\n        return false;\n    }\n\n    std::unique_ptr<void, decltype(&::free)> buffer(addr, ::free);\n\n    int iter = 0;\n    while (iter < 10) {\n        ssize_t n = TEMP_FAILURE_RETRY(pread(fd.get(), buffer.get(), page_size, 0));\n        if (n < 0) {\n            // Wait for sometime before retry\n            std::this_thread::sleep_for(100ms);\n        } else if (n == page_size) {\n            return true;\n        } else {\n            LOG(ERROR) << \"pread returned: \" << n << \" from: \" << dev << \" expected: \" << page_size;\n        }\n\n        iter += 1;\n    }\n\n    return false;\n}\n\nvoid SnapuserdSelinuxHelper::RelaunchFirstStageSnapuserd() {\n    if (!sm_->DetachFirstStageSnapuserdForSelinux()) {\n        LOG(FATAL) << \"Could not perform selinux transition\";\n    }\n\n    KillFirstStageSnapuserd(old_pid_);\n\n    auto fd = GetRamdiskSnapuserdFd();\n    if (!fd) {\n        LOG(FATAL) << \"Environment variable \" << kSnapuserdFirstStageFdVar << \" was not set!\";\n    }\n    unsetenv(kSnapuserdFirstStageFdVar);\n\n    RestoreconRamdiskSnapuserd(fd.value());\n\n    pid_t pid = fork();\n    if (pid < 0) {\n        PLOG(FATAL) << \"Fork to relaunch snapuserd failed\";\n    }\n    if (pid > 0) {\n        // We don't need the descriptor anymore, and it should be closed to\n        // avoid leaking into subprocesses.\n        close(fd.value());\n\n        setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);\n\n        LOG(INFO) << \"Relaunched snapuserd with pid: \" << pid;\n\n        // Since daemon is not started as a service, we have\n        // to explicitly set the OOM score to default which is unkillable\n        std::string oom_str = std::to_string(DEFAULT_OOM_SCORE_ADJUST);\n        std::string oom_file = android::base::StringPrintf(\"/proc/%d/oom_score_adj\", pid);\n        if (!android::base::WriteStringToFile(oom_str, oom_file)) {\n            PLOG(ERROR) << \"couldn't write oom_score_adj to snapuserd daemon with pid: \" << pid;\n        }\n\n        if (!TestSnapuserdIsReady()) {\n            PLOG(FATAL) << \"snapuserd daemon failed to launch\";\n        } else {\n            LOG(INFO) << \"snapuserd daemon is up and running\";\n        }\n\n        return;\n    }\n\n    // Make sure the descriptor is gone after we exec.\n    if (fcntl(fd.value(), F_SETFD, FD_CLOEXEC) < 0) {\n        PLOG(FATAL) << \"fcntl FD_CLOEXEC failed for snapuserd fd\";\n    }\n\n    std::vector<char*> argv;\n    for (auto& arg : argv_) {\n        argv.emplace_back(arg.data());\n    }\n    argv.emplace_back(nullptr);\n\n    int rv = syscall(SYS_execveat, fd.value(), \"\", reinterpret_cast<char* const*>(argv.data()),\n                     nullptr, AT_EMPTY_PATH);\n    if (rv < 0) {\n        PLOG(FATAL) << \"Failed to execveat() snapuserd\";\n    }\n}\n\nstd::unique_ptr<SnapuserdSelinuxHelper> SnapuserdSelinuxHelper::CreateIfNeeded() {\n    if (IsRecoveryMode()) {\n        return nullptr;\n    }\n\n    auto old_pid = GetSnapuserdFirstStagePid();\n    if (!old_pid) {\n        return nullptr;\n    }\n\n    auto sm = SnapshotManager::NewForFirstStageMount();\n    if (!sm) {\n        LOG(FATAL) << \"Unable to create SnapshotManager\";\n    }\n    return std::make_unique<SnapuserdSelinuxHelper>(std::move(sm), old_pid.value());\n}\n\nvoid KillFirstStageSnapuserd(pid_t pid) {\n    if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {\n        LOG(ERROR) << \"Kill snapuserd pid failed: \" << pid;\n    } else {\n        LOG(INFO) << \"Sent SIGTERM to snapuserd process \" << pid;\n    }\n}\n\nvoid CleanupSnapuserdSocket() {\n    auto socket_path = ANDROID_SOCKET_DIR \"/\"s + android::snapshot::kSnapuserdSocket;\n    if (access(socket_path.c_str(), F_OK) != 0) {\n        return;\n    }\n\n    // Tell the daemon to stop accepting connections and to gracefully exit\n    // once all outstanding handlers have terminated.\n    if (auto client = SnapuserdClient::Connect(android::snapshot::kSnapuserdSocket, 3s)) {\n        client->DetachSnapuserd();\n    }\n\n    // Unlink the socket so we can create it again in second-stage.\n    if (unlink(socket_path.c_str()) < 0) {\n        PLOG(FATAL) << \"unlink \" << socket_path << \" failed\";\n    }\n}\n\nvoid SaveRamdiskPathToSnapuserd() {\n    int fd = open(kSnapuserdPath, O_PATH);\n    if (fd < 0) {\n        PLOG(FATAL) << \"Unable to open snapuserd: \" << kSnapuserdPath;\n    }\n\n    auto value = std::to_string(fd);\n    if (setenv(kSnapuserdFirstStageFdVar, value.c_str(), 1) < 0) {\n        PLOG(FATAL) << \"setenv failed: \" << kSnapuserdFirstStageFdVar << \"=\" << value;\n    }\n}\n\nbool IsFirstStageSnapuserdRunning() {\n    return GetSnapuserdFirstStagePid().has_value();\n}\n\nstd::vector<std::string> GetSnapuserdFirstStageInfo() {\n    const char* pid_str = getenv(kSnapuserdFirstStageInfoVar);\n    if (!pid_str) {\n        return {};\n    }\n    return android::base::Split(pid_str, \",\");\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/snapuserd_transition.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <optional>\n#include <string>\n#include <vector>\n\n#include <libsnapshot/snapshot.h>\n\n#include \"block_dev_initializer.h\"\n\nnamespace android {\nnamespace init {\n\n// Fork and exec a new copy of snapuserd.\nvoid LaunchFirstStageSnapuserd();\n\nclass SnapuserdSelinuxHelper final {\n    using SnapshotManager = android::snapshot::SnapshotManager;\n\n  public:\n    SnapuserdSelinuxHelper(std::unique_ptr<SnapshotManager>&& sm, pid_t old_pid);\n\n    void StartTransition();\n    void FinishTransition();\n\n    // Return a helper for facilitating the selinux transition of snapuserd.\n    // If snapuserd is not in use, null is returned. StartTransition() should\n    // be called after reading policy. FinishTransition() should be called\n    // after loading policy. In between, no reads of /system or other dynamic\n    // partitions are possible.\n    static std::unique_ptr<SnapuserdSelinuxHelper> CreateIfNeeded();\n\n  private:\n    void RelaunchFirstStageSnapuserd();\n    void ExecSnapuserd();\n    bool TestSnapuserdIsReady();\n\n    std::unique_ptr<SnapshotManager> sm_;\n    BlockDevInitializer block_dev_init_;\n    pid_t old_pid_;\n    std::vector<std::string> argv_;\n};\n\n// Remove /dev/socket/snapuserd. This ensures that (1) the existing snapuserd\n// will receive no new requests, and (2) the next copy we transition to can\n// own the socket.\nvoid CleanupSnapuserdSocket();\n\n// Kill an instance of snapuserd given a pid.\nvoid KillFirstStageSnapuserd(pid_t pid);\n\n// Save an open fd to /system/bin (in the ramdisk) into an environment. This is\n// used to later execveat() snapuserd.\nvoid SaveRamdiskPathToSnapuserd();\n\n// Returns true if first-stage snapuserd is running.\nbool IsFirstStageSnapuserdRunning();\n\n// Return the pid of the first-stage instances of snapuserd, if it was started.\nstd::optional<pid_t> GetSnapuserdFirstStagePid();\n\n// Return snapuserd info strings that were set during first-stage init.\nstd::vector<std::string> GetSnapuserdFirstStageInfo();\n\n// Save an open fd to /system/bin (in the ramdisk) into an environment. This is\n// used to later execveat() snapuserd.\nvoid SaveRamdiskPathToSnapuserd();\n\n// Returns true if first-stage snapuserd is running.\nbool IsFirstStageSnapuserdRunning();\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/subcontext.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"subcontext.h\"\n\n#include <fcntl.h>\n#include <poll.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <selinux/android.h>\n\n#include \"action.h\"\n#include \"builtins.h\"\n#include \"mount_namespace.h\"\n#include \"proto_utils.h\"\n#include \"util.h\"\n\n#ifdef INIT_FULL_SOURCES\n#include <android/api-level.h>\n#include \"property_service.h\"\n#include \"selabel.h\"\n#include \"selinux.h\"\n#else\n#include \"host_init_stubs.h\"\n#endif\n\nusing android::base::GetExecutablePath;\nusing android::base::GetProperty;\nusing android::base::Join;\nusing android::base::Socketpair;\nusing android::base::Split;\nusing android::base::StartsWith;\nusing android::base::unique_fd;\n\nnamespace android {\nnamespace init {\nnamespace {\n\nstd::string shutdown_command;\nstatic bool subcontext_terminated_by_shutdown;\nstatic std::unique_ptr<Subcontext> subcontext;\n\nclass SubcontextProcess {\n  public:\n    SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)\n        : function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};\n    void MainLoop();\n\n  private:\n    void RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,\n                    SubcontextReply* reply) const;\n    void ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,\n                    SubcontextReply* reply) const;\n\n    const BuiltinFunctionMap* function_map_;\n    const std::string context_;\n    const int init_fd_;\n};\n\nvoid SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,\n                                   SubcontextReply* reply) const {\n    // Need to use ArraySplice instead of this code.\n    auto args = std::vector<std::string>();\n    for (const auto& string : execute_command.args()) {\n        args.emplace_back(string);\n    }\n\n    auto map_result = function_map_->Find(args);\n    Result<void> result;\n    if (!map_result.ok()) {\n        result = Error() << \"Cannot find command: \" << map_result.error();\n    } else {\n        result = RunBuiltinFunction(map_result->function, args, context_);\n    }\n\n    if (result.ok()) {\n        reply->set_success(true);\n    } else {\n        auto* failure = reply->mutable_failure();\n        failure->set_error_string(result.error().message());\n        failure->set_error_errno(result.error().code());\n    }\n}\n\nvoid SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,\n                                   SubcontextReply* reply) const {\n    for (const auto& arg : expand_args_command.args()) {\n        auto expanded_arg = ExpandProps(arg);\n        if (!expanded_arg.ok()) {\n            auto* failure = reply->mutable_failure();\n            failure->set_error_string(expanded_arg.error().message());\n            failure->set_error_errno(0);\n            return;\n        } else {\n            auto* expand_args_reply = reply->mutable_expand_args_reply();\n            expand_args_reply->add_expanded_args(*expanded_arg);\n        }\n    }\n}\n\nvoid SubcontextProcess::MainLoop() {\n    pollfd ufd[1];\n    ufd[0].events = POLLIN;\n    ufd[0].fd = init_fd_;\n\n    while (true) {\n        ufd[0].revents = 0;\n        int nr = TEMP_FAILURE_RETRY(poll(ufd, arraysize(ufd), -1));\n        if (nr == 0) continue;\n        if (nr < 0) {\n            PLOG(FATAL) << \"poll() of subcontext socket failed, continuing\";\n        }\n\n        auto init_message = ReadMessage(init_fd_);\n        if (!init_message.ok()) {\n            if (init_message.error().code() == 0) {\n                // If the init file descriptor was closed, let's exit quietly. If\n                // this was accidental, init will restart us. If init died, this\n                // avoids calling abort(3) unnecessarily.\n                return;\n            }\n            LOG(FATAL) << \"Could not read message from init: \" << init_message.error();\n        }\n\n        auto subcontext_command = SubcontextCommand();\n        if (!subcontext_command.ParseFromString(*init_message)) {\n            LOG(FATAL) << \"Unable to parse message from init\";\n        }\n\n        auto reply = SubcontextReply();\n        switch (subcontext_command.command_case()) {\n            case SubcontextCommand::kExecuteCommand: {\n                RunCommand(subcontext_command.execute_command(), &reply);\n                break;\n            }\n            case SubcontextCommand::kExpandArgsCommand: {\n                ExpandArgs(subcontext_command.expand_args_command(), &reply);\n                break;\n            }\n            default:\n                LOG(FATAL) << \"Unknown message type from init: \"\n                           << subcontext_command.command_case();\n        }\n\n        if (!shutdown_command.empty()) {\n            reply.set_trigger_shutdown(shutdown_command);\n            shutdown_command.clear();\n        }\n\n        if (auto result = SendMessage(init_fd_, reply); !result.ok()) {\n            LOG(FATAL) << \"Failed to send message to init: \" << result.error();\n        }\n    }\n}\n\n}  // namespace\n\nint SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map) {\n    if (argc < 4) LOG(FATAL) << \"Fewer than 4 args specified to subcontext (\" << argc << \")\";\n\n    auto context = std::string(argv[2]);\n    auto init_fd = std::atoi(argv[3]);\n\n    SelabelInitialize();\n\n    trigger_shutdown = [](const std::string& command) { shutdown_command = command; };\n\n    auto subcontext_process = SubcontextProcess(function_map, context, init_fd);\n    // Restore prio before main loop\n    setpriority(PRIO_PROCESS, 0, 0);\n    subcontext_process.MainLoop();\n    return 0;\n}\n\nvoid Subcontext::Fork() {\n    unique_fd subcontext_socket;\n    if (!Socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, &socket_, &subcontext_socket)) {\n        LOG(FATAL) << \"Could not create socket pair to communicate to subcontext\";\n        return;\n    }\n\n    auto result = fork();\n\n    if (result == -1) {\n        LOG(FATAL) << \"Could not fork subcontext\";\n    } else if (result == 0) {\n        socket_.reset();\n\n        // We explicitly do not use O_CLOEXEC here, such that we can reference this FD by number\n        // in the subcontext process after we exec.\n        int child_fd = dup(subcontext_socket.get());  // NOLINT(android-cloexec-dup)\n        if (child_fd < 0) {\n            PLOG(FATAL) << \"Could not dup child_fd\";\n        }\n\n        // We don't switch contexts if we're running the unit tests.  We don't use std::optional,\n        // since we still need a real context string to pass to the builtin functions.\n        if (context_ != kTestContext) {\n            if (setexeccon(context_.c_str()) < 0) {\n                PLOG(FATAL) << \"Could not set execcon for '\" << context_ << \"'\";\n            }\n        }\n#if defined(__ANDROID__)\n        // subcontext init runs in \"default\" mount namespace\n        // so that it can access /apex/*\n        if (auto result = SwitchToMountNamespaceIfNeeded(NS_DEFAULT); !result.ok()) {\n            LOG(FATAL) << \"Could not switch to \\\"default\\\" mount namespace: \" << result.error();\n        }\n#endif\n        auto init_path = GetExecutablePath();\n        auto child_fd_string = std::to_string(child_fd);\n        const char* args[] = {init_path.c_str(), \"subcontext\", context_.c_str(),\n                              child_fd_string.c_str(), nullptr};\n        execv(init_path.data(), const_cast<char**>(args));\n\n        PLOG(FATAL) << \"Could not execv subcontext init\";\n    } else {\n        subcontext_socket.reset();\n        pid_ = result;\n        LOG(INFO) << \"Forked subcontext for '\" << context_ << \"' with pid \" << pid_;\n    }\n}\n\nvoid Subcontext::Restart() {\n    LOG(ERROR) << \"Restarting subcontext '\" << context_ << \"'\";\n    if (pid_) {\n        kill(pid_, SIGKILL);\n    }\n    pid_ = 0;\n    socket_.reset();\n    Fork();\n}\n\nbool Subcontext::PathMatchesSubcontext(const std::string& path) const {\n    auto apex_name = GetApexNameFromFileName(path);\n    if (!apex_name.empty()) {\n        return std::find(apex_list_.begin(), apex_list_.end(), apex_name) != apex_list_.end();\n    }\n    for (const auto& prefix : path_prefixes_) {\n        if (StartsWith(path, prefix)) {\n            return true;\n        }\n    }\n    return false;\n}\n\nbool Subcontext::PartitionMatchesSubcontext(const std::string& partition) const {\n    return std::find(partitions_.begin(), partitions_.end(), partition) != partitions_.end();\n}\n\nvoid Subcontext::SetApexList(std::vector<std::string>&& apex_list) {\n    apex_list_ = std::move(apex_list);\n}\n\nResult<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {\n    if (auto result = SendMessage(socket_.get(), subcontext_command); !result.ok()) {\n        Restart();\n        return ErrnoError() << \"Failed to send message to subcontext\";\n    }\n\n    auto subcontext_message = ReadMessage(socket_.get());\n    if (!subcontext_message.ok()) {\n        Restart();\n        return Error() << \"Failed to receive result from subcontext: \" << subcontext_message.error();\n    }\n\n    auto subcontext_reply = SubcontextReply{};\n    if (!subcontext_reply.ParseFromString(*subcontext_message)) {\n        Restart();\n        return Error() << \"Unable to parse message from subcontext\";\n    }\n\n    if (subcontext_reply.has_trigger_shutdown()) {\n        trigger_shutdown(subcontext_reply.trigger_shutdown());\n    }\n\n    return subcontext_reply;\n}\n\nResult<void> Subcontext::Execute(const std::vector<std::string>& args) {\n    auto subcontext_command = SubcontextCommand();\n    std::copy(\n        args.begin(), args.end(),\n        RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));\n\n    auto subcontext_reply = TransmitMessage(subcontext_command);\n    if (!subcontext_reply.ok()) {\n        return subcontext_reply.error();\n    }\n\n    if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {\n        auto& failure = subcontext_reply->failure();\n        return ResultError<>(failure.error_string(), failure.error_errno());\n    }\n\n    if (subcontext_reply->reply_case() != SubcontextReply::kSuccess) {\n        return Error() << \"Unexpected message type from subcontext: \"\n                       << subcontext_reply->reply_case();\n    }\n\n    return {};\n}\n\nResult<std::vector<std::string>> Subcontext::ExpandArgs(const std::vector<std::string>& args) {\n    auto subcontext_command = SubcontextCommand{};\n    std::copy(args.begin(), args.end(),\n              RepeatedPtrFieldBackInserter(\n                  subcontext_command.mutable_expand_args_command()->mutable_args()));\n\n    auto subcontext_reply = TransmitMessage(subcontext_command);\n    if (!subcontext_reply.ok()) {\n        return subcontext_reply.error();\n    }\n\n    if (subcontext_reply->reply_case() == SubcontextReply::kFailure) {\n        auto& failure = subcontext_reply->failure();\n        return ResultError<>(failure.error_string(), failure.error_errno());\n    }\n\n    if (subcontext_reply->reply_case() != SubcontextReply::kExpandArgsReply) {\n        return Error() << \"Unexpected message type from subcontext: \"\n                       << subcontext_reply->reply_case();\n    }\n\n    auto& reply = subcontext_reply->expand_args_reply();\n    auto expanded_args = std::vector<std::string>{};\n    for (const auto& string : reply.expanded_args()) {\n        expanded_args.emplace_back(string);\n    }\n    return expanded_args;\n}\n\nvoid InitializeSubcontext() {\n    if (IsMicrodroid()) {\n        LOG(INFO) << \"Not using subcontext for microdroid\";\n        return;\n    }\n\n    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {\n        subcontext.reset(new Subcontext(std::vector<std::string>{\"/vendor\", \"/odm\"},\n                                        std::vector<std::string>{\"VENDOR\", \"ODM\"}, kVendorContext));\n    }\n}\n\nvoid InitializeHostSubcontext(std::vector<std::string> vendor_prefixes) {\n    subcontext.reset(new Subcontext(vendor_prefixes, {}, kVendorContext, /*host=*/true));\n}\n\nSubcontext* GetSubcontext() {\n    return subcontext.get();\n}\n\nbool SubcontextChildReap(pid_t pid) {\n    if (!subcontext) {\n        return false;\n    }\n    if (subcontext->pid() == pid) {\n        if (!subcontext_terminated_by_shutdown) {\n            subcontext->Restart();\n        }\n        return true;\n    }\n    return false;\n}\n\nvoid SubcontextTerminate() {\n    if (!subcontext) {\n        return;\n    }\n    subcontext_terminated_by_shutdown = true;\n    kill(subcontext->pid(), SIGTERM);\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/subcontext.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <signal.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n\n#include \"builtins.h\"\n#include \"result.h\"\n#include \"system/core/init/subcontext.pb.h\"\n\nnamespace android {\nnamespace init {\n\nstatic constexpr const char kInitContext[] = \"u:r:init:s0\";\nstatic constexpr const char kVendorContext[] = \"u:r:vendor_init:s0\";\nstatic constexpr const char kTestContext[] = \"test-test-test\";\n\nclass Subcontext {\n  public:\n    Subcontext(std::vector<std::string> path_prefixes, std::vector<std::string> partitions,\n               std::string_view context, bool host = false)\n        : path_prefixes_(std::move(path_prefixes)),\n          partitions_(std::move(partitions)),\n          context_(context.begin(), context.end()),\n          pid_(0) {\n        if (!host) {\n            Fork();\n        }\n    }\n\n    Result<void> Execute(const std::vector<std::string>& args);\n    Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);\n    void Restart();\n    bool PathMatchesSubcontext(const std::string& path) const;\n    bool PartitionMatchesSubcontext(const std::string& partition) const;\n    void SetApexList(std::vector<std::string>&& apex_list);\n\n    const std::string& context() const { return context_; }\n    pid_t pid() const { return pid_; }\n\n  private:\n    void Fork();\n    Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);\n\n    std::vector<std::string> path_prefixes_;\n    std::vector<std::string> partitions_;\n    std::vector<std::string> apex_list_;\n    std::string context_;\n    pid_t pid_;\n    android::base::unique_fd socket_;\n};\n\nint SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map);\nvoid InitializeSubcontext();\nvoid InitializeHostSubcontext(std::vector<std::string> vendor_prefixes);\nSubcontext* GetSubcontext();\nbool SubcontextChildReap(pid_t pid);\nvoid SubcontextTerminate();\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/subcontext.proto",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nsyntax = \"proto2\";\noption optimize_for = LITE_RUNTIME;\n\nmessage SubcontextCommand {\n    message ExecuteCommand { repeated string args = 1; }\n    message ExpandArgsCommand { repeated string args = 1; }\n    oneof command {\n        ExecuteCommand execute_command = 1;\n        ExpandArgsCommand expand_args_command = 2;\n    }\n}\n\nmessage SubcontextReply {\n    message Failure {\n        optional string error_string = 1;\n        optional int32 error_errno = 2;\n    }\n    message ExpandArgsReply { repeated string expanded_args = 1; }\n\n    oneof reply {\n        bool success = 1;\n        Failure failure = 2;\n        ExpandArgsReply expand_args_reply = 3;\n    }\n\n    optional string trigger_shutdown = 4;\n}"
  },
  {
    "path": "init/subcontext_benchmark.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"subcontext.h\"\n\n#include <benchmark/benchmark.h>\n#include <selinux/selinux.h>\n\nnamespace android {\nnamespace init {\n\nstatic void BenchmarkSuccess(benchmark::State& state) {\n    if (getuid() != 0) {\n        state.SkipWithError(\"Skipping benchmark, must be run as root.\");\n        return;\n    }\n    char* context;\n    if (getcon(&context) != 0) {\n        state.SkipWithError(\"getcon() failed\");\n        return;\n    }\n\n    auto subcontext = Subcontext({\"path\"}, {\"partition\"}, context);\n    free(context);\n\n    while (state.KeepRunning()) {\n        subcontext.Execute(std::vector<std::string>{\"return_success\"});\n    }\n\n    if (subcontext.pid() > 0) {\n        kill(subcontext.pid(), SIGTERM);\n        kill(subcontext.pid(), SIGKILL);\n    }\n}\n\nBENCHMARK(BenchmarkSuccess);\n\nBuiltinFunctionMap BuildTestFunctionMap() {\n    auto function = [](const BuiltinArguments& args) { return Result<void>{}; };\n    BuiltinFunctionMap test_function_map = {\n            {\"return_success\", {0, 0, {true, function}}},\n    };\n    return test_function_map;\n}\n\n}  // namespace init\n}  // namespace android\n\nint main(int argc, char** argv) {\n    if (argc > 1 && !strcmp(basename(argv[1]), \"subcontext\")) {\n        auto test_function_map = android::init::BuildTestFunctionMap();\n        return android::init::SubcontextMain(argc, argv, &test_function_map);\n    }\n\n    ::benchmark::Initialize(&argc, argv);\n    if (::benchmark::ReportUnrecognizedArguments(argc, argv)) return 1;\n    ::benchmark::RunSpecifiedBenchmarks();\n}\n"
  },
  {
    "path": "init/subcontext_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"subcontext.h\"\n\n#include <unistd.h>\n\n#include <chrono>\n\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <gtest/gtest.h>\n#include <selinux/selinux.h>\n\n#include \"builtin_arguments.h\"\n#include \"util.h\"\n\nusing namespace std::literals;\n\nusing android::base::GetProperty;\nusing android::base::Join;\nusing android::base::SetProperty;\nusing android::base::Split;\nusing android::base::WaitForProperty;\n\nnamespace android {\nnamespace init {\n\ntemplate <typename F>\nvoid RunTest(F&& test_function) {\n    auto subcontext = Subcontext({\"dummy_path\"}, {\"dummy_partition\"}, kTestContext);\n    ASSERT_NE(0, subcontext.pid());\n\n    test_function(subcontext);\n\n    if (subcontext.pid() > 0) {\n        kill(subcontext.pid(), SIGTERM);\n        kill(subcontext.pid(), SIGKILL);\n    }\n}\n\nTEST(subcontext, CheckDifferentPid) {\n    RunTest([](auto& subcontext) {\n        auto result = subcontext.Execute(std::vector<std::string>{\"return_pids_as_error\"});\n        ASSERT_FALSE(result.ok());\n\n        auto pids = Split(result.error().message(), \" \");\n        ASSERT_EQ(2U, pids.size());\n        auto our_pid = std::to_string(getpid());\n        EXPECT_NE(our_pid, pids[0]);\n        EXPECT_EQ(our_pid, pids[1]);\n    });\n}\n\nTEST(subcontext, SetProp) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n\n    RunTest([](auto& subcontext) {\n        SetProperty(\"init.test.subcontext\", \"fail\");\n        WaitForProperty(\"init.test.subcontext\", \"fail\");\n\n        auto args = std::vector<std::string>{\n            \"setprop\",\n            \"init.test.subcontext\",\n            \"success\",\n        };\n        auto result = subcontext.Execute(args);\n        ASSERT_RESULT_OK(result);\n\n        EXPECT_TRUE(WaitForProperty(\"init.test.subcontext\", \"success\", 10s));\n    });\n}\n\nTEST(subcontext, MultipleCommands) {\n    RunTest([](auto& subcontext) {\n        auto first_pid = subcontext.pid();\n\n        auto expected_words = std::vector<std::string>{\n            \"this\",\n            \"is\",\n            \"a\",\n            \"test\",\n        };\n\n        for (const auto& word : expected_words) {\n            auto args = std::vector<std::string>{\n                \"add_word\",\n                word,\n            };\n            auto result = subcontext.Execute(args);\n            ASSERT_RESULT_OK(result);\n        }\n\n        auto result = subcontext.Execute(std::vector<std::string>{\"return_words_as_error\"});\n        ASSERT_FALSE(result.ok());\n        EXPECT_EQ(Join(expected_words, \" \"), result.error().message());\n        EXPECT_EQ(first_pid, subcontext.pid());\n    });\n}\n\nTEST(subcontext, RecoverAfterAbort) {\n    RunTest([](auto& subcontext) {\n        auto first_pid = subcontext.pid();\n\n        auto result = subcontext.Execute(std::vector<std::string>{\"cause_log_fatal\"});\n        ASSERT_FALSE(result.ok());\n\n        auto result2 = subcontext.Execute(std::vector<std::string>{\"generate_sane_error\"});\n        ASSERT_FALSE(result2.ok());\n        EXPECT_EQ(\"Sane error!\", result2.error().message());\n        EXPECT_NE(subcontext.pid(), first_pid);\n    });\n}\n\nTEST(subcontext, ContextString) {\n    RunTest([](auto& subcontext) {\n        auto result = subcontext.Execute(std::vector<std::string>{\"return_context_as_error\"});\n        ASSERT_FALSE(result.ok());\n        ASSERT_EQ(kTestContext, result.error().message());\n    });\n}\n\nTEST(subcontext, TriggerShutdown) {\n    static constexpr const char kTestShutdownCommand[] = \"reboot,test-shutdown-command\";\n    static std::string trigger_shutdown_command;\n    trigger_shutdown = [](const std::string& command) { trigger_shutdown_command = command; };\n    RunTest([](auto& subcontext) {\n        auto result = subcontext.Execute(\n                std::vector<std::string>{\"trigger_shutdown\", kTestShutdownCommand});\n        ASSERT_RESULT_OK(result);\n    });\n    EXPECT_EQ(kTestShutdownCommand, trigger_shutdown_command);\n}\n\nTEST(subcontext, ExpandArgs) {\n    RunTest([](auto& subcontext) {\n        auto args = std::vector<std::string>{\n            \"first\",\n            \"${ro.hardware}\",\n            \"$$third\",\n        };\n        auto result = subcontext.ExpandArgs(args);\n        ASSERT_RESULT_OK(result);\n        ASSERT_EQ(3U, result->size());\n        EXPECT_EQ(args[0], result->at(0));\n        EXPECT_EQ(GetProperty(\"ro.hardware\", \"\"), result->at(1));\n        EXPECT_EQ(\"$third\", result->at(2));\n    });\n}\n\nTEST(subcontext, ExpandArgsFailure) {\n    RunTest([](auto& subcontext) {\n        auto args = std::vector<std::string>{\n            \"first\",\n            \"${\",\n        };\n        auto result = subcontext.ExpandArgs(args);\n        ASSERT_FALSE(result.ok());\n        EXPECT_EQ(\"unexpected end of string in '\" + args[1] + \"', looking for }\",\n                  result.error().message());\n    });\n}\n\nTEST(subcontext, PartitionMatchesSubcontext) {\n    RunTest([](auto& subcontext) {\n        static auto& existent_partition = \"dummy_partition\";\n        static auto& non_existent_partition = \"not_dummy_partition\";\n\n        auto existent_result = subcontext.PartitionMatchesSubcontext(existent_partition);\n        auto non_existent_result = subcontext.PartitionMatchesSubcontext(non_existent_partition);\n\n        ASSERT_TRUE(existent_result);\n        ASSERT_FALSE(non_existent_result);\n    });\n}\n\nBuiltinFunctionMap BuildTestFunctionMap() {\n    // For CheckDifferentPid\n    auto do_return_pids_as_error = [](const BuiltinArguments& args) -> Result<void> {\n        return Error() << getpid() << \" \" << getppid();\n    };\n\n    // For SetProp\n    auto do_setprop = [](const BuiltinArguments& args) {\n        android::base::SetProperty(args[1], args[2]);\n        return Result<void>{};\n    };\n\n    // For MultipleCommands\n    // Using a shared_ptr to extend lifetime of words to both lambdas\n    auto words = std::make_shared<std::vector<std::string>>();\n    auto do_add_word = [words](const BuiltinArguments& args) {\n        words->emplace_back(args[1]);\n        return Result<void>{};\n    };\n    auto do_return_words_as_error = [words](const BuiltinArguments& args) -> Result<void> {\n        return Error() << Join(*words, \" \");\n    };\n\n    // For RecoverAfterAbort\n    auto do_cause_log_fatal = [](const BuiltinArguments& args) -> Result<void> {\n        // Since this is an expected failure, disable debuggerd to not generate a tombstone.\n        signal(SIGABRT, SIG_DFL);\n        return Error() << std::string(4097, 'f');\n    };\n    auto do_generate_sane_error = [](const BuiltinArguments& args) -> Result<void> {\n        return Error() << \"Sane error!\";\n    };\n\n    // For ContextString\n    auto do_return_context_as_error = [](const BuiltinArguments& args) -> Result<void> {\n        return Error() << args.context;\n    };\n\n    auto do_trigger_shutdown = [](const BuiltinArguments& args) -> Result<void> {\n        trigger_shutdown(args[1]);\n        return {};\n    };\n\n    // clang-format off\n    BuiltinFunctionMap test_function_map = {\n        {\"return_pids_as_error\",        {0,     0,      {true,  do_return_pids_as_error}}},\n        {\"setprop\",                     {2,     2,      {true,  do_setprop}}},\n        {\"add_word\",                    {1,     1,      {true,  do_add_word}}},\n        {\"return_words_as_error\",       {0,     0,      {true,  do_return_words_as_error}}},\n        {\"cause_log_fatal\",             {0,     0,      {true,  do_cause_log_fatal}}},\n        {\"generate_sane_error\",         {0,     0,      {true,  do_generate_sane_error}}},\n        {\"return_context_as_error\",     {0,     0,      {true,  do_return_context_as_error}}},\n        {\"trigger_shutdown\",            {1,     1,      {true,  do_trigger_shutdown}}},\n    };\n    // clang-format on\n    return test_function_map;\n}\n\n}  // namespace init\n}  // namespace android\n\n// init_test.cpp contains the main entry point for all init tests.\nint SubcontextTestChildMain(int argc, char** argv) {\n    auto test_function_map = android::init::BuildTestFunctionMap();\n    return android::init::SubcontextMain(argc, argv, &test_function_map);\n}\n"
  },
  {
    "path": "init/switch_root.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"switch_root.h\"\n\n#include <fcntl.h>\n#include <mntent.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n\nusing android::base::StartsWith;\n\nusing namespace std::literals;\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nstd::vector<std::string> GetMounts(const std::string& new_root) {\n    auto fp = std::unique_ptr<std::FILE, decltype(&endmntent)>{setmntent(\"/proc/mounts\", \"re\"),\n                                                               endmntent};\n    if (fp == nullptr) {\n        PLOG(FATAL) << \"Failed to open /proc/mounts\";\n    }\n\n    std::vector<std::string> result;\n    mntent* mentry;\n    while ((mentry = getmntent(fp.get())) != nullptr) {\n        // We won't try to move rootfs.\n        if (mentry->mnt_dir == \"/\"s) {\n            continue;\n        }\n\n        // The new root mount is handled separately.\n        if (mentry->mnt_dir == new_root) {\n            continue;\n        }\n\n        // Move operates on subtrees, so do not try to move children of other mounts.\n        if (std::find_if(result.begin(), result.end(), [&mentry](const auto& older_mount) {\n                return StartsWith(mentry->mnt_dir, older_mount);\n            }) != result.end()) {\n            continue;\n        }\n\n        result.emplace_back(mentry->mnt_dir);\n    }\n\n    return result;\n}\n\n}  // namespace\n\nvoid SwitchRoot(const std::string& new_root) {\n    auto mounts = GetMounts(new_root);\n\n    LOG(INFO) << \"Switching root to '\" << new_root << \"'\";\n\n    for (const auto& mount_path : mounts) {\n        auto new_mount_path = new_root + mount_path;\n        mkdir(new_mount_path.c_str(), 0755);\n        if (mount(mount_path.c_str(), new_mount_path.c_str(), nullptr, MS_MOVE, nullptr) != 0) {\n            PLOG(FATAL) << \"Unable to move mount at '\" << mount_path << \"' to \"\n                        << \"'\" << new_mount_path << \"'\";\n        }\n    }\n\n    if (chdir(new_root.c_str()) != 0) {\n        PLOG(FATAL) << \"Could not chdir to new_root, '\" << new_root << \"'\";\n    }\n\n    if (mount(new_root.c_str(), \"/\", nullptr, MS_MOVE, nullptr) != 0) {\n        PLOG(FATAL) << \"Unable to move root mount to new_root, '\" << new_root << \"'\";\n    }\n\n    if (chroot(\".\") != 0) {\n        PLOG(FATAL) << \"Unable to chroot to new root\";\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/switch_root.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\nnamespace android {\nnamespace init {\n\nvoid SwitchRoot(const std::string& new_root);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/test_kill_services/Android.bp",
    "content": "package {\n    // See: http://go/android-license-faq\n    // A large-scale-change added 'default_applicable_licenses' to import\n    // all of the 'license_kinds' from \"system_core_init_license\"\n    // to get the below license kinds:\n    //   SPDX-license-identifier-Apache-2.0\n    default_applicable_licenses: [\"system_core_init_license\"],\n}\n\ncc_test {\n    name: \"init_kill_services_test\",\n    srcs: [\"init_kill_services_test.cpp\"],\n    shared_libs: [\n        \"libbase\",\n        \"libhidlbase\",\n    ],\n    test_suites: [\"general-tests\"],\n\n    // TODO(b/153565474): switch back to auto-generation\n    // and add back:\n    //     require_root: true,\n    auto_gen_config: false,\n}\n"
  },
  {
    "path": "init/test_kill_services/AndroidTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2020 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Runs init_kill_services_test.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-native\" />\n\n    <!-- cannot be autogenerated: b/153565474 -->\n    <target_preparer class=\"com.android.tradefed.targetprep.RebootTargetPreparer\">\n        <!-- flake mitigation, in case device is in bad state-->\n        <option name=\"pre-reboot\" value=\"true\" />\n        <!-- sometimes device gets into bad state, and we don't detect it in this test,\n          so the test succeeds and the next test fails. This is a really bad result, so\n          to avoid that, making sure we reboot the device again before running any more\n          tests.\n          TODO(b/152556737): add metrics for successful device recovery -->\n        <option name=\"post-reboot\" value=\"true\" />\n    </target_preparer>\n\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\"/>\n\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"init_kill_services_test->/data/local/tmp/init_kill_services_test\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        <option name=\"native-test-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"module-name\" value=\"init_kill_services_test\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "init/test_kill_services/OWNERS",
    "content": "smoreland@google.com\n"
  },
  {
    "path": "init/test_kill_services/init_kill_services_test.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <hidl/ServiceManagement.h>\n\n#include <iostream>\n\nusing ::android::base::GetProperty;\nusing ::android::base::SetProperty;\nusing ::android::base::WaitForProperty;\nusing ::android::hardware::isHidlSupported;\nusing std::literals::chrono_literals::operator\"\"s;\n\nvoid ExpectKillingServiceRecovers(const std::string& service_name) {\n    if (!isHidlSupported() && service_name == \"hwservicemanager\") {\n        GTEST_SKIP() << \"No HIDL support on device so hwservicemanager will not be running\";\n    }\n    LOG(INFO) << \"before we say hi to \" << service_name << \", I can't have apexd around!\";\n\n    // b/280514080 - servicemanager will restart apexd, and apexd will restart the\n    // system when crashed. This is fine as the device recovers, but it causes\n    // flakes in this test.\n    ASSERT_TRUE(WaitForProperty(\"init.svc.apexd\", \"stopped\", 120s))\n            << (system(\"cat /dev/binderfs/binder_logs/state\"), \"apexd won't stop\");\n\n    LOG(INFO) << \"hello \" << service_name << \"!\";\n    const std::string status_prop = \"init.svc.\" + service_name;\n    const std::string pid_prop = \"init.svc_debug_pid.\" + service_name;\n\n    const std::string initial_pid = GetProperty(pid_prop, \"\");\n\n    ASSERT_EQ(\"running\", GetProperty(status_prop, \"\")) << status_prop;\n    ASSERT_NE(\"\", initial_pid) << pid_prop;\n\n    LOG(INFO) << \"okay, now goodbye \" << service_name;\n    EXPECT_EQ(0, system((\"kill -9 \" + initial_pid).c_str()));\n\n    constexpr size_t kMaxWaitMilliseconds = 10000;\n    constexpr size_t kRetryWaitMilliseconds = 100;\n\n    constexpr size_t kRetryTimes = kMaxWaitMilliseconds / kRetryWaitMilliseconds;\n\n    for (size_t retry = 0; retry < kRetryTimes; retry++) {\n        const std::string& pid = GetProperty(pid_prop, \"\");\n        if (pid != initial_pid && pid != \"\") break;\n        LOG(INFO) << \"I said goodbye \" << service_name << \"!\";\n        usleep(kRetryWaitMilliseconds * 1000);\n    }\n\n    LOG(INFO) << \"are you still there \" << service_name << \"?\";\n\n    // svc_debug_pid is set after svc property\n    EXPECT_EQ(\"running\", GetProperty(status_prop, \"\"));\n\n    LOG(INFO) << \"I'm done with \" << service_name;\n}\n\nclass InitKillServicesTest : public ::testing::TestWithParam<std::string> {};\n\nTEST_P(InitKillServicesTest, KillCriticalProcesses) {\n    ExpectKillingServiceRecovers(GetParam());\n\n    // Ensure that init is still responding\n    EXPECT_TRUE(SetProperty(\"test.death.test\", \"asdf\"));\n    EXPECT_EQ(GetProperty(\"test.death.test\", \"\"), \"asdf\");\n    EXPECT_TRUE(SetProperty(\"test.death.test\", \"\"));\n}\n\nstatic inline std::string PrintName(const testing::TestParamInfo<std::string>& info) {\n    return info.param;\n}\n\nINSTANTIATE_TEST_CASE_P(\n        DeathTest, InitKillServicesTest,\n        ::testing::Values(\n                // clang-format off\n\n// TODO: we may want a more automatic way of testing this for services based on some\n// criteria (e.g. not disabled), but for now adding core services one at a time\n\n// BEGIN INTERNAL ONLY MERGE GUARD (add things here if internal only, move down later)\n// END INTERNAL ONLY MERGE GUARD\n\n// BEGIN AOSP ONLY (add things here if adding to AOSP)\n    \"lmkd\",\n    \"ueventd\",\n    \"hwservicemanager\",\n    \"servicemanager\",\n    \"system_suspend\"\n// END AOSP ONLY\n\n                // clang-format on\n                ),\n        PrintName);\n"
  },
  {
    "path": "init/test_service/Android.bp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    // See: http://go/android-license-faq\n    // A large-scale-change added 'default_applicable_licenses' to import\n    // all of the 'license_kinds' from \"system_core_init_license\"\n    // to get the below license kinds:\n    //   SPDX-license-identifier-Apache-2.0\n    default_applicable_licenses: [\"system_core_init_license\"],\n}\n\ncc_binary {\n    name: \"test_service\",\n    srcs: [\"test_service.cpp\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\"libbase\"],\n    init_rc: [\"test_service.rc\"],\n}\n"
  },
  {
    "path": "init/test_service/README.md",
    "content": "# Sample service for testing\nThis is a sample service that can be used for testing init.\n\n## Design\nThe service includes a `.rc` file that allows starting it from init.\n\n    service test_service /system/bin/test_service CapAmb 0000000000003000\n        class main\n        user system\n        group system\n        capabilities NET_ADMIN NET_RAW\n        disabled\n        oneshot\n\nThe service accepts any even number of arguments on the command line\n(i.e. any number of pairs of arguments.)\nIt will attempt to find the first element of each pair of arguments in\n`/proc/self/status`, and attempt to exactly match the second element of the pair\nto the relevant line of `proc/self/status`.\n\n### Example\nIn the above case, the service will look for lines containing `CapAmb`:\n\n    cat /proc/self/status\n    ...\n    CapAmb:\t0000000000003000\n\nAnd then attempt to exactly match the token after `:`, `0000000000003000`,\nwith the command-line argument.\nIf they match, the service exits successfully. If not, the service will exit\nwith an error.\n\n## Usage\n\tmmma -j <jobs> system/core/init/testservice\n\tadb root\n\tadb remount\n\tadb sync\n\tadb reboot\n\tadb root\n\tadb shell start test_service\n\tadb logcat -b all -d | grep test_service\n\nLook for an exit status of 0.\n"
  },
  {
    "path": "init/test_service/test_service.cpp",
    "content": "// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include <unistd.h>\n\n#include <map>\n#include <sstream>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n\nvoid Usage(char* argv[]) {\n    printf(\"Usage: %s <status field> <value> [<status field> <value>]*\\n\", argv[0]);\n    printf(\"E.g.: $ %s Uid \\\"1000 1000 1000 1000\\\"\\n\", argv[0]);\n}\n\nint main(int argc, char* argv[]) {\n    if (argc < 3) {\n        Usage(argv);\n        LOG(FATAL) << \"no status field requested\";\n    }\n    if (argc % 2 == 0) {\n        // Since |argc| counts argv[0], if |argc| is odd, then the number of\n        // command-line arguments is even.\n        Usage(argv);\n        LOG(FATAL) << \"need even number of command-line arguments\";\n    }\n\n    std::string status;\n    bool res = android::base::ReadFileToString(\"/proc/self/status\", &status, true);\n    if (!res) {\n        PLOG(FATAL) << \"could not read /proc/self/status\";\n    }\n\n    std::map<std::string, std::string> fields;\n    std::vector<std::string> lines = android::base::Split(status, \"\\n\");\n    for (const auto& line : lines) {\n        std::vector<std::string> tokens = android::base::Split(line, \":\");\n        if (tokens.size() >= 2) {\n            std::string field = tokens[0];\n            std::string value = android::base::Trim(tokens[1]);\n            if (field.length() > 0) {\n                fields[field] = value;\n            }\n        }\n    }\n\n    bool test_fails = false;\n    for (size_t i = 1; i < static_cast<size_t>(argc); i = i + 2) {\n        std::string expected_value = argv[i + 1];\n        auto f = fields.find(argv[i]);\n        if (f != fields.end()) {\n            if (f->second != expected_value) {\n                LOG(ERROR) << \"field '\" << argv[i] << \"' expected '\" << expected_value\n                           << \"', actual '\" << f->second << \"'\";\n                test_fails = true;\n            }\n        } else {\n            LOG(WARNING) << \"could not find field '\" << argv[i] << \"'\";\n        }\n    }\n\n    return test_fails ? 1 : 0;\n}\n"
  },
  {
    "path": "init/test_service/test_service.rc",
    "content": "service test_service /system/bin/test_service CapAmb 0000000000003000\n    class main\n    user system\n    group system\n    capabilities NET_ADMIN NET_RAW\n    disabled\n    oneshot\n"
  },
  {
    "path": "init/test_upgrade_mte/Android.bp",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"system_core_init_license\"],\n}\n\ncc_binary {\n    name: \"mte_upgrade_test_helper\",\n    srcs: [\"mte_upgrade_test_helper.cpp\"],\n    sanitize: {\n        memtag_heap: true,\n        diag: {\n            memtag_heap: false,\n        },\n    },\n    init_rc: [\n        \"mte_upgrade_test.rc\",\n    ],\n}\n\njava_test_host {\n    name: \"mte_upgrade_test\",\n    libs: [\"tradefed\"],\n    static_libs: [\n        \"frameworks-base-hostutils\",\n        \"cts-install-lib-host\",\n    ],\n    srcs: [\n        \"src/**/MteUpgradeTest.java\",\n        \":libtombstone_proto-src\",\n    ],\n    device_first_data: [\n        \":mte_upgrade_test_helper\",\n        \"mte_upgrade_test.rc\",\n    ],\n    test_config: \"AndroidTest.xml\",\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "init/test_upgrade_mte/AndroidTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2022 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Runs the MTE upgrade tests\">\n    <option name=\"test-suite-tag\" value=\"init_test_upgrade_mte\" />\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\"/>\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n      <option name=\"cleanup\" value=\"true\" />\n      <option name=\"remount-system\" value=\"true\" />\n\n      <option name=\"push-file\" key=\"mte_upgrade_test.rc\" value=\"/system/etc/init/mte_upgrade_test.rc\" />\n      <option name=\"push-file\" key=\"mte_upgrade_test_helper\" value=\"/system/bin/mte_upgrade_test_helper\" />\n      <option name=\"push-file\" key=\"mte_upgrade_test_helper\" value=\"/data/local/tmp/app_process64\" />\n      <option name=\"post-push\" value=\"chmod 644 /system/etc/init/mte_upgrade_test.rc\" />\n    </target_preparer>\n    <test class=\"com.android.tradefed.testtype.HostTest\" >\n        <option name=\"jar\" value=\"mte_upgrade_test.jar\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "init/test_upgrade_mte/OWNERS",
    "content": "fmayer@google.com\n\npcc@google.com\n"
  },
  {
    "path": "init/test_upgrade_mte/mte_upgrade_test.rc",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nservice mte_upgrade_test_helper /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}\n  class late_start\n  disabled\n  seclabel u:r:su:s0\n  user root\n\nservice mte_upgrade_test_helper_overridden /system/bin/mte_upgrade_test_helper ${sys.mte_crash_test_uuid}\n  class late_start\n  disabled\n  seclabel u:r:su:s0\n  user root\n  setenv BIONIC_MEMTAG_UPGRADE_SECS 0\n"
  },
  {
    "path": "init/test_upgrade_mte/mte_upgrade_test_helper.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <linux/prctl.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/prctl.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <memory>\n\nint MaybeDowngrade() {\n    int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);\n    if (res == -1) return 1;\n    if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) return 2;\n    time_t t = time(nullptr);\n    while (time(nullptr) - t < 100) {\n        res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);\n        if (static_cast<unsigned long>(res) & PR_MTE_TCF_ASYNC) {\n            return 0;\n        }\n        sleep(1);\n    }\n    return 3;\n}\n\nint main(int argc, char** argv) {\n    if (argc == 2 && strcmp(argv[1], \"--check-downgrade\") == 0) {\n        return MaybeDowngrade();\n    }\n    int res = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);\n    if (res == -1) abort();\n    if (argc == 2 && strcmp(argv[1], \"--get-mode\") == 0) {\n        if (res & PR_MTE_TCF_ASYNC) {\n            return 1;\n        }\n        if (res & PR_MTE_TCF_SYNC) {\n            return 2;\n        }\n        abort();\n    }\n\n    if (res & PR_MTE_TCF_ASYNC && res & PR_MTE_TCF_SYNC) {\n        // Disallow automatic upgrade from ASYNC mode.\n        if (prctl(PR_SET_TAGGED_ADDR_CTRL, res & ~PR_MTE_TCF_SYNC, 0, 0, 0) == -1) abort();\n    }\n    std::unique_ptr<volatile char[]> f(new char[1]);\n    // This out-of-bounds is on purpose: we are testing MTE, which is designed to turn\n    // out-of-bound errors into segfaults.\n    // This binary gets run by src/com/android/tests/init/MteUpgradeTest.java, which\n    // asserts that it crashes as expected.\n    f[17] = 'x';\n    return 0;\n}\n"
  },
  {
    "path": "init/test_upgrade_mte/src/com/android/tests/init/MteUpgradeTest.java",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.tests.init;\n\nimport static com.google.common.truth.Truth.assertThat;\n\nimport static org.junit.Assume.assumeTrue;\n\nimport com.android.server.os.TombstoneProtos.Tombstone;\nimport com.android.tradefed.testtype.DeviceJUnit4ClassRunner;\nimport com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;\nimport com.android.tradefed.util.CommandResult;\n\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\nimport java.util.ArrayList;\n\n@RunWith(DeviceJUnit4ClassRunner.class)\npublic class MteUpgradeTest extends BaseHostJUnit4Test {\n    @Before\n    public void setUp() throws Exception {\n        CommandResult result =\n                getDevice().executeShellV2Command(\"/system/bin/mte_upgrade_test_helper --checking\");\n        assumeTrue(\"mte_upgrade_test_binary needs to segfault\", result.getExitCode() == 139);\n    }\n\n    @After\n    public void tearDown() throws Exception {\n        // Easier here than in a finally in testCrash, and doesn't really hurt.\n        getDevice().executeShellV2Command(\"stop mte_upgrade_test_helper\");\n        getDevice().executeShellV2Command(\"stop mte_upgrade_test_helper_overridden\");\n        getDevice().setProperty(\"sys.mte_crash_test_uuid\", \"\");\n    }\n\n    Tombstone parseTombstone(String tombstonePath) throws Exception {\n        File tombstoneFile = getDevice().pullFile(tombstonePath);\n        InputStream istr = new FileInputStream(tombstoneFile);\n        Tombstone tombstoneProto;\n        try {\n            tombstoneProto = Tombstone.parseFrom(istr);\n        } finally {\n            istr.close();\n        }\n        return tombstoneProto;\n    }\n\n    @Test\n    public void testCrash() throws Exception {\n        String uuid = java.util.UUID.randomUUID().toString();\n        getDevice().reboot();\n        assertThat(getDevice().setProperty(\"sys.mte_crash_test_uuid\", uuid)).isTrue();\n\n        CommandResult result = getDevice().executeShellV2Command(\"start mte_upgrade_test_helper\");\n        assertThat(result.getExitCode()).isEqualTo(0);\n        java.lang.Thread.sleep(20000);\n        String[] tombstonesAfter = getDevice().getChildren(\"/data/tombstones\");\n        ArrayList<String> segvCodeNames = new ArrayList<String>();\n        for (String tombstone : tombstonesAfter) {\n            if (!tombstone.endsWith(\".pb\")) {\n                continue;\n            }\n            String tombstoneFilename = \"/data/tombstones/\" + tombstone;\n            Tombstone tombstoneProto = parseTombstone(tombstoneFilename);\n            if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {\n                continue;\n            }\n            assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo(\"SIGSEGV\");\n            segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());\n            getDevice().deleteFile(tombstoneFilename);\n            // remove the non .pb file as well.\n            getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));\n        }\n        assertThat(segvCodeNames.size()).isAtLeast(3);\n        assertThat(segvCodeNames.get(0)).isEqualTo(\"SEGV_MTEAERR\");\n        assertThat(segvCodeNames.get(1)).isEqualTo(\"SEGV_MTESERR\");\n        assertThat(segvCodeNames.get(2)).isEqualTo(\"SEGV_MTEAERR\");\n    }\n\n    @Test\n    public void testCrashOverridden() throws Exception {\n        String uuid = java.util.UUID.randomUUID().toString();\n        getDevice().reboot();\n        assertThat(getDevice().setProperty(\"sys.mte_crash_test_uuid\", uuid)).isTrue();\n\n        CommandResult result =\n                getDevice().executeShellV2Command(\"start mte_upgrade_test_helper_overridden\");\n        assertThat(result.getExitCode()).isEqualTo(0);\n        java.lang.Thread.sleep(20000);\n        String[] tombstonesAfter = getDevice().getChildren(\"/data/tombstones\");\n        ArrayList<String> segvCodeNames = new ArrayList<String>();\n        for (String tombstone : tombstonesAfter) {\n            if (!tombstone.endsWith(\".pb\")) {\n                continue;\n            }\n            String tombstoneFilename = \"/data/tombstones/\" + tombstone;\n            Tombstone tombstoneProto = parseTombstone(tombstoneFilename);\n            if (!tombstoneProto.getCommandLineList().stream().anyMatch(x -> x.contains(uuid))) {\n                continue;\n            }\n            assertThat(tombstoneProto.getSignalInfo().getName()).isEqualTo(\"SIGSEGV\");\n            segvCodeNames.add(tombstoneProto.getSignalInfo().getCodeName());\n            getDevice().deleteFile(tombstoneFilename);\n            // remove the non .pb file as well.\n            getDevice().deleteFile(tombstoneFilename.substring(0, tombstoneFilename.length() - 3));\n        }\n        assertThat(segvCodeNames.size()).isAtLeast(3);\n        assertThat(segvCodeNames.get(0)).isEqualTo(\"SEGV_MTEAERR\");\n        assertThat(segvCodeNames.get(1)).isEqualTo(\"SEGV_MTEAERR\");\n        assertThat(segvCodeNames.get(2)).isEqualTo(\"SEGV_MTEAERR\");\n    }\n\n    @Test\n    public void testDowngrade() throws Exception {\n        CommandResult result =\n                getDevice()\n                        .executeShellV2Command(\n                                \"MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5\"\n                                        + \" /system/bin/mte_upgrade_test_helper --check-downgrade\");\n        assertThat(result.getExitCode()).isEqualTo(0);\n    }\n\n    @Test\n    public void testAppProcess() throws Exception {\n        CommandResult result =\n                getDevice()\n                        .executeShellV2Command(\n                                \"MEMTAG_OPTIONS=async BIONIC_MEMTAG_UPGRADE_SECS=5\"\n                                        + \" /data/local/tmp/app_process64 --get-mode\");\n        assertThat(result.getExitCode()).isEqualTo(1);  // ASYNC\n    }\n}\n"
  },
  {
    "path": "init/test_utils/include/init-test-utils/service_utils.h",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <map>\n#include <set>\n\n#include <android-base/result.h>\n\nnamespace android {\nnamespace init {\n\n// this is service name -> interface declaration\n//\n// So, for:\n//     service foo ..\n//         interface aidl baz\n//         interface android.hardware.foo@1.0 IFoo\n//\n// We have:\n//     foo -> { aidl/baz, android.hardware.foo@1.0/IFoo }\nusing ServiceInterfacesMap = std::map<std::string, std::set<std::string>>;\nandroid::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap();\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/test_utils/service_utils.cpp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <string>\n\n#include <android-base/logging.h>\n\n#include \"../parser.h\"\n#include \"../service.h\"\n#include \"../service_list.h\"\n#include \"../service_parser.h\"\n#include \"include/init-test-utils/service_utils.h\"\n\nnamespace android {\nnamespace init {\n\nandroid::base::Result<ServiceInterfacesMap> GetOnDeviceServiceInterfacesMap() {\n    ServiceList& service_list = ServiceList::GetInstance();\n    Parser parser;\n    parser.AddSectionParser(\"service\", std::make_unique<ServiceParser>(&service_list, nullptr));\n    for (const auto& location : {\n                 \"/init.rc\",\n                 \"/system/etc/init\",\n                 \"/system_ext/etc/init\",\n                 \"/product/etc/init\",\n                 \"/odm/etc/init\",\n                 \"/vendor/etc/init\",\n         }) {\n        parser.ParseConfig(location);\n    }\n\n    ServiceInterfacesMap result;\n    for (const auto& service : service_list) {\n        // Create an entry for all services, including services that may not\n        // have any declared interfaces.\n        result[service->name()] = service->interfaces();\n    }\n    return result;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/tokenizer.cpp",
    "content": "#include \"tokenizer.h\"\n\n#include <android-base/macros.h>\n\nnamespace android {\nnamespace init {\n\nint next_token(struct parse_state *state)\n{\n    char *x = state->ptr;\n    char *s;\n\n    if (state->nexttoken) {\n        int t = state->nexttoken;\n        state->nexttoken = 0;\n        return t;\n    }\n\n    for (;;) {\n        switch (*x) {\n        case 0:\n            state->ptr = x;\n            return T_EOF;\n        case '\\n':\n            x++;\n            state->ptr = x;\n            return T_NEWLINE;\n        case ' ':\n        case '\\t':\n        case '\\r':\n            x++;\n            continue;\n        case '#':\n            while (*x && (*x != '\\n')) x++;\n            if (*x == '\\n') {\n                state->ptr = x+1;\n                return T_NEWLINE;\n            } else {\n                state->ptr = x;\n                return T_EOF;\n            }\n        default:\n            goto text;\n        }\n    }\n\ntextdone:\n    state->ptr = x;\n    *s = 0;\n    return T_TEXT;\ntext:\n    state->text = s = x;\ntextresume:\n    for (;;) {\n        switch (*x) {\n        case 0:\n            goto textdone;\n        case ' ':\n        case '\\t':\n        case '\\r':\n            x++;\n            goto textdone;\n        case '\\n':\n            state->nexttoken = T_NEWLINE;\n            x++;\n            goto textdone;\n        case '\"':\n            x++;\n            for (;;) {\n                switch (*x) {\n                case 0:\n                        /* unterminated quoted thing */\n                    state->ptr = x;\n                    return T_EOF;\n                case '\"':\n                    x++;\n                    goto textresume;\n                default:\n                    *s++ = *x++;\n                }\n            }\n            break;\n        case '\\\\':\n            x++;\n            switch (*x) {\n            case 0:\n                goto textdone;\n            case 'n':\n                *s++ = '\\n';\n                x++;\n                break;\n            case 'r':\n                *s++ = '\\r';\n                x++;\n                break;\n            case 't':\n                *s++ = '\\t';\n                x++;\n                break;\n            case '\\\\':\n                *s++ = '\\\\';\n                x++;\n                break;\n            case '\\r':\n                    /* \\ <cr> <lf> -> line continuation */\n                if (x[1] != '\\n') {\n                    x++;\n                    continue;\n                }\n                x++;\n                FALLTHROUGH_INTENDED;\n            case '\\n':\n                    /* \\ <lf> -> line continuation */\n                state->line++;\n                x++;\n                    /* eat any extra whitespace */\n                while((*x == ' ') || (*x == '\\t')) x++;\n                continue;\n            default:\n                    /* unknown escape -- just copy */\n                *s++ = *x++;\n            }\n            continue;\n        default:\n            *s++ = *x++;\n        }\n    }\n    return T_EOF;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/tokenizer.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_TOKENIZER_H_\n#define _INIT_TOKENIZER_H_\n\n#define T_EOF 0\n#define T_TEXT 1\n#define T_NEWLINE 2\n\nnamespace android {\nnamespace init {\n\nstruct parse_state\n{\n    char *ptr;\n    char *text;\n    int line;\n    int nexttoken;\n};\n\nint next_token(struct parse_state *state);\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/tokenizer_test.cpp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"tokenizer.h\"\n\n#include <string>\n#include <vector>\n\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace init {\n\nnamespace {\n\nvoid RunTest(const std::string& data, const std::vector<std::vector<std::string>>& expected_tokens) {\n    auto data_copy = std::string{data};\n    data_copy.push_back('\\n');\n    data_copy.push_back('\\0');\n\n    parse_state state;\n    state.line = 0;\n    state.ptr = data_copy.data();\n    state.nexttoken = 0;\n\n    std::vector<std::string> current_line;\n    std::vector<std::vector<std::string>> tokens;\n\n    while (true) {\n        switch (next_token(&state)) {\n            case T_EOF:\n                EXPECT_EQ(expected_tokens, tokens) << data;\n                return;\n            case T_NEWLINE:\n                tokens.emplace_back(std::move(current_line));\n                current_line.clear();\n                break;\n            case T_TEXT:\n                current_line.emplace_back(state.text);\n                break;\n        }\n    }\n}\n\n}  // namespace\n\nTEST(tokenizer, null) {\n    RunTest(\"\", {{}});\n}\n\nTEST(tokenizer, simple_oneline) {\n    RunTest(\"one two\\tthree\\rfour\", {{\"one\", \"two\", \"three\", \"four\"}});\n}\n\nTEST(tokenizer, simple_multiline) {\n    RunTest(\"1 2 3\\n4 5 6\\n7 8 9\", {{\"1\", \"2\", \"3\"}, {\"4\", \"5\", \"6\"}, {\"7\", \"8\", \"9\"}});\n}\n\nTEST(tokenizer, preceding_space) {\n    // Preceding spaces are ignored.\n    RunTest(\"    1 2 3\\n\\t\\t\\t\\t4 5 6\\n\\r\\r\\r\\r7 8 9\",\n            {{\"1\", \"2\", \"3\"}, {\"4\", \"5\", \"6\"}, {\"7\", \"8\", \"9\"}});\n}\n\nTEST(tokenizer, comments) {\n    // Entirely commented lines still produce a T_NEWLINE token for tracking line count.\n    RunTest(\"1 2 3\\n#4 5 6\\n7 8 9\", {{\"1\", \"2\", \"3\"}, {}, {\"7\", \"8\", \"9\"}});\n\n    RunTest(\"#1 2 3\\n4 5 6\\n7 8 9\", {{}, {\"4\", \"5\", \"6\"}, {\"7\", \"8\", \"9\"}});\n\n    RunTest(\"1 2 3\\n4 5 6\\n#7 8 9\", {{\"1\", \"2\", \"3\"}, {\"4\", \"5\", \"6\"}, {}});\n\n    RunTest(\"1 2 #3\\n4 #5 6\\n#7 8 9\", {{\"1\", \"2\"}, {\"4\"}, {}});\n}\n\nTEST(tokenizer, control_chars) {\n    // Literal \\n, \\r, \\t, and \\\\ produce the control characters \\n, \\r, \\t, and \\\\ respectively.\n    // Literal \\? produces ? for all other character '?'\n\n    RunTest(R\"(1 token\\ntoken 2)\", {{\"1\", \"token\\ntoken\", \"2\"}});\n    RunTest(R\"(1 token\\rtoken 2)\", {{\"1\", \"token\\rtoken\", \"2\"}});\n    RunTest(R\"(1 token\\ttoken 2)\", {{\"1\", \"token\\ttoken\", \"2\"}});\n    RunTest(R\"(1 token\\\\token 2)\", {{\"1\", \"token\\\\token\", \"2\"}});\n    RunTest(R\"(1 token\\btoken 2)\", {{\"1\", \"tokenbtoken\", \"2\"}});\n\n    RunTest(R\"(1 token\\n 2)\", {{\"1\", \"token\\n\", \"2\"}});\n    RunTest(R\"(1 token\\r 2)\", {{\"1\", \"token\\r\", \"2\"}});\n    RunTest(R\"(1 token\\t 2)\", {{\"1\", \"token\\t\", \"2\"}});\n    RunTest(R\"(1 token\\\\ 2)\", {{\"1\", \"token\\\\\", \"2\"}});\n    RunTest(R\"(1 token\\b 2)\", {{\"1\", \"tokenb\", \"2\"}});\n\n    RunTest(R\"(1 \\ntoken 2)\", {{\"1\", \"\\ntoken\", \"2\"}});\n    RunTest(R\"(1 \\rtoken 2)\", {{\"1\", \"\\rtoken\", \"2\"}});\n    RunTest(R\"(1 \\ttoken 2)\", {{\"1\", \"\\ttoken\", \"2\"}});\n    RunTest(R\"(1 \\\\token 2)\", {{\"1\", \"\\\\token\", \"2\"}});\n    RunTest(R\"(1 \\btoken 2)\", {{\"1\", \"btoken\", \"2\"}});\n\n    RunTest(R\"(1 \\n 2)\", {{\"1\", \"\\n\", \"2\"}});\n    RunTest(R\"(1 \\r 2)\", {{\"1\", \"\\r\", \"2\"}});\n    RunTest(R\"(1 \\t 2)\", {{\"1\", \"\\t\", \"2\"}});\n    RunTest(R\"(1 \\\\ 2)\", {{\"1\", \"\\\\\", \"2\"}});\n    RunTest(R\"(1 \\b 2)\", {{\"1\", \"b\", \"2\"}});\n}\n\nTEST(tokenizer, cr_lf) {\n    // \\ before \\n, \\r, or \\r\\n is interpreted as a line continuation\n    // Extra whitespace on the next line is eaten, except \\r unlike in the above tests.\n\n    RunTest(\"lf\\\\\\ncont\", {{\"lfcont\"}});\n    RunTest(\"lf\\\\\\n    \\t\\t\\t\\tcont\", {{\"lfcont\"}});\n\n    RunTest(\"crlf\\\\\\r\\ncont\", {{\"crlfcont\"}});\n    RunTest(\"crlf\\\\\\r\\n    \\t\\t\\t\\tcont\", {{\"crlfcont\"}});\n\n    RunTest(\"cr\\\\\\rcont\", {{\"crcont\"}});\n\n    RunTest(\"lfspace \\\\\\ncont\", {{\"lfspace\", \"cont\"}});\n    RunTest(\"lfspace \\\\\\n    \\t\\t\\t\\tcont\", {{\"lfspace\", \"cont\"}});\n\n    RunTest(\"crlfspace \\\\\\r\\ncont\", {{\"crlfspace\", \"cont\"}});\n    RunTest(\"crlfspace \\\\\\r\\n    \\t\\t\\t\\tcont\", {{\"crlfspace\", \"cont\"}});\n\n    RunTest(\"crspace \\\\\\rcont\", {{\"crspace\", \"cont\"}});\n}\n\nTEST(tokenizer, quoted) {\n    RunTest(\"\\\"quoted simple string\\\"\", {{\"quoted simple string\"}});\n\n    // Unterminated quotes just return T_EOF without any T_NEWLINE.\n    RunTest(\"\\\"unterminated quoted string\", {});\n\n    RunTest(\"\\\"1 2 3\\\"\\n \\\"unterminated quoted string\", {{\"1 2 3\"}});\n\n    // Escaping quotes is not allowed and are treated as an unterminated quoted string.\n    RunTest(\"\\\"quoted escaped quote\\\\\\\"\\\"\", {});\n    RunTest(\"\\\"quoted escaped\\\\\\\" quote\\\"\", {});\n    RunTest(\"\\\"\\\\\\\"quoted escaped quote\\\"\", {});\n\n    RunTest(\"\\\"quoted control characters \\\\n \\\\r \\\\t \\\\\\\\ \\\\b \\\\\\r \\\\\\n \\r \\n\\\"\",\n            {{\"quoted control characters \\\\n \\\\r \\\\t \\\\\\\\ \\\\b \\\\\\r \\\\\\n \\r \\n\"}});\n\n    RunTest(\"\\\"quoted simple string\\\" \\\"second quoted string\\\"\",\n            {{\"quoted simple string\", \"second quoted string\"}});\n\n    RunTest(\"\\\"# comment quoted string\\\"\", {{\"# comment quoted string\"}});\n\n    RunTest(\"\\\"Adjacent \\\"\\\"quoted strings\\\"\", {{\"Adjacent quoted strings\"}});\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/uevent.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_UEVENT_H\n#define _INIT_UEVENT_H\n\n#include <string>\n\nnamespace android {\nnamespace init {\n\nstruct Uevent {\n    std::string action;\n    std::string path;\n    std::string subsystem;\n    std::string driver;\n    std::string firmware;\n    std::string partition_name;\n    std::string partition_uuid;\n    std::string device_name;\n    std::string modalias;\n    int partition_num;\n    int major;\n    int minor;\n};\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/uevent_handler.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include \"uevent.h\"\n\nnamespace android {\nnamespace init {\n\nclass UeventHandler {\n  public:\n    virtual ~UeventHandler() = default;\n\n    virtual void HandleUevent(const Uevent& uevent) = 0;\n\n    virtual void ColdbootDone() {}\n};\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/uevent_listener.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"uevent_listener.h\"\n\n#include <fcntl.h>\n#include <poll.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <memory>\n\n#include <android-base/logging.h>\n#include <cutils/uevent.h>\n\nnamespace android {\nnamespace init {\n\nstatic void ParseEvent(const char* msg, Uevent* uevent) {\n    uevent->partition_num = -1;\n    uevent->major = -1;\n    uevent->minor = -1;\n    uevent->action.clear();\n    uevent->path.clear();\n    uevent->subsystem.clear();\n    uevent->driver.clear();\n    uevent->firmware.clear();\n    uevent->partition_name.clear();\n    uevent->device_name.clear();\n    uevent->modalias.clear();\n    // currently ignoring SEQNUM\n    while (*msg) {\n        if (!strncmp(msg, \"ACTION=\", 7)) {\n            msg += 7;\n            uevent->action = msg;\n        } else if (!strncmp(msg, \"DEVPATH=\", 8)) {\n            msg += 8;\n            uevent->path = msg;\n        } else if (!strncmp(msg, \"SUBSYSTEM=\", 10)) {\n            msg += 10;\n            uevent->subsystem = msg;\n        } else if (!strncmp(msg, \"DRIVER=\", 7)) {\n            msg += 7;\n            uevent->driver = msg;\n        } else if (!strncmp(msg, \"FIRMWARE=\", 9)) {\n            msg += 9;\n            uevent->firmware = msg;\n        } else if (!strncmp(msg, \"MAJOR=\", 6)) {\n            msg += 6;\n            uevent->major = atoi(msg);\n        } else if (!strncmp(msg, \"MINOR=\", 6)) {\n            msg += 6;\n            uevent->minor = atoi(msg);\n        } else if (!strncmp(msg, \"PARTN=\", 6)) {\n            msg += 6;\n            uevent->partition_num = atoi(msg);\n        } else if (!strncmp(msg, \"PARTNAME=\", 9)) {\n            msg += 9;\n            uevent->partition_name = msg;\n        } else if (!strncmp(msg, \"PARTUUID=\", 9)) {\n            msg += 9;\n            uevent->partition_uuid = msg;\n        } else if (!strncmp(msg, \"DEVNAME=\", 8)) {\n            msg += 8;\n            uevent->device_name = msg;\n        } else if (!strncmp(msg, \"MODALIAS=\", 9)) {\n            msg += 9;\n            uevent->modalias = msg;\n        }\n\n        // advance to after the next \\0\n        while (*msg++)\n            ;\n    }\n\n    if (LOG_UEVENTS) {\n        LOG(INFO) << \"event { '\" << uevent->action << \"', '\" << uevent->path << \"', '\"\n                  << uevent->subsystem << \"', '\" << uevent->firmware << \"', \" << uevent->major\n                  << \", \" << uevent->minor << \", \" << uevent->partition_uuid << \" }\";\n    }\n}\n\nUeventListener::UeventListener(size_t uevent_socket_rcvbuf_size) {\n    device_fd_.reset(uevent_open_socket(uevent_socket_rcvbuf_size, true));\n    if (device_fd_ == -1) {\n        LOG(FATAL) << \"Could not open uevent socket\";\n    }\n\n    fcntl(device_fd_.get(), F_SETFL, O_NONBLOCK);\n}\n\nReadUeventResult UeventListener::ReadUevent(Uevent* uevent) const {\n    char msg[UEVENT_MSG_LEN + 2];\n    int n = uevent_kernel_multicast_recv(device_fd_.get(), msg, UEVENT_MSG_LEN);\n    if (n <= 0) {\n        if (errno != EAGAIN && errno != EWOULDBLOCK) {\n            PLOG(ERROR) << \"Error reading from Uevent Fd\";\n        }\n        return ReadUeventResult::kFailed;\n    }\n    if (n >= UEVENT_MSG_LEN) {\n        LOG(ERROR) << \"Uevent overflowed buffer, discarding\";\n        return ReadUeventResult::kInvalid;\n    }\n\n    msg[n] = '\\0';\n    msg[n + 1] = '\\0';\n\n    ParseEvent(msg, uevent);\n\n    return ReadUeventResult::kSuccess;\n}\n\n// RegenerateUevents*() walks parts of the /sys tree and pokes the uevent files to cause the kernel\n// to regenerate device add uevents that have already happened.  This is particularly useful when\n// starting ueventd, to regenerate all of the uevents that it had previously missed.\n//\n// We drain any pending events from the netlink socket every time we poke another uevent file to\n// make sure we don't overrun the socket's buffer.\n//\n\nListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,\n                                                       const ListenerCallback& callback) const {\n    int dfd = dirfd(d);\n\n    int fd = openat(dfd, \"uevent\", O_WRONLY | O_CLOEXEC);\n    if (fd >= 0) {\n        write(fd, \"add\\n\", 4);\n        close(fd);\n\n        Uevent uevent;\n        ReadUeventResult result;\n        while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {\n            // Skip processing the uevent if it is invalid.\n            if (result == ReadUeventResult::kInvalid) continue;\n            if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;\n        }\n    }\n\n    dirent* de;\n    while ((de = readdir(d)) != nullptr) {\n        if (de->d_type != DT_DIR || de->d_name[0] == '.') continue;\n\n        fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);\n        if (fd < 0) continue;\n\n        std::unique_ptr<DIR, decltype(&closedir)> d2(fdopendir(fd), closedir);\n        if (d2 == 0) {\n            close(fd);\n        } else {\n            if (RegenerateUeventsForDir(d2.get(), callback) == ListenerAction::kStop) {\n                return ListenerAction::kStop;\n            }\n        }\n    }\n\n    // default is always to continue looking for uevents\n    return ListenerAction::kContinue;\n}\n\nListenerAction UeventListener::RegenerateUeventsForPath(const std::string& path,\n                                                        const ListenerCallback& callback) const {\n    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);\n    if (!d) return ListenerAction::kContinue;\n\n    return RegenerateUeventsForDir(d.get(), callback);\n}\n\nstatic const char* kRegenerationPaths[] = {\"/sys/devices\"};\n\nvoid UeventListener::RegenerateUevents(const ListenerCallback& callback) const {\n    for (const auto path : kRegenerationPaths) {\n        if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;\n    }\n}\n\nvoid UeventListener::Poll(const ListenerCallback& callback,\n                          const std::optional<std::chrono::milliseconds> relative_timeout) const {\n    using namespace std::chrono;\n\n    pollfd ufd = {\n            .events = POLLIN,\n            .fd = device_fd_.get(),\n    };\n\n    auto start_time = steady_clock::now();\n\n    while (true) {\n        ufd.revents = 0;\n\n        int timeout_ms = -1;\n        if (relative_timeout) {\n            auto now = steady_clock::now();\n            auto time_elapsed = duration_cast<milliseconds>(now - start_time);\n            if (time_elapsed > *relative_timeout) return;\n\n            auto remaining_timeout = *relative_timeout - time_elapsed;\n            timeout_ms = remaining_timeout.count();\n        }\n\n        int nr = poll(&ufd, 1, timeout_ms);\n        if (nr == 0) return;\n        if (nr < 0) {\n            PLOG(ERROR) << \"poll() of uevent socket failed, continuing\";\n            continue;\n        }\n        if (ufd.revents & POLLIN) {\n            // We're non-blocking, so if we receive a poll event keep processing until\n            // we have exhausted all uevent messages.\n            Uevent uevent;\n            ReadUeventResult result;\n            while ((result = ReadUevent(&uevent)) != ReadUeventResult::kFailed) {\n                // Skip processing the uevent if it is invalid.\n                if (result == ReadUeventResult::kInvalid) continue;\n                if (callback(uevent) == ListenerAction::kStop) return;\n            }\n        }\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/uevent_listener.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_UEVENT_LISTENER_H\n#define _INIT_UEVENT_LISTENER_H\n\n#include <dirent.h>\n\n#include <chrono>\n#include <functional>\n#include <optional>\n\n#include <android-base/unique_fd.h>\n\n#include \"uevent.h\"\n\n#define UEVENT_MSG_LEN 8192\n\nnamespace android {\nnamespace init {\n\nenum class ListenerAction {\n    kStop = 0,  // Stop regenerating uevents as we've handled the one(s) we're interested in.\n    kContinue,  // Continue regenerating uevents as we haven't seen the one(s) we're interested in.\n};\n\nenum class ReadUeventResult {\n    kSuccess = 0,  // Uevent was successfully read.\n    kFailed,       // Uevent reading has failed.\n    kInvalid,      // An Invalid Uevent was read (like say, the msg received is >= UEVENT_MSG_LEN).\n};\n\nusing ListenerCallback = std::function<ListenerAction(const Uevent&)>;\n\nclass UeventListener {\n  public:\n    UeventListener(size_t uevent_socket_rcvbuf_size);\n\n    void RegenerateUevents(const ListenerCallback& callback) const;\n    ListenerAction RegenerateUeventsForPath(const std::string& path,\n                                            const ListenerCallback& callback) const;\n    void Poll(const ListenerCallback& callback,\n              const std::optional<std::chrono::milliseconds> relative_timeout = {}) const;\n\n  private:\n    ReadUeventResult ReadUevent(Uevent* uevent) const;\n    ListenerAction RegenerateUeventsForDir(DIR* d, const ListenerCallback& callback) const;\n\n    android::base::unique_fd device_fd_;\n};\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/ueventd.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ueventd.h\"\n\n#include <android/api-level.h>\n#include <ctype.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <set>\n#include <thread>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <fstab/fstab.h>\n#include <selinux/android.h>\n#include <selinux/selinux.h>\n\n#include \"devices.h\"\n#include \"firmware_handler.h\"\n#include \"modalias_handler.h\"\n#include \"selabel.h\"\n#include \"selinux.h\"\n#include \"uevent_handler.h\"\n#include \"uevent_listener.h\"\n#include \"ueventd_parser.h\"\n#include \"util.h\"\n\n// At a high level, ueventd listens for uevent messages generated by the kernel through a netlink\n// socket.  When ueventd receives such a message it handles it by taking appropriate actions,\n// which can typically be creating a device node in /dev, setting file permissions, setting selinux\n// labels, etc.\n// Ueventd also handles loading of firmware that the kernel requests, and creates symlinks for block\n// and character devices.\n\n// When ueventd starts, it regenerates uevents for all currently registered devices by traversing\n// /sys and writing 'add' to each 'uevent' file that it finds.  This causes the kernel to generate\n// and resend uevent messages for all of the currently registered devices.  This is done, because\n// ueventd would not have been running when these devices were registered and therefore was unable\n// to receive their uevent messages and handle them appropriately.  This process is known as\n// 'cold boot'.\n\n// 'init' currently waits synchronously on the cold boot process of ueventd before it continues\n// its boot process.  For this reason, cold boot should be as quick as possible.  One way to achieve\n// a speed up here is to parallelize the handling of ueventd messages, which consume the bulk of the\n// time during cold boot.\n\n// Handling of uevent messages has two unique properties:\n// 1) It can be done in isolation; it doesn't need to read or write any status once it is started.\n// 2) It uses setegid() and setfscreatecon() so either care (aka locking) must be taken to ensure\n//    that no file system operations are done while the uevent process has an abnormal egid or\n//    fscreatecon or this handling must happen in a separate process.\n// Given the above two properties, it is best to fork() subprocesses to handle the uevents.  This\n// reduces the overhead and complexity that would be required in a solution with threads and locks.\n// In testing, a racy multithreaded solution has the same performance as the fork() solution, so\n// there is no reason to deal with the complexity of the former.\n\n// One other important caveat during the boot process is the handling of SELinux restorecon.\n// Since many devices have child devices, calling selinux_android_restorecon() recursively for each\n// device when its uevent is handled, results in multiple restorecon operations being done on a\n// given file.  It is more efficient to simply do restorecon recursively on /sys during cold boot,\n// than to do restorecon on each device as its uevent is handled.  This only applies to cold boot;\n// once that has completed, restorecon is done for each device as its uevent is handled.\n\n// With all of the above considered, the cold boot process has the below steps:\n// 1) ueventd regenerates uevents by doing the /sys traversal and listens to the netlink socket for\n//    the generated uevents.  It writes these uevents into a queue represented by a vector.\n//\n// 2) ueventd forks 'n' separate uevent handler subprocesses and has each of them to handle the\n//    uevents in the queue based on a starting offset (their process number) and a stride (the total\n//    number of processes).  Note that no IPC happens at this point and only const functions from\n//    DeviceHandler should be called from this context.\n//\n// 3) In parallel to the subprocesses handling the uevents, the main thread of ueventd calls\n//    selinux_android_restorecon() recursively on /sys/class, /sys/block, and /sys/devices.\n//\n// 4) Once the restorecon operation finishes, the main thread calls waitpid() to wait for all\n//    subprocess handlers to complete and exit.  Once this happens, it marks coldboot as having\n//    completed.\n//\n// At this point, ueventd is single threaded, poll()'s and then handles any future uevents.\n\n// Lastly, it should be noted that uevents that occur during the coldboot process are handled\n// without issue after the coldboot process completes.  This is because the uevent listener is\n// paused while the uevent handler and restorecon actions take place.  Once coldboot completes,\n// the uevent listener resumes in polling mode and will handle the uevents that occurred during\n// coldboot.\n\nnamespace android {\nnamespace init {\n\nclass ColdBoot {\n  public:\n    ColdBoot(UeventListener& uevent_listener,\n             std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers,\n             bool enable_parallel_restorecon,\n             std::vector<std::string> parallel_restorecon_queue)\n        : uevent_listener_(uevent_listener),\n          uevent_handlers_(uevent_handlers),\n          num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4),\n          enable_parallel_restorecon_(enable_parallel_restorecon),\n          parallel_restorecon_queue_(parallel_restorecon_queue) {}\n\n    void Run();\n\n  private:\n    void UeventHandlerMain(unsigned int process_num, unsigned int total_processes);\n    void RegenerateUevents();\n    void ForkSubProcesses();\n    void WaitForSubProcesses();\n    void RestoreConHandler(unsigned int process_num, unsigned int total_processes);\n    void GenerateRestoreCon(const std::string& directory);\n\n    UeventListener& uevent_listener_;\n    std::vector<std::unique_ptr<UeventHandler>>& uevent_handlers_;\n\n    unsigned int num_handler_subprocesses_;\n    bool enable_parallel_restorecon_;\n\n    std::vector<Uevent> uevent_queue_;\n\n    std::set<pid_t> subprocess_pids_;\n\n    std::vector<std::string> restorecon_queue_;\n\n    std::vector<std::string> parallel_restorecon_queue_;\n};\n\nvoid ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {\n    for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {\n        auto& uevent = uevent_queue_[i];\n\n        for (auto& uevent_handler : uevent_handlers_) {\n            uevent_handler->HandleUevent(uevent);\n        }\n    }\n}\n\nvoid ColdBoot::RestoreConHandler(unsigned int process_num, unsigned int total_processes) {\n    android::base::Timer t_process;\n\n    for (unsigned int i = process_num; i < restorecon_queue_.size(); i += total_processes) {\n        android::base::Timer t;\n        auto& dir = restorecon_queue_[i];\n\n        selinux_android_restorecon(dir.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE);\n\n        //Mark a dir restorecon operation for 50ms,\n        //Maybe you can add this dir to the ueventd.rc script to parallel processing\n        if (t.duration() > 50ms) {\n            LOG(INFO) << \"took \" << t.duration().count() <<\"ms restorecon '\"\n                        << dir.c_str() << \"' on process '\" << process_num  <<\"'\";\n        }\n    }\n\n    //Calculate process restorecon time\n    LOG(VERBOSE) << \"took \" << t_process.duration().count() << \"ms on process '\"\n                << process_num  << \"'\";\n}\n\nvoid ColdBoot::GenerateRestoreCon(const std::string& directory) {\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(directory.c_str()), &closedir);\n\n    if (!dir) {\n        PLOG(WARNING) << \"opendir \" << directory.c_str();\n        return;\n    }\n\n    struct dirent* dent;\n    while ((dent = readdir(dir.get())) != NULL) {\n        if (strcmp(dent->d_name, \".\") == 0 || strcmp(dent->d_name, \"..\") == 0) continue;\n\n        struct stat st;\n        if (fstatat(dirfd(dir.get()), dent->d_name, &st, 0) == -1) continue;\n\n        if (S_ISDIR(st.st_mode)) {\n            std::string fullpath = directory + \"/\" + dent->d_name;\n            auto parallel_restorecon =\n                std::find(parallel_restorecon_queue_.begin(),\n                    parallel_restorecon_queue_.end(), fullpath);\n            if (parallel_restorecon == parallel_restorecon_queue_.end()) {\n                restorecon_queue_.emplace_back(fullpath);\n            }\n        }\n    }\n}\n\nvoid ColdBoot::RegenerateUevents() {\n    uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {\n        uevent_queue_.emplace_back(uevent);\n        return ListenerAction::kContinue;\n    });\n}\n\nvoid ColdBoot::ForkSubProcesses() {\n    for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {\n        auto pid = fork();\n        if (pid < 0) {\n            PLOG(FATAL) << \"fork() failed!\";\n        }\n\n        if (pid == 0) {\n            UeventHandlerMain(i, num_handler_subprocesses_);\n            if (enable_parallel_restorecon_) {\n                RestoreConHandler(i, num_handler_subprocesses_);\n            }\n            _exit(EXIT_SUCCESS);\n        }\n\n        subprocess_pids_.emplace(pid);\n    }\n}\n\nvoid ColdBoot::WaitForSubProcesses() {\n    // Treat subprocesses that crash or get stuck the same as if ueventd itself has crashed or gets\n    // stuck.\n    //\n    // When a subprocess crashes, we fatally abort from ueventd.  init will restart ueventd when\n    // init reaps it, and the cold boot process will start again.  If this continues to fail, then\n    // since ueventd is marked as a critical service, init will reboot to bootloader.\n    //\n    // When a subprocess gets stuck, keep ueventd spinning waiting for it.  init has a timeout for\n    // cold boot and will reboot to the bootloader if ueventd does not complete in time.\n    while (!subprocess_pids_.empty()) {\n        int status;\n        pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0));\n        if (pid == -1) {\n            PLOG(ERROR) << \"waitpid() failed\";\n            continue;\n        }\n\n        auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);\n        if (it == subprocess_pids_.end()) continue;\n\n        if (WIFEXITED(status)) {\n            if (WEXITSTATUS(status) == EXIT_SUCCESS) {\n                subprocess_pids_.erase(it);\n            } else {\n                LOG(FATAL) << \"subprocess exited with status \" << WEXITSTATUS(status);\n            }\n        } else if (WIFSIGNALED(status)) {\n            LOG(FATAL) << \"subprocess killed by signal \" << WTERMSIG(status);\n        }\n    }\n}\n\nvoid ColdBoot::Run() {\n    android::base::Timer cold_boot_timer;\n\n    RegenerateUevents();\n\n    if (enable_parallel_restorecon_) {\n        if (parallel_restorecon_queue_.empty()) {\n            parallel_restorecon_queue_.emplace_back(\"/sys\");\n            // takes long time for /sys/devices, parallelize it\n            parallel_restorecon_queue_.emplace_back(\"/sys/devices\");\n            LOG(INFO) << \"Parallel processing directory is not set, set the default\";\n        }\n        for (const auto& dir : parallel_restorecon_queue_) {\n            selinux_android_restorecon(dir.c_str(), 0);\n            GenerateRestoreCon(dir);\n        }\n    }\n\n    ForkSubProcesses();\n\n    if (!enable_parallel_restorecon_) {\n        selinux_android_restorecon(\"/sys\", SELINUX_ANDROID_RESTORECON_RECURSE);\n    }\n\n    WaitForSubProcesses();\n\n    android::base::SetProperty(kColdBootDoneProp, \"true\");\n    LOG(INFO) << \"Coldboot took \" << cold_boot_timer.duration().count() / 1000.0f << \" seconds\";\n}\n\nstatic UeventdConfiguration GetConfiguration() {\n    if (IsMicrodroid()) {\n        return ParseConfig({\"/system/etc/ueventd.rc\", \"/vendor/etc/ueventd.rc\"});\n    }\n\n    auto hardware = android::base::GetProperty(\"ro.hardware\", \"\");\n\n    struct LegacyPathInfo {\n        std::string legacy_path;\n        std::string preferred;\n    };\n    std::vector<LegacyPathInfo> legacy_paths{\n            {\"/vendor/ueventd.rc\", \"/vendor/etc/ueventd.rc\"},\n            {\"/odm/ueventd.rc\", \"/odm/etc/ueventd.rc\"},\n            {\"/ueventd.\" + hardware + \".rc\", \"another ueventd.rc file\"}};\n\n    std::vector<std::string> canonical{\"/system/etc/ueventd.rc\"};\n\n    if (android::base::GetIntProperty(\"ro.product.first_api_level\", 10000) < __ANDROID_API_T__) {\n        // TODO: Remove these legacy paths once Android S is no longer supported.\n        for (const auto& info : legacy_paths) {\n            canonical.push_back(info.legacy_path);\n        }\n    } else {\n        // Warn if newer device is using legacy paths.\n        for (const auto& info : legacy_paths) {\n            if (access(info.legacy_path.c_str(), F_OK) == 0) {\n                LOG(FATAL_WITHOUT_ABORT)\n                        << \"Legacy ueventd configuration file detected and will not be parsed: \"\n                        << info.legacy_path << \". Please move your configuration to \"\n                        << info.preferred << \" instead.\";\n            }\n        }\n    }\n\n    return ParseConfig(canonical);\n}\n\nint ueventd_main(int argc, char** argv) {\n    /*\n     * init sets the umask to 077 for forked processes. We need to\n     * create files with exact permissions, without modification by\n     * the umask.\n     */\n    umask(000);\n\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n\n    LOG(INFO) << \"ueventd started!\";\n\n    SelinuxSetupKernelLogging();\n    SelabelInitialize();\n\n    std::vector<std::unique_ptr<UeventHandler>> uevent_handlers;\n\n    auto ueventd_configuration = GetConfiguration();\n\n    UeventListener uevent_listener(ueventd_configuration.uevent_socket_rcvbuf_size);\n\n    // Right after making DeviceHandler, replay all events looking for which\n    // block device has the boot partition. This lets us make symlinks\n    // for all of the other partitions on the same disk. Note that by the time\n    // we get here we know that the boot partition has already shown up (if\n    // we're looking for it) so just regenerating events is enough to know\n    // we'll see it.\n    std::unique_ptr<DeviceHandler> device_handler = std::make_unique<DeviceHandler>(\n            std::move(ueventd_configuration.dev_permissions),\n            std::move(ueventd_configuration.sysfs_permissions),\n            std::move(ueventd_configuration.drivers), std::move(ueventd_configuration.subsystems),\n            android::fs_mgr::GetBootDevices(), android::fs_mgr::GetBootPartUuid(), true);\n    uevent_listener.RegenerateUevents([&](const Uevent& uevent) -> ListenerAction {\n        bool uuid_check_done = device_handler->CheckUeventForBootPartUuid(uevent);\n        return uuid_check_done ? ListenerAction::kStop : ListenerAction::kContinue;\n    });\n\n    uevent_handlers.emplace_back(std::move(device_handler));\n    uevent_handlers.emplace_back(std::make_unique<FirmwareHandler>(\n            std::move(ueventd_configuration.firmware_directories),\n            std::move(ueventd_configuration.external_firmware_handlers)));\n\n    if (ueventd_configuration.enable_modalias_handling) {\n        std::vector<std::string> base_paths = {\"/odm/lib/modules\", \"/vendor/lib/modules\"};\n        uevent_handlers.emplace_back(std::make_unique<ModaliasHandler>(base_paths));\n    }\n    if (!android::base::GetBoolProperty(kColdBootDoneProp, false)) {\n        ColdBoot cold_boot(uevent_listener, uevent_handlers,\n                           ueventd_configuration.enable_parallel_restorecon,\n                           ueventd_configuration.parallel_restorecon_dirs);\n        cold_boot.Run();\n    }\n\n    for (auto& uevent_handler : uevent_handlers) {\n        uevent_handler->ColdbootDone();\n    }\n\n    // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.\n    signal(SIGCHLD, SIG_IGN);\n    // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN\n    // for SIGCHLD above.\n    while (waitpid(-1, nullptr, WNOHANG) > 0) {\n    }\n\n    // Restore prio before main loop\n    setpriority(PRIO_PROCESS, 0, 0);\n    uevent_listener.Poll([&uevent_handlers](const Uevent& uevent) {\n        for (auto& uevent_handler : uevent_handlers) {\n            uevent_handler->HandleUevent(uevent);\n        }\n        return ListenerAction::kContinue;\n    });\n\n    return 0;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/ueventd.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _INIT_UEVENTD_H_\n#define _INIT_UEVENTD_H_\n\nnamespace android {\nnamespace init {\n\nint ueventd_main(int argc, char** argv);\n\n}  // namespace init\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "init/ueventd_parser.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ueventd_parser.h\"\n\n#include <grp.h>\n#include <pwd.h>\n\n#include <android-base/parseint.h>\n\n#include \"import_parser.h\"\n#include \"keyword_map.h\"\n#include \"parser.h\"\n\nusing android::base::ParseByteCount;\n\nnamespace android {\nnamespace init {\n\nResult<void> ParsePermissionsLine(std::vector<std::string>&& args,\n                                  std::vector<SysfsPermissions>* out_sysfs_permissions,\n                                  std::vector<Permissions>* out_dev_permissions) {\n    bool is_sysfs = out_sysfs_permissions != nullptr;\n    if (is_sysfs && !(args.size() == 5 || args.size() == 6)) {\n        return Error() << \"/sys/ lines must have 5 or 6 entries\";\n    }\n\n    if (!is_sysfs && !(args.size() == 4 || args.size() == 5)) {\n        return Error() << \"/dev/ lines must have 4 or 5 entries\";\n    }\n\n    auto it = args.begin();\n    const std::string& name = *it++;\n\n    std::string sysfs_attribute;\n    if (is_sysfs) sysfs_attribute = *it++;\n\n    // args is now common to both sys and dev entries and contains: <perm> <uid> <gid>\n    std::string& perm_string = *it++;\n    char* end_pointer = 0;\n    mode_t perm = strtol(perm_string.c_str(), &end_pointer, 8);\n    if (end_pointer == nullptr || *end_pointer != '\\0') {\n        return Error() << \"invalid mode '\" << perm_string << \"'\";\n    }\n\n    std::string& uid_string = *it++;\n    passwd* pwd = getpwnam(uid_string.c_str());\n    if (!pwd) {\n        return Error() << \"invalid uid '\" << uid_string << \"'\";\n    }\n    uid_t uid = pwd->pw_uid;\n\n    std::string& gid_string = *it++;\n    struct group* grp = getgrnam(gid_string.c_str());\n    if (!grp) {\n        return Error() << \"invalid gid '\" << gid_string << \"'\";\n    }\n    gid_t gid = grp->gr_gid;\n\n    bool no_fnm_pathname = false;\n    if (it != args.end()) {\n        std::string& flags = *it++;\n        if (flags != \"no_fnm_pathname\") {\n            return Error() << \"invalid option '\" << flags << \"', only no_fnm_pathname is supported\";\n        }\n        no_fnm_pathname = true;\n    }\n\n    if (is_sysfs) {\n        out_sysfs_permissions->emplace_back(name, sysfs_attribute, perm, uid, gid, no_fnm_pathname);\n    } else {\n        out_dev_permissions->emplace_back(name, perm, uid, gid, no_fnm_pathname);\n    }\n    return {};\n}\n\nResult<void> ParseFirmwareDirectoriesLine(std::vector<std::string>&& args,\n                                          std::vector<std::string>* firmware_directories) {\n    if (args.size() < 2) {\n        return Error() << \"firmware_directories must have at least 1 entry\";\n    }\n\n    std::move(std::next(args.begin()), args.end(), std::back_inserter(*firmware_directories));\n\n    return {};\n}\n\nResult<void> ParseExternalFirmwareHandlerLine(\n        std::vector<std::string>&& args,\n        std::vector<ExternalFirmwareHandler>* external_firmware_handlers) {\n    if (args.size() != 4 && args.size() != 5) {\n        return Error() << \"external_firmware_handler lines must have 3 or 4 parameters\";\n    }\n\n    if (std::find_if(external_firmware_handlers->begin(), external_firmware_handlers->end(),\n                     [&args](const auto& other) { return other.devpath == args[1]; }) !=\n        external_firmware_handlers->end()) {\n        return Error() << \"found a previous external_firmware_handler with the same devpath, '\"\n                       << args[1] << \"'\";\n    }\n\n    passwd* pwd = getpwnam(args[2].c_str());\n    if (!pwd) {\n        return ErrnoError() << \"invalid handler uid'\" << args[2] << \"'\";\n    }\n\n    gid_t gid = 0;\n    int handler_index = 3;\n    if (args.size() == 5) {\n        struct group* grp = getgrnam(args[3].c_str());\n        if (!grp) {\n            return ErrnoError() << \"invalid handler gid '\" << args[3] << \"'\";\n        }\n        gid = grp->gr_gid;\n        handler_index = 4;\n    }\n\n    ExternalFirmwareHandler handler(std::move(args[1]), pwd->pw_uid, gid,\n                                    std::move(args[handler_index]));\n    external_firmware_handlers->emplace_back(std::move(handler));\n\n    return {};\n}\n\nResult<void> ParseEnabledDisabledLine(std::vector<std::string>&& args, bool* feature) {\n    if (args.size() != 2) {\n        return Error() << args[0] << \" lines take exactly one parameter\";\n    }\n\n    if (args[1] == \"enabled\") {\n        *feature = true;\n    } else if (args[1] == \"disabled\") {\n        *feature = false;\n    } else {\n        return Error() << args[0] << \" takes either 'enabled' or 'disabled' as a parameter\";\n    }\n\n    return {};\n}\n\nResult<void> ParseParallelRestoreconDirsLine(std::vector<std::string>&& args,\n                                          std::vector<std::string>* parallel_restorecon_dirs) {\n    if (args.size() != 2) {\n        return Error() << \"parallel_restorecon_dir lines must have exactly 2 parameters\";\n    }\n\n    std::move(std::next(args.begin()), args.end(), std::back_inserter(*parallel_restorecon_dirs));\n\n    return {};\n}\n\nResult<void> ParseUeventSocketRcvbufSizeLine(std::vector<std::string>&& args,\n                                             size_t* uevent_socket_rcvbuf_size) {\n    if (args.size() != 2) {\n        return Error() << \"uevent_socket_rcvbuf_size lines take exactly one parameter\";\n    }\n\n    size_t parsed_size;\n    if (!ParseByteCount(args[1], &parsed_size)) {\n        return Error() << \"could not parse size '\" << args[1] << \"' for uevent_socket_rcvbuf_line\";\n    }\n\n    *uevent_socket_rcvbuf_size = parsed_size;\n\n    return {};\n}\n\nclass SubsystemParser : public SectionParser {\n  public:\n    SubsystemParser(std::vector<Subsystem>* subsystems) : subsystems_(subsystems) {}\n    Result<void> ParseSection(std::vector<std::string>&& args, const std::string& filename,\n                              int line) override;\n    Result<void> ParseLineSection(std::vector<std::string>&& args, int line) override;\n    Result<void> EndSection() override;\n\n  private:\n    Result<void> ParseDevName(std::vector<std::string>&& args);\n    Result<void> ParseDirName(std::vector<std::string>&& args);\n\n    Subsystem subsystem_;\n    std::vector<Subsystem>* subsystems_;\n};\n\nResult<void> SubsystemParser::ParseSection(std::vector<std::string>&& args,\n                                           const std::string& filename, int line) {\n    if (args.size() != 2) {\n        return Error() << \"subsystems must have exactly one name\";\n    }\n\n    if (std::find(subsystems_->begin(), subsystems_->end(), args[1]) != subsystems_->end()) {\n        return Error() << \"ignoring duplicate subsystem entry\";\n    }\n\n    subsystem_ = Subsystem(std::move(args[1]));\n\n    return {};\n}\n\nResult<void> SubsystemParser::ParseDevName(std::vector<std::string>&& args) {\n    if (args[1] == \"uevent_devname\") {\n        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVNAME;\n        return {};\n    }\n    if (args[1] == \"uevent_devpath\") {\n        subsystem_.devname_source_ = Subsystem::DEVNAME_UEVENT_DEVPATH;\n        return {};\n    }\n    if (args[1] == \"sys_name\") {\n        subsystem_.devname_source_ = Subsystem::DEVNAME_SYS_NAME;\n        return {};\n    }\n\n    return Error() << \"invalid devname '\" << args[1] << \"'\";\n}\n\nResult<void> SubsystemParser::ParseDirName(std::vector<std::string>&& args) {\n    if (args[1].front() != '/') {\n        return Error() << \"dirname '\" << args[1] << \" ' does not start with '/'\";\n    }\n\n    subsystem_.dir_name_ = args[1];\n    return {};\n}\n\nResult<void> SubsystemParser::ParseLineSection(std::vector<std::string>&& args, int line) {\n    using OptionParser = Result<void> (SubsystemParser::*)(std::vector<std::string> && args);\n    // clang-format off\n    static const KeywordMap<OptionParser> parser_map = {\n        {\"devname\",     {1,     1,      &SubsystemParser::ParseDevName}},\n        {\"dirname\",     {1,     1,      &SubsystemParser::ParseDirName}},\n    };\n    // clang-format on\n\n    auto parser = parser_map.Find(args);\n\n    if (!parser.ok()) return Error() << parser.error();\n\n    return std::invoke(*parser, this, std::move(args));\n}\n\nResult<void> SubsystemParser::EndSection() {\n    subsystems_->emplace_back(std::move(subsystem_));\n\n    return {};\n}\n\nUeventdConfiguration ParseConfig(const std::vector<std::string>& configs) {\n    Parser parser;\n    UeventdConfiguration ueventd_configuration;\n\n    parser.AddSectionParser(\"import\", std::make_unique<ImportParser>(&parser));\n    parser.AddSectionParser(\"subsystem\",\n                            std::make_unique<SubsystemParser>(&ueventd_configuration.subsystems));\n    parser.AddSectionParser(\"driver\",\n                            std::make_unique<SubsystemParser>(&ueventd_configuration.drivers));\n\n    using namespace std::placeholders;\n    parser.AddSingleLineParser(\n            \"/sys/\",\n            std::bind(ParsePermissionsLine, _1, &ueventd_configuration.sysfs_permissions, nullptr));\n    parser.AddSingleLineParser(\"/dev/\", std::bind(ParsePermissionsLine, _1, nullptr,\n                                                  &ueventd_configuration.dev_permissions));\n    parser.AddSingleLineParser(\"firmware_directories\",\n                               std::bind(ParseFirmwareDirectoriesLine, _1,\n                                         &ueventd_configuration.firmware_directories));\n    parser.AddSingleLineParser(\"external_firmware_handler\",\n                               std::bind(ParseExternalFirmwareHandlerLine, _1,\n                                         &ueventd_configuration.external_firmware_handlers));\n    parser.AddSingleLineParser(\"modalias_handling\",\n                               std::bind(ParseEnabledDisabledLine, _1,\n                                         &ueventd_configuration.enable_modalias_handling));\n    parser.AddSingleLineParser(\"uevent_socket_rcvbuf_size\",\n                               std::bind(ParseUeventSocketRcvbufSizeLine, _1,\n                                         &ueventd_configuration.uevent_socket_rcvbuf_size));\n    parser.AddSingleLineParser(\"parallel_restorecon_dir\",\n                               std::bind(ParseParallelRestoreconDirsLine, _1,\n                                         &ueventd_configuration.parallel_restorecon_dirs));\n    parser.AddSingleLineParser(\"parallel_restorecon\",\n                               std::bind(ParseEnabledDisabledLine, _1,\n                                         &ueventd_configuration.enable_parallel_restorecon));\n\n    for (const auto& config : configs) {\n        parser.ParseConfig(config);\n    }\n\n    return ueventd_configuration;\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/ueventd_parser.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"devices.h\"\n#include \"firmware_handler.h\"\n\nnamespace android {\nnamespace init {\n\nstruct UeventdConfiguration {\n    std::vector<Subsystem> subsystems;\n    std::vector<Subsystem> drivers;\n    std::vector<SysfsPermissions> sysfs_permissions;\n    std::vector<Permissions> dev_permissions;\n    std::vector<std::string> firmware_directories;\n    std::vector<ExternalFirmwareHandler> external_firmware_handlers;\n    std::vector<std::string> parallel_restorecon_dirs;\n    bool enable_modalias_handling = false;\n    size_t uevent_socket_rcvbuf_size = 0;\n    bool enable_parallel_restorecon = false;\n};\n\nUeventdConfiguration ParseConfig(const std::vector<std::string>& configs);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/ueventd_parser_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"ueventd_parser.h\"\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n#include <private/android_filesystem_config.h>\n\n#include \"firmware_handler.h\"\n\nnamespace android {\nnamespace init {\n\nvoid TestSubsystems(const Subsystem& expected, const Subsystem& test) {\n    EXPECT_EQ(expected.name_, test.name_);\n    EXPECT_EQ(expected.devname_source_, test.devname_source_) << expected.name_;\n    EXPECT_EQ(expected.dir_name_, test.dir_name_) << expected.name_;\n}\n\nvoid TestPermissions(const Permissions& expected, const Permissions& test) {\n    EXPECT_EQ(expected.name_, test.name_);\n    EXPECT_EQ(expected.perm_, test.perm_) << expected.name_;\n    EXPECT_EQ(expected.uid_, test.uid_) << expected.name_;\n    EXPECT_EQ(expected.gid_, test.gid_) << expected.name_;\n    EXPECT_EQ(expected.prefix_, test.prefix_) << expected.name_;\n    EXPECT_EQ(expected.wildcard_, test.wildcard_) << expected.name_;\n}\n\nvoid TestSysfsPermissions(const SysfsPermissions& expected, const SysfsPermissions& test) {\n    TestPermissions(expected, test);\n    EXPECT_EQ(expected.attribute_, test.attribute_);\n}\n\nvoid TestExternalFirmwareHandler(const ExternalFirmwareHandler& expected,\n                                 const ExternalFirmwareHandler& test) {\n    EXPECT_EQ(expected.devpath, test.devpath) << expected.devpath;\n    EXPECT_EQ(expected.uid, test.uid) << expected.uid;\n    EXPECT_EQ(expected.gid, test.gid) << expected.gid;\n    EXPECT_EQ(expected.handler_path, test.handler_path) << expected.handler_path;\n}\n\ntemplate <typename T, typename F>\nvoid TestVector(const T& expected, const T& test, F function) {\n    ASSERT_EQ(expected.size(), test.size());\n    auto expected_it = expected.begin();\n    auto test_it = test.begin();\n\n    for (; expected_it != expected.end(); ++expected_it, ++test_it) {\n        function(*expected_it, *test_it);\n    }\n}\n\nvoid TestUeventdFile(const std::string& content, const UeventdConfiguration& expected) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    ASSERT_TRUE(android::base::WriteStringToFd(content, tf.fd));\n\n    auto result = ParseConfig({tf.path});\n\n    TestVector(expected.subsystems, result.subsystems, TestSubsystems);\n    TestVector(expected.sysfs_permissions, result.sysfs_permissions, TestSysfsPermissions);\n    TestVector(expected.dev_permissions, result.dev_permissions, TestPermissions);\n    EXPECT_EQ(expected.firmware_directories, result.firmware_directories);\n    TestVector(expected.external_firmware_handlers, result.external_firmware_handlers,\n               TestExternalFirmwareHandler);\n    EXPECT_EQ(expected.parallel_restorecon_dirs, result.parallel_restorecon_dirs);\n}\n\nTEST(ueventd_parser, EmptyFile) {\n    TestUeventdFile(\"\", {});\n}\n\nTEST(ueventd_parser, Subsystems) {\n    auto ueventd_file = R\"(\nsubsystem test_devname\n    devname uevent_devname\n\nsubsystem test_devpath_no_dirname\n    devname uevent_devpath\n\nsubsystem test_devname2\n    devname uevent_devname\n\nsubsystem test_devpath_dirname\n    devname uevent_devpath\n    dirname /dev/graphics\n)\";\n\n    auto subsystems = std::vector<Subsystem>{\n            {\"test_devname\", Subsystem::DEVNAME_UEVENT_DEVNAME, \"/dev\"},\n            {\"test_devpath_no_dirname\", Subsystem::DEVNAME_UEVENT_DEVPATH, \"/dev\"},\n            {\"test_devname2\", Subsystem::DEVNAME_UEVENT_DEVNAME, \"/dev\"},\n            {\"test_devpath_dirname\", Subsystem::DEVNAME_UEVENT_DEVPATH, \"/dev/graphics\"}};\n\n    TestUeventdFile(ueventd_file, {subsystems, {}, {}, {}, {}, {}, {}});\n}\n\nTEST(ueventd_parser, Drivers) {\n    auto ueventd_file = R\"(\ndriver test_devname\n    devname uevent_devname\n\ndriver test_devpath_no_dirname\n    devname uevent_devpath\n\ndriver test_devname2\n    devname uevent_devname\n\ndriver test_devpath_dirname\n    devname uevent_devpath\n    dirname /dev/graphics\n)\";\n\n    auto drivers = std::vector<Subsystem>{\n            {\"test_devname\", Subsystem::DEVNAME_UEVENT_DEVNAME, \"/dev\"},\n            {\"test_devpath_no_dirname\", Subsystem::DEVNAME_UEVENT_DEVPATH, \"/dev\"},\n            {\"test_devname2\", Subsystem::DEVNAME_UEVENT_DEVNAME, \"/dev\"},\n            {\"test_devpath_dirname\", Subsystem::DEVNAME_UEVENT_DEVPATH, \"/dev/graphics\"}};\n\n    TestUeventdFile(ueventd_file, {{}, drivers, {}, {}, {}, {}, {}, {}});\n}\n\nTEST(ueventd_parser, Permissions) {\n    auto ueventd_file = R\"(\n/dev/rtc0                 0640   system     system\n/dev/graphics/*           0660   root       graphics\n/dev/*/test               0660   root       system\n\n/sys/devices/platform/trusty.*      trusty_version    0440  root   log\n/sys/devices/virtual/input/input    enable            0660  root   input\n/sys/devices/virtual/*/input        poll_delay        0660  root   input    no_fnm_pathname\n)\";\n\n    auto permissions = std::vector<Permissions>{\n            {\"/dev/rtc0\", 0640, AID_SYSTEM, AID_SYSTEM, false},\n            {\"/dev/graphics/*\", 0660, AID_ROOT, AID_GRAPHICS, false},\n            {\"/dev/*/test\", 0660, AID_ROOT, AID_SYSTEM, false},\n    };\n\n    auto sysfs_permissions = std::vector<SysfsPermissions>{\n            {\"/sys/devices/platform/trusty.*\", \"trusty_version\", 0440, AID_ROOT, AID_LOG, false},\n            {\"/sys/devices/virtual/input/input\", \"enable\", 0660, AID_ROOT, AID_INPUT, false},\n            {\"/sys/devices/virtual/*/input\", \"poll_delay\", 0660, AID_ROOT, AID_INPUT, true},\n    };\n\n    TestUeventdFile(ueventd_file, {{}, {}, sysfs_permissions, permissions, {}, {}, {}});\n}\n\nTEST(ueventd_parser, FirmwareDirectories) {\n    auto ueventd_file = R\"(\nfirmware_directories /first/ /second /third\nfirmware_directories /more\n)\";\n\n    auto firmware_directories = std::vector<std::string>{\n            \"/first/\",\n            \"/second\",\n            \"/third\",\n            \"/more\",\n    };\n\n    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, firmware_directories, {}, {}});\n}\n\nTEST(ueventd_parser, ExternalFirmwareHandlers) {\n    auto ueventd_file = R\"(\nexternal_firmware_handler devpath root handler_path\nexternal_firmware_handler /devices/path/firmware/something001.bin system /vendor/bin/firmware_handler.sh\nexternal_firmware_handler /devices/path/firmware/something002.bin radio \"/vendor/bin/firmware_handler.sh --has --arguments\"\nexternal_firmware_handler /devices/path/firmware/* root \"/vendor/bin/firmware_handler.sh\"\nexternal_firmware_handler /devices/path/firmware/something* system \"/vendor/bin/firmware_handler.sh\"\nexternal_firmware_handler /devices/path/*/firmware/something*.bin radio \"/vendor/bin/firmware_handler.sh\"\nexternal_firmware_handler /devices/path/firmware/something003.bin system system /vendor/bin/firmware_handler.sh\nexternal_firmware_handler /devices/path/firmware/something004.bin radio radio \"/vendor/bin/firmware_handler.sh --has --arguments\"\n)\";\n\n    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{\n            {\n                    \"devpath\",\n                    AID_ROOT,\n                    AID_ROOT,\n                    \"handler_path\",\n            },\n            {\n                    \"/devices/path/firmware/something001.bin\",\n                    AID_SYSTEM,\n                    AID_ROOT,\n                    \"/vendor/bin/firmware_handler.sh\",\n            },\n            {\n                    \"/devices/path/firmware/something002.bin\",\n                    AID_RADIO,\n                    AID_ROOT,\n                    \"/vendor/bin/firmware_handler.sh --has --arguments\",\n            },\n            {\n                    \"/devices/path/firmware/\",\n                    AID_ROOT,\n                    AID_ROOT,\n                    \"/vendor/bin/firmware_handler.sh\",\n            },\n            {\n                    \"/devices/path/firmware/something\",\n                    AID_SYSTEM,\n                    AID_ROOT,\n                    \"/vendor/bin/firmware_handler.sh\",\n            },\n            {\n                    \"/devices/path/*/firmware/something*.bin\",\n                    AID_RADIO,\n                    AID_ROOT,\n                    \"/vendor/bin/firmware_handler.sh\",\n            },\n            {\n                    \"/devices/path/firmware/something003.bin\",\n                    AID_SYSTEM,\n                    AID_SYSTEM,\n                    \"/vendor/bin/firmware_handler.sh\",\n            },\n            {\n                    \"/devices/path/firmware/something004.bin\",\n                    AID_RADIO,\n                    AID_RADIO,\n                    \"/vendor/bin/firmware_handler.sh --has --arguments\",\n            },\n    };\n\n    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, external_firmware_handlers, {}});\n}\n\nTEST(ueventd_parser, ExternalFirmwareHandlersDuplicate) {\n    auto ueventd_file = R\"(\nexternal_firmware_handler devpath root handler_path\nexternal_firmware_handler devpath root handler_path2\n)\";\n\n    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{\n            {\n                    \"devpath\",\n                    AID_ROOT,\n                    AID_ROOT,\n                    \"handler_path\",\n            },\n    };\n\n    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, external_firmware_handlers, {}});\n}\n\nTEST(ueventd_parser, ParallelRestoreconDirs) {\n    auto ueventd_file = R\"(\nparallel_restorecon_dir /sys\nparallel_restorecon_dir /sys/devices\n)\";\n\n    auto parallel_restorecon_dirs = std::vector<std::string>{\n            \"/sys\",\n            \"/sys/devices\",\n    };\n\n    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, parallel_restorecon_dirs});\n}\n\nTEST(ueventd_parser, UeventSocketRcvbufSize) {\n    auto ueventd_file = R\"(\nuevent_socket_rcvbuf_size 8k\nuevent_socket_rcvbuf_size 8M\n)\";\n\n    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, {}, false, 8 * 1024 * 1024});\n}\n\nTEST(ueventd_parser, EnabledDisabledLines) {\n    auto ueventd_file = R\"(\nmodalias_handling enabled\nparallel_restorecon enabled\nmodalias_handling disabled\n)\";\n\n    TestUeventdFile(ueventd_file, {{}, {}, {}, {}, {}, {}, {}, false, 0, true});\n\n    auto ueventd_file2 = R\"(\nparallel_restorecon enabled\nmodalias_handling enabled\nparallel_restorecon disabled\n)\";\n\n    TestUeventdFile(ueventd_file2, {{}, {}, {}, {}, {}, {}, {}, true, 0, false});\n}\n\nTEST(ueventd_parser, AllTogether) {\n    auto ueventd_file = R\"(\n\n/dev/rtc0                 0640   system     system\nfirmware_directories /first/ /second /third\n/sys/devices/platform/trusty.*      trusty_version        0440  root   log\n\nsubsystem test_devname\n    devname uevent_devname\n\ndriver d_test_devpath\n    devname uevent_devpath\n\n/dev/graphics/*           0660   root       graphics\n\nsubsystem test_devpath_no_dirname\n    devname uevent_devpath\n\n/sys/devices/virtual/input/input   enable      0660  root   input\n\n## this is a comment\n\nsubsystem test_devname2\n## another comment\n    devname uevent_devname\n\nsubsystem test_devpath_dirname\n    devname uevent_devpath\n    dirname /dev/graphics\n\ndriver d_test_devname_dirname\n    devname uevent_devname\n    dirname /dev/sound\n\n/dev/*/test               0660   root       system\n/sys/devices/virtual/*/input   poll_delay  0660  root   input    no_fnm_pathname\nfirmware_directories /more\n\nexternal_firmware_handler /devices/path/firmware/firmware001.bin root /vendor/bin/touch.sh\n\nuevent_socket_rcvbuf_size 6M\nmodalias_handling enabled\nparallel_restorecon enabled\n\nparallel_restorecon_dir /sys\nparallel_restorecon_dir /sys/devices\n\n#ending comment\n)\";\n\n    auto subsystems = std::vector<Subsystem>{\n            {\"test_devname\", Subsystem::DEVNAME_UEVENT_DEVNAME, \"/dev\"},\n            {\"test_devpath_no_dirname\", Subsystem::DEVNAME_UEVENT_DEVPATH, \"/dev\"},\n            {\"test_devname2\", Subsystem::DEVNAME_UEVENT_DEVNAME, \"/dev\"},\n            {\"test_devpath_dirname\", Subsystem::DEVNAME_UEVENT_DEVPATH, \"/dev/graphics\"}};\n\n    auto drivers = std::vector<Subsystem>{\n            {\"d_test_devpath\", Subsystem::DEVNAME_UEVENT_DEVPATH, \"/dev\"},\n            {\"d_test_devname_dirname\", Subsystem::DEVNAME_UEVENT_DEVNAME, \"/dev/graphics\"}};\n\n    auto permissions = std::vector<Permissions>{\n            {\"/dev/rtc0\", 0640, AID_SYSTEM, AID_SYSTEM, false},\n            {\"/dev/graphics/*\", 0660, AID_ROOT, AID_GRAPHICS, false},\n            {\"/dev/*/test\", 0660, AID_ROOT, AID_SYSTEM, false},\n    };\n\n    auto sysfs_permissions = std::vector<SysfsPermissions>{\n            {\"/sys/devices/platform/trusty.*\", \"trusty_version\", 0440, AID_ROOT, AID_LOG, false},\n            {\"/sys/devices/virtual/input/input\", \"enable\", 0660, AID_ROOT, AID_INPUT, false},\n            {\"/sys/devices/virtual/*/input\", \"poll_delay\", 0660, AID_ROOT, AID_INPUT, true},\n    };\n\n    auto firmware_directories = std::vector<std::string>{\n            \"/first/\",\n            \"/second\",\n            \"/third\",\n            \"/more\",\n    };\n\n    auto external_firmware_handlers = std::vector<ExternalFirmwareHandler>{\n            {\"/devices/path/firmware/firmware001.bin\", AID_ROOT, AID_ROOT, \"/vendor/bin/touch.sh\"},\n    };\n\n    auto parallel_restorecon_dirs = std::vector<std::string>{\n            \"/sys\",\n            \"/sys/devices\",\n    };\n\n    size_t uevent_socket_rcvbuf_size = 6 * 1024 * 1024;\n\n    TestUeventdFile(ueventd_file,\n                    {subsystems, drivers, sysfs_permissions, permissions, firmware_directories,\n                     external_firmware_handlers, parallel_restorecon_dirs, true,\n                     uevent_socket_rcvbuf_size, true});\n}\n\n// All of these lines are ill-formed, so test that there is 0 output.\nTEST(ueventd_parser, ParseErrors) {\n    auto ueventd_file = R\"(\n\n/dev/rtc0                 badmode   baduidbad     system\n/dev/rtc0                 0640   baduidbad     system\n/dev/rtc0                 0640   system     baduidbad\nfirmware_directories #no directory listed\n/sys/devices/platform/trusty.*      trusty_version        badmode  root   log\n/sys/devices/platform/trusty.*      trusty_version        0440  baduidbad   log\n/sys/devices/platform/trusty.*      trusty_version        0440  root   baduidbad\n/sys/devices/platform/trusty.*      trusty_version        0440  root   root    bad_option\n\nuevent_socket_rcvbuf_size blah\n\nsubsystem #no name\n\nmodalias_handling\nmodalias_handling enabled enabled\nmodalias_handling blah\n\nparallel_restorecon\nparallel_restorecon enabled enabled\nparallel_restorecon blah\n\nexternal_firmware_handler\nexternal_firmware_handler blah blah\nexternal_firmware_handler blah blah blah blah\n\nparallel_restorecon_dir\nparallel_restorecon_dir /sys /sys/devices\n\n)\";\n\n    TestUeventdFile(ueventd_file, {});\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/ueventd_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <linux/futex.h>\n#include <pthread.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <atomic>\n#include <chrono>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/scopeguard.h>\n#include <gtest/gtest.h>\n#include <selinux/android.h>\n#include <selinux/label.h>\n#include <selinux/selinux.h>\n\nusing namespace std::chrono_literals;\nusing namespace std::string_literals;\n\ntemplate <typename T, typename F>\nvoid WriteFromMultipleThreads(std::vector<std::pair<std::string, T>>& files_and_parameters,\n                              F function) {\n    auto num_threads = files_and_parameters.size();\n    pthread_barrier_t barrier;\n    pthread_barrier_init(&barrier, nullptr, num_threads);\n    auto barrier_destroy =\n        android::base::make_scope_guard([&barrier]() { pthread_barrier_destroy(&barrier); });\n\n    auto make_thread_function = [&function, &barrier](const auto& file, const auto& parameter) {\n        return [&]() {\n            function(parameter);\n            pthread_barrier_wait(&barrier);\n            android::base::WriteStringToFile(\"<empty>\", file);\n        };\n    };\n\n    std::vector<std::thread> threads;\n    for (const auto& [file, parameter] : files_and_parameters) {\n        threads.emplace_back(std::thread(make_thread_function(file, parameter)));\n    }\n\n    for (auto& thread : threads) {\n        thread.join();\n    }\n}\n\nTEST(ueventd, setegid_IsPerThread) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n\n    TemporaryDir dir;\n\n    gid_t gid = 0;\n    std::vector<std::pair<std::string, gid_t>> files_and_gids;\n    std::generate_n(std::back_inserter(files_and_gids), 100, [&gid, &dir]() {\n        gid++;\n        return std::pair(dir.path + \"/gid_\"s + std::to_string(gid), gid);\n    });\n\n    WriteFromMultipleThreads(files_and_gids, [](gid_t gid) { EXPECT_EQ(0, setegid(gid)); });\n\n    for (const auto& [file, expected_gid] : files_and_gids) {\n        struct stat info;\n        ASSERT_EQ(0, stat(file.c_str(), &info));\n        EXPECT_EQ(expected_gid, info.st_gid);\n    }\n}\n\nTEST(ueventd, setfscreatecon_IsPerThread) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n    if (!is_selinux_enabled() || security_getenforce() == 1) {\n        GTEST_SKIP() << \"Skipping test, SELinux must be enabled and in permissive mode.\";\n        return;\n    }\n\n    const char* const contexts[] = {\n        \"u:object_r:audio_device:s0\",\n        \"u:object_r:sensors_device:s0\",\n        \"u:object_r:video_device:s0\",\n        \"u:object_r:zero_device:s0\",\n    };\n\n    TemporaryDir dir;\n    std::vector<std::pair<std::string, std::string>> files_and_contexts;\n    for (const char* context : contexts) {\n        files_and_contexts.emplace_back(dir.path + \"/context_\"s + context, context);\n    }\n\n    WriteFromMultipleThreads(files_and_contexts, [](const std::string& context) {\n        EXPECT_EQ(0, setfscreatecon(context.c_str()));\n    });\n\n    for (const auto& [file, expected_context] : files_and_contexts) {\n        char* file_context;\n        ASSERT_GT(getfilecon(file.c_str(), &file_context), 0);\n        EXPECT_EQ(expected_context, file_context);\n        freecon(file_context);\n    }\n}\n\nTEST(ueventd, selabel_lookup_MultiThreaded) {\n    if (getuid() != 0) {\n        GTEST_SKIP() << \"Skipping test, must be run as root.\";\n        return;\n    }\n\n    // Test parameters\n    constexpr auto num_threads = 10;\n    constexpr auto run_time = 200ms;\n\n    std::unique_ptr<selabel_handle, decltype(&selabel_close)> sehandle(\n        selinux_android_file_context_handle(), &selabel_close);\n\n    ASSERT_TRUE(sehandle);\n\n    struct {\n        const char* file;\n        int mode;\n        std::string expected_context;\n    } files_and_modes[] = {\n        {\"/dev/zero\", 020666, \"\"},\n        {\"/dev/null\", 020666, \"\"},\n        {\"/dev/random\", 020666, \"\"},\n        {\"/dev/urandom\", 020666, \"\"},\n    };\n\n    // Precondition, ensure that we can lookup all of these from a single thread, and store the\n    // expected context for each.\n    for (size_t i = 0; i < arraysize(files_and_modes); ++i) {\n        char* secontext;\n        ASSERT_EQ(0, selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,\n                                    files_and_modes[i].mode));\n        files_and_modes[i].expected_context = secontext;\n        freecon(secontext);\n    }\n\n    // Now that we know we can access them, and what their context should be, run in parallel.\n    std::atomic_bool stopped = false;\n    std::atomic_uint num_api_failures = 0;\n    std::atomic_uint num_context_check_failures = 0;\n    std::atomic_uint num_successes = 0;\n\n    auto thread_function = [&]() {\n        while (!stopped) {\n            for (size_t i = 0; i < arraysize(files_and_modes); ++i) {\n                char* secontext;\n                int result = selabel_lookup(sehandle.get(), &secontext, files_and_modes[i].file,\n                                            files_and_modes[i].mode);\n                if (result != 0) {\n                    num_api_failures++;\n                } else {\n                    if (files_and_modes[i].expected_context != secontext) {\n                        num_context_check_failures++;\n                    } else {\n                        num_successes++;\n                    }\n                    freecon(secontext);\n                }\n            }\n        }\n    };\n\n    std::vector<std::thread> threads;\n    std::generate_n(back_inserter(threads), num_threads,\n                    [&]() { return std::thread(thread_function); });\n\n    std::this_thread::sleep_for(run_time);\n    stopped = true;\n    for (auto& thread : threads) {\n        thread.join();\n    }\n\n    EXPECT_EQ(0U, num_api_failures);\n    EXPECT_EQ(0U, num_context_check_failures);\n    EXPECT_GT(num_successes, 0U);\n}\n"
  },
  {
    "path": "init/util.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"util.h\"\n\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <pwd.h>\n#include <signal.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <sys/wait.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <map>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/scopeguard.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <cutils/sockets.h>\n#include <selinux/android.h>\n\n#if defined(__ANDROID__)\n#include <fs_mgr.h>\n#endif\n\n#ifdef INIT_FULL_SOURCES\n#include <android/api-level.h>\n#include <sys/system_properties.h>\n\n#include \"reboot_utils.h\"\n#include \"selabel.h\"\n#include \"selinux.h\"\n#else\n#include \"host_init_stubs.h\"\n#endif\n\nusing android::base::boot_clock;\nusing android::base::StartsWith;\nusing namespace std::literals::string_literals;\n\nnamespace android {\nnamespace init {\n\nconst std::string kDataDirPrefix(\"/data/\");\n\nvoid (*trigger_shutdown)(const std::string& command) = nullptr;\n\n// DecodeUid() - decodes and returns the given string, which can be either the\n// numeric or name representation, into the integer uid or gid.\nResult<uid_t> DecodeUid(const std::string& name) {\n    if (isalpha(name[0])) {\n        passwd* pwd = getpwnam(name.c_str());\n        if (!pwd) return ErrnoError() << \"getpwnam failed\";\n\n        return pwd->pw_uid;\n    }\n\n    errno = 0;\n    uid_t result = static_cast<uid_t>(strtoul(name.c_str(), 0, 0));\n    if (errno) return ErrnoError() << \"strtoul failed\";\n\n    return result;\n}\n\n/*\n * CreateSocket - creates a Unix domain socket in ANDROID_SOCKET_DIR\n * (\"/dev/socket\") as dictated in init.rc. This socket is inherited by the\n * daemon. We communicate the file descriptor's value via the environment\n * variable ANDROID_SOCKET_ENV_PREFIX<name> (\"ANDROID_SOCKET_foo\").\n */\nResult<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,\n                         mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon) {\n    if (!socketcon.empty()) {\n        if (setsockcreatecon(socketcon.c_str()) == -1) {\n            return ErrnoError() << \"setsockcreatecon(\\\"\" << socketcon << \"\\\") failed\";\n        }\n    }\n\n    android::base::unique_fd fd(socket(PF_UNIX, type, 0));\n    if (fd < 0) {\n        return ErrnoError() << \"Failed to open socket '\" << name << \"'\";\n    }\n\n    if (!socketcon.empty()) setsockcreatecon(nullptr);\n\n    struct sockaddr_un addr;\n    memset(&addr, 0 , sizeof(addr));\n    addr.sun_family = AF_UNIX;\n    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR \"/%s\", name.c_str());\n\n    if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {\n        return ErrnoError() << \"Failed to unlink old socket '\" << name << \"'\";\n    }\n\n    std::string secontext;\n    if (SelabelLookupFileContext(addr.sun_path, S_IFSOCK, &secontext) && !secontext.empty()) {\n        setfscreatecon(secontext.c_str());\n    }\n\n    if (passcred) {\n        int on = 1;\n        if (setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) {\n            return ErrnoError() << \"Failed to set SO_PASSCRED '\" << name << \"'\";\n        }\n    }\n\n    int ret = bind(fd.get(), (struct sockaddr*)&addr, sizeof(addr));\n    int savederrno = errno;\n\n    if (!secontext.empty()) {\n        setfscreatecon(nullptr);\n    }\n\n    auto guard = android::base::make_scope_guard([&addr] { unlink(addr.sun_path); });\n\n    if (ret) {\n        errno = savederrno;\n        return ErrnoError() << \"Failed to bind socket '\" << name << \"'\";\n    }\n\n    if (lchown(addr.sun_path, uid, gid)) {\n        return ErrnoError() << \"Failed to lchown socket '\" << addr.sun_path << \"'\";\n    }\n    if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {\n        return ErrnoError() << \"Failed to fchmodat socket '\" << addr.sun_path << \"'\";\n    }\n    if (should_listen && listen(fd.get(), /* use OS maximum */ 1 << 30)) {\n        return ErrnoError() << \"Failed to listen on socket '\" << addr.sun_path << \"'\";\n    }\n\n    LOG(INFO) << \"Created socket '\" << addr.sun_path << \"'\"\n              << \", mode \" << std::oct << perm << std::dec\n              << \", user \" << uid\n              << \", group \" << gid;\n\n    guard.Disable();\n    return fd.release();\n}\n\nResult<std::string> ReadFile(const std::string& path) {\n    android::base::unique_fd fd(\n        TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));\n    if (fd == -1) {\n        return ErrnoError() << \"open() failed\";\n    }\n\n    // For security reasons, disallow world-writable\n    // or group-writable files.\n    struct stat sb;\n    if (fstat(fd.get(), &sb) == -1) {\n        return ErrnoError() << \"fstat failed()\";\n    }\n    if ((sb.st_mode & (S_IWGRP | S_IWOTH)) != 0) {\n        return Error() << \"Skipping insecure file\";\n    }\n\n    std::string content;\n    if (!android::base::ReadFdToString(fd, &content)) {\n        return ErrnoError() << \"Unable to read file contents\";\n    }\n    return content;\n}\n\nstatic int OpenFile(const std::string& path, int flags, mode_t mode) {\n    std::string secontext;\n    if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {\n        setfscreatecon(secontext.c_str());\n    }\n\n    int rc = open(path.c_str(), flags, mode);\n\n    if (!secontext.empty()) {\n        int save_errno = errno;\n        setfscreatecon(nullptr);\n        errno = save_errno;\n    }\n\n    return rc;\n}\n\nResult<void> WriteFile(const std::string& path, const std::string& content) {\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(\n        OpenFile(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));\n    if (fd == -1) {\n        return ErrnoError() << \"open() failed\";\n    }\n    if (!android::base::WriteStringToFd(content, fd)) {\n        return ErrnoError() << \"Unable to write file contents\";\n    }\n    return {};\n}\n\nbool mkdir_recursive(const std::string& path, mode_t mode) {\n    std::string::size_type slash = 0;\n    while ((slash = path.find('/', slash + 1)) != std::string::npos) {\n        auto directory = path.substr(0, slash);\n        struct stat info;\n        if (stat(directory.c_str(), &info) != 0) {\n            auto ret = make_dir(directory, mode);\n            if (!ret && errno != EEXIST) return false;\n        }\n    }\n    auto ret = make_dir(path, mode);\n    if (!ret && errno != EEXIST) return false;\n    return true;\n}\n\nint wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {\n    android::base::Timer t;\n    while (t.duration() < timeout) {\n        struct stat sb;\n        if (stat(filename, &sb) != -1) {\n            LOG(INFO) << \"wait for '\" << filename << \"' took \" << t;\n            return 0;\n        }\n        std::this_thread::sleep_for(10ms);\n    }\n    LOG(WARNING) << \"wait for '\" << filename << \"' timed out and took \" << t;\n    return -1;\n}\n\nbool make_dir(const std::string& path, mode_t mode) {\n    std::string secontext;\n    if (SelabelLookupFileContext(path, mode, &secontext) && !secontext.empty()) {\n        setfscreatecon(secontext.c_str());\n    }\n\n    int rc = mkdir(path.c_str(), mode);\n\n    if (!secontext.empty()) {\n        int save_errno = errno;\n        setfscreatecon(nullptr);\n        errno = save_errno;\n    }\n\n    return rc == 0;\n}\n\n/*\n * Returns true is pathname is a directory\n */\nbool is_dir(const char* pathname) {\n    struct stat info;\n    if (stat(pathname, &info) == -1) {\n        return false;\n    }\n    return S_ISDIR(info.st_mode);\n}\n\nResult<std::string> ExpandProps(const std::string& src) {\n    const char* src_ptr = src.c_str();\n\n    std::string dst;\n\n    /* - variables can either be $x.y or ${x.y}, in case they are only part\n     *   of the string.\n     * - will accept $$ as a literal $.\n     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,\n     *   bad things will happen\n     * - ${x.y:-default} will return default value if property empty.\n     */\n    while (*src_ptr) {\n        const char* c;\n\n        c = strchr(src_ptr, '$');\n        if (!c) {\n            dst.append(src_ptr);\n            return dst;\n        }\n\n        dst.append(src_ptr, c);\n        c++;\n\n        if (*c == '$') {\n            dst.push_back(*(c++));\n            src_ptr = c;\n            continue;\n        } else if (*c == '\\0') {\n            return dst;\n        }\n\n        std::string prop_name;\n        std::string def_val;\n        if (*c == '{') {\n            c++;\n            const char* end = strchr(c, '}');\n            if (!end) {\n                // failed to find closing brace, abort.\n                return Error() << \"unexpected end of string in '\" << src << \"', looking for }\";\n            }\n            prop_name = std::string(c, end);\n            c = end + 1;\n            size_t def = prop_name.find(\":-\");\n            if (def < prop_name.size()) {\n                def_val = prop_name.substr(def + 2);\n                prop_name = prop_name.substr(0, def);\n            }\n        } else {\n            prop_name = c;\n            if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__) {\n                return Error() << \"using deprecated syntax for specifying property '\" << c\n                               << \"', use ${name} instead\";\n            } else {\n                LOG(ERROR) << \"using deprecated syntax for specifying property '\" << c\n                           << \"', use ${name} instead\";\n            }\n            c += prop_name.size();\n        }\n\n        if (prop_name.empty()) {\n            return Error() << \"invalid zero-length property name in '\" << src << \"'\";\n        }\n\n        std::string prop_val = android::base::GetProperty(prop_name, \"\");\n        if (prop_val.empty()) {\n            if (def_val.empty()) {\n                return Error() << \"property '\" << prop_name << \"' doesn't exist while expanding '\"\n                               << src << \"'\";\n            }\n            prop_val = def_val;\n        }\n\n        dst.append(prop_val);\n        src_ptr = c;\n    }\n\n    return dst;\n}\n\n// Reads the content of device tree file under the platform's Android DT directory.\n// Returns true if the read is success, false otherwise.\nbool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {\n#if defined(__ANDROID__)\n    const std::string file_name = android::fs_mgr::GetAndroidDtDir() + sub_path;\n    if (android::base::ReadFileToString(file_name, dt_content)) {\n        if (!dt_content->empty()) {\n            dt_content->pop_back();  // Trims the trailing '\\0' out.\n            return true;\n        }\n    }\n#endif\n    return false;\n}\n\nbool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {\n    std::string dt_content;\n    if (read_android_dt_file(sub_path, &dt_content)) {\n        if (dt_content == expected_content) {\n            return true;\n        }\n    }\n    return false;\n}\n\nbool IsLegalPropertyName(const std::string& name) {\n    size_t namelen = name.size();\n\n    if (namelen < 1) return false;\n    if (name[0] == '.') return false;\n    if (name[namelen - 1] == '.') return false;\n\n    /* Only allow alphanumeric, plus '.', '-', '@', ':', or '_' */\n    /* Don't allow \"..\" to appear in a property name */\n    for (size_t i = 0; i < namelen; i++) {\n        if (name[i] == '.') {\n            // i=0 is guaranteed to never have a dot. See above.\n            if (name[i - 1] == '.') return false;\n            continue;\n        }\n        if (name[i] == '_' || name[i] == '-' || name[i] == '@' || name[i] == ':') continue;\n        if (name[i] >= 'a' && name[i] <= 'z') continue;\n        if (name[i] >= 'A' && name[i] <= 'Z') continue;\n        if (name[i] >= '0' && name[i] <= '9') continue;\n        return false;\n    }\n\n    return true;\n}\n\nResult<void> IsLegalPropertyValue(const std::string& name, const std::string& value) {\n    if (value.size() >= PROP_VALUE_MAX && !StartsWith(name, \"ro.\")) {\n        return Error() << \"Property value too long\";\n    }\n\n    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {\n        return Error() << \"Value is not a UTF8 encoded string\";\n    }\n\n    return {};\n}\n\n// Remove unnecessary slashes so that any later checks (e.g., the check for\n// whether the path is a top-level directory in /data) don't get confused.\nstd::string CleanDirPath(const std::string& path) {\n    std::string result;\n    result.reserve(path.length());\n    // Collapse duplicate slashes, e.g. //data//foo// => /data/foo/\n    for (char c : path) {\n        if (c != '/' || result.empty() || result.back() != '/') {\n            result += c;\n        }\n    }\n    // Remove trailing slash, e.g. /data/foo/ => /data/foo\n    if (result.length() > 1 && result.back() == '/') {\n        result.pop_back();\n    }\n    return result;\n}\n\nResult<MkdirOptions> ParseMkdir(const std::vector<std::string>& args) {\n    std::string path = CleanDirPath(args[1]);\n    const bool is_toplevel_data_dir =\n            StartsWith(path, kDataDirPrefix) &&\n            path.find_first_of('/', kDataDirPrefix.size()) == std::string::npos;\n    FscryptAction fscrypt_action =\n            is_toplevel_data_dir ? FscryptAction::kRequire : FscryptAction::kNone;\n    mode_t mode = 0755;\n    Result<uid_t> uid = -1;\n    Result<gid_t> gid = -1;\n    std::string ref_option = \"ref\";\n    bool set_option_encryption = false;\n    bool set_option_key = false;\n\n    for (size_t i = 2; i < args.size(); i++) {\n        switch (i) {\n            case 2:\n                mode = std::strtoul(args[2].c_str(), 0, 8);\n                break;\n            case 3:\n                uid = DecodeUid(args[3]);\n                if (!uid.ok()) {\n                    return Error()\n                           << \"Unable to decode UID for '\" << args[3] << \"': \" << uid.error();\n                }\n                break;\n            case 4:\n                gid = DecodeUid(args[4]);\n                if (!gid.ok()) {\n                    return Error()\n                           << \"Unable to decode GID for '\" << args[4] << \"': \" << gid.error();\n                }\n                break;\n            default:\n                auto parts = android::base::Split(args[i], \"=\");\n                if (parts.size() != 2) {\n                    return Error() << \"Can't parse option: '\" << args[i] << \"'\";\n                }\n                auto optname = parts[0];\n                auto optval = parts[1];\n                if (optname == \"encryption\") {\n                    if (set_option_encryption) {\n                        return Error() << \"Duplicated option: '\" << optname << \"'\";\n                    }\n                    if (optval == \"Require\") {\n                        fscrypt_action = FscryptAction::kRequire;\n                    } else if (optval == \"None\") {\n                        fscrypt_action = FscryptAction::kNone;\n                    } else if (optval == \"Attempt\") {\n                        fscrypt_action = FscryptAction::kAttempt;\n                    } else if (optval == \"DeleteIfNecessary\") {\n                        fscrypt_action = FscryptAction::kDeleteIfNecessary;\n                    } else {\n                        return Error() << \"Unknown encryption option: '\" << optval << \"'\";\n                    }\n                    set_option_encryption = true;\n                } else if (optname == \"key\") {\n                    if (set_option_key) {\n                        return Error() << \"Duplicated option: '\" << optname << \"'\";\n                    }\n                    if (optval == \"ref\" || optval == \"per_boot_ref\") {\n                        ref_option = optval;\n                    } else {\n                        return Error() << \"Unknown key option: '\" << optval << \"'\";\n                    }\n                    set_option_key = true;\n                } else {\n                    return Error() << \"Unknown option: '\" << args[i] << \"'\";\n                }\n        }\n    }\n    if (set_option_key && fscrypt_action == FscryptAction::kNone) {\n        return Error() << \"Key option set but encryption action is none\";\n    }\n    if (is_toplevel_data_dir) {\n        if (!set_option_encryption) {\n            LOG(WARNING) << \"Top-level directory needs encryption action, eg mkdir \" << path\n                         << \" <mode> <uid> <gid> encryption=Require\";\n        }\n        if (fscrypt_action == FscryptAction::kNone) {\n            LOG(INFO) << \"Not setting encryption policy on: \" << path;\n        }\n    }\n\n    return MkdirOptions{path, mode, *uid, *gid, fscrypt_action, ref_option};\n}\n\nResult<MountAllOptions> ParseMountAll(const std::vector<std::string>& args) {\n    bool compat_mode = false;\n    bool import_rc = false;\n    if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {\n        if (args.size() <= 1) {\n            return Error() << \"mount_all requires at least 1 argument\";\n        }\n        compat_mode = true;\n        import_rc = true;\n    }\n\n    std::size_t first_option_arg = args.size();\n    enum mount_mode mode = MOUNT_MODE_DEFAULT;\n\n    // If we are <= Q, then stop looking for non-fstab arguments at slot 2.\n    // Otherwise, stop looking at slot 1 (as the fstab path argument is optional >= R).\n    for (std::size_t na = args.size() - 1; na > (compat_mode ? 1 : 0); --na) {\n        if (args[na] == \"--early\") {\n            first_option_arg = na;\n            mode = MOUNT_MODE_EARLY;\n        } else if (args[na] == \"--late\") {\n            first_option_arg = na;\n            mode = MOUNT_MODE_LATE;\n            import_rc = false;\n        }\n    }\n\n    std::string fstab_path;\n    if (first_option_arg > 1) {\n        fstab_path = args[1];\n    } else if (compat_mode) {\n        return Error() << \"mount_all argument 1 must be the fstab path\";\n    }\n\n    std::vector<std::string> rc_paths;\n    for (std::size_t na = 2; na < first_option_arg; ++na) {\n        rc_paths.push_back(args[na]);\n    }\n\n    return MountAllOptions{rc_paths, fstab_path, mode, import_rc};\n}\n\nResult<std::pair<int, std::vector<std::string>>> ParseRestorecon(\n        const std::vector<std::string>& args) {\n    struct flag_type {\n        const char* name;\n        int value;\n    };\n    static const flag_type flags[] = {\n            {\"--recursive\", SELINUX_ANDROID_RESTORECON_RECURSE},\n            {\"--skip-ce\", SELINUX_ANDROID_RESTORECON_SKIPCE},\n            {\"--cross-filesystems\", SELINUX_ANDROID_RESTORECON_CROSS_FILESYSTEMS},\n            {\"--force\", SELINUX_ANDROID_RESTORECON_FORCE},\n            {\"--data-data\", SELINUX_ANDROID_RESTORECON_DATADATA},\n            {0, 0}};\n\n    int flag = 0;\n    std::vector<std::string> paths;\n\n    bool in_flags = true;\n    for (size_t i = 1; i < args.size(); ++i) {\n        if (android::base::StartsWith(args[i], \"--\")) {\n            if (!in_flags) {\n                return Error() << \"flags must precede paths\";\n            }\n            bool found = false;\n            for (size_t j = 0; flags[j].name; ++j) {\n                if (args[i] == flags[j].name) {\n                    flag |= flags[j].value;\n                    found = true;\n                    break;\n                }\n            }\n            if (!found) {\n                return Error() << \"bad flag \" << args[i];\n            }\n        } else {\n            in_flags = false;\n            paths.emplace_back(args[i]);\n        }\n    }\n    return std::pair(flag, paths);\n}\n\nResult<std::string> ParseSwaponAll(const std::vector<std::string>& args) {\n    if (args.size() <= 1) {\n        if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {\n            return Error() << \"swapon_all requires at least 1 argument\";\n        }\n        return {};\n    }\n    return args[1];\n}\n\nResult<std::string> ParseUmountAll(const std::vector<std::string>& args) {\n    if (args.size() <= 1) {\n        if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_Q__) {\n            return Error() << \"umount_all requires at least 1 argument\";\n        }\n        return {};\n    }\n    return args[1];\n}\n\nstatic void InitAborter(const char* abort_message) {\n    // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to\n    // simply abort instead of trying to reboot the system.\n    if (getpid() != 1) {\n        android::base::DefaultAborter(abort_message);\n        return;\n    }\n\n    InitFatalReboot(SIGABRT);\n}\n\n// The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial\n// console enabled and no initramfs, otherwise it does not provide any fds for stdin/stdout/stderr.\n// SetStdioToDevNull() is used to close these existing fds if they exist and replace them with\n// /dev/null regardless.\n//\n// In the case that these fds are provided by the kernel, the exec of second stage init causes an\n// SELinux denial as it does not have access to /dev/console.  In the case that they are not\n// provided, exec of any further process is potentially dangerous as the first fd's opened by that\n// process will take the stdin/stdout/stderr fileno's, which can cause issues if printf(), etc is\n// then used by that process.\n//\n// Lastly, simply calling SetStdioToDevNull() in first stage init is not enough, since first\n// stage init still runs in kernel context, future child processes will not have permissions to\n// access any fds that it opens, including the one opened below for /dev/null.  Therefore,\n// SetStdioToDevNull() must be called again in second stage init.\nvoid SetStdioToDevNull(char** argv) {\n    // Make stdin/stdout/stderr all point to /dev/null.\n    int fd = open(\"/dev/null\", O_RDWR);  // NOLINT(android-cloexec-open)\n    if (fd == -1) {\n        int saved_errno = errno;\n        android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);\n        errno = saved_errno;\n        PLOG(FATAL) << \"Couldn't open /dev/null\";\n    }\n    dup2(fd, STDIN_FILENO);\n    dup2(fd, STDOUT_FILENO);\n    dup2(fd, STDERR_FILENO);\n    if (fd > STDERR_FILENO) close(fd);\n}\n\nvoid InitKernelLogging(char** argv) {\n    SetFatalRebootTarget();\n    android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter);\n}\n\nbool IsRecoveryMode() {\n    return access(\"/system/bin/recovery\", F_OK) == 0;\n}\n\n// Check if default mount namespace is ready to be used with APEX modules\nstatic bool is_default_mount_namespace_ready = false;\n\nbool IsDefaultMountNamespaceReady() {\n    return is_default_mount_namespace_ready;\n}\n\nvoid SetDefaultMountNamespaceReady() {\n    is_default_mount_namespace_ready = true;\n}\n\nbool Has32BitAbi() {\n    static bool has = !android::base::GetProperty(\"ro.product.cpu.abilist32\", \"\").empty();\n    return has;\n}\n\nstd::string GetApexNameFromFileName(const std::string& path) {\n    static const std::string kApexDir = \"/apex/\";\n    if (StartsWith(path, kApexDir)) {\n        auto begin = kApexDir.size();\n        auto end = path.find('/', begin);\n        return path.substr(begin, end - begin);\n    }\n    return \"\";\n}\n\nstd::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,\n                                                int active_sdk) {\n    std::vector<std::string> filtered_configs;\n\n    std::map<std::string, std::pair<std::string, int>> script_map;\n    for (const auto& c : configs) {\n        int sdk = 0;\n        const std::vector<std::string> parts = android::base::Split(c, \".\");\n        std::string base;\n        if (parts.size() < 2) {\n            continue;\n        }\n\n        // parts[size()-1], aka the suffix, should be \"rc\" or \"#rc\"\n        // any other pattern gets discarded\n\n        const auto& suffix = parts[parts.size() - 1];\n        if (suffix == \"rc\") {\n            sdk = 0;\n        } else {\n            char trailer[9] = {0};\n            int r = sscanf(suffix.c_str(), \"%d%8s\", &sdk, trailer);\n            if (r != 2) {\n                continue;\n            }\n            if (strlen(trailer) > 2 || strcmp(trailer, \"rc\") != 0) {\n                continue;\n            }\n        }\n\n        if (sdk < 0 || sdk > active_sdk) {\n            continue;\n        }\n\n        base = parts[0];\n        for (unsigned int i = 1; i < parts.size() - 1; i++) {\n            base = base + \".\" + parts[i];\n        }\n\n        // is this preferred over what we already have\n        auto it = script_map.find(base);\n        if (it == script_map.end() || it->second.second < sdk) {\n            script_map[base] = std::make_pair(c, sdk);\n        }\n    }\n\n    for (const auto& m : script_map) {\n        filtered_configs.push_back(m.second.first);\n    }\n    return filtered_configs;\n}\n\n// Forks, executes the provided program in the child, and waits for the completion in the parent.\n// Child's stderr is captured and logged using LOG(ERROR).\nbool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]) {\n    // Create a pipe used for redirecting child process's output.\n    // * pipe_fds[0] is the FD the parent will use for reading.\n    // * pipe_fds[1] is the FD the child will use for writing.\n    int pipe_fds[2];\n    if (pipe(pipe_fds) == -1) {\n        PLOG(ERROR) << \"Failed to create pipe\";\n        return false;\n    }\n\n    pid_t child_pid = fork();\n    if (child_pid == -1) {\n        PLOG(ERROR) << \"Failed to fork for \" << filename;\n        return false;\n    }\n\n    if (child_pid == 0) {\n        // fork succeeded -- this is executing in the child process\n\n        // Close the pipe FD not used by this process\n        close(pipe_fds[0]);\n\n        // Redirect stderr to the pipe FD provided by the parent\n        if (TEMP_FAILURE_RETRY(dup2(pipe_fds[1], STDERR_FILENO)) == -1) {\n            PLOG(ERROR) << \"Failed to redirect stderr of \" << filename;\n            _exit(127);\n            return false;\n        }\n        close(pipe_fds[1]);\n\n        if (execv(filename, argv) == -1) {\n            PLOG(ERROR) << \"Failed to execve \" << filename;\n            return false;\n        }\n        // Unreachable because execve will have succeeded and replaced this code\n        // with child process's code.\n        _exit(127);\n        return false;\n    } else {\n        // fork succeeded -- this is executing in the original/parent process\n\n        // Close the pipe FD not used by this process\n        close(pipe_fds[1]);\n\n        // Log the redirected output of the child process.\n        // It's unfortunate that there's no standard way to obtain an istream for a file descriptor.\n        // As a result, we're buffering all output and logging it in one go at the end of the\n        // invocation, instead of logging it as it comes in.\n        const int child_out_fd = pipe_fds[0];\n        std::string child_output;\n        if (!android::base::ReadFdToString(child_out_fd, &child_output)) {\n            PLOG(ERROR) << \"Failed to capture full output of \" << filename;\n        }\n        close(child_out_fd);\n        if (!child_output.empty()) {\n            // Log captured output, line by line, because LOG expects to be invoked for each line\n            std::istringstream in(child_output);\n            std::string line;\n            while (std::getline(in, line)) {\n                LOG(ERROR) << filename << \": \" << line;\n            }\n        }\n\n        // Wait for child to terminate\n        int status;\n        if (TEMP_FAILURE_RETRY(waitpid(child_pid, &status, 0)) != child_pid) {\n            PLOG(ERROR) << \"Failed to wait for \" << filename;\n            return false;\n        }\n\n        if (WIFEXITED(status)) {\n            int status_code = WEXITSTATUS(status);\n            if (status_code == 0) {\n                return true;\n            } else {\n                LOG(ERROR) << filename << \" exited with status \" << status_code;\n            }\n        } else if (WIFSIGNALED(status)) {\n            LOG(ERROR) << filename << \" killed by signal \" << WTERMSIG(status);\n        } else if (WIFSTOPPED(status)) {\n            LOG(ERROR) << filename << \" stopped by signal \" << WSTOPSIG(status);\n        } else {\n            LOG(ERROR) << \"waitpid for \" << filename << \" returned unexpected status: \" << status;\n        }\n\n        return false;\n    }\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/util.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <chrono>\n#include <functional>\n#include <string>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n\n#include \"fscrypt_init_extensions.h\"\n#include \"result.h\"\n\nusing android::base::boot_clock;\n\nnamespace android {\nnamespace init {\n\nenum mount_mode {\n    MOUNT_MODE_DEFAULT = 0,\n    MOUNT_MODE_EARLY = 1,\n    MOUNT_MODE_LATE = 2,\n};\n\nstatic const char kColdBootDoneProp[] = \"ro.cold_boot_done\";\n\nextern void (*trigger_shutdown)(const std::string& command);\n\nResult<int> CreateSocket(const std::string& name, int type, bool passcred, bool should_listen,\n                         mode_t perm, uid_t uid, gid_t gid, const std::string& socketcon);\n\nResult<std::string> ReadFile(const std::string& path);\nResult<void> WriteFile(const std::string& path, const std::string& content);\n\nResult<uid_t> DecodeUid(const std::string& name);\n\nbool mkdir_recursive(const std::string& pathname, mode_t mode);\nint wait_for_file(const char* filename, std::chrono::nanoseconds timeout);\nbool make_dir(const std::string& path, mode_t mode);\nbool is_dir(const char* pathname);\nResult<std::string> ExpandProps(const std::string& src);\n\n// Reads or compares the content of device tree file under the platform's Android DT directory.\nbool read_android_dt_file(const std::string& sub_path, std::string* dt_content);\nbool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content);\n\nbool IsLegalPropertyName(const std::string& name);\nResult<void> IsLegalPropertyValue(const std::string& name, const std::string& value);\nstd::string CleanDirPath(const std::string& path);\n\nstruct MkdirOptions {\n    std::string target;\n    mode_t mode;\n    uid_t uid;\n    gid_t gid;\n    FscryptAction fscrypt_action;\n    std::string ref_option;\n};\n\nResult<MkdirOptions> ParseMkdir(const std::vector<std::string>& args);\n\nstruct MountAllOptions {\n    std::vector<std::string> rc_paths;\n    std::string fstab_path;\n    mount_mode mode;\n    bool import_rc;\n};\n\nResult<MountAllOptions> ParseMountAll(const std::vector<std::string>& args);\n\nResult<std::pair<int, std::vector<std::string>>> ParseRestorecon(\n        const std::vector<std::string>& args);\n\nResult<std::string> ParseSwaponAll(const std::vector<std::string>& args);\n\nResult<std::string> ParseUmountAll(const std::vector<std::string>& args);\n\nvoid SetStdioToDevNull(char** argv);\nvoid InitKernelLogging(char** argv);\nbool IsRecoveryMode();\n\nbool IsDefaultMountNamespaceReady();\nvoid SetDefaultMountNamespaceReady();\n\ninline constexpr bool IsMicrodroid() {\n#ifdef MICRODROID\n    return MICRODROID;\n#else\n    return false;\n#endif\n}\n\nbool Has32BitAbi();\n\nstd::string GetApexNameFromFileName(const std::string& path);\n\n// Compare all files */path.#rc and */path.rc with the same path prefix.\n// Keep the one with the highest # that doesn't exceed the system's SDK.\n// (.rc == .0rc for ranking purposes)\nstd::vector<std::string> FilterVersionedConfigs(const std::vector<std::string>& configs,\n                                                  int active_sdk);\n\n// Forks, executes the provided program in the child, and waits for the completion in the parent.\n// Child's stderr is captured and logged using LOG(ERROR).\nbool ForkExecveAndWaitForCompletion(const char* filename, char* const argv[]);\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "init/util_test.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"util.h\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <gtest/gtest.h>\n\nusing namespace std::literals::string_literals;\n\nnamespace android {\nnamespace init {\n\nTEST(util, ReadFile_ENOENT) {\n    errno = 0;\n    auto file_contents = ReadFile(\"/proc/does-not-exist\");\n    EXPECT_EQ(ENOENT, errno);\n    ASSERT_FALSE(file_contents.ok());\n    EXPECT_EQ(\"open() failed: No such file or directory\", file_contents.error().message());\n}\n\nTEST(util, ReadFileGroupWriteable) {\n    std::string s(\"hello\");\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    EXPECT_RESULT_OK(WriteFile(tf.path, s));\n    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);\n    auto file_contents = ReadFile(tf.path);\n    ASSERT_FALSE(file_contents.ok()) << strerror(errno);\n    EXPECT_EQ(\"Skipping insecure file\", file_contents.error().message());\n}\n\nTEST(util, ReadFileWorldWiteable) {\n    std::string s(\"hello\");\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    EXPECT_RESULT_OK(WriteFile(tf.path, s));\n    EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);\n    auto file_contents = ReadFile(tf.path);\n    ASSERT_FALSE(file_contents.ok());\n    EXPECT_EQ(\"Skipping insecure file\", file_contents.error().message());\n}\n\nTEST(util, ReadFileSymbolicLink) {\n    errno = 0;\n    // lrwxr-xr-x 1 root shell 6 2009-01-01 09:00 /system/bin/ps -> toybox\n    auto file_contents = ReadFile(\"/system/bin/ps\");\n    EXPECT_EQ(ELOOP, errno);\n    ASSERT_FALSE(file_contents.ok());\n    EXPECT_EQ(\"open() failed: Too many symbolic links encountered\",\n              file_contents.error().message());\n}\n\nTEST(util, ReadFileSuccess) {\n    auto file_contents = ReadFile(\"/proc/version\");\n    ASSERT_TRUE(file_contents.ok());\n    EXPECT_GT(file_contents->length(), 6U);\n    EXPECT_EQ('\\n', file_contents->at(file_contents->length() - 1));\n    (*file_contents)[5] = 0;\n    EXPECT_STREQ(\"Linux\", file_contents->c_str());\n}\n\nTEST(util, WriteFileBinary) {\n    std::string contents(\"abcd\");\n    contents.push_back('\\0');\n    contents.push_back('\\0');\n    contents.append(\"dcba\");\n    ASSERT_EQ(10u, contents.size());\n\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    EXPECT_RESULT_OK(WriteFile(tf.path, contents));\n\n    auto read_back_contents = ReadFile(tf.path);\n    ASSERT_RESULT_OK(read_back_contents);\n    EXPECT_EQ(contents, *read_back_contents);\n    EXPECT_EQ(10u, read_back_contents->size());\n}\n\nTEST(util, WriteFileNotExist) {\n    std::string s(\"hello\");\n    TemporaryDir test_dir;\n    std::string path = android::base::StringPrintf(\"%s/does-not-exist\", test_dir.path);\n    EXPECT_RESULT_OK(WriteFile(path, s));\n    auto file_contents = ReadFile(path);\n    ASSERT_RESULT_OK(file_contents);\n    EXPECT_EQ(s, *file_contents);\n    struct stat sb;\n    int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);\n    EXPECT_NE(-1, fd);\n    EXPECT_EQ(0, fstat(fd, &sb));\n    EXPECT_EQ(0, close(fd));\n    EXPECT_EQ((const unsigned int)(S_IRUSR | S_IWUSR), sb.st_mode & 0777);\n    EXPECT_EQ(0, unlink(path.c_str()));\n}\n\nTEST(util, WriteFileExist) {\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n    EXPECT_RESULT_OK(WriteFile(tf.path, \"1hello1\"));\n    auto file_contents = ReadFile(tf.path);\n    ASSERT_RESULT_OK(file_contents);\n    EXPECT_EQ(\"1hello1\", *file_contents);\n    EXPECT_RESULT_OK(WriteFile(tf.path, \"2ll2\"));\n    file_contents = ReadFile(tf.path);\n    ASSERT_RESULT_OK(file_contents);\n    EXPECT_EQ(\"2ll2\", *file_contents);\n}\n\nTEST(util, DecodeUid) {\n    auto decoded_uid = DecodeUid(\"root\");\n    EXPECT_TRUE(decoded_uid.ok());\n    EXPECT_EQ(0U, *decoded_uid);\n\n    decoded_uid = DecodeUid(\"toot\");\n    EXPECT_FALSE(decoded_uid.ok());\n    EXPECT_EQ(\"getpwnam failed: No such file or directory\", decoded_uid.error().message());\n\n    decoded_uid = DecodeUid(\"123\");\n    EXPECT_RESULT_OK(decoded_uid);\n    EXPECT_EQ(123U, *decoded_uid);\n}\n\nTEST(util, is_dir) {\n    TemporaryDir test_dir;\n    EXPECT_TRUE(is_dir(test_dir.path));\n    TemporaryFile tf;\n    EXPECT_FALSE(is_dir(tf.path));\n}\n\nTEST(util, mkdir_recursive) {\n    TemporaryDir test_dir;\n    std::string path = android::base::StringPrintf(\"%s/three/directories/deep\", test_dir.path);\n    EXPECT_TRUE(mkdir_recursive(path, 0755));\n    std::string path1 = android::base::StringPrintf(\"%s/three\", test_dir.path);\n    EXPECT_TRUE(is_dir(path1.c_str()));\n    std::string path2 = android::base::StringPrintf(\"%s/three/directories\", test_dir.path);\n    EXPECT_TRUE(is_dir(path1.c_str()));\n    std::string path3 = android::base::StringPrintf(\"%s/three/directories/deep\", test_dir.path);\n    EXPECT_TRUE(is_dir(path1.c_str()));\n}\n\nTEST(util, mkdir_recursive_extra_slashes) {\n    TemporaryDir test_dir;\n    std::string path = android::base::StringPrintf(\"%s/three////directories/deep//\", test_dir.path);\n    EXPECT_TRUE(mkdir_recursive(path, 0755));\n    std::string path1 = android::base::StringPrintf(\"%s/three\", test_dir.path);\n    EXPECT_TRUE(is_dir(path1.c_str()));\n    std::string path2 = android::base::StringPrintf(\"%s/three/directories\", test_dir.path);\n    EXPECT_TRUE(is_dir(path1.c_str()));\n    std::string path3 = android::base::StringPrintf(\"%s/three/directories/deep\", test_dir.path);\n    EXPECT_TRUE(is_dir(path1.c_str()));\n}\n\nTEST(util, CleanDirPath) {\n    EXPECT_EQ(\"\", CleanDirPath(\"\"));\n    EXPECT_EQ(\"/\", CleanDirPath(\"/\"));\n    EXPECT_EQ(\"/\", CleanDirPath(\"//\"));\n    EXPECT_EQ(\"/foo\", CleanDirPath(\"/foo\"));\n    EXPECT_EQ(\"/foo\", CleanDirPath(\"//foo\"));\n    EXPECT_EQ(\"/foo\", CleanDirPath(\"/foo/\"));\n    EXPECT_EQ(\"/foo/bar\", CleanDirPath(\"/foo/bar\"));\n    EXPECT_EQ(\"/foo/bar\", CleanDirPath(\"/foo/bar/\"));\n    EXPECT_EQ(\"/foo/bar\", CleanDirPath(\"/foo/bar////\"));\n    EXPECT_EQ(\"/foo/bar\", CleanDirPath(\"//foo//bar\"));\n}\n\n}  // namespace init\n}  // namespace android\n"
  },
  {
    "path": "janitors/OWNERS",
    "content": "# go/android-3p requires that all external projects have the \"janitors\" in\n# their OWNERS files.\n\n# These are also the \"owners\" for projects that don't really have owners\n# so much as volunteer janitors.\n\n# General maintenance.\nsadafebrahimi@google.com\n\n# C/C++.\nccross@google.com\ncferris@google.com\nenh@google.com\n\n# Java.\nmaco@google.com\n\n# Python.\ndwillemsen@google.com\n"
  },
  {
    "path": "libappfuse/Android.bp",
    "content": "// Copyright 2016 The Android Open Source Project\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libappfuse_defaults\",\n    local_include_dirs: [\"include\"],\n    shared_libs: [\"libbase\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\ncc_library_shared {\n    name: \"libappfuse\",\n    defaults: [\"libappfuse_defaults\"],\n    export_include_dirs: [\"include\"],\n    srcs: [\n        \"FuseAppLoop.cc\",\n        \"FuseBuffer.cc\",\n        \"FuseBridgeLoop.cc\",\n        \"EpollController.cc\",\n    ],\n}\n\ncc_test {\n    name: \"libappfuse_test\",\n    test_suites: [\"device-tests\"],\n    defaults: [\"libappfuse_defaults\"],\n    shared_libs: [\"libappfuse\"],\n    srcs: [\n        \"tests/FuseAppLoopTest.cc\",\n        \"tests/FuseBridgeLoopTest.cc\",\n        \"tests/FuseBufferTest.cc\",\n    ],\n}\n"
  },
  {
    "path": "libappfuse/AndroidTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2017 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Config for libappfuse_test\">\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"libappfuse_test->/data/local/tmp/libappfuse_test\" />\n    </target_preparer>\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        <option name=\"native-test-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"module-name\" value=\"libappfuse_test\" />\n    </test>\n</configuration>"
  },
  {
    "path": "libappfuse/EpollController.cc",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n\n#include \"libappfuse/EpollController.h\"\n\nnamespace android {\nnamespace fuse {\n\nEpollController::EpollController(base::unique_fd&& poll_fd) : poll_fd_(std::move(poll_fd)) {\n}\n\nbool EpollController::Wait(size_t event_count) {\n    events_.resize(event_count);\n    const int result = TEMP_FAILURE_RETRY(epoll_wait(poll_fd_, events_.data(), event_count, -1));\n    if (result == -1) {\n        PLOG(ERROR) << \"Failed to wait for epoll\";\n        return false;\n    }\n    events_.resize(result);\n    return true;\n}\n\nbool EpollController::AddFd(int fd, int events, void* data) {\n    return InvokeControl(EPOLL_CTL_ADD, fd, events, data);\n}\n\nbool EpollController::UpdateFd(int fd, int events, void* data) {\n    return InvokeControl(EPOLL_CTL_MOD, fd, events, data);\n}\n\nbool EpollController::RemoveFd(int fd) {\n    return InvokeControl(EPOLL_CTL_DEL, fd, /* events */ 0, nullptr);\n}\n\nconst std::vector<epoll_event>& EpollController::events() const {\n    return events_;\n}\n\nbool EpollController::InvokeControl(int op, int fd, int events, void* data) const {\n    epoll_event event;\n    memset(&event, 0, sizeof(event));\n    event.events = events;\n    event.data.ptr = data;\n    if (epoll_ctl(poll_fd_, op, fd, &event) == -1) {\n        PLOG(ERROR) << \"epoll_ctl() error op=\" << op;\n        return false;\n    }\n    return true;\n}\n}\n}\n"
  },
  {
    "path": "libappfuse/FuseAppLoop.cc",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#include \"libappfuse/FuseAppLoop.h\"\n\n#include <sys/eventfd.h>\n#include <sys/stat.h>\n\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n\n#include \"libappfuse/EpollController.h\"\n\nnamespace android {\nnamespace fuse {\n\nnamespace {\n\nbool HandleLookUp(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {\n    // AppFuse does not support directory structure now.\n    // It can lookup only files under the mount point.\n    if (buffer->request.header.nodeid != FUSE_ROOT_ID) {\n        LOG(ERROR) << \"Nodeid is not FUSE_ROOT_ID.\";\n        return loop->ReplySimple(buffer->request.header.unique, -ENOENT);\n    }\n\n    // Ensure that the filename ends with 0.\n    const size_t filename_length = buffer->request.header.len - sizeof(fuse_in_header);\n    if (buffer->request.lookup_name[filename_length - 1] != 0) {\n        LOG(ERROR) << \"File name does not end with 0.\";\n        return loop->ReplySimple(buffer->request.header.unique, -ENOENT);\n    }\n\n    const uint64_t inode = static_cast<uint64_t>(atol(buffer->request.lookup_name));\n    if (inode == 0 || inode == LONG_MAX) {\n        LOG(ERROR) << \"Invalid filename\";\n        return loop->ReplySimple(buffer->request.header.unique, -ENOENT);\n    }\n\n    callback->OnLookup(buffer->request.header.unique, inode);\n    return true;\n}\n\nbool HandleGetAttr(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {\n    if (buffer->request.header.nodeid == FUSE_ROOT_ID) {\n        return loop->ReplyGetAttr(buffer->request.header.unique, buffer->request.header.nodeid, 0,\n                                  S_IFDIR | 0777);\n    } else {\n        callback->OnGetAttr(buffer->request.header.unique, buffer->request.header.nodeid);\n        return true;\n    }\n}\n\nbool HandleRead(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {\n    if (buffer->request.read_in.size > kFuseMaxRead) {\n        return loop->ReplySimple(buffer->request.header.unique, -EINVAL);\n    }\n\n    callback->OnRead(buffer->request.header.unique, buffer->request.header.nodeid,\n                     buffer->request.read_in.offset, buffer->request.read_in.size);\n    return true;\n}\n\nbool HandleWrite(FuseAppLoop* loop, FuseBuffer* buffer, FuseAppLoopCallback* callback) {\n    if (buffer->request.write_in.size > kFuseMaxWrite) {\n        return loop->ReplySimple(buffer->request.header.unique, -EINVAL);\n    }\n\n    callback->OnWrite(buffer->request.header.unique, buffer->request.header.nodeid,\n                      buffer->request.write_in.offset, buffer->request.write_in.size,\n                      buffer->request.write_data);\n    return true;\n}\n\nbool HandleMessage(FuseAppLoop* loop, FuseBuffer* buffer, int fd, FuseAppLoopCallback* callback) {\n    if (!buffer->request.Read(fd)) {\n        return false;\n    }\n\n    const uint32_t opcode = buffer->request.header.opcode;\n    LOG(VERBOSE) << \"Read a fuse packet, opcode=\" << opcode;\n    switch (opcode) {\n        case FUSE_FORGET:\n            // Do not reply to FUSE_FORGET.\n            return true;\n\n        case FUSE_LOOKUP:\n            return HandleLookUp(loop, buffer, callback);\n\n        case FUSE_GETATTR:\n            return HandleGetAttr(loop, buffer, callback);\n\n        case FUSE_OPEN:\n            callback->OnOpen(buffer->request.header.unique, buffer->request.header.nodeid);\n            return true;\n\n        case FUSE_READ:\n            return HandleRead(loop, buffer, callback);\n\n        case FUSE_WRITE:\n            return HandleWrite(loop, buffer, callback);\n\n        case FUSE_RELEASE:\n            callback->OnRelease(buffer->request.header.unique, buffer->request.header.nodeid);\n            return true;\n\n        case FUSE_FSYNC:\n            callback->OnFsync(buffer->request.header.unique, buffer->request.header.nodeid);\n            return true;\n\n        default:\n            buffer->HandleNotImpl();\n            return buffer->response.Write(fd);\n    }\n}\n\n} // namespace\n\nFuseAppLoopCallback::~FuseAppLoopCallback() = default;\n\nFuseAppLoop::FuseAppLoop(base::unique_fd&& fd) : fd_(std::move(fd)) {}\n\nvoid FuseAppLoop::Break() {\n    const int64_t value = 1;\n    if (write(break_fd_, &value, sizeof(value)) == -1) {\n        PLOG(ERROR) << \"Failed to send a break event\";\n    }\n}\n\nbool FuseAppLoop::ReplySimple(uint64_t unique, int32_t result) {\n    if (result == -ENOSYS) {\n        // We should not return -ENOSYS because the kernel stops delivering FUSE\n        // command after receiving -ENOSYS as a result for the command.\n        result = -EBADF;\n    }\n    FuseSimpleResponse response;\n    response.Reset(0, result, unique);\n    return response.Write(fd_);\n}\n\nbool FuseAppLoop::ReplyLookup(uint64_t unique, uint64_t inode, int64_t size) {\n    FuseSimpleResponse response;\n    response.Reset(sizeof(fuse_entry_out), 0, unique);\n    response.entry_out.nodeid = inode;\n    response.entry_out.attr_valid = 10;\n    response.entry_out.entry_valid = 10;\n    response.entry_out.attr.ino = inode;\n    response.entry_out.attr.mode = S_IFREG | 0777;\n    response.entry_out.attr.size = size;\n    return response.Write(fd_);\n}\n\nbool FuseAppLoop::ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode) {\n    CHECK(mode == (S_IFREG | 0777) || mode == (S_IFDIR | 0777));\n    FuseSimpleResponse response;\n    response.Reset(sizeof(fuse_attr_out), 0, unique);\n    response.attr_out.attr_valid = 10;\n    response.attr_out.attr.ino = inode;\n    response.attr_out.attr.mode = mode;\n    response.attr_out.attr.size = size;\n    return response.Write(fd_);\n}\n\nbool FuseAppLoop::ReplyOpen(uint64_t unique, uint64_t fh) {\n    FuseSimpleResponse response;\n    response.Reset(sizeof(fuse_open_out), kFuseSuccess, unique);\n    response.open_out.fh = fh;\n    return response.Write(fd_);\n}\n\nbool FuseAppLoop::ReplyWrite(uint64_t unique, uint32_t size) {\n    CHECK(size <= kFuseMaxWrite);\n    FuseSimpleResponse response;\n    response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);\n    response.write_out.size = size;\n    return response.Write(fd_);\n}\n\nbool FuseAppLoop::ReplyRead(uint64_t unique, uint32_t size, const void* data) {\n    CHECK(size <= kFuseMaxRead);\n    FuseSimpleResponse response;\n    response.ResetHeader(size, kFuseSuccess, unique);\n    return response.WriteWithBody(fd_, sizeof(FuseResponse), data);\n}\n\nvoid FuseAppLoop::Start(FuseAppLoopCallback* callback) {\n    break_fd_.reset(eventfd(/* initval */ 0, EFD_CLOEXEC));\n    if (break_fd_.get() == -1) {\n        PLOG(ERROR) << \"Failed to open FD for break event\";\n        return;\n    }\n\n    base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));\n    if (epoll_fd.get() == -1) {\n        PLOG(ERROR) << \"Failed to open FD for epoll\";\n        return;\n    }\n\n    int last_event;\n    int break_event;\n\n    std::unique_ptr<EpollController> epoll_controller(new EpollController(std::move(epoll_fd)));\n    if (!epoll_controller->AddFd(fd_, EPOLLIN, &last_event)) {\n        return;\n    }\n    if (!epoll_controller->AddFd(break_fd_, EPOLLIN, &break_event)) {\n        return;\n    }\n\n    last_event = 0;\n    break_event = 0;\n\n    FuseBuffer buffer;\n    while (true) {\n        if (!epoll_controller->Wait(1)) {\n            break;\n        }\n        last_event = 0;\n        *reinterpret_cast<int*>(epoll_controller->events()[0].data.ptr) =\n            epoll_controller->events()[0].events;\n\n        if (break_event != 0 || (last_event & ~EPOLLIN) != 0) {\n            break;\n        }\n\n        if (!HandleMessage(this, &buffer, fd_, callback)) {\n            break;\n        }\n    }\n\n    LOG(VERBOSE) << \"FuseAppLoop exit\";\n}\n\n}  // namespace fuse\n}  // namespace android\n"
  },
  {
    "path": "libappfuse/FuseBridgeLoop.cc",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#include \"libappfuse/FuseBridgeLoop.h\"\n\n#include <sys/epoll.h>\n#include <sys/socket.h>\n\n#include <unordered_map>\n\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n\n#include \"libappfuse/EpollController.h\"\n\nnamespace android {\nnamespace fuse {\nnamespace {\n\nenum class FuseBridgeState { kWaitToReadEither, kWaitToReadProxy, kWaitToWriteProxy, kClosing };\n\nstruct FuseBridgeEntryEvent {\n    FuseBridgeEntry* entry;\n    int events;\n};\n\nvoid GetObservedEvents(FuseBridgeState state, int* device_events, int* proxy_events) {\n    switch (state) {\n        case FuseBridgeState::kWaitToReadEither:\n            *device_events = EPOLLIN;\n            *proxy_events = EPOLLIN;\n            return;\n        case FuseBridgeState::kWaitToReadProxy:\n            *device_events = 0;\n            *proxy_events = EPOLLIN;\n            return;\n        case FuseBridgeState::kWaitToWriteProxy:\n            *device_events = 0;\n            *proxy_events = EPOLLOUT;\n            return;\n        case FuseBridgeState::kClosing:\n            *device_events = 0;\n            *proxy_events = 0;\n            return;\n    }\n}\n\nvoid LogResponseError(const std::string& message, const FuseResponse& response) {\n    LOG(ERROR) << message << \": header.len=\" << response.header.len\n               << \" header.error=\" << response.header.error\n               << \" header.unique=\" << response.header.unique;\n}\n}\n\nclass FuseBridgeEntry {\n  public:\n    FuseBridgeEntry(int mount_id, base::unique_fd&& dev_fd, base::unique_fd&& proxy_fd)\n        : mount_id_(mount_id),\n          device_fd_(std::move(dev_fd)),\n          proxy_fd_(std::move(proxy_fd)),\n          state_(FuseBridgeState::kWaitToReadEither),\n          last_state_(FuseBridgeState::kWaitToReadEither),\n          last_device_events_({this, 0}),\n          last_proxy_events_({this, 0}),\n          open_count_(0) {}\n\n    // Transfer bytes depends on availability of FDs and the internal |state_|.\n    void Transfer(FuseBridgeLoopCallback* callback) {\n        constexpr int kUnexpectedEventMask = ~(EPOLLIN | EPOLLOUT);\n        const bool unexpected_event = (last_device_events_.events & kUnexpectedEventMask) ||\n                                      (last_proxy_events_.events & kUnexpectedEventMask);\n        const bool device_read_ready = last_device_events_.events & EPOLLIN;\n        const bool proxy_read_ready = last_proxy_events_.events & EPOLLIN;\n        const bool proxy_write_ready = last_proxy_events_.events & EPOLLOUT;\n\n        last_state_ = state_;\n        last_device_events_.events = 0;\n        last_proxy_events_.events = 0;\n\n        LOG(VERBOSE) << \"Transfer device_read_ready=\" << device_read_ready\n                     << \" proxy_read_ready=\" << proxy_read_ready\n                     << \" proxy_write_ready=\" << proxy_write_ready;\n\n        if (unexpected_event) {\n            LOG(ERROR) << \"Invalid epoll event is observed\";\n            state_ = FuseBridgeState::kClosing;\n            return;\n        }\n\n        switch (state_) {\n            case FuseBridgeState::kWaitToReadEither:\n                if (proxy_read_ready) {\n                    state_ = ReadFromProxy();\n                } else if (device_read_ready) {\n                    state_ = ReadFromDevice(callback);\n                }\n                return;\n\n            case FuseBridgeState::kWaitToReadProxy:\n                CHECK(proxy_read_ready);\n                state_ = ReadFromProxy();\n                return;\n\n            case FuseBridgeState::kWaitToWriteProxy:\n                CHECK(proxy_write_ready);\n                state_ = WriteToProxy();\n                return;\n\n            case FuseBridgeState::kClosing:\n                return;\n        }\n    }\n\n    bool IsClosing() const { return state_ == FuseBridgeState::kClosing; }\n\n    int mount_id() const { return mount_id_; }\n\n  private:\n    friend class BridgeEpollController;\n\n    FuseBridgeState ReadFromProxy() {\n        switch (buffer_.response.ReadOrAgain(proxy_fd_)) {\n            case ResultOrAgain::kSuccess:\n                break;\n            case ResultOrAgain::kFailure:\n                return FuseBridgeState::kClosing;\n            case ResultOrAgain::kAgain:\n                return FuseBridgeState::kWaitToReadProxy;\n        }\n\n        if (!buffer_.response.Write(device_fd_)) {\n            LogResponseError(\"Failed to write a reply from proxy to device\", buffer_.response);\n            return FuseBridgeState::kClosing;\n        }\n\n        auto it = opcode_map_.find(buffer_.response.header.unique);\n        if (it != opcode_map_.end()) {\n            switch (it->second) {\n                case FUSE_OPEN:\n                    if (buffer_.response.header.error == fuse::kFuseSuccess) {\n                        open_count_++;\n                    }\n                    break;\n\n                case FUSE_RELEASE:\n                    if (open_count_ > 0) {\n                        open_count_--;\n                    } else {\n                        LOG(WARNING) << \"Unexpected FUSE_RELEASE before opening a file.\";\n                        break;\n                    }\n                    if (open_count_ == 0) {\n                        return FuseBridgeState::kClosing;\n                    }\n                    break;\n            }\n            opcode_map_.erase(it);\n        }\n\n        return FuseBridgeState::kWaitToReadEither;\n    }\n\n    FuseBridgeState ReadFromDevice(FuseBridgeLoopCallback* callback) {\n        LOG(VERBOSE) << \"ReadFromDevice\";\n        if (!buffer_.request.Read(device_fd_)) {\n            return FuseBridgeState::kClosing;\n        }\n\n        const uint32_t opcode = buffer_.request.header.opcode;\n        const uint64_t unique = buffer_.request.header.unique;\n        LOG(VERBOSE) << \"Read a fuse packet, opcode=\" << opcode << \" unique=\" << unique;\n        if (unique == 0) {\n            return FuseBridgeState::kWaitToReadEither;\n        }\n        switch (opcode) {\n            case FUSE_FORGET:\n                // Do not reply to FUSE_FORGET.\n                return FuseBridgeState::kWaitToReadEither;\n\n            case FUSE_LOOKUP:\n            case FUSE_GETATTR:\n            case FUSE_OPEN:\n            case FUSE_READ:\n            case FUSE_WRITE:\n            case FUSE_RELEASE:\n            case FUSE_FSYNC:\n                if (opcode == FUSE_OPEN || opcode == FUSE_RELEASE) {\n                    opcode_map_.emplace(buffer_.request.header.unique, opcode);\n                }\n                return WriteToProxy();\n\n            case FUSE_INIT:\n                buffer_.HandleInit();\n                break;\n\n            default:\n                buffer_.HandleNotImpl();\n                break;\n        }\n\n        if (!buffer_.response.Write(device_fd_)) {\n            LogResponseError(\"Failed to write a response to device\", buffer_.response);\n            return FuseBridgeState::kClosing;\n        }\n\n        if (opcode == FUSE_INIT) {\n            callback->OnMount(mount_id_);\n        }\n\n        return FuseBridgeState::kWaitToReadEither;\n    }\n\n    FuseBridgeState WriteToProxy() {\n        switch (buffer_.request.WriteOrAgain(proxy_fd_)) {\n            case ResultOrAgain::kSuccess:\n                return FuseBridgeState::kWaitToReadEither;\n            case ResultOrAgain::kFailure:\n                LOG(ERROR) << \"Failed to write a request to proxy:\"\n                           << \" header.len=\" << buffer_.request.header.len\n                           << \" header.opcode=\" << buffer_.request.header.opcode\n                           << \" header.unique=\" << buffer_.request.header.unique\n                           << \" header.nodeid=\" << buffer_.request.header.nodeid;\n                return FuseBridgeState::kClosing;\n            case ResultOrAgain::kAgain:\n                return FuseBridgeState::kWaitToWriteProxy;\n        }\n    }\n\n    const int mount_id_;\n    base::unique_fd device_fd_;\n    base::unique_fd proxy_fd_;\n    FuseBuffer buffer_;\n    FuseBridgeState state_;\n    FuseBridgeState last_state_;\n    FuseBridgeEntryEvent last_device_events_;\n    FuseBridgeEntryEvent last_proxy_events_;\n\n    // Remember map between unique and opcode in fuse_in_header so that we can\n    // refer the opcode later.\n    std::unordered_map<uint64_t, uint32_t> opcode_map_;\n\n    int open_count_;\n\n    DISALLOW_COPY_AND_ASSIGN(FuseBridgeEntry);\n};\n\nclass BridgeEpollController : private EpollController {\n  public:\n    BridgeEpollController(base::unique_fd&& poll_fd) : EpollController(std::move(poll_fd)) {}\n\n    bool AddBridgePoll(FuseBridgeEntry* bridge) const {\n        return InvokeControl(EPOLL_CTL_ADD, bridge);\n    }\n\n    bool UpdateOrDeleteBridgePoll(FuseBridgeEntry* bridge) const {\n        return InvokeControl(\n            bridge->state_ != FuseBridgeState::kClosing ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, bridge);\n    }\n\n    bool Wait(size_t bridge_count, std::unordered_set<FuseBridgeEntry*>* entries_out) {\n        CHECK(entries_out);\n        const size_t event_count = std::max<size_t>(bridge_count * 2, 1);\n        if (!EpollController::Wait(event_count)) {\n            return false;\n        }\n        entries_out->clear();\n        for (const auto& event : events()) {\n            FuseBridgeEntryEvent* const entry_event =\n                reinterpret_cast<FuseBridgeEntryEvent*>(event.data.ptr);\n            entry_event->events = event.events;\n            entries_out->insert(entry_event->entry);\n        }\n        return true;\n    }\n\n  private:\n    bool InvokeControl(int op, FuseBridgeEntry* bridge) const {\n        LOG(VERBOSE) << \"InvokeControl op=\" << op << \" bridge=\" << bridge->mount_id_\n                     << \" state=\" << static_cast<int>(bridge->state_)\n                     << \" last_state=\" << static_cast<int>(bridge->last_state_);\n\n        int last_device_events;\n        int last_proxy_events;\n        int device_events;\n        int proxy_events;\n        GetObservedEvents(bridge->last_state_, &last_device_events, &last_proxy_events);\n        GetObservedEvents(bridge->state_, &device_events, &proxy_events);\n        bool result = true;\n        if (op != EPOLL_CTL_MOD || last_device_events != device_events) {\n            result &= EpollController::InvokeControl(op, bridge->device_fd_, device_events,\n                                                     &bridge->last_device_events_);\n        }\n        if (op != EPOLL_CTL_MOD || last_proxy_events != proxy_events) {\n            result &= EpollController::InvokeControl(op, bridge->proxy_fd_, proxy_events,\n                                                     &bridge->last_proxy_events_);\n        }\n        return result;\n    }\n};\n\nstd::recursive_mutex FuseBridgeLoop::mutex_;\n\nFuseBridgeLoop::FuseBridgeLoop() : opened_(true) {\n    base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));\n    if (epoll_fd.get() == -1) {\n        PLOG(ERROR) << \"Failed to open FD for epoll\";\n        opened_ = false;\n        return;\n    }\n    epoll_controller_.reset(new BridgeEpollController(std::move(epoll_fd)));\n}\n\nFuseBridgeLoop::~FuseBridgeLoop() { CHECK(bridges_.empty()); }\n\nbool FuseBridgeLoop::AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd) {\n    LOG(VERBOSE) << \"Adding bridge \" << mount_id;\n\n    std::unique_ptr<FuseBridgeEntry> bridge(\n        new FuseBridgeEntry(mount_id, std::move(dev_fd), std::move(proxy_fd)));\n    std::lock_guard<std::recursive_mutex> lock(mutex_);\n    if (!opened_) {\n        LOG(ERROR) << \"Tried to add a mount to a closed bridge\";\n        return false;\n    }\n    if (bridges_.count(mount_id)) {\n        LOG(ERROR) << \"Tried to add a mount point that has already been added\";\n        return false;\n    }\n    if (!epoll_controller_->AddBridgePoll(bridge.get())) {\n        return false;\n    }\n\n    bridges_.emplace(mount_id, std::move(bridge));\n    return true;\n}\n\nbool FuseBridgeLoop::ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,\n                                        FuseBridgeLoopCallback* callback) {\n    for (auto entry : entries) {\n        entry->Transfer(callback);\n        if (!epoll_controller_->UpdateOrDeleteBridgePoll(entry)) {\n            return false;\n        }\n        if (entry->IsClosing()) {\n            const int mount_id = entry->mount_id();\n            bridges_.erase(mount_id);\n            callback->OnClosed(mount_id);\n            if (bridges_.size() == 0) {\n                // All bridges are now closed.\n                return false;\n            }\n        }\n    }\n    return true;\n}\n\nvoid FuseBridgeLoop::Start(FuseBridgeLoopCallback* callback) {\n    LOG(DEBUG) << \"Start fuse bridge loop\";\n    std::unordered_set<FuseBridgeEntry*> entries;\n    while (true) {\n        const bool wait_result = epoll_controller_->Wait(bridges_.size(), &entries);\n        LOG(VERBOSE) << \"Receive epoll events\";\n        {\n            std::lock_guard<std::recursive_mutex> lock(mutex_);\n            if (!(wait_result && ProcessEventLocked(entries, callback))) {\n                for (auto it = bridges_.begin(); it != bridges_.end();) {\n                    callback->OnClosed(it->second->mount_id());\n                    it = bridges_.erase(it);\n                }\n                opened_ = false;\n                return;\n            }\n        }\n    }\n}\n\nvoid FuseBridgeLoop::Lock() {\n    mutex_.lock();\n}\n\nvoid FuseBridgeLoop::Unlock() {\n    mutex_.unlock();\n}\n\n}  // namespace fuse\n}  // namespace android\n"
  },
  {
    "path": "libappfuse/FuseBuffer.cc",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#include \"libappfuse/FuseBuffer.h\"\n\n#include <inttypes.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <type_traits>\n\n#include <sys/socket.h>\n#include <sys/uio.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n\nnamespace android {\nnamespace fuse {\nnamespace {\n\nconstexpr useconds_t kRetrySleepForWriting = 1000;  // 1 ms\n// This makes the total wait time to allocate a buffer 5 seconds\nconst int kNumberOfRetriesForWriting = 5000;\n\ntemplate <typename T>\nbool CheckHeaderLength(const FuseMessage<T>* self, const char* name, size_t max_size) {\n    const auto& header = static_cast<const T*>(self)->header;\n    if (header.len >= sizeof(header) && header.len <= max_size) {\n        return true;\n    } else {\n        LOG(ERROR) << \"Invalid header length is found in \" << name << \": \" << header.len;\n        return false;\n    }\n}\n\ntemplate <typename T>\nResultOrAgain ReadInternal(FuseMessage<T>* self, int fd, int sockflag) {\n    char* const buf = reinterpret_cast<char*>(self);\n    const ssize_t result = sockflag ? TEMP_FAILURE_RETRY(recv(fd, buf, sizeof(T), sockflag))\n                                    : TEMP_FAILURE_RETRY(read(fd, buf, sizeof(T)));\n\n    switch (result) {\n        case 0:\n            // Expected EOF.\n            return ResultOrAgain::kFailure;\n        case -1:\n            if (errno == EAGAIN) {\n                return ResultOrAgain::kAgain;\n            }\n            PLOG(ERROR) << \"Failed to read a FUSE message\";\n            return ResultOrAgain::kFailure;\n    }\n\n    const auto& header = static_cast<const T*>(self)->header;\n    if (result < static_cast<ssize_t>(sizeof(header))) {\n        LOG(ERROR) << \"Read bytes \" << result << \" are shorter than header size \" << sizeof(header);\n        return ResultOrAgain::kFailure;\n    }\n\n    if (!CheckHeaderLength<T>(self, \"Read\", sizeof(T))) {\n        return ResultOrAgain::kFailure;\n    }\n\n    if (static_cast<uint32_t>(result) != header.len) {\n        LOG(ERROR) << \"Read bytes \" << result << \" are different from header.len \" << header.len;\n        return ResultOrAgain::kFailure;\n    }\n\n    return ResultOrAgain::kSuccess;\n}\n\ntemplate <typename T>\nResultOrAgain WriteInternal(const FuseMessage<T>* self, int fd, int sockflag, const void* data,\n                            size_t max_size) {\n    if (!CheckHeaderLength<T>(self, \"Write\", max_size)) {\n        return ResultOrAgain::kFailure;\n    }\n\n    const char* const buf = reinterpret_cast<const char*>(self);\n    const auto& header = static_cast<const T*>(self)->header;\n    int retry = kNumberOfRetriesForWriting;\n\n    while (true) {\n        int result;\n        if (sockflag) {\n            CHECK(data == nullptr);\n            result = TEMP_FAILURE_RETRY(send(fd, buf, header.len, sockflag));\n        } else if (data) {\n            const struct iovec vec[] = {{const_cast<char*>(buf), sizeof(header)},\n                                        {const_cast<void*>(data), header.len - sizeof(header)}};\n            result = TEMP_FAILURE_RETRY(writev(fd, vec, arraysize(vec)));\n        } else {\n            result = TEMP_FAILURE_RETRY(write(fd, buf, header.len));\n        }\n        if (result == -1) {\n            switch (errno) {\n                case ENOBUFS:\n                    // When returning ENOBUFS, epoll still reports the FD is writable. Just usleep\n                    // and retry again.\n                    if (retry > 0) {\n                        usleep(kRetrySleepForWriting);\n                        retry--;\n                        continue;\n                    } else {\n                        LOG(ERROR) << \"Failed to write a FUSE message: ENOBUFS retries are failed\";\n                        return ResultOrAgain::kFailure;\n                    }\n                case EAGAIN:\n                    return ResultOrAgain::kAgain;\n                default:\n                    PLOG(ERROR) << \"Failed to write a FUSE message: \"\n                                << \"fd=\" << fd << \" \"\n                                << \"sockflag=\" << sockflag << \" \"\n                                << \"data=\" << data;\n                    return ResultOrAgain::kFailure;\n            }\n        }\n\n        if (static_cast<unsigned int>(result) != header.len) {\n            LOG(ERROR) << \"Written bytes \" << result << \" is different from length in header \"\n                       << header.len;\n            return ResultOrAgain::kFailure;\n        }\n        return ResultOrAgain::kSuccess;\n    }\n}\n}\n\nstatic_assert(std::is_standard_layout<FuseBuffer>::value,\n              \"FuseBuffer must be standard layout union.\");\n\nbool SetupMessageSockets(base::unique_fd (*result)[2]) {\n    base::unique_fd fds[2];\n    {\n        int raw_fds[2];\n        if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, raw_fds) == -1) {\n            PLOG(ERROR) << \"Failed to create sockets for proxy\";\n            return false;\n        }\n        fds[0].reset(raw_fds[0]);\n        fds[1].reset(raw_fds[1]);\n    }\n\n    constexpr int kMaxMessageSize = sizeof(FuseBuffer);\n    if (setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0 ||\n        setsockopt(fds[1], SOL_SOCKET, SO_SNDBUF, &kMaxMessageSize, sizeof(int)) != 0) {\n        PLOG(ERROR) << \"Failed to update buffer size for socket\";\n        return false;\n    }\n\n    (*result)[0] = std::move(fds[0]);\n    (*result)[1] = std::move(fds[1]);\n    return true;\n}\n\ntemplate <typename T>\nbool FuseMessage<T>::Read(int fd) {\n    return ReadInternal(this, fd, 0) == ResultOrAgain::kSuccess;\n}\n\ntemplate <typename T>\nResultOrAgain FuseMessage<T>::ReadOrAgain(int fd) {\n    return ReadInternal(this, fd, MSG_DONTWAIT);\n}\n\ntemplate <typename T>\nbool FuseMessage<T>::Write(int fd) const {\n    return WriteInternal(this, fd, 0, nullptr, sizeof(T)) == ResultOrAgain::kSuccess;\n}\n\ntemplate <typename T>\nbool FuseMessage<T>::WriteWithBody(int fd, size_t max_size, const void* data) const {\n    CHECK(data != nullptr);\n    return WriteInternal(this, fd, 0, data, max_size) == ResultOrAgain::kSuccess;\n}\n\ntemplate <typename T>\nResultOrAgain FuseMessage<T>::WriteOrAgain(int fd) const {\n    return WriteInternal(this, fd, MSG_DONTWAIT, nullptr, sizeof(T));\n}\n\nvoid FuseRequest::Reset(\n    uint32_t data_length, uint32_t opcode, uint64_t unique) {\n  memset(this, 0, sizeof(fuse_in_header) + data_length);\n  header.len = sizeof(fuse_in_header) + data_length;\n  header.opcode = opcode;\n  header.unique = unique;\n}\n\ntemplate <size_t N>\nvoid FuseResponseBase<N>::ResetHeader(uint32_t data_length, int32_t error, uint64_t unique) {\n    CHECK_LE(error, 0) << \"error should be zero or negative.\";\n    header.len = sizeof(fuse_out_header) + data_length;\n    header.error = error;\n    header.unique = unique;\n}\n\ntemplate <size_t N>\nvoid FuseResponseBase<N>::Reset(uint32_t data_length, int32_t error, uint64_t unique) {\n    memset(this, 0, sizeof(fuse_out_header) + data_length);\n    ResetHeader(data_length, error, unique);\n}\n\nvoid FuseBuffer::HandleInit() {\n  const fuse_init_in* const in = &request.init_in;\n\n  // Before writing |out|, we need to copy data from |in|.\n  const uint64_t unique = request.header.unique;\n  const uint32_t minor = in->minor;\n  const uint32_t max_readahead = in->max_readahead;\n\n  // Kernel 2.6.16 is the first stable kernel with struct fuse_init_out\n  // defined (fuse version 7.6). The structure is the same from 7.6 through\n  // 7.22. Beginning with 7.23, the structure increased in size and added\n  // new parameters.\n  if (in->major != FUSE_KERNEL_VERSION || in->minor < 6) {\n    LOG(ERROR) << \"Fuse kernel version mismatch: Kernel version \" << in->major\n        << \".\" << in->minor << \" Expected at least \" << FUSE_KERNEL_VERSION\n        << \".6\";\n    response.Reset(0, -EPERM, unique);\n    return;\n  }\n\n  // We limit ourselves to minor=15 because we don't handle BATCH_FORGET yet.\n  // Thus we need to use FUSE_COMPAT_22_INIT_OUT_SIZE.\n#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)\n  // FUSE_KERNEL_VERSION >= 23.\n  const size_t response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;\n#else\n  const size_t response_size = sizeof(fuse_init_out);\n#endif\n\n  response.Reset(response_size, kFuseSuccess, unique);\n  fuse_init_out* const out = &response.init_out;\n  out->major = FUSE_KERNEL_VERSION;\n  out->minor = std::min(minor, 15u);\n  out->max_readahead = max_readahead;\n  out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;\n  out->max_background = 32;\n  out->congestion_threshold = 32;\n  out->max_write = kFuseMaxWrite;\n}\n\nvoid FuseBuffer::HandleNotImpl() {\n  LOG(VERBOSE) << \"NOTIMPL op=\" << request.header.opcode << \" uniq=\"\n      << request.header.unique << \" nid=\" << request.header.nodeid;\n  // Add volatile as a workaround for compiler issue which removes the temporary\n  // variable.\n  const volatile uint64_t unique = request.header.unique;\n  response.Reset(0, -ENOSYS, unique);\n}\n\ntemplate class FuseMessage<FuseRequest>;\ntemplate class FuseMessage<FuseResponse>;\ntemplate class FuseMessage<FuseSimpleResponse>;\ntemplate struct FuseResponseBase<0u>;\ntemplate struct FuseResponseBase<kFuseMaxRead>;\n\n}  // namespace fuse\n}  // namespace android\n"
  },
  {
    "path": "libappfuse/OWNERS",
    "content": "hirono@google.com\n"
  },
  {
    "path": "libappfuse/include/libappfuse/EpollController.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_\n#define ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_\n\n#include <sys/epoll.h>\n\n#include <vector>\n\n#include <android-base/macros.h>\n#include <android-base/unique_fd.h>\n\nnamespace android {\nnamespace fuse {\n\nclass EpollController {\n  public:\n    explicit EpollController(base::unique_fd&& poll_fd);\n    bool Wait(size_t event_count);\n    bool AddFd(int fd, int events, void* data);\n    bool UpdateFd(int fd, int events, void* data);\n    bool RemoveFd(int fd);\n\n    const std::vector<epoll_event>& events() const;\n\n  protected:\n    bool InvokeControl(int op, int fd, int events, void* data) const;\n\n  private:\n    base::unique_fd poll_fd_;\n    std::vector<epoll_event> events_;\n\n    DISALLOW_COPY_AND_ASSIGN(EpollController);\n};\n}\n}\n\n#endif  // ANDROID_LIBAPPFUSE_EPOLLCONTROLLER_H_\n"
  },
  {
    "path": "libappfuse/include/libappfuse/FuseAppLoop.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_\n#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_\n\n#include <memory>\n#include <mutex>\n\n#include <android-base/unique_fd.h>\n\n#include \"libappfuse/FuseBuffer.h\"\n\nnamespace android {\nnamespace fuse {\n\nclass EpollController;\n\nclass FuseAppLoopCallback {\n public:\n   virtual void OnLookup(uint64_t unique, uint64_t inode) = 0;\n   virtual void OnGetAttr(uint64_t unique, uint64_t inode) = 0;\n   virtual void OnFsync(uint64_t unique, uint64_t inode) = 0;\n   virtual void OnWrite(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size,\n                        const void* data) = 0;\n   virtual void OnRead(uint64_t unique, uint64_t inode, uint64_t offset, uint32_t size) = 0;\n   virtual void OnOpen(uint64_t unique, uint64_t inode) = 0;\n   virtual void OnRelease(uint64_t unique, uint64_t inode) = 0;\n   virtual ~FuseAppLoopCallback();\n};\n\nclass FuseAppLoop final {\n  public:\n    FuseAppLoop(base::unique_fd&& fd);\n\n    void Start(FuseAppLoopCallback* callback);\n    void Break();\n\n    bool ReplySimple(uint64_t unique, int32_t result);\n    bool ReplyLookup(uint64_t unique, uint64_t inode, int64_t size);\n    bool ReplyGetAttr(uint64_t unique, uint64_t inode, int64_t size, int mode);\n    bool ReplyOpen(uint64_t unique, uint64_t fh);\n    bool ReplyWrite(uint64_t unique, uint32_t size);\n    bool ReplyRead(uint64_t unique, uint32_t size, const void* data);\n\n  private:\n    base::unique_fd fd_;\n    base::unique_fd break_fd_;\n\n    // Lock for multi-threading.\n    std::mutex mutex_;\n};\n\nbool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);\n\n}  // namespace fuse\n}  // namespace android\n\n#endif  // ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_\n"
  },
  {
    "path": "libappfuse/include/libappfuse/FuseBridgeLoop.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_\n#define ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_\n\n#include <map>\n#include <mutex>\n#include <queue>\n#include <unordered_set>\n\n#include <android-base/macros.h>\n\n#include \"libappfuse/FuseBuffer.h\"\n\nnamespace android {\nnamespace fuse {\n\nclass FuseBridgeLoopCallback {\n public:\n   virtual void OnMount(int mount_id) = 0;\n   virtual void OnClosed(int mount_id) = 0;\n   virtual ~FuseBridgeLoopCallback() = default;\n};\n\nclass FuseBridgeEntry;\nclass BridgeEpollController;\n\nclass FuseBridgeLoop final {\n  public:\n    FuseBridgeLoop();\n    ~FuseBridgeLoop();\n\n    void Start(FuseBridgeLoopCallback* callback);\n\n    // Add bridge to the loop. It's OK to invoke the method from a different\n    // thread from one which invokes |Start|.\n    bool AddBridge(int mount_id, base::unique_fd dev_fd, base::unique_fd proxy_fd);\n\n    static void Lock();\n\n    static void Unlock();\n\n  private:\n    bool ProcessEventLocked(const std::unordered_set<FuseBridgeEntry*>& entries,\n                            FuseBridgeLoopCallback* callback);\n\n    std::unique_ptr<BridgeEpollController> epoll_controller_;\n\n    // Map between |mount_id| and bridge entry.\n    std::map<int, std::unique_ptr<FuseBridgeEntry>> bridges_;\n\n    // Lock for multi-threading.\n    static std::recursive_mutex mutex_;\n\n    bool opened_;\n\n    DISALLOW_COPY_AND_ASSIGN(FuseBridgeLoop);\n};\n\n}  // namespace fuse\n}  // namespace android\n\n#endif  // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_\n"
  },
  {
    "path": "libappfuse/include/libappfuse/FuseBuffer.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_LIBAPPFUSE_FUSEBUFFER_H_\n#define ANDROID_LIBAPPFUSE_FUSEBUFFER_H_\n\n#include <android-base/unique_fd.h>\n#include <linux/fuse.h>\n\nnamespace android {\nnamespace fuse {\n\n// The numbers came from sdcard.c.\n// Maximum number of bytes to write/read in one request/one reply.\nconstexpr size_t kFuseMaxWrite = 128 * 1024;\nconstexpr size_t kFuseMaxRead = 128 * 1024;\nconstexpr int32_t kFuseSuccess = 0;\n\n// Setup sockets to transfer FuseMessage.\nbool SetupMessageSockets(base::unique_fd (*sockets)[2]);\n\nenum class ResultOrAgain {\n    kSuccess,\n    kFailure,\n    kAgain,\n};\n\ntemplate<typename T>\nclass FuseMessage {\n public:\n  bool Read(int fd);\n  bool Write(int fd) const;\n  bool WriteWithBody(int fd, size_t max_size, const void* data) const;\n  ResultOrAgain ReadOrAgain(int fd);\n  ResultOrAgain WriteOrAgain(int fd) const;\n};\n\n// FuseRequest represents file operation requests from /dev/fuse. It starts\n// from fuse_in_header. The body layout depends on the operation code.\nstruct FuseRequest : public FuseMessage<FuseRequest> {\n  fuse_in_header header;\n  union {\n    // for FUSE_WRITE\n    struct {\n      fuse_write_in write_in;\n      char write_data[kFuseMaxWrite];\n    };\n    // for FUSE_OPEN\n    fuse_open_in open_in;\n    // for FUSE_INIT\n    fuse_init_in init_in;\n    // for FUSE_READ\n    fuse_read_in read_in;\n    // for FUSE_LOOKUP\n    char lookup_name[kFuseMaxWrite];\n  };\n  void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);\n};\n\n// FuseResponse represents file operation responses to /dev/fuse. It starts\n// from fuse_out_header. The body layout depends on the operation code.\ntemplate <size_t N>\nstruct FuseResponseBase : public FuseMessage<FuseResponseBase<N>> {\n    fuse_out_header header;\n    union {\n        // for FUSE_INIT\n        fuse_init_out init_out;\n        // for FUSE_LOOKUP\n        fuse_entry_out entry_out;\n        // for FUSE_GETATTR\n        fuse_attr_out attr_out;\n        // for FUSE_OPEN\n        fuse_open_out open_out;\n        // for FUSE_READ\n        char read_data[N];\n        // for FUSE_WRITE\n        fuse_write_out write_out;\n    };\n    void Reset(uint32_t data_length, int32_t error, uint64_t unique);\n    void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);\n};\n\nusing FuseResponse = FuseResponseBase<kFuseMaxRead>;\nusing FuseSimpleResponse = FuseResponseBase<0u>;\n\n// To reduce memory usage, FuseBuffer shares the memory region for request and\n// response.\nunion FuseBuffer final {\n  FuseRequest request;\n  FuseResponse response;\n\n  void HandleInit();\n  void HandleNotImpl();\n};\n\n}  // namespace fuse\n}  // namespace android\n\n#endif  // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_\n"
  },
  {
    "path": "libappfuse/tests/FuseAppLoopTest.cc",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#include \"libappfuse/FuseAppLoop.h\"\n\n#include <sys/socket.h>\n\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n#include <thread>\n\n#include \"libappfuse/EpollController.h\"\n#include \"libappfuse/FuseBridgeLoop.h\"\n\nnamespace android {\nnamespace fuse {\nnamespace {\n\nconstexpr unsigned int kTestFileSize = 1024;\n\nstruct CallbackRequest {\n  uint32_t code;\n  uint64_t inode;\n};\n\nclass Callback : public FuseAppLoopCallback {\n public:\n  std::vector<CallbackRequest> requests;\n  FuseAppLoop* loop;\n\n  void OnGetAttr(uint64_t seq, uint64_t inode) override {\n      EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));\n      EXPECT_TRUE(loop->ReplyGetAttr(seq, inode, kTestFileSize, S_IFREG | 0777));\n  }\n\n  void OnLookup(uint64_t unique, uint64_t inode) override {\n      EXPECT_NE(FUSE_ROOT_ID, static_cast<int>(inode));\n      EXPECT_TRUE(loop->ReplyLookup(unique, inode, kTestFileSize));\n  }\n\n  void OnFsync(uint64_t seq, uint64_t inode) override {\n      requests.push_back({.code = FUSE_FSYNC, .inode = inode});\n      loop->ReplySimple(seq, 0);\n  }\n\n  void OnWrite(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,\n               uint32_t size ATTRIBUTE_UNUSED, const void* data ATTRIBUTE_UNUSED) override {\n      requests.push_back({.code = FUSE_WRITE, .inode = inode});\n      loop->ReplyWrite(seq, 0);\n  }\n\n  void OnRead(uint64_t seq, uint64_t inode, uint64_t offset ATTRIBUTE_UNUSED,\n              uint32_t size ATTRIBUTE_UNUSED) override {\n      requests.push_back({.code = FUSE_READ, .inode = inode});\n      loop->ReplySimple(seq, 0);\n  }\n\n  void OnOpen(uint64_t seq, uint64_t inode) override {\n      requests.push_back({.code = FUSE_OPEN, .inode = inode});\n      loop->ReplyOpen(seq, inode);\n  }\n\n  void OnRelease(uint64_t seq, uint64_t inode) override {\n      requests.push_back({.code = FUSE_RELEASE, .inode = inode});\n      loop->ReplySimple(seq, 0);\n  }\n};\n\nclass FuseAppLoopTest : public ::testing::Test {\n protected:\n   std::thread thread_;\n   base::unique_fd sockets_[2];\n   Callback callback_;\n   FuseRequest request_;\n   FuseResponse response_;\n   std::unique_ptr<FuseAppLoop> loop_;\n\n   void SetUp() override {\n       base::SetMinimumLogSeverity(base::VERBOSE);\n       ASSERT_TRUE(SetupMessageSockets(&sockets_));\n       loop_.reset(new FuseAppLoop(std::move(sockets_[1])));\n       callback_.loop = loop_.get();\n       thread_ = std::thread([this] { loop_->Start(&callback_); });\n  }\n\n  void CheckCallback(\n      size_t data_size, uint32_t code, size_t expected_out_size) {\n    request_.Reset(data_size, code, 1);\n    request_.header.nodeid = 10;\n\n    ASSERT_TRUE(request_.Write(sockets_[0]));\n    ASSERT_TRUE(response_.Read(sockets_[0]));\n\n    Close();\n\n    EXPECT_EQ(kFuseSuccess, response_.header.error);\n    EXPECT_EQ(sizeof(fuse_out_header) + expected_out_size,\n              response_.header.len);\n    EXPECT_EQ(1u, response_.header.unique);\n\n    ASSERT_EQ(1u, callback_.requests.size());\n    EXPECT_EQ(code, callback_.requests[0].code);\n    EXPECT_EQ(10u, callback_.requests[0].inode);\n  }\n\n  void Close() {\n    sockets_[0].reset();\n    sockets_[1].reset();\n    if (thread_.joinable()) {\n      thread_.join();\n    }\n  }\n\n  void TearDown() override {\n    Close();\n  }\n};\n\n}  // namespace\n\nTEST_F(FuseAppLoopTest, LookUp) {\n  request_.Reset(3u, FUSE_LOOKUP, 1);\n  request_.header.nodeid = FUSE_ROOT_ID;\n  strcpy(request_.lookup_name, \"10\");\n\n  ASSERT_TRUE(request_.Write(sockets_[0].get()));\n  ASSERT_TRUE(response_.Read(sockets_[0].get()));\n\n  EXPECT_EQ(kFuseSuccess, response_.header.error);\n  EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_entry_out),\n            response_.header.len);\n  EXPECT_EQ(1u, response_.header.unique);\n\n  EXPECT_EQ(10u, response_.entry_out.nodeid);\n  EXPECT_EQ(0u, response_.entry_out.generation);\n  EXPECT_EQ(10u, response_.entry_out.entry_valid);\n  EXPECT_EQ(10u, response_.entry_out.attr_valid);\n  EXPECT_EQ(0u, response_.entry_out.entry_valid_nsec);\n  EXPECT_EQ(0u, response_.entry_out.attr_valid_nsec);\n\n  EXPECT_EQ(10u, response_.entry_out.attr.ino);\n  EXPECT_EQ(kTestFileSize, response_.entry_out.attr.size);\n  EXPECT_EQ(0u, response_.entry_out.attr.blocks);\n  EXPECT_EQ(0u, response_.entry_out.attr.atime);\n  EXPECT_EQ(0u, response_.entry_out.attr.mtime);\n  EXPECT_EQ(0u, response_.entry_out.attr.ctime);\n  EXPECT_EQ(0u, response_.entry_out.attr.atimensec);\n  EXPECT_EQ(0u, response_.entry_out.attr.mtimensec);\n  EXPECT_EQ(0u, response_.entry_out.attr.ctimensec);\n  EXPECT_EQ(S_IFREG | 0777u, response_.entry_out.attr.mode);\n  EXPECT_EQ(0u, response_.entry_out.attr.nlink);\n  EXPECT_EQ(0u, response_.entry_out.attr.uid);\n  EXPECT_EQ(0u, response_.entry_out.attr.gid);\n  EXPECT_EQ(0u, response_.entry_out.attr.rdev);\n  EXPECT_EQ(0u, response_.entry_out.attr.blksize);\n  EXPECT_EQ(0u, response_.entry_out.attr.flags);\n}\n\nTEST_F(FuseAppLoopTest, LookUp_InvalidName) {\n  request_.Reset(3u, FUSE_LOOKUP, 1);\n  request_.header.nodeid = FUSE_ROOT_ID;\n  strcpy(request_.lookup_name, \"aa\");\n\n  ASSERT_TRUE(request_.Write(sockets_[0].get()));\n  ASSERT_TRUE(response_.Read(sockets_[0].get()));\n\n  EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);\n  EXPECT_EQ(-ENOENT, response_.header.error);\n  EXPECT_EQ(1u, response_.header.unique);\n}\n\nTEST_F(FuseAppLoopTest, LookUp_TooLargeName) {\n  request_.Reset(21u, FUSE_LOOKUP, 1);\n  request_.header.nodeid = FUSE_ROOT_ID;\n  strcpy(request_.lookup_name, \"18446744073709551616\");\n\n  ASSERT_TRUE(request_.Write(sockets_[0].get()));\n  ASSERT_TRUE(response_.Read(sockets_[0].get()));\n\n  EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);\n  EXPECT_EQ(-ENOENT, response_.header.error);\n  EXPECT_EQ(1u, response_.header.unique);\n}\n\nTEST_F(FuseAppLoopTest, GetAttr) {\n  request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);\n  request_.header.nodeid = 10;\n\n  ASSERT_TRUE(request_.Write(sockets_[0].get()));\n  ASSERT_TRUE(response_.Read(sockets_[0].get()));\n\n  EXPECT_EQ(kFuseSuccess, response_.header.error);\n  EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),\n            response_.header.len);\n  EXPECT_EQ(1u, response_.header.unique);\n\n  EXPECT_EQ(10u, response_.attr_out.attr_valid);\n  EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);\n\n  EXPECT_EQ(10u, response_.attr_out.attr.ino);\n  EXPECT_EQ(kTestFileSize, response_.attr_out.attr.size);\n  EXPECT_EQ(0u, response_.attr_out.attr.blocks);\n  EXPECT_EQ(0u, response_.attr_out.attr.atime);\n  EXPECT_EQ(0u, response_.attr_out.attr.mtime);\n  EXPECT_EQ(0u, response_.attr_out.attr.ctime);\n  EXPECT_EQ(0u, response_.attr_out.attr.atimensec);\n  EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);\n  EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);\n  EXPECT_EQ(S_IFREG | 0777u, response_.attr_out.attr.mode);\n  EXPECT_EQ(0u, response_.attr_out.attr.nlink);\n  EXPECT_EQ(0u, response_.attr_out.attr.uid);\n  EXPECT_EQ(0u, response_.attr_out.attr.gid);\n  EXPECT_EQ(0u, response_.attr_out.attr.rdev);\n  EXPECT_EQ(0u, response_.attr_out.attr.blksize);\n  EXPECT_EQ(0u, response_.attr_out.attr.flags);\n}\n\nTEST_F(FuseAppLoopTest, GetAttr_Root) {\n  request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);\n  request_.header.nodeid = FUSE_ROOT_ID;\n\n  ASSERT_TRUE(request_.Write(sockets_[0].get()));\n  ASSERT_TRUE(response_.Read(sockets_[0].get()));\n\n  EXPECT_EQ(kFuseSuccess, response_.header.error);\n  EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),\n            response_.header.len);\n  EXPECT_EQ(1u, response_.header.unique);\n\n  EXPECT_EQ(10u, response_.attr_out.attr_valid);\n  EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);\n\n  EXPECT_EQ(static_cast<unsigned>(FUSE_ROOT_ID), response_.attr_out.attr.ino);\n  EXPECT_EQ(0u, response_.attr_out.attr.size);\n  EXPECT_EQ(0u, response_.attr_out.attr.blocks);\n  EXPECT_EQ(0u, response_.attr_out.attr.atime);\n  EXPECT_EQ(0u, response_.attr_out.attr.mtime);\n  EXPECT_EQ(0u, response_.attr_out.attr.ctime);\n  EXPECT_EQ(0u, response_.attr_out.attr.atimensec);\n  EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);\n  EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);\n  EXPECT_EQ(S_IFDIR | 0777u, response_.attr_out.attr.mode);\n  EXPECT_EQ(0u, response_.attr_out.attr.nlink);\n  EXPECT_EQ(0u, response_.attr_out.attr.uid);\n  EXPECT_EQ(0u, response_.attr_out.attr.gid);\n  EXPECT_EQ(0u, response_.attr_out.attr.rdev);\n  EXPECT_EQ(0u, response_.attr_out.attr.blksize);\n  EXPECT_EQ(0u, response_.attr_out.attr.flags);\n}\n\nTEST_F(FuseAppLoopTest, Open) {\n  CheckCallback(sizeof(fuse_open_in), FUSE_OPEN, sizeof(fuse_open_out));\n}\n\nTEST_F(FuseAppLoopTest, Fsync) {\n  CheckCallback(0u, FUSE_FSYNC, 0u);\n}\n\nTEST_F(FuseAppLoopTest, Release) {\n  CheckCallback(0u, FUSE_RELEASE, 0u);\n}\n\nTEST_F(FuseAppLoopTest, Read) {\n  CheckCallback(sizeof(fuse_read_in), FUSE_READ, 0u);\n}\n\nTEST_F(FuseAppLoopTest, Write) {\n  CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));\n}\n\nTEST_F(FuseAppLoopTest, Break) {\n    // Ensure that the loop started.\n    request_.Reset(sizeof(fuse_open_in), FUSE_OPEN, 1);\n    request_.header.nodeid = 10;\n    ASSERT_TRUE(request_.Write(sockets_[0]));\n    ASSERT_TRUE(response_.Read(sockets_[0]));\n\n    loop_->Break();\n    if (thread_.joinable()) {\n        thread_.join();\n    }\n}\n\n}  // namespace fuse\n}  // namespace android\n"
  },
  {
    "path": "libappfuse/tests/FuseBridgeLoopTest.cc",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#include \"libappfuse/FuseBridgeLoop.h\"\n\n#include <sys/socket.h>\n\n#include <sstream>\n#include <thread>\n\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace fuse {\nnamespace {\n\nclass Callback : public FuseBridgeLoopCallback {\n public:\n  bool mounted;\n  bool closed;\n  Callback() : mounted(false), closed(false) {}\n\n  void OnMount(int /*mount_id*/) override { mounted = true; }\n\n  void OnClosed(int /* mount_id */) override { closed = true; }\n};\n\nclass FuseBridgeLoopTest : public ::testing::Test {\n protected:\n  base::unique_fd dev_sockets_[2];\n  base::unique_fd proxy_sockets_[2];\n  Callback callback_;\n  std::thread thread_;\n\n  FuseRequest request_;\n  FuseResponse response_;\n\n  void SetUp() override {\n    base::SetMinimumLogSeverity(base::VERBOSE);\n    ASSERT_TRUE(SetupMessageSockets(&dev_sockets_));\n    ASSERT_TRUE(SetupMessageSockets(&proxy_sockets_));\n    thread_ = std::thread([this] {\n        FuseBridgeLoop loop;\n        loop.AddBridge(1, std::move(dev_sockets_[1]), std::move(proxy_sockets_[0]));\n        loop.Start(&callback_);\n    });\n  }\n\n  void CheckNotImpl(uint32_t opcode) {\n    SCOPED_TRACE((std::ostringstream() << \"opcode: \" << opcode).str());\n\n    memset(&request_, 0, sizeof(FuseRequest));\n    request_.header.opcode = opcode;\n    request_.header.len = sizeof(fuse_in_header);\n    request_.header.unique = 1;\n    ASSERT_TRUE(request_.Write(dev_sockets_[0]));\n\n    memset(&response_, 0, sizeof(FuseResponse));\n    ASSERT_TRUE(response_.Read(dev_sockets_[0]));\n    EXPECT_EQ(-ENOSYS, response_.header.error);\n  }\n\n  void CheckProxy(uint32_t opcode) {\n    SCOPED_TRACE((std::ostringstream() << \"opcode: \" << opcode).str());\n\n    memset(&request_, 0, sizeof(FuseRequest));\n    request_.header.opcode = opcode;\n    request_.header.unique = opcode; // Use opcode as unique.\n    request_.header.len = sizeof(fuse_in_header);\n    ASSERT_TRUE(request_.Write(dev_sockets_[0]));\n\n    memset(&request_, 0, sizeof(FuseRequest));\n    ASSERT_TRUE(request_.Read(proxy_sockets_[1]));\n    EXPECT_EQ(opcode, request_.header.opcode);\n    EXPECT_EQ(opcode, request_.header.unique);\n\n    memset(&response_, 0, sizeof(FuseResponse));\n    response_.header.len = sizeof(fuse_out_header);\n    response_.header.unique = opcode;  // Use opcode as unique.\n    response_.header.error = kFuseSuccess;\n    ASSERT_TRUE(response_.Write(proxy_sockets_[1]));\n\n    memset(&response_, 0, sizeof(FuseResponse));\n    ASSERT_TRUE(response_.Read(dev_sockets_[0]));\n    EXPECT_EQ(opcode, response_.header.unique);\n    EXPECT_EQ(kFuseSuccess, response_.header.error);\n  }\n\n  void SendInitRequest(uint64_t unique) {\n    memset(&request_, 0, sizeof(FuseRequest));\n    request_.header.opcode = FUSE_INIT;\n    request_.header.unique = unique;\n    request_.header.len = sizeof(fuse_in_header) + sizeof(fuse_init_in);\n    request_.init_in.major = FUSE_KERNEL_VERSION;\n    request_.init_in.minor = FUSE_KERNEL_MINOR_VERSION;\n    ASSERT_TRUE(request_.Write(dev_sockets_[0]));\n  }\n\n  void Close() {\n    dev_sockets_[0].reset();\n    dev_sockets_[1].reset();\n    proxy_sockets_[0].reset();\n    proxy_sockets_[1].reset();\n    if (thread_.joinable()) {\n      thread_.join();\n    }\n    ASSERT_TRUE(callback_.closed);\n  }\n\n  void TearDown() override {\n    Close();\n  }\n};\n\n} //  namespace\n\nTEST_F(FuseBridgeLoopTest, FuseInit) {\n  SendInitRequest(1u);\n\n  memset(&response_, 0, sizeof(FuseResponse));\n  ASSERT_TRUE(response_.Read(dev_sockets_[0]));\n  EXPECT_EQ(kFuseSuccess, response_.header.error);\n  EXPECT_EQ(1u, response_.header.unique);\n\n  // Unmount.\n  Close();\n  EXPECT_TRUE(callback_.mounted);\n}\n\nTEST_F(FuseBridgeLoopTest, FuseForget) {\n  memset(&request_, 0, sizeof(FuseRequest));\n  request_.header.opcode = FUSE_FORGET;\n  request_.header.unique = 1u;\n  request_.header.len = sizeof(fuse_in_header) + sizeof(fuse_forget_in);\n  ASSERT_TRUE(request_.Write(dev_sockets_[0]));\n\n  SendInitRequest(2u);\n\n  memset(&response_, 0, sizeof(FuseResponse));\n  ASSERT_TRUE(response_.Read(dev_sockets_[0]));\n  EXPECT_EQ(2u, response_.header.unique) <<\n      \"The loop must not respond to FUSE_FORGET\";\n}\n\nTEST_F(FuseBridgeLoopTest, FuseNotImpl) {\n  CheckNotImpl(FUSE_SETATTR);\n  CheckNotImpl(FUSE_READLINK);\n  CheckNotImpl(FUSE_SYMLINK);\n  CheckNotImpl(FUSE_MKNOD);\n  CheckNotImpl(FUSE_MKDIR);\n  CheckNotImpl(FUSE_UNLINK);\n  CheckNotImpl(FUSE_RMDIR);\n  CheckNotImpl(FUSE_RENAME);\n  CheckNotImpl(FUSE_LINK);\n  CheckNotImpl(FUSE_STATFS);\n  CheckNotImpl(FUSE_SETXATTR);\n  CheckNotImpl(FUSE_GETXATTR);\n  CheckNotImpl(FUSE_LISTXATTR);\n  CheckNotImpl(FUSE_REMOVEXATTR);\n  CheckNotImpl(FUSE_FLUSH);\n  CheckNotImpl(FUSE_OPENDIR);\n  CheckNotImpl(FUSE_READDIR);\n  CheckNotImpl(FUSE_RELEASEDIR);\n  CheckNotImpl(FUSE_FSYNCDIR);\n  CheckNotImpl(FUSE_GETLK);\n  CheckNotImpl(FUSE_SETLK);\n  CheckNotImpl(FUSE_SETLKW);\n  CheckNotImpl(FUSE_ACCESS);\n  CheckNotImpl(FUSE_CREATE);\n  CheckNotImpl(FUSE_INTERRUPT);\n  CheckNotImpl(FUSE_BMAP);\n  CheckNotImpl(FUSE_DESTROY);\n  CheckNotImpl(FUSE_IOCTL);\n  CheckNotImpl(FUSE_POLL);\n  CheckNotImpl(FUSE_NOTIFY_REPLY);\n  CheckNotImpl(FUSE_BATCH_FORGET);\n  CheckNotImpl(FUSE_FALLOCATE);\n  CheckNotImpl(FUSE_READDIRPLUS);\n  CheckNotImpl(FUSE_RENAME2);\n  CheckNotImpl(FUSE_LSEEK);\n}\n\nTEST_F(FuseBridgeLoopTest, Proxy) {\n  CheckProxy(FUSE_LOOKUP);\n  CheckProxy(FUSE_GETATTR);\n  CheckProxy(FUSE_READ);\n  CheckProxy(FUSE_WRITE);\n  CheckProxy(FUSE_FSYNC);\n\n  // Invoke FUSE_OPEN and FUSE_RELEASE at last as the loop will exit when all files are closed.\n  CheckProxy(FUSE_OPEN);\n  CheckProxy(FUSE_RELEASE);\n\n  // Ensure the loop exits.\n  Close();\n}\n\n}  // namespace fuse\n}  // namespace android\n"
  },
  {
    "path": "libappfuse/tests/FuseBufferTest.cc",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specic language governing permissions and\n * limitations under the License.\n */\n\n#include \"libappfuse/FuseBuffer.h\"\n\n#include <fcntl.h>\n#include <string.h>\n#include <sys/socket.h>\n\n#include <thread>\n\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace fuse {\n\nconstexpr char kTempFile[] = \"/data/local/tmp/appfuse_test_dump\";\n\nvoid OpenTempFile(android::base::unique_fd* fd) {\n  fd->reset(open(kTempFile, O_CREAT | O_RDWR, 0600));\n  ASSERT_NE(-1, *fd) << strerror(errno);\n  unlink(kTempFile);\n  ASSERT_NE(-1, *fd) << strerror(errno);\n}\n\nvoid TestReadInvalidLength(size_t headerSize, size_t write_size) {\n  android::base::unique_fd fd;\n  OpenTempFile(&fd);\n\n  char buffer[std::max(headerSize, sizeof(FuseRequest))];\n  FuseRequest* const packet = reinterpret_cast<FuseRequest*>(buffer);\n  packet->header.len = headerSize;\n  ASSERT_NE(-1, write(fd, packet, write_size)) << strerror(errno);\n\n  lseek(fd, 0, SEEK_SET);\n  EXPECT_FALSE(packet->Read(fd));\n}\n\nvoid TestWriteInvalidLength(size_t size) {\n  android::base::unique_fd fd;\n  OpenTempFile(&fd);\n\n  char buffer[std::max(size, sizeof(FuseRequest))];\n  FuseRequest* const packet = reinterpret_cast<FuseRequest*>(buffer);\n  packet->header.len = size;\n  EXPECT_FALSE(packet->Write(fd));\n}\n\n// Use FuseRequest as a template instance of FuseMessage.\n\nTEST(FuseMessageTest, ReadAndWrite) {\n  android::base::unique_fd fd;\n  OpenTempFile(&fd);\n\n  FuseRequest request;\n  request.header.len = sizeof(FuseRequest);\n  request.header.opcode = 1;\n  request.header.unique = 2;\n  request.header.nodeid = 3;\n  request.header.uid = 4;\n  request.header.gid = 5;\n  request.header.pid = 6;\n  strcpy(request.lookup_name, \"test\");\n\n  ASSERT_TRUE(request.Write(fd));\n\n  memset(&request, 0, sizeof(FuseRequest));\n  lseek(fd, 0, SEEK_SET);\n\n  ASSERT_TRUE(request.Read(fd));\n  EXPECT_EQ(sizeof(FuseRequest), request.header.len);\n  EXPECT_EQ(1u, request.header.opcode);\n  EXPECT_EQ(2u, request.header.unique);\n  EXPECT_EQ(3u, request.header.nodeid);\n  EXPECT_EQ(4u, request.header.uid);\n  EXPECT_EQ(5u, request.header.gid);\n  EXPECT_EQ(6u, request.header.pid);\n  EXPECT_STREQ(\"test\", request.lookup_name);\n}\n\nTEST(FuseMessageTest, Read_InconsistentLength) {\n  TestReadInvalidLength(sizeof(fuse_in_header), sizeof(fuse_in_header) + 1);\n}\n\nTEST(FuseMessageTest, Read_TooLong) {\n  TestReadInvalidLength(sizeof(FuseRequest) + 1, sizeof(FuseRequest) + 1);\n}\n\nTEST(FuseMessageTest, Read_TooShort) {\n  TestReadInvalidLength(sizeof(fuse_in_header) - 1, sizeof(fuse_in_header) - 1);\n}\n\nTEST(FuseMessageTest, Write_TooLong) {\n  TestWriteInvalidLength(sizeof(FuseRequest) + 1);\n}\n\nTEST(FuseMessageTest, Write_TooShort) {\n  TestWriteInvalidLength(sizeof(fuse_in_header) - 1);\n}\n\nTEST(FuseResponseTest, Reset) {\n  FuseResponse response;\n  // Write 1 to the first ten bytes.\n  memset(response.read_data, 'a', 10);\n\n  response.Reset(0, -1, 2);\n  EXPECT_EQ(sizeof(fuse_out_header), response.header.len);\n  EXPECT_EQ(-1, response.header.error);\n  EXPECT_EQ(2u, response.header.unique);\n  EXPECT_EQ('a', response.read_data[0]);\n  EXPECT_EQ('a', response.read_data[9]);\n\n  response.Reset(5, -4, 3);\n  EXPECT_EQ(sizeof(fuse_out_header) + 5, response.header.len);\n  EXPECT_EQ(-4, response.header.error);\n  EXPECT_EQ(3u, response.header.unique);\n  EXPECT_EQ(0, response.read_data[0]);\n  EXPECT_EQ(0, response.read_data[1]);\n  EXPECT_EQ(0, response.read_data[2]);\n  EXPECT_EQ(0, response.read_data[3]);\n  EXPECT_EQ(0, response.read_data[4]);\n  EXPECT_EQ('a', response.read_data[5]);\n}\n\nTEST(FuseResponseTest, ResetHeader) {\n  FuseResponse response;\n  // Write 1 to the first ten bytes.\n  memset(response.read_data, 'a', 10);\n\n  response.ResetHeader(0, -1, 2);\n  EXPECT_EQ(sizeof(fuse_out_header), response.header.len);\n  EXPECT_EQ(-1, response.header.error);\n  EXPECT_EQ(2u, response.header.unique);\n  EXPECT_EQ('a', response.read_data[0]);\n  EXPECT_EQ('a', response.read_data[9]);\n\n  response.ResetHeader(5, -4, 3);\n  EXPECT_EQ(sizeof(fuse_out_header) + 5, response.header.len);\n  EXPECT_EQ(-4, response.header.error);\n  EXPECT_EQ(3u, response.header.unique);\n  EXPECT_EQ('a', response.read_data[0]);\n  EXPECT_EQ('a', response.read_data[9]);\n}\n\nTEST(FuseBufferTest, HandleInit) {\n  FuseBuffer buffer;\n  memset(&buffer, 0, sizeof(FuseBuffer));\n\n  buffer.request.header.opcode = FUSE_INIT;\n  buffer.request.init_in.major = FUSE_KERNEL_VERSION;\n  buffer.request.init_in.minor = FUSE_KERNEL_MINOR_VERSION;\n\n  buffer.HandleInit();\n\n  ASSERT_EQ(sizeof(fuse_out_header) + FUSE_COMPAT_22_INIT_OUT_SIZE,\n            buffer.response.header.len);\n  EXPECT_EQ(kFuseSuccess, buffer.response.header.error);\n  EXPECT_EQ(static_cast<unsigned int>(FUSE_KERNEL_VERSION),\n            buffer.response.init_out.major);\n  EXPECT_EQ(15u, buffer.response.init_out.minor);\n  EXPECT_EQ(static_cast<unsigned int>(FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES),\n      buffer.response.init_out.flags);\n  EXPECT_EQ(kFuseMaxWrite, buffer.response.init_out.max_write);\n}\n\nTEST(FuseBufferTest, HandleNotImpl) {\n  FuseBuffer buffer;\n  memset(&buffer, 0, sizeof(FuseBuffer));\n\n  buffer.HandleNotImpl();\n\n  ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);\n  EXPECT_EQ(-ENOSYS, buffer.response.header.error);\n}\n\nTEST(SetupMessageSocketsTest, Stress) {\n    constexpr int kCount = 1000;\n\n    FuseRequest request;\n    request.header.len = sizeof(FuseRequest);\n\n    base::unique_fd fds[2];\n    SetupMessageSockets(&fds);\n\n    std::thread thread([&fds] {\n        FuseRequest request;\n        for (int i = 0; i < kCount; ++i) {\n            ASSERT_TRUE(request.Read(fds[1]));\n            usleep(1000);\n        }\n    });\n\n    for (int i = 0; i < kCount; ++i) {\n        ASSERT_TRUE(request.Write(fds[0]));\n    }\n\n    thread.join();\n}\n\n} // namespace fuse\n} // namespace android\n"
  },
  {
    "path": "libasyncio/Android.bp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libasyncio_defaults\",\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-Wextra\",\n    ],\n}\n\ncc_library {\n    name: \"libasyncio\",\n    defaults: [\"libasyncio_defaults\"],\n    vendor_available: true,\n    recovery_available: true,\n    min_sdk_version: \"apex_inherit\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.adbd\",\n    ],\n    host_supported: true,\n    srcs: [\n        \"AsyncIO.cpp\",\n    ],\n\n    export_include_dirs: [\"include\"],\n    target: {\n        darwin: {\n            enabled: false,\n        },\n        linux_bionic: {\n            enabled: true,\n        },\n        windows: {\n            enabled: false,\n        },\n    },\n}\n"
  },
  {
    "path": "libasyncio/AsyncIO.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <asyncio/AsyncIO.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n#include <cstdint>\n#include <cstring>\n\nint io_setup(unsigned nr, aio_context_t* ctxp) {\n    return syscall(__NR_io_setup, nr, ctxp);\n}\n\nint io_destroy(aio_context_t ctx) {\n    return syscall(__NR_io_destroy, ctx);\n}\n\nint io_submit(aio_context_t ctx, long nr, iocb** iocbpp) {\n    return syscall(__NR_io_submit, ctx, nr, iocbpp);\n}\n\nint io_getevents(aio_context_t ctx, long min_nr, long max_nr, io_event* events, timespec* timeout) {\n    return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);\n}\n\nint io_cancel(aio_context_t ctx, iocb* iocbp, io_event* result) {\n    return syscall(__NR_io_cancel, ctx, iocbp, result);\n}\n\nvoid io_prep(iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read) {\n    memset(iocb, 0, sizeof(*iocb));\n    iocb->aio_fildes = fd;\n    iocb->aio_lio_opcode = read ? IOCB_CMD_PREAD : IOCB_CMD_PWRITE;\n    iocb->aio_reqprio = 0;\n    iocb->aio_buf = reinterpret_cast<uint64_t>(buf);\n    iocb->aio_nbytes = count;\n    iocb->aio_offset = offset;\n}\n\nvoid io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {\n    io_prep(iocb, fd, buf, count, offset, true);\n}\n\nvoid io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset) {\n    io_prep(iocb, fd, buf, count, offset, false);\n}\n"
  },
  {
    "path": "libasyncio/include/asyncio/AsyncIO.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _ASYNCIO_H\n#define _ASYNCIO_H\n\n#include <linux/aio_abi.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <sys/cdefs.h>\n#include <sys/types.h>\n#include <time.h>\n#include <unistd.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * Provides kernel aio operations.\n */\n\nint io_setup(unsigned nr, aio_context_t* ctxp);\nint io_destroy(aio_context_t ctx);\nint io_submit(aio_context_t ctx, long nr, struct iocb** iocbpp);\nint io_getevents(aio_context_t ctx, long min_nr, long max_nr, struct io_event* events,\n                 struct timespec* timeout);\nint io_cancel(aio_context_t ctx, struct iocb*, struct io_event* result);\n\nvoid io_prep_pread(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);\nvoid io_prep_pwrite(struct iocb* iocb, int fd, void* buf, size_t count, long long offset);\nvoid io_prep(struct iocb* iocb, int fd, const void* buf, uint64_t count, int64_t offset, bool read);\n\n#ifdef __cplusplus\n};\n#endif\n\n#endif  // ASYNCIO_H\n"
  },
  {
    "path": "libcrypto_utils/Android.bp",
    "content": "//\n// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libcrypto_utils\",\n    vendor_available: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    host_supported: true,\n    srcs: [\n        \"android_pubkey.cpp\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n    local_include_dirs: [\"include\"],\n    export_include_dirs: [\"include\"],\n    shared_libs: [\"libcrypto\"],\n    target: {\n        windows: {\n            enabled: true,\n        },\n    },\n    min_sdk_version: \"apex_inherit\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.adbd\",\n    ],\n}\n"
  },
  {
    "path": "libcrypto_utils/android_pubkey.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <crypto_utils/android_pubkey.h>\n\n#include <assert.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <openssl/bn.h>\n\n// Better safe than sorry.\n#if (ANDROID_PUBKEY_MODULUS_SIZE % 4) != 0\n#error RSA modulus size must be multiple of the word size!\n#endif\n\n// Size of the RSA modulus in words.\n#define ANDROID_PUBKEY_MODULUS_SIZE_WORDS (ANDROID_PUBKEY_MODULUS_SIZE / 4)\n\n// This file implements encoding and decoding logic for Android's custom RSA\n// public key binary format. Public keys are stored as a sequence of\n// little-endian 32 bit words. Note that Android only supports little-endian\n// processors, so we don't do any byte order conversions when parsing the binary\n// struct.\nstruct RSAPublicKey {\n  // Modulus length. This must be ANDROID_PUBKEY_MODULUS_SIZE.\n  uint32_t modulus_size_words;\n\n  // Precomputed montgomery parameter: -1 / n[0] mod 2^32\n  uint32_t n0inv;\n\n  // RSA modulus as a little-endian array.\n  uint8_t modulus[ANDROID_PUBKEY_MODULUS_SIZE];\n\n  // Montgomery parameter R^2 as a little-endian array.\n  uint8_t rr[ANDROID_PUBKEY_MODULUS_SIZE];\n\n  // RSA modulus: 3 or 65537\n  uint32_t exponent;\n};\n\nbool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key) {\n  const RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;\n  bool ret = false;\n  RSA* new_key = RSA_new();\n  BIGNUM* n = NULL;\n  BIGNUM* e = NULL;\n  if (!new_key) {\n    goto cleanup;\n  }\n\n  // Check |size| is large enough and the modulus size is correct.\n  if (size < sizeof(RSAPublicKey)) {\n    goto cleanup;\n  }\n  if (key_struct->modulus_size_words != ANDROID_PUBKEY_MODULUS_SIZE_WORDS) {\n    goto cleanup;\n  }\n\n  // Convert the modulus to big-endian byte order as expected by BN_bin2bn.\n  n = BN_le2bn(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, NULL);\n  if (!n) {\n    goto cleanup;\n  }\n\n  // Read the exponent.\n  e = BN_new();\n  if (!e || !BN_set_word(e, key_struct->exponent)) {\n    goto cleanup;\n  }\n\n  if (!RSA_set0_key(new_key, n, e, NULL)) {\n    goto cleanup;\n  }\n  // RSA_set0_key takes ownership of its inputs on success.\n  n = NULL;\n  e = NULL;\n\n  // Note that we don't extract the montgomery parameters n0inv and rr from\n  // the RSAPublicKey structure. They assume a word size of 32 bits, but\n  // BoringSSL may use a word size of 64 bits internally, so we're lacking the\n  // top 32 bits of n0inv in general. For now, we just ignore the parameters\n  // and have BoringSSL recompute them internally. More sophisticated logic can\n  // be added here if/when we want the additional speedup from using the\n  // pre-computed montgomery parameters.\n\n  *key = new_key;\n  new_key = NULL;\n  ret = true;\n\ncleanup:\n  RSA_free(new_key);\n  BN_free(n);\n  BN_free(e);\n  return ret;\n}\n\nbool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size) {\n  RSAPublicKey* key_struct = (RSAPublicKey*)key_buffer;\n  bool ret = false;\n  BN_CTX* ctx = BN_CTX_new();\n  BIGNUM* r32 = BN_new();\n  BIGNUM* n0inv = BN_new();\n  BIGNUM* rr = BN_new();\n\n  if (sizeof(RSAPublicKey) > size || RSA_size(key) != ANDROID_PUBKEY_MODULUS_SIZE) {\n    goto cleanup;\n  }\n\n  // Store the modulus size.\n  key_struct->modulus_size_words = ANDROID_PUBKEY_MODULUS_SIZE_WORDS;\n\n  // Compute and store n0inv = -1 / N[0] mod 2^32.\n  if (!ctx || !r32 || !n0inv || !BN_set_bit(r32, 32) || !BN_mod(n0inv, RSA_get0_n(key), r32, ctx) ||\n      !BN_mod_inverse(n0inv, n0inv, r32, ctx) || !BN_sub(n0inv, r32, n0inv)) {\n    goto cleanup;\n  }\n  key_struct->n0inv = (uint32_t)BN_get_word(n0inv);\n\n  // Store the modulus.\n  if (!BN_bn2le_padded(key_struct->modulus, ANDROID_PUBKEY_MODULUS_SIZE, RSA_get0_n(key))) {\n    goto cleanup;\n  }\n\n  // Compute and store rr = (2^(rsa_size)) ^ 2 mod N.\n  if (!ctx || !rr || !BN_set_bit(rr, ANDROID_PUBKEY_MODULUS_SIZE * 8) ||\n      !BN_mod_sqr(rr, rr, RSA_get0_n(key), ctx) ||\n      !BN_bn2le_padded(key_struct->rr, ANDROID_PUBKEY_MODULUS_SIZE, rr)) {\n    goto cleanup;\n  }\n\n  // Store the exponent.\n  key_struct->exponent = (uint32_t)BN_get_word(RSA_get0_e(key));\n\n  ret = true;\n\ncleanup:\n  BN_free(rr);\n  BN_free(n0inv);\n  BN_free(r32);\n  BN_CTX_free(ctx);\n  return ret;\n}\n"
  },
  {
    "path": "libcrypto_utils/include/crypto_utils/android_pubkey.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef CRYPTO_UTILS_ANDROID_PUBKEY_H\n#define CRYPTO_UTILS_ANDROID_PUBKEY_H\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n\n#include <openssl/rsa.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// Size of an RSA modulus such as an encrypted block or a signature.\n#define ANDROID_PUBKEY_MODULUS_SIZE (2048 / 8)\n\n// Size of an encoded RSA key.\n#define ANDROID_PUBKEY_ENCODED_SIZE \\\n  (3 * sizeof(uint32_t) + 2 * ANDROID_PUBKEY_MODULUS_SIZE)\n\n/* Allocates a new RSA |key| object, decodes a public RSA key stored in\n * Android's custom binary format from |key_buffer| and sets the key parameters\n * in |key|. |size| specifies the size of the key buffer and must be at least\n * |ANDROID_PUBKEY_ENCODED_SIZE|. The resulting |*key| can be used with the\n * standard BoringSSL API to perform public operations.\n *\n * Returns true if successful, in which case the caller receives ownership of\n * the |*key| object, i.e. needs to call RSA_free() when done with it. If there\n * is an error, |key| is left untouched and the return value will be false.\n */\nbool android_pubkey_decode(const uint8_t* key_buffer, size_t size, RSA** key);\n\n/* Encodes |key| in the Android RSA public key binary format and stores the\n * bytes in |key_buffer|. |key_buffer| should be of size at least\n * |ANDROID_PUBKEY_ENCODED_SIZE|.\n *\n * Returns true if successful, false on error.\n */\nbool android_pubkey_encode(const RSA* key, uint8_t* key_buffer, size_t size);\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n#endif  // CRYPTO_UTILS_ANDROID_PUBKEY_H\n"
  },
  {
    "path": "libcrypto_utils/tests/Android.bp",
    "content": "//\n// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test_host {\n    name: \"libcrypto_utils_test\",\n    srcs: [\"android_pubkey_test.cpp\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-Wextra\",\n    ],\n    shared_libs: [\n        \"libcrypto_utils\",\n        \"libcrypto\",\n    ],\n}\n"
  },
  {
    "path": "libcrypto_utils/tests/android_pubkey_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <crypto_utils/android_pubkey.h>\n\n#include <string.h>\n\n#include <memory>\n\n#include <openssl/obj_mac.h>\n#include <openssl/rsa.h>\n\n#include <gtest/gtest.h>\n\n// Test digest to verify.\nconst uint8_t kDigest[] = {\n    0x31, 0x5f, 0x5b, 0xdb, 0x76, 0xd0, 0x78, 0xc4, 0x3b, 0x8a, 0xc0,\n    0x06, 0x4e, 0x4a, 0x01, 0x64, 0x61, 0x2b, 0x1f, 0xce, 0x77, 0xc8,\n    0x69, 0x34, 0x5b, 0xfc, 0x94, 0xc7, 0x58, 0x94, 0xed, 0xd3,\n};\n\n// 2048 RSA test key.\nconst uint8_t kKey2048[ANDROID_PUBKEY_ENCODED_SIZE] = {\n    0x40, 0x00, 0x00, 0x00, 0x05, 0x75, 0x61, 0xd1, 0x33, 0xf0, 0x2d, 0x12,\n    0x45, 0xfb, 0xae, 0x07, 0x02, 0x15, 0x4f, 0x3a, 0x2b, 0xa3, 0xbc, 0x49,\n    0xbd, 0x14, 0x07, 0xa0, 0xc0, 0x9f, 0x0c, 0x52, 0x60, 0x77, 0x9f, 0xa2,\n    0x31, 0xd0, 0xa7, 0xfb, 0x7e, 0xde, 0xfb, 0xc9, 0x05, 0xc0, 0x97, 0xf7,\n    0x74, 0x99, 0xe6, 0xd1, 0x08, 0xa6, 0xc2, 0x59, 0x5a, 0xd8, 0x37, 0x1d,\n    0xe0, 0x48, 0x5e, 0x63, 0x44, 0x04, 0x8b, 0x05, 0x20, 0xf6, 0x25, 0x67,\n    0x38, 0xb2, 0xb6, 0xf9, 0xbe, 0xb6, 0x1d, 0x7f, 0x1b, 0x71, 0x8a, 0xeb,\n    0xb7, 0xf8, 0x01, 0xc1, 0x5e, 0xf7, 0xfe, 0x48, 0x08, 0x27, 0x0f, 0x27,\n    0x2a, 0x64, 0x1a, 0x43, 0x8d, 0xcf, 0x5a, 0x33, 0x5c, 0x18, 0xc5, 0xf4,\n    0xe7, 0xfe, 0xee, 0xd3, 0x12, 0x62, 0xad, 0x61, 0x78, 0x9a, 0x03, 0xb0,\n    0xaf, 0xab, 0x91, 0x57, 0x46, 0xbf, 0x18, 0xc6, 0xbc, 0x0c, 0x6b, 0x55,\n    0xcd, 0xda, 0xc4, 0xcc, 0x98, 0x46, 0x91, 0x99, 0xbc, 0xa3, 0xca, 0x6c,\n    0x86, 0xa6, 0x1c, 0x8f, 0xca, 0xf8, 0xf6, 0x8a, 0x00, 0x8e, 0x05, 0xd7,\n    0x13, 0x43, 0xe2, 0xf2, 0x1a, 0x13, 0xf3, 0x50, 0x13, 0xa4, 0xf2, 0x4e,\n    0x41, 0xb1, 0x36, 0x78, 0x55, 0x4c, 0x5e, 0x27, 0xc5, 0xc0, 0x4b, 0xd8,\n    0x93, 0xaa, 0x7e, 0xf0, 0x90, 0x08, 0x10, 0x26, 0x72, 0x6d, 0xb9, 0x21,\n    0xae, 0x4d, 0x01, 0x4b, 0x55, 0x1d, 0xe7, 0x1e, 0x5e, 0x31, 0x6e, 0x62,\n    0xd1, 0x33, 0x26, 0xcb, 0xdb, 0xfe, 0x72, 0x98, 0xc8, 0x06, 0x1c, 0x12,\n    0xdf, 0xfc, 0x74, 0xe5, 0x7a, 0x6f, 0xf5, 0xa3, 0x63, 0x08, 0xe3, 0x02,\n    0x68, 0x4d, 0x7c, 0x70, 0x05, 0xec, 0x95, 0x7e, 0x24, 0xa4, 0xbc, 0x4c,\n    0xcd, 0x39, 0x14, 0xb5, 0x2a, 0x8f, 0xc1, 0xe3, 0x4e, 0xfa, 0xf8, 0x70,\n    0x50, 0x8f, 0xd5, 0x8e, 0xc7, 0xb5, 0x32, 0x89, 0x4d, 0xbb, 0x6a, 0xc1,\n    0xc1, 0xa2, 0x42, 0x57, 0x57, 0xbd, 0x2a, 0xdc, 0xa6, 0xfd, 0xc8, 0x86,\n    0x44, 0x6a, 0x03, 0x5d, 0x4d, 0x28, 0xe1, 0xde, 0xb4, 0xa9, 0xa5, 0x03,\n    0x61, 0x7a, 0x5f, 0xb1, 0x09, 0x17, 0x2b, 0x9c, 0xa2, 0x54, 0x28, 0xad,\n    0x34, 0xc9, 0x5f, 0x6c, 0x9f, 0xb8, 0xd2, 0xa9, 0x78, 0xa7, 0xaa, 0xb3,\n    0x11, 0x2f, 0x65, 0x9b, 0x4e, 0x67, 0x0c, 0xcc, 0x20, 0x36, 0xbf, 0x26,\n    0x2b, 0x4e, 0xc0, 0xd4, 0xbd, 0x22, 0x64, 0xc4, 0x1c, 0x56, 0x69, 0xdb,\n    0x5f, 0x89, 0xe1, 0x75, 0x68, 0x8d, 0x0e, 0xab, 0x1c, 0x10, 0x1a, 0xc0,\n    0x12, 0x5d, 0x6f, 0xbd, 0x09, 0xbb, 0x47, 0xcb, 0xe7, 0x34, 0xef, 0x56,\n    0xab, 0xea, 0xc3, 0xe9, 0x7f, 0x9a, 0x3d, 0xe9, 0x2d, 0x14, 0x61, 0x25,\n    0x37, 0x5c, 0x3b, 0x4b, 0xaf, 0x5a, 0x4b, 0xc8, 0x99, 0x1a, 0x32, 0x8f,\n    0x54, 0x07, 0xd3, 0x57, 0x8a, 0x3d, 0x2a, 0xf7, 0x9e, 0x7e, 0x92, 0x2a,\n    0x50, 0xe9, 0xd8, 0xdb, 0xd6, 0x03, 0xd3, 0x8e, 0x54, 0x32, 0xce, 0x87,\n    0x93, 0x92, 0xe7, 0x75, 0xe1, 0x6b, 0x78, 0x1a, 0x85, 0xc2, 0x46, 0xa1,\n    0x31, 0xbb, 0xc7, 0xb9, 0x1d, 0xd1, 0x71, 0xe0, 0xe2, 0x9b, 0x9c, 0x0d,\n    0xa3, 0xcf, 0x93, 0x4d, 0x87, 0x7b, 0x65, 0xd9, 0xda, 0x4c, 0xd9, 0x6a,\n    0xa6, 0x36, 0xc2, 0xc7, 0xe3, 0x33, 0xe2, 0xc3, 0x83, 0xd1, 0x72, 0x54,\n    0x30, 0x81, 0x5e, 0x34, 0x2c, 0x61, 0xee, 0xf4, 0x48, 0x97, 0xb6, 0xaa,\n    0x47, 0x6a, 0x05, 0x09, 0xd8, 0x4d, 0x90, 0xaf, 0xa8, 0x4e, 0x82, 0xe4,\n    0x8e, 0xb5, 0xe2, 0x65, 0x86, 0x67, 0xe9, 0x5b, 0x4b, 0x9a, 0x68, 0x08,\n    0x30, 0xf6, 0x25, 0x8b, 0x20, 0xda, 0x26, 0x6f, 0xbd, 0x0d, 0xa5, 0xd8,\n    0x6a, 0x7b, 0x01, 0x2f, 0xab, 0x7b, 0xb5, 0xfe, 0x62, 0x37, 0x2d, 0x94,\n    0x43, 0x2f, 0x4d, 0x16, 0x01, 0x00, 0x01, 0x00,\n};\n\n// 2048 bit RSA signature.\nconst uint8_t kSignature2048[ANDROID_PUBKEY_MODULUS_SIZE] = {\n    0x3a, 0x11, 0x84, 0x40, 0xc1, 0x2f, 0x13, 0x8c, 0xde, 0xb0, 0xc3, 0x89,\n    0x8a, 0x63, 0xb2, 0x50, 0x93, 0x58, 0xc0, 0x0c, 0xb7, 0x08, 0xe7, 0x6c,\n    0x52, 0x87, 0x4e, 0x78, 0x89, 0xa3, 0x9a, 0x47, 0xeb, 0x11, 0x57, 0xbc,\n    0xb3, 0x97, 0xf8, 0x34, 0xf1, 0xf7, 0xbf, 0x3a, 0xfa, 0x1c, 0x6b, 0xdc,\n    0xd1, 0x02, 0xde, 0x9a, 0x0d, 0x72, 0xe7, 0x19, 0x63, 0x81, 0x46, 0x68,\n    0x1e, 0x63, 0x64, 0xc6, 0x59, 0xe7, 0x7c, 0x39, 0xed, 0x32, 0xd2, 0xd1,\n    0xd5, 0x1f, 0x13, 0x9b, 0x52, 0xdf, 0x34, 0xa3, 0xc0, 0xc4, 0x9a, 0x63,\n    0x9b, 0x9c, 0xbe, 0x22, 0xc8, 0xd8, 0x14, 0x2f, 0x4c, 0x78, 0x36, 0xdb,\n    0x16, 0x41, 0x67, 0xc1, 0x21, 0x8a, 0x73, 0xb2, 0xe5, 0xb0, 0xd3, 0x80,\n    0x91, 0x7a, 0xbf, 0xf9, 0x59, 0x4a, 0x4d, 0x78, 0x45, 0x44, 0xa1, 0x52,\n    0x86, 0x29, 0x48, 0x4d, 0xf0, 0x5d, 0xf2, 0x55, 0xa7, 0xcd, 0xc5, 0x2b,\n    0x7b, 0xe0, 0xb1, 0xf6, 0x2a, 0xd5, 0x61, 0xba, 0x1e, 0x1e, 0x3a, 0xf0,\n    0x55, 0xbc, 0x8c, 0x44, 0x41, 0xfc, 0xb8, 0x8c, 0x76, 0xbf, 0x80, 0x58,\n    0x82, 0x35, 0x4b, 0x0c, 0xfd, 0xef, 0xd5, 0x70, 0xd1, 0x64, 0xcb, 0x46,\n    0x58, 0x37, 0xbc, 0xa9, 0x7d, 0xd4, 0x70, 0xac, 0xce, 0xec, 0xca, 0x48,\n    0xcb, 0x0a, 0x40, 0x77, 0x04, 0x59, 0xca, 0x9c, 0x7d, 0x1a, 0x0b, 0xf0,\n    0xb5, 0xdd, 0xde, 0x71, 0x18, 0xb8, 0xef, 0x90, 0x2a, 0x09, 0x42, 0x39,\n    0x74, 0xff, 0x45, 0xa1, 0x39, 0x17, 0x50, 0x89, 0xa6, 0x5f, 0xbc, 0x9c,\n    0x0c, 0x9b, 0x47, 0x25, 0x79, 0x3e, 0xe3, 0xaa, 0xaf, 0xbe, 0x73, 0x6b,\n    0xcb, 0xe7, 0x35, 0xc1, 0x27, 0x09, 0xcd, 0xeb, 0xd7, 0xcf, 0x63, 0x83,\n    0x64, 0x8c, 0x45, 0x1c, 0x1d, 0x58, 0xcc, 0xd2, 0xf8, 0x2b, 0x4c, 0x4e,\n    0x14, 0x89, 0x2d, 0x70,\n};\n\nstruct AndroidPubkeyTest : public ::testing::Test {\n  void SetUp() override {\n    RSA* new_key = nullptr;\n    ASSERT_TRUE(android_pubkey_decode(kKey2048, sizeof(kKey2048), &new_key));\n    key_.reset(new_key);\n  }\n\n  std::unique_ptr<RSA, void(*)(RSA*)> key_ = {nullptr, RSA_free};\n};\n\nTEST_F(AndroidPubkeyTest, Decode) {\n  // Make sure the decoded key successfully verifies a valid signature.\n  EXPECT_TRUE(RSA_verify(NID_sha256, kDigest, sizeof(kDigest), kSignature2048,\n                         sizeof(kSignature2048), key_.get()));\n}\n\nTEST_F(AndroidPubkeyTest, Encode) {\n  uint8_t key_data[ANDROID_PUBKEY_ENCODED_SIZE];\n  ASSERT_TRUE(android_pubkey_encode(key_.get(), key_data, sizeof(key_data)));\n  ASSERT_EQ(0, memcmp(kKey2048, key_data, sizeof(kKey2048)));\n}\n"
  },
  {
    "path": "libcutils/Android.bp",
    "content": "package {\n    default_team: \"trendy_team_native_tools_libraries\",\n    default_applicable_licenses: [\"system_core_libcutils_license\"],\n}\n\nlicense {\n    name: \"system_core_libcutils_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n        \"SPDX-license-identifier-BSD\",\n        \"SPDX-license-identifier-MIT\", // strlcpy.c\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\nfilegroup {\n    name: \"android_filesystem_config_header\",\n    srcs: [\"include/private/android_filesystem_config.h\"],\n}\n\nrust_bindgen {\n    name: \"libandroid_ids\",\n    crate_name: \"android_ids\",\n    source_stem: \"bindings\",\n    wrapper_src: \"rust/aid_bindings.h\",\n    header_libs: [\"libcutils_headers\"],\n    visibility: [\n        \"//system/bpf/loader\",\n    ],\n}\n\ncc_defaults {\n    name: \"libcutils_defaults\",\n    cflags: [\n        \"-Wno-exit-time-destructors\",\n    ],\n\n    product_available: true,\n    ramdisk_available: true,\n    recovery_available: true,\n    vendor_available: true,\n    vendor_ramdisk_available: true,\n\n    host_supported: true,\n    native_bridge_supported: true,\n\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n}\n\ncc_library_headers {\n    name: \"libcutils_headers\",\n    defaults: [\"libcutils_defaults\"],\n\n    export_include_dirs: [\"include\"],\n    header_libs: [\"libprocessgroup_headers\"],\n    export_header_lib_headers: [\"libprocessgroup_headers\"],\n    target: {\n        vendor: {\n            override_export_include_dirs: [\"include_outside_system\"],\n        },\n        product: {\n            override_export_include_dirs: [\"include_outside_system\"],\n        },\n        linux_bionic: {\n            enabled: true,\n        },\n        windows: {\n            enabled: true,\n        },\n    },\n}\n\n// Socket specific parts of libcutils that are safe to statically link into an APEX.\ncc_library {\n    name: \"libcutils_sockets\",\n    defaults: [\"libcutils_defaults\"],\n\n    export_include_dirs: [\"include\"],\n\n    shared_libs: [\"liblog\"],\n    srcs: [\"sockets.cpp\"],\n    target: {\n        linux_bionic: {\n            enabled: true,\n        },\n\n        not_windows: {\n            srcs: [\n                \"socket_inaddr_any_server_unix.cpp\",\n                \"socket_local_client_unix.cpp\",\n                \"socket_local_server_unix.cpp\",\n                \"socket_network_client_unix.cpp\",\n                \"sockets_unix.cpp\",\n            ],\n        },\n\n        // \"not_windows\" means \"non-Windows host\".\n        android: {\n            srcs: [\n                \"android_get_control_file.cpp\",\n                \"socket_inaddr_any_server_unix.cpp\",\n                \"socket_local_client_unix.cpp\",\n                \"socket_local_server_unix.cpp\",\n                \"socket_network_client_unix.cpp\",\n                \"sockets_unix.cpp\",\n            ],\n            static_libs: [\"libbase\"],\n        },\n\n        windows: {\n            host_ldlibs: [\"-lws2_32\"],\n            srcs: [\n                \"socket_inaddr_any_server_windows.cpp\",\n                \"socket_network_client_windows.cpp\",\n                \"sockets_windows.cpp\",\n            ],\n\n            enabled: true,\n            cflags: [\n                \"-D_GNU_SOURCE\",\n            ],\n        },\n    },\n}\n\n// some files must not be compiled when building against Mingw\n// they correspond to features not used by our host development tools\n// which are also hard or even impossible to port to native Win32\nlibcutils_nonwindows_sources = [\n    \"fs.cpp\",\n    \"hashmap.cpp\",\n    \"multiuser.cpp\",\n    \"str_parms.cpp\",\n]\n\ncc_library {\n    name: \"libcutils\",\n    defaults: [\"libcutils_defaults\"],\n    double_loadable: true,\n    srcs: [\n        \"config_utils.cpp\",\n        \"iosched_policy.cpp\",\n        \"load_file.cpp\",\n        \"native_handle.cpp\",\n        \"properties.cpp\",\n        \"record_stream.cpp\",\n        \"strlcpy.c\",\n    ],\n\n    target: {\n        linux_bionic: {\n            enabled: true,\n            static_libs: [\n                \"libasync_safe\",\n            ],\n        },\n        linux: {\n            srcs: [\n                \"canned_fs_config.cpp\",\n                \"fs_config.cpp\",\n            ],\n        },\n        host: {\n            srcs: [\n                \"trace-host.cpp\",\n                \"ashmem-host.cpp\",\n            ],\n        },\n        not_windows: {\n            srcs: libcutils_nonwindows_sources,\n        },\n        windows: {\n            enabled: true,\n            host_ldlibs: [\"-lws2_32\"],\n        },\n        android: {\n            sanitize: {\n                misc_undefined: [\"integer\"],\n            },\n            static_libs: [\n                \"libasync_safe\",\n            ],\n            srcs: libcutils_nonwindows_sources + [\n                \"android_reboot.cpp\",\n                \"ashmem-dev.cpp\",\n                \"klog.cpp\",\n                \"partition_utils.cpp\",\n                \"qtaguid.cpp\",\n                \"trace-dev.cpp\",\n                \"uevent.cpp\",\n            ],\n        },\n\n        // qtaguid.cpp loads libnetd_client.so with dlopen().  Since\n        // the interface of libnetd_client.so may vary between AOSP\n        // releases, exclude qtaguid.cpp from the VNDK-SP variant.\n        vendor: {\n            exclude_srcs: [\n                \"qtaguid.cpp\",\n            ],\n            header_abi_checker: {\n                enabled: true,\n                ref_dump_dirs: [\"abi-dumps\"],\n            },\n        },\n        product: {\n            exclude_srcs: [\n                \"qtaguid.cpp\",\n            ],\n            header_abi_checker: {\n                enabled: true,\n                ref_dump_dirs: [\"abi-dumps\"],\n            },\n        },\n    },\n\n    whole_static_libs: [\"libcutils_sockets\"],\n    shared_libs: [\n        \"liblog\",\n        \"libbase\",\n    ],\n    header_libs: [\n        \"libbase_headers\",\n        \"libcutils_headers\",\n        \"libprocessgroup_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libcutils_headers\",\n        \"libprocessgroup_headers\",\n    ],\n    local_include_dirs: [\"include\"],\n\n    cflags: [\n        \"-Werror\",\n        \"-Wall\",\n        \"-Wextra\",\n    ],\n}\n\ncc_defaults {\n    name: \"libcutils_test_default\",\n    srcs: [\n        \"ashmem_base_test.cpp\",\n        \"native_handle_test.cpp\",\n        \"properties_test.cpp\",\n        \"sockets_test.cpp\",\n    ],\n\n    target: {\n        android: {\n            srcs: [\n                \"android_get_control_file_test.cpp\",\n                \"android_get_control_socket_test.cpp\",\n                \"ashmem_test.cpp\",\n                \"fs_config_test.cpp\",\n                \"multiuser_test.cpp\",\n                \"sched_policy_test.cpp\",\n                \"str_parms_test.cpp\",\n                \"trace-dev_test.cpp\",\n            ],\n        },\n\n        not_windows: {\n            srcs: [\n                \"str_parms_test.cpp\",\n            ],\n        },\n    },\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n}\n\nalways_static_test_libraries = [\n    \"libjsoncpp\",\n]\n\ntest_libraries = [\n    \"libcutils\",\n    \"liblog\",\n    \"libbase\",\n    \"libprocessgroup\",\n]\n\ncc_test {\n    name: \"libcutils_test\",\n    test_suites: [\"device-tests\"],\n    defaults: [\"libcutils_test_default\"],\n    host_supported: true,\n    shared_libs: test_libraries,\n    static_libs: always_static_test_libraries,\n    require_root: true,\n}\n\ncc_defaults {\n    name: \"libcutils_test_static_defaults\",\n    defaults: [\"libcutils_test_default\"],\n    stl: \"libc++_static\",\n    require_root: true,\n\n    target: {\n        android: {\n            static_executable: true,\n            static_libs: [\n                \"libprocessgroup_util\",\n            ] + test_libraries + always_static_test_libraries,\n        },\n        not_windows: {\n            static_libs: test_libraries + always_static_test_libraries,\n        },\n        windows: {\n            static_libs: [\n                \"libbase\",\n                \"libcutils\",\n                \"libcutils_sockets\",\n            ],\n            host_ldlibs: [\"-lws2_32\"],\n            enabled: true,\n        },\n    },\n}\n\ncc_test {\n    name: \"libcutils_test_static\",\n    host_supported: true,\n    test_suites: [\"device-tests\"],\n    defaults: [\"libcutils_test_static_defaults\"],\n}\n\ncc_test {\n    name: \"KernelLibcutilsTest\",\n    test_suites: [\n        \"general-tests\",\n        \"vts\",\n    ],\n    defaults: [\"libcutils_test_static_defaults\"],\n    test_config: \"KernelLibcutilsTest.xml\",\n}\n"
  },
  {
    "path": "libcutils/KernelLibcutilsTest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2020 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Runs KernelLibcutilsTest.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-native\" />\n\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\">\n    </target_preparer>\n\n    <target_preparer class=\"com.android.tradefed.targetprep.PushFilePreparer\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"KernelLibcutilsTest->/data/local/tests/unrestricted/KernelLibcutilsTest\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        <option name=\"native-test-device-path\" value=\"/data/local/tests/unrestricted\" />\n        <option name=\"module-name\" value=\"KernelLibcutilsTest\" />\n        <option name=\"include-filter\" value=\"*AshmemTest*\" />\n    </test>\n</configuration>\n"
  },
  {
    "path": "libcutils/MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "libcutils/NOTICE",
    "content": "\n   Copyright (c) 2005-2008, The Android Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "libcutils/OWNERS",
    "content": "# Bug component: 128577\ninclude platform/system/core:/janitors/OWNERS\n"
  },
  {
    "path": "libcutils/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"libcutils_test\"\n    }\n  ],\n  \"hwasan-presubmit\": [\n    {\n      \"name\": \"libcutils_test\"\n    }\n  ],\n  \"kernel-presubmit\": [\n    {\n      \"name\": \"libcutils_test\"\n    },\n    {\n      \"name\": \"KernelLibcutilsTest\"\n    }\n  ]\n}\n"
  },
  {
    "path": "libcutils/abi-dumps/arm64/source-based/libcutils.so.lsdump",
    "content": "{\n \"array_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIA0_i\",\n   \"name\" : \"int[0]\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  }\n ],\n \"builtin_types\" :\n [\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIa\",\n   \"name\" : \"signed char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIb\",\n   \"name\" : \"bool\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIc\",\n   \"name\" : \"char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIf\",\n   \"name\" : \"float\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIh\",\n   \"name\" : \"unsigned char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIi\",\n   \"name\" : \"int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIj\",\n   \"name\" : \"unsigned int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIl\",\n   \"name\" : \"long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIm\",\n   \"name\" : \"unsigned long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 16,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIo\",\n   \"name\" : \"unsigned __int128\",\n   \"size\" : 16\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIs\",\n   \"name\" : \"short\",\n   \"size\" : 2\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIt\",\n   \"name\" : \"unsigned short\",\n   \"size\" : 2\n  },\n  {\n   \"linker_set_key\" : \"_ZTIv\",\n   \"name\" : \"void\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIx\",\n   \"name\" : \"long long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIy\",\n   \"name\" : \"unsigned long long\",\n   \"size\" : 8\n  }\n ],\n \"elf_functions\" :\n [\n  {\n   \"name\" : \"_Z23socket_make_sockaddr_unPKciP11sockaddr_unPj\"\n  },\n  {\n   \"name\" : \"android_get_control_file\"\n  },\n  {\n   \"name\" : \"android_get_control_socket\"\n  },\n  {\n   \"name\" : \"android_get_ioprio\"\n  },\n  {\n   \"name\" : \"android_reboot\"\n  },\n  {\n   \"name\" : \"android_set_ioprio\"\n  },\n  {\n   \"name\" : \"ashmem_create_region\"\n  },\n  {\n   \"name\" : \"ashmem_get_size_region\"\n  },\n  {\n   \"name\" : \"ashmem_pin_region\"\n  },\n  {\n   \"name\" : \"ashmem_set_prot_region\"\n  },\n  {\n   \"name\" : \"ashmem_unpin_region\"\n  },\n  {\n   \"name\" : \"ashmem_valid\"\n  },\n  {\n   \"name\" : \"atrace_async_begin_body\"\n  },\n  {\n   \"name\" : \"atrace_async_end_body\"\n  },\n  {\n   \"name\" : \"atrace_async_for_track_begin_body\"\n  },\n  {\n   \"name\" : \"atrace_async_for_track_end_body\"\n  },\n  {\n   \"name\" : \"atrace_begin_body\"\n  },\n  {\n   \"name\" : \"atrace_end_body\"\n  },\n  {\n   \"name\" : \"atrace_get_enabled_tags\"\n  },\n  {\n   \"name\" : \"atrace_init\"\n  },\n  {\n   \"name\" : \"atrace_instant_body\"\n  },\n  {\n   \"name\" : \"atrace_instant_for_track_body\"\n  },\n  {\n   \"name\" : \"atrace_int64_body\"\n  },\n  {\n   \"name\" : \"atrace_int_body\"\n  },\n  {\n   \"name\" : \"atrace_set_tracing_enabled\"\n  },\n  {\n   \"name\" : \"atrace_setup\"\n  },\n  {\n   \"name\" : \"atrace_update_tags\"\n  },\n  {\n   \"name\" : \"canned_fs_config\"\n  },\n  {\n   \"name\" : \"config_bool\"\n  },\n  {\n   \"name\" : \"config_find\"\n  },\n  {\n   \"name\" : \"config_free\"\n  },\n  {\n   \"name\" : \"config_load\"\n  },\n  {\n   \"name\" : \"config_load_file\"\n  },\n  {\n   \"name\" : \"config_node\"\n  },\n  {\n   \"name\" : \"config_set\"\n  },\n  {\n   \"name\" : \"config_str\"\n  },\n  {\n   \"name\" : \"fs_config\"\n  },\n  {\n   \"name\" : \"fs_mkdirs\"\n  },\n  {\n   \"name\" : \"fs_prepare_dir\"\n  },\n  {\n   \"name\" : \"fs_prepare_dir_strict\"\n  },\n  {\n   \"name\" : \"fs_prepare_file_strict\"\n  },\n  {\n   \"name\" : \"fs_read_atomic_int\"\n  },\n  {\n   \"name\" : \"fs_write_atomic_int\"\n  },\n  {\n   \"name\" : \"get_fs_config\"\n  },\n  {\n   \"name\" : \"hashmapCreate\"\n  },\n  {\n   \"name\" : \"hashmapForEach\"\n  },\n  {\n   \"name\" : \"hashmapFree\"\n  },\n  {\n   \"name\" : \"hashmapGet\"\n  },\n  {\n   \"name\" : \"hashmapHash\"\n  },\n  {\n   \"name\" : \"hashmapLock\"\n  },\n  {\n   \"name\" : \"hashmapPut\"\n  },\n  {\n   \"name\" : \"hashmapRemove\"\n  },\n  {\n   \"name\" : \"hashmapUnlock\"\n  },\n  {\n   \"name\" : \"klog_set_level\"\n  },\n  {\n   \"name\" : \"klog_write\"\n  },\n  {\n   \"name\" : \"klog_writev\"\n  },\n  {\n   \"name\" : \"load_canned_fs_config\"\n  },\n  {\n   \"name\" : \"load_file\"\n  },\n  {\n   \"name\" : \"multiuser_convert_sdk_sandbox_to_app_uid\"\n  },\n  {\n   \"name\" : \"multiuser_get_app_id\"\n  },\n  {\n   \"name\" : \"multiuser_get_cache_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_ext_cache_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_ext_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_sdk_sandbox_uid\"\n  },\n  {\n   \"name\" : \"multiuser_get_shared_app_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_shared_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_uid\"\n  },\n  {\n   \"name\" : \"multiuser_get_user_id\"\n  },\n  {\n   \"name\" : \"native_handle_clone\"\n  },\n  {\n   \"name\" : \"native_handle_close\"\n  },\n  {\n   \"name\" : \"native_handle_close_with_tag\"\n  },\n  {\n   \"name\" : \"native_handle_create\"\n  },\n  {\n   \"name\" : \"native_handle_delete\"\n  },\n  {\n   \"name\" : \"native_handle_init\"\n  },\n  {\n   \"name\" : \"native_handle_set_fdsan_tag\"\n  },\n  {\n   \"name\" : \"native_handle_unset_fdsan_tag\"\n  },\n  {\n   \"name\" : \"partition_wiped\"\n  },\n  {\n   \"name\" : \"property_get\"\n  },\n  {\n   \"name\" : \"property_get_bool\"\n  },\n  {\n   \"name\" : \"property_get_int32\"\n  },\n  {\n   \"name\" : \"property_get_int64\"\n  },\n  {\n   \"name\" : \"property_list\"\n  },\n  {\n   \"name\" : \"property_set\"\n  },\n  {\n   \"name\" : \"record_stream_free\"\n  },\n  {\n   \"name\" : \"record_stream_get_next\"\n  },\n  {\n   \"name\" : \"record_stream_new\"\n  },\n  {\n   \"name\" : \"socket_close\"\n  },\n  {\n   \"name\" : \"socket_get_local_port\"\n  },\n  {\n   \"name\" : \"socket_inaddr_any_server\"\n  },\n  {\n   \"name\" : \"socket_local_client\"\n  },\n  {\n   \"name\" : \"socket_local_client_connect\"\n  },\n  {\n   \"name\" : \"socket_local_server\"\n  },\n  {\n   \"name\" : \"socket_local_server_bind\"\n  },\n  {\n   \"name\" : \"socket_network_client\"\n  },\n  {\n   \"name\" : \"socket_network_client_timeout\"\n  },\n  {\n   \"name\" : \"socket_send_buffers\"\n  },\n  {\n   \"name\" : \"str_parms_add_float\"\n  },\n  {\n   \"name\" : \"str_parms_add_int\"\n  },\n  {\n   \"name\" : \"str_parms_add_str\"\n  },\n  {\n   \"name\" : \"str_parms_create\"\n  },\n  {\n   \"name\" : \"str_parms_create_str\"\n  },\n  {\n   \"name\" : \"str_parms_del\"\n  },\n  {\n   \"name\" : \"str_parms_destroy\"\n  },\n  {\n   \"name\" : \"str_parms_dump\"\n  },\n  {\n   \"name\" : \"str_parms_get_float\"\n  },\n  {\n   \"name\" : \"str_parms_get_int\"\n  },\n  {\n   \"name\" : \"str_parms_get_str\"\n  },\n  {\n   \"name\" : \"str_parms_has_key\"\n  },\n  {\n   \"name\" : \"str_parms_to_str\"\n  },\n  {\n   \"name\" : \"uevent_bind\"\n  },\n  {\n   \"name\" : \"uevent_create_socket\"\n  },\n  {\n   \"name\" : \"uevent_kernel_multicast_recv\"\n  },\n  {\n   \"name\" : \"uevent_kernel_multicast_uid_recv\"\n  },\n  {\n   \"name\" : \"uevent_kernel_recv\"\n  },\n  {\n   \"name\" : \"uevent_open_socket\"\n  }\n ],\n \"elf_objects\" :\n [\n  {\n   \"name\" : \"atrace_enabled_tags\"\n  },\n  {\n   \"name\" : \"atrace_is_ready\"\n  },\n  {\n   \"name\" : \"atrace_marker_fd\"\n  }\n ],\n \"enum_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"IoSchedClass_NONE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"IoSchedClass_RT\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"IoSchedClass_BE\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"IoSchedClass_IDLE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI12IoSchedClass\",\n   \"name\" : \"IoSchedClass\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  }\n ],\n \"function_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFbPvS_E\",\n   \"name\" : \"bool (void *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFbPvS_S_E\",\n   \"name\" : \"bool (void *, void *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPvE\",\n   \"name\" : \"int (void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFvPKcS0_PvE\",\n   \"name\" : \"void (const char *, const char *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  }\n ],\n \"functions\" :\n [\n  {\n   \"function_name\" : \"android_get_control_file\",\n   \"linker_set_key\" : \"android_get_control_file\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/android_get_control_file.h\"\n  },\n  {\n   \"function_name\" : \"android_get_control_socket\",\n   \"linker_set_key\" : \"android_get_control_socket\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"android_get_ioprio\",\n   \"linker_set_key\" : \"android_get_ioprio\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP12IoSchedClass\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"function_name\" : \"android_reboot\",\n   \"linker_set_key\" : \"android_reboot\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/android_reboot.h\"\n  },\n  {\n   \"function_name\" : \"android_set_ioprio\",\n   \"linker_set_key\" : \"android_set_ioprio\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTI12IoSchedClass\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_create_region\",\n   \"linker_set_key\" : \"ashmem_create_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_get_size_region\",\n   \"linker_set_key\" : \"ashmem_get_size_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_pin_region\",\n   \"linker_set_key\" : \"ashmem_pin_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_set_prot_region\",\n   \"linker_set_key\" : \"ashmem_set_prot_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_unpin_region\",\n   \"linker_set_key\" : \"ashmem_unpin_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_valid\",\n   \"linker_set_key\" : \"ashmem_valid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_begin_body\",\n   \"linker_set_key\" : \"atrace_async_begin_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_end_body\",\n   \"linker_set_key\" : \"atrace_async_end_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_for_track_begin_body\",\n   \"linker_set_key\" : \"atrace_async_for_track_begin_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_for_track_end_body\",\n   \"linker_set_key\" : \"atrace_async_for_track_end_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_begin_body\",\n   \"linker_set_key\" : \"atrace_begin_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_end_body\",\n   \"linker_set_key\" : \"atrace_end_body\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_get_enabled_tags\",\n   \"linker_set_key\" : \"atrace_get_enabled_tags\",\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_init\",\n   \"linker_set_key\" : \"atrace_init\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_instant_body\",\n   \"linker_set_key\" : \"atrace_instant_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_instant_for_track_body\",\n   \"linker_set_key\" : \"atrace_instant_for_track_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_int64_body\",\n   \"linker_set_key\" : \"atrace_int64_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIl\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_int_body\",\n   \"linker_set_key\" : \"atrace_int_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_set_tracing_enabled\",\n   \"linker_set_key\" : \"atrace_set_tracing_enabled\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_setup\",\n   \"linker_set_key\" : \"atrace_setup\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_update_tags\",\n   \"linker_set_key\" : \"atrace_update_tags\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"canned_fs_config\",\n   \"linker_set_key\" : \"canned_fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/private/canned_fs_config.h\"\n  },\n  {\n   \"function_name\" : \"config_bool\",\n   \"linker_set_key\" : \"config_bool\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_find\",\n   \"linker_set_key\" : \"config_find\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP5cnode\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_free\",\n   \"linker_set_key\" : \"config_free\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_load\",\n   \"linker_set_key\" : \"config_load\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_load_file\",\n   \"linker_set_key\" : \"config_load_file\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_node\",\n   \"linker_set_key\" : \"config_node\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP5cnode\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_set\",\n   \"linker_set_key\" : \"config_set\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_str\",\n   \"linker_set_key\" : \"config_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPKc\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"fs_config\",\n   \"linker_set_key\" : \"fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  },\n  {\n   \"function_name\" : \"fs_mkdirs\",\n   \"linker_set_key\" : \"fs_mkdirs\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_prepare_dir\",\n   \"linker_set_key\" : \"fs_prepare_dir\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_prepare_dir_strict\",\n   \"linker_set_key\" : \"fs_prepare_dir_strict\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_prepare_file_strict\",\n   \"linker_set_key\" : \"fs_prepare_file_strict\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_read_atomic_int\",\n   \"linker_set_key\" : \"fs_read_atomic_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_write_atomic_int\",\n   \"linker_set_key\" : \"fs_write_atomic_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"get_fs_config\",\n   \"linker_set_key\" : \"get_fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP9fs_config\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  },\n  {\n   \"function_name\" : \"hashmapCreate\",\n   \"linker_set_key\" : \"hashmapCreate\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFbPvS_E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP7Hashmap\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapForEach\",\n   \"linker_set_key\" : \"hashmapForEach\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFbPvS_S_E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapFree\",\n   \"linker_set_key\" : \"hashmapFree\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapGet\",\n   \"linker_set_key\" : \"hashmapGet\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapHash\",\n   \"linker_set_key\" : \"hashmapHash\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapLock\",\n   \"linker_set_key\" : \"hashmapLock\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapPut\",\n   \"linker_set_key\" : \"hashmapPut\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapRemove\",\n   \"linker_set_key\" : \"hashmapRemove\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapUnlock\",\n   \"linker_set_key\" : \"hashmapUnlock\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"klog_set_level\",\n   \"linker_set_key\" : \"klog_set_level\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"function_name\" : \"klog_write\",\n   \"linker_set_key\" : \"klog_write\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"function_name\" : \"klog_writev\",\n   \"linker_set_key\" : \"klog_writev\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPK5iovec\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"function_name\" : \"load_canned_fs_config\",\n   \"linker_set_key\" : \"load_canned_fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/private/canned_fs_config.h\"\n  },\n  {\n   \"function_name\" : \"load_file\",\n   \"linker_set_key\" : \"load_file\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/misc.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_convert_sdk_sandbox_to_app_uid\",\n   \"linker_set_key\" : \"multiuser_convert_sdk_sandbox_to_app_uid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_app_id\",\n   \"linker_set_key\" : \"multiuser_get_app_id\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_cache_gid\",\n   \"linker_set_key\" : \"multiuser_get_cache_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_ext_cache_gid\",\n   \"linker_set_key\" : \"multiuser_get_ext_cache_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_ext_gid\",\n   \"linker_set_key\" : \"multiuser_get_ext_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_sdk_sandbox_uid\",\n   \"linker_set_key\" : \"multiuser_get_sdk_sandbox_uid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_shared_app_gid\",\n   \"linker_set_key\" : \"multiuser_get_shared_app_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_shared_gid\",\n   \"linker_set_key\" : \"multiuser_get_shared_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_uid\",\n   \"linker_set_key\" : \"multiuser_get_uid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_user_id\",\n   \"linker_set_key\" : \"multiuser_get_user_id\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_clone\",\n   \"linker_set_key\" : \"native_handle_clone\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP13native_handle\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_close\",\n   \"linker_set_key\" : \"native_handle_close\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_close_with_tag\",\n   \"linker_set_key\" : \"native_handle_close_with_tag\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_create\",\n   \"linker_set_key\" : \"native_handle_create\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP13native_handle\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_delete\",\n   \"linker_set_key\" : \"native_handle_delete\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_init\",\n   \"linker_set_key\" : \"native_handle_init\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP13native_handle\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_set_fdsan_tag\",\n   \"linker_set_key\" : \"native_handle_set_fdsan_tag\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_unset_fdsan_tag\",\n   \"linker_set_key\" : \"native_handle_unset_fdsan_tag\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"partition_wiped\",\n   \"linker_set_key\" : \"partition_wiped\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/partition_utils.h\"\n  },\n  {\n   \"function_name\" : \"property_get\",\n   \"linker_set_key\" : \"property_get\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_get_bool\",\n   \"linker_set_key\" : \"property_get_bool\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIa\"\n    }\n   ],\n   \"return_type\" : \"_ZTIa\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_get_int32\",\n   \"linker_set_key\" : \"property_get_int32\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_get_int64\",\n   \"linker_set_key\" : \"property_get_int64\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIl\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_list\",\n   \"linker_set_key\" : \"property_list\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFvPKcS0_PvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_set\",\n   \"linker_set_key\" : \"property_set\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"record_stream_free\",\n   \"linker_set_key\" : \"record_stream_free\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP12RecordStream\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"function_name\" : \"record_stream_get_next\",\n   \"linker_set_key\" : \"record_stream_get_next\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP12RecordStream\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"function_name\" : \"record_stream_new\",\n   \"linker_set_key\" : \"record_stream_new\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP12RecordStream\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"function_name\" : \"socket_close\",\n   \"linker_set_key\" : \"socket_close\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_get_local_port\",\n   \"linker_set_key\" : \"socket_get_local_port\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_inaddr_any_server\",\n   \"linker_set_key\" : \"socket_inaddr_any_server\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_client\",\n   \"linker_set_key\" : \"socket_local_client\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_client_connect\",\n   \"linker_set_key\" : \"socket_local_client_connect\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_server\",\n   \"linker_set_key\" : \"socket_local_server\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_server_bind\",\n   \"linker_set_key\" : \"socket_local_server_bind\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_network_client\",\n   \"linker_set_key\" : \"socket_network_client\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_network_client_timeout\",\n   \"linker_set_key\" : \"socket_network_client_timeout\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_send_buffers\",\n   \"linker_set_key\" : \"socket_send_buffers\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPK22cutils_socket_buffer_t\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_add_float\",\n   \"linker_set_key\" : \"str_parms_add_float\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_add_int\",\n   \"linker_set_key\" : \"str_parms_add_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_add_str\",\n   \"linker_set_key\" : \"str_parms_add_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_create\",\n   \"linker_set_key\" : \"str_parms_create\",\n   \"return_type\" : \"_ZTIP9str_parms\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_create_str\",\n   \"linker_set_key\" : \"str_parms_create_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP9str_parms\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_del\",\n   \"linker_set_key\" : \"str_parms_del\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_destroy\",\n   \"linker_set_key\" : \"str_parms_destroy\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_dump\",\n   \"linker_set_key\" : \"str_parms_dump\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_get_float\",\n   \"linker_set_key\" : \"str_parms_get_float\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPf\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_get_int\",\n   \"linker_set_key\" : \"str_parms_get_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_get_str\",\n   \"linker_set_key\" : \"str_parms_get_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_has_key\",\n   \"linker_set_key\" : \"str_parms_has_key\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_to_str\",\n   \"linker_set_key\" : \"str_parms_to_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPc\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"uevent_bind\",\n   \"linker_set_key\" : \"uevent_bind\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_create_socket\",\n   \"linker_set_key\" : \"uevent_create_socket\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_kernel_multicast_recv\",\n   \"linker_set_key\" : \"uevent_kernel_multicast_recv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_kernel_multicast_uid_recv\",\n   \"linker_set_key\" : \"uevent_kernel_multicast_uid_recv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_kernel_recv\",\n   \"linker_set_key\" : \"uevent_kernel_recv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_open_socket\",\n   \"linker_set_key\" : \"uevent_open_socket\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  }\n ],\n \"global_vars\" :\n [\n  {\n   \"linker_set_key\" : \"atrace_enabled_tags\",\n   \"name\" : \"atrace_enabled_tags\",\n   \"referenced_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"linker_set_key\" : \"atrace_is_ready\",\n   \"name\" : \"atrace_is_ready\",\n   \"referenced_type\" : \"_ZTINSt3__16atomicIbEE\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"linker_set_key\" : \"atrace_marker_fd\",\n   \"name\" : \"atrace_marker_fd\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  }\n ],\n \"lvalue_reference_types\" : [],\n \"pointer_types\" :\n [\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP12IoSchedClass\",\n   \"name\" : \"IoSchedClass *\",\n   \"referenced_type\" : \"_ZTI12IoSchedClass\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP12RecordStream\",\n   \"name\" : \"RecordStream *\",\n   \"referenced_type\" : \"_ZTI12RecordStream\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP13native_handle\",\n   \"name\" : \"native_handle *\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP5cnode\",\n   \"name\" : \"cnode *\",\n   \"referenced_type\" : \"_ZTI5cnode\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP7Hashmap\",\n   \"name\" : \"Hashmap *\",\n   \"referenced_type\" : \"_ZTI7Hashmap\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP9fs_config\",\n   \"name\" : \"fs_config *\",\n   \"referenced_type\" : \"_ZTI9fs_config\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP9str_parms\",\n   \"name\" : \"str_parms *\",\n   \"referenced_type\" : \"_ZTI9str_parms\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFbPvS_E\",\n   \"name\" : \"bool (*)(void *, void *)\",\n   \"referenced_type\" : \"_ZTIFbPvS_E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFbPvS_S_E\",\n   \"name\" : \"bool (*)(void *, void *, void *)\",\n   \"referenced_type\" : \"_ZTIFbPvS_S_E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFiPvE\",\n   \"name\" : \"int (*)(void *)\",\n   \"referenced_type\" : \"_ZTIFiPvE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFvPKcS0_PvE\",\n   \"name\" : \"void (*)(const char *, const char *, void *)\",\n   \"referenced_type\" : \"_ZTIFvPKcS0_PvE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPK13native_handle\",\n   \"name\" : \"const native_handle *\",\n   \"referenced_type\" : \"_ZTIK13native_handle\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPK22cutils_socket_buffer_t\",\n   \"name\" : \"const cutils_socket_buffer_t *\",\n   \"referenced_type\" : \"_ZTIK22cutils_socket_buffer_t\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPK5iovec\",\n   \"name\" : \"const iovec *\",\n   \"referenced_type\" : \"_ZTIK5iovec\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKc\",\n   \"name\" : \"const char *\",\n   \"referenced_type\" : \"_ZTIKc\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKv\",\n   \"name\" : \"const void *\",\n   \"referenced_type\" : \"_ZTIKv\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPPv\",\n   \"name\" : \"void **\",\n   \"referenced_type\" : \"_ZTIPv\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPc\",\n   \"name\" : \"char *\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPf\",\n   \"name\" : \"float *\",\n   \"referenced_type\" : \"_ZTIf\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPi\",\n   \"name\" : \"int *\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPj\",\n   \"name\" : \"unsigned int *\",\n   \"referenced_type\" : \"_ZTIj\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/misc.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPm\",\n   \"name\" : \"unsigned long *\",\n   \"referenced_type\" : \"_ZTIm\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPv\",\n   \"name\" : \"void *\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/misc.h\"\n  }\n ],\n \"qualified_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK13native_handle\",\n   \"name\" : \"const native_handle\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK22cutils_socket_buffer_t\",\n   \"name\" : \"const cutils_socket_buffer_t\",\n   \"referenced_type\" : \"_ZTI22cutils_socket_buffer_t\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK5iovec\",\n   \"name\" : \"const iovec\",\n   \"referenced_type\" : \"_ZTI5iovec\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKc\",\n   \"name\" : \"const char\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKv\",\n   \"name\" : \"const void\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  }\n ],\n \"record_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"version\",\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numFds\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numInts\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIA0_i\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13native_handle\",\n   \"name\" : \"native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"data\",\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"field_name\" : \"length\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI22cutils_socket_buffer_t\",\n   \"name\" : \"cutils_socket_buffer_t\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"next\",\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"field_name\" : \"first_child\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"field_name\" : \"last_child\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"field_name\" : \"name\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"field_name\" : \"value\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI5cnode\",\n   \"name\" : \"cnode\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"uid\",\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"gid\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"mode\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"capabilities\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI9fs_config\",\n   \"name\" : \"fs_config\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  }\n ],\n \"rvalue_reference_types\" : []\n}\n"
  },
  {
    "path": "libcutils/abi-dumps/arm_arm64/source-based/libcutils.so.lsdump",
    "content": "{\n \"array_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIA0_i\",\n   \"name\" : \"int[0]\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  }\n ],\n \"builtin_types\" :\n [\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIa\",\n   \"name\" : \"signed char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIb\",\n   \"name\" : \"bool\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIc\",\n   \"name\" : \"char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIf\",\n   \"name\" : \"float\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIh\",\n   \"name\" : \"unsigned char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIi\",\n   \"name\" : \"int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIj\",\n   \"name\" : \"unsigned int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIl\",\n   \"name\" : \"long\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIm\",\n   \"name\" : \"unsigned long\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIs\",\n   \"name\" : \"short\",\n   \"size\" : 2\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIt\",\n   \"name\" : \"unsigned short\",\n   \"size\" : 2\n  },\n  {\n   \"linker_set_key\" : \"_ZTIv\",\n   \"name\" : \"void\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIx\",\n   \"name\" : \"long long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIy\",\n   \"name\" : \"unsigned long long\",\n   \"size\" : 8\n  }\n ],\n \"elf_functions\" :\n [\n  {\n   \"name\" : \"_Z23socket_make_sockaddr_unPKciP11sockaddr_unPi\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android4base4TrimIRNSt3__112basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEEEES8_OT_\"\n  },\n  {\n   \"name\" : \"android_get_control_file\"\n  },\n  {\n   \"name\" : \"android_get_control_socket\"\n  },\n  {\n   \"name\" : \"android_get_ioprio\"\n  },\n  {\n   \"name\" : \"android_reboot\"\n  },\n  {\n   \"name\" : \"android_set_ioprio\"\n  },\n  {\n   \"name\" : \"ashmem_create_region\"\n  },\n  {\n   \"name\" : \"ashmem_get_size_region\"\n  },\n  {\n   \"name\" : \"ashmem_pin_region\"\n  },\n  {\n   \"name\" : \"ashmem_set_prot_region\"\n  },\n  {\n   \"name\" : \"ashmem_unpin_region\"\n  },\n  {\n   \"name\" : \"ashmem_valid\"\n  },\n  {\n   \"name\" : \"atrace_async_begin_body\"\n  },\n  {\n   \"name\" : \"atrace_async_end_body\"\n  },\n  {\n   \"name\" : \"atrace_async_for_track_begin_body\"\n  },\n  {\n   \"name\" : \"atrace_async_for_track_end_body\"\n  },\n  {\n   \"name\" : \"atrace_begin_body\"\n  },\n  {\n   \"name\" : \"atrace_end_body\"\n  },\n  {\n   \"name\" : \"atrace_get_enabled_tags\"\n  },\n  {\n   \"name\" : \"atrace_init\"\n  },\n  {\n   \"name\" : \"atrace_instant_body\"\n  },\n  {\n   \"name\" : \"atrace_instant_for_track_body\"\n  },\n  {\n   \"name\" : \"atrace_int64_body\"\n  },\n  {\n   \"name\" : \"atrace_int_body\"\n  },\n  {\n   \"name\" : \"atrace_set_tracing_enabled\"\n  },\n  {\n   \"name\" : \"atrace_setup\"\n  },\n  {\n   \"name\" : \"atrace_update_tags\"\n  },\n  {\n   \"name\" : \"canned_fs_config\"\n  },\n  {\n   \"name\" : \"config_bool\"\n  },\n  {\n   \"name\" : \"config_find\"\n  },\n  {\n   \"name\" : \"config_free\"\n  },\n  {\n   \"name\" : \"config_load\"\n  },\n  {\n   \"name\" : \"config_load_file\"\n  },\n  {\n   \"name\" : \"config_node\"\n  },\n  {\n   \"name\" : \"config_set\"\n  },\n  {\n   \"name\" : \"config_str\"\n  },\n  {\n   \"name\" : \"fs_config\"\n  },\n  {\n   \"name\" : \"fs_mkdirs\"\n  },\n  {\n   \"name\" : \"fs_prepare_dir\"\n  },\n  {\n   \"name\" : \"fs_prepare_dir_strict\"\n  },\n  {\n   \"name\" : \"fs_prepare_file_strict\"\n  },\n  {\n   \"name\" : \"fs_read_atomic_int\"\n  },\n  {\n   \"name\" : \"fs_write_atomic_int\"\n  },\n  {\n   \"name\" : \"get_fs_config\"\n  },\n  {\n   \"name\" : \"hashmapCreate\"\n  },\n  {\n   \"name\" : \"hashmapForEach\"\n  },\n  {\n   \"name\" : \"hashmapFree\"\n  },\n  {\n   \"name\" : \"hashmapGet\"\n  },\n  {\n   \"name\" : \"hashmapHash\"\n  },\n  {\n   \"name\" : \"hashmapLock\"\n  },\n  {\n   \"name\" : \"hashmapPut\"\n  },\n  {\n   \"name\" : \"hashmapRemove\"\n  },\n  {\n   \"name\" : \"hashmapUnlock\"\n  },\n  {\n   \"name\" : \"klog_set_level\"\n  },\n  {\n   \"name\" : \"klog_write\"\n  },\n  {\n   \"name\" : \"klog_writev\"\n  },\n  {\n   \"name\" : \"load_canned_fs_config\"\n  },\n  {\n   \"name\" : \"load_file\"\n  },\n  {\n   \"name\" : \"multiuser_convert_sdk_sandbox_to_app_uid\"\n  },\n  {\n   \"name\" : \"multiuser_get_app_id\"\n  },\n  {\n   \"name\" : \"multiuser_get_cache_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_ext_cache_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_ext_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_sdk_sandbox_uid\"\n  },\n  {\n   \"name\" : \"multiuser_get_shared_app_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_shared_gid\"\n  },\n  {\n   \"name\" : \"multiuser_get_uid\"\n  },\n  {\n   \"name\" : \"multiuser_get_user_id\"\n  },\n  {\n   \"name\" : \"native_handle_clone\"\n  },\n  {\n   \"name\" : \"native_handle_close\"\n  },\n  {\n   \"name\" : \"native_handle_close_with_tag\"\n  },\n  {\n   \"name\" : \"native_handle_create\"\n  },\n  {\n   \"name\" : \"native_handle_delete\"\n  },\n  {\n   \"name\" : \"native_handle_init\"\n  },\n  {\n   \"name\" : \"native_handle_set_fdsan_tag\"\n  },\n  {\n   \"name\" : \"native_handle_unset_fdsan_tag\"\n  },\n  {\n   \"name\" : \"partition_wiped\"\n  },\n  {\n   \"name\" : \"property_get\"\n  },\n  {\n   \"name\" : \"property_get_bool\"\n  },\n  {\n   \"name\" : \"property_get_int32\"\n  },\n  {\n   \"name\" : \"property_get_int64\"\n  },\n  {\n   \"name\" : \"property_list\"\n  },\n  {\n   \"name\" : \"property_set\"\n  },\n  {\n   \"name\" : \"record_stream_free\"\n  },\n  {\n   \"name\" : \"record_stream_get_next\"\n  },\n  {\n   \"name\" : \"record_stream_new\"\n  },\n  {\n   \"name\" : \"socket_close\"\n  },\n  {\n   \"name\" : \"socket_get_local_port\"\n  },\n  {\n   \"name\" : \"socket_inaddr_any_server\"\n  },\n  {\n   \"name\" : \"socket_local_client\"\n  },\n  {\n   \"name\" : \"socket_local_client_connect\"\n  },\n  {\n   \"name\" : \"socket_local_server\"\n  },\n  {\n   \"name\" : \"socket_local_server_bind\"\n  },\n  {\n   \"name\" : \"socket_network_client\"\n  },\n  {\n   \"name\" : \"socket_network_client_timeout\"\n  },\n  {\n   \"name\" : \"socket_send_buffers\"\n  },\n  {\n   \"name\" : \"str_parms_add_float\"\n  },\n  {\n   \"name\" : \"str_parms_add_int\"\n  },\n  {\n   \"name\" : \"str_parms_add_str\"\n  },\n  {\n   \"name\" : \"str_parms_create\"\n  },\n  {\n   \"name\" : \"str_parms_create_str\"\n  },\n  {\n   \"name\" : \"str_parms_del\"\n  },\n  {\n   \"name\" : \"str_parms_destroy\"\n  },\n  {\n   \"name\" : \"str_parms_dump\"\n  },\n  {\n   \"name\" : \"str_parms_get_float\"\n  },\n  {\n   \"name\" : \"str_parms_get_int\"\n  },\n  {\n   \"name\" : \"str_parms_get_str\"\n  },\n  {\n   \"name\" : \"str_parms_has_key\"\n  },\n  {\n   \"name\" : \"str_parms_to_str\"\n  },\n  {\n   \"name\" : \"uevent_bind\"\n  },\n  {\n   \"name\" : \"uevent_create_socket\"\n  },\n  {\n   \"name\" : \"uevent_kernel_multicast_recv\"\n  },\n  {\n   \"name\" : \"uevent_kernel_multicast_uid_recv\"\n  },\n  {\n   \"name\" : \"uevent_kernel_recv\"\n  },\n  {\n   \"name\" : \"uevent_open_socket\"\n  }\n ],\n \"elf_objects\" :\n [\n  {\n   \"name\" : \"atrace_enabled_tags\"\n  },\n  {\n   \"name\" : \"atrace_is_ready\"\n  },\n  {\n   \"name\" : \"atrace_marker_fd\"\n  }\n ],\n \"enum_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"IoSchedClass_NONE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"IoSchedClass_RT\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"IoSchedClass_BE\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"IoSchedClass_IDLE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI12IoSchedClass\",\n   \"name\" : \"IoSchedClass\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  }\n ],\n \"function_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFbPvS_E\",\n   \"name\" : \"bool (void *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFbPvS_S_E\",\n   \"name\" : \"bool (void *, void *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPvE\",\n   \"name\" : \"int (void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFvPKcS0_PvE\",\n   \"name\" : \"void (const char *, const char *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  }\n ],\n \"functions\" :\n [\n  {\n   \"function_name\" : \"android_get_control_file\",\n   \"linker_set_key\" : \"android_get_control_file\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/android_get_control_file.h\"\n  },\n  {\n   \"function_name\" : \"android_get_control_socket\",\n   \"linker_set_key\" : \"android_get_control_socket\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"android_get_ioprio\",\n   \"linker_set_key\" : \"android_get_ioprio\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP12IoSchedClass\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"function_name\" : \"android_reboot\",\n   \"linker_set_key\" : \"android_reboot\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/android_reboot.h\"\n  },\n  {\n   \"function_name\" : \"android_set_ioprio\",\n   \"linker_set_key\" : \"android_set_ioprio\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTI12IoSchedClass\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_create_region\",\n   \"linker_set_key\" : \"ashmem_create_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_get_size_region\",\n   \"linker_set_key\" : \"ashmem_get_size_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_pin_region\",\n   \"linker_set_key\" : \"ashmem_pin_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_set_prot_region\",\n   \"linker_set_key\" : \"ashmem_set_prot_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_unpin_region\",\n   \"linker_set_key\" : \"ashmem_unpin_region\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"ashmem_valid\",\n   \"linker_set_key\" : \"ashmem_valid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/ashmem.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_begin_body\",\n   \"linker_set_key\" : \"atrace_async_begin_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_end_body\",\n   \"linker_set_key\" : \"atrace_async_end_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_for_track_begin_body\",\n   \"linker_set_key\" : \"atrace_async_for_track_begin_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_async_for_track_end_body\",\n   \"linker_set_key\" : \"atrace_async_for_track_end_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_begin_body\",\n   \"linker_set_key\" : \"atrace_begin_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_end_body\",\n   \"linker_set_key\" : \"atrace_end_body\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_get_enabled_tags\",\n   \"linker_set_key\" : \"atrace_get_enabled_tags\",\n   \"return_type\" : \"_ZTIy\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_init\",\n   \"linker_set_key\" : \"atrace_init\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_instant_body\",\n   \"linker_set_key\" : \"atrace_instant_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_instant_for_track_body\",\n   \"linker_set_key\" : \"atrace_instant_for_track_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_int64_body\",\n   \"linker_set_key\" : \"atrace_int64_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIx\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_int_body\",\n   \"linker_set_key\" : \"atrace_int_body\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_set_tracing_enabled\",\n   \"linker_set_key\" : \"atrace_set_tracing_enabled\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_setup\",\n   \"linker_set_key\" : \"atrace_setup\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"atrace_update_tags\",\n   \"linker_set_key\" : \"atrace_update_tags\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"function_name\" : \"canned_fs_config\",\n   \"linker_set_key\" : \"canned_fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPy\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/private/canned_fs_config.h\"\n  },\n  {\n   \"function_name\" : \"config_bool\",\n   \"linker_set_key\" : \"config_bool\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_find\",\n   \"linker_set_key\" : \"config_find\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP5cnode\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_free\",\n   \"linker_set_key\" : \"config_free\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_load\",\n   \"linker_set_key\" : \"config_load\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_load_file\",\n   \"linker_set_key\" : \"config_load_file\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_node\",\n   \"linker_set_key\" : \"config_node\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP5cnode\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_set\",\n   \"linker_set_key\" : \"config_set\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"config_str\",\n   \"linker_set_key\" : \"config_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPKc\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"function_name\" : \"fs_config\",\n   \"linker_set_key\" : \"fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPy\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  },\n  {\n   \"function_name\" : \"fs_mkdirs\",\n   \"linker_set_key\" : \"fs_mkdirs\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIt\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_prepare_dir\",\n   \"linker_set_key\" : \"fs_prepare_dir\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_prepare_dir_strict\",\n   \"linker_set_key\" : \"fs_prepare_dir_strict\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_prepare_file_strict\",\n   \"linker_set_key\" : \"fs_prepare_file_strict\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_read_atomic_int\",\n   \"linker_set_key\" : \"fs_read_atomic_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"fs_write_atomic_int\",\n   \"linker_set_key\" : \"fs_write_atomic_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/fs.h\"\n  },\n  {\n   \"function_name\" : \"get_fs_config\",\n   \"linker_set_key\" : \"get_fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP9fs_config\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  },\n  {\n   \"function_name\" : \"hashmapCreate\",\n   \"linker_set_key\" : \"hashmapCreate\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFbPvS_E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP7Hashmap\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapForEach\",\n   \"linker_set_key\" : \"hashmapForEach\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFbPvS_S_E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapFree\",\n   \"linker_set_key\" : \"hashmapFree\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapGet\",\n   \"linker_set_key\" : \"hashmapGet\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapHash\",\n   \"linker_set_key\" : \"hashmapHash\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapLock\",\n   \"linker_set_key\" : \"hashmapLock\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapPut\",\n   \"linker_set_key\" : \"hashmapPut\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapRemove\",\n   \"linker_set_key\" : \"hashmapRemove\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"hashmapUnlock\",\n   \"linker_set_key\" : \"hashmapUnlock\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP7Hashmap\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"function_name\" : \"klog_set_level\",\n   \"linker_set_key\" : \"klog_set_level\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"function_name\" : \"klog_write\",\n   \"linker_set_key\" : \"klog_write\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"function_name\" : \"klog_writev\",\n   \"linker_set_key\" : \"klog_writev\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPK5iovec\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"function_name\" : \"load_canned_fs_config\",\n   \"linker_set_key\" : \"load_canned_fs_config\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/private/canned_fs_config.h\"\n  },\n  {\n   \"function_name\" : \"load_file\",\n   \"linker_set_key\" : \"load_file\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/misc.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_convert_sdk_sandbox_to_app_uid\",\n   \"linker_set_key\" : \"multiuser_convert_sdk_sandbox_to_app_uid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_app_id\",\n   \"linker_set_key\" : \"multiuser_get_app_id\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_cache_gid\",\n   \"linker_set_key\" : \"multiuser_get_cache_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_ext_cache_gid\",\n   \"linker_set_key\" : \"multiuser_get_ext_cache_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_ext_gid\",\n   \"linker_set_key\" : \"multiuser_get_ext_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_sdk_sandbox_uid\",\n   \"linker_set_key\" : \"multiuser_get_sdk_sandbox_uid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_shared_app_gid\",\n   \"linker_set_key\" : \"multiuser_get_shared_app_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_shared_gid\",\n   \"linker_set_key\" : \"multiuser_get_shared_gid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_uid\",\n   \"linker_set_key\" : \"multiuser_get_uid\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"multiuser_get_user_id\",\n   \"linker_set_key\" : \"multiuser_get_user_id\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/multiuser.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_clone\",\n   \"linker_set_key\" : \"native_handle_clone\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP13native_handle\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_close\",\n   \"linker_set_key\" : \"native_handle_close\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_close_with_tag\",\n   \"linker_set_key\" : \"native_handle_close_with_tag\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_create\",\n   \"linker_set_key\" : \"native_handle_create\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP13native_handle\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_delete\",\n   \"linker_set_key\" : \"native_handle_delete\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_init\",\n   \"linker_set_key\" : \"native_handle_init\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP13native_handle\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_set_fdsan_tag\",\n   \"linker_set_key\" : \"native_handle_set_fdsan_tag\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"native_handle_unset_fdsan_tag\",\n   \"linker_set_key\" : \"native_handle_unset_fdsan_tag\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPK13native_handle\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"function_name\" : \"partition_wiped\",\n   \"linker_set_key\" : \"partition_wiped\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/partition_utils.h\"\n  },\n  {\n   \"function_name\" : \"property_get\",\n   \"linker_set_key\" : \"property_get\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_get_bool\",\n   \"linker_set_key\" : \"property_get_bool\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIa\"\n    }\n   ],\n   \"return_type\" : \"_ZTIa\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_get_int32\",\n   \"linker_set_key\" : \"property_get_int32\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_get_int64\",\n   \"linker_set_key\" : \"property_get_int64\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIx\"\n    }\n   ],\n   \"return_type\" : \"_ZTIx\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_list\",\n   \"linker_set_key\" : \"property_list\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFvPKcS0_PvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"property_set\",\n   \"linker_set_key\" : \"property_set\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"function_name\" : \"record_stream_free\",\n   \"linker_set_key\" : \"record_stream_free\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP12RecordStream\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"function_name\" : \"record_stream_get_next\",\n   \"linker_set_key\" : \"record_stream_get_next\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP12RecordStream\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"function_name\" : \"record_stream_new\",\n   \"linker_set_key\" : \"record_stream_new\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP12RecordStream\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"function_name\" : \"socket_close\",\n   \"linker_set_key\" : \"socket_close\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_get_local_port\",\n   \"linker_set_key\" : \"socket_get_local_port\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_inaddr_any_server\",\n   \"linker_set_key\" : \"socket_inaddr_any_server\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_client\",\n   \"linker_set_key\" : \"socket_local_client\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_client_connect\",\n   \"linker_set_key\" : \"socket_local_client_connect\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_server\",\n   \"linker_set_key\" : \"socket_local_server\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_local_server_bind\",\n   \"linker_set_key\" : \"socket_local_server_bind\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_network_client\",\n   \"linker_set_key\" : \"socket_network_client\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_network_client_timeout\",\n   \"linker_set_key\" : \"socket_network_client_timeout\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"socket_send_buffers\",\n   \"linker_set_key\" : \"socket_send_buffers\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPK22cutils_socket_buffer_t\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_add_float\",\n   \"linker_set_key\" : \"str_parms_add_float\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_add_int\",\n   \"linker_set_key\" : \"str_parms_add_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_add_str\",\n   \"linker_set_key\" : \"str_parms_add_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_create\",\n   \"linker_set_key\" : \"str_parms_create\",\n   \"return_type\" : \"_ZTIP9str_parms\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_create_str\",\n   \"linker_set_key\" : \"str_parms_create_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIP9str_parms\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_del\",\n   \"linker_set_key\" : \"str_parms_del\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_destroy\",\n   \"linker_set_key\" : \"str_parms_destroy\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_dump\",\n   \"linker_set_key\" : \"str_parms_dump\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_get_float\",\n   \"linker_set_key\" : \"str_parms_get_float\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPf\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_get_int\",\n   \"linker_set_key\" : \"str_parms_get_int\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_get_str\",\n   \"linker_set_key\" : \"str_parms_get_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_has_key\",\n   \"linker_set_key\" : \"str_parms_has_key\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"str_parms_to_str\",\n   \"linker_set_key\" : \"str_parms_to_str\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP9str_parms\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPc\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"function_name\" : \"uevent_bind\",\n   \"linker_set_key\" : \"uevent_bind\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_create_socket\",\n   \"linker_set_key\" : \"uevent_create_socket\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_kernel_multicast_recv\",\n   \"linker_set_key\" : \"uevent_kernel_multicast_recv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_kernel_multicast_uid_recv\",\n   \"linker_set_key\" : \"uevent_kernel_multicast_uid_recv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_kernel_recv\",\n   \"linker_set_key\" : \"uevent_kernel_recv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  },\n  {\n   \"function_name\" : \"uevent_open_socket\",\n   \"linker_set_key\" : \"uevent_open_socket\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/uevent.h\"\n  }\n ],\n \"global_vars\" :\n [\n  {\n   \"linker_set_key\" : \"atrace_enabled_tags\",\n   \"name\" : \"atrace_enabled_tags\",\n   \"referenced_type\" : \"_ZTIy\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"linker_set_key\" : \"atrace_is_ready\",\n   \"name\" : \"atrace_is_ready\",\n   \"referenced_type\" : \"_ZTINSt3__16atomicIbEE\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  },\n  {\n   \"linker_set_key\" : \"atrace_marker_fd\",\n   \"name\" : \"atrace_marker_fd\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/trace.h\"\n  }\n ],\n \"lvalue_reference_types\" : [],\n \"pointer_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP12IoSchedClass\",\n   \"name\" : \"IoSchedClass *\",\n   \"referenced_type\" : \"_ZTI12IoSchedClass\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP12RecordStream\",\n   \"name\" : \"RecordStream *\",\n   \"referenced_type\" : \"_ZTI12RecordStream\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP13native_handle\",\n   \"name\" : \"native_handle *\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP5cnode\",\n   \"name\" : \"cnode *\",\n   \"referenced_type\" : \"_ZTI5cnode\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP7Hashmap\",\n   \"name\" : \"Hashmap *\",\n   \"referenced_type\" : \"_ZTI7Hashmap\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP9fs_config\",\n   \"name\" : \"fs_config *\",\n   \"referenced_type\" : \"_ZTI9fs_config\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP9str_parms\",\n   \"name\" : \"str_parms *\",\n   \"referenced_type\" : \"_ZTI9str_parms\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFbPvS_E\",\n   \"name\" : \"bool (*)(void *, void *)\",\n   \"referenced_type\" : \"_ZTIFbPvS_E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFbPvS_S_E\",\n   \"name\" : \"bool (*)(void *, void *, void *)\",\n   \"referenced_type\" : \"_ZTIFbPvS_S_E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFiPvE\",\n   \"name\" : \"int (*)(void *)\",\n   \"referenced_type\" : \"_ZTIFiPvE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/hashmap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFvPKcS0_PvE\",\n   \"name\" : \"void (*)(const char *, const char *, void *)\",\n   \"referenced_type\" : \"_ZTIFvPKcS0_PvE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/properties.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPK13native_handle\",\n   \"name\" : \"const native_handle *\",\n   \"referenced_type\" : \"_ZTIK13native_handle\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPK22cutils_socket_buffer_t\",\n   \"name\" : \"const cutils_socket_buffer_t *\",\n   \"referenced_type\" : \"_ZTIK22cutils_socket_buffer_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPK5iovec\",\n   \"name\" : \"const iovec *\",\n   \"referenced_type\" : \"_ZTIK5iovec\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKc\",\n   \"name\" : \"const char *\",\n   \"referenced_type\" : \"_ZTIKc\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKv\",\n   \"name\" : \"const void *\",\n   \"referenced_type\" : \"_ZTIKv\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPPv\",\n   \"name\" : \"void **\",\n   \"referenced_type\" : \"_ZTIPv\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/record_stream.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPc\",\n   \"name\" : \"char *\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPf\",\n   \"name\" : \"float *\",\n   \"referenced_type\" : \"_ZTIf\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/str_parms.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPi\",\n   \"name\" : \"int *\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/iosched_policy.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPj\",\n   \"name\" : \"unsigned int *\",\n   \"referenced_type\" : \"_ZTIj\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/misc.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPv\",\n   \"name\" : \"void *\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/cutils/misc.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPy\",\n   \"name\" : \"unsigned long long *\",\n   \"referenced_type\" : \"_ZTIy\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libcutils/include/private/canned_fs_config.h\"\n  }\n ],\n \"qualified_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK13native_handle\",\n   \"name\" : \"const native_handle\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK22cutils_socket_buffer_t\",\n   \"name\" : \"const cutils_socket_buffer_t\",\n   \"referenced_type\" : \"_ZTI22cutils_socket_buffer_t\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK5iovec\",\n   \"name\" : \"const iovec\",\n   \"referenced_type\" : \"_ZTI5iovec\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/klog.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKc\",\n   \"name\" : \"const char\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKv\",\n   \"name\" : \"const void\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  }\n ],\n \"record_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"version\",\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numFds\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numInts\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIA0_i\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13native_handle\",\n   \"name\" : \"native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libcutils/include/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"data\",\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"field_name\" : \"length\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI22cutils_socket_buffer_t\",\n   \"name\" : \"cutils_socket_buffer_t\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libcutils/include/cutils/sockets.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"next\",\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"field_name\" : \"first_child\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"field_name\" : \"last_child\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIP5cnode\"\n    },\n    {\n     \"field_name\" : \"name\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"field_name\" : \"value\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI5cnode\",\n   \"name\" : \"cnode\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libcutils/include/cutils/config_utils.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"uid\",\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"gid\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"mode\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"field_name\" : \"capabilities\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIy\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI9fs_config\",\n   \"name\" : \"fs_config\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libcutils/include/private/fs_config.h\"\n  }\n ],\n \"rvalue_reference_types\" : []\n}\n"
  },
  {
    "path": "libcutils/android_get_control_env.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\nint __android_get_control_from_env(const char* prefix, const char* name)\n        __attribute__((visibility(\"hidden\")));\n\n__END_DECLS\n"
  },
  {
    "path": "libcutils/android_get_control_file.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <cutils/android_get_control_file.h>\n\n#include <ctype.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n\n#include \"android_get_control_env.h\"\n\nint __android_get_control_from_env(const char* prefix, const char* name) {\n    if (!prefix || !name) return -1;\n\n    char *key = NULL;\n    if (asprintf(&key, \"%s%s\", prefix, name) < 0) return -1;\n    if (!key) return -1;\n\n    char *cp = key;\n    while (*cp) {\n        if (!isalnum(*cp)) *cp = '_';\n        ++cp;\n    }\n\n    const char* val = getenv(key);\n    free(key);\n    if (!val) return -1;\n\n    errno = 0;\n    long fd = strtol(val, NULL, 10);\n    if (errno) return -1;\n\n    // Since we are inheriting an fd, it could legitimately exceed _SC_OPEN_MAX\n    if ((fd < 0) || (fd > INT_MAX)) return -1;\n\n    // Still open?\n    if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;\n\n    return static_cast<int>(fd);\n}\n\nint android_get_control_file(const char* path) {\n    std::string given_path;\n    if (!android::base::Realpath(path, &given_path)) return -1;\n\n    // Try path, then realpath(path), as keys to get the fd from env.\n    auto fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, path);\n    if (fd < 0) {\n        fd = __android_get_control_from_env(ANDROID_FILE_ENV_PREFIX, given_path.c_str());\n        if (fd < 0) return fd;\n    }\n\n    // Find file path from /proc and make sure it is correct\n    auto proc = android::base::StringPrintf(\"/proc/self/fd/%d\", fd);\n    std::string fd_path;\n    if (!android::base::Realpath(proc, &fd_path)) return -1;\n\n    if (given_path != fd_path) return -1;\n    // It is what we think it is\n\n    return fd;\n}\n"
  },
  {
    "path": "libcutils/android_get_control_file_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <ctype.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/types.h>\n#include <time.h>\n\n#include <algorithm>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <cutils/android_get_control_file.h>\n#include <gtest/gtest.h>\n\nTEST(FilesTest, android_get_control_file) {\n    TemporaryFile tf;\n    ASSERT_GE(tf.fd, 0);\n\n    std::string key(ANDROID_FILE_ENV_PREFIX);\n    key += tf.path;\n\n    std::for_each(key.begin(), key.end(), [] (char& c) { c = isalnum(c) ? c : '_'; });\n\n    EXPECT_EQ(unsetenv(key.c_str()), 0);\n    EXPECT_EQ(android_get_control_file(tf.path), -1);\n\n    EXPECT_EQ(setenv(key.c_str(), android::base::StringPrintf(\"%d\", tf.fd).c_str(), true), 0);\n\n    EXPECT_EQ(android_get_control_file(tf.path), tf.fd);\n    close(tf.fd);\n    EXPECT_EQ(android_get_control_file(tf.path), -1);\n    EXPECT_EQ(unsetenv(key.c_str()), 0);\n    EXPECT_EQ(android_get_control_file(tf.path), -1);\n}\n"
  },
  {
    "path": "libcutils/android_get_control_socket_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <sys/un.h>\n#include <time.h>\n\n#include <cutils/sockets.h>\n#include <gtest/gtest.h>\n\n#ifndef SOCK_NONBLOCK\n#define SOCK_NONBLOCK 0\n#endif\n\n#ifndef SOCK_CLOEXEC\n#define SOCK_CLOEXEC 0\n#endif\n\nTEST(SocketsTest, android_get_control_socket) {\n    static const char key[] = ANDROID_SOCKET_ENV_PREFIX \"SocketsTest_android_get_control_socket\";\n    static const char* name = key + strlen(ANDROID_SOCKET_ENV_PREFIX);\n\n    EXPECT_EQ(unsetenv(key), 0);\n    EXPECT_EQ(android_get_control_socket(name), -1);\n\n    int fd;\n    ASSERT_GE(fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0), 0);\n#ifdef F_GETFL\n    int flags;\n    ASSERT_GE(flags = fcntl(fd, F_GETFL), 0);\n    ASSERT_GE(fcntl(fd, F_SETFL, flags | O_NONBLOCK), 0);\n#endif\n    EXPECT_EQ(android_get_control_socket(name), -1);\n\n    struct sockaddr_un addr;\n    memset(&addr, 0, sizeof(addr));\n    addr.sun_family = AF_UNIX;\n    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR\"/%s\", name);\n    unlink(addr.sun_path);\n\n    EXPECT_EQ(bind(fd, (struct sockaddr*)&addr, sizeof(addr)), 0);\n    EXPECT_EQ(android_get_control_socket(name), -1);\n\n    char val[32];\n    snprintf(val, sizeof(val), \"%d\", fd);\n    EXPECT_EQ(setenv(key, val, true), 0);\n\n    EXPECT_EQ(android_get_control_socket(name), fd);\n    socket_close(fd);\n    EXPECT_EQ(android_get_control_socket(name), -1);\n    EXPECT_EQ(unlink(addr.sun_path), 0);\n    EXPECT_EQ(android_get_control_socket(name), -1);\n    EXPECT_EQ(unsetenv(key), 0);\n    EXPECT_EQ(android_get_control_socket(name), -1);\n}\n"
  },
  {
    "path": "libcutils/android_reboot.cpp",
    "content": "/*\n * Copyright 2011, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/android_reboot.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include <cutils/properties.h>\n\n#define TAG \"android_reboot\"\n\nint android_reboot(unsigned cmd, int /*flags*/, const char* arg) {\n    int ret;\n    const char* restart_cmd = NULL;\n    char* prop_value;\n\n    switch (cmd) {\n        case ANDROID_RB_RESTART:  // deprecated\n        case ANDROID_RB_RESTART2:\n            restart_cmd = \"reboot\";\n            break;\n        case ANDROID_RB_POWEROFF:\n            restart_cmd = \"shutdown\";\n            break;\n        case ANDROID_RB_THERMOFF:\n            restart_cmd = \"shutdown,thermal\";\n            break;\n    }\n    if (!restart_cmd) return -1;\n    if (arg && arg[0]) {\n        ret = asprintf(&prop_value, \"%s,%s\", restart_cmd, arg);\n    } else {\n        ret = asprintf(&prop_value, \"%s\", restart_cmd);\n    }\n    if (ret < 0) return -1;\n    ret = property_set(ANDROID_RB_PROPERTY, prop_value);\n    free(prop_value);\n    return ret;\n}\n"
  },
  {
    "path": "libcutils/ashmem-dev.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/ashmem.h>\n\n/*\n * Implementation of the user-space ashmem API for devices, which have our\n * ashmem-enabled kernel. See ashmem-sim.c for the \"fake\" tmp-based version,\n * used by the simulator.\n */\n#define LOG_TAG \"ashmem\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/ashmem.h>\n#include <linux/memfd.h>\n#include <log/log.h>\n#include <pthread.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"ashmem-internal.h\"\n\n/* ashmem identity */\nstatic dev_t __ashmem_rdev;\n/*\n * If we trigger a signal handler in the middle of locked activity and the\n * signal handler calls ashmem, we could get into a deadlock state.\n */\nstatic pthread_mutex_t __ashmem_lock = PTHREAD_MUTEX_INITIALIZER;\n\n/*\n * has_memfd_support() determines if the device can use memfd. memfd support\n * has been there for long time, but certain things in it may be missing.  We\n * check for needed support in it. Also we check if the VNDK version of\n * libcutils being used is new enough, if its not, then we cannot use memfd\n * since the older copies may be using ashmem so we just use ashmem. Once all\n * Android devices that are getting updates are new enough (ex, they were\n * originally shipped with Android release > P), then we can just use memfd and\n * delete all ashmem code from libcutils (while preserving the interface).\n *\n * NOTE:\n * The sys.use_memfd property is set by default to false in Android\n * to temporarily disable memfd, till vendor and apps are ready for it.\n * The main issue: either apps or vendor processes can directly make ashmem\n * IOCTLs on FDs they receive by assuming they are ashmem, without going\n * through libcutils. Such fds could have very well be originally created with\n * libcutils hence they could be memfd. Thus the IOCTLs will break.\n *\n * Set default value of sys.use_memfd property to true once the issue is\n * resolved, so that the code can then self-detect if kernel support is present\n * on the device. The property can also set to true from adb shell, for\n * debugging.\n */\n\n/* set to true for verbose logging and other debug  */\nstatic bool debug_log = false;\n\n/* Determine if vendor processes would be ok with memfd in the system:\n *\n * Previously this function checked if memfd is supported by checking if\n * vendor VNDK version is greater than Q. As we can assume all treblelized\n * device using this code is up to date enough to use memfd, memfd is allowed\n * if the device is treblelized.\n */\nstatic bool check_vendor_memfd_allowed() {\n    static bool is_treblelized = android::base::GetBoolProperty(\"ro.treble.enabled\", false);\n\n    return is_treblelized;\n}\n\n/* Determine if memfd can be supported. This is just one-time hardwork\n * which will be cached by the caller.\n */\nstatic bool __has_memfd_support() {\n    if (check_vendor_memfd_allowed() == false) {\n        return false;\n    }\n\n    /* Used to turn on/off the detection at runtime, in the future this\n     * property will be removed once we switch everything over to ashmem.\n     * Currently it is used only for debugging to switch the system over.\n     */\n    if (!android::base::GetBoolProperty(\"sys.use_memfd\", false)) {\n        if (debug_log) {\n            ALOGD(\"sys.use_memfd=false so memfd disabled\");\n        }\n        return false;\n    }\n\n    // Check if kernel support exists, otherwise fall back to ashmem.\n    // This code needs to build on old API levels, so we can't use the libc\n    // wrapper.\n    android::base::unique_fd fd(\n            syscall(__NR_memfd_create, \"test_android_memfd\", MFD_CLOEXEC | MFD_ALLOW_SEALING));\n    if (fd == -1) {\n        ALOGE(\"memfd_create failed: %m, no memfd support\");\n        return false;\n    }\n\n    if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {\n        ALOGE(\"fcntl(F_ADD_SEALS) failed: %m, no memfd support\");\n        return false;\n    }\n\n    size_t buf_size = getpagesize();\n    if (ftruncate(fd, buf_size) == -1) {\n        ALOGE(\"ftruncate(%zd) failed to set memfd buffer size: %m, no memfd support\", buf_size);\n        return false;\n    }\n\n    /*\n     * Ensure that the kernel supports ashmem ioctl commands on memfds. If not,\n     * fall back to using ashmem.\n     */\n    int ashmem_size = TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, 0));\n    if (ashmem_size != static_cast<int>(buf_size)) {\n        ALOGE(\"ioctl(ASHMEM_GET_SIZE): %d != buf_size: %zd , no ashmem-memfd compat support\",\n              ashmem_size, buf_size);\n        return false;\n    }\n\n    if (debug_log) {\n        ALOGD(\"memfd: device has memfd support, using it\");\n    }\n    return true;\n}\n\nbool has_memfd_support() {\n    static bool memfd_supported = __has_memfd_support();\n    return memfd_supported;\n}\n\nstatic std::string get_ashmem_device_path() {\n    static const std::string boot_id_path = \"/proc/sys/kernel/random/boot_id\";\n    std::string boot_id;\n    if (!android::base::ReadFileToString(boot_id_path, &boot_id)) {\n        ALOGE(\"Failed to read %s: %m\", boot_id_path.c_str());\n        return \"\";\n    }\n    boot_id = android::base::Trim(boot_id);\n\n    return \"/dev/ashmem\" + boot_id;\n}\n\n/* logistics of getting file descriptor for ashmem */\nstatic int __ashmem_open_locked() {\n    static const std::string ashmem_device_path = get_ashmem_device_path();\n\n    if (ashmem_device_path.empty()) {\n        return -1;\n    }\n\n    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(ashmem_device_path.c_str(), O_RDWR | O_CLOEXEC)));\n    if (!fd.ok()) {\n        ALOGE(\"Unable to open ashmem device: %m\");\n        return -1;\n    }\n\n    struct stat st;\n    if (TEMP_FAILURE_RETRY(fstat(fd, &st)) == -1) {\n        return -1;\n    }\n    if (!S_ISCHR(st.st_mode) || !st.st_rdev) {\n        errno = ENOTTY;\n        return -1;\n    }\n\n    __ashmem_rdev = st.st_rdev;\n    return fd.release();\n}\n\nstatic int __ashmem_open() {\n    pthread_mutex_lock(&__ashmem_lock);\n    int fd = __ashmem_open_locked();\n    pthread_mutex_unlock(&__ashmem_lock);\n    return fd;\n}\n\n/* Make sure file descriptor references ashmem, negative number means false */\nstatic int __ashmem_is_ashmem(int fd, bool fatal) {\n    struct stat st;\n    if (fstat(fd, &st) < 0) {\n        return -1;\n    }\n\n    dev_t rdev = 0; /* Too much complexity to sniff __ashmem_rdev */\n    if (S_ISCHR(st.st_mode) && st.st_rdev) {\n        pthread_mutex_lock(&__ashmem_lock);\n        rdev = __ashmem_rdev;\n        if (rdev) {\n            pthread_mutex_unlock(&__ashmem_lock);\n        } else {\n            int fd = __ashmem_open_locked();\n            if (fd < 0) {\n                pthread_mutex_unlock(&__ashmem_lock);\n                return -1;\n            }\n            rdev = __ashmem_rdev;\n            pthread_mutex_unlock(&__ashmem_lock);\n\n            close(fd);\n        }\n\n        if (st.st_rdev == rdev) {\n            return 0;\n        }\n    }\n\n    if (fatal) {\n        if (rdev) {\n            LOG_ALWAYS_FATAL(\"illegal fd=%d mode=0%o rdev=%d:%d expected 0%o %d:%d\",\n              fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),\n              S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP,\n              major(rdev), minor(rdev));\n        } else {\n            LOG_ALWAYS_FATAL(\"illegal fd=%d mode=0%o rdev=%d:%d expected 0%o\",\n              fd, st.st_mode, major(st.st_rdev), minor(st.st_rdev),\n              S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IRGRP);\n        }\n        /* NOTREACHED */\n    }\n\n    errno = ENOTTY;\n    return -1;\n}\n\nstatic int __ashmem_check_failure(int fd, int result) {\n    if (result == -1 && errno == ENOTTY) __ashmem_is_ashmem(fd, true);\n    return result;\n}\n\nstatic bool is_ashmem_fd(int fd) {\n    static bool fd_check_error_once = false;\n\n    if (__ashmem_is_ashmem(fd, false) == 0) {\n        if (!fd_check_error_once) {\n            ALOGE(\"memfd: memfd expected but ashmem fd used - please use libcutils\");\n            fd_check_error_once = true;\n        }\n\n        return true;\n    }\n\n    return false;\n}\n\nstatic bool is_memfd_fd(int fd) {\n    return has_memfd_support() && !is_ashmem_fd(fd);\n}\n\nint ashmem_valid(int fd) {\n    if (is_memfd_fd(fd)) {\n        return 1;\n    }\n\n    return __ashmem_is_ashmem(fd, false) >= 0;\n}\n\nstatic int memfd_create_region(const char* name, size_t size) {\n    // This code needs to build on old API levels, so we can't use the libc\n    // wrapper.\n    android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_CLOEXEC | MFD_ALLOW_SEALING));\n\n    if (fd == -1) {\n        ALOGE(\"memfd_create(%s, %zd) failed: %m\", name, size);\n        return -1;\n    }\n\n    if (ftruncate(fd, size) == -1) {\n        ALOGE(\"ftruncate(%s, %zd) failed for memfd creation: %m\", name, size);\n        return -1;\n    }\n\n    // forbid size changes to match ashmem behaviour\n    if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) == -1) {\n        ALOGE(\"memfd_create(%s, %zd) F_ADD_SEALS failed: %m\", name, size);\n        return -1;\n    }\n\n    if (debug_log) {\n        ALOGE(\"memfd_create(%s, %zd) success. fd=%d\", name, size, fd.get());\n    }\n    return fd.release();\n}\n\n/*\n * ashmem_create_region - creates a new ashmem region and returns the file\n * descriptor, or <0 on error\n *\n * `name' is an optional label to give the region (visible in /proc/pid/maps)\n * `size' is the size of the region, in page-aligned bytes\n */\nint ashmem_create_region(const char* name, size_t size) {\n    if (name == NULL) name = \"none\";\n\n    if (has_memfd_support()) {\n        return memfd_create_region(name, size);\n    }\n\n    android::base::unique_fd fd(__ashmem_open());\n    if (!fd.ok() ||\n        TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_NAME, name) < 0) ||\n        TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_SIZE, size) < 0)) {\n        return -1;\n    }\n    return fd.release();\n}\n\nstatic int memfd_set_prot_region(int fd, int prot) {\n    int seals = fcntl(fd, F_GET_SEALS);\n    if (seals == -1) {\n        ALOGE(\"memfd_set_prot_region(%d, %d): F_GET_SEALS failed: %m\", fd, prot);\n        return -1;\n    }\n\n    if (prot & PROT_WRITE) {\n        /* Now we want the buffer to be read-write, let's check if the buffer\n         * has been previously marked as read-only before, if so return error\n         */\n        if (seals & F_SEAL_FUTURE_WRITE) {\n            ALOGE(\"memfd_set_prot_region(%d, %d): region is write protected\", fd, prot);\n            errno = EINVAL;  // inline with ashmem error code, if already in\n                             // read-only mode\n            return -1;\n        }\n        return 0;\n    }\n\n    /* We would only allow read-only for any future file operations */\n    if (fcntl(fd, F_ADD_SEALS, F_SEAL_FUTURE_WRITE) == -1) {\n        ALOGE(\"memfd_set_prot_region(%d, %d): F_SEAL_FUTURE_WRITE seal failed: %m\", fd, prot);\n        return -1;\n    }\n\n    return 0;\n}\n\nint ashmem_set_prot_region(int fd, int prot) {\n    if (is_memfd_fd(fd)) {\n        return memfd_set_prot_region(fd, prot);\n    }\n\n    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_SET_PROT_MASK, prot)));\n}\n\nstatic int do_pin(int op, int fd, size_t offset, size_t length) {\n    static bool already_warned_about_pin_deprecation = false;\n    if (!already_warned_about_pin_deprecation || debug_log) {\n        ALOGE(\"Pinning is deprecated since Android Q. Please use trim or other methods.\");\n        already_warned_about_pin_deprecation = true;\n    }\n\n    if (is_memfd_fd(fd)) {\n        return 0;\n    }\n\n    // TODO: should LP64 reject too-large offset/len?\n    ashmem_pin pin = { static_cast<uint32_t>(offset), static_cast<uint32_t>(length) };\n    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, op, &pin)));\n}\n\nint ashmem_pin_region(int fd, size_t offset, size_t length) {\n    return do_pin(ASHMEM_PIN, fd, offset, length);\n}\n\nint ashmem_unpin_region(int fd, size_t offset, size_t length) {\n    return do_pin(ASHMEM_UNPIN, fd, offset, length);\n}\n\nint ashmem_get_size_region(int fd) {\n    if (is_memfd_fd(fd)) {\n        struct stat sb;\n        if (fstat(fd, &sb) == -1) {\n            ALOGE(\"ashmem_get_size_region(%d): fstat failed: %m\", fd);\n            return -1;\n        }\n        return sb.st_size;\n    }\n\n    return __ashmem_check_failure(fd, TEMP_FAILURE_RETRY(ioctl(fd, ASHMEM_GET_SIZE, NULL)));\n}\n"
  },
  {
    "path": "libcutils/ashmem-host.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/ashmem.h>\n\n/*\n * Implementation of the user-space ashmem API for the simulator, which lacks an\n * ashmem-enabled kernel. See ashmem-dev.c for the real ashmem-based version.  A\n * disk-backed temp file is the best option that is consistently supported\n * across all host platforms.\n */\n\n#include <android-base/unique_fd.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <time.h>\n#include <unistd.h>\n#include <utils/Compat.h>\n#include <memory>\n\nusing android::base::unique_fd;\n\nstatic bool ashmem_validate_stat(int fd, struct stat* buf) {\n    int result = fstat(fd, buf);\n    if (result == -1) {\n        return false;\n    }\n\n    // Check if this is an ashmem region. Since there's no such thing on the host,\n    // we can't actually implement that. Check that it's at least a regular file.\n    if (!S_ISREG(buf->st_mode)) {\n        errno = ENOTTY;\n        return false;\n    }\n    // In Win32, unlike Unix, the temp file is not unlinked immediately after\n    // creation.\n#if !defined(_WIN32)\n    if (buf->st_nlink != 0) {\n        errno = ENOTTY;\n        return false;\n    }\n#endif\n    return true;\n}\n\nint ashmem_valid(int fd) {\n    struct stat buf;\n    return ashmem_validate_stat(fd, &buf);\n}\n\nint ashmem_create_region(const char* /*ignored*/, size_t size) {\n    // Files returned by tmpfile are automatically removed.\n    std::unique_ptr<FILE, decltype(&fclose)> tmp(tmpfile(), &fclose);\n\n    if (!tmp) {\n        return -1;\n    }\n    int fd = fileno(tmp.get());\n    if (fd == -1) {\n        return -1;\n    }\n    unique_fd dupfd = unique_fd(dup(fd));\n    if (dupfd == -1) {\n        return -1;\n    }\n    if (TEMP_FAILURE_RETRY(ftruncate(dupfd, size)) == -1) {\n        return -1;\n    }\n    return dupfd.release();\n}\n\nint ashmem_set_prot_region(int /*fd*/, int /*prot*/) {\n    return 0;\n}\n\nint ashmem_pin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {\n    return 0 /*ASHMEM_NOT_PURGED*/;\n}\n\nint ashmem_unpin_region(int /*fd*/, size_t /*offset*/, size_t /*len*/) {\n    return 0 /*ASHMEM_IS_UNPINNED*/;\n}\n\nint ashmem_get_size_region(int fd)\n{\n    struct stat buf;\n    if (!ashmem_validate_stat(fd, &buf)) {\n        return -1;\n    }\n\n    return buf.st_size;\n}\n"
  },
  {
    "path": "libcutils/ashmem-internal.h",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nbool has_memfd_support();\n"
  },
  {
    "path": "libcutils/ashmem_base_test.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include <algorithm>\n\n#include <unistd.h>\n\n#include <android-base/mapped_file.h>\n#include <android-base/unique_fd.h>\n#include <cutils/ashmem.h>\n\n/*\n * Tests in AshmemBaseTest are designed to run on Android as well as host\n * platforms (Linux, Mac, Windows).\n */\n\n#if defined(_WIN32)\nstatic inline size_t getpagesize() {\n    return 4096;\n}\n#endif\n\nusing android::base::unique_fd;\n\nTEST(AshmemBaseTest, BasicTest) {\n    const size_t size = getpagesize();\n    std::vector<uint8_t> data(size);\n    std::generate(data.begin(), data.end(), [n = 0]() mutable { return n++ & 0xFF; });\n\n    unique_fd fd = unique_fd(ashmem_create_region(nullptr, size));\n    ASSERT_TRUE(fd >= 0);\n    ASSERT_TRUE(ashmem_valid(fd));\n    ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));\n\n    std::unique_ptr<android::base::MappedFile> mapped =\n            android::base::MappedFile::FromFd(fd, 0, size, PROT_READ | PROT_WRITE);\n    EXPECT_TRUE(mapped.get() != nullptr);\n    void* region1 = mapped->data();\n    EXPECT_TRUE(region1 != nullptr);\n\n    memcpy(region1, data.data(), size);\n    ASSERT_EQ(0, memcmp(region1, data.data(), size));\n\n    std::unique_ptr<android::base::MappedFile> mapped2 =\n            android::base::MappedFile::FromFd(fd, 0, size, PROT_READ | PROT_WRITE);\n    EXPECT_TRUE(mapped2.get() != nullptr);\n    void* region2 = mapped2->data();\n    EXPECT_TRUE(region2 != nullptr);\n    ASSERT_EQ(0, memcmp(region2, data.data(), size));\n}\n"
  },
  {
    "path": "libcutils/ashmem_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <linux/fs.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <vector>\n\n#include <android-base/macros.h>\n#include <android-base/unique_fd.h>\n#include <cutils/ashmem.h>\n#include <gtest/gtest.h>\n\n#include \"ashmem-internal.h\"\n\nusing android::base::unique_fd;\n\nstatic void TestCreateRegion(size_t size, unique_fd &fd, int prot) {\n    fd = unique_fd(ashmem_create_region(nullptr, size));\n    ASSERT_TRUE(fd >= 0);\n    ASSERT_TRUE(ashmem_valid(fd));\n    ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));\n    ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));\n\n    // We've been inconsistent historically about whether or not these file\n    // descriptors were CLOEXEC. Make sure we're consistent going forward.\n    // https://issuetracker.google.com/165667331\n    ASSERT_EQ(FD_CLOEXEC, (fcntl(fd, F_GETFD) & FD_CLOEXEC));\n}\n\nstatic void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {\n    ASSERT_TRUE(fd >= 0);\n    ASSERT_TRUE(ashmem_valid(fd));\n    *region = mmap(nullptr, size, prot, MAP_SHARED, fd, off);\n    ASSERT_NE(MAP_FAILED, *region);\n}\n\nstatic void TestProtDenied(const unique_fd &fd, size_t size, int prot) {\n    ASSERT_TRUE(fd >= 0);\n    ASSERT_TRUE(ashmem_valid(fd));\n    EXPECT_EQ(MAP_FAILED, mmap(nullptr, size, prot, MAP_SHARED, fd, 0));\n}\n\nstatic void TestProtIs(const unique_fd& fd, int prot) {\n    ASSERT_TRUE(fd >= 0);\n    ASSERT_TRUE(ashmem_valid(fd));\n    EXPECT_EQ(prot, ioctl(fd, ASHMEM_GET_PROT_MASK));\n}\n\nstatic void FillData(std::vector<uint8_t>& data) {\n    for (size_t i = 0; i < data.size(); i++) {\n        data[i] = i & 0xFF;\n    }\n}\n\nstatic void waitForChildProcessExit(pid_t pid) {\n    int exitStatus;\n    pid_t childPid = waitpid(pid, &exitStatus, 0);\n\n    ASSERT_GT(childPid, 0);\n    ASSERT_TRUE(WIFEXITED(exitStatus));\n    ASSERT_EQ(0, WEXITSTATUS(exitStatus));\n}\n\nstatic void ForkTest(const unique_fd &fd, size_t size) {\n    void* region1 = nullptr;\n    std::vector<uint8_t> data(size);\n    FillData(data);\n\n    ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region1));\n\n    memcpy(region1, data.data(), size);\n    ASSERT_EQ(0, memcmp(region1, data.data(), size));\n    EXPECT_EQ(0, munmap(region1, size));\n\n\n    pid_t pid = fork();\n    if (!pid) {\n        if (!ashmem_valid(fd)) {\n            _exit(3);\n        }\n\n        void *region2 = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);\n        if (region2 == MAP_FAILED) {\n            _exit(1);\n        } else if (memcmp(region2, data.data(), size) != 0){\n            _exit(2);\n        }\n\n        // Clear the ashmem buffer here to ensure that updates to the contents\n        // of the buffer are visible across processes with a reference to the\n        // buffer.\n        memset(region2, 0, size);\n        munmap(region2, size);\n        _exit(0);\n    } else {\n        ASSERT_GT(pid, 0);\n        ASSERT_NO_FATAL_FAILURE(waitForChildProcessExit(pid));\n    }\n\n    memset(data.data(), 0, size);\n    void *region2;\n    ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ | PROT_WRITE, &region2));\n    ASSERT_EQ(0, memcmp(region2, data.data(), size));\n    EXPECT_EQ(0, munmap(region2, size));\n}\n\nstatic void FileOperationsTest(const unique_fd &fd, size_t size) {\n    void* region = nullptr;\n\n    const size_t pageSize = getpagesize();\n    const size_t dataSize = pageSize * 2;\n    const size_t holeSize = pageSize;\n    ASSERT_NO_FATAL_FAILURE(TestMmap(fd, dataSize, PROT_READ | PROT_WRITE, &region, holeSize));\n\n    std::vector<uint8_t> data(dataSize);\n    FillData(data);\n    memcpy(region, data.data(), dataSize);\n\n    const off_t dataStart = holeSize;\n    const off_t dataEnd = dataStart + dataSize;\n\n    // The sequence of seeks below looks something like this:\n    //\n    // [    ][data][data][    ]\n    // --^                          lseek(99, SEEK_SET)\n    //   ------^                    lseek(dataStart, SEEK_CUR)\n    // ------^                      lseek(0, SEEK_DATA)\n    //       ------------^          lseek(dataStart, SEEK_HOLE)\n    //                      ^--     lseek(-99, SEEK_END)\n    //                ^------       lseek(-dataStart, SEEK_CUR)\n    const struct {\n        // lseek() parameters\n        off_t offset;\n        int whence;\n        // Expected lseek() return value\n        off_t ret;\n    } seeks[] = {\n            {99, SEEK_SET, 99},\n            {dataStart, SEEK_CUR, dataStart + 99},\n            {0, SEEK_DATA, dataStart},\n            {dataStart, SEEK_HOLE, dataEnd},\n            {-99, SEEK_END, static_cast<off_t>(size) - 99},\n            {-dataStart, SEEK_CUR, dataEnd - 99},\n    };\n    for (const auto& cfg : seeks) {\n        errno = 0;\n        ASSERT_TRUE(ashmem_valid(fd));\n        auto off = lseek(fd, cfg.offset, cfg.whence);\n        ASSERT_EQ(cfg.ret, off) << \"lseek(\" << cfg.offset << \", \" << cfg.whence << \") failed\"\n                                << (errno ? \": \" : \"\") << (errno ? strerror(errno) : \"\");\n\n        if (off >= dataStart && off < dataEnd) {\n            off_t dataOff = off - dataStart;\n            ssize_t readSize = dataSize - dataOff;\n            uint8_t buf[readSize];\n\n            ASSERT_EQ(readSize, TEMP_FAILURE_RETRY(read(fd, buf, readSize)));\n            EXPECT_EQ(0, memcmp(buf, &data[dataOff], readSize));\n        }\n    }\n\n    EXPECT_EQ(0, munmap(region, dataSize));\n}\n\nstatic void ProtTestROBuffer(const unique_fd &fd, size_t size) {\n    void *region;\n\n    TestProtDenied(fd, size, PROT_WRITE);\n    TestProtIs(fd, PROT_READ | PROT_EXEC);\n    ASSERT_NO_FATAL_FAILURE(TestMmap(fd, size, PROT_READ, &region));\n    EXPECT_EQ(0, munmap(region, size));\n}\n\nstatic void ProtTestRWBuffer(const unique_fd &fd, size_t size) {\n    TestProtIs(fd, PROT_READ | PROT_WRITE | PROT_EXEC);\n    ASSERT_EQ(0, ashmem_set_prot_region(fd, PROT_READ | PROT_EXEC));\n    errno = 0;\n    ASSERT_EQ(-1, ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE |\n                                         PROT_EXEC))\n        << \"kernel shouldn't allow adding protection bits\";\n    EXPECT_EQ(EINVAL, errno);\n    TestProtIs(fd, PROT_READ | PROT_EXEC);\n    TestProtDenied(fd, size, PROT_WRITE);\n}\n\nstatic void ForkProtTest(const unique_fd &fd, size_t size) {\n    pid_t pid = fork();\n    if (!pid) {\n        // Change buffer mapping permissions to read-only to ensure that\n        // updates to the buffer's mapping permissions are visible across\n        // processes that reference the buffer.\n        if (!ashmem_valid(fd)) {\n            _exit(3);\n        } else if (ashmem_set_prot_region(fd, PROT_READ) == -1) {\n            _exit(1);\n        }\n        _exit(0);\n    } else {\n        ASSERT_GT(pid, 0);\n        ASSERT_NO_FATAL_FAILURE(waitForChildProcessExit(pid));\n    }\n\n    ASSERT_NO_FATAL_FAILURE(TestProtDenied(fd, size, PROT_WRITE));\n}\n\nstatic void ForkMultiRegionTest(unique_fd fds[], int nRegions, size_t size) {\n    std::vector<uint8_t> data(size);\n    FillData(data);\n\n    for (int i = 0; i < nRegions; i++) {\n        void* region = nullptr;\n        ASSERT_NO_FATAL_FAILURE(TestMmap(fds[i], size, PROT_READ | PROT_WRITE, &region));\n        memcpy(region, data.data(), size);\n        ASSERT_EQ(0, memcmp(region, data.data(), size));\n        EXPECT_EQ(0, munmap(region, size));\n    }\n\n    pid_t pid = fork();\n    if (!pid) {\n        // Clear each ashmem buffer in the context of the child process to\n        // ensure that the updates are visible to the parent process later.\n        for (int i = 0; i < nRegions; i++) {\n            if (!ashmem_valid(fds[i])) {\n                _exit(3);\n            }\n            void *region = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fds[i], 0);\n            if (region == MAP_FAILED) {\n                _exit(1);\n            }\n            if (memcmp(region, data.data(), size) != 0) {\n                munmap(region, size);\n                _exit(2);\n            }\n            memset(region, 0, size);\n            munmap(region, size);\n        }\n        _exit(0);\n    } else {\n        ASSERT_GT(pid, 0);\n        ASSERT_NO_FATAL_FAILURE(waitForChildProcessExit(pid));\n    }\n\n    memset(data.data(), 0, size);\n    for (int i = 0; i < nRegions; i++) {\n        void *region;\n        ASSERT_NO_FATAL_FAILURE(TestMmap(fds[i], size, PROT_READ | PROT_WRITE, &region));\n        ASSERT_EQ(0, memcmp(region, data.data(), size));\n        EXPECT_EQ(0, munmap(region, size));\n    }\n\n}\n\nTEST(AshmemTest, ForkTest) {\n    const size_t size = getpagesize();\n    unique_fd fd;\n\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));\n    ASSERT_NO_FATAL_FAILURE(ForkTest(fd, size));\n}\n\nTEST(AshmemTest, FileOperationsTest) {\n    const size_t pageSize = getpagesize();\n    // Allocate a 4-page buffer, but leave page-sized holes on either side in\n    // the test.\n    const size_t size = pageSize * 4;\n    unique_fd fd;\n\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));\n    ASSERT_NO_FATAL_FAILURE(FileOperationsTest(fd, size));\n}\n\nTEST(AshmemTest, ProtTest) {\n    unique_fd fd;\n    const size_t size = getpagesize();\n\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_EXEC));\n    ASSERT_NO_FATAL_FAILURE(ProtTestROBuffer(fd, size));\n\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE | PROT_EXEC));\n    ASSERT_NO_FATAL_FAILURE(ProtTestRWBuffer(fd, size));\n}\n\nTEST(AshmemTest, ForkProtTest) {\n    unique_fd fd;\n    const size_t size = getpagesize();\n\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fd, PROT_READ | PROT_WRITE));\n    ASSERT_NO_FATAL_FAILURE(ForkProtTest(fd, size));\n}\n\nTEST(AshmemTest, ForkMultiRegionTest) {\n    const size_t size = getpagesize();\n    constexpr int nRegions = 16;\n    unique_fd fds[nRegions];\n\n    for (int i = 0; i < nRegions; i++) {\n        ASSERT_NO_FATAL_FAILURE(TestCreateRegion(size, fds[i], PROT_READ | PROT_WRITE));\n    }\n\n    ASSERT_NO_FATAL_FAILURE(ForkMultiRegionTest(fds, nRegions, size));\n}\n\nclass AshmemTestMemfdAshmemCompat : public ::testing::Test {\n protected:\n  void SetUp() override {\n    if (!has_memfd_support()){\n        GTEST_SKIP() << \"No memfd support; skipping memfd-ashmem compat tests\";\n    }\n  }\n};\n\nTEST_F(AshmemTestMemfdAshmemCompat, SetNameTest) {\n    unique_fd fd;\n\n    // ioctl() should fail, since memfd names cannot be changed after the buffer has been created.\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |\n                                                                PROT_EXEC));\n    ASSERT_LT(ioctl(fd, ASHMEM_SET_NAME, \"invalid-command\"), 0);\n}\n\nTEST_F(AshmemTestMemfdAshmemCompat, GetNameTest) {\n    unique_fd fd;\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |\n                                                                PROT_EXEC));\n\n    char testBuf[ASHMEM_NAME_LEN];\n    ASSERT_EQ(0, ioctl(fd, ASHMEM_GET_NAME, &testBuf));\n    // ashmem_create_region(nullptr, ...) creates memfds with the name \"none\".\n    ASSERT_STREQ(testBuf, \"none\");\n}\n\nTEST_F(AshmemTestMemfdAshmemCompat, SetSizeTest) {\n    unique_fd fd;\n\n    // ioctl() should fail, since libcutils sets and seals the buffer size after creating it.\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |\n                                                                PROT_EXEC));\n    ASSERT_LT(ioctl(fd, ASHMEM_SET_SIZE, 2 * getpagesize()), 0);\n}\n\nTEST_F(AshmemTestMemfdAshmemCompat, GetSizeTest) {\n    unique_fd fd;\n    size_t bufSize = getpagesize();\n\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(bufSize, fd, PROT_READ | PROT_WRITE | PROT_EXEC));\n    ASSERT_EQ(static_cast<int>(bufSize), ioctl(fd, ASHMEM_GET_SIZE, 0));\n}\n\nTEST_F(AshmemTestMemfdAshmemCompat, ProtMaskTest) {\n    unique_fd fd;\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |\n                                                                PROT_EXEC));\n\n    // We can only change PROT_WRITE for memfds since memfd implements ashmem's prot_mask through\n    // file seals, and only write seals exist.\n    //\n    // All memfd files start off as being writable (i.e. PROT_WRITE is part of the prot_mask).\n    // Test to ensure that the implementation only clears the PROT_WRITE bit when requested.\n    ASSERT_EQ(0, ioctl(fd, ASHMEM_SET_PROT_MASK, PROT_READ | PROT_WRITE | PROT_EXEC));\n    int prot = ioctl(fd, ASHMEM_GET_PROT_MASK, 0);\n    ASSERT_NE(prot, -1);\n    ASSERT_TRUE(prot & PROT_WRITE) << prot;\n\n    ASSERT_EQ(0, ioctl(fd, ASHMEM_SET_PROT_MASK, PROT_READ | PROT_EXEC));\n    prot = ioctl(fd, ASHMEM_GET_PROT_MASK, 0);\n    ASSERT_NE(prot, -1);\n    ASSERT_TRUE(!(prot & PROT_WRITE)) << prot;\n\n    // The shim layer should implement clearing PROT_WRITE via file seals, so check the file\n    // seals to ensure that F_SEAL_FUTURE_WRITE is set.\n    int seals = fcntl(fd, F_GET_SEALS, 0);\n    ASSERT_NE(seals, -1);\n    ASSERT_TRUE(seals & F_SEAL_FUTURE_WRITE) << seals;\n\n    // Similarly, ensure that file seals affect prot_mask\n    unique_fd fd2;\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd2, PROT_READ | PROT_WRITE |\n                                                                PROT_EXEC));\n    ASSERT_EQ(0, fcntl(fd2, F_ADD_SEALS, F_SEAL_FUTURE_WRITE));\n    prot = ioctl(fd2, ASHMEM_GET_PROT_MASK, 0);\n    ASSERT_NE(prot, -1);\n    ASSERT_TRUE(!(prot & PROT_WRITE)) << prot;\n\n    // And finally, ensure that adding back permissions fails\n    ASSERT_LT(ioctl(fd2, ASHMEM_SET_PROT_MASK, PROT_READ | PROT_WRITE | PROT_EXEC), 0);\n}\n\nTEST_F(AshmemTestMemfdAshmemCompat, FileIDTest) {\n    unique_fd fd;\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |\n                                                                PROT_EXEC));\n\n    unsigned long ino;\n    ASSERT_EQ(0, ioctl(fd, ASHMEM_GET_FILE_ID, &ino));\n    struct stat st;\n    ASSERT_EQ(0, fstat(fd, &st));\n    ASSERT_EQ(ino, st.st_ino);\n}\n\nTEST_F(AshmemTestMemfdAshmemCompat, UnpinningTest) {\n    unique_fd fd;\n    size_t bufSize = getpagesize();\n    ASSERT_NO_FATAL_FAILURE(TestCreateRegion(getpagesize(), fd, PROT_READ | PROT_WRITE |\n                                                                PROT_EXEC));\n\n    struct ashmem_pin pin = {\n        .offset = 0,\n        .len = static_cast<uint32_t>(bufSize),\n    };\n    ASSERT_EQ(0, ioctl(fd, ASHMEM_UNPIN, &pin));\n    // ASHMEM_UNPIN should just be a nop\n    ASSERT_EQ(ASHMEM_IS_PINNED, ioctl(fd, ASHMEM_GET_PIN_STATUS, 0));\n\n    // This shouldn't do anything; when we pin the page, it shouldn't have been purged.\n    ASSERT_EQ(0, ioctl(fd, ASHMEM_PURGE_ALL_CACHES, 0));\n    ASSERT_EQ(ASHMEM_NOT_PURGED, ioctl(fd, ASHMEM_PIN, &pin));\n}"
  },
  {
    "path": "libcutils/canned_fs_config.cpp",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <private/android_filesystem_config.h>\n#include <private/canned_fs_config.h>\n#include <private/fs_config.h>\n\n#include <android-base/strings.h>\n\n#include <errno.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <algorithm>\n#include <fstream>\n#include <iostream>\n#include <string>\n#include <vector>\n\nusing android::base::ConsumePrefix;\nusing android::base::StartsWith;\nusing android::base::Tokenize;\n\nstruct Entry {\n    std::string path;\n    unsigned uid;\n    unsigned gid;\n    unsigned mode;\n    uint64_t capabilities;\n};\n\nstatic std::vector<Entry> canned_data;\n\nint load_canned_fs_config(const char* fn) {\n    std::ifstream input(fn);\n    for (std::string line; std::getline(input, line);) {\n        // Historical: the root dir can be represented as a space character.\n        // e.g. \" 1000 1000 0755\" is parsed as\n        // path = \" \", uid = 1000, gid = 1000, mode = 0755.\n        // But at the same time, we also have accepted\n        // \"/ 1000 1000 0755\".\n        if (StartsWith(line, \" \")) {\n            line.insert(line.begin(), '/');\n        }\n\n        std::vector<std::string> tokens = Tokenize(line, \" \");\n        if (tokens.size() < 4) {\n            std::cerr << \"Ill-formed line: \" << line << \" in \" << fn << std::endl;\n            return -1;\n        }\n\n        // Historical: remove the leading '/' if exists.\n        std::string path(tokens[0].front() == '/' ? std::string(tokens[0], 1) : tokens[0]);\n\n        Entry e{\n                .path = std::move(path),\n                .uid = static_cast<unsigned int>(atoi(tokens[1].c_str())),\n                .gid = static_cast<unsigned int>(atoi(tokens[2].c_str())),\n                // mode is in octal\n                .mode = static_cast<unsigned int>(strtol(tokens[3].c_str(), nullptr, 8)),\n                .capabilities = 0,\n        };\n\n        for (size_t i = 4; i < tokens.size(); i++) {\n            std::string_view sv = tokens[i];\n            if (ConsumePrefix(&sv, \"capabilities=\")) {\n                e.capabilities = strtoll(std::string(sv).c_str(), nullptr, 0);\n                break;\n            }\n            // Historical: there can be tokens like \"selabel=...\" here. They have been ignored.\n            // It's not an error because selabels are applied separately in e2fsdroid using the\n            // file_contexts files set via -S option.\n            std::cerr << \"info: ignored token \\\"\" << sv << \"\\\" in \" << fn << std::endl;\n        }\n\n        canned_data.emplace_back(std::move(e));\n    }\n\n    // Note: we used to sort the entries by path names. This was to improve the lookup performance\n    // by doing binary search. However, this is no longer the case. The lookup performance is not\n    // critical because this tool runs on the host, not on the device. Now, there can be multiple\n    // entries for the same path. Then the one that comes the last wins. This is to allow overriding\n    // platform provided fs_config with a user provided fs_config by appending the latter to the\n    // former.\n    //\n    // To implement the strategy, reverse the entries order, and search from the top.\n    std::reverse(canned_data.begin(), canned_data.end());\n\n    std::cout << \"loaded \" << canned_data.size() << \" fs_config entries\" << std::endl;\n    return 0;\n}\n\nvoid canned_fs_config(const char* path, [[maybe_unused]] int dir,\n                      [[maybe_unused]] const char* target_out_path, unsigned* uid, unsigned* gid,\n                      unsigned* mode, uint64_t* capabilities) {\n    if (path != nullptr && path[0] == '/') path++;  // canned paths lack the leading '/'\n\n    const Entry* found = nullptr;\n    // canned_data is already reversed. First match wins.\n    for (const auto& entry : canned_data) {\n        if (path == entry.path) {\n            found = &entry;\n            break;\n        }\n        continue;\n    }\n\n    if (found == nullptr) {\n        std::cerr << \"failed to find \" << path << \" in canned fs_config\" << std::endl;\n        exit(1);\n    }\n\n    *uid = found->uid;\n    *gid = found->gid;\n    *mode = found->mode;\n    *capabilities = found->capabilities;\n}\n"
  },
  {
    "path": "libcutils/config_utils.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/config_utils.h>\n\n#include <string.h>\n#include <ctype.h>\n#include <stdlib.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n#include <cutils/misc.h>\n\ncnode* config_node(const char *name, const char *value)\n{\n    cnode* node = static_cast<cnode*>(calloc(sizeof(cnode), 1));\n    if(node) {\n        node->name = name ? name : \"\";\n        node->value = value ? value : \"\";\n    }\n\n    return node;\n}\n\ncnode* config_find(cnode *root, const char *name)\n{\n    cnode *node, *match = NULL;\n\n    /* we walk the whole list, as we need to return the last (newest) entry */\n    for(node = root->first_child; node; node = node->next)\n        if(!strcmp(node->name, name))\n            match = node;\n\n    return match;\n}\n\nstatic cnode* _config_create(cnode *root, const char *name)\n{\n    cnode *node;\n\n    node = config_node(name, NULL);\n\n    if(root->last_child)\n        root->last_child->next = node;\n    else\n        root->first_child = node;\n\n    root->last_child = node;\n\n    return node;\n}\n\nint config_bool(cnode *root, const char *name, int _default)\n{\n    cnode *node;\n        \n    node = config_find(root, name);\n    if(!node)\n        return _default;\n\n    switch(node->value[0]) {\n    case 'y':\n    case 'Y':\n    case '1':\n        return 1;\n    default:\n        return 0;\n    }\n}\n\nconst char* config_str(cnode *root, const char *name, const char *_default)\n{\n    cnode *node;\n\n    node = config_find(root, name);\n    if(!node)\n        return _default;\n    return node->value;\n}\n\nvoid config_set(cnode *root, const char *name, const char *value)\n{\n    cnode *node;\n\n    node = config_find(root, name);\n    if(node)\n        node->value = value;\n    else {\n        node = _config_create(root, name);\n        node->value = value;\n    }\n}\n\n#define T_EOF 0\n#define T_TEXT 1\n#define T_DOT 2\n#define T_OBRACE 3\n#define T_CBRACE 4\n\ntypedef struct\n{\n    char *data;\n    char *text;\n    int len;\n    char next;\n} cstate;\n\nstatic int _lex(cstate *cs, int value)\n{\n    char c;\n    char *s;\n    char *data;\n\n    data = cs->data;\n\n    if(cs->next != 0) {\n        c = cs->next;\n        cs->next = 0;\n        goto got_c;\n    }\n\nrestart:\n    for(;;) {\n        c = *data++;\n    got_c:\n        if(isspace(c))\n            continue;\n\n        switch(c) {\n        case 0:\n            return T_EOF;\n\n        case '#':\n            for(;;) {\n                switch(*data) {\n                case 0:\n                    cs->data = data;\n                    return T_EOF;\n                case '\\n':\n                    cs->data = data + 1;\n                    goto restart;\n                default:\n                    data++;\n                }\n            }\n            break;\n            \n        case '.':\n            cs->data = data;\n            return T_DOT;\n\n        case '{':\n            cs->data = data;\n            return T_OBRACE;\n\n        case '}':\n            cs->data = data;\n            return T_CBRACE;\n\n        default:\n            s = data - 1;\n\n            if(value) {\n                for(;;) {\n                    if(*data == 0) {\n                        cs->data = data;\n                        break;\n                    }\n                    if(*data == '\\n') {\n                        cs->data = data + 1;\n                        *data-- = 0;\n                        break;\n                    }\n                    data++;\n                }\n\n                    /* strip trailing whitespace */\n                while(data > s){\n                    if(!isspace(*data)) break;\n                    *data-- = 0;\n                }\n\n                goto got_text;                \n            } else {\n                for(;;) {\n                    if(isspace(*data)) {\n                        *data = 0;\n                        cs->data = data + 1;\n                        goto got_text;\n                    }\n                    switch(*data) {\n                    case 0:\n                        cs->data = data;\n                        goto got_text;\n                    case '.':\n                    case '{':\n                    case '}':\n                        cs->next = *data;\n                        *data = 0;\n                        cs->data = data + 1;\n                        goto got_text;\n                    default:\n                        data++;\n                    }\n                }\n            }\n        }\n    }\n\ngot_text:\n    cs->text = s;\n    return T_TEXT;\n}\n\n#if 0\nchar *TOKENNAMES[] = { \"EOF\", \"TEXT\", \"DOT\", \"OBRACE\", \"CBRACE\" };\n\nstatic int lex(cstate *cs, int value)\n{\n    int tok = _lex(cs, value);\n    printf(\"TOKEN(%d) %s %s\\n\", value, TOKENNAMES[tok],\n           tok == T_TEXT ? cs->text : \"\");\n    return tok;\n}\n#else\n#define lex(cs,v) _lex(cs,v)\n#endif\n\nstatic int parse_expr(cstate *cs, cnode *node);\n\nstatic int parse_block(cstate *cs, cnode *node)\n{\n    for(;;){\n        switch(lex(cs, 0)){\n        case T_TEXT:\n            if(parse_expr(cs, node)) return -1;\n            continue;\n\n        case T_CBRACE:\n            return 0;\n\n        default:\n            return -1;\n        }\n    }\n}\n\nstatic int parse_expr(cstate *cs, cnode *root)\n{\n    cnode *node;\n\n        /* last token was T_TEXT */\n    node = config_find(root, cs->text);\n    if(!node || *node->value)\n        node = _config_create(root, cs->text);\n\n    for(;;) {\n        switch(lex(cs, 1)) {\n        case T_DOT:\n            if(lex(cs, 0) != T_TEXT)\n                return -1;\n            node = _config_create(node, cs->text);\n            continue;\n\n        case T_TEXT:\n            node->value = cs->text;\n            return 0;\n\n        case T_OBRACE:\n            return parse_block(cs, node);\n\n        default:\n            return -1;\n        }\n    }\n}\n\nvoid config_load(cnode *root, char *data)\n{\n    if(data != 0) {\n        cstate cs;\n        cs.data = data;\n        cs.next = 0;\n\n        for(;;) {\n            switch(lex(&cs, 0)) {\n            case T_TEXT:\n                if(parse_expr(&cs, root))\n                    return;\n                break;\n            default:\n                return;\n            }\n        }\n    }\n}\n\nvoid config_load_file(cnode *root, const char *fn)\n{\n    char* data = static_cast<char*>(load_file(fn, nullptr));\n    config_load(root, data);\n    // TODO: deliberate leak :-/\n}\n\nvoid config_free(cnode *root)\n{\n    cnode *cur = root->first_child;\n\n    while (cur) {\n        cnode *prev = cur;\n        config_free(cur);\n        cur = cur->next;\n        free(prev);\n    }\n}\n"
  },
  {
    "path": "libcutils/fs.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/fs.h>\n\n#define LOG_TAG \"cutils\"\n\n/* These defines are only needed because prebuilt headers are out of date */\n#define __USE_XOPEN2K8 1\n#define _ATFILE_SOURCE 1\n#define _GNU_SOURCE 1\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <log/log.h>\n\n#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)\n#define BUF_SIZE 64\n\nstatic int fs_prepare_path_impl(const char* path, mode_t mode, uid_t uid, gid_t gid,\n        int allow_fixup, int prepare_as_dir) {\n    // TODO: fix the goto hell below.\n    int type_ok;\n    int owner_match;\n    int mode_match;\n\n    // Check if path needs to be created\n    struct stat sb;\n    int create_result = -1;\n    if (TEMP_FAILURE_RETRY(lstat(path, &sb)) == -1) {\n        if (errno == ENOENT) {\n            goto create;\n        } else {\n            ALOGE(\"Failed to lstat(%s): %s\", path, strerror(errno));\n            return -1;\n        }\n    }\n\n    // Exists, verify status\n    type_ok = prepare_as_dir ? S_ISDIR(sb.st_mode) : S_ISREG(sb.st_mode);\n    if (!type_ok) {\n        ALOGE(\"Not a %s: %s\", (prepare_as_dir ? \"directory\" : \"regular file\"), path);\n        return -1;\n    }\n\n    owner_match = ((sb.st_uid == uid) && (sb.st_gid == gid));\n    mode_match = ((sb.st_mode & ALL_PERMS) == mode);\n    if (owner_match && mode_match) {\n        return 0;\n    } else if (allow_fixup) {\n        goto fixup;\n    } else {\n        if (!owner_match) {\n            ALOGE(\"Expected path %s with owner %d:%d but found %d:%d\",\n                    path, uid, gid, sb.st_uid, sb.st_gid);\n            return -1;\n        } else {\n            ALOGW(\"Expected path %s with mode %o but found %o\",\n                    path, mode, (sb.st_mode & ALL_PERMS));\n            return 0;\n        }\n    }\n\ncreate:\n    create_result = prepare_as_dir\n        ? TEMP_FAILURE_RETRY(mkdir(path, mode))\n        : TEMP_FAILURE_RETRY(open(path, O_CREAT | O_CLOEXEC | O_NOFOLLOW | O_RDONLY, 0644));\n    if (create_result == -1) {\n        if (errno != EEXIST) {\n            ALOGE(\"Failed to %s(%s): %s\",\n                    (prepare_as_dir ? \"mkdir\" : \"open\"), path, strerror(errno));\n            return -1;\n        }\n    } else if (!prepare_as_dir) {\n        // For regular files we need to make sure we close the descriptor\n        if (close(create_result) == -1) {\n            ALOGW(\"Failed to close file after create %s: %s\", path, strerror(errno));\n        }\n    }\nfixup:\n    if (TEMP_FAILURE_RETRY(chmod(path, mode)) == -1) {\n        ALOGE(\"Failed to chmod(%s, %d): %s\", path, mode, strerror(errno));\n        return -1;\n    }\n    if (TEMP_FAILURE_RETRY(chown(path, uid, gid)) == -1) {\n        ALOGE(\"Failed to chown(%s, %d, %d): %s\", path, uid, gid, strerror(errno));\n        return -1;\n    }\n\n    return 0;\n}\n\nint fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid) {\n    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 1, /*prepare_as_dir*/ 1);\n}\n\nint fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {\n    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 1);\n}\n\nint fs_prepare_file_strict(const char* path, mode_t mode, uid_t uid, gid_t gid) {\n    return fs_prepare_path_impl(path, mode, uid, gid, /*allow_fixup*/ 0, /*prepare_as_dir*/ 0);\n}\n\nint fs_read_atomic_int(const char* path, int* out_value) {\n    int fd = TEMP_FAILURE_RETRY(open(path, O_RDONLY));\n    if (fd == -1) {\n        ALOGE(\"Failed to read %s: %s\", path, strerror(errno));\n        return -1;\n    }\n\n    char buf[BUF_SIZE];\n    if (TEMP_FAILURE_RETRY(read(fd, buf, BUF_SIZE)) == -1) {\n        ALOGE(\"Failed to read %s: %s\", path, strerror(errno));\n        goto fail;\n    }\n    if (sscanf(buf, \"%d\", out_value) != 1) {\n        ALOGE(\"Failed to parse %s: %s\", path, strerror(errno));\n        goto fail;\n    }\n    close(fd);\n    return 0;\n\nfail:\n    close(fd);\n    *out_value = -1;\n    return -1;\n}\n\nint fs_write_atomic_int(const char* path, int value) {\n    char temp[PATH_MAX];\n    if (snprintf(temp, PATH_MAX, \"%s.XXXXXX\", path) >= PATH_MAX) {\n        ALOGE(\"Path too long\");\n        return -1;\n    }\n\n    int fd = TEMP_FAILURE_RETRY(mkstemp(temp));\n    if (fd == -1) {\n        ALOGE(\"Failed to open %s: %s\", temp, strerror(errno));\n        return -1;\n    }\n\n    char buf[BUF_SIZE];\n    int len = snprintf(buf, BUF_SIZE, \"%d\", value) + 1;\n    if (len > BUF_SIZE) {\n        ALOGE(\"Value %d too large: %s\", value, strerror(errno));\n        goto fail;\n    }\n    if (TEMP_FAILURE_RETRY(write(fd, buf, len)) < len) {\n        ALOGE(\"Failed to write %s: %s\", temp, strerror(errno));\n        goto fail;\n    }\n    if (close(fd) == -1) {\n        ALOGE(\"Failed to close %s: %s\", temp, strerror(errno));\n        goto fail_closed;\n    }\n\n    if (rename(temp, path) == -1) {\n        ALOGE(\"Failed to rename %s to %s: %s\", temp, path, strerror(errno));\n        goto fail_closed;\n    }\n\n    return 0;\n\nfail:\n    close(fd);\nfail_closed:\n    unlink(temp);\n    return -1;\n}\n\n#ifndef __APPLE__\n\nint fs_mkdirs(const char* path, mode_t mode) {\n    if (*path != '/') {\n        ALOGE(\"Relative paths are not allowed: %s\", path);\n        return -EINVAL;\n    }\n\n    int fd = open(\"/\", 0);\n    if (fd == -1) {\n        ALOGE(\"Failed to open(/): %s\", strerror(errno));\n        return -errno;\n    }\n\n    struct stat sb;\n    int res = 0;\n    char* buf = strdup(path);\n    char* segment = buf + 1;\n    char* p = segment;\n    while (*p != '\\0') {\n        if (*p == '/') {\n            *p = '\\0';\n\n            if (!strcmp(segment, \"..\") || !strcmp(segment, \".\") || !strcmp(segment, \"\")) {\n                ALOGE(\"Invalid path: %s\", buf);\n                res = -EINVAL;\n                goto done_close;\n            }\n\n            if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {\n                if (errno == ENOENT) {\n                    /* Nothing there yet; let's create it! */\n                    if (mkdirat(fd, segment, mode) != 0) {\n                        if (errno == EEXIST) {\n                            /* We raced with someone; ignore */\n                        } else {\n                            ALOGE(\"Failed to mkdirat(%s): %s\", buf, strerror(errno));\n                            res = -errno;\n                            goto done_close;\n                        }\n                    }\n                } else {\n                    ALOGE(\"Failed to fstatat(%s): %s\", buf, strerror(errno));\n                    res = -errno;\n                    goto done_close;\n                }\n            } else {\n                if (S_ISLNK(sb.st_mode)) {\n                    ALOGE(\"Symbolic links are not allowed: %s\", buf);\n                    res = -ELOOP;\n                    goto done_close;\n                }\n                if (!S_ISDIR(sb.st_mode)) {\n                    ALOGE(\"Existing segment not a directory: %s\", buf);\n                    res = -ENOTDIR;\n                    goto done_close;\n                }\n            }\n\n            /* Yay, segment is ready for us to step into */\n            int next_fd;\n            if ((next_fd = openat(fd, segment, O_NOFOLLOW | O_CLOEXEC)) == -1) {\n                ALOGE(\"Failed to openat(%s): %s\", buf, strerror(errno));\n                res = -errno;\n                goto done_close;\n            }\n\n            close(fd);\n            fd = next_fd;\n\n            *p = '/';\n            segment = p + 1;\n        }\n        p++;\n    }\n\ndone_close:\n    close(fd);\n    free(buf);\n    return res;\n}\n\n#endif\n"
  },
  {
    "path": "libcutils/fs_config.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <private/fs_config.h>\n\n// This file is used to define the properties of the filesystem\n// images generated by build tools (mkbootfs and mkyaffs2image) and\n// by the device side of adb.\n\n#define LOG_TAG \"fs_config\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <fnmatch.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <string>\n\n#include <android-base/strings.h>\n#include <cutils/fs.h>\n#include <log/log.h>\n#include <private/android_filesystem_config.h>\n\n#include \"fs_config.h\"\n\nusing android::base::EndsWith;\nusing android::base::StartsWith;\n\n#define ALIGN(x, alignment) (((x) + ((alignment)-1)) & ~((alignment)-1))\n#define CAP_MASK_LONG(cap_name) (1ULL << (cap_name))\n\n// Rules for directories.\n// These rules are applied based on \"first match\", so they\n// should start with the most specific path and work their\n// way up to the root.\n\nstatic const struct fs_path_config android_dirs[] = {\n        // clang-format off\n    { 00770, AID_SYSTEM,       AID_CACHE,        0, \"cache\" },\n    { 00555, AID_ROOT,         AID_ROOT,         0, \"config\" },\n    { 00771, AID_SYSTEM,       AID_SYSTEM,       0, \"data/app\" },\n    { 00771, AID_SYSTEM,       AID_SYSTEM,       0, \"data/app-private\" },\n    { 00771, AID_SYSTEM,       AID_SYSTEM,       0, \"data/app-ephemeral\" },\n    { 00771, AID_ROOT,         AID_ROOT,         0, \"data/dalvik-cache\" },\n    { 00771, AID_SYSTEM,       AID_SYSTEM,       0, \"data/data\" },\n    { 00771, AID_SHELL,        AID_SHELL,        0, \"data/local/tmp\" },\n    { 00771, AID_SHELL,        AID_SHELL,        0, \"data/local\" },\n    { 00770, AID_DHCP,         AID_DHCP,         0, \"data/misc/dhcp\" },\n    { 00771, AID_SHARED_RELRO, AID_SHARED_RELRO, 0, \"data/misc/shared_relro\" },\n    { 01771, AID_SYSTEM,       AID_MISC,         0, \"data/misc\" },\n    { 00775, AID_MEDIA_RW,     AID_MEDIA_RW,     0, \"data/media/Music\" },\n    { 00775, AID_MEDIA_RW,     AID_MEDIA_RW,     0, \"data/media\" },\n    { 00775, AID_ROOT,         AID_ROOT,         0, \"data/preloads\" },\n    { 00771, AID_SYSTEM,       AID_SYSTEM,       0, \"data\" },\n    { 00755, AID_ROOT,         AID_SYSTEM,       0, \"mnt\" },\n    { 00751, AID_ROOT,         AID_SHELL,        0, \"product/bin\" },\n    { 00777, AID_ROOT,         AID_ROOT,         0, \"sdcard\" },\n    { 00751, AID_ROOT,         AID_SDCARD_R,     0, \"storage\" },\n    { 00751, AID_ROOT,         AID_SHELL,        0, \"system/bin\" },\n    { 00755, AID_ROOT,         AID_ROOT,         0, \"system/etc/ppp\" },\n    { 00755, AID_ROOT,         AID_SHELL,        0, \"system/vendor\" },\n    { 00750, AID_ROOT,         AID_SHELL,        0, \"system/xbin\" },\n    { 00751, AID_ROOT,         AID_SHELL,        0, \"system_ext/bin\" },\n    { 00751, AID_ROOT,         AID_SHELL,        0, \"vendor/bin\" },\n    { 00755, AID_ROOT,         AID_SHELL,        0, \"vendor\" },\n    {},\n        // clang-format on\n};\n#ifndef __ANDROID_VNDK__\nauto __for_testing_only__android_dirs = android_dirs;\n#endif\n\n// Rules for files.\n// These rules are applied based on \"first match\", so they\n// should start with the most specific path and work their\n// way up to the root. Prefixes ending in * denotes wildcard\n// and will allow partial matches.\nstatic const char sys_conf_dir[] = \"/system/etc/fs_config_dirs\";\nstatic const char sys_conf_file[] = \"/system/etc/fs_config_files\";\n// No restrictions are placed on the vendor and oem file-system config files,\n// although the developer is advised to restrict the scope to the /vendor or\n// oem/ file-system since the intent is to provide support for customized\n// portions of a separate vendor.img or oem.img.  Has to remain open so that\n// customization can also land on /system/vendor, /system/oem, /system/odm,\n// /system/product or /system/system_ext.\n//\n// We expect build-time checking or filtering when constructing the associated\n// fs_config_* files (see build/tools/fs_config/fs_config_generate.c)\nstatic const char ven_conf_dir[] = \"/vendor/etc/fs_config_dirs\";\nstatic const char ven_conf_file[] = \"/vendor/etc/fs_config_files\";\nstatic const char oem_conf_dir[] = \"/oem/etc/fs_config_dirs\";\nstatic const char oem_conf_file[] = \"/oem/etc/fs_config_files\";\nstatic const char odm_conf_dir[] = \"/odm/etc/fs_config_dirs\";\nstatic const char odm_conf_file[] = \"/odm/etc/fs_config_files\";\nstatic const char product_conf_dir[] = \"/product/etc/fs_config_dirs\";\nstatic const char product_conf_file[] = \"/product/etc/fs_config_files\";\nstatic const char system_ext_conf_dir[] = \"/system_ext/etc/fs_config_dirs\";\nstatic const char system_ext_conf_file[] = \"/system_ext/etc/fs_config_files\";\nstatic const char* conf[][2] = {\n        {sys_conf_file, sys_conf_dir},         {ven_conf_file, ven_conf_dir},\n        {oem_conf_file, oem_conf_dir},         {odm_conf_file, odm_conf_dir},\n        {product_conf_file, product_conf_dir}, {system_ext_conf_file, system_ext_conf_dir},\n};\n\n// Do not use android_files to grant Linux capabilities.  Use ambient capabilities in their\n// associated init.rc file instead.  See https://source.android.com/devices/tech/config/ambient.\n\n// Do not place any new vendor/, data/vendor/, etc entries in android_files.\n// Vendor entries should be done via a vendor or device specific config.fs.\n// See https://source.android.com/devices/tech/config/filesystem#using-file-system-capabilities\nstatic const struct fs_path_config android_files[] = {\n        // clang-format off\n    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, \"data/app/*\" },\n    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, \"data/app-ephemeral/*\" },\n    { 00644, AID_SYSTEM,    AID_SYSTEM,    0, \"data/app-private/*\" },\n    { 00644, AID_APP,       AID_APP,       0, \"data/data/*\" },\n    { 00644, AID_MEDIA_RW,  AID_MEDIA_RW,  0, \"data/media/*\" },\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"default.prop\" }, // legacy\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"system/etc/prop.default\" },\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"odm/build.prop\" }, // legacy; only for P release\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"odm/default.prop\" }, // legacy; only for P release\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"odm/etc/build.prop\" },\n    { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_dir + 1 },\n    { 00444, AID_ROOT,      AID_ROOT,      0, odm_conf_file + 1 },\n    { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_dir + 1 },\n    { 00444, AID_ROOT,      AID_ROOT,      0, oem_conf_file + 1 },\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"product/build.prop\" },\n    { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_dir + 1 },\n    { 00444, AID_ROOT,      AID_ROOT,      0, product_conf_file + 1 },\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"system_ext/build.prop\" },\n    { 00444, AID_ROOT,      AID_ROOT,      0, system_ext_conf_dir + 1 },\n    { 00444, AID_ROOT,      AID_ROOT,      0, system_ext_conf_file + 1 },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"system/bin/crash_dump32\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"system/bin/crash_dump64\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"system/bin/debuggerd\" },\n    { 00550, AID_LOGD,      AID_LOGD,      0, \"system/bin/logd\" },\n    { 00700, AID_ROOT,      AID_ROOT,      0, \"system/bin/secilc\" },\n    { 00750, AID_ROOT,      AID_ROOT,      0, \"system/bin/uncrypt\" },\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"system/build.prop\" },\n    { 00444, AID_ROOT,      AID_ROOT,      0, sys_conf_dir + 1 },\n    { 00444, AID_ROOT,      AID_ROOT,      0, sys_conf_file + 1 },\n    { 00440, AID_ROOT,      AID_SHELL,     0, \"system/etc/init.goldfish.rc\" },\n    { 00550, AID_ROOT,      AID_SHELL,     0, \"system/etc/init.goldfish.sh\" },\n    { 00550, AID_ROOT,      AID_SHELL,     0, \"system/etc/init.ril\" },\n    { 00555, AID_ROOT,      AID_ROOT,      0, \"system/etc/ppp/*\" },\n    { 00555, AID_ROOT,      AID_ROOT,      0, \"system/etc/rc.*\" },\n    { 00750, AID_ROOT,      AID_ROOT,      0, \"vendor/bin/install-recovery.sh\" },\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"vendor/build.prop\" },\n    { 00600, AID_ROOT,      AID_ROOT,      0, \"vendor/default.prop\" },\n    { 00440, AID_ROOT,      AID_ROOT,      0, \"vendor/etc/recovery.img\" },\n    { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_dir + 1 },\n    { 00444, AID_ROOT,      AID_ROOT,      0, ven_conf_file + 1 },\n\n    // the following two files are INTENTIONALLY set-uid, but they\n    // are NOT included on user builds.\n    { 06755, AID_ROOT,      AID_ROOT,      0, \"system/xbin/procmem\" },\n    { 04750, AID_ROOT,      AID_SHELL,     0, \"system/xbin/su\" },\n\n    // the following files have enhanced capabilities and ARE included\n    // in user builds.\n    { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),\n                                              \"system/bin/inputflinger\" },\n    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |\n                                           CAP_MASK_LONG(CAP_SETGID),\n                                              \"system/bin/run-as\" },\n    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |\n                                           CAP_MASK_LONG(CAP_SETGID),\n                                              \"system/bin/simpleperf_app_runner\" },\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/e2fsck\" },\n#ifdef __LP64__\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/linker64\" },\n#else\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/linker\" },\n#endif\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/resize2fs\" },\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/snapuserd\" },\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/snapuserd_ramdisk\" },\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/tune2fs\" },\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage_ramdisk/system/bin/fsck.f2fs\" },\n    // generic defaults\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"bin/*\" },\n    { 00755, AID_ROOT,      AID_ROOT,      0, \"first_stage.sh\"},\n    { 00640, AID_ROOT,      AID_SHELL,     0, \"fstab.*\" },\n    { 00750, AID_ROOT,      AID_SHELL,     0, \"init*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"*.rc\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"odm/bin/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"odm/framework/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"odm/app/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"odm/priv-app/*\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"product/bin/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"product/framework/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"product/app/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"product/priv-app/*\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"system/bin/*\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"system/xbin/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"system/framework/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"system/app/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"system/priv-app/*\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"system_ext/bin/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"system_ext/framework/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"system_ext/app/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"system_ext/priv-app/*\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"vendor/bin/*\" },\n    { 00755, AID_ROOT,      AID_SHELL,     0, \"vendor/xbin/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"vendor/framework/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"vendor/app/*\" },\n    { 00644, AID_ROOT,      AID_ROOT,      0, \"vendor/priv-app/*\" },\n    {},\n        // clang-format on\n};\n#ifndef __ANDROID_VNDK__\nauto __for_testing_only__android_files = android_files;\n#endif\n\nstatic size_t strip(const char* path, size_t len, const char suffix[]) {\n    if (len < strlen(suffix)) return len;\n    if (strncmp(path + len - strlen(suffix), suffix, strlen(suffix))) return len;\n    return len - strlen(suffix);\n}\n\nstatic int fs_config_open(int dir, int which, const char* target_out_path) {\n    int fd = -1;\n\n    if (target_out_path && *target_out_path) {\n        // target_out_path is the path to the directory holding content of\n        // system partition but as we cannot guarantee it ends with '/system'\n        // or with or without a trailing slash, need to strip them carefully.\n        char* name = NULL;\n        size_t len = strlen(target_out_path);\n        len = strip(target_out_path, len, \"/\");\n        len = strip(target_out_path, len, \"/system\");\n        if (asprintf(&name, \"%.*s%s\", (int)len, target_out_path, conf[which][dir]) != -1) {\n            fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY));\n            free(name);\n        }\n    }\n    if (fd < 0) {\n        fd = TEMP_FAILURE_RETRY(open(conf[which][dir], O_RDONLY));\n    }\n    return fd;\n}\n\n// if path is \"odm/<stuff>\", \"oem/<stuff>\", \"product/<stuff>\",\n// \"system_ext/<stuff>\" or \"vendor/<stuff>\"\nstatic bool is_partition(const std::string& path) {\n    static const char* partitions[] = {\"odm/\", \"oem/\", \"product/\", \"system_ext/\", \"vendor/\"};\n    for (size_t i = 0; i < (sizeof(partitions) / sizeof(partitions[0])); ++i) {\n        if (StartsWith(path, partitions[i])) return true;\n    }\n    return false;\n}\n\n// alias prefixes of \"<partition>/<stuff>\" to \"system/<partition>/<stuff>\" or\n// \"system/<partition>/<stuff>\" to \"<partition>/<stuff>\"\nstatic bool fs_config_cmp(bool dir, const char* prefix, size_t len, const char* path, size_t plen) {\n    std::string pattern(prefix, len);\n    std::string input(path, plen);\n\n    // Massage pattern and input so that they can be used by fnmatch where\n    // directories have to end with /.\n    if (dir) {\n        if (!EndsWith(input, \"/\")) {\n            input.append(\"/\");\n        }\n\n        if (!EndsWith(pattern, \"/*\")) {\n            if (EndsWith(pattern, \"/\")) {\n                pattern.append(\"*\");\n            } else {\n                pattern.append(\"/*\");\n            }\n        }\n    }\n\n    // no FNM_PATHNAME is set in order to match a/b/c/d with a/*\n    // FNM_ESCAPE is set in order to prevent using \\\\? and \\\\* and maintenance issues.\n    const int fnm_flags = FNM_NOESCAPE;\n    if (fnmatch(pattern.c_str(), input.c_str(), fnm_flags) == 0) return true;\n\n    // Check match between logical partition's files and patterns.\n    static constexpr const char* kLogicalPartitions[] = {\"system/product/\", \"system/system_ext/\",\n                                                         \"system/vendor/\", \"vendor/odm/\"};\n    for (auto& logical_partition : kLogicalPartitions) {\n        if (StartsWith(input, logical_partition)) {\n            std::string input_in_partition = input.substr(input.find('/') + 1);\n            if (!is_partition(input_in_partition)) continue;\n            if (fnmatch(pattern.c_str(), input_in_partition.c_str(), fnm_flags) == 0) {\n                return true;\n            }\n        }\n    }\n    return false;\n}\n#ifndef __ANDROID_VNDK__\nauto __for_testing_only__fs_config_cmp = fs_config_cmp;\n#endif\n\nbool get_fs_config(const char* path, bool dir, const char* target_out_path,\n                   struct fs_config* fs_conf) {\n    const struct fs_path_config* pc;\n    size_t which, plen;\n\n    if (path[0] == '/') {\n        path++;\n    }\n\n    plen = strlen(path);\n\n    for (which = 0; which < (sizeof(conf) / sizeof(conf[0])); ++which) {\n        struct fs_path_config_from_file header;\n\n        int fd = fs_config_open(dir, which, target_out_path);\n        if (fd < 0) continue;\n\n        while (TEMP_FAILURE_RETRY(read(fd, &header, sizeof(header))) == sizeof(header)) {\n            char* prefix;\n            uint16_t host_len = header.len;\n            ssize_t len, remainder = host_len - sizeof(header);\n            if (remainder <= 0) {\n                ALOGE(\"%s len is corrupted\", conf[which][dir]);\n                break;\n            }\n            prefix = static_cast<char*>(calloc(1, remainder));\n            if (!prefix) {\n                ALOGE(\"%s out of memory\", conf[which][dir]);\n                break;\n            }\n            if (TEMP_FAILURE_RETRY(read(fd, prefix, remainder)) != remainder) {\n                free(prefix);\n                ALOGE(\"%s prefix is truncated\", conf[which][dir]);\n                break;\n            }\n            len = strnlen(prefix, remainder);\n            if (len >= remainder) {  // missing a terminating null\n                free(prefix);\n                ALOGE(\"%s is corrupted\", conf[which][dir]);\n                break;\n            }\n            if (fs_config_cmp(dir, prefix, len, path, plen)) {\n                free(prefix);\n                close(fd);\n                fs_conf->uid = header.uid;\n                fs_conf->gid = header.gid;\n                fs_conf->mode = header.mode;\n                fs_conf->capabilities = header.capabilities;\n                return true;\n            }\n            free(prefix);\n        }\n        close(fd);\n    }\n\n    for (pc = dir ? android_dirs : android_files; pc->prefix; pc++) {\n        if (fs_config_cmp(dir, pc->prefix, strlen(pc->prefix), path, plen)) {\n            fs_conf->uid = pc->uid;\n            fs_conf->gid = pc->gid;\n            fs_conf->mode = pc->mode;\n            fs_conf->capabilities = pc->capabilities;\n            return true;\n        }\n    }\n    return false;\n}\n\nvoid fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,\n               unsigned* mode, uint64_t* capabilities) {\n    struct fs_config conf;\n    if (get_fs_config(path, dir, target_out_path, &conf)) {\n        *uid = conf.uid;\n        *gid = conf.gid;\n        *mode = (*mode & S_IFMT) | conf.mode;\n        *capabilities = conf.capabilities;\n    } else {\n        *uid = AID_ROOT;\n        *gid = AID_ROOT;\n        *mode = (*mode & S_IFMT) | (dir ? 0755 : 0644);\n        *capabilities = 0;\n    }\n}\n"
  },
  {
    "path": "libcutils/fs_config.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n// Binary format for the runtime <partition>/etc/fs_config_(dirs|files) filesystem override files.\nstruct fs_path_config_from_file {\n    uint16_t len;\n    uint16_t mode;\n    uint16_t uid;\n    uint16_t gid;\n    uint64_t capabilities;\n    char prefix[];\n} __attribute__((__aligned__(sizeof(uint64_t))));\n\nstruct fs_path_config {\n    unsigned mode;\n    unsigned uid;\n    unsigned gid;\n    uint64_t capabilities;\n    const char* prefix;\n};\n"
  },
  {
    "path": "libcutils/fs_config_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <inttypes.h>\n\n#include <string>\n\n#include <gtest/gtest.h>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n\n#include <private/android_filesystem_config.h>\n\n#include \"fs_config.h\"\n\nextern const fs_path_config* __for_testing_only__android_dirs;\nextern const fs_path_config* __for_testing_only__android_files;\nextern bool (*__for_testing_only__fs_config_cmp)(bool, const char*, size_t, const char*, size_t);\n\n// Maximum entries in system/core/libcutils/fs_config.cpp:android_* before we\n// hit a nullptr termination, before we declare the list is just too big or\n// could be missing the nullptr.\nstatic constexpr size_t max_idx = 4096;\n\nstatic const struct fs_config_cmp_test {\n    bool dir;\n    const char* prefix;\n    const char* path;\n    bool match;\n} fs_config_cmp_tests[] = {\n        // clang-format off\n    { true,  \"system/lib\",             \"system/lib/hw\",           true  },\n    { true,  \"vendor/lib\",             \"system/vendor/lib/hw\",    true  },\n    { true,  \"system/vendor/lib\",      \"vendor/lib/hw\",           false },\n    { true,  \"system/vendor/lib\",      \"system/vendor/lib/hw\",    true  },\n    { true,  \"foo/*/bar/*\",            \"foo/1/bar/2\",             true  },\n    { true,  \"foo/*/bar/*\",            \"foo/1/bar\",               true  },\n    { true,  \"foo/*/bar/*\",            \"foo/1/bar/2/3\",           true  },\n    { true,  \"foo/*/bar/*\",            \"foo/1/bar/2/3/\",          true  },\n    { false, \"vendor/bin/wifi\",        \"system/vendor/bin/w\",     false },\n    { false, \"vendor/bin/wifi\",        \"system/vendor/bin/wifi\",  true  },\n    { false, \"vendor/bin/wifi\",        \"system/vendor/bin/wifi2\", false },\n    { false, \"system/vendor/bin/wifi\", \"system/vendor/bin/wifi\",  true, },\n    { false, \"odm/bin/wifi\",           \"system/odm/bin/wifi\",     false },\n    { false, \"odm/bin/wifi\",           \"vendor/odm/bin/wifi\",     true  },\n    { false, \"oem/bin/wifi\",           \"system/oem/bin/wifi\",     false },\n    { false, \"data/bin/wifi\",          \"system/data/bin/wifi\",    false },\n    { false, \"system/bin/*\",           \"system/bin/wifi\",         true  },\n    { false, \"vendor/bin/*\",           \"system/vendor/bin/wifi\",  true  },\n    { false, \"system/bin/*\",           \"system/bin\",              false },\n    { false, \"system/vendor/bin/*\",    \"vendor/bin/wifi\",         false },\n    { false, \"foo/*/bar/*\",            \"foo/1/bar/2\",             true  },\n    { false, \"foo/*/bar/*\",            \"foo/1/bar\",               false },\n    { false, \"foo/*/bar/*\",            \"foo/1/bar/2/3\",           true  },\n    { false, \"foo/*/bar/*.so\",         \"foo/1/bar/2/3\",           false },\n    { false, \"foo/*/bar/*.so\",         \"foo/1/bar/2.so\",          true  },\n    { false, \"foo/*/bar/*.so\",         \"foo/1/bar/2/3.so\",        true  },\n    { false, NULL,                     NULL,                      false },\n        // clang-format on\n};\n\nstatic bool check_unique(std::vector<const char*>& paths, const std::string& config_name,\n                         const std::string& prefix) {\n    bool retval = false;\n\n    std::string alternate = \"system/\" + prefix;\n\n    for (size_t idx = 0; idx < paths.size(); ++idx) {\n        size_t second;\n        std::string path(paths[idx]);\n        // check if there are multiple identical paths\n        for (second = idx + 1; second < paths.size(); ++second) {\n            if (path == paths[second]) {\n                GTEST_LOG_(ERROR) << \"duplicate paths in \" << config_name << \": \" << paths[idx];\n                retval = true;\n                break;\n            }\n        }\n\n        // check if path is <partition>/\n        if (android::base::StartsWith(path, prefix)) {\n            // rebuild path to be system/<partition>/... to check for alias\n            path = alternate + path.substr(prefix.size());\n            for (second = 0; second < paths.size(); ++second) {\n                if (path == paths[second]) {\n                    GTEST_LOG_(ERROR) << \"duplicate alias paths in \" << config_name << \": \"\n                                      << paths[idx] << \" and \" << paths[second]\n                                      << \" (remove latter)\";\n                    retval = true;\n                    break;\n                }\n            }\n            continue;\n        }\n\n        // check if path is system/<partition>/\n        if (android::base::StartsWith(path, alternate)) {\n            // rebuild path to be <partition>/... to check for alias\n            path = prefix + path.substr(alternate.size());\n            for (second = 0; second < paths.size(); ++second) {\n                if (path == paths[second]) break;\n            }\n            if (second >= paths.size()) {\n                GTEST_LOG_(ERROR) << \"replace path in \" << config_name << \": \" << paths[idx]\n                                  << \" with \" << path;\n                retval = true;\n            }\n        }\n    }\n    return retval;\n}\n\nstatic bool check_unique(const fs_path_config* paths, const char* type_name,\n                         const std::string& prefix) {\n    std::string config(\"system/core/libcutils/fs_config.cpp:android_\");\n    config += type_name;\n    config += \"[]\";\n\n    bool retval = false;\n    std::vector<const char*> paths_tmp;\n    for (size_t idx = 0; paths[idx].prefix; ++idx) {\n        if (idx > max_idx) {\n            GTEST_LOG_(WARNING) << config << \": has no end (missing null prefix)\";\n            retval = true;\n            break;\n        }\n        paths_tmp.push_back(paths[idx].prefix);\n    }\n\n    return check_unique(paths_tmp, config, prefix) || retval;\n}\n\nstatic bool check_fs_config_cmp(const fs_config_cmp_test* tests) {\n    bool match, retval = false;\n    for (size_t idx = 0; tests[idx].prefix; ++idx) {\n        match = __for_testing_only__fs_config_cmp(tests[idx].dir, tests[idx].prefix,\n                                                  strlen(tests[idx].prefix), tests[idx].path,\n                                                  strlen(tests[idx].path));\n        if (match != tests[idx].match) {\n            GTEST_LOG_(ERROR) << tests[idx].path << (match ? \" matched \" : \" didn't match \")\n                              << tests[idx].prefix;\n            retval = true;\n            break;\n        }\n    }\n    return retval;\n}\n\n#define endof(pointer, field) (offsetof(typeof(*(pointer)), field) + sizeof((pointer)->field))\n\nstatic bool check_unique(const std::string& config, const std::string& prefix) {\n    int retval = false;\n\n    std::string data;\n    if (!android::base::ReadFileToString(config, &data)) return retval;\n\n    const fs_path_config_from_file* pc =\n        reinterpret_cast<const fs_path_config_from_file*>(data.c_str());\n    size_t len = data.size();\n\n    std::vector<const char*> paths_tmp;\n    size_t entry_number = 0;\n    while (len > 0) {\n        uint16_t host_len = (len >= endof(pc, len)) ? pc->len : INT16_MAX;\n        if (host_len > len) {\n            GTEST_LOG_(WARNING) << config << \": truncated at entry \" << entry_number << \" (\"\n                                << host_len << \" > \" << len << \")\";\n            const std::string unknown(\"?\");\n            GTEST_LOG_(WARNING)\n                << config << \": entry[\" << entry_number << \"]={ \"\n                << \"len=\" << ((len >= endof(pc, len))\n                                  ? android::base::StringPrintf(\"%\" PRIu16, pc->len)\n                                  : unknown)\n                << \", mode=\" << ((len >= endof(pc, mode))\n                                     ? android::base::StringPrintf(\"0%\" PRIo16, pc->mode)\n                                     : unknown)\n                << \", uid=\" << ((len >= endof(pc, uid))\n                                    ? android::base::StringPrintf(\"%\" PRIu16, pc->uid)\n                                    : unknown)\n                << \", gid=\" << ((len >= endof(pc, gid))\n                                    ? android::base::StringPrintf(\"%\" PRIu16, pc->gid)\n                                    : unknown)\n                << \", capabilities=\"\n                << ((len >= endof(pc, capabilities))\n                        ? android::base::StringPrintf(\"0x%\" PRIx64, pc->capabilities)\n                        : unknown)\n                << \", prefix=\"\n                << ((len >= offsetof(fs_path_config_from_file, prefix))\n                        ? android::base::StringPrintf(\n                              \"\\\"%.*s...\", (int)(len - offsetof(fs_path_config_from_file, prefix)),\n                              pc->prefix)\n                        : unknown)\n                << \" }\";\n            retval = true;\n            break;\n        }\n        paths_tmp.push_back(pc->prefix);\n\n        pc = reinterpret_cast<const fs_path_config_from_file*>(reinterpret_cast<const char*>(pc) +\n                                                               host_len);\n        len -= host_len;\n        ++entry_number;\n    }\n\n    return check_unique(paths_tmp, config, prefix) || retval;\n}\n\nvoid check_two(const fs_path_config* paths, const char* type_name, const char* prefix) {\n    ASSERT_FALSE(paths == nullptr);\n    ASSERT_FALSE(type_name == nullptr);\n    ASSERT_FALSE(prefix == nullptr);\n    bool check_internal = check_unique(paths, type_name, prefix);\n    EXPECT_FALSE(check_internal);\n    bool check_overrides =\n        check_unique(std::string(\"/\") + prefix + \"etc/fs_config_\" + type_name, prefix);\n    EXPECT_FALSE(check_overrides);\n}\n\nTEST(fs_config, vendor_dirs_alias) {\n    check_two(__for_testing_only__android_dirs, \"dirs\", \"vendor/\");\n}\n\nTEST(fs_config, vendor_files_alias) {\n    check_two(__for_testing_only__android_files, \"files\", \"vendor/\");\n}\n\nTEST(fs_config, oem_dirs_alias) {\n    check_two(__for_testing_only__android_dirs, \"dirs\", \"oem/\");\n}\n\nTEST(fs_config, oem_files_alias) {\n    check_two(__for_testing_only__android_files, \"files\", \"oem/\");\n}\n\nTEST(fs_config, odm_dirs_alias) {\n    check_two(__for_testing_only__android_dirs, \"dirs\", \"odm/\");\n}\n\nTEST(fs_config, odm_files_alias) {\n    check_two(__for_testing_only__android_files, \"files\", \"odm/\");\n}\n\nTEST(fs_config, system_alias) {\n    EXPECT_FALSE(check_fs_config_cmp(fs_config_cmp_tests));\n}\n"
  },
  {
    "path": "libcutils/hashmap.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/hashmap.h>\n\n#include <assert.h>\n#include <errno.h>\n#include <pthread.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n\ntypedef struct Entry Entry;\nstruct Entry {\n    void* key;\n    int hash;\n    void* value;\n    Entry* next;\n};\n\nstruct Hashmap {\n    Entry** buckets;\n    size_t bucketCount;\n    int (*hash)(void* key);\n    bool (*equals)(void* keyA, void* keyB);\n    pthread_mutex_t lock;\n    size_t size;\n};\n\nHashmap* hashmapCreate(size_t initialCapacity,\n        int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB)) {\n    assert(hash != NULL);\n    assert(equals != NULL);\n\n    Hashmap* map = static_cast<Hashmap*>(malloc(sizeof(Hashmap)));\n    if (map == NULL) {\n        return NULL;\n    }\n\n    // 0.75 load factor.\n    size_t minimumBucketCount = initialCapacity * 4 / 3;\n    map->bucketCount = 1;\n    while (map->bucketCount <= minimumBucketCount) {\n        // Bucket count must be power of 2.\n        map->bucketCount <<= 1;\n    }\n\n    map->buckets = static_cast<Entry**>(calloc(map->bucketCount, sizeof(Entry*)));\n    if (map->buckets == NULL) {\n        free(map);\n        return NULL;\n    }\n\n    map->size = 0;\n\n    map->hash = hash;\n    map->equals = equals;\n\n    pthread_mutex_init(&map->lock, nullptr);\n\n    return map;\n}\n\n/**\n * Hashes the given key.\n */\n#ifdef __clang__\n__attribute__((no_sanitize(\"integer\")))\n#endif\nstatic inline int hashKey(Hashmap* map, void* key) {\n    int h = map->hash(key);\n\n    // We apply this secondary hashing discovered by Doug Lea to defend\n    // against bad hashes.\n    h += ~(h << 9);\n    h ^= (((unsigned int) h) >> 14);\n    h += (h << 4);\n    h ^= (((unsigned int) h) >> 10);\n\n    return h;\n}\n\nstatic inline size_t calculateIndex(size_t bucketCount, int hash) {\n    return ((size_t) hash) & (bucketCount - 1);\n}\n\nstatic void expandIfNecessary(Hashmap* map) {\n    // If the load factor exceeds 0.75...\n    if (map->size > (map->bucketCount * 3 / 4)) {\n        // Start off with a 0.33 load factor.\n        size_t newBucketCount = map->bucketCount << 1;\n        Entry** newBuckets = static_cast<Entry**>(calloc(newBucketCount, sizeof(Entry*)));\n        if (newBuckets == NULL) {\n            // Abort expansion.\n            return;\n        }\n\n        // Move over existing entries.\n        size_t i;\n        for (i = 0; i < map->bucketCount; i++) {\n            Entry* entry = map->buckets[i];\n            while (entry != NULL) {\n                Entry* next = entry->next;\n                size_t index = calculateIndex(newBucketCount, entry->hash);\n                entry->next = newBuckets[index];\n                newBuckets[index] = entry;\n                entry = next;\n            }\n        }\n\n        // Copy over internals.\n        free(map->buckets);\n        map->buckets = newBuckets;\n        map->bucketCount = newBucketCount;\n    }\n}\n\nvoid hashmapLock(Hashmap* map) {\n    pthread_mutex_lock(&map->lock);\n}\n\nvoid hashmapUnlock(Hashmap* map) {\n    pthread_mutex_unlock(&map->lock);\n}\n\nvoid hashmapFree(Hashmap* map) {\n    size_t i;\n    for (i = 0; i < map->bucketCount; i++) {\n        Entry* entry = map->buckets[i];\n        while (entry != NULL) {\n            Entry* next = entry->next;\n            free(entry);\n            entry = next;\n        }\n    }\n    free(map->buckets);\n    pthread_mutex_destroy(&map->lock);\n    free(map);\n}\n\n#ifdef __clang__\n__attribute__((no_sanitize(\"integer\")))\n#endif\n/* FIXME: relies on signed integer overflow, which is undefined behavior */\nint hashmapHash(void* key, size_t keySize) {\n    int h = keySize;\n    char* data = (char*) key;\n    size_t i;\n    for (i = 0; i < keySize; i++) {\n        h = h * 31 + *data;\n        data++;\n    }\n    return h;\n}\n\nstatic Entry* createEntry(void* key, int hash, void* value) {\n    Entry* entry = static_cast<Entry*>(malloc(sizeof(Entry)));\n    if (entry == NULL) {\n        return NULL;\n    }\n    entry->key = key;\n    entry->hash = hash;\n    entry->value = value;\n    entry->next = NULL;\n    return entry;\n}\n\nstatic inline bool equalKeys(void* keyA, int hashA, void* keyB, int hashB,\n        bool (*equals)(void*, void*)) {\n    if (keyA == keyB) {\n        return true;\n    }\n    if (hashA != hashB) {\n        return false;\n    }\n    return equals(keyA, keyB);\n}\n\nvoid* hashmapPut(Hashmap* map, void* key, void* value) {\n    int hash = hashKey(map, key);\n    size_t index = calculateIndex(map->bucketCount, hash);\n\n    Entry** p = &(map->buckets[index]);\n    while (true) {\n        Entry* current = *p;\n\n        // Add a new entry.\n        if (current == NULL) {\n            *p = createEntry(key, hash, value);\n            if (*p == NULL) {\n                errno = ENOMEM;\n                return NULL;\n            }\n            map->size++;\n            expandIfNecessary(map);\n            return NULL;\n        }\n\n        // Replace existing entry.\n        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {\n            void* oldValue = current->value;\n            current->value = value;\n            return oldValue;\n        }\n\n        // Move to next entry.\n        p = &current->next;\n    }\n}\n\nvoid* hashmapGet(Hashmap* map, void* key) {\n    int hash = hashKey(map, key);\n    size_t index = calculateIndex(map->bucketCount, hash);\n\n    Entry* entry = map->buckets[index];\n    while (entry != NULL) {\n        if (equalKeys(entry->key, entry->hash, key, hash, map->equals)) {\n            return entry->value;\n        }\n        entry = entry->next;\n    }\n\n    return NULL;\n}\n\nvoid* hashmapRemove(Hashmap* map, void* key) {\n    int hash = hashKey(map, key);\n    size_t index = calculateIndex(map->bucketCount, hash);\n\n    // Pointer to the current entry.\n    Entry** p = &(map->buckets[index]);\n    Entry* current;\n    while ((current = *p) != NULL) {\n        if (equalKeys(current->key, current->hash, key, hash, map->equals)) {\n            void* value = current->value;\n            *p = current->next;\n            free(current);\n            map->size--;\n            return value;\n        }\n\n        p = &current->next;\n    }\n\n    return NULL;\n}\n\nvoid hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),\n                    void* context) {\n    size_t i;\n    for (i = 0; i < map->bucketCount; i++) {\n        Entry* entry = map->buckets[i];\n        while (entry != NULL) {\n            Entry *next = entry->next;\n            if (!callback(entry->key, entry->value, context)) {\n                return;\n            }\n            entry = next;\n        }\n    }\n}\n"
  },
  {
    "path": "libcutils/include/cutils/android_get_control_file.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_ANDROID_GET_CONTROL_FILE_H\n#define __CUTILS_ANDROID_GET_CONTROL_FILE_H\n\n#define ANDROID_FILE_ENV_PREFIX \"ANDROID_FILE_\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * android_get_control_file - simple helper function to get the file\n * descriptor of our init-managed file. `path' is the filename path as\n * given in init.rc. Returns -1 on error.\n */\nint android_get_control_file(const char* path);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CUTILS_ANDROID_GET_CONTROL_FILE_H */\n"
  },
  {
    "path": "libcutils/include/cutils/android_reboot.h",
    "content": "/*\n * Copyright 2011, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\n/* Commands */\n#define ANDROID_RB_RESTART 0xDEAD0001 /* deprecated. Use RESTART2. */\n#define ANDROID_RB_POWEROFF 0xDEAD0002\n#define ANDROID_RB_RESTART2 0xDEAD0003\n#define ANDROID_RB_THERMOFF 0xDEAD0004\n\n/* Properties */\n#define ANDROID_RB_PROPERTY \"sys.powerctl\"\n\n/* Android reboot reason stored in this property */\n#define LAST_REBOOT_REASON_PROPERTY \"persist.sys.boot.reason\"\n#define LAST_REBOOT_REASON_FILE \"/metadata/bootstat/\" LAST_REBOOT_REASON_PROPERTY\n\n/* Reboot or shutdown the system.\n * This call uses ANDROID_RB_PROPERTY to request reboot to init process.\n * Due to that, process calling this should have proper selinux permission\n * to write to the property or the call will fail.\n */\nint android_reboot(unsigned cmd, int flags, const char* arg);\n\n__END_DECLS\n"
  },
  {
    "path": "libcutils/include/cutils/ashmem.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stddef.h>\n\n#if defined(__BIONIC__)\n#include <linux/ashmem.h>\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint ashmem_valid(int fd);\nint ashmem_create_region(const char *name, size_t size);\nint ashmem_set_prot_region(int fd, int prot);\nint ashmem_pin_region(int fd, size_t offset, size_t len);\nint ashmem_unpin_region(int fd, size_t offset, size_t len);\nint ashmem_get_size_region(int fd);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libcutils/include/cutils/atomic.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_CUTILS_ATOMIC_H\n#define ANDROID_CUTILS_ATOMIC_H\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <stdatomic.h>\n\n#ifndef ANDROID_ATOMIC_INLINE\n#define ANDROID_ATOMIC_INLINE static inline\n#endif\n\n/*\n * A handful of basic atomic operations.\n * THESE ARE HERE FOR LEGACY REASONS ONLY.  AVOID.\n *\n * PREFERRED ALTERNATIVES:\n * - Use C++/C/pthread locks/mutexes whenever there is not a\n *   convincing reason to do otherwise.  Note that very clever and\n *   complicated, but correct, lock-free code is often slower than\n *   using locks, especially where nontrivial data structures\n *   are involved.\n * - C11 stdatomic.h.\n * - Where supported, C++11 std::atomic<T> .\n *\n * PLEASE STOP READING HERE UNLESS YOU ARE TRYING TO UNDERSTAND\n * OR UPDATE OLD CODE.\n *\n * The \"acquire\" and \"release\" terms can be defined intuitively in terms\n * of the placement of memory barriers in a simple lock implementation:\n *   - wait until compare-and-swap(lock-is-free --> lock-is-held) succeeds\n *   - barrier\n *   - [do work]\n *   - barrier\n *   - store(lock-is-free)\n * In very crude terms, the initial (acquire) barrier prevents any of the\n * \"work\" from happening before the lock is held, and the later (release)\n * barrier ensures that all of the work happens before the lock is released.\n * (Think of cached writes, cache read-ahead, and instruction reordering\n * around the CAS and store instructions.)\n *\n * The barriers must apply to both the compiler and the CPU.  Note it is\n * legal for instructions that occur before an \"acquire\" barrier to be\n * moved down below it, and for instructions that occur after a \"release\"\n * barrier to be moved up above it.\n *\n * The ARM-driven implementation we use here is short on subtlety,\n * and actually requests a full barrier from the compiler and the CPU.\n * The only difference between acquire and release is in whether they\n * are issued before or after the atomic operation with which they\n * are associated.  To ease the transition to C/C++ atomic intrinsics,\n * you should not rely on this, and instead assume that only the minimal\n * acquire/release protection is provided.\n *\n * NOTE: all int32_t* values are expected to be aligned on 32-bit boundaries.\n * If they are not, atomicity is not guaranteed.\n */\n\nANDROID_ATOMIC_INLINE\nvolatile atomic_int_least32_t* to_atomic_int_least32_t(volatile const int32_t* addr) {\n#ifdef __cplusplus\n    return reinterpret_cast<volatile atomic_int_least32_t*>(const_cast<volatile int32_t*>(addr));\n#else\n    return (volatile atomic_int_least32_t*)addr;\n#endif\n}\n\n/*\n * Basic arithmetic and bitwise operations.  These all provide a\n * barrier with \"release\" ordering, and return the previous value.\n *\n * These have the same characteristics (e.g. what happens on overflow)\n * as the equivalent non-atomic C operations.\n */\nANDROID_ATOMIC_INLINE\nint32_t android_atomic_inc(volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n        /* Int32_t, if it exists, is the same as int_least32_t. */\n    return atomic_fetch_add_explicit(a, 1, memory_order_release);\n}\n\nANDROID_ATOMIC_INLINE\nint32_t android_atomic_dec(volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    return atomic_fetch_sub_explicit(a, 1, memory_order_release);\n}\n\nANDROID_ATOMIC_INLINE\nint32_t android_atomic_add(int32_t value, volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    return atomic_fetch_add_explicit(a, value, memory_order_release);\n}\n\nANDROID_ATOMIC_INLINE\nint32_t android_atomic_and(int32_t value, volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    return atomic_fetch_and_explicit(a, value, memory_order_release);\n}\n\nANDROID_ATOMIC_INLINE\nint32_t android_atomic_or(int32_t value, volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    return atomic_fetch_or_explicit(a, value, memory_order_release);\n}\n\n/*\n * Perform an atomic load with \"acquire\" or \"release\" ordering.\n *\n * Note that the notion of a \"release\" ordering for a load does not\n * really fit into the C11 or C++11 memory model.  The extra ordering\n * is normally observable only by code using memory_order_relaxed\n * atomics, or data races.  In the rare cases in which such ordering\n * is called for, use memory_order_relaxed atomics and a leading\n * atomic_thread_fence (typically with memory_order_acquire,\n * not memory_order_release!) instead.  If you do not understand\n * this comment, you are in the vast majority, and should not be\n * using release loads or replacing them with anything other than\n * locks or default sequentially consistent atomics.\n */\nANDROID_ATOMIC_INLINE\nint32_t android_atomic_acquire_load(volatile const int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    return atomic_load_explicit(a, memory_order_acquire);\n}\n\nANDROID_ATOMIC_INLINE\nint32_t android_atomic_release_load(volatile const int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    atomic_thread_fence(memory_order_seq_cst);\n    /* Any reasonable clients of this interface would probably prefer   */\n    /* something weaker.  But some remaining clients seem to be         */\n    /* abusing this API in strange ways, e.g. by using it as a fence.   */\n    /* Thus we are conservative until we can get rid of remaining       */\n    /* clients (and this function).                                     */\n    return atomic_load_explicit(a, memory_order_relaxed);\n}\n\n/*\n * Perform an atomic store with \"acquire\" or \"release\" ordering.\n *\n * Note that the notion of an \"acquire\" ordering for a store does not\n * really fit into the C11 or C++11 memory model.  The extra ordering\n * is normally observable only by code using memory_order_relaxed\n * atomics, or data races.  In the rare cases in which such ordering\n * is called for, use memory_order_relaxed atomics and a trailing\n * atomic_thread_fence (typically with memory_order_release,\n * not memory_order_acquire!) instead.\n */\nANDROID_ATOMIC_INLINE\nvoid android_atomic_acquire_store(int32_t value, volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    atomic_store_explicit(a, value, memory_order_relaxed);\n    atomic_thread_fence(memory_order_seq_cst);\n    /* Again overly conservative to accomodate weird clients.   */\n}\n\nANDROID_ATOMIC_INLINE\nvoid android_atomic_release_store(int32_t value, volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    atomic_store_explicit(a, value, memory_order_release);\n}\n\n/*\n * Compare-and-set operation with \"acquire\" or \"release\" ordering.\n *\n * This returns zero if the new value was successfully stored, which will\n * only happen when *addr == oldvalue.\n *\n * (The return value is inverted from implementations on other platforms,\n * but matches the ARM ldrex/strex result.)\n *\n * Implementations that use the release CAS in a loop may be less efficient\n * than possible, because we re-issue the memory barrier on each iteration.\n */\nANDROID_ATOMIC_INLINE\nint android_atomic_acquire_cas(int32_t oldvalue, int32_t newvalue,\n                           volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    return !atomic_compare_exchange_strong_explicit(\n                                          a, &oldvalue, newvalue,\n                                          memory_order_acquire,\n                                          memory_order_acquire);\n}\n\nANDROID_ATOMIC_INLINE\nint android_atomic_release_cas(int32_t oldvalue, int32_t newvalue,\n                               volatile int32_t* addr)\n{\n    volatile atomic_int_least32_t* a = to_atomic_int_least32_t(addr);\n    return !atomic_compare_exchange_strong_explicit(\n                                          a, &oldvalue, newvalue,\n                                          memory_order_release,\n                                          memory_order_relaxed);\n}\n\n/*\n * Fence primitives.\n */\nANDROID_ATOMIC_INLINE\nvoid android_compiler_barrier(void)\n{\n    __asm__ __volatile__ (\"\" : : : \"memory\");\n    /* Could probably also be:                          */\n    /* atomic_signal_fence(memory_order_seq_cst);       */\n}\n\nANDROID_ATOMIC_INLINE\nvoid android_memory_barrier(void)\n{\n    atomic_thread_fence(memory_order_seq_cst);\n}\n\n/*\n * Aliases for code using an older version of this header.  These are now\n * deprecated and should not be used.  The definitions will be removed\n * in a future release.\n */\n#define android_atomic_write android_atomic_release_store\n#define android_atomic_cmpxchg android_atomic_release_cas\n\n#endif // ANDROID_CUTILS_ATOMIC_H\n"
  },
  {
    "path": "libcutils/include/cutils/bitops.h",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_BITOPS_H\n#define __CUTILS_BITOPS_H\n\n#include <stdbool.h>\n#include <string.h>\n#include <strings.h>\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\nstatic inline int popcount(unsigned int x) {\n    return __builtin_popcount(x);\n}\n\nstatic inline int popcountl(unsigned long x) {\n    return __builtin_popcountl(x);\n}\n\nstatic inline int popcountll(unsigned long long x) {\n    return __builtin_popcountll(x);\n}\n\n__END_DECLS\n\n#endif /* __CUTILS_BITOPS_H */\n"
  },
  {
    "path": "libcutils/include/cutils/compiler.h",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_CUTILS_COMPILER_H\n#define ANDROID_CUTILS_COMPILER_H\n\n/*\n * helps the compiler's optimizer predicting branches\n */\n\n#ifdef __cplusplus\n#   define CC_LIKELY( exp )    (__builtin_expect( !!(exp), true ))\n#   define CC_UNLIKELY( exp )  (__builtin_expect( !!(exp), false ))\n#else\n#   define CC_LIKELY( exp )    (__builtin_expect( !!(exp), 1 ))\n#   define CC_UNLIKELY( exp )  (__builtin_expect( !!(exp), 0 ))\n#endif\n\n/**\n * exports marked symbols\n *\n * if used on a C++ class declaration, this macro must be inserted\n * after the \"class\" keyword. For instance:\n *\n * template <typename TYPE>\n * class ANDROID_API Singleton { }\n */\n\n#define ANDROID_API __attribute__((visibility(\"default\")))\n\n#endif // ANDROID_CUTILS_COMPILER_H\n"
  },
  {
    "path": "libcutils/include/cutils/config_utils.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_CONFIG_UTILS_H\n#define __CUTILS_CONFIG_UTILS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n    \ntypedef struct cnode cnode;\n\n\nstruct cnode\n{\n    cnode *next;\n    cnode *first_child;\n    cnode *last_child;\n    const char *name;\n    const char *value;\n};\n\n/* parse a text string into a config node tree */\nvoid config_load(cnode *root, char *data);\n\n/* parse a file into a config node tree */\nvoid config_load_file(cnode *root, const char *fn);\n\n/* create a single config node */\ncnode* config_node(const char *name, const char *value);\n\n/* locate a named child of a config node */\ncnode* config_find(cnode *root, const char *name);\n\n/* look up a child by name and return the boolean value */\nint config_bool(cnode *root, const char *name, int _default);\n\n/* look up a child by name and return the string value */\nconst char* config_str(cnode *root, const char *name, const char *_default);\n\n/* add a named child to a config node (or modify it if it already exists) */\nvoid config_set(cnode *root, const char *name, const char *value);\n\n/* free a config node tree */\nvoid config_free(cnode *root);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libcutils/include/cutils/fs.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_FS_H\n#define __CUTILS_FS_H\n\n#include <sys/types.h>\n#include <unistd.h>\n\n/*\n * TEMP_FAILURE_RETRY is defined by some, but not all, versions of\n * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's\n * not already defined, then define it here.\n */\n#ifndef TEMP_FAILURE_RETRY\n/* Used to retry syscalls that can return EINTR. */\n#define TEMP_FAILURE_RETRY(exp) ({         \\\n    typeof (exp) _rc;                      \\\n    do {                                   \\\n        _rc = (exp);                       \\\n    } while (_rc == -1 && errno == EINTR); \\\n    _rc; })\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Ensure that directory exists with given mode and owners.  If it exists\n * with a different mode or owners, they are fixed to match the given values.\n */\nextern int fs_prepare_dir(const char* path, mode_t mode, uid_t uid, gid_t gid);\n\n/*\n * Ensure that directory exists with given mode and owners.  If it exists\n * with different owners, they are not fixed and -1 is returned.\n */\nextern int fs_prepare_dir_strict(const char* path, mode_t mode, uid_t uid, gid_t gid);\n\n/*\n * Ensure that file exists with given mode and owners.  If it exists\n * with different owners, they are not fixed and -1 is returned.\n */\nextern int fs_prepare_file_strict(const char* path, mode_t mode, uid_t uid, gid_t gid);\n\n\n/*\n * Read single plaintext integer from given file, correctly handling files\n * partially written with fs_write_atomic_int().\n */\nextern int fs_read_atomic_int(const char* path, int* value);\n\n/*\n * Write single plaintext integer to given file, creating backup while\n * in progress.\n */\nextern int fs_write_atomic_int(const char* path, int value);\n\n/*\n * Ensure that all directories along given path exist, creating parent\n * directories as needed.  Validates that given path is absolute and that\n * it contains no relative \".\" or \"..\" paths or symlinks.  Last path segment\n * is treated as filename and ignored, unless the path ends with \"/\".\n */\nextern int fs_mkdirs(const char* path, mode_t mode);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CUTILS_FS_H */\n"
  },
  {
    "path": "libcutils/include/cutils/hashmap.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Hash map.\n *\n * Use std::map or std::unordered_map instead.\n * https://en.cppreference.com/w/cpp/container\n */\n\n#ifndef __HASHMAP_H\n#define __HASHMAP_H\n\n#include <stdbool.h>\n#include <stdlib.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/** A hash map. */\ntypedef struct Hashmap Hashmap;\n\n/**\n * Creates a new hash map. Returns NULL if memory allocation fails.\n *\n * @param initialCapacity number of expected entries\n * @param hash function which hashes keys\n * @param equals function which compares keys for equality\n */\nHashmap* hashmapCreate(size_t initialCapacity,\n        int (*hash)(void* key), bool (*equals)(void* keyA, void* keyB));\n\n/**\n * Frees the hash map. Does not free the keys or values themselves.\n */\nvoid hashmapFree(Hashmap* map);\n\n/**\n * Hashes the memory pointed to by key with the given size. Useful for\n * implementing hash functions.\n */\nint hashmapHash(void* key, size_t keySize);\n\n/**\n * Puts value for the given key in the map. Returns pre-existing value if\n * any.\n *\n * If memory allocation fails, this function returns NULL, the map's size\n * does not increase, and errno is set to ENOMEM.\n */\nvoid* hashmapPut(Hashmap* map, void* key, void* value);\n\n/**\n * Gets a value from the map. Returns NULL if no entry for the given key is\n * found or if the value itself is NULL.\n */\nvoid* hashmapGet(Hashmap* map, void* key);\n\n/**\n * Removes an entry from the map. Returns the removed value or NULL if no\n * entry was present.\n */\nvoid* hashmapRemove(Hashmap* map, void* key);\n\n/**\n * Invokes the given callback on each entry in the map. Stops iterating if\n * the callback returns false.\n */\nvoid hashmapForEach(Hashmap* map, bool (*callback)(void* key, void* value, void* context),\n                    void* context);\n\n/**\n * Concurrency support.\n */\n\n/**\n * Locks the hash map so only the current thread can access it.\n */\nvoid hashmapLock(Hashmap* map);\n\n/**\n * Unlocks the hash map so other threads can access it.\n */\nvoid hashmapUnlock(Hashmap* map);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __HASHMAP_H */\n"
  },
  {
    "path": "libcutils/include/cutils/iosched_policy.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_IOSCHED_POLICY_H\n#define __CUTILS_IOSCHED_POLICY_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    IoSchedClass_NONE,\n    IoSchedClass_RT,\n    IoSchedClass_BE,\n    IoSchedClass_IDLE,\n} IoSchedClass;\n\nextern int android_set_ioprio(int pid, IoSchedClass clazz, int ioprio);\nextern int android_get_ioprio(int pid, IoSchedClass *clazz, int *ioprio);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CUTILS_IOSCHED_POLICY_H */ \n"
  },
  {
    "path": "libcutils/include/cutils/klog.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _CUTILS_KLOG_H_\n#define _CUTILS_KLOG_H_\n\n#include <sys/cdefs.h>\n#include <sys/uio.h>\n#include <stdarg.h>\n\n__BEGIN_DECLS\n\nvoid klog_set_level(int level);\n\nvoid klog_write(int level, const char *fmt, ...)\n    __attribute__ ((format(printf, 2, 3)));\nvoid klog_writev(int level, const struct iovec* iov, int iov_count);\n\n__END_DECLS\n\n#define KLOG_ERROR_LEVEL   3\n#define KLOG_WARNING_LEVEL 4\n#define KLOG_NOTICE_LEVEL  5\n#define KLOG_INFO_LEVEL    6\n#define KLOG_DEBUG_LEVEL   7\n\n#define KLOG_ERROR(tag,x...)   klog_write(KLOG_ERROR_LEVEL, \"<3>\" tag \": \" x)\n#define KLOG_WARNING(tag,x...) klog_write(KLOG_WARNING_LEVEL, \"<4>\" tag \": \" x)\n#define KLOG_NOTICE(tag,x...)  klog_write(KLOG_NOTICE_LEVEL, \"<5>\" tag \": \" x)\n#define KLOG_INFO(tag,x...)    klog_write(KLOG_INFO_LEVEL, \"<6>\" tag \": \" x)\n#define KLOG_DEBUG(tag,x...)   klog_write(KLOG_DEBUG_LEVEL, \"<7>\" tag \": \" x)\n\n#endif\n"
  },
  {
    "path": "libcutils/include/cutils/list.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stddef.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif /* __cplusplus */\n\nstruct listnode\n{\n    struct listnode *next;\n    struct listnode *prev;\n};\n\n#define node_to_item(node, container, member) \\\n    (container *) (((char*) (node)) - offsetof(container, member))\n\n#define list_declare(name) \\\n    struct listnode name = { \\\n        .next = &(name), \\\n        .prev = &(name), \\\n    }\n\n#define list_for_each_reverse(node, list) \\\n    for ((node) = (list)->prev; (node) != (list); (node) = (node)->prev)\n\n#define list_for_each_safe(node, n, list) \\\n    for ((node) = (list)->next, (n) = (node)->next; \\\n         (node) != (list); \\\n         (node) = (n), (n) = (node)->next)\n\n#define list_for_each(node, list)                                                \\\n    for (struct listnode* __n = ((node) = (list)->next)->next; (node) != (list); \\\n         (node) = __n, __n = (node)->next)\n\nstatic inline void list_init(struct listnode *node)\n{\n    node->next = node;\n    node->prev = node;\n}\n\nstatic inline void list_add_tail(struct listnode *head, struct listnode *item)\n{\n    item->next = head;\n    item->prev = head->prev;\n    head->prev->next = item;\n    head->prev = item;\n}\n\nstatic inline void list_add_head(struct listnode *head, struct listnode *item)\n{\n    item->next = head->next;\n    item->prev = head;\n    head->next->prev = item;\n    head->next = item;\n}\n\nstatic inline void list_remove(struct listnode *item)\n{\n    item->next->prev = item->prev;\n    item->prev->next = item->next;\n}\n\n#define list_empty(list) ((list) == (list)->next)\n#define list_head(list) ((list)->next)\n#define list_tail(list) ((list)->prev)\n\n#ifdef __cplusplus\n};\n#endif /* __cplusplus */\n"
  },
  {
    "path": "libcutils/include/cutils/log.h",
    "content": "#include <log/log.h>\n"
  },
  {
    "path": "libcutils/include/cutils/memory.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#if defined(__GLIBC__) || defined(_WIN32)\n/* Declaration of strlcpy() for platforms that don't already have it. */\nsize_t strlcpy(char *dst, const char *src, size_t size);\n#endif\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n"
  },
  {
    "path": "libcutils/include/cutils/misc.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_MISC_H\n#define __CUTILS_MISC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n        /* Load an entire file into a malloc'd chunk of memory\n         * that is length_of_file + 1 (null terminator).  If\n         * sz is non-zero, return the size of the file via sz.\n         * Returns 0 on failure.\n         */\nextern void *load_file(const char *fn, unsigned *sz);\n\n        /* This is the range of UIDs (and GIDs) that are reserved\n         * for assigning to applications.\n         */\n#define FIRST_APPLICATION_UID 10000\n#define LAST_APPLICATION_UID 99999\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CUTILS_MISC_H */ \n"
  },
  {
    "path": "libcutils/include/cutils/multiuser.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_MULTIUSER_H\n#define __CUTILS_MULTIUSER_H\n\n#include <sys/types.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef uid_t userid_t;\ntypedef uid_t appid_t;\n\nextern userid_t multiuser_get_user_id(uid_t uid);\nextern appid_t multiuser_get_app_id(uid_t uid);\n\nextern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);\nextern uid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id);\nextern uid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid);\n\nextern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);\nextern gid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id);\nextern gid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id);\nextern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);\n\n/* TODO: switch callers over to multiuser_get_shared_gid() */\nextern gid_t multiuser_get_shared_app_gid(uid_t uid);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CUTILS_MULTIUSER_H */\n"
  },
  {
    "path": "libcutils/include/cutils/native_handle.h",
    "content": "/*\n * Copyright (C) 2009 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef NATIVE_HANDLE_H_\n#define NATIVE_HANDLE_H_\n\n#include <stdalign.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define NATIVE_HANDLE_MAX_FDS 1024\n#define NATIVE_HANDLE_MAX_INTS 1024\n\n/* Declare a char array for use with native_handle_init */\n#define NATIVE_HANDLE_DECLARE_STORAGE(name, maxFds, maxInts) \\\n    alignas(native_handle_t) char (name)[                            \\\n      sizeof(native_handle_t) + sizeof(int) * ((maxFds) + (maxInts))]\n\ntypedef struct native_handle\n{\n    int version;        /* sizeof(native_handle_t) */\n    int numFds;         /* number of file-descriptors at &data[0] */\n    int numInts;        /* number of ints at &data[numFds] */\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wzero-length-array\"\n#endif\n    int data[0];        /* numFds + numInts ints */\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n} native_handle_t;\n\ntypedef const native_handle_t* buffer_handle_t;\n\n/*\n * Closes the file descriptors contained in this native_handle_t, which may\n * either be untagged or tagged for ownership by this native_handle_t via\n * native_handle_set_tag(). Mixing untagged and tagged fds in the same\n * native_handle_t is not permitted and triggers an fdsan exception, but\n * native_handle_set_fdsan_tag() can be used to bring consistency if this is\n * intentional.\n *\n * If it's known that fds are tagged, prefer native_handle_close_with_tag() for\n * better safety.\n *\n * return 0 on success, or a negative error code on failure\n */\nint native_handle_close(const native_handle_t* h);\n\n/*\n * Equivalent to native_handle_close(), but throws an fdsan exception if the fds\n * are untagged. Use if it's known that the fds in this native_handle_t were\n * previously tagged via native_handle_set_tag().\n */\nint native_handle_close_with_tag(const native_handle_t* h);\n\n/*\n * Initializes a native_handle_t from storage.  storage must be declared with\n * NATIVE_HANDLE_DECLARE_STORAGE.  numFds and numInts must not respectively\n * exceed maxFds and maxInts used to declare the storage.\n */\nnative_handle_t* native_handle_init(char* storage, int numFds, int numInts);\n\n/*\n * Creates a native_handle_t and initializes it. Must be destroyed with\n * native_handle_delete(). Note that numFds must be <= NATIVE_HANDLE_MAX_FDS,\n * numInts must be <= NATIVE_HANDLE_MAX_INTS, and both must be >= 0.\n */\nnative_handle_t* native_handle_create(int numFds, int numInts);\n\n/*\n * Updates the fdsan tag for any file descriptors contained in the supplied\n * handle to indicate that they are owned by this handle and should only be\n * closed via native_handle_close()/native_handle_close_with_tag(). Each fd in\n * the handle must have a tag of either 0 (unset) or the tag associated with\n * this handle, otherwise an fdsan exception will be triggered.\n */\nvoid native_handle_set_fdsan_tag(const native_handle_t* handle);\n\n/*\n * Clears the fdsan tag for any file descriptors contained in the supplied\n * native_handle_t. Use if this native_handle_t is giving up ownership of its\n * fds, but the fdsan tags were previously set. Each fd in the handle must have\n * a tag of either 0 (unset) or the tag associated with this handle, otherwise\n * an fdsan exception will be triggered.\n */\nvoid native_handle_unset_fdsan_tag(const native_handle_t* handle);\n\n/*\n * Creates a native_handle_t and initializes it from another native_handle_t.\n * Must be destroyed with native_handle_delete().\n */\nnative_handle_t* native_handle_clone(const native_handle_t* handle);\n\n/*\n * Frees a native_handle_t allocated with native_handle_create().\n * This ONLY frees the memory allocated for the native_handle_t, but doesn't\n * close the file descriptors; which can be achieved with native_handle_close().\n *\n * return 0 on success, or a negative error code on failure\n */\nint native_handle_delete(native_handle_t* h);\n\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* NATIVE_HANDLE_H_ */\n"
  },
  {
    "path": "libcutils/include/cutils/partition_utils.h",
    "content": "/*\n * Copyright 2011, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_PARTITION_WIPED_H__\n#define __CUTILS_PARTITION_WIPED_H__\n\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\nint partition_wiped(const char* source);\n\n__END_DECLS\n\n#endif /* __CUTILS_PARTITION_WIPED_H__ */\n"
  },
  {
    "path": "libcutils/include/cutils/properties.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/cdefs.h>\n#include <stddef.h>\n#include <stdint.h>\n\n#if __has_include(<sys/system_properties.h>)\n#include <sys/system_properties.h>\n#else\n#define PROP_VALUE_MAX 92\n#endif\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n//\n// Deprecated.\n//\n// See <android-base/properties.h> for better API.\n//\n\n#define PROPERTY_KEY_MAX PROP_NAME_MAX\n#define PROPERTY_VALUE_MAX PROP_VALUE_MAX\n\n/* property_get: returns the length of the value which will never be\n** greater than PROPERTY_VALUE_MAX - 1 and will always be zero terminated.\n** (the length does not include the terminating zero).\n**\n** If the property read fails or returns an empty value, the default\n** value is used (if nonnull).\n*/\nint property_get(const char* key, char* value, const char* default_value);\n\n/* property_get_bool: returns the value of key coerced into a\n** boolean. If the property is not set, then the default value is returned.\n**\n* The following is considered to be true (1):\n**   \"1\", \"true\", \"y\", \"yes\", \"on\"\n**\n** The following is considered to be false (0):\n**   \"0\", \"false\", \"n\", \"no\", \"off\"\n**\n** The conversion is whitespace-sensitive (e.g. \" off\" will not be false).\n**\n** If no property with this key is set (or the key is NULL) or the boolean\n** conversion fails, the default value is returned.\n**/\nint8_t property_get_bool(const char *key, int8_t default_value);\n\n/* property_get_int64: returns the value of key truncated and coerced into a\n** int64_t. If the property is not set, then the default value is used.\n**\n** The numeric conversion is identical to strtoimax with the base inferred:\n** - All digits up to the first non-digit characters are read\n** - The longest consecutive prefix of digits is converted to a long\n**\n** Valid strings of digits are:\n** - An optional sign character + or -\n** - An optional prefix indicating the base (otherwise base 10 is assumed)\n**   -- 0 prefix is octal\n**   -- 0x / 0X prefix is hex\n**\n** Leading/trailing whitespace is ignored. Overflow/underflow will cause\n** numeric conversion to fail.\n**\n** If no property with this key is set (or the key is NULL) or the numeric\n** conversion fails, the default value is returned.\n**/\nint64_t property_get_int64(const char *key, int64_t default_value);\n\n/* property_get_int32: returns the value of key truncated and coerced into an\n** int32_t. If the property is not set, then the default value is used.\n**\n** The numeric conversion is identical to strtoimax with the base inferred:\n** - All digits up to the first non-digit characters are read\n** - The longest consecutive prefix of digits is converted to a long\n**\n** Valid strings of digits are:\n** - An optional sign character + or -\n** - An optional prefix indicating the base (otherwise base 10 is assumed)\n**   -- 0 prefix is octal\n**   -- 0x / 0X prefix is hex\n**\n** Leading/trailing whitespace is ignored. Overflow/underflow will cause\n** numeric conversion to fail.\n**\n** If no property with this key is set (or the key is NULL) or the numeric\n** conversion fails, the default value is returned.\n**/\nint32_t property_get_int32(const char *key, int32_t default_value);\n\n/* property_set: returns 0 on success, < 0 on failure\n*/\nint property_set(const char *key, const char *value);\n\nint property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);\n\n#if defined(__BIONIC_FORTIFY)\n#define __property_get_err_str \"property_get() called with too small of a buffer\"\n\n#if defined(__clang__)\n\n/* Some projects use -Weverything; diagnose_if is clang-specific. */\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wgcc-compat\"\nint property_get(const char* key, char* value, const char* default_value)\n    __clang_error_if(__bos(value) != __BIONIC_FORTIFY_UNKNOWN_SIZE &&\n                         __bos(value) < PROPERTY_VALUE_MAX,\n                     __property_get_err_str);\n#pragma clang diagnostic pop\n\n#else /* defined(__clang__) */\n\nextern int __property_get_real(const char *, char *, const char *)\n    __asm__(__USER_LABEL_PREFIX__ \"property_get\");\n__errordecl(__property_get_too_small_error, __property_get_err_str);\n\n__BIONIC_FORTIFY_INLINE\nint property_get(const char *key, char *value, const char *default_value) {\n    size_t bos = __bos(value);\n    if (bos < PROPERTY_VALUE_MAX) {\n        __property_get_too_small_error();\n    }\n    return __property_get_real(key, value, default_value);\n}\n\n#endif /* defined(__clang__) */\n\n#undef __property_get_err_str\n#endif /* defined(__BIONIC_FORTIFY) */\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libcutils/include/cutils/qtaguid.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_QTAGUID_H\n#define __CUTILS_QTAGUID_H\n\n#include <sys/types.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Set tags (and owning UIDs) for network sockets.\n */\nextern int qtaguid_tagSocket(int sockfd, int tag, uid_t uid);\n\n/*\n * Untag a network socket before closing.\n */\nextern int qtaguid_untagSocket(int sockfd);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CUTILS_QTAG_UID_H */\n"
  },
  {
    "path": "libcutils/include/cutils/record_stream.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * A simple utility for reading fixed records out of a stream fd\n */\n\n#ifndef _CUTILS_RECORD_STREAM_H\n#define _CUTILS_RECORD_STREAM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h>\n\ntypedef struct RecordStream RecordStream;\n\nextern RecordStream *record_stream_new(int fd, size_t maxRecordLen);\nextern void record_stream_free(RecordStream *p_rs);\n\nextern int record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, \n                                    size_t *p_outRecordLen);\n\n#ifdef __cplusplus\n}\n#endif\n\n\n#endif /*_CUTILS_RECORD_STREAM_H*/\n\n"
  },
  {
    "path": "libcutils/include/cutils/sched_policy.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_SCHED_POLICY_H\n#define __CUTILS_SCHED_POLICY_H\n\n/*\n * For backwards compatibility only\n * New users should include processgroup/sched_policy.h directly\n */\n#include <processgroup/sched_policy.h>\n\n#endif /* __CUTILS_SCHED_POLICY_H */ \n"
  },
  {
    "path": "libcutils/include/cutils/sockets.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <errno.h>\n#include <limits.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdbool.h>\n\n#if defined(_WIN32)\n\n#include <winsock2.h>\n#include <ws2tcpip.h>\n\ntypedef int  socklen_t;\ntypedef SOCKET cutils_socket_t;\n\n#else\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n\ntypedef int cutils_socket_t;\n#define INVALID_SOCKET (-1)\n\n#endif\n\n#define ANDROID_SOCKET_ENV_PREFIX\t\"ANDROID_SOCKET_\"\n#define ANDROID_SOCKET_DIR\t\t\"/dev/socket\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * android_get_control_socket - simple helper function to get the file\n * descriptor of our init-managed Unix domain socket. `name' is the name of the\n * socket, as given in init.rc. Returns -1 on error.\n */\nint android_get_control_socket(const char* name);\n\n/*\n * See also android.os.LocalSocketAddress.Namespace\n */\n// Linux \"abstract\" (non-filesystem) namespace\n#define ANDROID_SOCKET_NAMESPACE_ABSTRACT 0\n// Android \"reserved\" (/dev/socket) namespace\n#define ANDROID_SOCKET_NAMESPACE_RESERVED 1\n// Normal filesystem namespace\n#define ANDROID_SOCKET_NAMESPACE_FILESYSTEM 2\n\n/*\n * Functions to create sockets for some common usages.\n *\n * All these functions are implemented for Unix, but only a few are implemented\n * for Windows. Those which are can be identified by the cutils_socket_t\n * return type. The idea is to be able to use this return value with the\n * standard Unix socket functions on any platform.\n *\n * On Unix the returned cutils_socket_t is a standard int file descriptor and\n * can always be used as normal with all file descriptor functions.\n *\n * On Windows utils_socket_t is an unsigned int pointer, and is only valid\n * with functions that specifically take a socket, e.g. send(), sendto(),\n * recv(), and recvfrom(). General file descriptor functions such as read(),\n * write(), and close() will not work with utils_socket_t and will require\n * special handling.\n *\n * These functions return INVALID_SOCKET (-1) on failure for all platforms.\n */\ncutils_socket_t socket_network_client(const char* host, int port, int type);\nint socket_network_client_timeout(const char* host, int port, int type,\n                                  int timeout, int* getaddrinfo_error);\nint socket_local_server(const char* name, int namespaceId, int type);\nint socket_local_server_bind(int s, const char* name, int namespaceId);\nint socket_local_client_connect(int fd, const char *name, int namespaceId,\n                                int type);\nint socket_local_client(const char* name, int namespaceId, int type);\ncutils_socket_t socket_inaddr_any_server(int port, int type);\n\n/*\n * Closes a cutils_socket_t. Windows doesn't allow calling close() on a socket\n * so this is a cross-platform way to close a cutils_socket_t.\n *\n * Returns 0 on success.\n */\nint socket_close(cutils_socket_t sock);\n\n/*\n * Returns the local port the socket is bound to or -1 on error.\n */\nint socket_get_local_port(cutils_socket_t sock);\n\n/*\n * Sends to a socket from multiple buffers; wraps writev() on Unix or WSASend()\n * on Windows. This can give significant speedup compared to calling send()\n * multiple times.\n *\n * Example usage:\n *   cutils_socket_buffer_t buffers[2] = { {data0, len0}, {data1, len1} };\n *   socket_send_buffers(sock, buffers, 2);\n *\n * If you try to pass more than SOCKET_SEND_BUFFERS_MAX_BUFFERS buffers into\n * this function it will return -1 without sending anything.\n *\n * Returns the number of bytes written or -1 on error.\n */\ntypedef struct {\n  const void* data;\n  size_t length;\n} cutils_socket_buffer_t;\n\n#define SOCKET_SEND_BUFFERS_MAX_BUFFERS 16\n\nssize_t socket_send_buffers(cutils_socket_t sock,\n                            const cutils_socket_buffer_t* buffers,\n                            size_t num_buffers);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libcutils/include/cutils/str_parms.h",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_STR_PARMS_H\n#define __CUTILS_STR_PARMS_H\n\n#include <stdint.h>\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\nstruct str_parms;\n\nstruct str_parms *str_parms_create(void);\nstruct str_parms *str_parms_create_str(const char *_string);\nvoid str_parms_destroy(struct str_parms *str_parms);\n\nvoid str_parms_del(struct str_parms *str_parms, const char *key);\n\nint str_parms_add_str(struct str_parms *str_parms, const char *key,\n                      const char *value);\nint str_parms_add_int(struct str_parms *str_parms, const char *key, int value);\n\nint str_parms_add_float(struct str_parms *str_parms, const char *key,\n                        float value);\n\n// Returns non-zero if the str_parms contains the specified key.\nint str_parms_has_key(struct str_parms *str_parms, const char *key);\n\n// Gets value associated with the specified key (if present), placing it in the buffer\n// pointed to by the out_val parameter.  Returns the length of the returned string value.\n// If 'key' isn't in the parms, then return -ENOENT (-2) and leave 'out_val' untouched.\nint str_parms_get_str(struct str_parms *str_parms, const char *key,\n                      char *out_val, int len);\nint str_parms_get_int(struct str_parms *str_parms, const char *key,\n                      int *out_val);\nint str_parms_get_float(struct str_parms *str_parms, const char *key,\n                        float *out_val);\n\nchar *str_parms_to_str(struct str_parms *str_parms);\n\n/* debug */\nvoid str_parms_dump(struct str_parms *str_parms);\n\n__END_DECLS\n\n#endif /* __CUTILS_STR_PARMS_H */\n"
  },
  {
    "path": "libcutils/include/cutils/trace.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_CUTILS_TRACE_H\n#define _LIBS_CUTILS_TRACE_H\n\n#include <inttypes.h>\n#include <stdatomic.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/cdefs.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <cutils/compiler.h>\n\n__BEGIN_DECLS\n\n/**\n * The ATRACE_TAG macro can be defined before including this header to trace\n * using one of the tags defined below.  It must be defined to one of the\n * following ATRACE_TAG_* macros.  The trace tag is used to filter tracing in\n * userland to avoid some of the runtime cost of tracing when it is not desired.\n *\n * Defining ATRACE_TAG to be ATRACE_TAG_ALWAYS will result in the tracing always\n * being enabled - this should ONLY be done for debug code, as userland tracing\n * has a performance cost even when the trace is not being recorded.  Defining\n * ATRACE_TAG to be ATRACE_TAG_NEVER or leaving ATRACE_TAG undefined will result\n * in the tracing always being disabled.\n *\n * ATRACE_TAG_HAL should be bitwise ORed with the relevant tags for tracing\n * within a hardware module.  For example a camera hardware module would set:\n * #define ATRACE_TAG  (ATRACE_TAG_CAMERA | ATRACE_TAG_HAL)\n *\n * Keep these in sync with frameworks/base/core/java/android/os/Trace.java.\n */\n#define ATRACE_TAG_NEVER            0       // This tag is never enabled.\n#define ATRACE_TAG_ALWAYS           (1<<0)  // This tag is always enabled.\n#define ATRACE_TAG_GRAPHICS         (1<<1)\n#define ATRACE_TAG_INPUT            (1<<2)\n#define ATRACE_TAG_VIEW             (1<<3)\n#define ATRACE_TAG_WEBVIEW          (1<<4)\n#define ATRACE_TAG_WINDOW_MANAGER   (1<<5)\n#define ATRACE_TAG_ACTIVITY_MANAGER (1<<6)\n#define ATRACE_TAG_SYNC_MANAGER     (1<<7)\n#define ATRACE_TAG_AUDIO            (1<<8)\n#define ATRACE_TAG_VIDEO            (1<<9)\n#define ATRACE_TAG_CAMERA           (1<<10)\n#define ATRACE_TAG_HAL              (1<<11)\n#define ATRACE_TAG_APP              (1<<12)\n#define ATRACE_TAG_RESOURCES        (1<<13)\n#define ATRACE_TAG_DALVIK           (1<<14)\n#define ATRACE_TAG_RS               (1<<15)\n#define ATRACE_TAG_BIONIC           (1<<16)\n#define ATRACE_TAG_POWER            (1<<17)\n#define ATRACE_TAG_PACKAGE_MANAGER  (1<<18)\n#define ATRACE_TAG_SYSTEM_SERVER    (1<<19)\n#define ATRACE_TAG_DATABASE         (1<<20)\n#define ATRACE_TAG_NETWORK          (1<<21)\n#define ATRACE_TAG_ADB              (1<<22)\n#define ATRACE_TAG_VIBRATOR         (1<<23)\n#define ATRACE_TAG_AIDL             (1<<24)\n#define ATRACE_TAG_NNAPI            (1<<25)\n#define ATRACE_TAG_RRO              (1<<26)\n#define ATRACE_TAG_THERMAL          (1 << 27)\n#define ATRACE_TAG_LAST             ATRACE_TAG_THERMAL\n\n// Reserved for initialization.\n#define ATRACE_TAG_NOT_READY        (1ULL<<63)\n\n#define ATRACE_TAG_VALID_MASK ((ATRACE_TAG_LAST - 1) | ATRACE_TAG_LAST)\n\n#ifndef ATRACE_TAG\n#define ATRACE_TAG ATRACE_TAG_NEVER\n#elif ATRACE_TAG > ATRACE_TAG_VALID_MASK\n#error ATRACE_TAG must be defined to be one of the tags defined in cutils/trace.h\n#endif\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_begin_body(const char*);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_end_body();\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_async_begin_body(const char*, int32_t);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_async_end_body(const char*, int32_t);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_async_for_track_begin_body(const char*, const char*, int32_t);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_async_for_track_end_body(const char*, int32_t);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_instant_body(const char*);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_instant_for_track_body(const char*, const char*);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_int_body(const char*, int32_t);\n\n/** Internal implementation detail. Do not use. */\nvoid atrace_int64_body(const char*, int64_t);\n\n/**\n * Opens the trace file for writing and reads the property for initial tags.\n * The atrace.tags.enableflags property sets the tags to trace.\n * This function should not be explicitly called, the first call to any normal\n * trace function will cause it to be run safely.\n */\nvoid atrace_setup();\n\n/**\n * If tracing is ready, set atrace_enabled_tags to the system property\n * debug.atrace.tags.enableflags. Can be used as a sysprop change callback.\n */\nvoid atrace_update_tags();\n\n/**\n * Set whether tracing is enabled for the current process.  This is used to\n * prevent tracing within the Zygote process.\n */\nvoid atrace_set_tracing_enabled(bool enabled);\n\n/**\n * This is always set to false. This forces code that uses an old version\n * of this header to always call into atrace_setup, in which we call\n * atrace_init unconditionally.\n */\nextern atomic_bool atrace_is_ready;\n\n/**\n * Set of ATRACE_TAG flags to trace for, initialized to ATRACE_TAG_NOT_READY.\n * A value of zero indicates setup has failed.\n * Any other nonzero value indicates setup has succeeded, and tracing is on.\n */\nextern uint64_t atrace_enabled_tags;\n\n/**\n * Handle to the kernel's trace buffer, initialized to -1.\n * Any other value indicates setup has succeeded, and is a valid fd for tracing.\n */\nextern int atrace_marker_fd;\n\n/**\n * atrace_init readies the process for tracing by opening the trace_marker file.\n * Calling any trace function causes this to be run, so calling it is optional.\n * This can be explicitly run to avoid setup delay on first trace function.\n */\n#define ATRACE_INIT() atrace_init()\n#define ATRACE_GET_ENABLED_TAGS() atrace_get_enabled_tags()\n\nvoid atrace_init();\nuint64_t atrace_get_enabled_tags();\n\n/**\n * Test if a given tag is currently enabled.\n * Returns nonzero if the tag is enabled, otherwise zero.\n * It can be used as a guard condition around more expensive trace calculations.\n */\n#define ATRACE_ENABLED() atrace_is_tag_enabled(ATRACE_TAG)\nstatic inline uint64_t atrace_is_tag_enabled(uint64_t tag)\n{\n    return atrace_get_enabled_tags() & tag;\n}\n\n/**\n * Trace the beginning of a context.  name is used to identify the context.\n * This is often used to time function execution.\n */\n#define ATRACE_BEGIN(name) atrace_begin(ATRACE_TAG, name)\nstatic inline void atrace_begin(uint64_t tag, const char* name)\n{\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_begin_body(name);\n    }\n}\n\n/**\n * Trace the end of a context.\n * This should match up (and occur after) a corresponding ATRACE_BEGIN.\n */\n#define ATRACE_END() atrace_end(ATRACE_TAG)\nstatic inline void atrace_end(uint64_t tag)\n{\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_end_body();\n    }\n}\n\n/**\n * Trace the beginning of an asynchronous event. Unlike ATRACE_BEGIN/ATRACE_END\n * contexts, asynchronous events do not need to be nested. The name describes\n * the event, and the cookie provides a unique identifier for distinguishing\n * simultaneous events. The name and cookie used to begin an event must be\n * used to end it.\n */\n#define ATRACE_ASYNC_BEGIN(name, cookie) \\\n    atrace_async_begin(ATRACE_TAG, name, cookie)\nstatic inline void atrace_async_begin(uint64_t tag, const char* name,\n        int32_t cookie)\n{\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_async_begin_body(name, cookie);\n    }\n}\n\n/**\n * Trace the end of an asynchronous event.\n * This should have a corresponding ATRACE_ASYNC_BEGIN.\n */\n#define ATRACE_ASYNC_END(name, cookie) atrace_async_end(ATRACE_TAG, name, cookie)\nstatic inline void atrace_async_end(uint64_t tag, const char* name, int32_t cookie)\n{\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_async_end_body(name, cookie);\n    }\n}\n\n/**\n * Trace the beginning of an asynchronous event. In addition to the name and a\n * cookie as in ATRACE_ASYNC_BEGIN/ATRACE_ASYNC_END, a track name argument is\n * provided, which is the name of the row where this async event should be\n * recorded. The track name, name, and cookie used to begin an event must be\n * used to end it.\n * The cookie here must be unique on the track_name level, not the name level.\n */\n#define ATRACE_ASYNC_FOR_TRACK_BEGIN(track_name, name, cookie) \\\n    atrace_async_for_track_begin(ATRACE_TAG, track_name, name, cookie)\nstatic inline void atrace_async_for_track_begin(uint64_t tag, const char* track_name,\n                                                const char* name, int32_t cookie) {\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_async_for_track_begin_body(track_name, name, cookie);\n    }\n}\n\n/**\n * Trace the end of an asynchronous event.\n * This should correspond to a previous ATRACE_ASYNC_FOR_TRACK_BEGIN.\n */\n#define ATRACE_ASYNC_FOR_TRACK_END(track_name, cookie) \\\n    atrace_async_for_track_end(ATRACE_TAG, track_name, cookie)\nstatic inline void atrace_async_for_track_end(uint64_t tag, const char* track_name,\n                                              int32_t cookie) {\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_async_for_track_end_body(track_name, cookie);\n    }\n}\n\n/**\n * Trace an instantaneous context. name is used to identify the context.\n *\n * An \"instant\" is an event with no defined duration. Visually is displayed like a single marker\n * in the timeline (rather than a span, in the case of begin/end events).\n *\n * By default, instant events are added into a dedicated track that has the same name of the event.\n * Use atrace_instant_for_track to put different instant events into the same timeline track/row.\n */\n#define ATRACE_INSTANT(name) atrace_instant(ATRACE_TAG, name)\nstatic inline void atrace_instant(uint64_t tag, const char* name) {\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_instant_body(name);\n    }\n}\n\n/**\n * Trace an instantaneous context. name is used to identify the context.\n * track_name is the name of the row where the event should be recorded.\n *\n * An \"instant\" is an event with no defined duration. Visually is displayed like a single marker\n * in the timeline (rather than a span, in the case of begin/end events).\n */\n#define ATRACE_INSTANT_FOR_TRACK(trackName, name) \\\n    atrace_instant_for_track(ATRACE_TAG, trackName, name)\nstatic inline void atrace_instant_for_track(uint64_t tag, const char* track_name,\n                                            const char* name) {\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_instant_for_track_body(track_name, name);\n    }\n}\n\n/**\n * Traces an integer counter value.  name is used to identify the counter.\n * This can be used to track how a value changes over time.\n */\n#define ATRACE_INT(name, value) atrace_int(ATRACE_TAG, name, value)\nstatic inline void atrace_int(uint64_t tag, const char* name, int32_t value)\n{\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_int_body(name, value);\n    }\n}\n\n/**\n * Traces a 64-bit integer counter value.  name is used to identify the\n * counter. This can be used to track how a value changes over time.\n */\n#define ATRACE_INT64(name, value) atrace_int64(ATRACE_TAG, name, value)\nstatic inline void atrace_int64(uint64_t tag, const char* name, int64_t value)\n{\n    if (CC_UNLIKELY(atrace_is_tag_enabled(tag))) {\n        atrace_int64_body(name, value);\n    }\n}\n\n__END_DECLS\n\n#endif // _LIBS_CUTILS_TRACE_H\n"
  },
  {
    "path": "libcutils/include/cutils/uevent.h",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __CUTILS_UEVENT_H\n#define __CUTILS_UEVENT_H\n\n#include <stdbool.h>\n#include <sys/socket.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint uevent_create_socket(int buf_sz, bool passcred);\nint uevent_bind(int socket);\nint uevent_open_socket(int buf_sz, bool passcred);\nssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length);\nssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid);\nssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* __CUTILS_UEVENT_H */\n"
  },
  {
    "path": "libcutils/include/private/android_filesystem_config.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * This file is consumed by build/tools/fs_config and is used\n * for generating various files. Anything #define AID_<name>\n * becomes the mapping for getpwnam/getpwuid, etc. The <name>\n * field is lowercased.\n * For example:\n * #define AID_FOO_BAR 6666 becomes a friendly name of \"foo_bar\"\n *\n * The above holds true with the exception of:\n *   mediacodec\n *   mediaex\n *   mediadrm\n * Whose friendly names do not match the #define statements.\n *\n * This file must only be used for platform (Google managed, and submitted through AOSP), AIDs.  3rd\n * party AIDs must be added via config.fs, which will place them in the corresponding partition's\n * passwd and group files.  There are ranges in this file reserved for AIDs for each 3rd party\n * partition, from which the system reads passwd and group files.\n */\n\n#pragma once\n\n/* This is the main Users and Groups config for the platform.\n * DO NOT EVER RENUMBER\n */\n\n#define AID_ROOT 0 /* traditional unix root user */\n\n/* The following are for tests like LTP and should only be used for testing. */\n#define AID_DAEMON 1 /* Traditional unix daemon owner. */\n#define AID_BIN 2    /* Traditional unix binaries owner. */\n#define AID_SYS 3    /* A group with the same gid on Linux/macOS/Android. */\n\n#define AID_SYSTEM 1000 /* system server */\n\n#define AID_RADIO 1001           /* telephony subsystem, RIL */\n#define AID_BLUETOOTH 1002       /* bluetooth subsystem */\n#define AID_GRAPHICS 1003        /* graphics devices */\n#define AID_INPUT 1004           /* input devices */\n#define AID_AUDIO 1005           /* audio devices */\n#define AID_CAMERA 1006          /* camera devices */\n#define AID_LOG 1007             /* log devices */\n#define AID_COMPASS 1008         /* compass device */\n#define AID_MOUNT 1009           /* mountd socket */\n#define AID_WIFI 1010            /* wifi subsystem */\n#define AID_ADB 1011             /* android debug bridge (adbd) */\n#define AID_INSTALL 1012         /* group for installing packages */\n#define AID_MEDIA 1013           /* mediaserver process */\n#define AID_DHCP 1014            /* dhcp client */\n#define AID_SDCARD_RW 1015       /* external storage write access */\n#define AID_VPN 1016             /* vpn system */\n#define AID_KEYSTORE 1017        /* keystore subsystem */\n#define AID_USB 1018             /* USB devices */\n#define AID_DRM 1019             /* DRM server */\n#define AID_MDNSR 1020           /* MulticastDNSResponder (service discovery) */\n#define AID_GPS 1021             /* GPS daemon */\n#define AID_UNUSED1 1022         /* deprecated, DO NOT USE */\n#define AID_MEDIA_RW 1023        /* internal media storage write access */\n#define AID_MTP 1024             /* MTP USB driver access */\n#define AID_UNUSED2 1025         /* deprecated, DO NOT USE */\n#define AID_DRMRPC 1026          /* group for drm rpc */\n#define AID_NFC 1027             /* nfc subsystem */\n#define AID_SDCARD_R 1028        /* external storage read access */\n#define AID_CLAT 1029            /* clat part of nat464 */\n#define AID_LOOP_RADIO 1030      /* loop radio devices */\n#define AID_MEDIA_DRM 1031       /* MediaDrm plugins */\n#define AID_PACKAGE_INFO 1032    /* access to installed package details */\n#define AID_SDCARD_PICS 1033     /* external storage photos access */\n#define AID_SDCARD_AV 1034       /* external storage audio/video access */\n#define AID_SDCARD_ALL 1035      /* access all users external storage */\n#define AID_LOGD 1036            /* log daemon */\n#define AID_SHARED_RELRO 1037    /* creator of shared GNU RELRO files */\n#define AID_DBUS 1038            /* dbus-daemon IPC broker process */\n#define AID_TLSDATE 1039         /* tlsdate unprivileged user */\n#define AID_MEDIA_EX 1040        /* mediaextractor process */\n#define AID_AUDIOSERVER 1041     /* audioserver process */\n#define AID_METRICS_COLL 1042    /* metrics_collector process */\n#define AID_METRICSD 1043        /* metricsd process */\n#define AID_WEBSERV 1044         /* webservd process */\n#define AID_DEBUGGERD 1045       /* debuggerd unprivileged user */\n#define AID_MEDIA_CODEC 1046     /* mediacodec process */\n#define AID_CAMERASERVER 1047    /* cameraserver process */\n#define AID_FIREWALL 1048        /* firewalld process */\n#define AID_TRUNKS 1049          /* trunksd process (TPM daemon) */\n#define AID_NVRAM 1050           /* Access-controlled NVRAM */\n#define AID_DNS 1051             /* DNS resolution daemon (system: netd) */\n#define AID_DNS_TETHER 1052      /* DNS resolution daemon (tether: dnsmasq) */\n#define AID_WEBVIEW_ZYGOTE 1053  /* WebView zygote process */\n#define AID_VEHICLE_NETWORK 1054 /* Vehicle network service */\n#define AID_MEDIA_AUDIO 1055     /* GID for audio files on internal media storage */\n#define AID_MEDIA_VIDEO 1056     /* GID for video files on internal media storage */\n#define AID_MEDIA_IMAGE 1057     /* GID for image files on internal media storage */\n#define AID_TOMBSTONED 1058      /* tombstoned user */\n#define AID_MEDIA_OBB 1059       /* GID for OBB files on internal media storage */\n#define AID_ESE 1060             /* embedded secure element (eSE) subsystem */\n#define AID_OTA_UPDATE 1061      /* resource tracking UID for OTA updates */\n#define AID_AUTOMOTIVE_EVS 1062  /* Automotive rear and surround view system */\n#define AID_LOWPAN 1063          /* LoWPAN subsystem */\n#define AID_HSM 1064             /* hardware security module subsystem */\n#define AID_RESERVED_DISK 1065   /* GID that has access to reserved disk space */\n#define AID_STATSD 1066          /* statsd daemon */\n#define AID_INCIDENTD 1067       /* incidentd daemon */\n#define AID_SECURE_ELEMENT 1068  /* secure element subsystem */\n#define AID_LMKD 1069            /* low memory killer daemon */\n#define AID_LLKD 1070            /* live lock daemon */\n#define AID_IORAPD 1071          /* input/output readahead and pin daemon */\n#define AID_GPU_SERVICE 1072     /* GPU service daemon */\n#define AID_NETWORK_STACK 1073   /* network stack service */\n#define AID_GSID 1074            /* GSI service daemon */\n#define AID_FSVERITY_CERT 1075   /* fs-verity key ownership in keystore */\n#define AID_CREDSTORE 1076       /* identity credential manager service */\n#define AID_EXTERNAL_STORAGE 1077 /* Full external storage access including USB OTG volumes */\n#define AID_EXT_DATA_RW 1078      /* GID for app-private data directories on external storage */\n#define AID_EXT_OBB_RW 1079       /* GID for OBB directories on external storage */\n#define AID_CONTEXT_HUB 1080      /* GID for access to the Context Hub */\n#define AID_VIRTUALIZATIONSERVICE 1081 /* VirtualizationService daemon */\n#define AID_ARTD 1082             /* ART Service daemon */\n#define AID_UWB 1083              /* UWB subsystem */\n#define AID_THREAD_NETWORK 1084   /* Thread Network subsystem */\n#define AID_DICED 1085            /* Android's DICE daemon */\n#define AID_DMESGD 1086           /* dmesg parsing daemon for kernel report collection */\n#define AID_JC_WEAVER 1087        /* Javacard Weaver HAL - to manage omapi ARA rules */\n#define AID_JC_STRONGBOX 1088     /* Javacard Strongbox HAL - to manage omapi ARA rules */\n#define AID_JC_IDENTITYCRED 1089  /* Javacard Identity Cred HAL - to manage omapi ARA rules */\n#define AID_SDK_SANDBOX 1090      /* SDK sandbox virtual UID */\n#define AID_SECURITY_LOG_WRITER 1091 /* write to security log */\n#define AID_PRNG_SEEDER 1092         /* PRNG seeder daemon */\n#define AID_UPROBESTATS 1093         /* uid for uprobestats */\n#define AID_CROS_EC 1094             /* uid for accessing ChromeOS EC (cros_ec) */\n#define AID_MMD 1095                 /* uid for memory management daemon */\n// Additions to this file must be made in AOSP, *not* in internal branches.\n// You will also need to update expect_ids() in bionic/tests/grp_pwd_test.cpp.\n\n#define AID_SHELL 2000 /* adb and debug shell user */\n#define AID_CACHE 2001 /* cache access */\n#define AID_DIAG 2002  /* access to diagnostic resources */\n\n/* The range 2900-2999 is reserved for the vendor partition */\n/* Note that the two 'OEM' ranges pre-dated the vendor partition, so they take the legacy 'OEM'\n * name. Additionally, they pre-dated passwd/group files, so there are users and groups named oem_#\n * created automatically for all values in these ranges.  If there is a user/group in a passwd/group\n * file corresponding to this range, both the oem_# and user/group names will resolve to the same\n * value. */\n#define AID_OEM_RESERVED_START 2900\n#define AID_OEM_RESERVED_END 2999\n\n/* The 3000 series are intended for use as supplemental group ids only.\n * They indicate special Android capabilities that the kernel is aware of. */\n#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */\n#define AID_NET_BT 3002       /* bluetooth: create sco, rfcomm or l2cap sockets */\n#define AID_INET 3003         /* can create AF_INET and AF_INET6 sockets */\n#define AID_NET_RAW 3004      /* can create raw INET sockets */\n#define AID_NET_ADMIN 3005    /* can configure interfaces and routing tables. */\n#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */\n#define AID_NET_BW_ACCT 3007  /* change bandwidth statistics accounting */\n#define AID_READPROC 3009     /* Allow /proc read access */\n#define AID_WAKELOCK 3010     /* Allow system wakelock read/write access */\n#define AID_UHID 3011         /* Allow read/write to /dev/uhid node */\n#define AID_READTRACEFS 3012  /* Allow tracefs read */\n#define AID_VIRTUALMACHINE 3013 /* Allows VMs to tune for performance*/\n// Additions to this file must be made in AOSP, *not* in internal branches.\n// You will also need to update expect_ids() in bionic/tests/grp_pwd_test.cpp.\n\n/* The range 5000-5999 is also reserved for vendor partition. */\n#define AID_OEM_RESERVED_2_START 5000\n#define AID_OEM_RESERVED_2_END 5999\n\n/* The range 6000-6499 is reserved for the system partition. */\n#define AID_SYSTEM_RESERVED_START 6000\n#define AID_SYSTEM_RESERVED_END 6499\n\n/* The range 6500-6999 is reserved for the odm partition. */\n#define AID_ODM_RESERVED_START 6500\n#define AID_ODM_RESERVED_END 6999\n\n/* The range 7000-7499 is reserved for the product partition. */\n#define AID_PRODUCT_RESERVED_START 7000\n#define AID_PRODUCT_RESERVED_END 7499\n\n/* The range 7500-7999 is reserved for the system_ext partition. */\n#define AID_SYSTEM_EXT_RESERVED_START 7500\n#define AID_SYSTEM_EXT_RESERVED_END 7999\n\n#define AID_EVERYBODY 9997 /* shared between all apps in the same profile */\n#define AID_MISC 9998      /* access to misc storage */\n#define AID_NOBODY 9999\n\n#define AID_APP 10000       /* TODO: switch users over to AID_APP_START */\n#define AID_APP_START 10000 /* first app user */\n#define AID_APP_END 19999   /* last app user */\n\n#define AID_CACHE_GID_START 20000 /* start of gids for apps to mark cached data */\n#define AID_CACHE_GID_END 29999   /* end of gids for apps to mark cached data */\n\n#define AID_EXT_GID_START 30000 /* start of gids for apps to mark external data */\n#define AID_EXT_GID_END 39999   /* end of gids for apps to mark external data */\n\n#define AID_EXT_CACHE_GID_START 40000 /* start of gids for apps to mark external cached data */\n#define AID_EXT_CACHE_GID_END 49999   /* end of gids for apps to mark external cached data */\n\n#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */\n#define AID_SHARED_GID_END 59999   /* end of gids for apps in each user to share */\n\n/*\n * This is a magic number in the kernel and not something that was picked\n * arbitrarily. This value is returned whenever a uid that has no mapping in the\n * user namespace is returned to userspace:\n * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/highuid.h?h=v4.4#n40\n */\n#define AID_OVERFLOWUID 65534 /* unmapped user in the user namespace */\n\n/* use the ranges below to determine whether a process is sdk sandbox */\n#define AID_SDK_SANDBOX_PROCESS_START 20000 /* start of uids allocated to sdk sandbox processes */\n#define AID_SDK_SANDBOX_PROCESS_END 29999   /* end of uids allocated to sdk sandbox processes */\n\n/* use the ranges below to determine whether a process is isolated */\n#define AID_ISOLATED_START 90000 /* start of uids for fully isolated sandboxed processes */\n#define AID_ISOLATED_END 99999   /* end of uids for fully isolated sandboxed processes */\n\n#define AID_USER 100000        /* TODO: switch users over to AID_USER_OFFSET */\n#define AID_USER_OFFSET 100000 /* offset for uid ranges for each user */\n\n/*\n * android_ids has moved to pwd/grp functionality.\n * If you need to add one, the structure is now\n * auto-generated based on the AID_ constraints\n * documented at the top of this header file.\n * Also see build/tools/fs_config for more details.\n */\n"
  },
  {
    "path": "libcutils/include/private/android_projectid_config.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n/*\n * This file describes the project ID values we use for filesystem quota\n * tracking.  It is used on devices that don't have the sdcardfs kernel module,\n * which requires us to use filesystem project IDs for efficient quota\n * calculation.\n *\n * These values are typically set on files and directories using extended\n * attributes; see vold for examples.\n */\n\n/* Default project ID for files on external storage. */\n#define PROJECT_ID_EXT_DEFAULT 1000\n/* Project ID for audio files on external storage. */\n#define PROJECT_ID_EXT_MEDIA_AUDIO 1001\n/* Project ID for video files on external storage. */\n#define PROJECT_ID_EXT_MEDIA_VIDEO 1002\n/* Project ID for image files on external storage. */\n#define PROJECT_ID_EXT_MEDIA_IMAGE 1003\n\n/* Start of project IDs for apps to mark external app data. */\n#define PROJECT_ID_EXT_DATA_START 20000\n/* End of project IDs for apps to mark external app data. */\n#define PROJECT_ID_EXT_DATA_END 29999\n\n/* Start of project IDs for apps to mark external cached data. */\n#define PROJECT_ID_EXT_CACHE_START 30000\n/* End of project IDs for apps to mark external cached data. */\n#define PROJECT_ID_EXT_CACHE_END 39999\n\n/* Start of project IDs for apps to mark external OBB data. */\n#define PROJECT_ID_EXT_OBB_START 40000\n/* End of project IDs for apps to mark external OBB data. */\n#define PROJECT_ID_EXT_OBB_END 49999\n\n/* Start of project IDs for apps to mark internal app data. */\n#define PROJECT_ID_APP_START 50000\n/* End of project IDs for apps to mark internal app data. */\n#define PROJECT_ID_APP_END 59999\n\n/* Start of project IDs for apps to mark internal app cache data. */\n#define PROJECT_ID_APP_CACHE_START 60000\n/* End of project IDs for apps to mark internal app cache data. */\n#define PROJECT_ID_APP_CACHE_END 69999\n"
  },
  {
    "path": "libcutils/include/private/canned_fs_config.h",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <inttypes.h>\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\nint load_canned_fs_config(const char* fn);\nvoid canned_fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid,\n                      unsigned* gid, unsigned* mode, uint64_t* capabilities);\n\n__END_DECLS\n"
  },
  {
    "path": "libcutils/include/private/fs_config.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* This file is used to define the properties of the filesystem\n** images generated by build tools (mkbootfs and mkyaffs2image) and\n** by the device side of adb.\n*/\n\n#pragma once\n\n#include <fcntl.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <sys/cdefs.h>\n#include <unistd.h>\n\n#include <linux/capability.h>\n\n/* Rules for directories and files has moved to system/code/libcutils/fs_config.c */\n\n__BEGIN_DECLS\n\n/* This API is deprecated. New users should call get_fs_config. */\nvoid fs_config(const char* path, int dir, const char* target_out_path, unsigned* uid, unsigned* gid,\n               unsigned* mode, uint64_t* capabilities);\n\nstruct fs_config {\n  uid_t uid;\n  gid_t gid;\n  mode_t mode;\n  uint64_t capabilities;\n};\n\n/*\n * If a file system configuration was found for the specified path, store it to *conf.\n * Returns whether a file system configuration was found.\n *\n * dir: Whether path refers to a directory.\n * target_out_path: Path to the base directory to read the file system configuration from, or a null\n * pointer to use the root directory as the base. Host code should pass $ANDROID_PRODUCT_OUT or\n * equivalent, and device code should pass a null pointer.\n */\nbool get_fs_config(const char* path, bool dir, const char* target_out_path,\n                   struct fs_config* conf);\n\n__END_DECLS\n"
  },
  {
    "path": "libcutils/iosched_policy.cpp",
    "content": "/*\n** Copyright 2007, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\n#include <cutils/iosched_policy.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#if defined(__ANDROID__)\n#include <linux/ioprio.h>\n#include <sys/syscall.h>\n#define __android_unused\n#else\n#define __android_unused __attribute__((__unused__))\n#endif\n\nint android_set_ioprio(int pid __android_unused, IoSchedClass clazz __android_unused, int ioprio __android_unused) {\n#if defined(__ANDROID__)\n    if (syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, pid, ioprio | (clazz << IOPRIO_CLASS_SHIFT))) {\n        return -1;\n    }\n#endif\n    return 0;\n}\n\nint android_get_ioprio(int pid __android_unused, IoSchedClass *clazz, int *ioprio) {\n#if defined(__ANDROID__)\n    int rc;\n\n    if ((rc = syscall(SYS_ioprio_get, IOPRIO_WHO_PROCESS, pid)) < 0) {\n        return -1;\n    }\n\n    *clazz = static_cast<IoSchedClass>(rc >> IOPRIO_CLASS_SHIFT);\n    *ioprio = (rc & 0xff);\n#else\n    *clazz = IoSchedClass_NONE;\n    *ioprio = 0;\n#endif\n    return 0;\n}\n"
  },
  {
    "path": "libcutils/klog.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/klog.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <cutils/android_get_control_file.h>\n\nstatic int klog_level = KLOG_INFO_LEVEL;\n\nvoid klog_set_level(int level) {\n    klog_level = level;\n}\n\nstatic int __open_klog(void) {\n    static const char kmsg_device[] = \"/dev/kmsg\";\n\n    int ret = android_get_control_file(kmsg_device);\n    if (ret >= 0) return ret;\n    return TEMP_FAILURE_RETRY(open(kmsg_device, O_WRONLY | O_CLOEXEC));\n}\n\n#define LOG_BUF_MAX 512\n\nvoid klog_writev(int level, const struct iovec* iov, int iov_count) {\n    if (level > klog_level) return;\n\n    static int klog_fd = __open_klog();\n    if (klog_fd == -1) return;\n    TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));\n}\n\nvoid klog_write(int level, const char* fmt, ...) {\n    if (level > klog_level) return;\n\n    char buf[LOG_BUF_MAX];\n    va_list ap;\n    va_start(ap, fmt);\n    vsnprintf(buf, sizeof(buf), fmt, ap);\n    va_end(ap);\n\n    buf[LOG_BUF_MAX - 1] = 0;\n\n    struct iovec iov[1];\n    iov[0].iov_base = buf;\n    iov[0].iov_len = strlen(buf);\n    klog_writev(level, iov, 1);\n}\n"
  },
  {
    "path": "libcutils/load_file.cpp",
    "content": "/* libs/cutils/load_file.c\n**\n** Copyright 2006, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\n#include <cutils/misc.h>\n\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nvoid *load_file(const char *fn, unsigned *_sz)\n{\n    char *data;\n    int sz;\n    int fd;\n\n    data = 0;\n    fd = open(fn, O_RDONLY);\n    if(fd < 0) return 0;\n\n    sz = lseek(fd, 0, SEEK_END);\n    if(sz < 0) goto oops;\n\n    if(lseek(fd, 0, SEEK_SET) != 0) goto oops;\n\n    data = (char*) malloc(sz + 1);\n    if(data == 0) goto oops;\n\n    if(read(fd, data, sz) != sz) goto oops;\n    close(fd);\n    data[sz] = 0;\n\n    if(_sz) *_sz = sz;\n    return data;\n\noops:\n    close(fd);\n    if(data != 0) free(data);\n    return 0;\n}\n"
  },
  {
    "path": "libcutils/multiuser.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/multiuser.h>\n#include <private/android_filesystem_config.h>\n\nuserid_t multiuser_get_user_id(uid_t uid) {\n    return uid / AID_USER_OFFSET;\n}\n\nappid_t multiuser_get_app_id(uid_t uid) {\n    return uid % AID_USER_OFFSET;\n}\n\nuid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {\n    return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);\n}\n\nuid_t multiuser_get_sdk_sandbox_uid(userid_t user_id, appid_t app_id) {\n    int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;\n    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {\n        return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET) + sdk_sandbox_offset;\n    } else {\n        return -1;\n    }\n}\n\nuid_t multiuser_convert_sdk_sandbox_to_app_uid(uid_t uid) {\n    appid_t app_id = multiuser_get_app_id(uid);\n    int sdk_sandbox_offset = AID_SDK_SANDBOX_PROCESS_START - AID_APP_START;\n    if (app_id >= AID_SDK_SANDBOX_PROCESS_START && app_id <= AID_SDK_SANDBOX_PROCESS_END) {\n        return uid - sdk_sandbox_offset;\n    } else {\n        return -1;\n    }\n}\n\ngid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {\n    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {\n        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);\n    } else {\n        return -1;\n    }\n}\n\ngid_t multiuser_get_ext_gid(userid_t user_id, appid_t app_id) {\n    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {\n        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_GID_START);\n    } else {\n        return -1;\n    }\n}\n\ngid_t multiuser_get_ext_cache_gid(userid_t user_id, appid_t app_id) {\n    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {\n        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_EXT_CACHE_GID_START);\n    } else {\n        return -1;\n    }\n}\n\ngid_t multiuser_get_shared_gid(userid_t, appid_t app_id) {\n    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {\n        return (app_id - AID_APP_START) + AID_SHARED_GID_START;\n    } else if (app_id >= AID_ROOT && app_id <= AID_APP_START) {\n        return app_id;\n    } else {\n        return -1;\n    }\n}\n\ngid_t multiuser_get_shared_app_gid(uid_t uid) {\n    return multiuser_get_shared_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));\n}\n"
  },
  {
    "path": "libcutils/multiuser_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/multiuser.h>\n#include <gtest/gtest.h>\n\nstatic constexpr auto ERR_GID = static_cast<gid_t>(-1);\nstatic constexpr auto ERR_UID = static_cast<uid_t>(-1);\n\nTEST(MultiuserTest, TestMerge) {\n    EXPECT_EQ(0U, multiuser_get_uid(0, 0));\n    EXPECT_EQ(1000U, multiuser_get_uid(0, 1000));\n    EXPECT_EQ(10000U, multiuser_get_uid(0, 10000));\n    EXPECT_EQ(50000U, multiuser_get_uid(0, 50000));\n    EXPECT_EQ(1000000U, multiuser_get_uid(10, 0));\n    EXPECT_EQ(1001000U, multiuser_get_uid(10, 1000));\n    EXPECT_EQ(1010000U, multiuser_get_uid(10, 10000));\n    EXPECT_EQ(1050000U, multiuser_get_uid(10, 50000));\n}\n\nTEST(MultiuserTest, TestSdkSandboxUid) {\n    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 0));\n    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 1000));\n    EXPECT_EQ(20000U, multiuser_get_sdk_sandbox_uid(0, 10000));\n    EXPECT_EQ(25000U, multiuser_get_sdk_sandbox_uid(0, 15000));\n    EXPECT_EQ(29999U, multiuser_get_sdk_sandbox_uid(0, 19999));\n    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(0, 50000));\n\n    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 0));\n    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 1000));\n    EXPECT_EQ(1020000U, multiuser_get_sdk_sandbox_uid(10, 10000));\n    EXPECT_EQ(1025000U, multiuser_get_sdk_sandbox_uid(10, 15000));\n    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 20000));\n    EXPECT_EQ(ERR_UID, multiuser_get_sdk_sandbox_uid(10, 50000));\n}\n\nTEST(MultiuserTest, TestSdkSandboxUidConvertation) {\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(0));\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000));\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(10000));\n    EXPECT_EQ(10000U, multiuser_convert_sdk_sandbox_to_app_uid(20000));\n    EXPECT_EQ(15000U, multiuser_convert_sdk_sandbox_to_app_uid(25000));\n    EXPECT_EQ(19999U, multiuser_convert_sdk_sandbox_to_app_uid(29999));\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(50000));\n\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1000000));\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1001000));\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1010000));\n    EXPECT_EQ(1010000U, multiuser_convert_sdk_sandbox_to_app_uid(1020000));\n    EXPECT_EQ(1015000U, multiuser_convert_sdk_sandbox_to_app_uid(1025000));\n    EXPECT_EQ(1019999U, multiuser_convert_sdk_sandbox_to_app_uid(1029999));\n    EXPECT_EQ(ERR_UID, multiuser_convert_sdk_sandbox_to_app_uid(1050000));\n}\n\nTEST(MultiuserTest, TestSplitUser) {\n    EXPECT_EQ(0U, multiuser_get_user_id(0));\n    EXPECT_EQ(0U, multiuser_get_user_id(1000));\n    EXPECT_EQ(0U, multiuser_get_user_id(10000));\n    EXPECT_EQ(0U, multiuser_get_user_id(50000));\n    EXPECT_EQ(10U, multiuser_get_user_id(1000000));\n    EXPECT_EQ(10U, multiuser_get_user_id(1001000));\n    EXPECT_EQ(10U, multiuser_get_user_id(1010000));\n    EXPECT_EQ(10U, multiuser_get_user_id(1050000));\n}\n\nTEST(MultiuserTest, TestSplitApp) {\n    EXPECT_EQ(0U, multiuser_get_app_id(0));\n    EXPECT_EQ(1000U, multiuser_get_app_id(1000));\n    EXPECT_EQ(10000U, multiuser_get_app_id(10000));\n    EXPECT_EQ(50000U, multiuser_get_app_id(50000));\n    EXPECT_EQ(0U, multiuser_get_app_id(1000000));\n    EXPECT_EQ(1000U, multiuser_get_app_id(1001000));\n    EXPECT_EQ(10000U, multiuser_get_app_id(1010000));\n    EXPECT_EQ(50000U, multiuser_get_app_id(1050000));\n}\n\nTEST(MultiuserTest, TestCache) {\n    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 0));\n    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 1000));\n    EXPECT_EQ(20000U, multiuser_get_cache_gid(0, 10000));\n    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(0, 50000));\n    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 0));\n    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 1000));\n    EXPECT_EQ(1020000U, multiuser_get_cache_gid(10, 10000));\n    EXPECT_EQ(ERR_GID, multiuser_get_cache_gid(10, 50000));\n}\n\nTEST(MultiuserTest, TestExt) {\n    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 0));\n    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 1000));\n    EXPECT_EQ(30000U, multiuser_get_ext_gid(0, 10000));\n    EXPECT_EQ(ERR_GID, multiuser_get_ext_gid(0, 50000));\n    EXPECT_EQ(1030000U, multiuser_get_ext_gid(10, 10000));\n}\n\nTEST(MultiuserTest, TestExtCache) {\n    EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 0));\n    EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 1000));\n    EXPECT_EQ(40000U, multiuser_get_ext_cache_gid(0, 10000));\n    EXPECT_EQ(ERR_GID, multiuser_get_ext_cache_gid(0, 50000));\n    EXPECT_EQ(1040000U, multiuser_get_ext_cache_gid(10, 10000));\n}\n\nTEST(MultiuserTest, TestShared) {\n    EXPECT_EQ(0U, multiuser_get_shared_gid(0, 0));\n    EXPECT_EQ(1000U, multiuser_get_shared_gid(0, 1000));\n    EXPECT_EQ(50000U, multiuser_get_shared_gid(0, 10000));\n    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(0, 50000));\n    EXPECT_EQ(0U, multiuser_get_shared_gid(10, 0));\n    EXPECT_EQ(1000U, multiuser_get_shared_gid(10, 1000));\n    EXPECT_EQ(50000U, multiuser_get_shared_gid(10, 10000));\n    EXPECT_EQ(ERR_GID, multiuser_get_shared_gid(10, 50000));\n}\n"
  },
  {
    "path": "libcutils/native_handle.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/native_handle.h>\n\n#include <errno.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n// Needs to come after stdlib includes to capture the __BIONIC__ definition\n#ifdef __BIONIC__\n#include <android/fdsan.h>\n#endif\n\nnamespace {\n\n#if !defined(__BIONIC__)\n// fdsan stubs when not linked against bionic\n#define ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE 0\n\nuint64_t android_fdsan_create_owner_tag(int /*type*/, uint64_t /*tag*/) {\n    return 0;\n}\nuint64_t android_fdsan_get_owner_tag(int /*fd*/) {\n    return 0;\n}\nint android_fdsan_close_with_tag(int fd, uint64_t /*tag*/) {\n    return close(fd);\n}\nvoid android_fdsan_exchange_owner_tag(int /*fd*/, uint64_t /*expected_tag*/, uint64_t /*tag*/) {}\n#endif  // !__BIONIC__\n\nuint64_t get_fdsan_tag(const native_handle_t* handle) {\n    return android_fdsan_create_owner_tag(ANDROID_FDSAN_OWNER_TYPE_NATIVE_HANDLE,\n                                          reinterpret_cast<uint64_t>(handle));\n}\n\nint close_internal(const native_handle_t* h, bool allowUntagged) {\n    if (!h) return 0;\n\n    if (h->version != sizeof(native_handle_t)) return -EINVAL;\n\n    const int numFds = h->numFds;\n    uint64_t tag;\n    if (allowUntagged && numFds > 0 && android_fdsan_get_owner_tag(h->data[0]) == 0) {\n        tag = 0;\n    } else {\n        tag = get_fdsan_tag(h);\n    }\n    int saved_errno = errno;\n    for (int i = 0; i < numFds; ++i) {\n        android_fdsan_close_with_tag(h->data[i], tag);\n    }\n    errno = saved_errno;\n    return 0;\n}\n\nvoid swap_fdsan_tags(const native_handle_t* handle, uint64_t expected_tag, uint64_t new_tag) {\n    if (!handle || handle->version != sizeof(native_handle_t)) return;\n\n    for (int i = 0; i < handle->numFds; i++) {\n        // allow for idempotence to make the APIs easier to use\n        if (android_fdsan_get_owner_tag(handle->data[i]) != new_tag) {\n            android_fdsan_exchange_owner_tag(handle->data[i], expected_tag, new_tag);\n        }\n    }\n}\n\n}  // anonymous namespace\n\nnative_handle_t* native_handle_init(char* storage, int numFds, int numInts) {\n    if ((uintptr_t)storage % alignof(native_handle_t)) {\n        errno = EINVAL;\n        return NULL;\n    }\n\n    native_handle_t* handle = (native_handle_t*)storage;\n    handle->version = sizeof(native_handle_t);\n    handle->numFds = numFds;\n    handle->numInts = numInts;\n    return handle;\n}\n\nnative_handle_t* native_handle_create(int numFds, int numInts) {\n    if (numFds < 0 || numInts < 0 || numFds > NATIVE_HANDLE_MAX_FDS ||\n        numInts > NATIVE_HANDLE_MAX_INTS) {\n        errno = EINVAL;\n        return NULL;\n    }\n\n    size_t mallocSize = sizeof(native_handle_t) + (sizeof(int) * (numFds + numInts));\n    native_handle_t* h = static_cast<native_handle_t*>(malloc(mallocSize));\n    if (h) {\n        h->version = sizeof(native_handle_t);\n        h->numFds = numFds;\n        h->numInts = numInts;\n    }\n    return h;\n}\n\nvoid native_handle_set_fdsan_tag(const native_handle_t* handle) {\n    swap_fdsan_tags(handle, 0, get_fdsan_tag(handle));\n}\n\nvoid native_handle_unset_fdsan_tag(const native_handle_t* handle) {\n    swap_fdsan_tags(handle, get_fdsan_tag(handle), 0);\n}\n\nnative_handle_t* native_handle_clone(const native_handle_t* handle) {\n    native_handle_t* clone = native_handle_create(handle->numFds, handle->numInts);\n    if (clone == NULL) return NULL;\n\n    for (int i = 0; i < handle->numFds; i++) {\n        clone->data[i] = dup(handle->data[i]);\n        if (clone->data[i] == -1) {\n            clone->numFds = i;\n            native_handle_close(clone);\n            native_handle_delete(clone);\n            return NULL;\n        }\n    }\n\n    memcpy(&clone->data[handle->numFds], &handle->data[handle->numFds],\n           sizeof(int) * handle->numInts);\n\n    return clone;\n}\n\nint native_handle_delete(native_handle_t* h) {\n    if (h) {\n        if (h->version != sizeof(native_handle_t)) return -EINVAL;\n        free(h);\n    }\n    return 0;\n}\n\nint native_handle_close(const native_handle_t* h) {\n    return close_internal(h, /*allowUntagged=*/true);\n}\n\nint native_handle_close_with_tag(const native_handle_t* h) {\n    return close_internal(h, /*allowUntagged=*/false);\n}\n"
  },
  {
    "path": "libcutils/native_handle_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/native_handle.h>\n\n#include <gtest/gtest.h>\n\nTEST(native_handle, native_handle_delete) {\n    ASSERT_EQ(0, native_handle_delete(nullptr));\n}\n\nTEST(native_handle, native_handle_close) {\n    ASSERT_EQ(0, native_handle_close(nullptr));\n}\n"
  },
  {
    "path": "libcutils/partition_utils.cpp",
    "content": "/*\n * Copyright 2011, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/partition_utils.h>\n\n#include <fcntl.h>\n#include <sys/ioctl.h>\n#include <sys/mount.h> /* for BLKGETSIZE */\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <cutils/properties.h>\n\nstatic int only_one_char(uint8_t *buf, int len, uint8_t c)\n{\n    int i, ret;\n\n    ret = 1;\n    for (i=0; i<len; i++) {\n        if (buf[i] != c) {\n            ret = 0;\n            break;\n        }\n    }\n    return ret;\n}\n\nint partition_wiped(const char* source) {\n    uint8_t buf[4096];\n    int fd, ret;\n\n    if ((fd = open(source, O_RDONLY)) < 0) {\n        return 0;\n    }\n\n    ret = read(fd, buf, sizeof(buf));\n    close(fd);\n\n    if (ret != sizeof(buf)) {\n        return 0;\n    }\n\n    /* Check for all zeros */\n    if (only_one_char(buf, sizeof(buf), 0)) {\n       return 1;\n    }\n\n    /* Check for all ones */\n    if (only_one_char(buf, sizeof(buf), 0xff)) {\n       return 1;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "libcutils/properties.cpp",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/properties.h>\n\n#include <errno.h>\n#include <inttypes.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <android-base/properties.h>\n\nint8_t property_get_bool(const char* key, int8_t default_value) {\n    if (!key) return default_value;\n\n    int8_t result = default_value;\n    char buf[PROPERTY_VALUE_MAX] = {};\n\n    int len = property_get(key, buf, \"\");\n    if (len == 1) {\n        char ch = buf[0];\n        if (ch == '0' || ch == 'n') {\n            result = false;\n        } else if (ch == '1' || ch == 'y') {\n            result = true;\n        }\n    } else if (len > 1) {\n        if (!strcmp(buf, \"no\") || !strcmp(buf, \"false\") || !strcmp(buf, \"off\")) {\n            result = false;\n        } else if (!strcmp(buf, \"yes\") || !strcmp(buf, \"true\") || !strcmp(buf, \"on\")) {\n            result = true;\n        }\n    }\n\n    return result;\n}\n\ntemplate <typename T>\nstatic T property_get_int(const char* key, T default_value) {\n    if (!key) return default_value;\n\n    char value[PROPERTY_VALUE_MAX] = {};\n    if (property_get(key, value, \"\") < 1) return default_value;\n\n    // libcutils unwisely allows octal, which libbase doesn't.\n    T result = default_value;\n    int saved_errno = errno;\n    errno = 0;\n    char* end = nullptr;\n    intmax_t v = strtoimax(value, &end, 0);\n    if (errno != ERANGE && end != value && v >= std::numeric_limits<T>::min() &&\n        v <= std::numeric_limits<T>::max()) {\n        result = v;\n    }\n    errno = saved_errno;\n    return result;\n}\n\nint64_t property_get_int64(const char* key, int64_t default_value) {\n    return property_get_int<int64_t>(key, default_value);\n}\n\nint32_t property_get_int32(const char* key, int32_t default_value) {\n    return property_get_int<int32_t>(key, default_value);\n}\n\nint property_set(const char* key, const char* value) {\n    return __system_property_set(key, value);\n}\n\nint property_get(const char* key, char* value, const char* default_value) {\n    int len = __system_property_get(key, value);\n    if (len < 1 && default_value) {\n        snprintf(value, PROPERTY_VALUE_MAX, \"%s\", default_value);\n        return strlen(value);\n    }\n    return len;\n}\n\n#if __has_include(<sys/system_properties.h>)\n\n#include <sys/system_properties.h>\n\nstruct callback_data {\n    void (*callback)(const char* name, const char* value, void* cookie);\n    void* cookie;\n};\n\nstatic void trampoline(void* raw_data, const char* name, const char* value, unsigned /*serial*/) {\n    callback_data* data = reinterpret_cast<callback_data*>(raw_data);\n    data->callback(name, value, data->cookie);\n}\n\nstatic void property_list_callback(const prop_info* pi, void* data) {\n    __system_property_read_callback(pi, trampoline, data);\n}\n\nint property_list(void (*fn)(const char* name, const char* value, void* cookie), void* cookie) {\n    callback_data data = {fn, cookie};\n    return __system_property_foreach(property_list_callback, &data);\n}\n\n#endif\n"
  },
  {
    "path": "libcutils/properties_test.cpp",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Properties_test\"\n\n#include <limits.h>\n\n#include <iostream>\n#include <sstream>\n#include <string>\n\n#include <android/log.h>\n#include <android-base/macros.h>\n#include <cutils/properties.h>\n#include <gtest/gtest.h>\n\nnamespace android {\n\n#define STRINGIFY_INNER(x) #x\n#define STRINGIFY(x) STRINGIFY_INNER(x)\n#define ASSERT_OK(x) ASSERT_EQ(0, (x))\n#define EXPECT_OK(x) EXPECT_EQ(0, (x))\n\n#define PROPERTY_TEST_KEY \"libcutils.test.key\"\n#define PROPERTY_TEST_VALUE_DEFAULT \"<<<default_value>>>\"\n\ntemplate <typename T>\nstatic std::string HexString(T value) {\n    std::stringstream ss;\n    ss << \"0x\" << std::hex << std::uppercase << value;\n    return ss.str();\n}\n\ntemplate <typename T>\nstatic ::testing::AssertionResult AssertEqualHex(const char *mExpr,\n        const char *nExpr,\n        T m,\n        T n) {\n    if (m == n) {\n        return ::testing::AssertionSuccess();\n    }\n\n    return ::testing::AssertionFailure()\n        << mExpr << \" and \" << nExpr << \" (expected: \" << HexString(m) <<\n        \", actual: \" << HexString(n) << \") are not equal\";\n}\n\nclass PropertiesTest : public testing::Test {\npublic:\n    PropertiesTest() : mValue() {}\nprotected:\n    virtual void SetUp() {\n        EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));\n    }\n\n    virtual void TearDown() {\n        EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/NULL));\n    }\n\n    char mValue[PROPERTY_VALUE_MAX];\n\n    template <typename T>\n    static std::string ToString(T value) {\n        std::stringstream ss;\n        ss << value;\n\n        return ss.str();\n    }\n\n    // Return length of property read; value is written into mValue\n    int SetAndGetProperty(const char* value, const char* defaultValue = PROPERTY_TEST_VALUE_DEFAULT) {\n        EXPECT_OK(property_set(PROPERTY_TEST_KEY, value)) << \"value: '\" << value << \"'\";\n        return property_get(PROPERTY_TEST_KEY, mValue, defaultValue);\n    }\n\n    void ResetValue(unsigned char c = 0xFF) {\n        for (size_t i = 0; i < arraysize(mValue); ++i) {\n            mValue[i] = (char) c;\n        }\n    }\n};\n\nTEST_F(PropertiesTest, property_set_null_key) {\n    // Null key -> unsuccessful set\n    EXPECT_GT(0, property_set(/*key*/ NULL, PROPERTY_TEST_VALUE_DEFAULT));\n}\n\nTEST_F(PropertiesTest, property_set_null_value) {\n    // Null value -> OK, and it clears the value\n    EXPECT_OK(property_set(PROPERTY_TEST_KEY, /*value*/ NULL));\n    ResetValue();\n\n    // Since the value is null, default value will be returned\n    size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);\n    EXPECT_EQ(strlen(PROPERTY_TEST_VALUE_DEFAULT), len);\n    EXPECT_STREQ(PROPERTY_TEST_VALUE_DEFAULT, mValue);\n}\n\nTEST_F(PropertiesTest, property_set) {\n    // Trivial case => get returns what was set\n    size_t len = SetAndGetProperty(\"hello_world\");\n    EXPECT_EQ(strlen(\"hello_world\"), len) << \"hello_world key\";\n    EXPECT_STREQ(\"hello_world\", mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_set_empty) {\n    // Set to empty string => get returns default always\n    const char* EMPTY_STRING_DEFAULT = \"EMPTY_STRING\";\n    size_t len = SetAndGetProperty(\"\", EMPTY_STRING_DEFAULT);\n    EXPECT_EQ(strlen(EMPTY_STRING_DEFAULT), len) << \"empty key\";\n    EXPECT_STREQ(EMPTY_STRING_DEFAULT, mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_set_max_length) {\n    // Set to max length => get returns what was set\n    std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');\n\n    int len = SetAndGetProperty(maxLengthString.c_str());\n    EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len) << \"max length key\";\n    EXPECT_STREQ(maxLengthString.c_str(), mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_set_too_long) {\n    // Set to max length + 1 => set fails\n    const char* VALID_TEST_VALUE = \"VALID_VALUE\";\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, VALID_TEST_VALUE));\n\n    std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');\n\n    // Expect that the value set fails since it's too long\n    EXPECT_GT(0, property_set(PROPERTY_TEST_KEY, oneLongerString.c_str()));\n    size_t len = property_get(PROPERTY_TEST_KEY, mValue, PROPERTY_TEST_VALUE_DEFAULT);\n\n    EXPECT_EQ(strlen(VALID_TEST_VALUE), len) << \"set should've failed\";\n    EXPECT_STREQ(VALID_TEST_VALUE, mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_get_too_long) {\n    // Try to use a default value that's too long => get truncates the value\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"\"));\n\n    std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');\n    std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');\n\n    // Expect that the value is truncated since it's too long (by 1)\n    int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());\n    EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);\n    EXPECT_STREQ(maxLengthString.c_str(), mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_get_default_too_long) {\n    // Try to use a default value that's the max length => get succeeds\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"\"));\n\n    std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');\n\n    // Expect that the value matches maxLengthString\n    int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());\n    EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);\n    EXPECT_STREQ(maxLengthString.c_str(), mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_get_default_okay) {\n    // Try to use a default value of length one => get succeeds\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"\"));\n\n    std::string oneCharString = std::string(1, 'c');\n\n    // Expect that the value matches oneCharString\n    int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());\n    EXPECT_EQ(1, len);\n    EXPECT_STREQ(oneCharString.c_str(), mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_get_default_empty) {\n    // Try to use a default value of length zero => get succeeds\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"\"));\n\n    std::string zeroCharString = std::string(0, 'd');\n\n    // Expect that the value matches oneCharString\n    int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());\n    EXPECT_EQ(0, len);\n    EXPECT_STREQ(zeroCharString.c_str(), mValue);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_get_default_NULL) {\n    // Try to use a NULL default value => get returns 0\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"\"));\n\n    // Expect a return value of 0\n    int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);\n    EXPECT_EQ(0, len);\n    ResetValue();\n}\n\nTEST_F(PropertiesTest, property_get_bool_0) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"0\"));\n    ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));\n}\n\nTEST_F(PropertiesTest, property_get_bool_1) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"1\"));\n    ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));\n}\n\nTEST_F(PropertiesTest, property_get_bool_false) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"false\"));\n    ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));\n}\n\nTEST_F(PropertiesTest, property_get_bool_n) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"n\"));\n    ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));\n}\n\nTEST_F(PropertiesTest, property_get_bool_no) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"no\"));\n    ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));\n}\n\nTEST_F(PropertiesTest, property_get_bool_off) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"off\"));\n    ASSERT_FALSE(property_get_bool(PROPERTY_TEST_KEY, true));\n}\n\nTEST_F(PropertiesTest, property_get_bool_on) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"on\"));\n    ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));\n}\n\nTEST_F(PropertiesTest, property_get_bool_true) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"true\"));\n    ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));\n}\n\nTEST_F(PropertiesTest, property_get_bool_y) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"y\"));\n    ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));\n}\n\nTEST_F(PropertiesTest, property_get_bool_yes) {\n    ASSERT_OK(property_set(PROPERTY_TEST_KEY, \"yes\"));\n    ASSERT_TRUE(property_get_bool(PROPERTY_TEST_KEY, false));\n}\n\nTEST_F(PropertiesTest, property_get_bool_neither) {\n    const char *valuesNeither[] = { \"x0\", \"x1\", \"2\", \"-2\", \"True\", \"False\", \"garbage\", \"\", \" \",\n            \"+1\", \"  1  \", \"  true\", \"  true  \", \"  y  \", \"  yes\", \"yes  \",\n            \"+0\", \"-0\", \"00\", \"  00  \", \"  false\", \"false  \",\n    };\n    for (size_t i = 0; i < arraysize(valuesNeither); ++i) {\n        ASSERT_OK(property_set(PROPERTY_TEST_KEY, valuesNeither[i]));\n\n        // The default value should always be used\n        bool val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/true);\n        EXPECT_TRUE(val) << \"Property should've been NEITHER (true) for string value: '\" << valuesNeither[i] << \"'\";\n\n        val = property_get_bool(PROPERTY_TEST_KEY, /*default_value*/false);\n        EXPECT_FALSE(val) << \"Property should've been NEITHER (false) for string value: '\" << valuesNeither[i] << \"'\";\n    }\n}\n\nTEST_F(PropertiesTest, property_get_int64) {\n    const int64_t DEFAULT_VALUE = INT64_C(0xDEADBEEFBEEFDEAD);\n\n    const std::string longMaxString = ToString(INT64_MAX);\n    const std::string longStringOverflow = longMaxString + \"0\";\n\n    const std::string longMinString = ToString(INT64_MIN);\n    const std::string longStringUnderflow = longMinString + \"0\";\n\n    const char* setValues[] = {\n        // base 10\n        \"1\", \"2\", \"12345\", \"-1\", \"-2\", \"-12345\",\n        // base 16\n        \"0xFF\", \"0x0FF\", \"0xC0FFEE\",\n        // base 8\n        \"0\", \"01234\", \"07\",\n        // corner cases\n        \"       2\", \"2      \", \"+0\", \"-0\", \"  +0   \", longMaxString.c_str(), longMinString.c_str(),\n        // failing cases\n        NULL, \"\", \" \", \"    \", \"hello\", \"     true     \", \"y\",\n        longStringOverflow.c_str(), longStringUnderflow.c_str(),\n    };\n\n    int64_t getValues[] = {\n        // base 10\n        1, 2, 12345, -1, -2, -12345,\n        // base 16\n        0xFF, 0x0FF, 0xC0FFEE,\n        // base 8\n        0, 01234, 07,\n        // corner cases\n        2, 2, 0, 0, 0, INT64_MAX, INT64_MIN,\n        // failing cases\n        DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE,\n        DEFAULT_VALUE, DEFAULT_VALUE,\n    };\n\n    ASSERT_EQ(arraysize(setValues), arraysize(getValues));\n\n    for (size_t i = 0; i < arraysize(setValues); ++i) {\n        ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));\n\n        int64_t val = property_get_int64(PROPERTY_TEST_KEY, DEFAULT_VALUE);\n        EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << \"Property was set to '\" << setValues[i] << \"'\";\n    }\n}\n\nTEST_F(PropertiesTest, property_get_int32) {\n    const int32_t DEFAULT_VALUE = INT32_C(0xDEADBEEF);\n\n    const std::string intMaxString = ToString(INT32_MAX);\n    const std::string intStringOverflow = intMaxString + \"0\";\n\n    const std::string intMinString = ToString(INT32_MIN);\n    const std::string intStringUnderflow = intMinString + \"0\";\n\n    const char* setValues[] = {\n        // base 10\n        \"1\", \"2\", \"12345\", \"-1\", \"-2\", \"-12345\",\n        // base 16\n        \"0xFF\", \"0x0FF\", \"0xC0FFEE\", \"0Xf00\",\n        // base 8\n        \"0\", \"01234\", \"07\",\n        // corner cases\n        \"       2\", \"2      \", \"+0\", \"-0\", \"  +0   \", intMaxString.c_str(), intMinString.c_str(),\n        // failing cases\n        NULL, \"\", \" \", \"    \", \"hello\", \"     true     \", \"y\",\n        intStringOverflow.c_str(), intStringUnderflow.c_str(),\n    };\n\n    int32_t getValues[] = {\n        // base 10\n        1, 2, 12345, -1, -2, -12345,\n        // base 16\n        0xFF, 0x0FF, 0xC0FFEE, 0Xf00,\n        // base 8\n        0, 01234, 07,\n        // corner cases\n        2, 2, 0, 0, 0, INT32_MAX, INT32_MIN,\n        // failing cases\n        DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE, DEFAULT_VALUE,\n        DEFAULT_VALUE, DEFAULT_VALUE,\n    };\n\n    ASSERT_EQ(arraysize(setValues), arraysize(getValues));\n\n    for (size_t i = 0; i < arraysize(setValues); ++i) {\n        ASSERT_OK(property_set(PROPERTY_TEST_KEY, setValues[i]));\n\n        int32_t val = property_get_int32(PROPERTY_TEST_KEY, DEFAULT_VALUE);\n        EXPECT_PRED_FORMAT2(AssertEqualHex, getValues[i], val) << \"Property was set to '\" << setValues[i] << \"'\";\n    }\n}\n\n} // namespace android\n"
  },
  {
    "path": "libcutils/qtaguid.cpp",
    "content": "/*\n * Copyright 2017, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/qtaguid.h>\n\n// #define LOG_NDEBUG 0\n\n#define LOG_TAG \"qtaguid\"\n\n#include <dlfcn.h>\n#include <errno.h>\n#include <inttypes.h>\n\n#include <log/log.h>\n\nstruct netdHandler {\n    int (*netdTagSocket)(int, uint32_t, uid_t);\n    int (*netdUntagSocket)(int);\n};\n\nstatic int stubTagSocket(int, uint32_t, uid_t) {\n    return -EREMOTEIO;\n}\n\nstatic int stubUntagSocket(int) {\n    return -EREMOTEIO;\n}\n\nstatic netdHandler initHandler(void) {\n    const netdHandler stubHandler = { stubTagSocket, stubUntagSocket };\n\n    void* netdClientHandle = dlopen(\"libnetd_client.so\", RTLD_NOW);\n    if (!netdClientHandle) {\n        ALOGE(\"Failed to open libnetd_client.so: %s\", dlerror());\n        return stubHandler;\n    }\n\n    netdHandler handler;\n    handler.netdTagSocket = (int (*)(int, uint32_t, uid_t))dlsym(netdClientHandle, \"tagSocket\");\n    if (!handler.netdTagSocket) {\n        ALOGE(\"load netdTagSocket handler failed: %s\", dlerror());\n        return stubHandler;\n    }\n\n    handler.netdUntagSocket = (int (*)(int))dlsym(netdClientHandle, \"untagSocket\");\n    if (!handler.netdUntagSocket) {\n        ALOGE(\"load netdUntagSocket handler failed: %s\", dlerror());\n        return stubHandler;\n    }\n\n    return handler;\n}\n\n// The language guarantees that this object will be initialized in a thread-safe way.\nstatic const netdHandler& getHandler() {\n    static const netdHandler instance = initHandler();\n    return instance;\n}\n\nint qtaguid_tagSocket(int sockfd, int tag, uid_t uid) {\n    ALOGV(\"Tagging socket %d with tag %u for uid %d\", sockfd, tag, uid);\n    return getHandler().netdTagSocket(sockfd, tag, uid);\n}\n\nint qtaguid_untagSocket(int sockfd) {\n    ALOGV(\"Untagging socket %d\", sockfd);\n    return getHandler().netdUntagSocket(sockfd);\n}\n"
  },
  {
    "path": "libcutils/record_stream.cpp",
    "content": "/* libs/cutils/record_stream.c\n**\n** Copyright 2006, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\n#include <cutils/record_stream.h>\n\n#include <stdlib.h>\n#include <unistd.h>\n#include <assert.h>\n#include <errno.h>\n#include <string.h>\n#include <stdint.h>\n#if defined(_WIN32)\n#include <winsock2.h>   /* for ntohl */\n#else\n#include <netinet/in.h>\n#endif\n\n#define HEADER_SIZE 4\n\nstruct RecordStream {\n    int fd;\n    size_t maxRecordLen;\n\n    unsigned char *buffer;\n\n    unsigned char *unconsumed;\n    unsigned char *read_end;\n    unsigned char *buffer_end;\n};\n\n\nextern RecordStream *record_stream_new(int fd, size_t maxRecordLen)\n{\n    RecordStream *ret;\n\n    assert (maxRecordLen <= 0xffff);\n\n    ret = (RecordStream *)calloc(1, sizeof(RecordStream));\n\n    ret->fd = fd;\n    ret->maxRecordLen = maxRecordLen;\n    ret->buffer = (unsigned char *)malloc (maxRecordLen + HEADER_SIZE);\n    \n    ret->unconsumed = ret->buffer;\n    ret->read_end = ret->buffer;\n    ret->buffer_end = ret->buffer + maxRecordLen + HEADER_SIZE;\n\n    return ret;\n}\n\n\nextern void record_stream_free(RecordStream *rs)\n{\n    free(rs->buffer);\n    free(rs);\n}\n\n\n/* returns NULL; if there isn't a full record in the buffer */\nstatic unsigned char * getEndOfRecord (unsigned char *p_begin,\n                                            unsigned char *p_end)\n{\n    size_t len;\n    unsigned char * p_ret;\n\n    if (p_end < p_begin + HEADER_SIZE) {\n        return NULL;\n    }\n\n    //First four bytes are length\n    len = ntohl(*((uint32_t *)p_begin));\n\n    p_ret = p_begin + HEADER_SIZE + len;\n\n    if (p_end < p_ret) {\n        return NULL;\n    }\n\n    return p_ret;\n}\n\nstatic void *getNextRecord (RecordStream *p_rs, size_t *p_outRecordLen)\n{\n    unsigned char *record_start, *record_end;\n\n    record_end = getEndOfRecord (p_rs->unconsumed, p_rs->read_end);\n\n    if (record_end != NULL) {\n        /* one full line in the buffer */\n        record_start = p_rs->unconsumed + HEADER_SIZE;\n        p_rs->unconsumed = record_end;\n\n        *p_outRecordLen = record_end - record_start;\n\n        return record_start;\n    }\n\n    return NULL;\n}\n\n/**\n * Reads the next record from stream fd\n * Records are prefixed by a 16-bit big endian length value\n * Records may not be larger than maxRecordLen\n *\n * Doesn't guard against EINTR\n *\n * p_outRecord and p_outRecordLen may not be NULL\n *\n * Return 0 on success, -1 on fail\n * Returns 0 with *p_outRecord set to NULL on end of stream\n * Returns -1 / errno = EAGAIN if it needs to read again\n */\nint record_stream_get_next (RecordStream *p_rs, void ** p_outRecord, \n                                    size_t *p_outRecordLen)\n{\n    void *ret;\n\n    ssize_t countRead;\n\n    /* is there one record already in the buffer? */\n    ret = getNextRecord (p_rs, p_outRecordLen);\n\n    if (ret != NULL) {\n        *p_outRecord = ret;\n        return 0;\n    }\n\n    // if the buffer is full and we don't have a full record\n    if (p_rs->unconsumed == p_rs->buffer \n        && p_rs->read_end == p_rs->buffer_end\n    ) {\n        // this should never happen\n        //ALOGE(\"max record length exceeded\\n\");\n        assert (0);\n        errno = EFBIG;\n        return -1;\n    }\n\n    if (p_rs->unconsumed != p_rs->buffer) {\n        // move remainder to the beginning of the buffer\n        size_t toMove;\n\n        toMove = p_rs->read_end - p_rs->unconsumed;\n        if (toMove) {\n            memmove(p_rs->buffer, p_rs->unconsumed, toMove);\n        }\n\n        p_rs->read_end = p_rs->buffer + toMove;\n        p_rs->unconsumed = p_rs->buffer;\n    }\n\n    countRead = read (p_rs->fd, p_rs->read_end, p_rs->buffer_end - p_rs->read_end);\n\n    if (countRead <= 0) {\n        /* note: end-of-stream drops through here too */\n        *p_outRecord = NULL;\n        return countRead;\n    }\n\n    p_rs->read_end += countRead;\n\n    ret = getNextRecord (p_rs, p_outRecordLen);\n\n    if (ret == NULL) {\n        /* not enough of a buffer to for a whole command */\n        errno = EAGAIN;\n        return -1;\n    }\n\n    *p_outRecord = ret;        \n    return 0;\n}\n"
  },
  {
    "path": "libcutils/rust/aid_bindings.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <private/android_filesystem_config.h>\n"
  },
  {
    "path": "libcutils/sched_policy_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <algorithm>\n#include <chrono>\n#include <thread>\n#include <vector>\n\n#include <sys/capability.h>\n\n#include <cutils/sched_policy.h>\n\n#include <gtest/gtest.h>\n\nbool hasCapSysNice() {\n    __user_cap_header_struct header;\n    memset(&header, 0, sizeof(header));\n    header.version = _LINUX_CAPABILITY_VERSION_3;\n\n    __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3];\n    if (capget(&header, &caps[0])) {\n        GTEST_LOG_(WARNING) << \"failed to get process capabilities\";\n        return false;\n    }\n\n    auto nice_idx = CAP_TO_INDEX(CAP_SYS_NICE);\n    auto nice_mask = CAP_TO_MASK(CAP_SYS_NICE);\n    return caps[nice_idx].effective & nice_mask;\n}\n\nlong long medianSleepTime() {\n    std::vector<long long> sleepTimes;\n    constexpr size_t numSamples = 100;\n\n    for (size_t i = 0; i < numSamples; i++) {\n        auto start = std::chrono::steady_clock::now();\n        std::this_thread::sleep_for(std::chrono::nanoseconds(1));\n        auto end = std::chrono::steady_clock::now();\n\n        auto diff = end - start;\n        sleepTimes.push_back(diff.count());\n    }\n\n    constexpr auto median = numSamples / 2;\n    std::nth_element(sleepTimes.begin(), sleepTimes.begin() + median,\n            sleepTimes.end());\n    return sleepTimes[median];\n}\n\nstatic void AssertPolicy(SchedPolicy expected_policy) {\n    SchedPolicy current_policy;\n    ASSERT_EQ(0, get_sched_policy(0, &current_policy));\n    EXPECT_EQ(expected_policy, current_policy);\n}\n\nTEST(SchedPolicy, set_sched_policy) {\n    ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));\n    ASSERT_EQ(0, set_cpuset_policy(0, SP_BACKGROUND));\n    AssertPolicy(SP_BACKGROUND);\n\n    ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));\n    ASSERT_EQ(0, set_cpuset_policy(0, SP_FOREGROUND));\n    AssertPolicy(SP_FOREGROUND);\n}\n\nTEST(SchedPolicy, set_sched_policy_timerslack) {\n    if (!hasCapSysNice()) {\n        GTEST_LOG_(INFO) << \"skipping test that requires CAP_SYS_NICE\";\n        return;\n    }\n\n    // A measureable effect of scheduling policy is that the kernel has 800x\n    // greater slack time in waking up a sleeping background thread.\n    //\n    // Look for 10x difference in how long FB and BG threads actually sleep\n    // when trying to sleep for 1 ns.  This difference is large enough not\n    // to happen by chance, but small enough (compared to 800x) to keep inherent\n    // fuzziness in scheduler behavior from causing false negatives.\n    const unsigned int BG_FG_SLACK_FACTOR = 10;\n\n    ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));\n    auto bgSleepTime = medianSleepTime();\n\n    ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));\n    auto fgSleepTime = medianSleepTime();\n\n    ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);\n}\n\nTEST(SchedPolicy, get_sched_policy_name) {\n    EXPECT_STREQ(\"bg\", get_sched_policy_name(SP_BACKGROUND));\n    EXPECT_EQ(nullptr, get_sched_policy_name(SchedPolicy(-2)));\n    EXPECT_EQ(nullptr, get_sched_policy_name(SP_CNT));\n}\n\nTEST(SchedPolicy, get_cpuset_policy_profile_name) {\n    EXPECT_STREQ(\"CPUSET_SP_BACKGROUND\", get_cpuset_policy_profile_name(SP_BACKGROUND));\n    EXPECT_EQ(nullptr, get_cpuset_policy_profile_name(SchedPolicy(-2)));\n    EXPECT_EQ(nullptr, get_cpuset_policy_profile_name(SP_CNT));\n}\n\nTEST(SchedPolicy, get_sched_policy_profile_name) {\n    EXPECT_STREQ(\"SCHED_SP_BACKGROUND\", get_sched_policy_profile_name(SP_BACKGROUND));\n    EXPECT_EQ(nullptr, get_sched_policy_profile_name(SchedPolicy(-2)));\n    EXPECT_EQ(nullptr, get_sched_policy_profile_name(SP_CNT));\n}\n"
  },
  {
    "path": "libcutils/socket_inaddr_any_server_unix.cpp",
    "content": "/*\n** Copyright 2006, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\n#include <cutils/sockets.h>\n\n#include <errno.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <sys/socket.h>\n#include <sys/select.h>\n#include <sys/types.h>\n#include <netinet/in.h>\n\n#define LISTEN_BACKLOG 4\n\n/* open listen() port on any interface */\nint socket_inaddr_any_server(int port, int type)\n{\n    struct sockaddr_in6 addr;\n    int s, n;\n\n    memset(&addr, 0, sizeof(addr));\n    addr.sin6_family = AF_INET6;\n    addr.sin6_port = htons(port);\n    addr.sin6_addr = in6addr_any;\n\n    s = socket(AF_INET6, type, 0);\n    if (s < 0) return -1;\n\n    n = 1;\n    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));\n\n    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {\n        close(s);\n        return -1;\n    }\n\n    if (type == SOCK_STREAM) {\n        int ret;\n\n        ret = listen(s, LISTEN_BACKLOG);\n\n        if (ret < 0) {\n            close(s);\n            return -1; \n        }\n    }\n\n    return s;\n}\n"
  },
  {
    "path": "libcutils/socket_inaddr_any_server_windows.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <cutils/sockets.h>\n\n#include <errno.h>\n\n#define LISTEN_BACKLOG 4\n\nextern bool initialize_windows_sockets();\n\nSOCKET socket_inaddr_any_server(int port, int type) {\n    if (!initialize_windows_sockets()) {\n      return INVALID_SOCKET;\n    }\n\n    SOCKET sock = socket(AF_INET6, type, 0);\n    if (sock == INVALID_SOCKET) {\n        return INVALID_SOCKET;\n    }\n\n    // Enforce exclusive addresses so nobody can steal the port from us (1),\n    // and enable dual-stack so both IPv4 and IPv6 work (2).\n    // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.\n    // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.\n    int exclusive = 1;\n    DWORD v6_only = 0;\n    if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char*)&exclusive,\n                   sizeof(exclusive)) == SOCKET_ERROR ||\n        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6_only,\n                   sizeof(v6_only)) == SOCKET_ERROR) {\n        closesocket(sock);\n        return INVALID_SOCKET;\n    }\n\n    // Bind the socket to our local port.\n    struct sockaddr_in6 addr;\n    memset(&addr, 0, sizeof(addr));\n    addr.sin6_family = AF_INET6;\n    addr.sin6_port = htons(port);\n    addr.sin6_addr = in6addr_any;\n    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {\n        closesocket(sock);\n        return INVALID_SOCKET;\n    }\n\n    // Start listening for connections if this is a TCP socket.\n    if (type == SOCK_STREAM && listen(sock, LISTEN_BACKLOG) == SOCKET_ERROR) {\n        closesocket(sock);\n        return INVALID_SOCKET;\n    }\n\n    return sock;\n}\n"
  },
  {
    "path": "libcutils/socket_local_client_unix.cpp",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/sockets.h>\n\n#include <errno.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#if defined(_WIN32)\n\nint socket_local_client(const char *name, int namespaceId, int type)\n{\n    errno = ENOSYS;\n    return -1;\n}\n\n#else /* !_WIN32 */\n\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <sys/select.h>\n#include <sys/types.h>\n\n#include \"socket_local_unix.h\"\n\n#define LISTEN_BACKLOG 4\n\n/* Documented in header file. */\nint socket_make_sockaddr_un(const char *name, int namespaceId, \n        struct sockaddr_un *p_addr, socklen_t *alen)\n{\n    memset (p_addr, 0, sizeof (*p_addr));\n    size_t namelen;\n\n    switch (namespaceId) {\n        case ANDROID_SOCKET_NAMESPACE_ABSTRACT:\n#if defined(__linux__)\n            namelen  = strlen(name);\n\n            // Test with length +1 for the *initial* '\\0'.\n            if ((namelen + 1) > sizeof(p_addr->sun_path)) {\n                goto error;\n            }\n\n            /*\n             * Note: The path in this case is *not* supposed to be\n             * '\\0'-terminated. (\"man 7 unix\" for the gory details.)\n             */\n            \n            p_addr->sun_path[0] = 0;\n            memcpy(p_addr->sun_path + 1, name, namelen);\n#else\n            /* this OS doesn't have the Linux abstract namespace */\n\n            namelen = strlen(name) + strlen(FILESYSTEM_SOCKET_PREFIX);\n            /* unix_path_max appears to be missing on linux */\n            if (namelen > sizeof(*p_addr) \n                    - offsetof(struct sockaddr_un, sun_path) - 1) {\n                goto error;\n            }\n\n            strcpy(p_addr->sun_path, FILESYSTEM_SOCKET_PREFIX);\n            strcat(p_addr->sun_path, name);\n#endif\n        break;\n\n        case ANDROID_SOCKET_NAMESPACE_RESERVED:\n            namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);\n            /* unix_path_max appears to be missing on linux */\n            if (namelen > sizeof(*p_addr) \n                    - offsetof(struct sockaddr_un, sun_path) - 1) {\n                goto error;\n            }\n\n            strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);\n            strcat(p_addr->sun_path, name);\n        break;\n\n        case ANDROID_SOCKET_NAMESPACE_FILESYSTEM:\n            namelen = strlen(name);\n            /* unix_path_max appears to be missing on linux */\n            if (namelen > sizeof(*p_addr) \n                    - offsetof(struct sockaddr_un, sun_path) - 1) {\n                goto error;\n            }\n\n            strcpy(p_addr->sun_path, name);\n        break;\n        default:\n            // invalid namespace id\n            return -1;\n    }\n\n    p_addr->sun_family = AF_LOCAL;\n    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;\n    return 0;\nerror:\n    return -1;\n}\n\n/**\n * connect to peer named \"name\" on fd\n * returns same fd or -1 on error.\n * fd is not closed on error. that's your job.\n * \n * Used by AndroidSocketImpl\n */\nint socket_local_client_connect(int fd, const char* name, int namespaceId, int /*type*/) {\n    struct sockaddr_un addr;\n    socklen_t alen;\n    int err;\n\n    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);\n\n    if (err < 0) {\n        goto error;\n    }\n\n    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {\n        goto error;\n    }\n\n    return fd;\n\nerror:\n    return -1;\n}\n\n/** \n * connect to peer named \"name\"\n * returns fd or -1 on error\n */\nint socket_local_client(const char *name, int namespaceId, int type)\n{\n    int s;\n\n    s = socket(AF_LOCAL, type, 0);\n    if(s < 0) return -1;\n\n    if ( 0 > socket_local_client_connect(s, name, namespaceId, type)) {\n        close(s);\n        return -1;\n    }\n\n    return s;\n}\n\n#endif /* !_WIN32 */\n"
  },
  {
    "path": "libcutils/socket_local_server_unix.cpp",
    "content": "/* libs/cutils/socket_local_server.c\n**\n** Copyright 2006, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\n#include <cutils/sockets.h>\n\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <errno.h>\n#include <stddef.h>\n\n#if defined(_WIN32)\n\nint socket_local_server(const char *name, int namespaceId, int type)\n{\n    errno = ENOSYS;\n    return -1;\n}\n\n#else /* !_WIN32 */\n\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <sys/select.h>\n#include <sys/types.h>\n#include <netinet/in.h>\n\n#include \"socket_local_unix.h\"\n\n#define LISTEN_BACKLOG 4\n\n/* Only the bottom bits are really the socket type; there are flags too. */\n#define SOCK_TYPE_MASK 0xf\n\n/**\n * Binds a pre-created socket(AF_LOCAL) 's' to 'name'\n * returns 's' on success, -1 on fail\n *\n * Does not call listen()\n */\nint socket_local_server_bind(int s, const char *name, int namespaceId)\n{\n    struct sockaddr_un addr;\n    socklen_t alen;\n    int n;\n    int err;\n\n    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);\n\n    if (err < 0) {\n        return -1;\n    }\n\n    /* basically: if this is a filesystem path, unlink first */\n#if !defined(__linux__)\n    if (1) {\n#else\n    if (namespaceId == ANDROID_SOCKET_NAMESPACE_RESERVED\n        || namespaceId == ANDROID_SOCKET_NAMESPACE_FILESYSTEM) {\n#endif\n        /*ignore ENOENT*/\n        unlink(addr.sun_path);\n    }\n\n    n = 1;\n    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n));\n\n    if(bind(s, (struct sockaddr *) &addr, alen) < 0) {\n        return -1;\n    }\n\n    return s;\n\n}\n\n\n/** Open a server-side UNIX domain datagram socket in the Linux non-filesystem \n *  namespace\n *\n *  Returns fd on success, -1 on fail\n */\n\nint socket_local_server(const char *name, int namespaceId, int type)\n{\n    int err;\n    int s;\n    \n    s = socket(AF_LOCAL, type, 0);\n    if (s < 0) return -1;\n\n    err = socket_local_server_bind(s, name, namespaceId);\n\n    if (err < 0) {\n        close(s);\n        return -1;\n    }\n\n    if ((type & SOCK_TYPE_MASK) == SOCK_STREAM) {\n        int ret;\n\n        ret = listen(s, LISTEN_BACKLOG);\n\n        if (ret < 0) {\n            close(s);\n            return -1;\n        }\n    }\n\n    return s;\n}\n\n#endif /* !_WIN32 */\n"
  },
  {
    "path": "libcutils/socket_local_unix.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __SOCKET_LOCAL_H\n#define __SOCKET_LOCAL_H\n\n#include <sys/socket.h>\n\n#define FILESYSTEM_SOCKET_PREFIX \"/tmp/\" \n#define ANDROID_RESERVED_SOCKET_PREFIX \"/dev/socket/\"\n\n/*\n * Set up a given sockaddr_un, to have it refer to the given\n * name in the given namespace. The namespace must be one\n * of <code>ANDROID_SOCKET_NAMESPACE_ABSTRACT</code>,\n * <code>ANDROID_SOCKET_NAMESPACE_RESERVED</code>, or\n * <code>ANDROID_SOCKET_NAMESPACE_FILESYSTEM</code>. Upon success,\n * the pointed at sockaddr_un is filled in and the pointed at\n * socklen_t is set to indicate the final length. This function\n * will fail if the namespace is invalid (not one of the indicated\n * constants) or if the name is too long.\n * \n * @return 0 on success or -1 on failure\n */ \nint socket_make_sockaddr_un(const char *name, int namespaceId, \n        struct sockaddr_un *p_addr, socklen_t *alen);\n\n#endif\n"
  },
  {
    "path": "libcutils/socket_network_client_unix.cpp",
    "content": "/*\n** Copyright 2006, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\"); \n** you may not use this file except in compliance with the License. \n** You may obtain a copy of the License at \n**\n**     http://www.apache.org/licenses/LICENSE-2.0 \n**\n** Unless required by applicable law or agreed to in writing, software \n** distributed under the License is distributed on an \"AS IS\" BASIS, \n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n** See the License for the specific language governing permissions and \n** limitations under the License.\n*/\n\n#include <cutils/sockets.h>\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <sys/socket.h>\n#include <sys/select.h>\n#include <sys/types.h>\n#include <netinet/in.h>\n#include <netdb.h>\n\nstatic int toggle_O_NONBLOCK(int s) {\n    int flags = fcntl(s, F_GETFL);\n    if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {\n        close(s);\n        return -1;\n    }\n    return s;\n}\n\n// Connect to the given host and port.\n// 'timeout' is in seconds (0 for no timeout).\n// Returns a file descriptor or -1 on error.\n// On error, check *getaddrinfo_error (for use with gai_strerror) first;\n// if that's 0, use errno instead.\nint socket_network_client_timeout(const char* host, int port, int type, int timeout,\n                                  int* getaddrinfo_error) {\n    struct addrinfo hints;\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_socktype = type;\n\n    char port_str[16];\n    snprintf(port_str, sizeof(port_str), \"%d\", port);\n\n    struct addrinfo* addrs;\n    *getaddrinfo_error = getaddrinfo(host, port_str, &hints, &addrs);\n    if (*getaddrinfo_error != 0) {\n        return -1;\n    }\n\n    int result = -1;\n    for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {\n        // The Mac doesn't have SOCK_NONBLOCK.\n        int s = socket(addr->ai_family, type, addr->ai_protocol);\n        if (s == -1 || toggle_O_NONBLOCK(s) == -1) break;\n\n        int rc = connect(s, addr->ai_addr, addr->ai_addrlen);\n        if (rc == 0) {\n            result = toggle_O_NONBLOCK(s);\n            break;\n        } else if (rc == -1 && errno != EINPROGRESS) {\n            close(s);\n            continue;\n        }\n\n        fd_set r_set;\n        FD_ZERO(&r_set);\n        FD_SET(s, &r_set);\n        fd_set w_set = r_set;\n\n        struct timeval ts;\n        ts.tv_sec = timeout;\n        ts.tv_usec = 0;\n        if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {\n            close(s);\n            break;\n        }\n        if (rc == 0) {  // we had a timeout\n            errno = ETIMEDOUT;\n            close(s);\n            break;\n        }\n\n        int error = 0;\n        socklen_t len = sizeof(error);\n        if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {\n            if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {\n                close(s);\n                break;\n            }\n        } else {\n            close(s);\n            break;\n        }\n\n        if (error) {  // check if we had a socket error\n            // TODO: Update the timeout.\n            errno = error;\n            close(s);\n            continue;\n        }\n\n        result = toggle_O_NONBLOCK(s);\n        break;\n    }\n\n    freeaddrinfo(addrs);\n    return result;\n}\n\nint socket_network_client(const char* host, int port, int type) {\n    int getaddrinfo_error;\n    return socket_network_client_timeout(host, port, type, 0, &getaddrinfo_error);\n}\n"
  },
  {
    "path": "libcutils/socket_network_client_windows.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <cutils/sockets.h>\n\nextern bool initialize_windows_sockets();\n\nSOCKET socket_network_client(const char* host, int port, int type) {\n    if (!initialize_windows_sockets()) {\n        return INVALID_SOCKET;\n    }\n\n    // First resolve the host and port parameters into a usable network address.\n    struct addrinfo hints;\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_socktype = type;\n\n    struct addrinfo* address = NULL;\n    char port_str[16];\n    snprintf(port_str, sizeof(port_str), \"%d\", port);\n    if (getaddrinfo(host, port_str, &hints, &address) != 0 || address == NULL) {\n        if (address != NULL) {\n            freeaddrinfo(address);\n        }\n        return INVALID_SOCKET;\n    }\n\n    // Now create and connect the socket.\n    SOCKET sock = socket(address->ai_family, address->ai_socktype,\n                         address->ai_protocol);\n    if (sock == INVALID_SOCKET) {\n        freeaddrinfo(address);\n        return INVALID_SOCKET;\n    }\n\n    if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) {\n        closesocket(sock);\n        freeaddrinfo(address);\n        return INVALID_SOCKET;\n    }\n\n    freeaddrinfo(address);\n    return sock;\n}\n"
  },
  {
    "path": "libcutils/sockets.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n// This file contains socket implementation that can be shared between\n// platforms as long as the correct headers are included.\n\n#include <cutils/sockets.h>\n\nint socket_get_local_port(cutils_socket_t sock) {\n    sockaddr_storage addr;\n    socklen_t addr_size = sizeof(addr);\n\n    if (getsockname(sock, reinterpret_cast<sockaddr*>(&addr), &addr_size) == 0) {\n        // sockaddr_in and sockaddr_in6 always overlap the port field.\n        return ntohs(reinterpret_cast<sockaddr_in*>(&addr)->sin_port);\n    }\n    return -1;\n}\n"
  },
  {
    "path": "libcutils/sockets_test.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Tests socket functionality using loopback connections. Requires IPv4 and\n// IPv6 capabilities. These tests assume that no UDP packets are lost, which\n// should be the case for loopback communication, but is not guaranteed.\n\n#include <string.h>\n#include <time.h>\n\n#include <cutils/sockets.h>\n#include <gtest/gtest.h>\n\n// Makes sure the passed sockets are valid, sends data between them, and closes\n// them. Any failures are logged with gtest.\n//\n// On Mac recvfrom() will not fill in the address for TCP sockets, so we need\n// separate logic paths depending on socket type.\nstatic void TestConnectedSockets(cutils_socket_t server, cutils_socket_t client,\n                                 int type) {\n    ASSERT_NE(INVALID_SOCKET, server);\n    ASSERT_NE(INVALID_SOCKET, client);\n\n    char buffer[128];\n    sockaddr_storage addr;\n    socklen_t addr_size = sizeof(addr);\n\n    // Send client -> server first to get the UDP client's address.\n    ASSERT_EQ(3, send(client, \"foo\", 3, 0));\n    if (type == SOCK_DGRAM) {\n        EXPECT_EQ(3, recvfrom(server, buffer, sizeof(buffer), 0,\n                              reinterpret_cast<sockaddr*>(&addr), &addr_size));\n    } else {\n        EXPECT_EQ(3, recv(server, buffer, sizeof(buffer), 0));\n    }\n    EXPECT_EQ(0, memcmp(buffer, \"foo\", 3));\n\n    // Now send server -> client.\n    if (type == SOCK_DGRAM) {\n        ASSERT_EQ(3, sendto(server, \"bar\", 3, 0,\n                            reinterpret_cast<sockaddr*>(&addr), addr_size));\n    } else {\n        ASSERT_EQ(3, send(server, \"bar\", 3, 0));\n    }\n    EXPECT_EQ(3, recv(client, buffer, sizeof(buffer), 0));\n    EXPECT_EQ(0, memcmp(buffer, \"bar\", 3));\n\n    // Send multiple buffers using socket_send_buffers().\n    std::string data[] = {\"foo\", \"bar\", \"12345\"};\n    cutils_socket_buffer_t socket_buffers[] = { {data[0].data(), data[0].length()},\n                                                {data[1].data(), data[1].length()},\n                                                {data[2].data(), data[2].length()} };\n    EXPECT_EQ(11, socket_send_buffers(client, socket_buffers, 3));\n    EXPECT_EQ(11, recv(server, buffer, sizeof(buffer), 0));\n    EXPECT_EQ(0, memcmp(buffer, \"foobar12345\", 11));\n\n    EXPECT_EQ(0, socket_close(server));\n    EXPECT_EQ(0, socket_close(client));\n}\n\n// Tests socket_get_local_port().\nTEST(SocketsTest, TestGetLocalPort) {\n    cutils_socket_t server;\n\n    // Check a bunch of ports so that we can ignore any conflicts in case\n    // of ports already being taken, but if a server is able to start up we\n    // should always be able to read its port.\n    for (int port : {10000, 12345, 15999, 20202, 25000}) {\n        for (int type : {SOCK_DGRAM, SOCK_STREAM}) {\n            server = socket_inaddr_any_server(port, type);\n            if (server != INVALID_SOCKET) {\n                EXPECT_EQ(port, socket_get_local_port(server));\n            }\n            socket_close(server);\n        }\n    }\n\n    // Check expected failure for an invalid socket.\n    EXPECT_EQ(-1, socket_get_local_port(INVALID_SOCKET));\n}\n\n// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 UDP.\nTEST(SocketsTest, TestIpv4UdpLoopback) {\n    cutils_socket_t server = socket_inaddr_any_server(0, SOCK_DGRAM);\n    cutils_socket_t client = socket_network_client(\n            \"127.0.0.1\", socket_get_local_port(server), SOCK_DGRAM);\n\n    TestConnectedSockets(server, client, SOCK_DGRAM);\n}\n\n// Tests socket_inaddr_any_server() and socket_network_client() for IPv4 TCP.\nTEST(SocketsTest, TestIpv4TcpLoopback) {\n    cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);\n    ASSERT_NE(INVALID_SOCKET, server);\n\n    cutils_socket_t client = socket_network_client(\n            \"127.0.0.1\", socket_get_local_port(server), SOCK_STREAM);\n    cutils_socket_t handler = accept(server, nullptr, nullptr);\n    EXPECT_EQ(0, socket_close(server));\n\n    TestConnectedSockets(handler, client, SOCK_STREAM);\n}\n\n// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 UDP.\nTEST(SocketsTest, TestIpv6UdpLoopback) {\n    cutils_socket_t server = socket_inaddr_any_server(0, SOCK_DGRAM);\n    cutils_socket_t client = socket_network_client(\n            \"::1\", socket_get_local_port(server), SOCK_DGRAM);\n\n    TestConnectedSockets(server, client, SOCK_DGRAM);\n}\n\n// Tests socket_inaddr_any_server() and socket_network_client() for IPv6 TCP.\nTEST(SocketsTest, TestIpv6TcpLoopback) {\n    cutils_socket_t server = socket_inaddr_any_server(0, SOCK_STREAM);\n    ASSERT_NE(INVALID_SOCKET, server);\n\n    cutils_socket_t client = socket_network_client(\n            \"::1\", socket_get_local_port(server), SOCK_STREAM);\n    cutils_socket_t handler = accept(server, nullptr, nullptr);\n    EXPECT_EQ(0, socket_close(server));\n\n    TestConnectedSockets(handler, client, SOCK_STREAM);\n}\n\n// Tests socket_send_buffers() failure.\nTEST(SocketsTest, TestSocketSendBuffersFailure) {\n    EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));\n}\n"
  },
  {
    "path": "libcutils/sockets_unix.cpp",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/sockets.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/uio.h>\n#include <sys/un.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"android_get_control_env.h\"\n\nint socket_close(int sock) {\n    return close(sock);\n}\n\nssize_t socket_send_buffers(cutils_socket_t sock,\n                            const cutils_socket_buffer_t* buffers,\n                            size_t num_buffers) {\n    if (num_buffers > SOCKET_SEND_BUFFERS_MAX_BUFFERS) {\n        return -1;\n    }\n\n    iovec iovec_buffers[SOCKET_SEND_BUFFERS_MAX_BUFFERS];\n    for (size_t i = 0; i < num_buffers; ++i) {\n        // It's safe to cast away const here; iovec declares non-const\n        // void* because it's used for both send and receive, but since\n        // we're only sending, the data won't be modified.\n        iovec_buffers[i].iov_base = const_cast<void*>(buffers[i].data);\n        iovec_buffers[i].iov_len = buffers[i].length;\n    }\n\n    return writev(sock, iovec_buffers, num_buffers);\n}\n\n#if defined(__ANDROID__)\nint android_get_control_socket(const char* name) {\n    int fd = __android_get_control_from_env(ANDROID_SOCKET_ENV_PREFIX, name);\n\n    if (fd < 0) return fd;\n\n    // Compare to UNIX domain socket name, must match!\n    struct sockaddr_un addr;\n    socklen_t addrlen = sizeof(addr);\n    int ret = getsockname(fd, (struct sockaddr*)&addr, &addrlen);\n    if (ret < 0) return -1;\n\n    constexpr char prefix[] = ANDROID_SOCKET_DIR \"/\";\n    constexpr size_t prefix_size = sizeof(prefix) - sizeof('\\0');\n    if ((strncmp(addr.sun_path, prefix, prefix_size) == 0) &&\n        (strcmp(addr.sun_path + prefix_size, name) == 0)) {\n        // It is what we think it is\n        return fd;\n    }\n    return -1;\n}\n#else\nint android_get_control_socket(const char*) {\n    return -1;\n}\n#endif\n"
  },
  {
    "path": "libcutils/sockets_windows.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <cutils/sockets.h>\n\n// https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx\n// claims WSACleanup() should be called before program exit, but general\n// consensus seems to be that it hasn't actually been necessary for a long time,\n// likely since Windows 3.1. Additionally, trying to properly use WSACleanup()\n// can be extremely tricky and cause deadlock when using threads or atexit().\n//\n// Both adb (1) and Chrome (2) purposefully avoid WSACleanup() with no issues.\n// (1) https://android.googlesource.com/platform/packages/modules/adb.git/+/main/sysdeps_win32.cpp\n// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc\nbool initialize_windows_sockets() {\n    // There's no harm in calling WSAStartup() multiple times but no benefit\n    // either, we may as well skip it after the first.\n    static bool init_success = false;\n\n    if (!init_success) {\n        WSADATA wsaData;\n        init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);\n    }\n\n    return init_success;\n}\n\nint socket_close(cutils_socket_t sock) {\n    return closesocket(sock);\n}\n\nssize_t socket_send_buffers(cutils_socket_t sock,\n                            const cutils_socket_buffer_t* buffers,\n                            size_t num_buffers) {\n    if (num_buffers > SOCKET_SEND_BUFFERS_MAX_BUFFERS) {\n        return -1;\n    }\n\n    WSABUF wsa_buffers[SOCKET_SEND_BUFFERS_MAX_BUFFERS];\n    for (size_t i = 0; i < num_buffers; ++i) {\n        // It's safe to cast away const here; WSABUF declares non-const\n        // void* because it's used for both send and receive, but since\n        // we're only sending, the data won't be modified.\n        wsa_buffers[i].buf =\n                reinterpret_cast<char*>(const_cast<void*>(buffers[i].data));\n        wsa_buffers[i].len = buffers[i].length;\n    }\n\n    DWORD bytes_sent = 0;\n    if (WSASend(sock, wsa_buffers, num_buffers, &bytes_sent, 0, nullptr,\n                nullptr) != SOCKET_ERROR) {\n        return bytes_sent;\n    }\n\n    return -1;\n}\n\nint android_get_control_socket(const char*) {\n    return -1;\n}\n"
  },
  {
    "path": "libcutils/str_parms.cpp",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/str_parms.h>\n\n#define LOG_TAG \"str_params\"\n//#define LOG_NDEBUG 0\n\n#define _GNU_SOURCE 1\n#include <errno.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <cutils/hashmap.h>\n#include <cutils/memory.h>\n#include <log/log.h>\n\n/* When an object is allocated but not freed in a function,\n * because its ownership is released to other object like a hashmap,\n * call RELEASE_OWNERSHIP to tell the clang analyzer and avoid\n * false warnings about potential memory leak.\n * For now, a \"temporary\" assignment to global variables\n * is enough to confuse the clang static analyzer.\n */\n#ifdef __clang_analyzer__\nstatic void *released_pointer;\n#define RELEASE_OWNERSHIP(x) { released_pointer = x; released_pointer = 0; }\n#else\n#define RELEASE_OWNERSHIP(x)\n#endif\n\nstruct str_parms {\n    Hashmap *map;\n};\n\n\nstatic bool str_eq(void *key_a, void *key_b)\n{\n    return !strcmp((const char *)key_a, (const char *)key_b);\n}\n\n/* use djb hash unless we find it inadequate */\n#ifdef __clang__\n__attribute__((no_sanitize(\"integer\")))\n#endif\nstatic int str_hash_fn(void *str)\n{\n    uint32_t hash = 5381;\n\n    for (char* p = static_cast<char*>(str); p && *p; p++)\n        hash = ((hash << 5) + hash) + *p;\n    return (int)hash;\n}\n\nstruct str_parms *str_parms_create(void)\n{\n    str_parms* s = static_cast<str_parms*>(calloc(1, sizeof(str_parms)));\n    if (!s) return NULL;\n\n    s->map = hashmapCreate(5, str_hash_fn, str_eq);\n    if (!s->map) {\n        free(s);\n        return NULL;\n    }\n\n    return s;\n}\n\nstruct remove_ctxt {\n    struct str_parms *str_parms;\n    const char *key;\n};\n\nstatic bool remove_pair(void *key, void *value, void *context)\n{\n    remove_ctxt* ctxt = static_cast<remove_ctxt*>(context);\n    bool should_continue;\n\n    /*\n     * - if key is not supplied, then we are removing all entries,\n     *   so remove key and continue (i.e. return true)\n     * - if key is supplied and matches, then remove it and don't\n     *   continue (return false). Otherwise, return true and keep searching\n     *   for key.\n     *\n     */\n    if (!ctxt->key) {\n        should_continue = true;\n        goto do_remove;\n    } else if (!strcmp(ctxt->key, static_cast<const char*>(key))) {\n        should_continue = false;\n        goto do_remove;\n    }\n\n    return true;\n\ndo_remove:\n    hashmapRemove(ctxt->str_parms->map, key);\n    free(key);\n    free(value);\n    return should_continue;\n}\n\nvoid str_parms_del(struct str_parms *str_parms, const char *key)\n{\n    struct remove_ctxt ctxt = {\n        .str_parms = str_parms,\n        .key = key,\n    };\n    hashmapForEach(str_parms->map, remove_pair, &ctxt);\n}\n\nvoid str_parms_destroy(struct str_parms *str_parms)\n{\n    struct remove_ctxt ctxt = {\n        .str_parms = str_parms,\n    };\n\n    hashmapForEach(str_parms->map, remove_pair, &ctxt);\n    hashmapFree(str_parms->map);\n    free(str_parms);\n}\n\nstruct str_parms *str_parms_create_str(const char *_string)\n{\n    struct str_parms *str_parms;\n    char *str;\n    char *kvpair;\n    char *tmpstr;\n    int items = 0;\n\n    str_parms = str_parms_create();\n    if (!str_parms)\n        goto err_create_str_parms;\n\n    str = strdup(_string);\n    if (!str)\n        goto err_strdup;\n\n    ALOGV(\"%s: source string == '%s'\\n\", __func__, _string);\n\n    kvpair = strtok_r(str, \";\", &tmpstr);\n    while (kvpair && *kvpair) {\n        char *eq = strchr(kvpair, '='); /* would love strchrnul */\n        char *value;\n        char *key;\n        void *old_val;\n\n        if (eq == kvpair)\n            goto next_pair;\n\n        if (eq) {\n            key = strndup(kvpair, eq - kvpair);\n            if (*(++eq))\n                value = strdup(eq);\n            else\n                value = strdup(\"\");\n        } else {\n            key = strdup(kvpair);\n            value = strdup(\"\");\n        }\n\n        /* if we replaced a value, free it */\n        old_val = hashmapPut(str_parms->map, key, value);\n        RELEASE_OWNERSHIP(value);\n        if (old_val) {\n            free(old_val);\n            free(key);\n        } else {\n            RELEASE_OWNERSHIP(key);\n        }\n\n        items++;\nnext_pair:\n        kvpair = strtok_r(NULL, \";\", &tmpstr);\n    }\n\n    if (!items)\n        ALOGV(\"%s: no items found in string\\n\", __func__);\n\n    free(str);\n\n    return str_parms;\n\nerr_strdup:\n    str_parms_destroy(str_parms);\nerr_create_str_parms:\n    return NULL;\n}\n\nint str_parms_add_str(struct str_parms *str_parms, const char *key,\n                      const char *value)\n{\n    void *tmp_key = NULL;\n    void *tmp_val = NULL;\n    void *old_val = NULL;\n\n    // strdup and hashmapPut both set errno on failure.\n    // Set errno to 0 so we can recognize whether anything went wrong.\n    int saved_errno = errno;\n    errno = 0;\n\n    tmp_key = strdup(key);\n    if (tmp_key == NULL) {\n        goto clean_up;\n    }\n\n    tmp_val = strdup(value);\n    if (tmp_val == NULL) {\n        goto clean_up;\n    }\n\n    old_val = hashmapPut(str_parms->map, tmp_key, tmp_val);\n    if (old_val == NULL) {\n        // Did hashmapPut fail?\n        if (errno == ENOMEM) {\n            goto clean_up;\n        }\n        // For new keys, hashmap takes ownership of tmp_key and tmp_val.\n        RELEASE_OWNERSHIP(tmp_key);\n        RELEASE_OWNERSHIP(tmp_val);\n        tmp_key = tmp_val = NULL;\n    } else {\n        // For existing keys, hashmap takes ownership of tmp_val.\n        // (It also gives up ownership of old_val.)\n        RELEASE_OWNERSHIP(tmp_val);\n        tmp_val = NULL;\n    }\n\nclean_up:\n    free(tmp_key);\n    free(tmp_val);\n    free(old_val);\n    int result = -errno;\n    errno = saved_errno;\n    return result;\n}\n\nint str_parms_add_int(struct str_parms *str_parms, const char *key, int value)\n{\n    char val_str[12];\n    int ret;\n\n    ret = snprintf(val_str, sizeof(val_str), \"%d\", value);\n    if (ret < 0)\n        return -EINVAL;\n\n    ret = str_parms_add_str(str_parms, key, val_str);\n    return ret;\n}\n\nint str_parms_add_float(struct str_parms *str_parms, const char *key,\n                        float value)\n{\n    char val_str[23];\n    int ret;\n\n    ret = snprintf(val_str, sizeof(val_str), \"%.10f\", value);\n    if (ret < 0)\n        return -EINVAL;\n\n    ret = str_parms_add_str(str_parms, key, val_str);\n    return ret;\n}\n\nint str_parms_has_key(struct str_parms *str_parms, const char *key) {\n    return hashmapGet(str_parms->map, (void *)key) != NULL;\n}\n\nint str_parms_get_str(struct str_parms *str_parms, const char *key, char *val,\n                      int len)\n{\n    // TODO: hashmapGet should take a const* key.\n    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));\n    if (value)\n        return strlcpy(val, value, len);\n\n    return -ENOENT;\n}\n\nint str_parms_get_int(struct str_parms *str_parms, const char *key, int *val)\n{\n    char *end;\n\n    // TODO: hashmapGet should take a const* key.\n    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)key));\n    if (!value)\n        return -ENOENT;\n\n    *val = (int)strtol(value, &end, 0);\n    if (*value != '\\0' && *end == '\\0')\n        return 0;\n\n    return -EINVAL;\n}\n\nint str_parms_get_float(struct str_parms *str_parms, const char *key,\n                        float *val)\n{\n    float out;\n    char *end;\n\n    // TODO: hashmapGet should take a const* key.\n    char* value = static_cast<char*>(hashmapGet(str_parms->map, (void*)(key)));\n    if (!value)\n        return -ENOENT;\n\n    out = strtof(value, &end);\n    if (*value == '\\0' || *end != '\\0')\n        return -EINVAL;\n\n    *val = out;\n    return 0;\n}\n\nstatic bool combine_strings(void *key, void *value, void *context)\n{\n    char** old_str = static_cast<char**>(context);\n    char *new_str;\n    int ret;\n\n    ret = asprintf(&new_str, \"%s%s%s=%s\",\n                   *old_str ? *old_str : \"\",\n                   *old_str ? \";\" : \"\",\n                   (char *)key,\n                   (char *)value);\n    if (*old_str)\n        free(*old_str);\n\n    if (ret >= 0) {\n        *old_str = new_str;\n        return true;\n    }\n\n    *old_str = NULL;\n    return false;\n}\n\nchar *str_parms_to_str(struct str_parms *str_parms)\n{\n    char *str = NULL;\n    hashmapForEach(str_parms->map, combine_strings, &str);\n    return (str != NULL) ? str : strdup(\"\");\n}\n\nstatic bool dump_entry(void* key, void* value, void* /*context*/) {\n    ALOGI(\"key: '%s' value: '%s'\\n\", (char *)key, (char *)value);\n    return true;\n}\n\nvoid str_parms_dump(struct str_parms *str_parms)\n{\n    hashmapForEach(str_parms->map, dump_entry, str_parms);\n}\n"
  },
  {
    "path": "libcutils/str_parms_test.cpp",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/str_parms.h>\n#include <gtest/gtest.h>\n\nstatic void test_str_parms_str(const char* str, const char* expected) {\n    str_parms* str_parms = str_parms_create_str(str);\n    str_parms_add_str(str_parms, \"dude\", \"woah\");\n    str_parms_add_str(str_parms, \"dude\", \"woah\");\n    str_parms_del(str_parms, \"dude\");\n    str_parms_dump(str_parms);\n    char* out_str = str_parms_to_str(str_parms);\n    str_parms_destroy(str_parms);\n    ASSERT_STREQ(expected, out_str) << str;\n    free(out_str);\n}\n\nTEST(str_parms, smoke) {\n    test_str_parms_str(\"\", \"\");\n    test_str_parms_str(\";\", \"\");\n    test_str_parms_str(\"=\", \"\");\n    test_str_parms_str(\"=;\", \"\");\n    test_str_parms_str(\"=bar\", \"\");\n    test_str_parms_str(\"=bar;\", \"\");\n    test_str_parms_str(\"foo=\", \"foo=\");\n    test_str_parms_str(\"foo=;\", \"foo=\");\n    test_str_parms_str(\"foo=bar\", \"foo=bar\");\n    test_str_parms_str(\"foo=bar;\", \"foo=bar\");\n    test_str_parms_str(\"foo=bar;baz\", \"foo=bar;baz=\");\n    test_str_parms_str(\"foo=bar;baz=\", \"foo=bar;baz=\");\n    test_str_parms_str(\"foo=bar;baz=bat\", \"foo=bar;baz=bat\");\n    test_str_parms_str(\"foo=bar;baz=bat;\", \"foo=bar;baz=bat\");\n    test_str_parms_str(\"foo=bar1;baz=bat;foo=bar2\", \"foo=bar2;baz=bat\");\n}\n\nTEST(str_parms, put_ENOMEM) {\n    // hashmapPut reports errors by setting errno to ENOMEM.\n    // Test that we're not confused by running in an environment where this is already true.\n    errno = ENOMEM;\n    test_str_parms_str(\"foo=bar;baz=\", \"foo=bar;baz=\");\n    ASSERT_EQ(ENOMEM, errno);\n    test_str_parms_str(\"foo=bar;baz=\", \"foo=bar;baz=\");\n}\n"
  },
  {
    "path": "libcutils/strlcpy.c",
    "content": "/*\n * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>\n *\n * Permission to use, copy, modify, and distribute this software for any\n * purpose with or without fee is hereby granted, provided that the above\n * copyright notice and this permission notice appear in all copies.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n */\n\n#include <sys/types.h>\n\n#if defined(__GLIBC__) || defined(_WIN32)\n\n#include <string.h>\n\n#include <cutils/memory.h>\n\n/* Implementation of strlcpy() for platforms that don't already have it. */\n\n/*\n * Copy src to string dst of size siz.  At most siz-1 characters\n * will be copied.  Always NUL terminates (unless siz == 0).\n * Returns strlen(src); if retval >= siz, truncation occurred.\n */\nsize_t\nstrlcpy(char *dst, const char *src, size_t siz)\n{\n\tchar *d = dst;\n\tconst char *s = src;\n\tsize_t n = siz;\n\n\t/* Copy as many bytes as will fit */\n\tif (n != 0) {\n\t\twhile (--n != 0) {\n\t\t\tif ((*d++ = *s++) == '\\0')\n\t\t\t\tbreak;\n\t\t}\n  }\n\n\t/* Not enough room in dst, add NUL and traverse rest of src */\n\tif (n == 0) {\n\t\tif (siz != 0)\n\t\t\t*d = '\\0';\t\t/* NUL-terminate dst */\n\t\twhile (*s++)\n\t\t\t;\n\t}\n\n\treturn(s - src - 1);\t/* count does not include NUL */\n}\n\n#endif\n"
  },
  {
    "path": "libcutils/trace-container.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/trace.h>\n\n#include \"trace-dev.inc\"\n\n#include <cutils/sockets.h>\n#include <sys/stat.h>\n#include <time.h>\n\n/**\n * For tracing in container, tags are written into a socket\n * instead of ftrace. Additional data is appended so we need extra space.\n */\n#define CONTAINER_ATRACE_MESSAGE_LENGTH (ATRACE_MESSAGE_LENGTH + 512)\n\nstatic pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;\n\n// Variables used for tracing in container with socket.\n// Note that we need to manually close and reopen socket when Zygote is forking. This requires\n// writing and closing sockets on multiple threads. A rwlock is used for avoiding concurrent\n// operation on the file descriptor.\nstatic bool             atrace_use_container_sock    = false;\nstatic int              atrace_container_sock_fd     = -1;\nstatic pthread_mutex_t  atrace_enabling_mutex        = PTHREAD_MUTEX_INITIALIZER;\nstatic pthread_rwlock_t atrace_container_sock_rwlock = PTHREAD_RWLOCK_INITIALIZER;\n\nstatic void atrace_seq_number_changed(uint32_t, uint32_t seq_no) {\n    pthread_once(&atrace_once_control, atrace_init_once);\n    atomic_store_explicit(&last_sequence_number, seq_no, memory_order_relaxed);\n}\n\nstatic bool atrace_init_container_sock()\n{\n    pthread_rwlock_wrlock(&atrace_container_sock_rwlock);\n    atrace_container_sock_fd =\n        socket_local_client(\"trace\", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET);\n    if (atrace_container_sock_fd < 0) {\n        ALOGE(\"Error opening container trace socket: %s (%d)\", strerror(errno), errno);\n    }\n    pthread_rwlock_unlock(&atrace_container_sock_rwlock);\n    return atrace_container_sock_fd != -1;\n}\n\nstatic void atrace_close_container_sock()\n{\n    pthread_rwlock_wrlock(&atrace_container_sock_rwlock);\n    if (atrace_container_sock_fd != -1) close(atrace_container_sock_fd);\n    atrace_container_sock_fd = -1;\n    pthread_rwlock_unlock(&atrace_container_sock_rwlock);\n}\n\n// Set whether tracing is enabled in this process.  This is used to prevent\n// the Zygote process from tracing.  We need to close the socket in the container when tracing is\n// disabled, and reopen it again after Zygote forking.\nvoid atrace_set_tracing_enabled(bool enabled)\n{\n    pthread_mutex_lock(&atrace_enabling_mutex);\n    if (atrace_use_container_sock) {\n        bool already_enabled = atomic_load_explicit(&atrace_is_enabled, memory_order_acquire);\n        if (enabled && !already_enabled) {\n            // Trace was disabled previously. Re-initialize container socket.\n            atrace_init_container_sock();\n        } else if (!enabled && already_enabled) {\n            // Trace was enabled previously. Close container socket.\n            atrace_close_container_sock();\n        }\n    }\n    atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);\n    pthread_mutex_unlock(&atrace_enabling_mutex);\n    atrace_update_tags();\n}\n\nstatic void atrace_init_once()\n{\n    atrace_marker_fd = open(\"/sys/kernel/tracing/trace_marker\", O_WRONLY | O_CLOEXEC);\n    if (atrace_marker_fd < 0) {\n        // try debugfs\n        atrace_marker_fd = open(\"/sys/kernel/debug/tracing/trace_marker\", O_WRONLY | O_CLOEXEC);\n        if (atrace_marker_fd < 0) {\n            // We're in container, ftrace may be disabled. In such case, we use the\n            // socket to write trace event.\n\n            // Protect the initialization of container socket from\n            // atrace_set_tracing_enabled.\n            pthread_mutex_lock(&atrace_enabling_mutex);\n            atrace_use_container_sock = true;\n            bool success = false;\n            if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {\n                success = atrace_init_container_sock();\n            }\n            pthread_mutex_unlock(&atrace_enabling_mutex);\n\n            if (!success) {\n                atrace_enabled_tags = 0;\n                goto done;\n            }\n        }\n    }\n    atrace_enabled_tags = atrace_get_property();\n\ndone:\n    atomic_store_explicit(&atrace_is_ready, true, memory_order_release);\n}\n\nvoid atrace_setup()\n{\n    pthread_once(&atrace_once_control, atrace_init_once);\n}\n\nstatic inline uint64_t gettime(clockid_t clk_id)\n{\n    struct timespec ts;\n    clock_gettime(clk_id, &ts);\n    return ts.tv_sec * 1000000 + ts.tv_nsec / 1000;\n}\n\n// Write trace events to container trace file. Note that we need to amend tid and time information\n// here comparing to normal ftrace, where those informations are added by kernel.\n#define WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, \\\n        track_name, name, value) { \\\n    char buf[CONTAINER_ATRACE_MESSAGE_LENGTH]; \\\n    const char* track_name_sep = track_name[0] != '\\0' ? \"|\" : \"\"; \\\n    int pid = getpid(); \\\n    int tid = gettid(); \\\n    uint64_t ts = gettime(CLOCK_MONOTONIC); \\\n    uint64_t tts = gettime(CLOCK_THREAD_CPUTIME_ID); \\\n    int len = snprintf( \\\n            buf, sizeof(buf), \\\n            ph \"|%d|%d|%\" PRIu64 \"|%\" PRIu64 sep_before_name \"%s%s%s\" value_format, \\\n            pid, tid, ts, tts, track_name, track_name_sep, name, value); \\\n    if (len >= (int) sizeof(buf)) { \\\n        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \\\n        /* Truncate the name to make the message fit. */ \\\n        if (name_len > 0) { \\\n            len = snprintf( \\\n                buf, sizeof(buf), \\\n                ph \"|%d|%d|%\" PRIu64 \"|%\" PRIu64 sep_before_name \"%s%s%.*s\" value_format, \\\n                pid, tid, ts, tts, track_name, track_name_sep, name_len, name, value); \\\n        } else { \\\n            int track_name_len = 0; \\\n            if (track_name[0] != '\\0') { \\\n                track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \\\n            } \\\n            if (track_name_len <= 0){ \\\n                /* Data is still too long. Drop it. */ \\\n                len = 0; \\\n            } else { \\\n                /* Truncate the trackName and name to make the message fit. */ \\\n                len = snprintf( \\\n                    buf, sizeof(buf), \\\n                    ph \"|%d|%d|%\" PRIu64 \"|%\" PRIu64 sep_before_name \"%.*s|%.1s\" value_format, \\\n                    pid, tid, ts, tts, track_name_len, track_name, name, value); \\\n            } \\\n        } \\\n    } \\\n    if (len > 0) { \\\n        write(atrace_container_sock_fd, buf, len); \\\n    } \\\n}\n\n#define WRITE_MSG_IN_CONTAINER(ph, sep_before_name, value_format, track_name, name, value) { \\\n    pthread_rwlock_rdlock(&atrace_container_sock_rwlock); \\\n    if (atrace_container_sock_fd != -1) { \\\n       WRITE_MSG_IN_CONTAINER_LOCKED(ph, sep_before_name, value_format, track_name, name, value); \\\n    } \\\n    pthread_rwlock_unlock(&atrace_container_sock_rwlock); \\\n}\n\nvoid atrace_begin_body(const char* name)\n{\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"B\", \"|\", \"%s\", \"\", name, \"\");\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"B|%d|\", \"%s\", \"\", name, \"\");\n}\n\nvoid atrace_end_body()\n{\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"E\", \"\", \"%s\", \"\", \"\", \"\");\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"E|%d\", \"%s\", \"\", \"\", \"\");\n}\n\nvoid atrace_async_begin_body(const char* name, int32_t cookie)\n{\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"S\", \"|\", \"|%d\", \"\", name, cookie);\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"S|%d|\", \"|%\" PRId32, \"\", name, cookie);\n}\n\nvoid atrace_async_end_body(const char* name, int32_t cookie)\n{\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"F\", \"|\", \"|%d\", \"\", name, cookie);\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"F|%d|\", \"|%\" PRId32, \"\", name, cookie);\n}\n\nvoid atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"G\", \"|\", \"|%d\", track_name, name, cookie);\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"G|%d|\", \"|%\" PRId32, track_name, name, cookie);\n}\n\nvoid atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"H\", \"|\", \"|%d\", \"\", track_name, cookie);\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"H|%d|\", \"|%\" PRId32, \"\", track_name, cookie);\n}\n\nvoid atrace_instant_body(const char* name) {\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"I\", \"|\", \"%s\", \"\", name, \"\");\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"I|%d|\", \"%s\", \"\", name, \"\");\n}\n\nvoid atrace_instant_for_track_body(const char* track_name, const char* name) {\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"N\", \"|\", \"%s\", track_name, name, \"\");\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"N|%d|\", \"%s\", track_name, name, \"\");\n}\n\nvoid atrace_int_body(const char* name, int32_t value)\n{\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"C\", \"|\", \"|%\" PRId32, \"\", name, value);\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"C|%d|\", \"|%\" PRId32, \"\", name, value);\n}\n\nvoid atrace_int64_body(const char* name, int64_t value)\n{\n    if (CC_LIKELY(atrace_use_container_sock)) {\n        WRITE_MSG_IN_CONTAINER(\"C\", \"|\", \"|%\" PRId64, \"\", name, value);\n        return;\n    }\n\n    if (atrace_marker_fd < 0) return;\n\n    WRITE_MSG(\"C|%d|\", \"|%\" PRId64, \"\", name, value);\n}\n"
  },
  {
    "path": "libcutils/trace-dev.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/trace.h>\n\n#include \"trace-dev.inc\"\n\nstatic pthread_once_t atrace_once_control = PTHREAD_ONCE_INIT;\n\n// Set whether tracing is enabled in this process.  This is used to prevent\n// the Zygote process from tracing.\nvoid atrace_set_tracing_enabled(bool enabled)\n{\n    atomic_store_explicit(&atrace_is_enabled, enabled, memory_order_release);\n    atrace_update_tags();\n}\n\nstatic void atrace_init_once()\n{\n    atrace_marker_fd = open(\"/sys/kernel/tracing/trace_marker\", O_WRONLY | O_CLOEXEC);\n    if (atrace_marker_fd == -1) {\n        atrace_marker_fd = open(\"/sys/kernel/debug/tracing/trace_marker\", O_WRONLY | O_CLOEXEC);\n    }\n\n    if (atrace_marker_fd == -1) {\n        ALOGE(\"Error opening trace file: %s (%d)\", strerror(errno), errno);\n        atrace_enabled_tags = 0;\n    } else {\n      atrace_enabled_tags = atrace_get_property();\n    }\n}\n\nstatic void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no) {\n    if (!atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {\n        return;\n    }\n\n    // Someone raced us.\n    if (!atomic_compare_exchange_strong(&last_sequence_number, &prev_seq_no, seq_no)) {\n        return;\n    }\n\n    if (CC_UNLIKELY(prev_seq_no == kSeqNoNotInit)) {\n#if defined(__BIONIC__)\n        const prop_info* new_pi = __system_property_find(\"debug.atrace.tags.enableflags\");\n        if (new_pi) atrace_property_info = new_pi;\n#endif\n        pthread_once(&atrace_once_control, atrace_init_once);\n    }\n\n    atrace_update_tags();\n}\n\nvoid atrace_setup()\n{\n    atrace_init();\n}\n\nvoid atrace_begin_body(const char* name)\n{\n    WRITE_MSG(\"B|%d|\", \"%s\", \"\", name, \"\");\n}\n\nvoid atrace_end_body()\n{\n    WRITE_MSG(\"E|%d\", \"%s\", \"\", \"\", \"\");\n}\n\nvoid atrace_async_begin_body(const char* name, int32_t cookie)\n{\n    WRITE_MSG(\"S|%d|\", \"|%\" PRId32, \"\", name, cookie);\n}\n\nvoid atrace_async_end_body(const char* name, int32_t cookie)\n{\n    WRITE_MSG(\"F|%d|\", \"|%\" PRId32, \"\", name, cookie);\n}\n\nvoid atrace_async_for_track_begin_body(const char* track_name, const char* name, int32_t cookie) {\n    WRITE_MSG(\"G|%d|\", \"|%\" PRId32, track_name, name, cookie);\n}\n\nvoid atrace_async_for_track_end_body(const char* track_name, int32_t cookie) {\n    WRITE_MSG(\"H|%d|\", \"|%\" PRId32, \"\", track_name, cookie);\n}\n\nvoid atrace_instant_body(const char* name) {\n    WRITE_MSG(\"I|%d|\", \"%s\", \"\", name, \"\");\n}\n\nvoid atrace_instant_for_track_body(const char* track_name, const char* name) {\n    WRITE_MSG(\"N|%d|\", \"%s\", track_name, name, \"\");\n}\n\nvoid atrace_int_body(const char* name, int32_t value)\n{\n    WRITE_MSG(\"C|%d|\", \"|%\" PRId32, \"\", name, value);\n}\n\nvoid atrace_int64_body(const char* name, int64_t value)\n{\n    WRITE_MSG(\"C|%d|\", \"|%\" PRId64, \"\", name, value);\n}\n"
  },
  {
    "path": "libcutils/trace-dev.inc",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __TRACE_DEV_INC\n#define __TRACE_DEV_INC\n\n#define LOG_TAG \"cutils-trace\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <fnmatch.h>\n#include <limits.h>\n#include <pthread.h>\n#include <stdatomic.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/types.h>\n\n#include <cutils/compiler.h>\n#include <cutils/properties.h>\n#include <cutils/trace.h>\n#include <log/log.h>\n#include <log/log_properties.h>\n\n#if defined(__BIONIC__)\n#include <sys/system_properties.h>\n#endif\n\n/**\n * Maximum size of a message that can be logged to the trace buffer.\n * Note this message includes a tag, the pid, and the string given as the name.\n * Names should be kept short to get the most use of the trace buffer.\n */\n#define ATRACE_MESSAGE_LENGTH 1024\n\nconstexpr uint32_t kSeqNoNotInit = static_cast<uint32_t>(-1);\n\natomic_bool              atrace_is_ready      = false;\nint                      atrace_marker_fd     = -1;\nuint64_t                 atrace_enabled_tags  = ATRACE_TAG_NOT_READY;\nstatic atomic_bool       atrace_is_enabled    = true;\nstatic pthread_mutex_t   atrace_tags_mutex    = PTHREAD_MUTEX_INITIALIZER;\n\n/**\n * Sequence number of debug.atrace.tags.enableflags the last time the enabled\n * tags were reloaded.\n **/\nstatic _Atomic(uint32_t) last_sequence_number = kSeqNoNotInit;\n\n#if defined(__BIONIC__)\n// All zero prop_info that has a sequence number of 0. This is easier than\n// depending on implementation details of the property implementation.\n//\n// prop_info is static_assert-ed to be 96 bytes, which cannot change due to\n// ABI compatibility.\nalignas(uint64_t) static char empty_pi[96];\nstatic const prop_info* atrace_property_info = reinterpret_cast<const prop_info*>(empty_pi);\n#endif\n\n/**\n * This is called when the sequence number of debug.atrace.tags.enableflags\n * changes and we need to reload the enabled tags.\n **/\nstatic void atrace_seq_number_changed(uint32_t prev_seq_no, uint32_t seq_no);\n\nvoid atrace_init() {\n#if defined(__BIONIC__)\n    uint32_t seq_no = __system_property_serial(atrace_property_info);  // Acquire semantics.\n#else\n    uint32_t seq_no = 0;\n#endif\n    uint32_t prev_seq_no = atomic_load_explicit(&last_sequence_number, memory_order_relaxed);\n    if (CC_UNLIKELY(seq_no != prev_seq_no)) {\n        atrace_seq_number_changed(prev_seq_no, seq_no);\n    }\n}\n\nuint64_t atrace_get_enabled_tags()\n{\n    atrace_init();\n    return atrace_enabled_tags;\n}\n\n// Check whether the given command line matches one of the comma-separated\n// values listed in the app_cmdlines property.\nstatic bool atrace_is_cmdline_match(const char* cmdline)\n{\n    int count = property_get_int32(\"debug.atrace.app_number\", 0);\n\n    char buf[PROPERTY_KEY_MAX];\n    char value[PROPERTY_VALUE_MAX];\n\n    for (int i = 0; i < count; i++) {\n        snprintf(buf, sizeof(buf), \"debug.atrace.app_%d\", i);\n        property_get(buf, value, \"\");\n        if (fnmatch(value, cmdline, FNM_NOESCAPE) == 0) {\n            return true;\n        }\n    }\n\n    return false;\n}\n\n// Determine whether application-level tracing is enabled for this process.\nstatic bool atrace_is_app_tracing_enabled()\n{\n    bool result = false;\n\n    // Check whether tracing is enabled for this process.\n    FILE * file = fopen(\"/proc/self/cmdline\", \"re\");\n    if (file) {\n        char cmdline[4096];\n        if (fgets(cmdline, sizeof(cmdline), file)) {\n            result = atrace_is_cmdline_match(cmdline);\n        } else {\n            ALOGE(\"Error reading cmdline: %s (%d)\", strerror(errno), errno);\n        }\n        fclose(file);\n    } else {\n        ALOGE(\"Error opening /proc/self/cmdline: %s (%d)\", strerror(errno),\n                errno);\n    }\n\n    return result;\n}\n\n// Read the sysprop and return the value tags should be set to\nstatic uint64_t atrace_get_property()\n{\n    char value[PROPERTY_VALUE_MAX];\n    char *endptr;\n    uint64_t tags;\n\n    property_get(\"debug.atrace.tags.enableflags\", value, \"0\");\n    errno = 0;\n    tags = strtoull(value, &endptr, 0);\n    if (value[0] == '\\0' || *endptr != '\\0') {\n        ALOGE(\"Error parsing trace property: Not a number: %s\", value);\n        return 0;\n    } else if (errno == ERANGE || tags == ULLONG_MAX) {\n        ALOGE(\"Error parsing trace property: Number too large: %s\", value);\n        return 0;\n    }\n\n    // Only set the \"app\" tag if this process was selected for app-level debug\n    // tracing.\n    if (atrace_is_app_tracing_enabled()) {\n        tags |= ATRACE_TAG_APP;\n    } else {\n        tags &= ~ATRACE_TAG_APP;\n    }\n\n    return (tags | ATRACE_TAG_ALWAYS) & ATRACE_TAG_VALID_MASK;\n}\n\n// Update tags if tracing is ready. Useful as a sysprop change callback.\nvoid atrace_update_tags()\n{\n    uint64_t tags;\n    if (atomic_load_explicit(&atrace_is_enabled, memory_order_acquire)) {\n        tags = atrace_get_property();\n        pthread_mutex_lock(&atrace_tags_mutex);\n        atrace_enabled_tags = tags;\n        pthread_mutex_unlock(&atrace_tags_mutex);\n    } else {\n        // Tracing is disabled for this process, so we simply don't\n        // initialize the tags.\n        pthread_mutex_lock(&atrace_tags_mutex);\n        atrace_enabled_tags = ATRACE_TAG_NOT_READY;\n        pthread_mutex_unlock(&atrace_tags_mutex);\n    }\n}\n\n#define WRITE_MSG(format_begin, format_end, track_name, name, value) { \\\n    char buf[ATRACE_MESSAGE_LENGTH] __attribute__((uninitialized));     \\\n    const char* track_name_sep = track_name[0] != '\\0' ? \"|\" : \"\"; \\\n    int pid = getpid(); \\\n    int len = snprintf(buf, sizeof(buf), format_begin \"%s%s%s\" format_end, pid, \\\n        track_name, track_name_sep, name, value); \\\n    if (len >= (int) sizeof(buf)) { \\\n        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \\\n        /* Truncate the name to make the message fit. */ \\\n        if (name_len > 0) { \\\n            len = snprintf(buf, sizeof(buf), format_begin \"%s%s%.*s\" format_end, pid, \\\n                track_name, track_name_sep, name_len, name, value); \\\n        } else { \\\n            int track_name_len = 0; \\\n            if (track_name[0] != '\\0') { \\\n                track_name_len = strlen(track_name) - (len - strlen(name) - sizeof(buf)) - 2; \\\n            } \\\n            if (track_name_len <= 0) { \\\n                /* Data is still too long. Drop it. */ \\\n                len = 0; \\\n            } else { \\\n                /* Truncate the trackName and name to make the message fit */ \\\n                len = snprintf(buf, sizeof(buf), format_begin \"%.*s|%.1s\" format_end, pid, \\\n                    track_name_len, track_name, name, value); \\\n            } \\\n        } \\\n    } \\\n    if (len > 0) { \\\n        write(atrace_marker_fd, buf, len); \\\n    } \\\n}\n\n#endif  // __TRACE_DEV_INC\n"
  },
  {
    "path": "libcutils/trace-dev_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <memory>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <gtest/gtest.h>\n\n#include \"../trace-dev.cpp\"\n\nclass TraceDevTest : public ::testing::Test {\n protected:\n  void SetUp() override {\n    lseek(tmp_file_.fd, 0, SEEK_SET);\n    atrace_marker_fd = tmp_file_.fd;\n  }\n\n  void TearDown() override {\n    atrace_marker_fd = -1;\n  }\n\n  TemporaryFile tmp_file_;\n\n  static std::string MakeName(size_t length) {\n    std::string name;\n    for (size_t i = 0; i < length; i++) {\n      name += '0' + (i % 10);\n    }\n    return name;\n  }\n};\n\nTEST_F(TraceDevTest, atrace_begin_body_normal) {\n  atrace_begin_body(\"fake_name\");\n\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  std::string expected = android::base::StringPrintf(\"B|%d|fake_name\", getpid());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_begin_body_exact) {\n  std::string expected = android::base::StringPrintf(\"B|%d|\", getpid());\n  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);\n  atrace_begin_body(name.c_str());\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  expected += name;\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n  // Add a single character and verify we get the exact same value as before.\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  name += '*';\n  atrace_begin_body(name.c_str());\n  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_begin_body_truncated) {\n  std::string expected = android::base::StringPrintf(\"B|%d|\", getpid());\n  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n  atrace_begin_body(name.c_str());\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;\n  expected += android::base::StringPrintf(\"%.*s\", expected_len, name.c_str());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_begin_body_normal) {\n  atrace_async_begin_body(\"fake_name\", 12345);\n\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  std::string expected = android::base::StringPrintf(\"S|%d|fake_name|12345\", getpid());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_begin_body_exact) {\n  std::string expected = android::base::StringPrintf(\"S|%d|\", getpid());\n  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);\n  atrace_async_begin_body(name.c_str(), 12345);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  expected += name + \"|12345\";\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n  // Add a single character and verify we get the exact same value as before.\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  name += '*';\n  atrace_async_begin_body(name.c_str(), 12345);\n  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_begin_body_truncated) {\n  std::string expected = android::base::StringPrintf(\"S|%d|\", getpid());\n  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n  atrace_async_begin_body(name.c_str(), 12345);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;\n  expected += android::base::StringPrintf(\"%.*s|12345\", expected_len, name.c_str());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_end_body_normal) {\n  atrace_async_end_body(\"fake_name\", 12345);\n\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  std::string expected = android::base::StringPrintf(\"F|%d|fake_name|12345\", getpid());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_end_body_exact) {\n  std::string expected = android::base::StringPrintf(\"F|%d|\", getpid());\n  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);\n  atrace_async_end_body(name.c_str(), 12345);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  expected += name + \"|12345\";\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n  // Add a single character and verify we get the exact same value as before.\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  name += '*';\n  atrace_async_end_body(name.c_str(), 12345);\n  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_end_body_truncated) {\n  std::string expected = android::base::StringPrintf(\"F|%d|\", getpid());\n  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n  atrace_async_end_body(name.c_str(), 12345);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;\n  expected += android::base::StringPrintf(\"%.*s|12345\", expected_len, name.c_str());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_begin_body_normal) {\n    atrace_async_for_track_begin_body(\"fake_track\", \"fake_name\", 12345);\n\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    std::string expected = android::base::StringPrintf(\"G|%d|fake_track|fake_name|12345\", getpid());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_track_name) {\n    const int name_size = 5;\n    std::string expected = android::base::StringPrintf(\"G|%d|\", getpid());\n    std::string track_name =\n            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size - 6);\n    atrace_async_for_track_begin_body(track_name.c_str(), \"name\", 12345);\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    expected += track_name + \"|name|12345\";\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n    // Add a single character and verify name truncation\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    track_name += '*';\n    expected = android::base::StringPrintf(\"G|%d|\", getpid());\n    expected += track_name + \"|nam|12345\";\n    atrace_async_for_track_begin_body(track_name.c_str(), \"name\", 12345);\n    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_track_name) {\n    std::string expected = android::base::StringPrintf(\"G|%d|\", getpid());\n    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_async_for_track_begin_body(track_name.c_str(), \"name\", 12345);\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 9;\n    expected += android::base::StringPrintf(\"%.*s|n|12345\", expected_len, track_name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_begin_body_exact_name) {\n    const int track_name_size = 11;\n    std::string expected = android::base::StringPrintf(\"G|%d|\", getpid());\n    std::string name =\n            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size - 6);\n    atrace_async_for_track_begin_body(\"track_name\", name.c_str(), 12345);\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    expected += \"track_name|\" + name + \"|12345\";\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n    // Add a single character and verify we get the same value as before.\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    name += '*';\n    atrace_async_for_track_begin_body(\"track_name\", name.c_str(), 12345);\n    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_name) {\n    std::string expected = android::base::StringPrintf(\"G|%d|track_name|\", getpid());\n    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_async_for_track_begin_body(\"track_name\", name.c_str(), 12345);\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1 - 6;\n    expected += android::base::StringPrintf(\"%.*s|12345\", expected_len, name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_begin_body_truncated_both) {\n    std::string expected = android::base::StringPrintf(\"G|%d|\", getpid());\n    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_async_for_track_begin_body(track_name.c_str(), name.c_str(), 12345);\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3 - 6;\n    expected += android::base::StringPrintf(\"%.*s|%.1s|12345\", expected_len, track_name.c_str(),\n                                            name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_end_body_normal) {\n    atrace_async_for_track_end_body(\"fake_track\", 12345);\n\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    std::string expected = android::base::StringPrintf(\"H|%d|fake_track|12345\", getpid());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_end_body_exact) {\n    std::string expected = android::base::StringPrintf(\"H|%d|\", getpid());\n    std::string track_name =\n            MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);\n    atrace_async_for_track_end_body(track_name.c_str(), 12345);\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    expected += track_name + \"|12345\";\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n    // Add a single character and verify we get the exact same value as before.\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    track_name += '*';\n    atrace_async_for_track_end_body(track_name.c_str(), 12345);\n    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_async_for_track_end_body_truncated) {\n    std::string expected = android::base::StringPrintf(\"H|%d|\", getpid());\n    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_async_for_track_end_body(track_name.c_str(), 12345);\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;\n    expected += android::base::StringPrintf(\"%.*s|12345\", expected_len, track_name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_body_normal) {\n    atrace_instant_body(\"fake_name\");\n\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    std::string expected = android::base::StringPrintf(\"I|%d|fake_name\", getpid());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_body_exact) {\n    std::string expected = android::base::StringPrintf(\"I|%d|\", getpid());\n    std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);\n    atrace_instant_body(name.c_str());\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    expected += name;\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n    // Add a single character and verify we get the exact same value as before.\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    name += '*';\n    atrace_instant_body(name.c_str());\n    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_body_truncated) {\n    std::string expected = android::base::StringPrintf(\"I|%d|\", getpid());\n    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_instant_body(name.c_str());\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;\n    expected += android::base::StringPrintf(\"%.*s\", expected_len, name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_for_track_body_normal) {\n    atrace_instant_for_track_body(\"fake_track\", \"fake_name\");\n\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    std::string expected = android::base::StringPrintf(\"N|%d|fake_track|fake_name\", getpid());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_for_track_body_exact_track_name) {\n    const int name_size = 5;\n    std::string expected = android::base::StringPrintf(\"N|%d|\", getpid());\n    std::string track_name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - name_size);\n    atrace_instant_for_track_body(track_name.c_str(), \"name\");\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    expected += track_name + \"|name\";\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n    // Add a single character and verify name truncation\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    track_name += '*';\n    expected = android::base::StringPrintf(\"N|%d|\", getpid());\n    expected += track_name + \"|nam\";\n    atrace_instant_for_track_body(track_name.c_str(), \"name\");\n    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_track_name) {\n    std::string expected = android::base::StringPrintf(\"N|%d|\", getpid());\n    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_instant_for_track_body(track_name.c_str(), \"name\");\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;\n    expected += android::base::StringPrintf(\"%.*s|n\", expected_len, track_name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_for_track_body_exact_name) {\n    const int track_name_size = 11;\n    std::string expected = android::base::StringPrintf(\"N|%d|\", getpid());\n    std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1 - track_name_size);\n    atrace_instant_for_track_body(\"track_name\", name.c_str());\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    expected += \"track_name|\" + name;\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n    // Add a single character and verify we get the same value as before.\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    name += '*';\n    atrace_instant_for_track_body(\"track_name\", name.c_str());\n    EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_name) {\n    std::string expected = android::base::StringPrintf(\"N|%d|track_name|\", getpid());\n    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_instant_for_track_body(\"track_name\", name.c_str());\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;\n    expected += android::base::StringPrintf(\"%.*s\", expected_len, name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_instant_for_track_body_truncated_both) {\n    std::string expected = android::base::StringPrintf(\"N|%d|\", getpid());\n    std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    std::string track_name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n    atrace_instant_for_track_body(track_name.c_str(), name.c_str());\n\n    ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n    ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n    std::string actual;\n    ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n    int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 3;\n    expected +=\n        android::base::StringPrintf(\"%.*s|%.1s\", expected_len, track_name.c_str(), name.c_str());\n    ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_int_body_normal) {\n  atrace_int_body(\"fake_name\", 12345);\n\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  std::string expected = android::base::StringPrintf(\"C|%d|fake_name|12345\", getpid());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_int_body_exact) {\n  std::string expected = android::base::StringPrintf(\"C|%d|\", getpid());\n  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);\n  atrace_int_body(name.c_str(), 12345);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  expected += name + \"|12345\";\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n  // Add a single character and verify we get the exact same value as before.\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  name += '*';\n  atrace_int_body(name.c_str(), 12345);\n  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_int_body_truncated) {\n  std::string expected = android::base::StringPrintf(\"C|%d|\", getpid());\n  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n  atrace_int_body(name.c_str(), 12345);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;\n  expected += android::base::StringPrintf(\"%.*s|12345\", expected_len, name.c_str());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_int64_body_normal) {\n  atrace_int64_body(\"fake_name\", 17179869183L);\n\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  std::string expected = android::base::StringPrintf(\"C|%d|fake_name|17179869183\", getpid());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_int64_body_exact) {\n  std::string expected = android::base::StringPrintf(\"C|%d|\", getpid());\n  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 13);\n  atrace_int64_body(name.c_str(), 17179869183L);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  expected += name + \"|17179869183\";\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n\n  // Add a single character and verify we get the exact same value as before.\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  name += '*';\n  atrace_int64_body(name.c_str(), 17179869183L);\n  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n\nTEST_F(TraceDevTest, atrace_int64_body_truncated) {\n  std::string expected = android::base::StringPrintf(\"C|%d|\", getpid());\n  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);\n  atrace_int64_body(name.c_str(), 17179869183L);\n\n  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));\n  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));\n\n  std::string actual;\n  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));\n  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 13;\n  expected += android::base::StringPrintf(\"%.*s|17179869183\", expected_len, name.c_str());\n  ASSERT_STREQ(expected.c_str(), actual.c_str());\n}\n"
  },
  {
    "path": "libcutils/trace-host.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/trace.h>\n\natomic_bool             atrace_is_ready      = true;\nint                     atrace_marker_fd     = -1;\nuint64_t                atrace_enabled_tags  = 0;\n\nvoid atrace_set_tracing_enabled(bool /*enabled*/) {}\nvoid atrace_update_tags() { }\nvoid atrace_setup() { }\nvoid atrace_begin_body(const char* /*name*/) {}\nvoid atrace_end_body() { }\nvoid atrace_async_begin_body(const char* /*name*/, int32_t /*cookie*/) {}\nvoid atrace_async_end_body(const char* /*name*/, int32_t /*cookie*/) {}\nvoid atrace_async_for_track_begin_body(const char* /*track_name*/, const char* /*name*/,\n                                       int32_t /*cookie*/) {}\nvoid atrace_async_for_track_end_body(const char* /*track_name*/, int32_t /*cookie*/) {}\nvoid atrace_instant_body(const char* /*name*/) {}\nvoid atrace_instant_for_track_body(const char* /*track_name*/, const char* /*name*/) {}\nvoid atrace_int_body(const char* /*name*/, int32_t /*value*/) {}\nvoid atrace_int64_body(const char* /*name*/, int64_t /*value*/) {}\nvoid atrace_init() {}\nuint64_t atrace_get_enabled_tags()\n{\n    return ATRACE_TAG_NOT_READY;\n}\n"
  },
  {
    "path": "libcutils/uevent.cpp",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cutils/uevent.h>\n\n#include <errno.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <string.h>\n#include <strings.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n\n#include <linux/netlink.h>\n\nextern \"C\" {\n\n/**\n * Like recv(), but checks that messages actually originate from the kernel.\n */\nssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {\n    uid_t uid = -1;\n    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);\n}\n\n/**\n * Like the above, but passes a uid_t in by pointer. In the event that this\n * fails due to a bad uid check, the uid_t will be set to the uid of the\n * socket's peer.\n *\n * If this method rejects a netlink message from outside the kernel, it\n * returns -1, sets errno to EIO, and sets \"user\" to the UID associated with the\n * message. If the peer UID cannot be determined, \"user\" is set to -1.\"\n */\nssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {\n    return uevent_kernel_recv(socket, buffer, length, true, uid);\n}\n\nssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {\n    struct iovec iov = {buffer, length};\n    struct sockaddr_nl addr;\n    char control[CMSG_SPACE(sizeof(struct ucred))];\n    struct msghdr hdr = {\n        &addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,\n    };\n    struct ucred* cred;\n\n    *uid = -1;\n    ssize_t n = TEMP_FAILURE_RETRY(recvmsg(socket, &hdr, 0));\n    if (n <= 0) {\n        return n;\n    }\n\n    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);\n    if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {\n        /* ignoring netlink message with no sender credentials */\n        goto out;\n    }\n\n    cred = (struct ucred*)CMSG_DATA(cmsg);\n    *uid = cred->uid;\n\n    if (addr.nl_pid != 0) {\n        /* ignore non-kernel */\n        goto out;\n    }\n    if (require_group && addr.nl_groups == 0) {\n        /* ignore unicast messages when requested */\n        goto out;\n    }\n\n    return n;\n\nout:\n    /* clear residual potentially malicious data */\n    bzero(buffer, length);\n    errno = EIO;\n    return -1;\n}\n\n/*\n * Creates an unbound netlink socket for receiving uevent messages.\n * @buf_sz: socket receive buffer size.\n * @passcred: whether or not to enable receiving the SCM_CREDENTIALS control\n *\tmessage.\n *\n * Returns: a socket descriptor upon success or -1 upon failure.\n */\nint uevent_create_socket(int buf_sz, bool passcred) {\n    int s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);\n    if (s < 0) {\n        return -1;\n    }\n\n    int buf_sz_readback = 0;\n    socklen_t optlen = sizeof(buf_sz_readback);\n\n    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||\n          getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {\n        close(s);\n        return -1;\n    }\n    /* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we\n     * want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in\n     * case we don't have CAP_NET_ADMIN. This is the case, for example, for\n     * healthd. */\n    if (buf_sz_readback < 2 * buf_sz) {\n        if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {\n            close(s);\n            return -1;\n        }\n    }\n\n    int on = passcred;\n\n    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));\n\n    return s;\n}\n\n/*\n * Binds a netlink socket. Binding a netlink socket makes the kernel start\n * sending netlink messages to that netlink socket.\n *\n * Returns: 0 upon success; -1 upon error.\n */\nint uevent_bind(int socket) {\n    struct sockaddr_nl addr = {\n            .nl_family = AF_NETLINK,\n            .nl_pid = 0,\n            .nl_groups = 0xffffffff,\n    };\n    return bind(socket, (struct sockaddr*)&addr, sizeof(addr));\n}\n\n/*\n * Creates a bound netlink socket for receiving uevent messages.\n * @buf_sz: socket receive buffer size.\n * @passcred: whether or not to enable receiving the SCM_CREDENTIALS control\n *\tmessage.\n *\n * Returns: a socket descriptor upon success or -1 upon failure.\n */\nint uevent_open_socket(int buf_sz, bool passcred) {\n    int s = uevent_create_socket(buf_sz, passcred);\n    if (s < 0) {\n        return -1;\n    }\n\n    if (uevent_bind(s) < 0) {\n        close(s);\n        return -1;\n    }\n\n    return s;\n}\n\n}  // extern \"C\"\n"
  },
  {
    "path": "libgrallocusage/Android.bp",
    "content": "// Copyright 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"system_core_libgrallocusage_license\"],\n}\n\n// Added automatically by a large-scale-change\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_libgrallocusage_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\ncc_library {\n    name: \"libgrallocusage\",\n    vendor_available: true,\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    cppflags: [\n        \"-Wextra\",\n    ],\n    srcs: [\"GrallocUsageConversion.cpp\"],\n    export_include_dirs: [\"include\"],\n    shared_libs: [\"android.hardware.graphics.allocator@2.0\"],\n    header_libs: [\"libhardware_headers\"],\n    min_sdk_version: \"29\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.media.swcodec\",\n        \"test_com.android.media.swcodec\",\n    ],\n}\n"
  },
  {
    "path": "libgrallocusage/GrallocUsageConversion.cpp",
    "content": "/*\n * Copyright 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <grallocusage/GrallocUsageConversion.h>\n\n#include <hardware/gralloc.h>\n#include <hardware/gralloc1.h>\n\nvoid android_convertGralloc0To1Usage(int32_t usage, uint64_t* producerUsage,\n                                     uint64_t* consumerUsage) {\n    constexpr uint64_t PRODUCER_MASK =\n        GRALLOC1_PRODUCER_USAGE_CPU_READ |\n        /* GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN | */\n        GRALLOC1_PRODUCER_USAGE_CPU_WRITE |\n        /* GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN | */\n        GRALLOC1_PRODUCER_USAGE_GPU_RENDER_TARGET | GRALLOC1_PRODUCER_USAGE_PROTECTED |\n        GRALLOC1_PRODUCER_USAGE_CAMERA | GRALLOC1_PRODUCER_USAGE_VIDEO_DECODER |\n        GRALLOC1_PRODUCER_USAGE_SENSOR_DIRECT_DATA;\n    constexpr uint64_t CONSUMER_MASK =\n        GRALLOC1_CONSUMER_USAGE_CPU_READ |\n        /* GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN | */\n        GRALLOC1_CONSUMER_USAGE_GPU_TEXTURE | GRALLOC1_CONSUMER_USAGE_HWCOMPOSER |\n        GRALLOC1_CONSUMER_USAGE_CLIENT_TARGET | GRALLOC1_CONSUMER_USAGE_CURSOR |\n        GRALLOC1_CONSUMER_USAGE_VIDEO_ENCODER | GRALLOC1_CONSUMER_USAGE_CAMERA |\n        GRALLOC1_CONSUMER_USAGE_RENDERSCRIPT | GRALLOC1_CONSUMER_USAGE_GPU_DATA_BUFFER;\n    *producerUsage = static_cast<uint64_t>(usage) & PRODUCER_MASK;\n    *consumerUsage = static_cast<uint64_t>(usage) & CONSUMER_MASK;\n    if ((static_cast<uint32_t>(usage) & GRALLOC_USAGE_SW_READ_OFTEN) == GRALLOC_USAGE_SW_READ_OFTEN) {\n        *producerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN;\n        *consumerUsage |= GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN;\n    }\n    if ((static_cast<uint32_t>(usage) & GRALLOC_USAGE_SW_WRITE_OFTEN) ==\n        GRALLOC_USAGE_SW_WRITE_OFTEN) {\n        *producerUsage |= GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN;\n    }\n}\n\nint32_t android_convertGralloc1To0Usage(uint64_t producerUsage, uint64_t consumerUsage) {\n    static_assert(uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) ==\n                      uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_READ_OFTEN),\n                  \"expected ConsumerUsage and ProducerUsage CPU_READ_OFTEN bits to match\");\n    uint64_t merged = producerUsage | consumerUsage;\n    if ((merged & (GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN)) ==\n        GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN) {\n        merged &= ~uint64_t(GRALLOC1_CONSUMER_USAGE_CPU_READ_OFTEN);\n        merged |= GRALLOC_USAGE_SW_READ_OFTEN;\n    }\n    if ((merged & (GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN)) ==\n        GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN) {\n        merged &= ~uint64_t(GRALLOC1_PRODUCER_USAGE_CPU_WRITE_OFTEN);\n        merged |= GRALLOC_USAGE_SW_WRITE_OFTEN;\n    }\n    return static_cast<int32_t>(merged);\n}\n"
  },
  {
    "path": "libgrallocusage/MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "libgrallocusage/NOTICE",
    "content": "\n   Copyright (c) 2005-2008, The Android Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "libgrallocusage/OWNERS",
    "content": "jreck@google.com\n"
  },
  {
    "path": "libgrallocusage/include/grallocusage/GrallocUsageConversion.h",
    "content": "/*\n * Copyright 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H\n#define ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H 1\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// Conversion functions are out-of-line so that users don't have to be exposed to\n// android/hardware/graphics/allocator/2.0/types.h and link against\n// android.hardware.graphics.allocator@2.0 to get that in their search path.\n\n// Convert a 32-bit gralloc0 usage mask to a producer/consumer pair of 64-bit usage masks as used\n// by android.hardware.graphics.allocator@2.0 (and gralloc1). This conversion properly handles the\n// mismatch between a.h.g.allocator@2.0's CPU_{READ,WRITE}_OFTEN and gralloc0's\n// SW_{READ,WRITE}_OFTEN.\nvoid android_convertGralloc0To1Usage(int32_t usage, uint64_t* producerUsage,\n                                     uint64_t* consumerUsage);\n\n// Convert a producer/consumer pair of 64-bit usage masks as used by\n// android.hardware.graphics.allocator@2.0 (and gralloc1) to a 32-bit gralloc0 usage mask. This\n// conversion properly handles the mismatch between a.h.g.allocator@2.0's CPU_{READ,WRITE}_OFTEN\n// and gralloc0's SW_{READ,WRITE}_OFTEN.\nint32_t android_convertGralloc1To0Usage(uint64_t producerUsage, uint64_t consumerUsage);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif  // ANDROID_GRALLOCUSAGE_GRALLOC_USAGE_CONVERSION_H\n"
  },
  {
    "path": "libkeyutils/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"system_core_libkeyutils_license\"],\n}\n\nlicense {\n    name: \"system_core_libkeyutils_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\"SPDX-license-identifier-BSD\"],\n    license_text: [\"NOTICE\"],\n}\n\ncc_library {\n    name: \"libkeyutils\",\n    cflags: [\"-Werror\"],\n    defaults: [\"linux_bionic_supported\"],\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    export_include_dirs: [\"include/\"],\n    local_include_dirs: [\"include/\"],\n    srcs: [\"keyutils.cpp\"],\n    stl: \"none\",\n}\n\ncc_test {\n    name: \"libkeyutils-tests\",\n    cflags: [\"-Werror\"],\n    shared_libs: [\"libkeyutils\"],\n    srcs: [\"keyutils_test.cpp\"],\n    test_suites: [\"device-tests\"],\n}\n"
  },
  {
    "path": "libkeyutils/NOTICE",
    "content": "Copyright (C) 2017 The Android Open Source Project\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions\nare met:\n * Redistributions of source code must retain the above copyright\n   notice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above copyright\n   notice, this list of conditions and the following disclaimer in\n   the documentation and/or other materials provided with the\n   distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\nFOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\nCOPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\nINCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\nOF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\nAND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\nOF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGE.\n"
  },
  {
    "path": "libkeyutils/include/keyutils.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#ifndef _KEYUTILS_H_\n#define _KEYUTILS_H_\n\n#include <linux/keyctl.h>\n#include <stdint.h>\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\ntypedef int32_t key_serial_t;\n\nkey_serial_t add_key(const char* type, const char* description, const void* payload,\n                     size_t payload_length, key_serial_t ring_id);\n\nkey_serial_t keyctl_get_keyring_ID(key_serial_t id, int create);\n\nlong keyctl_revoke(key_serial_t id); /* TODO: remove this */\n\nlong keyctl_search(key_serial_t ring_id, const char* type, const char* description,\n                   key_serial_t dest_ring_id);\n\nlong keyctl_setperm(key_serial_t id, int permissions);\n\nlong keyctl_unlink(key_serial_t key, key_serial_t keyring);\n\nlong keyctl_restrict_keyring(key_serial_t keyring, const char* type, const char* restriction);\n\nlong keyctl_get_security(key_serial_t key, char* buffer, size_t buflen);\n\n__END_DECLS\n\n#endif\n"
  },
  {
    "path": "libkeyutils/keyutils.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <keyutils.h>\n\n#include <stdarg.h>\n#include <sys/syscall.h>\n#include <unistd.h>\n\n// keyctl(2) is deliberately not exposed. Callers should use the typed APIs instead.\n\nkey_serial_t add_key(const char* type, const char* description, const void* payload,\n                     size_t payload_length, key_serial_t ring_id) {\n  return syscall(__NR_add_key, type, description, payload, payload_length, ring_id);\n}\n\nkey_serial_t keyctl_get_keyring_ID(key_serial_t id, int create) {\n  return syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID, id, create);\n}\n\nlong keyctl_revoke(key_serial_t id) {\n  return syscall(__NR_keyctl, KEYCTL_REVOKE, id);\n}\n\nlong keyctl_search(key_serial_t ring_id, const char* type, const char* description,\n                   key_serial_t dest_ring_id) {\n  return syscall(__NR_keyctl, KEYCTL_SEARCH, ring_id, type, description, dest_ring_id);\n}\n\nlong keyctl_setperm(key_serial_t id, int permissions) {\n  return syscall(__NR_keyctl, KEYCTL_SETPERM, id, permissions);\n}\n\nlong keyctl_unlink(key_serial_t key, key_serial_t keyring) {\n  return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring);\n}\n\nlong keyctl_restrict_keyring(key_serial_t keyring, const char* type, const char* restriction) {\n  return syscall(__NR_keyctl, KEYCTL_RESTRICT_KEYRING, keyring, type, restriction);\n}\n\nlong keyctl_get_security(key_serial_t id, char* buffer, size_t buflen) {\n  return syscall(__NR_keyctl, KEYCTL_GET_SECURITY, id, buffer, buflen);\n}\n"
  },
  {
    "path": "libkeyutils/keyutils_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *  * Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n *  * Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in\n *    the documentation and/or other materials provided with the\n *    distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS\n * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\n * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,\n * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS\n * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT\n * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <keyutils.h>\n\n#include <dlfcn.h>\n\n#include <gtest/gtest.h>\n\nTEST(keyutils, smoke) {\n  // Check that the exported type is the right size.\n  ASSERT_EQ(4U, sizeof(key_serial_t));\n\n  // Check that all the functions actually exist.\n  ASSERT_TRUE(dlsym(nullptr, \"add_key\") != nullptr);\n  ASSERT_TRUE(dlsym(nullptr, \"keyctl_get_keyring_ID\") != nullptr);\n  ASSERT_TRUE(dlsym(nullptr, \"keyctl_revoke\") != nullptr);\n  ASSERT_TRUE(dlsym(nullptr, \"keyctl_search\") != nullptr);\n  ASSERT_TRUE(dlsym(nullptr, \"keyctl_setperm\") != nullptr);\n  ASSERT_TRUE(dlsym(nullptr, \"keyctl_unlink\") != nullptr);\n}\n"
  },
  {
    "path": "libmodprobe/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libmodprobe\",\n    cflags: [\n        \"-Werror\",\n    ],\n    vendor_available: true,\n    ramdisk_available: true,\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    host_supported: true,\n    srcs: [\n        \"exthandler.cpp\",\n        \"libmodprobe.cpp\",\n        \"libmodprobe_ext.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    export_include_dirs: [\"include/\"],\n}\n\ncc_test {\n    name: \"libmodprobe_tests\",\n    cflags: [\"-Werror\"],\n    shared_libs: [\n        \"libbase\",\n    ],\n    local_include_dirs: [\"include/\"],\n    srcs: [\n        \"exthandler.cpp\",\n        \"libmodprobe_test.cpp\",\n        \"libmodprobe.cpp\",\n        \"libmodprobe_ext_test.cpp\",\n    ],\n    test_suites: [\"device-tests\"],\n}\n"
  },
  {
    "path": "libmodprobe/OWNERS",
    "content": "dvander@google.com\nwillmcvicker@google.com\n"
  },
  {
    "path": "libmodprobe/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"libmodprobe_tests\"\n    }\n  ],\n  \"hwasan-postsubmit\": [\n    {\n      \"name\": \"libmodprobe_tests\"\n    }\n  ]\n}\n"
  },
  {
    "path": "libmodprobe/exthandler.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <exthandler/exthandler.h>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <fnmatch.h>\n#include <grp.h>\n#include <pwd.h>\n#include <sys/wait.h>\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::ReadFdToString;\nusing android::base::Result;\nusing android::base::Split;\nusing android::base::Trim;\nusing android::base::unique_fd;\n\nResult<std::string> RunExternalHandler(const std::string& handler, uid_t uid, gid_t gid,\n                                       std::unordered_map<std::string, std::string>& envs_map) {\n    unique_fd child_stdout;\n    unique_fd parent_stdout;\n    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stdout, &parent_stdout)) {\n        return ErrnoError() << \"Socketpair() for stdout failed\";\n    }\n\n    unique_fd child_stderr;\n    unique_fd parent_stderr;\n    if (!Socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, &child_stderr, &parent_stderr)) {\n        return ErrnoError() << \"Socketpair() for stderr failed\";\n    }\n\n    signal(SIGCHLD, SIG_DFL);\n\n    auto pid = fork();\n    if (pid < 0) {\n        return ErrnoError() << \"fork() failed\";\n    }\n\n    if (pid == 0) {\n        for (auto it = envs_map.begin(); it != envs_map.end(); ++it) {\n            setenv(it->first.c_str(), it->second.c_str(), 1);\n        }\n        parent_stdout.reset();\n        parent_stderr.reset();\n        close(STDOUT_FILENO);\n        close(STDERR_FILENO);\n        dup2(child_stdout.get(), STDOUT_FILENO);\n        dup2(child_stderr.get(), STDERR_FILENO);\n\n        auto args = Split(handler, \" \");\n        std::vector<char*> c_args;\n        for (auto& arg : args) {\n            c_args.emplace_back(arg.data());\n        }\n        c_args.emplace_back(nullptr);\n\n        if (gid != 0) {\n            if (setgid(gid) != 0) {\n                fprintf(stderr, \"setgid() failed: %s\", strerror(errno));\n                _exit(EXIT_FAILURE);\n            }\n        }\n\n        if (setuid(uid) != 0) {\n            fprintf(stderr, \"setuid() failed: %s\", strerror(errno));\n            _exit(EXIT_FAILURE);\n        }\n\n        execv(c_args[0], c_args.data());\n        fprintf(stderr, \"exec() failed: %s\", strerror(errno));\n        _exit(EXIT_FAILURE);\n    }\n\n    child_stdout.reset();\n    child_stderr.reset();\n\n    int status;\n    pid_t waited_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));\n    if (waited_pid == -1) {\n        return ErrnoError() << \"waitpid() failed\";\n    }\n\n    std::string stdout_content;\n    if (!ReadFdToString(parent_stdout.get(), &stdout_content)) {\n        return ErrnoError() << \"ReadFdToString() for stdout failed\";\n    }\n\n    std::string stderr_content;\n    if (ReadFdToString(parent_stderr.get(), &stderr_content)) {\n        auto messages = Split(stderr_content, \"\\n\");\n        for (const auto& message : messages) {\n            if (!message.empty()) {\n                LOG(ERROR) << \"External Handler: \" << message;\n            }\n        }\n    } else {\n        LOG(ERROR) << \"ReadFdToString() for stderr failed\";\n    }\n\n    if (WIFEXITED(status)) {\n        if (WEXITSTATUS(status) == EXIT_SUCCESS) {\n            return Trim(stdout_content);\n        } else {\n            return Error() << \"exited with status \" << WEXITSTATUS(status);\n        }\n    } else if (WIFSIGNALED(status)) {\n        return Error() << \"killed by signal \" << WTERMSIG(status);\n    }\n\n    return Error() << \"unexpected exit status \" << status;\n}\n"
  },
  {
    "path": "libmodprobe/include/exthandler/exthandler.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <android-base/result.h>\n#include <string>\n#include <sys/types.h>\n\nandroid::base::Result<std::string> RunExternalHandler(\n        const std::string& handler, uid_t uid, gid_t gid,\n        std::unordered_map<std::string, std::string>& envs_map);\n"
  },
  {
    "path": "libmodprobe/include/modprobe/modprobe.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <mutex>\n#include <set>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <android-base/thread_annotations.h>\n\nclass Modprobe {\n  public:\n    Modprobe(const std::vector<std::string>&, const std::string load_file = \"modules.load\",\n             bool use_blocklist = true);\n\n    bool LoadModulesParallel(int num_threads);\n    bool LoadListedModules(bool strict = true);\n    bool LoadWithAliases(const std::string& module_name, bool strict,\n                         const std::string& parameters = \"\");\n    bool Remove(const std::string& module_name);\n    std::vector<std::string> ListModules(const std::string& pattern);\n    bool GetAllDependencies(const std::string& module, std::vector<std::string>* pre_dependencies,\n                            std::vector<std::string>* dependencies,\n                            std::vector<std::string>* post_dependencies);\n    void ResetModuleCount() { module_count_ = 0; }\n    int GetModuleCount() { return module_count_; }\n    bool IsBlocklisted(const std::string& module_name);\n\n  private:\n    std::string MakeCanonical(const std::string& module_path);\n    bool InsmodWithDeps(const std::string& module_name, const std::string& parameters);\n    bool Insmod(const std::string& path_name, const std::string& parameters);\n    bool Rmmod(const std::string& module_name);\n    std::vector<std::string> GetDependencies(const std::string& module);\n    bool ModuleExists(const std::string& module_name);\n    void AddOption(const std::string& module_name, const std::string& option_name,\n                   const std::string& value);\n    std::string GetKernelCmdline();\n\n    bool ParseDepCallback(const std::string& base_path, const std::vector<std::string>& args);\n    bool ParseAliasCallback(const std::vector<std::string>& args);\n    bool ParseSoftdepCallback(const std::vector<std::string>& args);\n    bool ParseLoadCallback(const std::vector<std::string>& args);\n    bool ParseOptionsCallback(const std::vector<std::string>& args);\n    bool ParseDynOptionsCallback(const std::vector<std::string>& args);\n    bool ParseBlocklistCallback(const std::vector<std::string>& args);\n    void ParseKernelCmdlineOptions();\n    void ParseCfg(const std::string& cfg, std::function<bool(const std::vector<std::string>&)> f);\n\n    std::vector<std::pair<std::string, std::string>> module_aliases_;\n    std::unordered_map<std::string, std::vector<std::string>> module_deps_;\n    std::vector<std::pair<std::string, std::string>> module_pre_softdep_;\n    std::vector<std::pair<std::string, std::string>> module_post_softdep_;\n    std::vector<std::string> module_load_;\n    std::unordered_map<std::string, std::string> module_options_;\n    std::set<std::string> module_blocklist_;\n    std::mutex module_loaded_lock_;\n    std::unordered_set<std::string> module_loaded_;\n    std::unordered_set<std::string> module_loaded_paths_;\n    int module_count_ = 0;\n    bool blocklist_enabled = false;\n};\n"
  },
  {
    "path": "libmodprobe/libmodprobe.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <modprobe/modprobe.h>\n\n#include <fnmatch.h>\n#include <grp.h>\n#include <pwd.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/wait.h>\n\n#include <algorithm>\n#include <map>\n#include <set>\n#include <string>\n#include <thread>\n#include <vector>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n\n#include \"exthandler/exthandler.h\"\n\nstd::string Modprobe::MakeCanonical(const std::string& module_path) {\n    auto start = module_path.find_last_of('/');\n    if (start == std::string::npos) {\n        start = 0;\n    } else {\n        start += 1;\n    }\n    auto end = module_path.size();\n    if (android::base::EndsWith(module_path, \".ko\")) {\n        end -= 3;\n    }\n    if ((end - start) <= 1) {\n        LOG(ERROR) << \"malformed module name: \" << module_path;\n        return \"\";\n    }\n    std::string module_name = module_path.substr(start, end - start);\n    // module names can have '-', but their file names will have '_'\n    std::replace(module_name.begin(), module_name.end(), '-', '_');\n    return module_name;\n}\n\nbool Modprobe::ParseDepCallback(const std::string& base_path,\n                                const std::vector<std::string>& args) {\n    std::vector<std::string> deps;\n    std::string prefix = \"\";\n\n    // Set first item as our modules path\n    std::string::size_type pos = args[0].find(':');\n    if (args[0][0] != '/') {\n        prefix = base_path + \"/\";\n    }\n    if (pos != std::string::npos) {\n        deps.emplace_back(prefix + args[0].substr(0, pos));\n    } else {\n        LOG(ERROR) << \"dependency lines must start with name followed by ':'\";\n        return false;\n    }\n\n    // Remaining items are dependencies of our module\n    for (auto arg = args.begin() + 1; arg != args.end(); ++arg) {\n        if ((*arg)[0] != '/') {\n            prefix = base_path + \"/\";\n        } else {\n            prefix = \"\";\n        }\n        deps.push_back(prefix + *arg);\n    }\n\n    std::string canonical_name = MakeCanonical(args[0].substr(0, pos));\n    if (canonical_name.empty()) {\n        return false;\n    }\n    this->module_deps_[canonical_name] = deps;\n\n    return true;\n}\n\nbool Modprobe::ParseAliasCallback(const std::vector<std::string>& args) {\n    auto it = args.begin();\n    const std::string& type = *it++;\n\n    if (type != \"alias\") {\n        LOG(ERROR) << \"non-alias line encountered in modules.alias, found \" << type;\n        return false;\n    }\n\n    if (args.size() != 3) {\n        LOG(ERROR) << \"alias lines in modules.alias must have 3 entries, not \" << args.size();\n        return false;\n    }\n\n    const std::string& alias = *it++;\n    const std::string& module_name = *it++;\n    this->module_aliases_.emplace_back(alias, module_name);\n\n    return true;\n}\n\nbool Modprobe::ParseSoftdepCallback(const std::vector<std::string>& args) {\n    auto it = args.begin();\n    const std::string& type = *it++;\n    std::string state = \"\";\n\n    if (type != \"softdep\") {\n        LOG(ERROR) << \"non-softdep line encountered in modules.softdep, found \" << type;\n        return false;\n    }\n\n    if (args.size() < 4) {\n        LOG(ERROR) << \"softdep lines in modules.softdep must have at least 4 entries\";\n        return false;\n    }\n\n    const std::string& module = *it++;\n    while (it != args.end()) {\n        const std::string& token = *it++;\n        if (token == \"pre:\" || token == \"post:\") {\n            state = token;\n            continue;\n        }\n        if (state == \"\") {\n            LOG(ERROR) << \"malformed modules.softdep at token \" << token;\n            return false;\n        }\n        if (state == \"pre:\") {\n            this->module_pre_softdep_.emplace_back(module, token);\n        } else {\n            this->module_post_softdep_.emplace_back(module, token);\n        }\n    }\n\n    return true;\n}\n\nbool Modprobe::ParseLoadCallback(const std::vector<std::string>& args) {\n    auto it = args.begin();\n    const std::string& module = *it++;\n\n    const std::string& canonical_name = MakeCanonical(module);\n    if (canonical_name.empty()) {\n        return false;\n    }\n    this->module_load_.emplace_back(canonical_name);\n\n    return true;\n}\n\nbool Modprobe::ParseOptionsCallback(const std::vector<std::string>& args) {\n    auto it = args.begin();\n    const std::string& type = *it++;\n\n    if (type == \"dyn_options\") {\n        return ParseDynOptionsCallback(std::vector<std::string>(it, args.end()));\n    }\n\n    if (type != \"options\") {\n        LOG(ERROR) << \"non-options line encountered in modules.options\";\n        return false;\n    }\n\n    if (args.size() < 2) {\n        LOG(ERROR) << \"lines in modules.options must have at least 2 entries, not \" << args.size();\n        return false;\n    }\n\n    const std::string& module = *it++;\n    std::string options = \"\";\n\n    const std::string& canonical_name = MakeCanonical(module);\n    if (canonical_name.empty()) {\n        return false;\n    }\n\n    while (it != args.end()) {\n        options += *it++;\n        if (it != args.end()) {\n            options += \" \";\n        }\n    }\n\n    auto [unused, inserted] = this->module_options_.emplace(canonical_name, options);\n    if (!inserted) {\n        LOG(ERROR) << \"multiple options lines present for module \" << module;\n        return false;\n    }\n    return true;\n}\n\nbool Modprobe::ParseDynOptionsCallback(const std::vector<std::string>& args) {\n    auto it = args.begin();\n    int arg_size = 3;\n\n    if (args.size() < arg_size) {\n        LOG(ERROR) << \"dyn_options lines in modules.options must have at least\" << arg_size\n                   << \" entries, not \" << args.size();\n        return false;\n    }\n\n    const std::string& module = *it++;\n\n    const std::string& canonical_name = MakeCanonical(module);\n    if (canonical_name.empty()) {\n        return false;\n    }\n\n    const std::string& pwnam = *it++;\n    passwd* pwd = getpwnam(pwnam.c_str());\n    if (!pwd) {\n        LOG(ERROR) << \"invalid handler uid'\" << pwnam << \"'\";\n        return false;\n    }\n\n    std::string handler_with_args =\n            android::base::Join(std::vector<std::string>(it, args.end()), ' ');\n    handler_with_args.erase(std::remove(handler_with_args.begin(), handler_with_args.end(), '\\\"'),\n                            handler_with_args.end());\n\n    LOG(DEBUG) << \"Launching external module options handler: '\" << handler_with_args\n               << \" for module: \" << module;\n\n    // There is no need to set envs for external module options handler - pass\n    // empty map.\n    std::unordered_map<std::string, std::string> envs_map;\n    auto result = RunExternalHandler(handler_with_args, pwd->pw_uid, 0, envs_map);\n    if (!result.ok()) {\n        LOG(ERROR) << \"External module handler failed: \" << result.error();\n        return false;\n    }\n\n    LOG(INFO) << \"Dynamic options for module: \" << module << \" are '\" << *result << \"'\";\n\n    auto [unused, inserted] = this->module_options_.emplace(canonical_name, *result);\n    if (!inserted) {\n        LOG(ERROR) << \"multiple options lines present for module \" << module;\n        return false;\n    }\n    return true;\n}\n\nbool Modprobe::ParseBlocklistCallback(const std::vector<std::string>& args) {\n    auto it = args.begin();\n    const std::string& type = *it++;\n\n    if (type != \"blocklist\") {\n        LOG(ERROR) << \"non-blocklist line encountered in modules.blocklist\";\n        return false;\n    }\n\n    if (args.size() != 2) {\n        LOG(ERROR) << \"lines in modules.blocklist must have exactly 2 entries, not \" << args.size();\n        return false;\n    }\n\n    const std::string& module = *it++;\n\n    const std::string& canonical_name = MakeCanonical(module);\n    if (canonical_name.empty()) {\n        return false;\n    }\n    this->module_blocklist_.emplace(canonical_name);\n\n    return true;\n}\n\nvoid Modprobe::ParseCfg(const std::string& cfg,\n                        std::function<bool(const std::vector<std::string>&)> f) {\n    std::string cfg_contents;\n    if (!android::base::ReadFileToString(cfg, &cfg_contents, false)) {\n        return;\n    }\n\n    std::vector<std::string> lines = android::base::Split(cfg_contents, \"\\n\");\n    for (const auto& line : lines) {\n        if (line.empty() || line[0] == '#') {\n            continue;\n        }\n        const std::vector<std::string> args = android::base::Split(line, \" \");\n        if (args.empty()) continue;\n        f(args);\n    }\n    return;\n}\n\nvoid Modprobe::AddOption(const std::string& module_name, const std::string& option_name,\n                         const std::string& value) {\n    auto canonical_name = MakeCanonical(module_name);\n    auto options_iter = module_options_.find(canonical_name);\n    auto option_str = option_name + \"=\" + value;\n    if (options_iter != module_options_.end()) {\n        options_iter->second = options_iter->second + \" \" + option_str;\n    } else {\n        module_options_.emplace(canonical_name, option_str);\n    }\n}\n\nvoid Modprobe::ParseKernelCmdlineOptions(void) {\n    std::string cmdline = GetKernelCmdline();\n    std::string module_name = \"\";\n    std::string option_name = \"\";\n    std::string value = \"\";\n    bool in_module = true;\n    bool in_option = false;\n    bool in_value = false;\n    bool in_quotes = false;\n    int start = 0;\n\n    for (int i = 0; i < cmdline.size(); i++) {\n        if (cmdline[i] == '\"') {\n            in_quotes = !in_quotes;\n        }\n\n        if (in_quotes) continue;\n\n        if (cmdline[i] == ' ') {\n            if (in_value) {\n                value = cmdline.substr(start, i - start);\n                if (!module_name.empty() && !option_name.empty()) {\n                    AddOption(module_name, option_name, value);\n                }\n            }\n            module_name = \"\";\n            option_name = \"\";\n            value = \"\";\n            in_value = false;\n            start = i + 1;\n            in_module = true;\n            continue;\n        }\n\n        if (cmdline[i] == '.') {\n            if (in_module) {\n                module_name = cmdline.substr(start, i - start);\n                start = i + 1;\n                in_module = false;\n            }\n            in_option = true;\n            continue;\n        }\n\n        if (cmdline[i] == '=') {\n            if (in_option) {\n                option_name = cmdline.substr(start, i - start);\n                start = i + 1;\n                in_option = false;\n            }\n            in_value = true;\n            continue;\n        }\n    }\n    if (in_value && !in_quotes) {\n        value = cmdline.substr(start, cmdline.size() - start);\n        if (!module_name.empty() && !option_name.empty()) {\n            AddOption(module_name, option_name, value);\n        }\n    }\n}\n\nModprobe::Modprobe(const std::vector<std::string>& base_paths, const std::string load_file,\n                   bool use_blocklist)\n    : blocklist_enabled(use_blocklist) {\n    using namespace std::placeholders;\n\n    for (const auto& base_path : base_paths) {\n        auto alias_callback = std::bind(&Modprobe::ParseAliasCallback, this, _1);\n        ParseCfg(base_path + \"/modules.alias\", alias_callback);\n\n        auto dep_callback = std::bind(&Modprobe::ParseDepCallback, this, base_path, _1);\n        ParseCfg(base_path + \"/modules.dep\", dep_callback);\n\n        auto softdep_callback = std::bind(&Modprobe::ParseSoftdepCallback, this, _1);\n        ParseCfg(base_path + \"/modules.softdep\", softdep_callback);\n\n        auto load_callback = std::bind(&Modprobe::ParseLoadCallback, this, _1);\n        ParseCfg(base_path + \"/\" + load_file, load_callback);\n\n        auto options_callback = std::bind(&Modprobe::ParseOptionsCallback, this, _1);\n        ParseCfg(base_path + \"/modules.options\", options_callback);\n\n        auto blocklist_callback = std::bind(&Modprobe::ParseBlocklistCallback, this, _1);\n        ParseCfg(base_path + \"/modules.blocklist\", blocklist_callback);\n    }\n\n    ParseKernelCmdlineOptions();\n}\n\nstd::vector<std::string> Modprobe::GetDependencies(const std::string& module) {\n    auto it = module_deps_.find(module);\n    if (it == module_deps_.end()) {\n        return {};\n    }\n    return it->second;\n}\n\nbool Modprobe::InsmodWithDeps(const std::string& module_name, const std::string& parameters) {\n    if (module_name.empty()) {\n        LOG(ERROR) << \"Need valid module name, given: \" << module_name;\n        return false;\n    }\n\n    auto dependencies = GetDependencies(module_name);\n    if (dependencies.empty()) {\n        LOG(ERROR) << \"Module \" << module_name << \" not in dependency file\";\n        return false;\n    }\n\n    // load module dependencies in reverse order\n    for (auto dep = dependencies.rbegin(); dep != dependencies.rend() - 1; ++dep) {\n        LOG(VERBOSE) << \"Loading hard dep for '\" << module_name << \"': \" << *dep;\n        if (!LoadWithAliases(*dep, true)) {\n            return false;\n        }\n    }\n\n    // try to load soft pre-dependencies\n    for (const auto& [module, softdep] : module_pre_softdep_) {\n        if (module_name == module) {\n            LOG(VERBOSE) << \"Loading soft pre-dep for '\" << module << \"': \" << softdep;\n            LoadWithAliases(softdep, false);\n        }\n    }\n\n    // load target module itself with args\n    if (!Insmod(dependencies[0], parameters)) {\n        return false;\n    }\n\n    // try to load soft post-dependencies\n    for (const auto& [module, softdep] : module_post_softdep_) {\n        if (module_name == module) {\n            LOG(VERBOSE) << \"Loading soft post-dep for '\" << module << \"': \" << softdep;\n            LoadWithAliases(softdep, false);\n        }\n    }\n\n    return true;\n}\n\nbool Modprobe::LoadWithAliases(const std::string& module_name, bool strict,\n                               const std::string& parameters) {\n    auto canonical_name = MakeCanonical(module_name);\n    if (module_loaded_.count(canonical_name)) {\n        return true;\n    }\n\n    std::set<std::string> modules_to_load = {canonical_name};\n    bool module_loaded = false;\n\n    // use aliases to expand list of modules to load (multiple modules\n    // may alias themselves to the requested name)\n    for (const auto& [alias, aliased_module] : module_aliases_) {\n        if (fnmatch(alias.c_str(), module_name.c_str(), 0) != 0) continue;\n        LOG(VERBOSE) << \"Found alias for '\" << module_name << \"': '\" << aliased_module;\n        if (module_loaded_.count(MakeCanonical(aliased_module))) continue;\n        modules_to_load.emplace(aliased_module);\n    }\n\n    // attempt to load all modules aliased to this name\n    for (const auto& module : modules_to_load) {\n        if (!ModuleExists(module)) continue;\n        if (InsmodWithDeps(module, parameters)) module_loaded = true;\n    }\n\n    if (strict && !module_loaded) {\n        LOG(ERROR) << \"LoadWithAliases was unable to load \" << module_name\n                   << \", tried: \" << android::base::Join(modules_to_load, \", \");\n        return false;\n    }\n    return true;\n}\n\nbool Modprobe::IsBlocklisted(const std::string& module_name) {\n    if (!blocklist_enabled) return false;\n\n    auto canonical_name = MakeCanonical(module_name);\n    auto dependencies = GetDependencies(canonical_name);\n    for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {\n        if (module_blocklist_.count(MakeCanonical(*dep))) return true;\n    }\n\n    return module_blocklist_.count(canonical_name) > 0;\n}\n\n// Another option to load kernel modules. load independent modules dependencies\n// in parallel and then update dependency list of other remaining modules,\n// repeat these steps until all modules are loaded.\n// Discard all blocklist.\n// Softdeps are taken care in InsmodWithDeps().\nbool Modprobe::LoadModulesParallel(int num_threads) {\n    bool ret = true;\n    std::map<std::string, std::vector<std::string>> mod_with_deps;\n\n    // Get dependencies\n    for (const auto& module : module_load_) {\n        // Skip blocklist modules\n        if (IsBlocklisted(module)) {\n            LOG(VERBOSE) << \"LMP: Blocklist: Module \" << module << \" skipping...\";\n            continue;\n        }\n        auto dependencies = GetDependencies(MakeCanonical(module));\n        if (dependencies.empty()) {\n            LOG(ERROR) << \"LMP: Hard-dep: Module \" << module\n                       << \" not in .dep file\";\n            return false;\n        }\n        mod_with_deps[MakeCanonical(module)] = dependencies;\n    }\n\n    while (!mod_with_deps.empty()) {\n        std::vector<std::thread> threads;\n        std::vector<std::string> mods_path_to_load;\n        std::mutex vector_lock;\n\n        // Find independent modules\n        for (const auto& [it_mod, it_dep] : mod_with_deps) {\n            auto itd_last = it_dep.rbegin();\n            if (itd_last == it_dep.rend())\n                continue;\n\n            auto cnd_last = MakeCanonical(*itd_last);\n            // Hard-dependencies cannot be blocklisted\n            if (IsBlocklisted(cnd_last)) {\n                LOG(ERROR) << \"LMP: Blocklist: Module-dep \" << cnd_last\n                           << \" : failed to load module \" << it_mod;\n                return false;\n            }\n\n            std::string str = \"load_sequential=1\";\n            auto it = module_options_[cnd_last].find(str);\n            if (it != std::string::npos) {\n                module_options_[cnd_last].erase(it, it + str.size());\n\n                if (!LoadWithAliases(cnd_last, true)) {\n                    return false;\n                }\n            } else {\n                if (std::find(mods_path_to_load.begin(), mods_path_to_load.end(),\n                            cnd_last) == mods_path_to_load.end()) {\n                    mods_path_to_load.emplace_back(cnd_last);\n                }\n            }\n        }\n\n        // Load independent modules in parallel\n        auto thread_function = [&] {\n            std::unique_lock lk(vector_lock);\n            while (!mods_path_to_load.empty()) {\n                auto ret_load = true;\n                auto mod_to_load = std::move(mods_path_to_load.back());\n                mods_path_to_load.pop_back();\n\n                lk.unlock();\n                ret_load &= LoadWithAliases(mod_to_load, true);\n                lk.lock();\n                if (!ret_load) {\n                    ret &= ret_load;\n                }\n            }\n        };\n\n        std::generate_n(std::back_inserter(threads), num_threads,\n                        [&] { return std::thread(thread_function); });\n\n        // Wait for the threads.\n        for (auto& thread : threads) {\n            thread.join();\n        }\n\n        if (!ret) return ret;\n\n        std::lock_guard guard(module_loaded_lock_);\n        // Remove loaded module form mod_with_deps and soft dependencies of other modules\n        for (const auto& module_loaded : module_loaded_)\n            mod_with_deps.erase(module_loaded);\n\n        // Remove loaded module form dependencies of other modules which are not loaded yet\n        for (const auto& module_loaded_path : module_loaded_paths_) {\n            for (auto& [mod, deps] : mod_with_deps) {\n                auto it = std::find(deps.begin(), deps.end(), module_loaded_path);\n                if (it != deps.end()) {\n                    deps.erase(it);\n                }\n            }\n        }\n    }\n\n    return ret;\n}\n\nbool Modprobe::LoadListedModules(bool strict) {\n    auto ret = true;\n    for (const auto& module : module_load_) {\n        if (!LoadWithAliases(module, true)) {\n            if (IsBlocklisted(module)) continue;\n            ret = false;\n            if (strict) break;\n        }\n    }\n    return ret;\n}\n\nbool Modprobe::Remove(const std::string& module_name) {\n    auto dependencies = GetDependencies(MakeCanonical(module_name));\n    for (auto dep = dependencies.begin(); dep != dependencies.end(); ++dep) {\n        Rmmod(*dep);\n    }\n    Rmmod(module_name);\n    return true;\n}\n\nstd::vector<std::string> Modprobe::ListModules(const std::string& pattern) {\n    std::vector<std::string> rv;\n    for (const auto& [module, deps] : module_deps_) {\n        // Attempt to match both the canonical module name and the module filename.\n        if (!fnmatch(pattern.c_str(), module.c_str(), 0)) {\n            rv.emplace_back(module);\n        } else if (!fnmatch(pattern.c_str(), android::base::Basename(deps[0]).c_str(), 0)) {\n            rv.emplace_back(deps[0]);\n        }\n    }\n    return rv;\n}\n\nbool Modprobe::GetAllDependencies(const std::string& module,\n                                  std::vector<std::string>* pre_dependencies,\n                                  std::vector<std::string>* dependencies,\n                                  std::vector<std::string>* post_dependencies) {\n    std::string canonical_name = MakeCanonical(module);\n    if (pre_dependencies) {\n        pre_dependencies->clear();\n        for (const auto& [it_module, it_softdep] : module_pre_softdep_) {\n            if (canonical_name == it_module) {\n                pre_dependencies->emplace_back(it_softdep);\n            }\n        }\n    }\n    if (dependencies) {\n        dependencies->clear();\n        auto hard_deps = GetDependencies(canonical_name);\n        if (hard_deps.empty()) {\n            return false;\n        }\n        for (auto dep = hard_deps.rbegin(); dep != hard_deps.rend(); dep++) {\n            dependencies->emplace_back(*dep);\n        }\n    }\n    if (post_dependencies) {\n        for (const auto& [it_module, it_softdep] : module_post_softdep_) {\n            if (canonical_name == it_module) {\n                post_dependencies->emplace_back(it_softdep);\n            }\n        }\n    }\n    return true;\n}\n"
  },
  {
    "path": "libmodprobe/libmodprobe_ext.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/stat.h>\n#include <sys/syscall.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n\n#include <modprobe/modprobe.h>\n\nstd::string Modprobe::GetKernelCmdline(void) {\n    std::string cmdline;\n    if (!android::base::ReadFileToString(\"/proc/cmdline\", &cmdline)) {\n        return \"\";\n    }\n    return cmdline;\n}\n\nbool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {\n    android::base::unique_fd fd(\n            TEMP_FAILURE_RETRY(open(path_name.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));\n    if (fd == -1) {\n        PLOG(ERROR) << \"Could not open module '\" << path_name << \"'\";\n        return false;\n    }\n\n    auto canonical_name = MakeCanonical(path_name);\n    std::string options = \"\";\n    auto options_iter = module_options_.find(canonical_name);\n    if (options_iter != module_options_.end()) {\n        options = options_iter->second;\n    }\n    if (!parameters.empty()) {\n        options = options + \" \" + parameters;\n    }\n\n    LOG(INFO) << \"Loading module \" << path_name << \" with args '\" << options << \"'\";\n    int ret = syscall(__NR_finit_module, fd.get(), options.c_str(), 0);\n    if (ret != 0) {\n        if (errno == EEXIST) {\n            // Module already loaded\n            std::lock_guard guard(module_loaded_lock_);\n            module_loaded_paths_.emplace(path_name);\n            module_loaded_.emplace(canonical_name);\n            return true;\n        }\n        PLOG(ERROR) << \"Failed to insmod '\" << path_name << \"' with args '\" << options << \"'\";\n        return false;\n    }\n\n    LOG(INFO) << \"Loaded kernel module \" << path_name;\n    std::lock_guard guard(module_loaded_lock_);\n    module_loaded_paths_.emplace(path_name);\n    module_loaded_.emplace(canonical_name);\n    module_count_++;\n    return true;\n}\n\nbool Modprobe::Rmmod(const std::string& module_name) {\n    auto canonical_name = MakeCanonical(module_name);\n    int ret = syscall(__NR_delete_module, canonical_name.c_str(), O_NONBLOCK);\n    if (ret != 0) {\n        PLOG(ERROR) << \"Failed to remove module '\" << module_name << \"'\";\n        return false;\n    }\n    std::lock_guard guard(module_loaded_lock_);\n    module_loaded_.erase(canonical_name);\n    return true;\n}\n\nbool Modprobe::ModuleExists(const std::string& module_name) {\n    struct stat fileStat {};\n    if (blocklist_enabled && module_blocklist_.count(module_name)) {\n        LOG(INFO) << \"module \" << module_name << \" is blocklisted\";\n        return false;\n    }\n    auto deps = GetDependencies(module_name);\n    if (deps.empty()) {\n        // missing deps can happen in the case of an alias\n        return false;\n    }\n    if (stat(deps.front().c_str(), &fileStat)) {\n        PLOG(INFO) << \"module \" << module_name << \" can't be loaded; can't access \" << deps.front();\n        return false;\n    }\n    if (!S_ISREG(fileStat.st_mode)) {\n        LOG(INFO) << \"module \" << module_name << \" is not a regular file\";\n        return false;\n    }\n    return true;\n}\n"
  },
  {
    "path": "libmodprobe/libmodprobe_ext_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/stat.h>\n#include <sys/syscall.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n\n#include <modprobe/modprobe.h>\n\n#include \"libmodprobe_test.h\"\n\nstd::string Modprobe::GetKernelCmdline(void) {\n    return kernel_cmdline;\n}\n\nbool Modprobe::Insmod(const std::string& path_name, const std::string& parameters) {\n    auto deps = GetDependencies(MakeCanonical(path_name));\n    if (deps.empty()) {\n        return false;\n    }\n    if (std::find(test_modules.begin(), test_modules.end(), deps.front()) == test_modules.end()) {\n        return false;\n    }\n    for (auto it = modules_loaded.begin(); it != modules_loaded.end(); ++it) {\n        if (android::base::StartsWith(*it, path_name)) {\n            return true;\n        }\n    }\n    std::string options;\n    auto options_iter = module_options_.find(MakeCanonical(path_name));\n    if (options_iter != module_options_.end()) {\n        options = \" \" + options_iter->second;\n    }\n    if (!parameters.empty()) {\n        options = options + \" \" + parameters;\n    }\n\n    modules_loaded.emplace_back(path_name + options);\n    module_count_++;\n    return true;\n}\n\nbool Modprobe::Rmmod(const std::string& module_name) {\n    for (auto it = modules_loaded.begin(); it != modules_loaded.end(); it++) {\n        if (*it == module_name || android::base::StartsWith(*it, module_name + \" \")) {\n            modules_loaded.erase(it);\n            return true;\n        }\n    }\n    return false;\n}\n\nbool Modprobe::ModuleExists(const std::string& module_name) {\n    auto deps = GetDependencies(module_name);\n    if (blocklist_enabled && module_blocklist_.count(module_name)) {\n        return false;\n    }\n    if (deps.empty()) {\n        // missing deps can happen in the case of an alias\n        return false;\n    }\n    return std::find(test_modules.begin(), test_modules.end(), deps.front()) != test_modules.end();\n}\n"
  },
  {
    "path": "libmodprobe/libmodprobe_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <functional>\n\n#include <android-base/file.h>\n#include <android-base/macros.h>\n#include <android-base/unique_fd.h>\n#include <gtest/gtest.h>\n\n#include <modprobe/modprobe.h>\n\n#include \"libmodprobe_test.h\"\n\n// Used by libmodprobe_ext_test to check if requested modules are present.\nstd::vector<std::string> test_modules;\n\n// Used by libmodprobe_ext_test to report which modules would have been loaded.\nstd::vector<std::string> modules_loaded;\n\n// Used by libmodprobe_ext_test to fake a kernel commandline\nstd::string kernel_cmdline;\n\nTEST(libmodprobe, Test) {\n    kernel_cmdline =\n            \"flag1 flag2 test1.option1=50 test4.option3=\\\"set x\\\" test1.option2=60 \"\n            \"test8. test5.option1= test10.option1=1\";\n    test_modules = {\n            \"/test1.ko\",  \"/test2.ko\",  \"/test3.ko\",  \"/test4.ko\",  \"/test5.ko\",\n            \"/test6.ko\",  \"/test7.ko\",  \"/test8.ko\",  \"/test9.ko\",  \"/test10.ko\",\n            \"/test11.ko\", \"/test12.ko\", \"/test13.ko\", \"/test14.ko\", \"/test15.ko\",\n    };\n\n    std::vector<std::string> expected_modules_loaded = {\n            \"/test14.ko\",\n            \"/test15.ko\",\n            \"/test3.ko\",\n            \"/test4.ko option3=\\\"set x\\\"\",\n            \"/test1.ko option1=50 option2=60\",\n            \"/test6.ko\",\n            \"/test2.ko\",\n            \"/test5.ko option1=\",\n            \"/test8.ko\",\n            \"/test7.ko param1=4\",\n            \"/test9.ko param_x=1 param_y=2 param_z=3\",\n            \"/test10.ko option1=1\",\n            \"/test12.ko\",\n            \"/test11.ko\",\n            \"/test13.ko\",\n    };\n\n    std::vector<std::string> expected_after_remove = {\n            \"/test14.ko\",\n            \"/test15.ko\",\n            \"/test1.ko option1=50 option2=60\",\n            \"/test6.ko\",\n            \"/test2.ko\",\n            \"/test5.ko option1=\",\n            \"/test8.ko\",\n            \"/test7.ko param1=4\",\n            \"/test9.ko param_x=1 param_y=2 param_z=3\",\n            \"/test10.ko option1=1\",\n            \"/test12.ko\",\n            \"/test11.ko\",\n            \"/test13.ko\",\n    };\n\n    std::vector<std::string> expected_modules_blocklist_enabled = {\n            \"/test1.ko option1=50 option2=60\",\n            \"/test6.ko\",\n            \"/test2.ko\",\n            \"/test5.ko option1=\",\n            \"/test8.ko\",\n            \"/test7.ko param1=4\",\n            \"/test12.ko\",\n            \"/test11.ko\",\n            \"/test13.ko\",\n    };\n\n    const std::string modules_dep =\n            \"test1.ko:\\n\"\n            \"test2.ko:\\n\"\n            \"test3.ko:\\n\"\n            \"test4.ko: test3.ko\\n\"\n            \"test5.ko: test2.ko test6.ko\\n\"\n            \"test6.ko:\\n\"\n            \"test7.ko:\\n\"\n            \"test8.ko:\\n\"\n            \"test9.ko:\\n\"\n            \"test10.ko:\\n\"\n            \"test11.ko:\\n\"\n            \"test12.ko:\\n\"\n            \"test13.ko:\\n\"\n            \"test14.ko:\\n\"\n            \"test15.ko:\\n\";\n\n    const std::string modules_softdep =\n            \"softdep test7 pre: test8\\n\"\n            \"softdep test9 post: test10\\n\"\n            \"softdep test11 pre: test12 post: test13\\n\"\n            \"softdep test3 pre: test141516\\n\";\n\n    const std::string modules_alias =\n            \"# Aliases extracted from modules themselves.\\n\"\n            \"\\n\"\n            \"alias test141516 test14\\n\"\n            \"alias test141516 test15\\n\"\n            \"alias test141516 test16\\n\";\n\n    const std::string modules_options =\n            \"options test7.ko param1=4\\n\"\n            \"options test9.ko param_x=1 param_y=2 param_z=3\\n\"\n            \"options test100.ko param_1=1\\n\";\n\n    const std::string modules_blocklist =\n            \"blocklist test9.ko\\n\"\n            \"blocklist test3.ko\\n\";\n\n    const std::string modules_load =\n            \"test4.ko\\n\"\n            \"test1.ko\\n\"\n            \"test3.ko\\n\"\n            \"test5.ko\\n\"\n            \"test7.ko\\n\"\n            \"test9.ko\\n\"\n            \"test11.ko\\n\";\n\n    TemporaryDir dir;\n    auto dir_path = std::string(dir.path);\n    ASSERT_TRUE(android::base::WriteStringToFile(modules_alias, dir_path + \"/modules.alias\", 0600,\n                                                 getuid(), getgid()));\n\n    ASSERT_TRUE(android::base::WriteStringToFile(modules_dep, dir_path + \"/modules.dep\", 0600,\n                                                 getuid(), getgid()));\n    ASSERT_TRUE(android::base::WriteStringToFile(modules_softdep, dir_path + \"/modules.softdep\",\n                                                 0600, getuid(), getgid()));\n    ASSERT_TRUE(android::base::WriteStringToFile(modules_options, dir_path + \"/modules.options\",\n                                                 0600, getuid(), getgid()));\n    ASSERT_TRUE(android::base::WriteStringToFile(modules_load, dir_path + \"/modules.load\", 0600,\n                                                 getuid(), getgid()));\n    ASSERT_TRUE(android::base::WriteStringToFile(modules_blocklist, dir_path + \"/modules.blocklist\",\n                                                 0600, getuid(), getgid()));\n\n    for (auto i = test_modules.begin(); i != test_modules.end(); ++i) {\n        *i = dir.path + *i;\n    }\n\n    Modprobe m({dir.path}, \"modules.load\", false);\n    EXPECT_TRUE(m.LoadListedModules());\n\n    GTEST_LOG_(INFO) << \"Expected modules loaded (in order):\";\n    for (auto i = expected_modules_loaded.begin(); i != expected_modules_loaded.end(); ++i) {\n        *i = dir.path + *i;\n        GTEST_LOG_(INFO) << \"\\\"\" << *i << \"\\\"\";\n    }\n    GTEST_LOG_(INFO) << \"Actual modules loaded (in order):\";\n    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {\n        GTEST_LOG_(INFO) << \"\\\"\" << *i << \"\\\"\";\n    }\n\n    EXPECT_TRUE(modules_loaded == expected_modules_loaded);\n\n    EXPECT_TRUE(m.GetModuleCount() == 15);\n    EXPECT_TRUE(m.Remove(\"test4\"));\n\n    GTEST_LOG_(INFO) << \"Expected modules loaded after removing test4 (in order):\";\n    for (auto i = expected_after_remove.begin(); i != expected_after_remove.end(); ++i) {\n        *i = dir.path + *i;\n        GTEST_LOG_(INFO) << \"\\\"\" << *i << \"\\\"\";\n    }\n    GTEST_LOG_(INFO) << \"Actual modules loaded after removing test4 (in order):\";\n    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {\n        GTEST_LOG_(INFO) << \"\\\"\" << *i << \"\\\"\";\n    }\n\n    EXPECT_TRUE(modules_loaded == expected_after_remove);\n\n    Modprobe m2({dir.path});\n\n    EXPECT_FALSE(m2.LoadWithAliases(\"test4\", true));\n    while (modules_loaded.size() > 0) EXPECT_TRUE(m2.Remove(modules_loaded.front()));\n    EXPECT_TRUE(m2.LoadListedModules());\n\n    GTEST_LOG_(INFO) << \"Expected modules loaded after enabling blocklist (in order):\";\n    for (auto i = expected_modules_blocklist_enabled.begin();\n         i != expected_modules_blocklist_enabled.end(); ++i) {\n        *i = dir.path + *i;\n        GTEST_LOG_(INFO) << \"\\\"\" << *i << \"\\\"\";\n    }\n    GTEST_LOG_(INFO) << \"Actual modules loaded with blocklist enabled (in order):\";\n    for (auto i = modules_loaded.begin(); i != modules_loaded.end(); ++i) {\n        GTEST_LOG_(INFO) << \"\\\"\" << *i << \"\\\"\";\n    }\n    EXPECT_TRUE(modules_loaded == expected_modules_blocklist_enabled);\n}\n\nTEST(libmodprobe, ModuleDepLineWithoutColonIsSkipped) {\n    TemporaryDir dir;\n    auto dir_path = std::string(dir.path);\n    ASSERT_TRUE(android::base::WriteStringToFile(\n            \"no_colon.ko no_colon.ko\\n\", dir_path + \"/modules.dep\", 0600, getuid(), getgid()));\n\n    kernel_cmdline = \"\";\n    test_modules = {dir_path + \"/no_colon.ko\"};\n\n    Modprobe m({dir.path});\n    EXPECT_FALSE(m.LoadWithAliases(\"no_colon\", true));\n}\n"
  },
  {
    "path": "libmodprobe/libmodprobe_test.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n#include <vector>\n\nextern std::string kernel_cmdline;\nextern std::vector<std::string> test_modules;\nextern std::vector<std::string> modules_loaded;\n"
  },
  {
    "path": "libnetutils/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"system_core_libnetutils_license\"],\n}\n\n// Added automatically by a large-scale-change\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_libnetutils_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\ncc_library_shared {\n    name: \"libnetutils\",\n    vendor_available: true,\n\n    srcs: [\n        \"dhcpclient.c\",\n        \"dhcpmsg.c\",\n        \"ifc_utils.c\",\n        \"packet.c\",\n    ],\n\n    shared_libs: [\n        \"libcutils\",\n        \"liblog\",\n    ],\n\n    static_libs: [\n        \"libip_checksum\",\n    ],\n\n    cflags: [\"-Werror\"],\n\n    export_include_dirs: [\"include\"],\n    // TODO: remove connectivity module dependency, or have this lib build against the ndk\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.tethering\",\n    ],\n}\n\ncc_binary {\n    name: \"dhcpdbg\",\n\n    srcs: [\n        \"dhcptool.c\",\n    ],\n\n    shared_libs: [\n        \"libnetutils\",\n    ],\n\n    cflags: [\"-Werror\"],\n}\n"
  },
  {
    "path": "libnetutils/NOTICE",
    "content": "\n   Copyright (c) 2005-2008, The Android Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "libnetutils/OWNERS",
    "content": "include platform/system/netd:/OWNERS\n\n"
  },
  {
    "path": "libnetutils/dhcpclient.c",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"DHCP\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <poll.h>\n#include <netinet/in.h>\n#include <stdarg.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/select.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <cutils/properties.h>\n#include <log/log.h>\n\n#include <netutils/ifc.h>\n#include \"dhcpmsg.h\"\n#include \"packet.h\"\n\n#define VERBOSE 2\n\nstatic int verbose = 1;\nstatic char errmsg[2048];\n\ntypedef unsigned long long msecs_t;\n#if VERBOSE\nvoid dump_dhcp_msg(dhcp_msg *msg, int len);\n#endif\n\nmsecs_t get_msecs(void)\n{\n    struct timespec ts;\n\n    if (clock_gettime(CLOCK_MONOTONIC, &ts)) {\n        return 0;\n    } else {\n        return (((msecs_t) ts.tv_sec) * ((msecs_t) 1000)) +\n            (((msecs_t) ts.tv_nsec) / ((msecs_t) 1000000));\n    }\n}\n\nvoid printerr(char *fmt, ...)\n{\n    va_list ap;\n\n    va_start(ap, fmt);\n    vsnprintf(errmsg, sizeof(errmsg), fmt, ap);\n    va_end(ap);\n\n    ALOGD(\"%s\", errmsg);\n}\n\nconst char *dhcp_lasterror()\n{\n    return errmsg;\n}\n\nint fatal(const char *reason)\n{\n    printerr(\"%s: %s\\n\", reason, strerror(errno));\n    return -1;\n//    exit(1);\n}\n\nconst char *ipaddr(in_addr_t addr)\n{\n    struct in_addr in_addr;\n\n    in_addr.s_addr = addr;\n    return inet_ntoa(in_addr);\n}\n\nextern int ipv4NetmaskToPrefixLength(in_addr_t mask);\n\ntypedef struct dhcp_info dhcp_info;\n\nstruct dhcp_info {\n    uint32_t type;\n\n    uint32_t ipaddr;\n    uint32_t gateway;\n    uint32_t prefixLength;\n\n    uint32_t dns1;\n    uint32_t dns2;\n\n    uint32_t serveraddr;\n    uint32_t lease;\n};\n\ndhcp_info last_good_info;\n\nvoid get_dhcp_info(uint32_t *ipaddr, uint32_t *gateway, uint32_t *prefixLength,\n                   uint32_t *dns1, uint32_t *dns2, uint32_t *server,\n                   uint32_t *lease)\n{\n    *ipaddr = last_good_info.ipaddr;\n    *gateway = last_good_info.gateway;\n    *prefixLength = last_good_info.prefixLength;\n    *dns1 = last_good_info.dns1;\n    *dns2 = last_good_info.dns2;\n    *server = last_good_info.serveraddr;\n    *lease = last_good_info.lease;\n}\n\nstatic int dhcp_configure(const char *ifname, dhcp_info *info)\n{\n    last_good_info = *info;\n    return ifc_configure(ifname, info->ipaddr, info->prefixLength, info->gateway,\n                         info->dns1, info->dns2);\n}\n\nstatic const char *dhcp_type_to_name(uint32_t type)\n{\n    switch(type) {\n    case DHCPDISCOVER: return \"discover\";\n    case DHCPOFFER:    return \"offer\";\n    case DHCPREQUEST:  return \"request\";\n    case DHCPDECLINE:  return \"decline\";\n    case DHCPACK:      return \"ack\";\n    case DHCPNAK:      return \"nak\";\n    case DHCPRELEASE:  return \"release\";\n    case DHCPINFORM:   return \"inform\";\n    default:           return \"???\";\n    }\n}\n\nvoid dump_dhcp_info(dhcp_info *info)\n{\n    char addr[20], gway[20];\n    ALOGD(\"--- dhcp %s (%d) ---\",\n            dhcp_type_to_name(info->type), info->type);\n    strcpy(addr, ipaddr(info->ipaddr));\n    strcpy(gway, ipaddr(info->gateway));\n    ALOGD(\"ip %s gw %s prefixLength %d\", addr, gway, info->prefixLength);\n    if (info->dns1) ALOGD(\"dns1: %s\", ipaddr(info->dns1));\n    if (info->dns2) ALOGD(\"dns2: %s\", ipaddr(info->dns2));\n    ALOGD(\"server %s, lease %d seconds\",\n            ipaddr(info->serveraddr), info->lease);\n}\n\n\nint decode_dhcp_msg(dhcp_msg *msg, int len, dhcp_info *info)\n{\n    uint8_t *x;\n    unsigned int opt;\n    int optlen;\n\n    memset(info, 0, sizeof(dhcp_info));\n    if (len < (DHCP_MSG_FIXED_SIZE + 4)) return -1;\n\n    len -= (DHCP_MSG_FIXED_SIZE + 4);\n\n    if (msg->options[0] != OPT_COOKIE1) return -1;\n    if (msg->options[1] != OPT_COOKIE2) return -1;\n    if (msg->options[2] != OPT_COOKIE3) return -1;\n    if (msg->options[3] != OPT_COOKIE4) return -1;\n\n    x = msg->options + 4;\n\n    while (len > 2) {\n        opt = *x++;\n        if (opt == OPT_PAD) {\n            len--;\n            continue;\n        }\n        if (opt == OPT_END) {\n            break;\n        }\n        optlen = *x++;\n        len -= 2;\n        if (optlen > len) {\n            break;\n        }\n        switch(opt) {\n        case OPT_SUBNET_MASK:\n            if (optlen >= 4) {\n                in_addr_t mask;\n                memcpy(&mask, x, 4);\n                info->prefixLength = ipv4NetmaskToPrefixLength(mask);\n            }\n            break;\n        case OPT_GATEWAY:\n            if (optlen >= 4) memcpy(&info->gateway, x, 4);\n            break;\n        case OPT_DNS:\n            if (optlen >= 4) memcpy(&info->dns1, x + 0, 4);\n            if (optlen >= 8) memcpy(&info->dns2, x + 4, 4);\n            break;\n        case OPT_LEASE_TIME:\n            if (optlen >= 4) {\n                memcpy(&info->lease, x, 4);\n                info->lease = ntohl(info->lease);\n            }\n            break;\n        case OPT_SERVER_ID:\n            if (optlen >= 4) memcpy(&info->serveraddr, x, 4);\n            break;\n        case OPT_MESSAGE_TYPE:\n            info->type = *x;\n            break;\n        default:\n            break;\n        }\n        x += optlen;\n        len -= optlen;\n    }\n\n    info->ipaddr = msg->yiaddr;\n\n    return 0;\n}\n\n#if VERBOSE\n\nstatic void hex2str(char *buf, size_t buf_size, const unsigned char *array, int len)\n{\n    int i;\n    char *cp = buf;\n    char *buf_end = buf + buf_size;\n    for (i = 0; i < len; i++) {\n        cp += snprintf(cp, buf_end - cp, \" %02x \", array[i]);\n    }\n}\n\nvoid dump_dhcp_msg(dhcp_msg *msg, int len)\n{\n    unsigned char *x;\n    unsigned int n,c;\n    int optsz;\n    const char *name;\n    char buf[2048];\n\n    ALOGD(\"===== DHCP message:\");\n    if (len < DHCP_MSG_FIXED_SIZE) {\n        ALOGD(\"Invalid length %d, should be %d\", len, DHCP_MSG_FIXED_SIZE);\n        return;\n    }\n\n    len -= DHCP_MSG_FIXED_SIZE;\n\n    if (msg->op == OP_BOOTREQUEST)\n        name = \"BOOTREQUEST\";\n    else if (msg->op == OP_BOOTREPLY)\n        name = \"BOOTREPLY\";\n    else\n        name = \"????\";\n    ALOGD(\"op = %s (%d), htype = %d, hlen = %d, hops = %d\",\n           name, msg->op, msg->htype, msg->hlen, msg->hops);\n    ALOGD(\"xid = 0x%08x secs = %d, flags = 0x%04x optlen = %d\",\n           ntohl(msg->xid), ntohs(msg->secs), ntohs(msg->flags), len);\n    ALOGD(\"ciaddr = %s\", ipaddr(msg->ciaddr));\n    ALOGD(\"yiaddr = %s\", ipaddr(msg->yiaddr));\n    ALOGD(\"siaddr = %s\", ipaddr(msg->siaddr));\n    ALOGD(\"giaddr = %s\", ipaddr(msg->giaddr));\n\n    c = msg->hlen > 16 ? 16 : msg->hlen;\n    hex2str(buf, sizeof(buf), msg->chaddr, c);\n    ALOGD(\"chaddr = {%s}\", buf);\n\n    for (n = 0; n < 64; n++) {\n        unsigned char x = msg->sname[n];\n        if ((x < ' ') || (x > 127)) {\n            if (x == 0) break;\n            msg->sname[n] = '.';\n        }\n    }\n    msg->sname[63] = 0;\n\n    for (n = 0; n < 128; n++) {\n        unsigned char x = msg->file[n];\n        if ((x < ' ') || (x > 127)) {\n            if (x == 0) break;\n            msg->file[n] = '.';\n        }\n    }\n    msg->file[127] = 0;\n\n    ALOGD(\"sname = '%s'\", msg->sname);\n    ALOGD(\"file = '%s'\", msg->file);\n\n    if (len < 4) return;\n    len -= 4;\n    x = msg->options + 4;\n\n    while (len > 2) {\n        if (*x == 0) {\n            x++;\n            len--;\n            continue;\n        }\n        if (*x == OPT_END) {\n            break;\n        }\n        len -= 2;\n        optsz = x[1];\n        if (optsz > len) break;\n        if (x[0] == OPT_DOMAIN_NAME || x[0] == OPT_MESSAGE) {\n            if ((unsigned int)optsz < sizeof(buf) - 1) {\n                n = optsz;\n            } else {\n                n = sizeof(buf) - 1;\n            }\n            memcpy(buf, &x[2], n);\n            buf[n] = '\\0';\n        } else {\n            hex2str(buf, sizeof(buf), &x[2], optsz);\n        }\n        if (x[0] == OPT_MESSAGE_TYPE)\n            name = dhcp_type_to_name(x[2]);\n        else\n            name = NULL;\n        ALOGD(\"op %d len %d {%s} %s\", x[0], optsz, buf, name == NULL ? \"\" : name);\n        len -= optsz;\n        x = x + optsz + 2;\n    }\n}\n\n#endif\n\nstatic int send_message(int sock, int if_index, dhcp_msg  *msg, int size)\n{\n#if VERBOSE > 1\n    dump_dhcp_msg(msg, size);\n#endif\n    return send_packet(sock, if_index, msg, size, INADDR_ANY, INADDR_BROADCAST,\n                       PORT_BOOTP_CLIENT, PORT_BOOTP_SERVER);\n}\n\nstatic int is_valid_reply(dhcp_msg *msg, dhcp_msg *reply, int sz)\n{\n    if (sz < DHCP_MSG_FIXED_SIZE) {\n        if (verbose) ALOGD(\"Wrong size %d != %d\\n\", sz, DHCP_MSG_FIXED_SIZE);\n        return 0;\n    }\n    if (reply->op != OP_BOOTREPLY) {\n        if (verbose) ALOGD(\"Wrong Op %d != %d\\n\", reply->op, OP_BOOTREPLY);\n        return 0;\n    }\n    if (reply->xid != msg->xid) {\n        if (verbose) ALOGD(\"Wrong Xid 0x%x != 0x%x\\n\", ntohl(reply->xid),\n                           ntohl(msg->xid));\n        return 0;\n    }\n    if (reply->htype != msg->htype) {\n        if (verbose) ALOGD(\"Wrong Htype %d != %d\\n\", reply->htype, msg->htype);\n        return 0;\n    }\n    if (reply->hlen != msg->hlen) {\n        if (verbose) ALOGD(\"Wrong Hlen %d != %d\\n\", reply->hlen, msg->hlen);\n        return 0;\n    }\n    if (memcmp(msg->chaddr, reply->chaddr, msg->hlen)) {\n        if (verbose) ALOGD(\"Wrong chaddr %x != %x\\n\", *(reply->chaddr),*(msg->chaddr));\n        return 0;\n    }\n    return 1;\n}\n\n#define STATE_SELECTING  1\n#define STATE_REQUESTING 2\n\n#define TIMEOUT_INITIAL   4000\n#define TIMEOUT_MAX      32000\n\nint dhcp_init_ifc(const char *ifname)\n{\n    dhcp_msg discover_msg;\n    dhcp_msg request_msg;\n    dhcp_msg reply;\n    dhcp_msg *msg;\n    dhcp_info info;\n    int s, r, size;\n    int valid_reply;\n    uint32_t xid;\n    unsigned char hwaddr[6];\n    struct pollfd pfd;\n    unsigned int state;\n    unsigned int timeout;\n    int if_index;\n\n    xid = (uint32_t) get_msecs();\n\n    if (ifc_get_hwaddr(ifname, hwaddr)) {\n        return fatal(\"cannot obtain interface address\");\n    }\n    if (ifc_get_ifindex(ifname, &if_index)) {\n        return fatal(\"cannot obtain interface index\");\n    }\n\n    s = open_raw_socket(ifname, hwaddr, if_index);\n\n    timeout = TIMEOUT_INITIAL;\n    state = STATE_SELECTING;\n    info.type = 0;\n    goto transmit;\n\n    for (;;) {\n        pfd.fd = s;\n        pfd.events = POLLIN;\n        pfd.revents = 0;\n        r = poll(&pfd, 1, timeout);\n\n        if (r == 0) {\n#if VERBOSE\n            printerr(\"TIMEOUT\\n\");\n#endif\n            if (timeout >= TIMEOUT_MAX) {\n                printerr(\"timed out\\n\");\n                if ( info.type == DHCPOFFER ) {\n                    printerr(\"no acknowledgement from DHCP server\\nconfiguring %s with offered parameters\\n\", ifname);\n                    return dhcp_configure(ifname, &info);\n                }\n                errno = ETIME;\n                close(s);\n                return -1;\n            }\n            timeout = timeout * 2;\n\n        transmit:\n            size = 0;\n            msg = NULL;\n            switch(state) {\n            case STATE_SELECTING:\n                msg = &discover_msg;\n                size = init_dhcp_discover_msg(msg, hwaddr, xid);\n                break;\n            case STATE_REQUESTING:\n                msg = &request_msg;\n                size = init_dhcp_request_msg(msg, hwaddr, xid, info.ipaddr, info.serveraddr);\n                break;\n            default:\n                r = 0;\n            }\n            if (size != 0) {\n                r = send_message(s, if_index, msg, size);\n                if (r < 0) {\n                    printerr(\"error sending dhcp msg: %s\\n\", strerror(errno));\n                }\n            }\n            continue;\n        }\n\n        if (r < 0) {\n            if ((errno == EAGAIN) || (errno == EINTR)) {\n                continue;\n            }\n            return fatal(\"poll failed\");\n        }\n\n        errno = 0;\n        r = receive_packet(s, &reply);\n        if (r < 0) {\n            if (errno != 0) {\n                ALOGD(\"receive_packet failed (%d): %s\", r, strerror(errno));\n                if (errno == ENETDOWN || errno == ENXIO) {\n                    return -1;\n                }\n            }\n            continue;\n        }\n\n#if VERBOSE > 1\n        dump_dhcp_msg(&reply, r);\n#endif\n        decode_dhcp_msg(&reply, r, &info);\n\n        if (state == STATE_SELECTING) {\n            valid_reply = is_valid_reply(&discover_msg, &reply, r);\n        } else {\n            valid_reply = is_valid_reply(&request_msg, &reply, r);\n        }\n        if (!valid_reply) {\n            printerr(\"invalid reply\\n\");\n            continue;\n        }\n\n        if (verbose) dump_dhcp_info(&info);\n\n        switch(state) {\n        case STATE_SELECTING:\n            if (info.type == DHCPOFFER) {\n                state = STATE_REQUESTING;\n                timeout = TIMEOUT_INITIAL;\n                xid++;\n                goto transmit;\n            }\n            break;\n        case STATE_REQUESTING:\n            if (info.type == DHCPACK) {\n                printerr(\"configuring %s\\n\", ifname);\n                close(s);\n                return dhcp_configure(ifname, &info);\n            } else if (info.type == DHCPNAK) {\n                printerr(\"configuration request denied\\n\");\n                close(s);\n                return -1;\n            } else {\n                printerr(\"ignoring %s message in state %d\\n\",\n                         dhcp_type_to_name(info.type), state);\n            }\n            break;\n        }\n    }\n    close(s);\n    return 0;\n}\n\nint do_dhcp(char *iname)\n{\n    if (ifc_set_addr(iname, 0)) {\n        printerr(\"failed to set ip addr for %s to 0.0.0.0: %s\\n\", iname, strerror(errno));\n        return -1;\n    }\n\n    if (ifc_up(iname)) {\n        printerr(\"failed to bring up interface %s: %s\\n\", iname, strerror(errno));\n        return -1;\n    }\n\n    return dhcp_init_ifc(iname);\n}\n"
  },
  {
    "path": "libnetutils/dhcpmsg.c",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <stdarg.h>\n#include <string.h>\n#include <netinet/in.h>\n\n#include \"dhcpmsg.h\"\n\nstatic void *init_dhcp_msg(dhcp_msg *msg, int type, void *hwaddr, uint32_t xid)\n{\n    uint8_t *x;\n\n    memset(msg, 0, sizeof(dhcp_msg));\n\n    msg->op = OP_BOOTREQUEST;\n    msg->htype = HTYPE_ETHER;\n    msg->hlen = 6;\n    msg->hops = 0;\n\n    msg->flags = htons(FLAGS_BROADCAST);\n\n    msg->xid = xid;\n\n    memcpy(msg->chaddr, hwaddr, 6);\n\n    x = msg->options;\n\n    *x++ = OPT_COOKIE1;\n    *x++ = OPT_COOKIE2;\n    *x++ = OPT_COOKIE3;\n    *x++ = OPT_COOKIE4;\n\n    *x++ = OPT_MESSAGE_TYPE;\n    *x++ = 1;\n    *x++ = type;\n\n    return x;\n}\n\nint init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid)\n{\n    uint8_t *x;\n\n    x = init_dhcp_msg(msg, DHCPDISCOVER, hwaddr, xid);\n\n    *x++ = OPT_PARAMETER_LIST;\n    *x++ = 4;\n    *x++ = OPT_SUBNET_MASK;\n    *x++ = OPT_GATEWAY;\n    *x++ = OPT_DNS;\n    *x++ = OPT_BROADCAST_ADDR;\n\n    *x++ = OPT_END;\n\n    return DHCP_MSG_FIXED_SIZE + (x - msg->options);\n}\n\nint init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,\n                          uint32_t ipaddr, uint32_t serveraddr)\n{\n    uint8_t *x;\n\n    x = init_dhcp_msg(msg, DHCPREQUEST, hwaddr, xid);\n\n    *x++ = OPT_PARAMETER_LIST;\n    *x++ = 4;\n    *x++ = OPT_SUBNET_MASK;\n    *x++ = OPT_GATEWAY;\n    *x++ = OPT_DNS;\n    *x++ = OPT_BROADCAST_ADDR;\n\n    *x++ = OPT_REQUESTED_IP;\n    *x++ = 4;\n    memcpy(x, &ipaddr, 4);\n    x +=  4;\n\n    *x++ = OPT_SERVER_ID;\n    *x++ = 4;\n    memcpy(x, &serveraddr, 4);\n    x += 4;\n\n    *x++ = OPT_END;\n\n    return DHCP_MSG_FIXED_SIZE + (x - msg->options);\n}\n"
  },
  {
    "path": "libnetutils/dhcpmsg.h",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); \n * you may not use this file except in compliance with the License. \n * You may obtain a copy of the License at \n *\n *     http://www.apache.org/licenses/LICENSE-2.0 \n *\n * Unless required by applicable law or agreed to in writing, software \n * distributed under the License is distributed on an \"AS IS\" BASIS, \n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n * See the License for the specific language governing permissions and \n * limitations under the License.\n */\n\n#ifndef _WIFI_DHCP_H_\n#define _WIFI_DHCP_H_\n\n#include <sys/types.h>\n\n#define PORT_BOOTP_SERVER 67\n#define PORT_BOOTP_CLIENT 68\n\n/* RFC 2131 p 9 */\ntypedef struct dhcp_msg dhcp_msg;\n\n#define OP_BOOTREQUEST 1\n#define OP_BOOTREPLY   2\n\n#define FLAGS_BROADCAST 0x8000\n\n#define HTYPE_ETHER    1\n\nstruct dhcp_msg\n{\n    uint8_t op;           /* BOOTREQUEST / BOOTREPLY    */\n    uint8_t htype;        /* hw addr type               */\n    uint8_t hlen;         /* hw addr len                */\n    uint8_t hops;         /* client set to 0            */\n    \n    uint32_t xid;         /* transaction id             */\n\n    uint16_t secs;        /* seconds since start of acq */\n    uint16_t flags;\n\n    uint32_t ciaddr;      /* client IP addr             */\n    uint32_t yiaddr;      /* your (client) IP addr      */\n    uint32_t siaddr;      /* ip addr of next server     */\n                          /* (DHCPOFFER and DHCPACK)    */\n    uint32_t giaddr;      /* relay agent IP addr        */\n\n    uint8_t chaddr[16];  /* client hw addr             */\n    char sname[64];      /* asciiz server hostname     */\n    char file[128];      /* asciiz boot file name      */\n\n    uint8_t options[1024];  /* optional parameters        */\n};\n\n#define DHCP_MSG_FIXED_SIZE 236\n\n/* first four bytes of options are a cookie to indicate that\n** the payload are DHCP options as opposed to some other BOOTP\n** extension.\n*/\n#define OPT_COOKIE1          0x63\n#define OPT_COOKIE2          0x82\n#define OPT_COOKIE3          0x53\n#define OPT_COOKIE4          0x63\n\n/* BOOTP/DHCP options - see RFC 2132 */\n#define OPT_PAD              0\n\n#define OPT_SUBNET_MASK      1     /* 4 <ipaddr> */\n#define OPT_TIME_OFFSET      2     /* 4 <seconds> */\n#define OPT_GATEWAY          3     /* 4*n <ipaddr> * n */\n#define OPT_DNS              6     /* 4*n <ipaddr> * n */\n#define OPT_DOMAIN_NAME      15    /* n <domainnamestring> */\n#define OPT_BROADCAST_ADDR   28    /* 4 <ipaddr> */\n\n#define OPT_REQUESTED_IP     50    /* 4 <ipaddr> */\n#define OPT_LEASE_TIME       51    /* 4 <seconds> */\n#define OPT_MESSAGE_TYPE     53    /* 1 <msgtype> */\n#define OPT_SERVER_ID        54    /* 4 <ipaddr> */\n#define OPT_PARAMETER_LIST   55    /* n <optcode> * n */\n#define OPT_MESSAGE          56    /* n <errorstring> */\n#define OPT_CLASS_ID         60    /* n <opaque> */\n#define OPT_CLIENT_ID        61    /* n <opaque> */\n#define OPT_END              255\n\n/* DHCP message types */\n#define DHCPDISCOVER         1\n#define DHCPOFFER            2\n#define DHCPREQUEST          3\n#define DHCPDECLINE          4\n#define DHCPACK              5\n#define DHCPNAK              6\n#define DHCPRELEASE          7\n#define DHCPINFORM           8\n\nint init_dhcp_discover_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid);\n\nint init_dhcp_request_msg(dhcp_msg *msg, void *hwaddr, uint32_t xid,\n                          uint32_t ipaddr, uint32_t serveraddr);\n\n#endif\n"
  },
  {
    "path": "libnetutils/dhcptool.c",
    "content": "/*\n * Copyright 2015, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <err.h>\n#include <errno.h>\n#include <error.h>\n#include <stdbool.h>\n#include <stdlib.h>\n\n#include <netutils/ifc.h>\n\nextern int do_dhcp(char*);\n\nint main(int argc, char* argv[]) {\n    if (argc != 2) {\n        error(EXIT_FAILURE, 0, \"usage: %s INTERFACE\", argv[0]);\n    }\n\n    char* interface = argv[1];\n    if (ifc_init()) {\n        err(errno, \"dhcptool %s: ifc_init failed\", interface);\n        ifc_close();\n        return EXIT_FAILURE;\n    }\n\n    int rc = do_dhcp(interface);\n    if (rc) {\n        err(errno, \"dhcptool %s: do_dhcp failed\", interface);\n    }\n    warn(\"IP assignment is for debug purposes ONLY\");\n    ifc_close();\n\n    return rc ? EXIT_FAILURE : EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "libnetutils/ifc_utils.c",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <arpa/inet.h>\n#include <errno.h>\n#include <linux/if.h>\n#include <linux/if_ether.h>\n#include <linux/if_arp.h>\n#include <linux/netlink.h>\n#include <linux/route.h>\n#include <linux/ipv6_route.h>\n#include <linux/rtnetlink.h>\n#include <linux/sockios.h>\n#include <net/if.h>\n#include <netdb.h>\n#include <netinet/in.h>\n#include <pthread.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/select.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#ifdef ANDROID\n#define LOG_TAG \"NetUtils\"\n#include <cutils/properties.h>\n#include <log/log.h>\n#else\n#define ALOGD printf\n#define ALOGW printf\n#endif\n\n#include \"netutils/ifc.h\"\n\n#if defined(__ANDROID__)\n/* SIOCKILLADDR is an Android extension. */\n#define SIOCKILLADDR 0x8939\n#endif\n\nstatic int ifc_ctl_sock = -1;\nstatic int ifc_ctl_sock6 = -1;\nstatic pthread_mutex_t ifc_sock_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;\nstatic pthread_mutex_t ifc_sock6_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;\nvoid printerr(char *fmt, ...);\n\n#define DBG 0\n#define INET_ADDRLEN 4\n#define INET6_ADDRLEN 16\n\nin_addr_t prefixLengthToIpv4Netmask(int prefix_length)\n{\n    in_addr_t mask = 0;\n\n    // C99 (6.5.7): shifts of 32 bits have undefined results\n    if (prefix_length <= 0 || prefix_length > 32) {\n        return 0;\n    }\n\n    mask = ~mask << (32 - prefix_length);\n    mask = htonl(mask);\n\n    return mask;\n}\n\nint ipv4NetmaskToPrefixLength(in_addr_t mask)\n{\n    int prefixLength = 0;\n    uint32_t m = (uint32_t)ntohl(mask);\n    while (m & 0x80000000) {\n        prefixLength++;\n        m = m << 1;\n    }\n    return prefixLength;\n}\n\nstatic const char *ipaddr_to_string(in_addr_t addr)\n{\n    struct in_addr in_addr;\n\n    in_addr.s_addr = addr;\n    return inet_ntoa(in_addr);\n}\n\nint string_to_ip(const char *string, struct sockaddr_storage *ss) {\n    struct addrinfo hints, *ai;\n    int ret;\n\n    if (ss == NULL) {\n        return -EFAULT;\n    }\n\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_flags = AI_NUMERICHOST;\n    hints.ai_socktype = SOCK_DGRAM;\n\n    ret = getaddrinfo(string, NULL, &hints, &ai);\n    if (ret == 0) {\n        memcpy(ss, ai->ai_addr, ai->ai_addrlen);\n        freeaddrinfo(ai);\n    } else {\n        // Getaddrinfo has its own error codes. Convert to negative errno.\n        // There, the only thing that can reasonably happen is that the passed-in string is invalid.\n        ret = (ret == EAI_SYSTEM) ? -errno : -EINVAL;\n    }\n\n    return ret;\n}\n\nint ifc_init(void)\n{\n    int ret;\n\n    pthread_mutex_lock(&ifc_sock_mutex);\n    if (ifc_ctl_sock == -1) {\n        ifc_ctl_sock = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n        if (ifc_ctl_sock < 0) {\n            printerr(\"socket() failed: %s\\n\", strerror(errno));\n        }\n    }\n\n    ret = ifc_ctl_sock < 0 ? -1 : 0;\n    if (DBG) printerr(\"ifc_init_returning %d\", ret);\n    return ret;\n}\n\nint ifc_init6(void)\n{\n    pthread_mutex_lock(&ifc_sock6_mutex);\n    if (ifc_ctl_sock6 == -1) {\n        ifc_ctl_sock6 = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n        if (ifc_ctl_sock6 < 0) {\n            printerr(\"socket() failed: %s\\n\", strerror(errno));\n        }\n    }\n    return ifc_ctl_sock6 < 0 ? -1 : 0;\n}\n\nvoid ifc_close(void)\n{\n    if (DBG) printerr(\"ifc_close\");\n    if (ifc_ctl_sock != -1) {\n        (void)close(ifc_ctl_sock);\n        ifc_ctl_sock = -1;\n    }\n    pthread_mutex_unlock(&ifc_sock_mutex);\n}\n\nvoid ifc_close6(void)\n{\n    if (ifc_ctl_sock6 != -1) {\n        (void)close(ifc_ctl_sock6);\n        ifc_ctl_sock6 = -1;\n    }\n    pthread_mutex_unlock(&ifc_sock6_mutex);\n}\n\nstatic void ifc_init_ifr(const char *name, struct ifreq *ifr)\n{\n    memset(ifr, 0, sizeof(struct ifreq));\n    strncpy(ifr->ifr_name, name, IFNAMSIZ);\n    ifr->ifr_name[IFNAMSIZ - 1] = 0;\n}\n\nint ifc_get_hwaddr(const char *name, void *ptr)\n{\n    int r;\n    struct ifreq ifr;\n    ifc_init_ifr(name, &ifr);\n\n    r = ioctl(ifc_ctl_sock, SIOCGIFHWADDR, &ifr);\n    if(r < 0) return -1;\n\n    memcpy(ptr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);\n    return 0;\n}\n\nint ifc_get_ifindex(const char *name, int *if_indexp)\n{\n    int r;\n    struct ifreq ifr;\n    ifc_init_ifr(name, &ifr);\n\n    r = ioctl(ifc_ctl_sock, SIOCGIFINDEX, &ifr);\n    if(r < 0) return -1;\n\n    *if_indexp = ifr.ifr_ifindex;\n    return 0;\n}\n\nstatic int ifc_set_flags(const char *name, unsigned set, unsigned clr)\n{\n    struct ifreq ifr;\n    ifc_init_ifr(name, &ifr);\n\n    if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) return -1;\n    ifr.ifr_flags = (ifr.ifr_flags & (~clr)) | set;\n    return ioctl(ifc_ctl_sock, SIOCSIFFLAGS, &ifr);\n}\n\nint ifc_up(const char *name)\n{\n    int ret = ifc_set_flags(name, IFF_UP, 0);\n    if (DBG) printerr(\"ifc_up(%s) = %d\", name, ret);\n    return ret;\n}\n\nint ifc_down(const char *name)\n{\n    int ret = ifc_set_flags(name, 0, IFF_UP);\n    if (DBG) printerr(\"ifc_down(%s) = %d\", name, ret);\n    return ret;\n}\n\nstatic void init_sockaddr_in(struct sockaddr *sa, in_addr_t addr)\n{\n    struct sockaddr_in *sin = (struct sockaddr_in *) sa;\n    sin->sin_family = AF_INET;\n    sin->sin_port = 0;\n    sin->sin_addr.s_addr = addr;\n}\n\nint ifc_set_addr(const char *name, in_addr_t addr)\n{\n    struct ifreq ifr;\n    int ret;\n\n    ifc_init_ifr(name, &ifr);\n    init_sockaddr_in(&ifr.ifr_addr, addr);\n\n    ret = ioctl(ifc_ctl_sock, SIOCSIFADDR, &ifr);\n    if (DBG) printerr(\"ifc_set_addr(%s, xx) = %d\", name, ret);\n    return ret;\n}\n\n/*\n * Adds or deletes an IP address on an interface.\n *\n * Action is one of:\n * - RTM_NEWADDR (to add a new address)\n * - RTM_DELADDR (to delete an existing address)\n *\n * Returns zero on success and negative errno on failure.\n */\nint ifc_act_on_address(int action, const char* name, const char* address, int prefixlen,\n                       bool nodad) {\n    int ifindex, s, len, ret;\n    struct sockaddr_storage ss;\n    int saved_errno;\n    void *addr;\n    size_t addrlen;\n    struct {\n        struct nlmsghdr n;\n        struct ifaddrmsg r;\n        // Allow for IPv4 or IPv6 address, headers, IPv4 broadcast address and padding.\n        char attrbuf[NLMSG_ALIGN(sizeof(struct rtattr)) + NLMSG_ALIGN(INET6_ADDRLEN) +\n                     NLMSG_ALIGN(sizeof(struct rtattr)) + NLMSG_ALIGN(INET_ADDRLEN)];\n    } req;\n    struct rtattr *rta;\n    struct nlmsghdr *nh;\n    struct nlmsgerr *err;\n\n    // Get interface ID.\n    ifindex = if_nametoindex(name);\n    if (ifindex == 0) {\n        return -errno;\n    }\n\n    // Convert string representation to sockaddr_storage.\n    ret = string_to_ip(address, &ss);\n    if (ret) {\n        return ret;\n    }\n\n    // Determine address type and length.\n    if (ss.ss_family == AF_INET) {\n        struct sockaddr_in *sin = (struct sockaddr_in *) &ss;\n        addr = &sin->sin_addr;\n        addrlen = INET_ADDRLEN;\n    } else if (ss.ss_family == AF_INET6) {\n        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) &ss;\n        addr = &sin6->sin6_addr;\n        addrlen = INET6_ADDRLEN;\n    } else {\n        return -EAFNOSUPPORT;\n    }\n\n    // Fill in netlink structures.\n    memset(&req, 0, sizeof(req));\n\n    // Netlink message header.\n    req.n.nlmsg_len = NLMSG_LENGTH(sizeof(req.r));\n    req.n.nlmsg_type = action;\n    req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;\n    req.n.nlmsg_pid = getpid();\n\n    // Interface address message header.\n    req.r.ifa_family = ss.ss_family;\n    req.r.ifa_flags = nodad ? IFA_F_NODAD : 0;\n    req.r.ifa_prefixlen = prefixlen;\n    req.r.ifa_index = ifindex;\n\n    // Routing attribute. Contains the actual IP address.\n    rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len));\n    rta->rta_type = IFA_LOCAL;\n    rta->rta_len = RTA_LENGTH(addrlen);\n    req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);\n    memcpy(RTA_DATA(rta), addr, addrlen);\n\n    // Add an explicit IFA_BROADCAST for IPv4 RTM_NEWADDRs.\n    if (ss.ss_family == AF_INET && action == RTM_NEWADDR) {\n        rta = (struct rtattr *) (((char *) &req) + NLMSG_ALIGN(req.n.nlmsg_len));\n        rta->rta_type = IFA_BROADCAST;\n        rta->rta_len = RTA_LENGTH(addrlen);\n        req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len) + RTA_LENGTH(addrlen);\n        ((struct in_addr *)addr)->s_addr |= htonl((1<<(32-prefixlen))-1);\n        memcpy(RTA_DATA(rta), addr, addrlen);\n    }\n\n    s = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);\n    if (s < 0) {\n        return -errno;\n    }\n\n    if (send(s, &req, req.n.nlmsg_len, 0) < 0) {\n        saved_errno = errno;\n        close(s);\n        return -saved_errno;\n    }\n\n    char buf[NLMSG_ALIGN(sizeof(struct nlmsgerr)) + sizeof(req)];\n    len = recv(s, buf, sizeof(buf), 0);\n    saved_errno = errno;\n    close(s);\n    if (len < 0) {\n        return -saved_errno;\n    }\n\n    // Parse the acknowledgement to find the return code.\n    nh = (struct nlmsghdr *) buf;\n    if (!NLMSG_OK(nh, (unsigned) len) || nh->nlmsg_type != NLMSG_ERROR) {\n        return -EINVAL;\n    }\n    err = NLMSG_DATA(nh);\n\n    // Return code is negative errno.\n    return err->error;\n}\n\n// Pass bitwise complement of prefix length to disable DAD, ie. use ~64 instead of 64.\n// Returns zero on success and negative errno on failure.\nint ifc_add_address(const char *name, const char *address, int prefixlen) {\n    bool nodad = (prefixlen < 0);\n    if (nodad) prefixlen = ~prefixlen;\n    return ifc_act_on_address(RTM_NEWADDR, name, address, prefixlen, nodad);\n}\n\n// Returns zero on success and negative errno on failure.\nint ifc_del_address(const char *name, const char * address, int prefixlen) {\n    bool nodad = (prefixlen < 0);\n    if (nodad) prefixlen = ~prefixlen;\n    return ifc_act_on_address(RTM_DELADDR, name, address, prefixlen, nodad);\n}\n\n/*\n * Clears IPv6 addresses on the specified interface.\n */\nint ifc_clear_ipv6_addresses(const char *name) {\n    char rawaddrstr[INET6_ADDRSTRLEN], addrstr[INET6_ADDRSTRLEN];\n    unsigned int prefixlen;\n    int lasterror = 0, i, j, ret;\n    char ifname[64];  // Currently, IFNAMSIZ = 16.\n    FILE *f = fopen(\"/proc/net/if_inet6\", \"r\");\n    if (!f) {\n        return -errno;\n    }\n\n    // Format:\n    // 20010db8000a0001fc446aa4b5b347ed 03 40 00 01    wlan0\n    while (fscanf(f, \"%32s %*02x %02x %*02x %*02x %63s\\n\",\n                  rawaddrstr, &prefixlen, ifname) == 3) {\n        // Is this the interface we're looking for?\n        if (strcmp(name, ifname)) {\n            continue;\n        }\n\n        // Put the colons back into the address.\n        for (i = 0, j = 0; i < 32; i++, j++) {\n            addrstr[j] = rawaddrstr[i];\n            if (i % 4 == 3) {\n                addrstr[++j] = ':';\n            }\n        }\n        addrstr[j - 1] = '\\0';\n\n        // Don't delete the link-local address as well, or it will disable IPv6\n        // on the interface.\n        if (strncmp(addrstr, \"fe80:\", 5) == 0) {\n            continue;\n        }\n\n        ret = ifc_del_address(ifname, addrstr, prefixlen);\n        if (ret) {\n            ALOGE(\"Deleting address %s/%d on %s: %s\", addrstr, prefixlen, ifname,\n                 strerror(-ret));\n            lasterror = ret;\n        }\n    }\n\n    fclose(f);\n    return lasterror;\n}\n\n/*\n * Clears IPv4 addresses on the specified interface.\n */\nvoid ifc_clear_ipv4_addresses(const char *name) {\n    unsigned count, addr;\n    ifc_init();\n    for (count=0, addr=1;((addr != 0) && (count < 255)); count++) {\n        if (ifc_get_addr(name, &addr) < 0)\n            break;\n        if (addr)\n            ifc_set_addr(name, 0);\n    }\n    ifc_close();\n}\n\n/*\n * Clears all IP addresses on the specified interface.\n */\nint ifc_clear_addresses(const char *name) {\n    ifc_clear_ipv4_addresses(name);\n    return ifc_clear_ipv6_addresses(name);\n}\n\nint ifc_set_hwaddr(const char *name, const void *ptr)\n{\n    struct ifreq ifr;\n    ifc_init_ifr(name, &ifr);\n\n    ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;\n    memcpy(&ifr.ifr_hwaddr.sa_data, ptr, ETH_ALEN);\n    return ioctl(ifc_ctl_sock, SIOCSIFHWADDR, &ifr);\n}\n\nint ifc_set_mask(const char *name, in_addr_t mask)\n{\n    struct ifreq ifr;\n    int ret;\n\n    ifc_init_ifr(name, &ifr);\n    init_sockaddr_in(&ifr.ifr_addr, mask);\n\n    ret = ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);\n    if (DBG) printerr(\"ifc_set_mask(%s, xx) = %d\", name, ret);\n    return ret;\n}\n\nint ifc_set_prefixLength(const char *name, int prefixLength)\n{\n    struct ifreq ifr;\n    // TODO - support ipv6\n    if (prefixLength > 32 || prefixLength < 0) return -1;\n\n    in_addr_t mask = prefixLengthToIpv4Netmask(prefixLength);\n    ifc_init_ifr(name, &ifr);\n    init_sockaddr_in(&ifr.ifr_addr, mask);\n\n    return ioctl(ifc_ctl_sock, SIOCSIFNETMASK, &ifr);\n}\n\nint ifc_get_addr(const char *name, in_addr_t *addr)\n{\n    struct ifreq ifr;\n    int ret = 0;\n\n    ifc_init_ifr(name, &ifr);\n    if (addr != NULL) {\n        ret = ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr);\n        if (ret < 0) {\n            *addr = 0;\n        } else {\n            *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;\n        }\n    }\n    return ret;\n}\n\nint ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength, unsigned *flags)\n{\n    struct ifreq ifr;\n    ifc_init_ifr(name, &ifr);\n\n    if (addr != NULL) {\n        if(ioctl(ifc_ctl_sock, SIOCGIFADDR, &ifr) < 0) {\n            *addr = 0;\n        } else {\n            *addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;\n        }\n    }\n\n    if (prefixLength != NULL) {\n        if(ioctl(ifc_ctl_sock, SIOCGIFNETMASK, &ifr) < 0) {\n            *prefixLength = 0;\n        } else {\n            *prefixLength = ipv4NetmaskToPrefixLength(\n                    ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr);\n        }\n    }\n\n    if (flags != NULL) {\n        if(ioctl(ifc_ctl_sock, SIOCGIFFLAGS, &ifr) < 0) {\n            *flags = 0;\n        } else {\n            *flags = ifr.ifr_flags;\n        }\n    }\n\n    return 0;\n}\n\nint ifc_act_on_ipv4_route(int action, const char *ifname, struct in_addr dst, int prefix_length,\n      struct in_addr gw)\n{\n    struct rtentry rt;\n    int result;\n    in_addr_t netmask;\n\n    memset(&rt, 0, sizeof(rt));\n\n    rt.rt_dst.sa_family = AF_INET;\n    rt.rt_dev = (void*) ifname;\n\n    netmask = prefixLengthToIpv4Netmask(prefix_length);\n    init_sockaddr_in(&rt.rt_genmask, netmask);\n    init_sockaddr_in(&rt.rt_dst, dst.s_addr);\n    rt.rt_flags = RTF_UP;\n\n    if (prefix_length == 32) {\n        rt.rt_flags |= RTF_HOST;\n    }\n\n    if (gw.s_addr != 0) {\n        rt.rt_flags |= RTF_GATEWAY;\n        init_sockaddr_in(&rt.rt_gateway, gw.s_addr);\n    }\n\n    ifc_init();\n\n    if (ifc_ctl_sock < 0) {\n        ifc_close();\n        return -errno;\n    }\n\n    result = ioctl(ifc_ctl_sock, action, &rt);\n    if (result < 0) {\n        if (errno == EEXIST) {\n            result = 0;\n        } else {\n            result = -errno;\n        }\n    }\n    ifc_close();\n    return result;\n}\n\n/* deprecated - v4 only */\nint ifc_create_default_route(const char *name, in_addr_t gw)\n{\n    struct in_addr in_dst, in_gw;\n\n    in_dst.s_addr = 0;\n    in_gw.s_addr = gw;\n\n    int ret = ifc_act_on_ipv4_route(SIOCADDRT, name, in_dst, 0, in_gw);\n    if (DBG) printerr(\"ifc_create_default_route(%s, %d) = %d\", name, gw, ret);\n    return ret;\n}\n\n// Needed by code in hidden partner repositories / branches, so don't delete.\nint ifc_enable(const char *ifname)\n{\n    int result;\n\n    ifc_init();\n    result = ifc_up(ifname);\n    ifc_close();\n    return result;\n}\n\n// Needed by code in hidden partner repositories / branches, so don't delete.\nint ifc_disable(const char *ifname)\n{\n    unsigned addr, count;\n    int result;\n\n    ifc_init();\n    result = ifc_down(ifname);\n\n    ifc_set_addr(ifname, 0);\n    for (count=0, addr=1;((addr != 0) && (count < 255)); count++) {\n       if (ifc_get_addr(ifname, &addr) < 0)\n            break;\n       if (addr)\n          ifc_set_addr(ifname, 0);\n    }\n\n    ifc_close();\n    return result;\n}\n\nint ifc_reset_connections(const char *ifname, const int reset_mask)\n{\n#if defined(__ANDROID__)\n    int result, success;\n    in_addr_t myaddr = 0;\n    struct ifreq ifr;\n    struct in6_ifreq ifr6;\n\n    if (reset_mask & RESET_IPV4_ADDRESSES) {\n        /* IPv4. Clear connections on the IP address. */\n        ifc_init();\n        if (!(reset_mask & RESET_IGNORE_INTERFACE_ADDRESS)) {\n            ifc_get_info(ifname, &myaddr, NULL, NULL);\n        }\n        ifc_init_ifr(ifname, &ifr);\n        init_sockaddr_in(&ifr.ifr_addr, myaddr);\n        result = ioctl(ifc_ctl_sock, SIOCKILLADDR,  &ifr);\n        ifc_close();\n    } else {\n        result = 0;\n    }\n\n    if (reset_mask & RESET_IPV6_ADDRESSES) {\n        /*\n         * IPv6. On Linux, when an interface goes down it loses all its IPv6\n         * addresses, so we don't know which connections belonged to that interface\n         * So we clear all unused IPv6 connections on the device by specifying an\n         * empty IPv6 address.\n         */\n        ifc_init6();\n        // This implicitly specifies an address of ::, i.e., kill all IPv6 sockets.\n        memset(&ifr6, 0, sizeof(ifr6));\n        success = ioctl(ifc_ctl_sock6, SIOCKILLADDR,  &ifr6);\n        if (result == 0) {\n            result = success;\n        }\n        ifc_close6();\n    }\n\n    return result;\n#else\n    return 0;\n#endif\n}\n\n/*\n * Removes the default route for the named interface.\n */\nint ifc_remove_default_route(const char *ifname)\n{\n    struct rtentry rt;\n    int result;\n\n    ifc_init();\n    memset(&rt, 0, sizeof(rt));\n    rt.rt_dev = (void *)ifname;\n    rt.rt_flags = RTF_UP|RTF_GATEWAY;\n    init_sockaddr_in(&rt.rt_dst, 0);\n    if ((result = ioctl(ifc_ctl_sock, SIOCDELRT, &rt)) < 0) {\n        ALOGD(\"failed to remove default route for %s: %s\", ifname, strerror(errno));\n    }\n    ifc_close();\n    return result;\n}\n\nint\nifc_configure(const char *ifname,\n        in_addr_t address,\n        uint32_t prefixLength,\n        in_addr_t gateway,\n        in_addr_t dns1,\n        in_addr_t dns2) {\n\n    char dns_prop_name[PROPERTY_KEY_MAX];\n\n    ifc_init();\n\n    if (ifc_up(ifname)) {\n        printerr(\"failed to turn on interface %s: %s\\n\", ifname, strerror(errno));\n        ifc_close();\n        return -1;\n    }\n    if (ifc_set_addr(ifname, address)) {\n        printerr(\"failed to set ipaddr %s: %s\\n\", ipaddr_to_string(address), strerror(errno));\n        ifc_close();\n        return -1;\n    }\n    if (ifc_set_prefixLength(ifname, prefixLength)) {\n        printerr(\"failed to set prefixLength %d: %s\\n\", prefixLength, strerror(errno));\n        ifc_close();\n        return -1;\n    }\n    if (ifc_create_default_route(ifname, gateway)) {\n        printerr(\"failed to set default route %s: %s\\n\", ipaddr_to_string(gateway), strerror(errno));\n        ifc_close();\n        return -1;\n    }\n\n    ifc_close();\n\n    snprintf(dns_prop_name, sizeof(dns_prop_name), \"net.%s.dns1\", ifname);\n    property_set(dns_prop_name, dns1 ? ipaddr_to_string(dns1) : \"\");\n    snprintf(dns_prop_name, sizeof(dns_prop_name), \"net.%s.dns2\", ifname);\n    property_set(dns_prop_name, dns2 ? ipaddr_to_string(dns2) : \"\");\n\n    return 0;\n}\n"
  },
  {
    "path": "libnetutils/include/netutils/ifc.h",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); \n * you may not use this file except in compliance with the License. \n * You may obtain a copy of the License at \n *\n *     http://www.apache.org/licenses/LICENSE-2.0 \n *\n * Unless required by applicable law or agreed to in writing, software \n * distributed under the License is distributed on an \"AS IS\" BASIS, \n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n * See the License for the specific language governing permissions and \n * limitations under the License.\n */\n\n#ifndef _NETUTILS_IFC_H_\n#define _NETUTILS_IFC_H_\n\n#include <arpa/inet.h>\n#include <stdbool.h>\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\nextern int ifc_init(void);\nextern void ifc_close(void);\n\nextern int ifc_get_ifindex(const char *name, int *if_indexp);\nextern int ifc_get_hwaddr(const char *name, void *ptr);\n\nextern int ifc_up(const char *name);\nextern int ifc_down(const char *name);\n\nextern int ifc_enable(const char *ifname);\nextern int ifc_disable(const char *ifname);\n\n#define RESET_IPV4_ADDRESSES 0x01\n#define RESET_IPV6_ADDRESSES 0x02\n#define RESET_IGNORE_INTERFACE_ADDRESS 0x04\n#define RESET_ALL_ADDRESSES  (RESET_IPV4_ADDRESSES | RESET_IPV6_ADDRESSES)\nextern int ifc_reset_connections(const char *ifname, const int reset_mask);\n\nextern int ifc_get_addr(const char *name, in_addr_t *addr);\nextern int ifc_set_addr(const char *name, in_addr_t addr);\nextern int ifc_act_on_address(int action, const char* name, const char* address, int prefixlen,\n                              bool nodad);\nextern int ifc_add_address(const char *name, const char *address,\n                           int prefixlen);\nextern int ifc_del_address(const char *name, const char *address,\n                           int prefixlen);\nextern int ifc_set_prefixLength(const char *name, int prefixLength);\nextern int ifc_set_hwaddr(const char *name, const void *ptr);\nextern int ifc_clear_addresses(const char *name);\n\nextern int ifc_create_default_route(const char *name, in_addr_t addr);\nextern int ifc_remove_default_route(const char *ifname);\nextern int ifc_get_info(const char *name, in_addr_t *addr, int *prefixLength,\n                        unsigned *flags);\n\nextern int ifc_configure(const char *ifname, in_addr_t address,\n                         uint32_t prefixLength, in_addr_t gateway,\n                         in_addr_t dns1, in_addr_t dns2);\n\nextern in_addr_t prefixLengthToIpv4Netmask(int prefix_length);\n\n__END_DECLS\n\n#endif /* _NETUTILS_IFC_H_ */\n"
  },
  {
    "path": "libnetutils/packet.c",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/uio.h>\n#include <linux/if_ether.h>\n#include <linux/if_packet.h>\n#include <netinet/in.h>\n#include <netinet/ip.h>\n#include <netinet/udp.h>\n#include <unistd.h>\n\n#ifdef ANDROID\n#define LOG_TAG \"DHCP\"\n#include <log/log.h>\n#else\n#include <stdio.h>\n#define ALOGD printf\n#define ALOGW printf\n#endif\n\n#include \"dhcpmsg.h\"\n\nint fatal(const char*);\n\nint open_raw_socket(const char* ifname __unused, uint8_t hwaddr[ETH_ALEN], int if_index) {\n    int s = socket(PF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, 0);\n    if (s < 0) return fatal(\"socket(PF_PACKET)\");\n\n    struct sockaddr_ll bindaddr = {\n            .sll_family = AF_PACKET,\n            .sll_protocol = htons(ETH_P_IP),\n            .sll_ifindex = if_index,\n            .sll_halen = ETH_ALEN,\n    };\n    memcpy(bindaddr.sll_addr, hwaddr, ETH_ALEN);\n\n    if (bind(s, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) {\n        close(s);\n        return fatal(\"Cannot bind raw socket to interface\");\n    }\n\n    return s;\n}\n\nstatic uint32_t checksum(void *buffer, unsigned int count, uint32_t startsum)\n{\n    uint16_t *up = (uint16_t *)buffer;\n    uint32_t sum = startsum;\n    uint32_t upper16;\n\n    while (count > 1) {\n        sum += *up++;\n        count -= 2;\n    }\n    if (count > 0) {\n        sum += (uint16_t) *(uint8_t *)up;\n    }\n    while ((upper16 = (sum >> 16)) != 0) {\n        sum = (sum & 0xffff) + upper16;\n    }\n    return sum;\n}\n\nstatic uint32_t finish_sum(uint32_t sum)\n{\n    return ~sum & 0xffff;\n}\n\nint send_packet(int s, int if_index, struct dhcp_msg *msg, int size,\n                uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport)\n{\n    struct iphdr ip;\n    struct udphdr udp;\n    struct iovec iov[3];\n    uint32_t udpsum;\n    uint16_t temp;\n    struct msghdr msghdr;\n    struct sockaddr_ll destaddr;\n\n    ip.version = IPVERSION;\n    ip.ihl = sizeof(ip) >> 2;\n    ip.tos = 0;\n    ip.tot_len = htons(sizeof(ip) + sizeof(udp) + size);\n    ip.id = 0;\n    ip.frag_off = 0;\n    ip.ttl = IPDEFTTL;\n    ip.protocol = IPPROTO_UDP;\n    ip.check = 0;\n    ip.saddr = saddr;\n    ip.daddr = daddr;\n    ip.check = finish_sum(checksum(&ip, sizeof(ip), 0));\n\n    udp.source = htons(sport);\n    udp.dest = htons(dport);\n    udp.len = htons(sizeof(udp) + size);\n    udp.check = 0;\n\n    /* Calculate checksum for pseudo header */\n    udpsum = checksum(&ip.saddr, sizeof(ip.saddr), 0);\n    udpsum = checksum(&ip.daddr, sizeof(ip.daddr), udpsum);\n    temp = htons(IPPROTO_UDP);\n    udpsum = checksum(&temp, sizeof(temp), udpsum);\n    temp = udp.len;\n    udpsum = checksum(&temp, sizeof(temp), udpsum);\n\n    /* Add in the checksum for the udp header */\n    udpsum = checksum(&udp, sizeof(udp), udpsum);\n\n    /* Add in the checksum for the data */\n    udpsum = checksum(msg, size, udpsum);\n    udp.check = finish_sum(udpsum);\n\n    iov[0].iov_base = (char *)&ip;\n    iov[0].iov_len = sizeof(ip);\n    iov[1].iov_base = (char *)&udp;\n    iov[1].iov_len = sizeof(udp);\n    iov[2].iov_base = (char *)msg;\n    iov[2].iov_len = size;\n    memset(&destaddr, 0, sizeof(destaddr));\n    destaddr.sll_family = AF_PACKET;\n    destaddr.sll_protocol = htons(ETH_P_IP);\n    destaddr.sll_ifindex = if_index;\n    destaddr.sll_halen = ETH_ALEN;\n    memcpy(destaddr.sll_addr, \"\\xff\\xff\\xff\\xff\\xff\\xff\", ETH_ALEN);\n\n    msghdr.msg_name = &destaddr;\n    msghdr.msg_namelen = sizeof(destaddr);\n    msghdr.msg_iov = iov;\n    msghdr.msg_iovlen = sizeof(iov) / sizeof(struct iovec);\n    msghdr.msg_flags = 0;\n    msghdr.msg_control = 0;\n    msghdr.msg_controllen = 0;\n    return sendmsg(s, &msghdr, 0);\n}\n\nint receive_packet(int s, struct dhcp_msg *msg)\n{\n    int nread;\n    int is_valid;\n    struct dhcp_packet {\n        struct iphdr ip;\n        struct udphdr udp;\n        struct dhcp_msg dhcp;\n    } packet;\n    int dhcp_size;\n    uint32_t sum;\n    uint16_t temp;\n    uint32_t saddr, daddr;\n\n    nread = read(s, &packet, sizeof(packet));\n    if (nread < 0) {\n        return -1;\n    }\n    /*\n     * The raw packet interface gives us all packets received by the\n     * network interface. We need to filter out all packets that are\n     * not meant for us.\n     */\n    is_valid = 0;\n    if (nread < (int)(sizeof(struct iphdr) + sizeof(struct udphdr))) {\n#if VERBOSE\n        ALOGD(\"Packet is too small (%d) to be a UDP datagram\", nread);\n#endif\n    } else if (packet.ip.version != IPVERSION || packet.ip.ihl != (sizeof(packet.ip) >> 2)) {\n#if VERBOSE\n        ALOGD(\"Not a valid IP packet\");\n#endif\n    } else if (nread < ntohs(packet.ip.tot_len)) {\n#if VERBOSE\n        ALOGD(\"Packet was truncated (read %d, needed %d)\", nread, ntohs(packet.ip.tot_len));\n#endif\n    } else if (packet.ip.protocol != IPPROTO_UDP) {\n#if VERBOSE\n        ALOGD(\"IP protocol (%d) is not UDP\", packet.ip.protocol);\n#endif\n    } else if (packet.udp.dest != htons(PORT_BOOTP_CLIENT)) {\n#if VERBOSE\n        ALOGD(\"UDP dest port (%d) is not DHCP client\", ntohs(packet.udp.dest));\n#endif\n    } else {\n        is_valid = 1;\n    }\n\n    if (!is_valid) {\n        return -1;\n    }\n\n    /* Seems like it's probably a valid DHCP packet */\n    /* validate IP header checksum */\n    sum = finish_sum(checksum(&packet.ip, sizeof(packet.ip), 0));\n    if (sum != 0) {\n        ALOGW(\"IP header checksum failure (0x%x)\", packet.ip.check);\n        return -1;\n    }\n    /*\n     * Validate the UDP checksum.\n     * Since we don't need the IP header anymore, we \"borrow\" it\n     * to construct the pseudo header used in the checksum calculation.\n     */\n    dhcp_size = ntohs(packet.udp.len) - sizeof(packet.udp);\n    /*\n     * check validity of dhcp_size.\n     * 1) cannot be negative or zero.\n     * 2) src buffer contains enough bytes to copy\n     * 3) cannot exceed destination buffer\n     */\n    if ((dhcp_size <= 0) ||\n        ((int)(nread - sizeof(struct iphdr) - sizeof(struct udphdr)) < dhcp_size) ||\n        ((int)sizeof(struct dhcp_msg) < dhcp_size)) {\n#if VERBOSE\n        ALOGD(\"Malformed Packet\");\n#endif\n        return -1;\n    }\n    saddr = packet.ip.saddr;\n    daddr = packet.ip.daddr;\n    nread = ntohs(packet.ip.tot_len);\n    memset(&packet.ip, 0, sizeof(packet.ip));\n    packet.ip.saddr = saddr;\n    packet.ip.daddr = daddr;\n    packet.ip.protocol = IPPROTO_UDP;\n    packet.ip.tot_len = packet.udp.len;\n    temp = packet.udp.check;\n    packet.udp.check = 0;\n    sum = finish_sum(checksum(&packet, nread, 0));\n    packet.udp.check = temp;\n    if (!sum)\n        sum = finish_sum(sum);\n    if (temp != sum) {\n        ALOGW(\"UDP header checksum failure (0x%x should be 0x%x)\", sum, temp);\n        return -1;\n    }\n    memcpy(msg, &packet.dhcp, dhcp_size);\n    return dhcp_size;\n}\n"
  },
  {
    "path": "libnetutils/packet.h",
    "content": "/*\n * Copyright 2008, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); \n * you may not use this file except in compliance with the License. \n * You may obtain a copy of the License at \n *\n *     http://www.apache.org/licenses/LICENSE-2.0 \n *\n * Unless required by applicable law or agreed to in writing, software \n * distributed under the License is distributed on an \"AS IS\" BASIS, \n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. \n * See the License for the specific language governing permissions and \n * limitations under the License.\n */\n\n#ifndef _WIFI_PACKET_H_\n#define _WIFI_PACKET_H_\n\n#include <linux/if_ether.h>\n\nint open_raw_socket(const char* ifname, uint8_t hwaddr[ETH_ALEN], int if_index);\nint send_packet(int s, int if_index, struct dhcp_msg *msg, int size,\n                uint32_t saddr, uint32_t daddr, uint32_t sport, uint32_t dport);\nint receive_packet(int s, struct dhcp_msg *msg);\n\n#endif\n"
  },
  {
    "path": "libpackagelistparser/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libpackagelistparser\",\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    srcs: [\"packagelistparser.cpp\"],\n    shared_libs: [\"liblog\"],\n    local_include_dirs: [\"include\"],\n    export_include_dirs: [\"include\"],\n\n    sanitize: {\n        misc_undefined: [\"integer\"],\n    },\n}\n\ncc_test {\n    name: \"libpackagelistparser_test\",\n    srcs: [\"packagelistparser_test.cpp\"],\n    shared_libs: [\n        \"libbase\",\n        \"libpackagelistparser\",\n    ],\n    test_suites: [\"device-tests\"],\n    require_root: true,\n}\n"
  },
  {
    "path": "libpackagelistparser/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"libpackagelistparser_test\"\n    }\n  ],\n  \"hwasan-presubmit\": [\n    {\n      \"name\": \"libpackagelistparser_test\"\n    }\n  ]\n}\n"
  },
  {
    "path": "libpackagelistparser/include/packagelistparser/packagelistparser.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdbool.h>\n#include <sys/types.h>\n\n__BEGIN_DECLS\n\ntypedef struct gid_list {\n  /** Number of gids. */\n  size_t cnt;\n\n  /** Array of gids. */\n  gid_t* gids;\n} gid_list;\n\ntypedef struct pkg_info {\n  /** Package name like \"com.android.blah\". */\n  char* name;\n\n  /**\n   * Package uid like 10014.\n   * Note that apexes and SDK libraries may have a bogus 0xffffffff value.\n   */\n  uid_t uid;\n\n  /** Package's AndroidManifest.xml debuggable flag. */\n  bool debuggable;\n\n  /** Package data directory like \"/data/user/0/com.android.blah\" */\n  char* data_dir;\n\n  /** Package SELinux info. */\n  char* seinfo;\n\n  /** Package's list of gids. */\n  gid_list gids;\n\n  /** Spare pointer for the caller to stash extra data off. */\n  void* private_data;\n\n  /** Package's AndroidManifest.xml profileable flag. */\n  bool profileable_from_shell;\n\n  /** Package's AndroidManifest.xml version code. */\n  long version_code;\n} pkg_info;\n\n/**\n * Parses the system's default package list.\n * Invokes `callback` once for each package.\n * The callback owns the `pkg_info*` and should call packagelist_free().\n * The callback should return `false` to exit early or `true` to continue.\n */\nbool packagelist_parse(bool (*callback)(pkg_info* info, void* user_data), void* user_data);\n\n/**\n * Parses the given package list.\n * Invokes `callback` once for each package.\n * The callback owns the `pkg_info*` and should call packagelist_free().\n * The callback should return `false` to exit early or `true` to continue.\n */\nbool packagelist_parse_file(const char* path, bool (*callback)(pkg_info* info, void* user_data),\n                            void* user_data);\n\n/** Frees the given `pkg_info`. */\nvoid packagelist_free(pkg_info* info);\n\n__END_DECLS\n"
  },
  {
    "path": "libpackagelistparser/packagelistparser.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"packagelistparser\"\n\n#include <packagelistparser/packagelistparser.h>\n\n#include <errno.h>\n#include <inttypes.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/limits.h>\n\n#include <memory>\n\n#include <log/log.h>\n\nstatic bool parse_gids(const char* path, size_t line_number, const char* gids, pkg_info* info) {\n  // Nothing to do?\n  if (!gids || !strcmp(gids, \"none\")) return true;\n\n  // How much space do we need?\n  info->gids.cnt = 1;\n  for (const char* p = gids; *p; ++p) {\n    if (*p == ',') ++info->gids.cnt;\n  }\n\n  // Allocate the space.\n  info->gids.gids = new gid_t[info->gids.cnt];\n  if (!info->gids.gids) return false;\n\n  // And parse the individual gids.\n  size_t i = 0;\n  while (true) {\n    char* end;\n    unsigned long gid = strtoul(gids, &end, 10);\n    if (gid > GID_MAX) {\n      ALOGE(\"%s:%zu: gid %lu > GID_MAX\", path, line_number, gid);\n      return false;\n    }\n\n    if (i >= info->gids.cnt) return false;\n    info->gids.gids[i++] = gid;\n\n    if (*end == '\\0') return true;\n    if (*end != ',') return false;\n    gids = end + 1;\n  }\n  return true;\n}\n\nstatic bool parse_line(const char* path, size_t line_number, const char* line, pkg_info* info) {\n  int debuggable;\n  char* gid_list;\n  int profileable_from_shell = 0;\n  int fields =\n      sscanf(line, \"%ms %u %d %ms %ms %ms %d %ld\", &info->name, &info->uid,\n             &debuggable, &info->data_dir, &info->seinfo, &gid_list,\n             &profileable_from_shell, &info->version_code);\n\n  // Handle the more complicated gids field and free the temporary string.\n  bool gids_okay = parse_gids(path, line_number, gid_list, info);\n  free(gid_list);\n  if (!gids_okay) return false;\n\n  // Did we see enough fields to be getting on with?\n  // The final fields are optional (and not usually present).\n  if (fields < 6) {\n    ALOGE(\"%s:%zu: too few fields in line\", path, line_number);\n    return false;\n  }\n\n  // Convert integers to bools.\n  info->debuggable = debuggable;\n  info->profileable_from_shell = profileable_from_shell;\n\n  return true;\n}\n\nbool packagelist_parse_file(const char* path, bool (*callback)(pkg_info*, void*), void* user_data) {\n  std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(path, \"re\"), &fclose);\n  if (!fp) {\n    ALOGE(\"couldn't open '%s': %s\", path, strerror(errno));\n    return false;\n  }\n\n  size_t line_number = 0;\n  char* line = nullptr;\n  size_t allocated_length = 0;\n  while (getline(&line, &allocated_length, fp.get()) > 0) {\n    ++line_number;\n    std::unique_ptr<pkg_info, decltype(&packagelist_free)> info(\n        static_cast<pkg_info*>(calloc(1, sizeof(pkg_info))), &packagelist_free);\n    if (!info) {\n      ALOGE(\"%s:%zu: couldn't allocate pkg_info\", path, line_number);\n      return false;\n    }\n\n    if (!parse_line(path, line_number, line, info.get())) return false;\n\n    if (!callback(info.release(), user_data)) break;\n  }\n  free(line);\n  return true;\n}\n\nbool packagelist_parse(bool (*callback)(pkg_info*, void*), void* user_data) {\n  return packagelist_parse_file(\"/data/system/packages.list\", callback, user_data);\n}\n\nvoid packagelist_free(pkg_info* info) {\n  if (!info) return;\n\n  free(info->name);\n  free(info->data_dir);\n  free(info->seinfo);\n  delete[] info->gids.gids;\n  free(info);\n}\n"
  },
  {
    "path": "libpackagelistparser/packagelistparser_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <packagelistparser/packagelistparser.h>\n\n#include <memory>\n\n#include <android-base/file.h>\n\n#include <gtest/gtest.h>\n\nTEST(packagelistparser, smoke) {\n  TemporaryFile tf;\n  android::base::WriteStringToFile(\n      // No gids.\n      \"com.test.a0 10014 0 /data/user/0/com.test.a0 platform:privapp:targetSdkVersion=19 none\\n\"\n      // One gid.\n      \"com.test.a1 10007 1 /data/user/0/com.test.a1 platform:privapp:targetSdkVersion=21 1023\\n\"\n      // Multiple gids.\n      \"com.test.a2 10011 0 /data/user/0/com.test.a2 media:privapp:targetSdkVersion=30 \"\n      \"2001,1065,1023,3003,3007,1024\\n\"\n      // The two new fields (profileable flag and version code).\n      \"com.test.a3 10022 0 /data/user/0/com.test.a3 selabel:blah none 1 123\\n\",\n      tf.path);\n\n  std::vector<pkg_info*> packages;\n  packagelist_parse_file(\n      tf.path,\n      [](pkg_info* info, void* user_data) -> bool {\n        reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info);\n        return true;\n      },\n      &packages);\n\n  ASSERT_EQ(4U, packages.size());\n\n  ASSERT_STREQ(\"com.test.a0\", packages[0]->name);\n  ASSERT_EQ(10014, packages[0]->uid);\n  ASSERT_FALSE(packages[0]->debuggable);\n  ASSERT_STREQ(\"/data/user/0/com.test.a0\", packages[0]->data_dir);\n  ASSERT_STREQ(\"platform:privapp:targetSdkVersion=19\", packages[0]->seinfo);\n  ASSERT_EQ(0U, packages[0]->gids.cnt);\n  ASSERT_FALSE(packages[0]->profileable_from_shell);\n  ASSERT_EQ(0, packages[0]->version_code);\n\n  ASSERT_STREQ(\"com.test.a1\", packages[1]->name);\n  ASSERT_EQ(10007, packages[1]->uid);\n  ASSERT_TRUE(packages[1]->debuggable);\n  ASSERT_STREQ(\"/data/user/0/com.test.a1\", packages[1]->data_dir);\n  ASSERT_STREQ(\"platform:privapp:targetSdkVersion=21\", packages[1]->seinfo);\n  ASSERT_EQ(1U, packages[1]->gids.cnt);\n  ASSERT_EQ(1023U, packages[1]->gids.gids[0]);\n  ASSERT_FALSE(packages[0]->profileable_from_shell);\n  ASSERT_EQ(0, packages[0]->version_code);\n\n  ASSERT_STREQ(\"com.test.a2\", packages[2]->name);\n  ASSERT_EQ(10011, packages[2]->uid);\n  ASSERT_FALSE(packages[2]->debuggable);\n  ASSERT_STREQ(\"/data/user/0/com.test.a2\", packages[2]->data_dir);\n  ASSERT_STREQ(\"media:privapp:targetSdkVersion=30\", packages[2]->seinfo);\n  ASSERT_EQ(6U, packages[2]->gids.cnt);\n  ASSERT_EQ(2001U, packages[2]->gids.gids[0]);\n  ASSERT_EQ(1024U, packages[2]->gids.gids[5]);\n  ASSERT_FALSE(packages[0]->profileable_from_shell);\n  ASSERT_EQ(0, packages[0]->version_code);\n\n  ASSERT_STREQ(\"com.test.a3\", packages[3]->name);\n  ASSERT_EQ(10022, packages[3]->uid);\n  ASSERT_FALSE(packages[3]->debuggable);\n  ASSERT_STREQ(\"/data/user/0/com.test.a3\", packages[3]->data_dir);\n  ASSERT_STREQ(\"selabel:blah\", packages[3]->seinfo);\n  ASSERT_EQ(0U, packages[3]->gids.cnt);\n  ASSERT_TRUE(packages[3]->profileable_from_shell);\n  ASSERT_EQ(123, packages[3]->version_code);\n\n  for (auto& package : packages) packagelist_free(package);\n}\n\nTEST(packagelistparser, early_exit) {\n  TemporaryFile tf;\n  android::base::WriteStringToFile(\n      \"com.test.a0 1 0 / a none\\n\"\n      \"com.test.a1 1 0 / a none\\n\"\n      \"com.test.a2 1 0 / a none\\n\",\n      tf.path);\n\n  std::vector<pkg_info*> packages;\n  packagelist_parse_file(\n      tf.path,\n      [](pkg_info* info, void* user_data) -> bool {\n        std::vector<pkg_info*>* p = reinterpret_cast<std::vector<pkg_info*>*>(user_data);\n        p->push_back(info);\n        return p->size() < 2;\n      },\n      &packages);\n\n  ASSERT_EQ(2U, packages.size());\n\n  ASSERT_STREQ(\"com.test.a0\", packages[0]->name);\n  ASSERT_STREQ(\"com.test.a1\", packages[1]->name);\n\n  for (auto& package : packages) packagelist_free(package);\n}\n\nTEST(packagelistparser, system_package_list) {\n  // Check that we can actually read the packages.list installed on the device.\n  std::vector<pkg_info*> packages;\n  packagelist_parse(\n      [](pkg_info* info, void* user_data) -> bool {\n        reinterpret_cast<std::vector<pkg_info*>*>(user_data)->push_back(info);\n        return true;\n      },\n      &packages);\n  // Not much we can say for sure about what we expect, other than that there\n  // are likely to be lots of packages...\n  ASSERT_GT(packages.size(), 10U);\n}\n\nTEST(packagelistparser, packagelist_free_nullptr) {\n  packagelist_free(nullptr);\n}\n"
  },
  {
    "path": "libprocessgroup/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nsoong_config_module_type {\n    name: \"libprocessgroup_flag_aware_cc_defaults\",\n    module_type: \"cc_defaults\",\n    config_namespace: \"ANDROID\",\n    bool_variables: [\n        \"cgroup_v2_sys_app_isolation\",\n    ],\n    properties: [\n        \"cflags\",\n    ],\n}\n\nlibprocessgroup_flag_aware_cc_defaults {\n    name: \"libprocessgroup_build_flags_cc\",\n    cpp_std: \"gnu++23\",\n    soong_config_variables: {\n        cgroup_v2_sys_app_isolation: {\n            cflags: [\n                \"-DCGROUP_V2_SYS_APP_ISOLATION=true\",\n            ],\n        },\n    },\n}\n\ncc_library_headers {\n    name: \"libprocessgroup_headers\",\n    vendor_available: true,\n    product_available: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    host_supported: true,\n    native_bridge_supported: true,\n    export_include_dirs: [\"include\"],\n    target: {\n        linux_bionic: {\n            enabled: true,\n        },\n        windows: {\n            enabled: true,\n        },\n    },\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n}\n\ncc_library {\n    srcs: [\n        \"cgroup_map.cpp\",\n        \"processgroup.cpp\",\n        \"sched_policy.cpp\",\n        \"task_profiles.cpp\",\n    ],\n    name: \"libprocessgroup\",\n    host_supported: true,\n    native_bridge_supported: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    vendor_available: true,\n    product_available: true,\n    double_loadable: true,\n    shared_libs: [\n        \"libbase\",\n    ],\n    static_libs: [\n        \"libjsoncpp\",\n        \"libprocessgroup_util\",\n    ],\n    // for cutils/android_filesystem_config.h\n    header_libs: [\n        \"libcutils_headers\",\n        \"libprocessgroup_headers\",\n    ],\n    export_include_dirs: [\"include\"],\n    export_header_lib_headers: [\n        \"libprocessgroup_headers\",\n    ],\n    defaults: [\"libprocessgroup_build_flags_cc\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"29\",\n}\n\ncc_test {\n    name: \"task_profiles_test\",\n    host_supported: true,\n    defaults: [\"libprocessgroup_build_flags_cc\"],\n    srcs: [\n        \"task_profiles_test.cpp\",\n    ],\n    header_libs: [\n        \"libcutils_headers\",\n        \"libprocessgroup_headers\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libprocessgroup\",\n    ],\n    static_libs: [\n        \"libgmock\",\n        \"libprocessgroup_util\",\n    ],\n}\n"
  },
  {
    "path": "libprocessgroup/OWNERS",
    "content": "# Bug component: 1293033\nsurenb@google.com\ntjmercier@google.com"
  },
  {
    "path": "libprocessgroup/TEST_MAPPING",
    "content": "{\n  \"presubmit-large\": [\n    {\n      \"name\": \"StagedRollbackTest\"\n    }\n  ]\n}\n"
  },
  {
    "path": "libprocessgroup/build_flags.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#ifndef CGROUP_V2_SYS_APP_ISOLATION\n#define CGROUP_V2_SYS_APP_ISOLATION false\n#endif\n\nnamespace android::libprocessgroup_flags {\n\ninline consteval bool cgroup_v2_sys_app_isolation() {\n    return CGROUP_V2_SYS_APP_ISOLATION;\n}\n\n}  // namespace android::libprocessgroup_flags\n"
  },
  {
    "path": "libprocessgroup/cgroup_map.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//#define LOG_NDEBUG 0\n#define LOG_TAG \"libprocessgroup\"\n\n#include <errno.h>\n#include <unistd.h>\n\n#include <regex>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <cgroup_map.h>\n#include <processgroup/processgroup.h>\n#include <processgroup/util.h>\n\nusing android::base::StringPrintf;\nusing android::base::WriteStringToFile;\n\nstatic constexpr const char* CGROUP_PROCS_FILE = \"/cgroup.procs\";\nstatic constexpr const char* CGROUP_TASKS_FILE = \"/tasks\";\nstatic constexpr const char* CGROUP_TASKS_FILE_V2 = \"/cgroup.threads\";\n\nuint32_t CgroupControllerWrapper::version() const {\n    CHECK(HasValue());\n    return controller_->version();\n}\n\nconst char* CgroupControllerWrapper::name() const {\n    CHECK(HasValue());\n    return controller_->name();\n}\n\nconst char* CgroupControllerWrapper::path() const {\n    CHECK(HasValue());\n    return controller_->path();\n}\n\nbool CgroupControllerWrapper::HasValue() const {\n    return controller_ != nullptr;\n}\n\nbool CgroupControllerWrapper::IsUsable() {\n    if (!HasValue()) return false;\n\n    if (state_ == UNKNOWN) {\n        if (__builtin_available(android 30, *)) {\n            uint32_t flags = controller_->flags();\n            state_ = (flags & CGROUPRC_CONTROLLER_FLAG_MOUNTED) != 0 ? USABLE : MISSING;\n        } else {\n            state_ = access(GetProcsFilePath(\"\", 0, 0).c_str(), F_OK) == 0 ? USABLE : MISSING;\n        }\n    }\n\n    return state_ == USABLE;\n}\n\nstd::string CgroupControllerWrapper::GetTasksFilePath(const std::string& rel_path) const {\n    std::string tasks_path = path();\n\n    if (!rel_path.empty()) {\n        tasks_path += \"/\" + rel_path;\n    }\n    return (version() == 1) ? tasks_path + CGROUP_TASKS_FILE : tasks_path + CGROUP_TASKS_FILE_V2;\n}\n\nstd::string CgroupControllerWrapper::GetProcsFilePath(const std::string& rel_path, uid_t uid,\n                                                      pid_t pid) const {\n    std::string proc_path(path());\n    proc_path.append(\"/\").append(rel_path);\n    proc_path = regex_replace(proc_path, std::regex(\"<uid>\"), std::to_string(uid));\n    proc_path = regex_replace(proc_path, std::regex(\"<pid>\"), std::to_string(pid));\n\n    return proc_path.append(CGROUP_PROCS_FILE);\n}\n\nbool CgroupControllerWrapper::GetTaskGroup(pid_t tid, std::string* group) const {\n    std::string file_name = StringPrintf(\"/proc/%d/cgroup\", tid);\n    std::string content;\n    if (!android::base::ReadFileToString(file_name, &content)) {\n        PLOG(ERROR) << \"Failed to read \" << file_name;\n        return false;\n    }\n\n    // if group is null and tid exists return early because\n    // user is not interested in cgroup membership\n    if (group == nullptr) {\n        return true;\n    }\n\n    std::string cg_tag;\n\n    if (version() == 2) {\n        cg_tag = \"0::\";\n    } else {\n        cg_tag = StringPrintf(\":%s:\", name());\n    }\n    size_t start_pos = content.find(cg_tag);\n    if (start_pos == std::string::npos) {\n        return false;\n    }\n\n    start_pos += cg_tag.length() + 1;  // skip '/'\n    size_t end_pos = content.find('\\n', start_pos);\n    if (end_pos == std::string::npos) {\n        *group = content.substr(start_pos, std::string::npos);\n    } else {\n        *group = content.substr(start_pos, end_pos - start_pos);\n    }\n\n    return true;\n}\n\nCgroupMap::CgroupMap() {\n    if (!LoadDescriptors()) {\n        LOG(ERROR) << \"CgroupMap::LoadDescriptors called for [\" << getpid() << \"] failed\";\n    }\n}\n\nCgroupMap& CgroupMap::GetInstance() {\n    // Deliberately leak this object to avoid a race between destruction on\n    // process exit and concurrent access from another thread.\n    static auto* instance = new CgroupMap;\n    return *instance;\n}\n\nbool CgroupMap::LoadDescriptors() {\n    if (!loaded_) {\n        loaded_ = ReadDescriptors(&descriptors_);\n    }\n    return loaded_;\n}\n\nvoid CgroupMap::Print() const {\n    if (!loaded_) {\n        LOG(ERROR) << \"CgroupMap::Print called for [\" << getpid()\n                   << \"] failed, cgroups were not initialized properly\";\n        return;\n    }\n    LOG(INFO) << \"Controller count = \" << descriptors_.size();\n\n    LOG(INFO) << \"Mounted cgroups:\";\n\n    for (const auto& [name, descriptor] : descriptors_) {\n        LOG(INFO) << \"\\t\" << descriptor.controller()->name() << \" ver \"\n                  << descriptor.controller()->version() << \" path \"\n                  << descriptor.controller()->path() << \" flags \"\n                  << descriptor.controller()->flags();\n    }\n}\n\nCgroupControllerWrapper CgroupMap::FindController(const std::string& name) const {\n    if (!loaded_) {\n        LOG(ERROR) << \"CgroupMap::FindController called for [\" << getpid()\n                   << \"] failed, cgroups were not initialized properly\";\n        return CgroupControllerWrapper(nullptr);\n    }\n\n    if (const auto it = descriptors_.find(name); it != descriptors_.end()) {\n        return CgroupControllerWrapper(it->second.controller());\n    }\n\n    return CgroupControllerWrapper(nullptr);\n}\n\nCgroupControllerWrapper CgroupMap::FindControllerByPath(const std::string& path) const {\n    if (!loaded_) {\n        LOG(ERROR) << \"CgroupMap::FindControllerByPath called for [\" << getpid()\n                   << \"] failed, cgroups were not initialized properly\";\n        return CgroupControllerWrapper(nullptr);\n    }\n\n    for (const auto& [name, descriptor] : descriptors_) {\n        if (path.starts_with(descriptor.controller()->path())) {\n            return CgroupControllerWrapper(descriptor.controller());\n        }\n    }\n\n    return CgroupControllerWrapper(nullptr);\n}\n\nbool CgroupMap::ActivateControllers(const std::string& path) const {\n    return ::ActivateControllers(path, descriptors_);\n}\n"
  },
  {
    "path": "libprocessgroup/cgroup_map.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <cstdint>\n#include <string>\n\n#include <processgroup/cgroup_controller.h>\n#include <processgroup/util.h>\n\n// Convenient wrapper of a CgroupController pointer.\nclass CgroupControllerWrapper {\n  public:\n    // Does not own controller\n    explicit CgroupControllerWrapper(const CgroupController* controller)\n        : controller_(controller) {}\n\n    uint32_t version() const;\n    const char* name() const;\n    const char* path() const;\n\n    bool HasValue() const;\n    bool IsUsable();\n\n    std::string GetTasksFilePath(const std::string& path) const;\n    std::string GetProcsFilePath(const std::string& path, uid_t uid, pid_t pid) const;\n    bool GetTaskGroup(pid_t tid, std::string* group) const;\n\n  private:\n    enum ControllerState {\n        UNKNOWN = 0,\n        USABLE = 1,\n        MISSING = 2,\n    };\n\n    const CgroupController* controller_ = nullptr; // CgroupMap owns the object behind this pointer\n    ControllerState state_ = ControllerState::UNKNOWN;\n};\n\nclass CgroupMap {\n  public:\n    static CgroupMap& GetInstance();\n    CgroupControllerWrapper FindController(const std::string& name) const;\n    CgroupControllerWrapper FindControllerByPath(const std::string& path) const;\n    bool ActivateControllers(const std::string& path) const;\n\n  private:\n    bool loaded_ = false;\n    CgroupDescriptorMap descriptors_;\n    CgroupMap();\n    bool LoadDescriptors();\n    void Print() const;\n};\n"
  },
  {
    "path": "libprocessgroup/cgrouprc/Android.bp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libcgrouprc\",\n    // Do not ever mark this as vendor_available; otherwise, vendor modules\n    // that links to the static library will behave unexpectedly. All on-device\n    // modules should use libprocessgroup which links to the LL-NDK library\n    // defined below. The static library is built for tests.\n    vendor_available: false,\n    native_bridge_supported: true,\n    llndk: {\n        symbol_file: \"libcgrouprc.map.txt\",\n    },\n    srcs: [\n        \"a_cgroup_controller.cpp\",\n        \"a_cgroup_file.cpp\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    export_include_dirs: [\n        \"include\",\n    ],\n    header_libs: [\n        \"libprocessgroup_headers\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    static_libs: [\n        \"libjsoncpp\",\n        \"libprocessgroup_util\",\n    ],\n    stubs: {\n        symbol_file: \"libcgrouprc.map.txt\",\n        versions: [\"29\"],\n    },\n    target: {\n        linux: {\n            version_script: \"libcgrouprc.map.txt\",\n        },\n    },\n}\n"
  },
  {
    "path": "libprocessgroup/cgrouprc/a_cgroup_controller.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n#include <android/cgrouprc.h>\n\n#include \"cgrouprc_internal.h\"\n\n// All ACgroupController_* functions implicitly convert the pointer back\n// to the original CgroupController pointer before invoking the member functions.\n\nuint32_t ACgroupController_getVersion(const ACgroupController* controller) {\n    CHECK(controller != nullptr);\n    return controller->version();\n}\n\nuint32_t ACgroupController_getFlags(const ACgroupController* controller) {\n    CHECK(controller != nullptr);\n    return controller->flags();\n}\n\nconst char* ACgroupController_getName(const ACgroupController* controller) {\n    CHECK(controller != nullptr);\n    return controller->name();\n}\n\nconst char* ACgroupController_getPath(const ACgroupController* controller) {\n    CHECK(controller != nullptr);\n    return controller->path();\n}\n"
  },
  {
    "path": "libprocessgroup/cgrouprc/a_cgroup_file.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <iterator>\n\n#include <android-base/logging.h>\n#include <android/cgrouprc.h>\n#include <processgroup/util.h>\n\n#include \"cgrouprc_internal.h\"\n\nstatic CgroupDescriptorMap* LoadDescriptors() {\n    CgroupDescriptorMap* descriptors = new CgroupDescriptorMap;\n    if (!ReadDescriptors(descriptors)) {\n        LOG(ERROR) << \"Failed to load cgroup description file\";\n        return nullptr;\n    }\n    return descriptors;\n}\n\nstatic const CgroupDescriptorMap* GetInstance() {\n    // Deliberately leak this object (not munmap) to avoid a race between destruction on\n    // process exit and concurrent access from another thread.\n    static const CgroupDescriptorMap* descriptors = LoadDescriptors();\n    return descriptors;\n}\n\nuint32_t ACgroupFile_getVersion() {\n    static constexpr uint32_t FILE_VERSION_1 = 1;\n    auto descriptors = GetInstance();\n    if (descriptors == nullptr) return 0;\n    // There has only ever been one version, and there will be no more since cgroup.rc is no more\n    return FILE_VERSION_1;\n}\n\nuint32_t ACgroupFile_getControllerCount() {\n    auto descriptors = GetInstance();\n    if (descriptors == nullptr) return 0;\n    return descriptors->size();\n}\n\nconst ACgroupController* ACgroupFile_getController(uint32_t index) {\n    auto descriptors = GetInstance();\n    if (descriptors == nullptr) return nullptr;\n    CHECK(index < descriptors->size());\n    // Although the object is not actually an ACgroupController object, all ACgroupController_*\n    // functions implicitly convert ACgroupController* back to CgroupController* before invoking\n    // member functions.\n    const CgroupController* p = std::next(descriptors->begin(), index)->second.controller();\n    return static_cast<const ACgroupController*>(p);\n}\n"
  },
  {
    "path": "libprocessgroup/cgrouprc/cgrouprc_internal.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <processgroup/cgroup_controller.h>\n\nstruct ACgroupController : CgroupController {};\n"
  },
  {
    "path": "libprocessgroup/cgrouprc/include/android/cgrouprc.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/cdefs.h>\n#include <stdint.h>\n\n__BEGIN_DECLS\n\nstruct ACgroupController;\ntypedef struct ACgroupController ACgroupController;\n\n// ACgroupFile\n\n/**\n * Returns file version. See android::cgrouprc::format::CgroupFile for a list of valid versions\n * for the file.\n * If ACgroupFile_init() isn't called, initialization will be done first.\n * If initialization failed, return 0.\n */\n__attribute__((warn_unused_result)) uint32_t ACgroupFile_getVersion() __INTRODUCED_IN(29);\n\n/**\n * Returns the number of controllers.\n * If ACgroupFile_init() isn't called, initialization will be done first.\n * If initialization failed, return 0.\n */\n__attribute__((warn_unused_result)) uint32_t ACgroupFile_getControllerCount() __INTRODUCED_IN(29);\n\n/**\n * Returns the controller at the given index.\n * Returnss nullptr if the given index exceeds getControllerCount().\n * If ACgroupFile_init() isn't called, initialization will be done first.\n * If initialization failed, return 0.\n */\n__attribute__((warn_unused_result)) const ACgroupController* ACgroupFile_getController(\n        uint32_t index) __INTRODUCED_IN(29);\n\n// ACgroupController\n\n/**\n * Returns the version of the given controller.\n * If the given controller is null, return 0.\n */\n__attribute__((warn_unused_result)) uint32_t ACgroupController_getVersion(const ACgroupController*)\n        __INTRODUCED_IN(29);\n\n/**\n * Flag bitmask used in ACgroupController_getFlags\n */\n#define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1\n#define CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION 0x2\n#define CGROUPRC_CONTROLLER_FLAG_OPTIONAL 0x4\n\n/**\n * Returns the flags bitmask of the given controller.\n * If the given controller is null, return 0.\n */\n__attribute__((warn_unused_result, weak)) uint32_t ACgroupController_getFlags(\n        const ACgroupController*) __INTRODUCED_IN(30);\n\n/**\n * Returns the name of the given controller.\n * If the given controller is null, return nullptr.\n */\n__attribute__((warn_unused_result)) const char* ACgroupController_getName(const ACgroupController*)\n        __INTRODUCED_IN(29);\n\n/**\n * Returns the path of the given controller.\n * If the given controller is null, return nullptr.\n */\n__attribute__((warn_unused_result)) const char* ACgroupController_getPath(const ACgroupController*)\n        __INTRODUCED_IN(29);\n\n__END_DECLS\n"
  },
  {
    "path": "libprocessgroup/cgrouprc/libcgrouprc.map.txt",
    "content": "LIBCGROUPRC { # introduced=29\n  global:\n    ACgroupFile_getVersion; # llndk systemapi\n    ACgroupFile_getControllerCount; # llndk systemapi\n    ACgroupFile_getController; # llndk systemapi\n    ACgroupController_getVersion; # llndk systemapi\n    ACgroupController_getName; # llndk systemapi\n    ACgroupController_getPath; # llndk systemapi\n  local:\n    *;\n};\n\nLIBCGROUPRC_30 { # introduced=30\n  global:\n    ACgroupController_getFlags; # llndk systemapi\n  local:\n    *;\n};\n"
  },
  {
    "path": "libprocessgroup/include/processgroup/processgroup.h",
    "content": "/*\n *  Copyright 2014 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n#include <initializer_list>\n#include <span>\n#include <string>\n#include <string_view>\n#include <vector>\n\nstatic constexpr std::string CGROUPV2_HIERARCHY_NAME = \"cgroup2\";\n\nbool CgroupsAvailable();\nbool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path);\nbool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name);\nbool CgroupGetAttributePath(const std::string& attr_name, std::string* path);\n// Provides the path for an attribute in a specific process group\n// Returns false in case of error, true in case of success\nbool CgroupGetAttributePathForTask(const std::string& attr_name, pid_t tid, std::string* path);\nbool CgroupGetAttributePathForProcess(std::string_view attr_name, uid_t uid, pid_t pid,\n                                      std::string &path);\n\nbool SetTaskProfiles(pid_t tid, const std::vector<std::string>& profiles,\n                     bool use_fd_cache = false);\nbool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);\nbool SetUserProfiles(uid_t uid, const std::vector<std::string>& profiles);\n\nbool SetTaskProfiles(pid_t tid, std::initializer_list<std::string_view> profiles,\n                     bool use_fd_cache = false);\nbool SetProcessProfiles(uid_t uid, pid_t pid, std::initializer_list<std::string_view> profiles);\n#if _LIBCPP_STD_VER > 17\nbool SetTaskProfiles(pid_t tid, std::span<const std::string_view> profiles,\n                     bool use_fd_cache = false);\nbool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const std::string_view> profiles);\n#endif\n\n\n#ifndef __ANDROID_VNDK__\n\nbool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles);\n\nbool UsePerAppMemcg();\n\n// Drop the fd cache of cgroup path. It is used for when resource caching is enabled and a process\n// loses the access to the path, the access checking (See SetCgroupAction::EnableResourceCaching)\n// should be active again. E.g. Zygote specialization for child process.\nvoid DropTaskProfilesResourceCaching();\n\n// Return 0 if all processes were killed and the cgroup was successfully removed.\n// Returns -1 in the case of an error occurring or if there are processes still running.\nint killProcessGroup(uid_t uid, pid_t initialPid, int signal);\n\n// Returns the same as killProcessGroup(), however it does not retry, which means\n// that it only returns 0 in the case that the cgroup exists and it contains no processes.\nint killProcessGroupOnce(uid_t uid, pid_t initialPid, int signal);\n\n// Sends the provided signal to all members of a process group, but does not wait for processes to\n// exit, or for the cgroup to be removed. Callers should also ensure that killProcessGroup is called\n// later to ensure the cgroup is fully removed, otherwise system resources will leak.\n// Returns true if no errors are encountered sending signals, otherwise false.\nbool sendSignalToProcessGroup(uid_t uid, pid_t initialPid, int signal);\n\nint createProcessGroup(uid_t uid, pid_t initialPid, bool memControl = false);\n\n// Set various properties of a process group. For these functions to work, the process group must\n// have been created by passing memControl=true to createProcessGroup.\n[[deprecated(\"Unsupported in memcg v2\")]]\nbool setProcessGroupSwappiness(uid_t uid, pid_t initialPid, int swappiness);\nbool setProcessGroupSoftLimit(uid_t uid, pid_t initialPid, int64_t softLimitInBytes);\nbool setProcessGroupLimit(uid_t uid, pid_t initialPid, int64_t limitInBytes);\n\nvoid removeAllEmptyProcessGroups(void);\n\n// Check if a profile can be applied without failing.\n// Returns true if it can be applied without failing, false otherwise\nbool isProfileValidForProcess(const std::string& profile_name, uid_t uid, pid_t pid);\n\n#endif // __ANDROID_VNDK__\n"
  },
  {
    "path": "libprocessgroup/include/processgroup/sched_policy.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n * Check if Linux kernel enables CPUSETS feature.\n *\n * Return value: 1 if Linux kernel CONFIG_CPUSETS=y; 0 otherwise.\n */\nextern bool cpusets_enabled();\n\n/* Keep in sync with THREAD_GROUP_* in frameworks/base/core/java/android/os/Process.java */\ntypedef enum {\n    SP_DEFAULT = -1,\n    SP_BACKGROUND = 0,\n    SP_FOREGROUND = 1,\n    SP_SYSTEM = 2,\n    SP_AUDIO_APP = 3,\n    SP_AUDIO_SYS = 4,\n    SP_TOP_APP = 5,\n    SP_RT_APP = 6,\n    SP_RESTRICTED = 7,\n    SP_FOREGROUND_WINDOW = 8,\n    SP_CNT,\n    SP_MAX = SP_CNT - 1,\n    SP_SYSTEM_DEFAULT = SP_FOREGROUND,\n} SchedPolicy;\n\nextern int set_cpuset_policy(int tid, SchedPolicy policy);\n\n/* Assign thread tid to the cgroup associated with the specified policy.\n * If the thread is a thread group leader, that is it's gettid() == getpid(),\n * then the other threads in the same thread group are _not_ affected.\n * On platforms which support gettid(), zero tid means current thread.\n * Return value: 0 for success, or -errno for error.\n */\nextern int set_sched_policy(int tid, SchedPolicy policy);\n\n/* Return the policy associated with the cgroup of thread tid via policy pointer.\n * On platforms which support gettid(), zero tid means current thread.\n * Return value: 0 for success, or -1 for error and set errno.\n */\nextern int get_sched_policy(int tid, SchedPolicy* policy);\n\n/* Return a displayable string corresponding to policy.\n * Return value: NUL-terminated name of unspecified length, nullptr if invalid;\n * the caller is responsible for displaying the useful part of the string.\n */\nextern const char* get_sched_policy_name(SchedPolicy policy);\n\n/* Return the aggregated task profile name corresponding to cpuset policy.\n * Return value: NUL-terminated name of unspecified length, nullptr if invalid;\n * the caller could use it to call SetTaskProfiles.\n */\nextern const char* get_cpuset_policy_profile_name(SchedPolicy policy);\n\n/* Return the aggregated task profile name corresponding to sched policy.\n * Return value: NUL-terminated name of unspecified length, nullptr if invalid;\n * the caller could use it to call SetTaskProfiles.\n */\nextern const char* get_sched_policy_profile_name(SchedPolicy policy);\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "libprocessgroup/internal.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\nstatic const std::string CGROUP_V2_ROOT_DEFAULT = \"/sys/fs/cgroup\";"
  },
  {
    "path": "libprocessgroup/processgroup.cpp",
    "content": "/*\n *  Copyright 2014 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n//#define LOG_NDEBUG 0\n#define LOG_TAG \"libprocessgroup\"\n\n#include <assert.h>\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <poll.h>\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <cstring>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <set>\n#include <string>\n#include <string_view>\n#include <thread>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <cutils/android_filesystem_config.h>\n#include <processgroup/processgroup.h>\n#include <task_profiles.h>\n\nusing android::base::GetBoolProperty;\nusing android::base::StringPrintf;\nusing android::base::WriteStringToFile;\n\nusing namespace std::chrono_literals;\n\n#define PROCESSGROUP_CGROUP_PROCS_FILE \"cgroup.procs\"\n#define PROCESSGROUP_CGROUP_KILL_FILE \"cgroup.kill\"\n#define PROCESSGROUP_CGROUP_EVENTS_FILE \"cgroup.events\"\n\nbool CgroupsAvailable() {\n    static bool cgroups_available = access(\"/proc/cgroups\", F_OK) == 0;\n    return cgroups_available;\n}\n\nbool CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) {\n    auto controller = CgroupMap::GetInstance().FindController(cgroup_name);\n\n    if (!controller.HasValue()) {\n        return false;\n    }\n\n    if (path) {\n        *path = controller.path();\n    }\n\n    return true;\n}\n\nstatic bool CgroupKillAvailable() {\n    static std::once_flag f;\n    static bool cgroup_kill_available = false;\n    std::call_once(f, []() {\n        std::string cg_kill;\n        CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cg_kill);\n        // cgroup.kill is not on the root cgroup, so check a non-root cgroup that should always\n        // exist\n        cg_kill = ConvertUidToPath(cg_kill.c_str(), AID_ROOT, true) + '/' +\n            PROCESSGROUP_CGROUP_KILL_FILE;\n        cgroup_kill_available = access(cg_kill.c_str(), F_OK) == 0;\n    });\n\n    return cgroup_kill_available;\n}\n\nstatic bool CgroupGetMemcgAppsPath(std::string* path) {\n    CgroupControllerWrapper controller = CgroupMap::GetInstance().FindController(\"memory\");\n\n    if (!controller.HasValue()) {\n        return false;\n    }\n\n    if (path) {\n        *path = controller.path();\n        if (controller.version() == 1) {\n            *path += \"/apps\";\n        }\n    }\n\n    return true;\n}\n\nbool CgroupGetControllerFromPath(const std::string& path, std::string* cgroup_name) {\n    auto controller = CgroupMap::GetInstance().FindControllerByPath(path);\n\n    if (!controller.HasValue()) {\n        return false;\n    }\n\n    if (cgroup_name) {\n        *cgroup_name = controller.name();\n    }\n\n    return true;\n}\n\nbool CgroupGetAttributePath(const std::string& attr_name, std::string* path) {\n    const TaskProfiles& tp = TaskProfiles::GetInstance();\n    const IProfileAttribute* attr = tp.GetAttribute(attr_name);\n\n    if (attr == nullptr) {\n        return false;\n    }\n\n    if (path) {\n        *path = StringPrintf(\"%s/%s\", attr->controller()->path(), attr->file_name().c_str());\n    }\n\n    return true;\n}\n\nbool CgroupGetAttributePathForTask(const std::string& attr_name, pid_t tid, std::string* path) {\n    const TaskProfiles& tp = TaskProfiles::GetInstance();\n    const IProfileAttribute* attr = tp.GetAttribute(attr_name);\n\n    if (attr == nullptr) {\n        return false;\n    }\n\n    if (!attr->GetPathForTask(tid, path)) {\n        LOG(ERROR) << \"Failed to find cgroup for tid \" << tid;\n        return false;\n    }\n\n    return true;\n}\n\nbool CgroupGetAttributePathForProcess(std::string_view attr_name, uid_t uid, pid_t pid,\n                                      std::string &path) {\n    const TaskProfiles& tp = TaskProfiles::GetInstance();\n    const IProfileAttribute* attr = tp.GetAttribute(attr_name);\n\n    if (attr == nullptr) {\n        return false;\n    }\n\n    if (!attr->GetPathForProcess(uid, pid, &path)) {\n        LOG(ERROR) << \"Failed to find cgroup for uid \" << uid << \" pid \" << pid;\n        return false;\n    }\n\n    return true;\n}\n\nbool UsePerAppMemcg() {\n    bool low_ram_device = GetBoolProperty(\"ro.config.low_ram\", false);\n    return GetBoolProperty(\"ro.config.per_app_memcg\", low_ram_device);\n}\n\nstatic bool isMemoryCgroupSupported() {\n    static bool memcg_supported = CgroupMap::GetInstance().FindController(\"memory\").IsUsable();\n\n    return memcg_supported;\n}\n\nvoid DropTaskProfilesResourceCaching() {\n    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_TASK);\n    TaskProfiles::GetInstance().DropResourceCaching(ProfileAction::RCT_PROCESS);\n}\n\nbool SetProcessProfiles(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {\n    return TaskProfiles::GetInstance().SetProcessProfiles(\n            uid, pid, std::span<const std::string>(profiles), false);\n}\n\nbool SetProcessProfiles(uid_t uid, pid_t pid, std::initializer_list<std::string_view> profiles) {\n    return TaskProfiles::GetInstance().SetProcessProfiles(\n            uid, pid, std::span<const std::string_view>(profiles), false);\n}\n\nbool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const std::string_view> profiles) {\n    return TaskProfiles::GetInstance().SetProcessProfiles(uid, pid, profiles, false);\n}\n\nbool SetProcessProfilesCached(uid_t uid, pid_t pid, const std::vector<std::string>& profiles) {\n    return TaskProfiles::GetInstance().SetProcessProfiles(\n            uid, pid, std::span<const std::string>(profiles), true);\n}\n\nbool SetTaskProfiles(pid_t tid, const std::vector<std::string>& profiles, bool use_fd_cache) {\n    return TaskProfiles::GetInstance().SetTaskProfiles(tid, std::span<const std::string>(profiles),\n                                                       use_fd_cache);\n}\n\nbool SetTaskProfiles(pid_t tid, std::initializer_list<std::string_view> profiles,\n                     bool use_fd_cache) {\n    return TaskProfiles::GetInstance().SetTaskProfiles(\n            tid, std::span<const std::string_view>(profiles), use_fd_cache);\n}\n\nbool SetTaskProfiles(pid_t tid, std::span<const std::string_view> profiles, bool use_fd_cache) {\n    return TaskProfiles::GetInstance().SetTaskProfiles(tid, profiles, use_fd_cache);\n}\n\n// C wrapper for SetProcessProfiles.\n// No need to have this in the header file because this function is specifically for crosvm. Crosvm\n// which is written in Rust has its own declaration of this foreign function and doesn't rely on the\n// header. See\n// https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/3574427/5/src/linux/android.rs#12\nextern \"C\" bool android_set_process_profiles(uid_t uid, pid_t pid, size_t num_profiles,\n                                             const char* profiles[]) {\n    std::vector<std::string_view> profiles_;\n    profiles_.reserve(num_profiles);\n    for (size_t i = 0; i < num_profiles; i++) {\n        profiles_.emplace_back(profiles[i]);\n    }\n    return SetProcessProfiles(uid, pid, std::span<const std::string_view>(profiles_));\n}\n\nbool SetUserProfiles(uid_t uid, const std::vector<std::string>& profiles) {\n    return TaskProfiles::GetInstance().SetUserProfiles(uid, std::span<const std::string>(profiles),\n                                                       false);\n}\n\nstatic int RemoveCgroup(const char* cgroup, uid_t uid, pid_t pid, bool v2_path) {\n    auto path = ConvertUidPidToPath(cgroup, uid, pid, v2_path);\n    int ret = TEMP_FAILURE_RETRY(rmdir(path.c_str()));\n\n    if (!ret && uid >= AID_ISOLATED_START && uid <= AID_ISOLATED_END) {\n        // Isolated UIDs are unlikely to be reused soon after removal,\n        // so free up the kernel resources for the UID level cgroup.\n        path = ConvertUidToPath(cgroup, uid, v2_path);\n        ret = TEMP_FAILURE_RETRY(rmdir(path.c_str()));\n    }\n\n    if (ret < 0 && errno == ENOENT) {\n        // This function is idempoetent, but still warn here.\n        LOG(WARNING) << \"RemoveCgroup: \" << path << \" does not exist.\";\n        ret = 0;\n    }\n\n    return ret;\n}\n\nstatic bool RemoveEmptyUidCgroups(const std::string& uid_path) {\n    std::unique_ptr<DIR, decltype(&closedir)> uid(opendir(uid_path.c_str()), closedir);\n    bool empty = true;\n    if (uid != NULL) {\n        dirent* dir;\n        while ((dir = readdir(uid.get())) != nullptr) {\n            if (dir->d_type != DT_DIR) {\n                continue;\n            }\n\n            if (!std::string_view(dir->d_name).starts_with(\"pid_\")) {\n                continue;\n            }\n\n            auto path = StringPrintf(\"%s/%s\", uid_path.c_str(), dir->d_name);\n            LOG(VERBOSE) << \"Removing \" << path;\n            if (rmdir(path.c_str()) == -1) {\n                if (errno != EBUSY) {\n                    PLOG(WARNING) << \"Failed to remove \" << path;\n                }\n                empty = false;\n            }\n        }\n    }\n    return empty;\n}\n\nvoid removeAllEmptyProcessGroups() {\n    LOG(VERBOSE) << \"removeAllEmptyProcessGroups()\";\n\n    std::vector<std::string> cgroups;\n    std::string path, memcg_apps_path;\n\n    if (CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &path)) {\n        cgroups.push_back(path);\n    }\n    if (CgroupGetMemcgAppsPath(&memcg_apps_path) && memcg_apps_path != path) {\n        cgroups.push_back(memcg_apps_path);\n    }\n\n    for (std::string cgroup_root_path : cgroups) {\n        std::unique_ptr<DIR, decltype(&closedir)> root(opendir(cgroup_root_path.c_str()), closedir);\n        if (root == NULL) {\n            PLOG(ERROR) << __func__ << \" failed to open \" << cgroup_root_path;\n        } else {\n            dirent* dir;\n            while ((dir = readdir(root.get())) != nullptr) {\n                if (dir->d_type != DT_DIR) {\n                    continue;\n                }\n\n                if (!std::string_view(dir->d_name).starts_with(\"uid_\")) {\n                    continue;\n                }\n\n                auto path = StringPrintf(\"%s/%s\", cgroup_root_path.c_str(), dir->d_name);\n                if (!RemoveEmptyUidCgroups(path)) {\n                    LOG(VERBOSE) << \"Skip removing \" << path;\n                    continue;\n                }\n                LOG(VERBOSE) << \"Removing \" << path;\n                if (rmdir(path.c_str()) == -1 && errno != EBUSY) {\n                    PLOG(WARNING) << \"Failed to remove \" << path;\n                }\n            }\n        }\n    }\n}\n\n/**\n * Process groups are primarily created by the Zygote, meaning that uid/pid groups are created by\n * the user root. Ownership for the newly created cgroup and all of its files must thus be\n * transferred for the user/group passed as uid/gid before system_server can properly access them.\n */\nstatic bool MkdirAndChown(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {\n    if (mkdir(path.c_str(), mode) == -1) {\n        if (errno == EEXIST) {\n            // Directory already exists and permissions have been set at the time it was created\n            return true;\n        }\n        return false;\n    }\n\n    auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);\n\n    if (dir == NULL) {\n        PLOG(ERROR) << \"opendir failed for \" << path;\n        goto err;\n    }\n\n    struct dirent* dir_entry;\n    while ((dir_entry = readdir(dir.get()))) {\n        if (!strcmp(\"..\", dir_entry->d_name)) {\n            continue;\n        }\n\n        std::string file_path = path + \"/\" + dir_entry->d_name;\n\n        if (lchown(file_path.c_str(), uid, gid) < 0) {\n            PLOG(ERROR) << \"lchown failed for \" << file_path;\n            goto err;\n        }\n\n        if (fchmodat(AT_FDCWD, file_path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0) {\n            PLOG(ERROR) << \"fchmodat failed for \" << file_path;\n            goto err;\n        }\n    }\n\n    return true;\nerr:\n    int saved_errno = errno;\n    rmdir(path.c_str());\n    errno = saved_errno;\n\n    return false;\n}\n\nbool sendSignalToProcessGroup(uid_t uid, pid_t initialPid, int signal) {\n    std::set<pid_t> pgids, pids;\n\n    if (CgroupsAvailable()) {\n        std::string hierarchy_root_path, cgroup_v2_path;\n        CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);\n        cgroup_v2_path = ConvertUidPidToPath(hierarchy_root_path.c_str(), uid, initialPid, true);\n\n        if (signal == SIGKILL && CgroupKillAvailable()) {\n            LOG(VERBOSE) << \"Using \" << PROCESSGROUP_CGROUP_KILL_FILE << \" to SIGKILL \"\n                         << cgroup_v2_path;\n\n            // We need to kill the process group in addition to the cgroup. For normal apps they\n            // should completely overlap, but system_server kills depend on process group kills to\n            // take down apps which are in their own cgroups and not individually targeted.\n            if (kill(-initialPid, signal) == -1 && errno != ESRCH) {\n                PLOG(WARNING) << \"kill(\" << -initialPid << \", \" << signal << \") failed\";\n            }\n\n            const std::string killfilepath = cgroup_v2_path + '/' + PROCESSGROUP_CGROUP_KILL_FILE;\n            if (WriteStringToFile(\"1\", killfilepath)) {\n                return true;\n            } else {\n                PLOG(ERROR) << \"Failed to write 1 to \" << killfilepath;\n                // Fallback to cgroup.procs below\n            }\n        }\n\n        // Since cgroup.kill only sends SIGKILLs, we read cgroup.procs to find each process to\n        // signal individually. This is more costly than using cgroup.kill for SIGKILLs.\n        LOG(VERBOSE) << \"Using \" << PROCESSGROUP_CGROUP_PROCS_FILE << \" to signal (\" << signal\n                     << \") \" << cgroup_v2_path;\n\n        // We separate all of the pids in the cgroup into those pids that are also the leaders of\n        // process groups (stored in the pgids set) and those that are not (stored in the pids set).\n        const auto procsfilepath = cgroup_v2_path + '/' + PROCESSGROUP_CGROUP_PROCS_FILE;\n        std::unique_ptr<FILE, decltype(&fclose)> fp(fopen(procsfilepath.c_str(), \"re\"), fclose);\n        if (!fp) {\n            // This should only happen if the cgroup has already been removed with a successful call\n            // to killProcessGroup. Callers should only retry sendSignalToProcessGroup or\n            // killProcessGroup calls if they fail without ENOENT.\n            PLOG(ERROR) << \"Failed to open \" << procsfilepath;\n            kill(-initialPid, signal);\n            return false;\n        }\n\n        pid_t pid;\n        bool file_is_empty = true;\n        while (fscanf(fp.get(), \"%d\\n\", &pid) == 1 && pid >= 0) {\n            file_is_empty = false;\n            if (pid == 0) {\n                // Should never happen...  but if it does, trying to kill this\n                // will boomerang right back and kill us!  Let's not let that happen.\n                LOG(WARNING)\n                        << \"Yikes, we've been told to kill pid 0!  How about we don't do that?\";\n                continue;\n            }\n            pid_t pgid = getpgid(pid);\n            if (pgid == -1) PLOG(ERROR) << \"getpgid(\" << pid << \") failed\";\n            if (pgid == pid) {\n                pgids.emplace(pid);\n            } else {\n                pids.emplace(pid);\n            }\n        }\n        if (!file_is_empty) {\n            // Erase all pids that will be killed when we kill the process groups.\n            for (auto it = pids.begin(); it != pids.end();) {\n                pid_t pgid = getpgid(*it);\n                if (pgids.count(pgid) == 1) {\n                    it = pids.erase(it);\n                } else {\n                    ++it;\n                }\n            }\n        }\n    }\n\n    pgids.emplace(initialPid);\n\n    // Kill all process groups.\n    for (const auto pgid : pgids) {\n        LOG(VERBOSE) << \"Killing process group \" << -pgid << \" in uid \" << uid\n                     << \" as part of process cgroup \" << initialPid;\n\n        if (kill(-pgid, signal) == -1 && errno != ESRCH) {\n            PLOG(WARNING) << \"kill(\" << -pgid << \", \" << signal << \") failed\";\n        }\n    }\n\n    // Kill remaining pids.\n    for (const auto pid : pids) {\n        LOG(VERBOSE) << \"Killing pid \" << pid << \" in uid \" << uid << \" as part of process cgroup \"\n                     << initialPid;\n\n        if (kill(pid, signal) == -1 && errno != ESRCH) {\n            PLOG(WARNING) << \"kill(\" << pid << \", \" << signal << \") failed\";\n        }\n    }\n\n    return true;\n}\n\ntemplate <typename T>\nstatic std::chrono::milliseconds toMillisec(T&& duration) {\n    return std::chrono::duration_cast<std::chrono::milliseconds>(duration);\n}\n\nenum class populated_status\n{\n    populated,\n    not_populated,\n    error\n};\n\nstatic populated_status cgroupIsPopulated(int events_fd) {\n    const std::string POPULATED_KEY(\"populated \");\n    const std::string::size_type MAX_EVENTS_FILE_SIZE = 32;\n\n    std::string buf;\n    buf.resize(MAX_EVENTS_FILE_SIZE);\n    ssize_t len = TEMP_FAILURE_RETRY(pread(events_fd, buf.data(), buf.size(), 0));\n    if (len == -1) {\n        PLOG(ERROR) << \"Could not read cgroup.events: \";\n        // Potentially ENODEV if the cgroup has been removed since we opened this file, but that\n        // shouldn't have happened yet.\n        return populated_status::error;\n    }\n\n    if (len == 0) {\n        LOG(ERROR) << \"cgroup.events EOF\";\n        return populated_status::error;\n    }\n\n    buf.resize(len);\n\n    const std::string::size_type pos = buf.find(POPULATED_KEY);\n    if (pos == std::string::npos) {\n        LOG(ERROR) << \"Could not find populated key in cgroup.events\";\n        return populated_status::error;\n    }\n\n    if (pos + POPULATED_KEY.size() + 1 > len) {\n        LOG(ERROR) << \"Partial read of cgroup.events\";\n        return populated_status::error;\n    }\n\n    return buf[pos + POPULATED_KEY.size()] == '1' ?\n        populated_status::populated : populated_status::not_populated;\n}\n\n// The default timeout of 2200ms comes from the default number of retries in a previous\n// implementation of this function. The default retry value was 40 for killing and 400 for cgroup\n// removal with 5ms sleeps between each retry.\nstatic int KillProcessGroup(\n        uid_t uid, pid_t initialPid, int signal, bool once = false,\n        std::chrono::steady_clock::time_point until = std::chrono::steady_clock::now() + 2200ms) {\n    if (uid < 0) {\n        LOG(ERROR) << __func__ << \": invalid UID \" << uid;\n        return -1;\n    }\n    if (initialPid <= 0) {\n        LOG(ERROR) << __func__ << \": invalid PID \" << initialPid;\n        return -1;\n    }\n\n    // Always attempt to send a kill signal to at least the initialPid, at least once, regardless of\n    // whether its cgroup exists or not. This should only be necessary if a bug results in the\n    // migration of the targeted process out of its cgroup, which we will also attempt to kill.\n    const bool signal_ret = sendSignalToProcessGroup(uid, initialPid, signal);\n\n    if (!CgroupsAvailable() || !signal_ret) return signal_ret ? 0 : -1;\n\n    std::string hierarchy_root_path;\n    CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &hierarchy_root_path);\n\n    const std::string cgroup_v2_path =\n            ConvertUidPidToPath(hierarchy_root_path.c_str(), uid, initialPid, true);\n\n    const std::string eventsfile = cgroup_v2_path + '/' + PROCESSGROUP_CGROUP_EVENTS_FILE;\n    android::base::unique_fd events_fd(open(eventsfile.c_str(), O_RDONLY));\n    if (events_fd.get() == -1) {\n        PLOG(WARNING) << \"Error opening \" << eventsfile << \" for KillProcessGroup\";\n        return -1;\n    }\n\n    struct pollfd fds = {\n        .fd = events_fd,\n        .events = POLLPRI,\n    };\n\n    const std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();\n\n    // The primary reason to loop here is to capture any new forks or migrations that could occur\n    // after we send signals to the original set of processes, but before all of those processes\n    // exit and the cgroup becomes unpopulated, or before we remove the cgroup. We try hard to\n    // ensure this completes successfully to avoid permanent memory leaks, but we still place a\n    // large default upper bound on the amount of time we spend in this loop. The amount of CPU\n    // contention, and the amount of work that needs to be done in do_exit for each process\n    // determines how long this will take.\n    int ret;\n    do {\n        populated_status populated;\n        while ((populated = cgroupIsPopulated(events_fd.get())) == populated_status::populated &&\n               std::chrono::steady_clock::now() < until) {\n\n            sendSignalToProcessGroup(uid, initialPid, signal);\n            if (once) {\n                populated = cgroupIsPopulated(events_fd.get());\n                break;\n            }\n\n            const std::chrono::steady_clock::time_point poll_start =\n                    std::chrono::steady_clock::now();\n\n            if (poll_start < until)\n                ret = TEMP_FAILURE_RETRY(poll(&fds, 1, toMillisec(until - poll_start).count()));\n\n            if (ret == -1) {\n                // Fallback to 5ms sleeps if poll fails\n                PLOG(ERROR) << \"Poll on \" << eventsfile << \"failed\";\n                const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();\n                if (now < until)\n                    std::this_thread::sleep_for(std::min(5ms, toMillisec(until - now)));\n            }\n\n            LOG(VERBOSE) << \"Waited \"\n                         << toMillisec(std::chrono::steady_clock::now() - poll_start).count()\n                         << \" ms for \" << eventsfile << \" poll\";\n        }\n\n        const std::chrono::milliseconds kill_duration =\n                toMillisec(std::chrono::steady_clock::now() - start);\n\n        if (populated == populated_status::populated) {\n            LOG(WARNING) << \"Still waiting on process(es) to exit for cgroup \" << cgroup_v2_path\n                         << \" after \" << kill_duration.count() << \" ms\";\n            // We'll still try the cgroup removal below which we expect to log an error.\n        } else if (populated == populated_status::not_populated) {\n            LOG(VERBOSE) << \"Killed all processes under cgroup \" << cgroup_v2_path\n                         << \" after \" << kill_duration.count() << \" ms\";\n        }\n\n        ret = RemoveCgroup(hierarchy_root_path.c_str(), uid, initialPid, true);\n        if (ret)\n            PLOG(ERROR) << \"Unable to remove cgroup \" << cgroup_v2_path;\n        else\n            LOG(INFO) << \"Removed cgroup \" << cgroup_v2_path;\n\n        if (isMemoryCgroupSupported() && UsePerAppMemcg()) {\n            // This per-application memcg v1 case should eventually be removed after migration to\n            // memcg v2.\n            std::string memcg_apps_path;\n            if (CgroupGetMemcgAppsPath(&memcg_apps_path) &&\n                (ret = RemoveCgroup(memcg_apps_path.c_str(), uid, initialPid, false)) < 0) {\n                const auto memcg_v1_cgroup_path =\n                        ConvertUidPidToPath(memcg_apps_path.c_str(), uid, initialPid, false);\n                PLOG(ERROR) << \"Unable to remove memcg v1 cgroup \" << memcg_v1_cgroup_path;\n            }\n        }\n\n        if (once) break;\n        if (std::chrono::steady_clock::now() >= until) break;\n    } while (ret && errno == EBUSY);\n\n    return ret;\n}\n\nint killProcessGroup(uid_t uid, pid_t initialPid, int signal) {\n    return KillProcessGroup(uid, initialPid, signal);\n}\n\nint killProcessGroupOnce(uid_t uid, pid_t initialPid, int signal) {\n    return KillProcessGroup(uid, initialPid, signal, true);\n}\n\nstatic int createProcessGroupInternal(uid_t uid, pid_t initialPid, std::string cgroup,\n                                      bool activate_controllers) {\n    auto uid_path = ConvertUidToPath(cgroup.c_str(), uid, activate_controllers);\n\n    struct stat cgroup_stat;\n    mode_t cgroup_mode = 0750;\n    uid_t cgroup_uid = AID_SYSTEM;\n    gid_t cgroup_gid = AID_SYSTEM;\n    int ret = 0;\n\n    if (stat(cgroup.c_str(), &cgroup_stat) < 0) {\n        PLOG(ERROR) << \"Failed to get stats for \" << cgroup;\n    } else {\n        cgroup_mode = cgroup_stat.st_mode;\n        cgroup_uid = cgroup_stat.st_uid;\n        cgroup_gid = cgroup_stat.st_gid;\n    }\n\n    if (!MkdirAndChown(uid_path, cgroup_mode, cgroup_uid, cgroup_gid)) {\n        PLOG(ERROR) << \"Failed to make and chown \" << uid_path;\n        return -errno;\n    }\n    if (activate_controllers) {\n        if (!CgroupMap::GetInstance().ActivateControllers(uid_path)) {\n            PLOG(ERROR) << \"Failed to activate controllers in \" << uid_path;\n            return -errno;\n        }\n    }\n\n    auto uid_pid_path = ConvertUidPidToPath(cgroup.c_str(), uid, initialPid, activate_controllers);\n\n    if (!MkdirAndChown(uid_pid_path, cgroup_mode, cgroup_uid, cgroup_gid)) {\n        PLOG(ERROR) << \"Failed to make and chown \" << uid_pid_path;\n        return -errno;\n    }\n\n    auto uid_pid_procs_file = uid_pid_path + '/' + PROCESSGROUP_CGROUP_PROCS_FILE;\n\n    if (!WriteStringToFile(std::to_string(initialPid), uid_pid_procs_file)) {\n        ret = -errno;\n        PLOG(ERROR) << \"Failed to write '\" << initialPid << \"' to \" << uid_pid_procs_file;\n    }\n\n    return ret;\n}\n\nint createProcessGroup(uid_t uid, pid_t initialPid, bool memControl) {\n    if (uid < 0) {\n        LOG(ERROR) << __func__ << \": invalid UID \" << uid;\n        return -1;\n    }\n    if (initialPid <= 0) {\n        LOG(ERROR) << __func__ << \": invalid PID \" << initialPid;\n        return -1;\n    }\n\n    if (memControl && !UsePerAppMemcg()) {\n        LOG(ERROR) << \"service memory controls are used without per-process memory cgroup support\";\n        return -EINVAL;\n    }\n\n    if (std::string memcg_apps_path;\n        isMemoryCgroupSupported() && UsePerAppMemcg() && CgroupGetMemcgAppsPath(&memcg_apps_path)) {\n        // Note by bvanassche: passing 'false' as fourth argument below implies that the v1\n        // hierarchy is used. It is not clear to me whether the above conditions guarantee that the\n        // v1 hierarchy is used.\n        int ret = createProcessGroupInternal(uid, initialPid, memcg_apps_path, false);\n        if (ret != 0) {\n            return ret;\n        }\n    }\n\n    std::string cgroup;\n    CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cgroup);\n    return createProcessGroupInternal(uid, initialPid, cgroup, true);\n}\n\nstatic bool SetProcessGroupValue(pid_t tid, const std::string& attr_name, int64_t value) {\n    if (!isMemoryCgroupSupported()) {\n        LOG(ERROR) << \"Memcg is not mounted.\";\n        return false;\n    }\n\n    std::string path;\n    if (!CgroupGetAttributePathForTask(attr_name, tid, &path)) {\n        LOG(ERROR) << \"Failed to find attribute '\" << attr_name << \"'\";\n        return false;\n    }\n\n    if (!WriteStringToFile(std::to_string(value), path)) {\n        PLOG(ERROR) << \"Failed to write '\" << value << \"' to \" << path;\n        return false;\n    }\n    return true;\n}\n\nbool setProcessGroupSwappiness(uid_t, pid_t pid, int swappiness) {\n    return SetProcessGroupValue(pid, \"MemSwappiness\", swappiness);\n}\n\nbool setProcessGroupSoftLimit(uid_t, pid_t pid, int64_t soft_limit_in_bytes) {\n    return SetProcessGroupValue(pid, \"MemSoftLimit\", soft_limit_in_bytes);\n}\n\nbool setProcessGroupLimit(uid_t, pid_t pid, int64_t limit_in_bytes) {\n    return SetProcessGroupValue(pid, \"MemLimit\", limit_in_bytes);\n}\n\nbool isProfileValidForProcess(const std::string& profile_name, uid_t uid, pid_t pid) {\n    const TaskProfile* tp = TaskProfiles::GetInstance().GetProfile(profile_name);\n\n    if (tp == nullptr) {\n        return false;\n    }\n\n    return tp->IsValidForProcess(uid, pid);\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/Android.bp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nprebuilt_etc {\n    name: \"cgroups.json\",\n    src: \"cgroups.json\",\n}\n\nprebuilt_etc {\n    name: \"cgroups.recovery.json\",\n    filename: \"cgroups.json\",\n    recovery: true,\n    src: \"cgroups.recovery.json\",\n}\n\nprebuilt_etc {\n    name: \"task_profiles.json\",\n    src: \"task_profiles.json\",\n}\n\ncc_defaults {\n    name: \"libprocessgroup_test_defaults\",\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n\n        // Needed for headers from libprotobuf.\n        \"-Wno-unused-parameter\",\n    ],\n}\n\ncc_library_static {\n    name: \"libprocessgroup_proto\",\n    host_supported: true,\n    defaults: [\"libprocessgroup_test_defaults\"],\n    srcs: [\n        \"cgroups.proto\",\n        \"task_profiles.proto\",\n    ],\n    proto: {\n        type: \"full\",\n        export_proto_headers: true,\n    },\n}\n\ncc_test_host {\n    name: \"libprocessgroup_proto_test\",\n    defaults: [\"libprocessgroup_test_defaults\"],\n    srcs: [\n        \"test.cpp\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"libgmock\",\n        \"liblog\",\n        \"libjsoncpp\",\n        \"libjsonpbverify\",\n        \"libjsonpbparse\",\n        \"libprocessgroup_proto\",\n    ],\n    shared_libs: [\n        \"libprotobuf-cpp-full\",\n    ],\n    data: [\n        \"cgroups.json\",\n        \"cgroups.recovery.json\",\n        \"task_profiles.json\",\n    ],\n}\n\ncc_test {\n    name: \"vts_processgroup_validate_test\",\n    defaults: [\"libprocessgroup_test_defaults\"],\n    srcs: [\n        \"test_vendor.cpp\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"libgmock\",\n        \"liblog\",\n        \"libjsoncpp\",\n        \"libjsonpbverify\",\n        \"libjsonpbparse\",\n        \"libprocessgroup_proto\",\n    ],\n    shared_libs: [\n        \"libprotobuf-cpp-full\",\n    ],\n    test_suites: [\n        \"vts\",\n    ],\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/cgroups.json",
    "content": "{\n  \"Cgroups\": [\n    {\n      \"Controller\": \"blkio\",\n      \"Path\": \"/dev/blkio\",\n      \"Mode\": \"0775\",\n      \"UID\": \"system\",\n      \"GID\": \"system\"\n    },\n    {\n      \"Controller\": \"cpu\",\n      \"Path\": \"/dev/cpuctl\",\n      \"Mode\": \"0755\",\n      \"UID\": \"system\",\n      \"GID\": \"system\"\n    },\n    {\n      \"Controller\": \"cpuset\",\n      \"Path\": \"/dev/cpuset\",\n      \"Mode\": \"0755\",\n      \"UID\": \"system\",\n      \"GID\": \"system\"\n    }\n  ],\n  \"Cgroups2\": {\n    \"Path\": \"/sys/fs/cgroup\",\n    \"Mode\": \"0775\",\n    \"UID\": \"system\",\n    \"GID\": \"system\",\n    \"Controllers\": [\n      {\n        \"Controller\": \"freezer\",\n        \"Path\": \".\"\n      },\n      {\n        \"Controller\": \"memory\",\n        \"Path\": \".\",\n        \"NeedsActivation\": true,\n        \"MaxActivationDepth\": 3,\n        \"Optional\": true\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/cgroups.proto",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nsyntax = \"proto3\";\n\npackage android.profiles;\n\n// Next: 3\nmessage Cgroups {\n    repeated Cgroup cgroups = 1 [json_name = \"Cgroups\"];\n    Cgroups2 cgroups2 = 2 [json_name = \"Cgroups2\"];\n}\n\n// Next: 9\nmessage Cgroup {\n    string controller = 1 [json_name = \"Controller\"];\n    string path = 2 [json_name = \"Path\"];\n    string mode = 3 [json_name = \"Mode\"];\n    string uid = 4 [json_name = \"UID\"];\n    string gid = 5 [json_name = \"GID\"];\n// Booleans default to false when not specified. File reconstruction fails\n// when a boolean is specified as false, so leave unspecified in that case\n// https://developers.google.com/protocol-buffers/docs/proto3#default\n    bool needs_activation = 6 [json_name = \"NeedsActivation\"];\n    bool is_optional = 7 [json_name = \"Optional\"];\n    optional uint32 max_activation_depth = 8 [json_name = \"MaxActivationDepth\"];\n}\n\n// Next: 6\nmessage Cgroups2 {\n    string path = 1 [json_name = \"Path\"];\n    string mode = 2 [json_name = \"Mode\"];\n    string uid = 3 [json_name = \"UID\"];\n    string gid = 4 [json_name = \"GID\"];\n    repeated Cgroup controllers = 5 [json_name = \"Controllers\"];\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/cgroups.recovery.json",
    "content": "{\n  \"Cgroups2\": {\n    \"Path\": \"/sys/fs/cgroup\",\n    \"Mode\": \"0755\",\n    \"UID\": \"root\",\n    \"GID\": \"root\"\n  }\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/cgroups_test.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <gmock/gmock.h>\n#include <jsonpb/json_schema_test.h>\n\n#include \"cgroups.pb.h\"\n\nusing ::testing::MatchesRegex;\n\nnamespace android {\nnamespace profiles {\n\nclass CgroupsTest : public jsonpb::JsonSchemaTest {\n  public:\n    void SetUp() override {\n        JsonSchemaTest::SetUp();\n        cgroups_ = static_cast<Cgroups*>(message());\n    }\n    Cgroups* cgroups_;\n};\n\nTEST_P(CgroupsTest, CgroupRequiredFields) {\n    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {\n        auto&& cgroup = cgroups_->cgroups(i);\n        EXPECT_FALSE(cgroup.controller().empty())\n                << \"No controller name for cgroup #\" << i << \" in \" << file_path_;\n        EXPECT_FALSE(cgroup.path().empty()) << \"No path for cgroup #\" << i << \" in \" << file_path_;\n    }\n}\n\nTEST_P(CgroupsTest, Cgroup2RequiredFields) {\n    if (cgroups_->has_cgroups2()) {\n        EXPECT_FALSE(cgroups_->cgroups2().path().empty())\n                << \"No path for cgroup2 in \" << file_path_;\n    }\n}\n\n// \"Mode\" field must be in the format of \"0xxx\".\nstatic inline constexpr const char* REGEX_MODE = \"(0[0-7]{3})?\";\nTEST_P(CgroupsTest, CgroupMode) {\n    for (int i = 0; i < cgroups_->cgroups_size(); ++i) {\n        EXPECT_THAT(cgroups_->cgroups(i).mode(), MatchesRegex(REGEX_MODE))\n                << \"For cgroup controller #\" << i << \" in \" << file_path_;\n    }\n}\n\nTEST_P(CgroupsTest, Cgroup2Mode) {\n    EXPECT_THAT(cgroups_->cgroups2().mode(), MatchesRegex(REGEX_MODE))\n            << \"For cgroups2 in \" << file_path_;\n}\n\n}  // namespace profiles\n}  // namespace android\n"
  },
  {
    "path": "libprocessgroup/profiles/task_profiles.json",
    "content": "{\n  \"Attributes\": [\n    {\n      \"Name\": \"LowCapacityCPUs\",\n      \"Controller\": \"cpuset\",\n      \"File\": \"background/cpus\"\n    },\n    {\n      \"Name\": \"HighCapacityCPUs\",\n      \"Controller\": \"cpuset\",\n      \"File\": \"foreground/cpus\"\n    },\n    {\n      \"Name\": \"HighCapacityWICPUs\",\n      \"Controller\": \"cpuset\",\n      \"File\": \"foreground_window/cpus\"\n    },\n    {\n      \"Name\": \"MaxCapacityCPUs\",\n      \"Controller\": \"cpuset\",\n      \"File\": \"top-app/cpus\"\n    },\n    {\n      \"Name\": \"MemStats\",\n      \"Controller\": \"memory\",\n      \"File\": \"memory.stat\"\n    },\n    {\n      \"Name\": \"MemLimit\",\n      \"Controller\": \"memory\",\n      \"File\": \"memory.limit_in_bytes\",\n      \"FileV2\": \"memory.max\"\n    },\n    {\n      \"Name\": \"MemSoftLimit\",\n      \"Controller\": \"memory\",\n      \"File\": \"memory.soft_limit_in_bytes\",\n      \"FileV2\": \"memory.low\"\n    },\n    {\n      \"Name\": \"MemSwappiness\",\n      \"Controller\": \"memory\",\n      \"File\": \"memory.swappiness\"\n    },\n    {\n      \"Name\": \"MemUsage\",\n      \"Controller\": \"memory\",\n      \"File\": \"memory.usage_in_bytes\"\n    },\n    {\n      \"Name\": \"MemAndSwapUsage\",\n      \"Controller\": \"memory\",\n      \"File\": \"memory.memsw.usage_in_bytes\"\n    },\n    {\n      \"Name\": \"MemPressureLevel\",\n      \"Controller\": \"memory\",\n      \"File\": \"memory.pressure_level\"\n    },\n    {\n      \"Name\": \"MemCgroupEventControl\",\n      \"Controller\": \"memory\",\n      \"File\": \"cgroup.event_control\"\n    },\n    {\n      \"Name\": \"UClampMin\",\n      \"Controller\": \"cpu\",\n      \"File\": \"cpu.uclamp.min\"\n    },\n    {\n      \"Name\": \"UClampMax\",\n      \"Controller\": \"cpu\",\n      \"File\": \"cpu.uclamp.max\"\n    },\n    {\n      \"Name\": \"UClampLatencySensitive\",\n      \"Controller\": \"cpu\",\n      \"File\": \"cpu.uclamp.latency_sensitive\"\n    },\n    {\n      \"Name\": \"FreezerState\",\n      \"Controller\": \"freezer\",\n      \"File\": \"cgroup.freeze\"\n    },\n    {\n      \"Name\": \"CgroupProcs\",\n      \"Controller\": \"cgroup2\",\n      \"File\": \"cgroup.procs\"\n    }\n  ],\n\n  \"Profiles\": [\n    {\n      \"Name\": \"HighEnergySaving\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"Frozen\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"FreezerState\",\n            \"Value\": \"1\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"Unfrozen\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"FreezerState\",\n            \"Value\": \"0\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"NormalPerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"system\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"ServicePerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"system-background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"HighPerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"foreground\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"HighPerformanceWI\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"foreground_window\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"MaxPerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"top-app\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"RealtimePerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"rt\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"RealTimeInputScheduling\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetSchedulerPolicy\",\n          \"Params\":\n          {\n            \"Policy\": \"SCHED_FIFO\",\n            \"Priority\": \"2\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"CameraServicePerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"camera-daemon\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"NNApiHALPerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"nnapi-hal\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"Dex2oatPerformance\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpu\",\n            \"Path\": \"dex2oat\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"CpuPolicySpread\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"UClampLatencySensitive\",\n            \"Value\": \"1\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"CpuPolicyPack\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"UClampLatencySensitive\",\n            \"Value\": \"0\"\n          }\n        }\n      ]\n    },\n\n    {\n      \"Name\": \"VrKernelCapacity\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"VrServiceCapacityLow\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"system/background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"VrServiceCapacityNormal\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"system\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"VrServiceCapacityHigh\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"system/performance\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"VrProcessCapacityLow\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"application/background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"VrProcessCapacityNormal\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"application\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"VrProcessCapacityHigh\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"application/performance\"\n          }\n        }\n      ]\n    },\n\n    {\n      \"Name\": \"ProcessCapacityLow\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"ProcessCapacityNormal\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"ProcessCapacityHigh\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"foreground\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"ProcessCapacityHighWI\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"foreground_window\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"ProcessCapacityMax\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"top-app\"\n          }\n        }\n      ]\n    },\n\n    {\n      \"Name\": \"ServiceCapacityLow\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"system-background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"ServiceCapacityRestricted\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"restricted\"\n          }\n        }\n      ]\n    },\n\n    {\n      \"Name\": \"CameraServiceCapacity\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"camera-daemon\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"LowIoPriority\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"blkio\",\n            \"Path\": \"background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"NormalIoPriority\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"blkio\",\n            \"Path\": \"\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"HighIoPriority\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"blkio\",\n            \"Path\": \"\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"MaxIoPriority\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"blkio\",\n            \"Path\": \"\"\n          }\n        }\n      ]\n    },\n\n    {\n      \"Name\": \"TimerSlackHigh\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetTimerSlack\",\n          \"Params\":\n          {\n            \"Slack\": \"40000000\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"TimerSlackNormal\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetTimerSlack\",\n          \"Params\":\n          {\n            \"Slack\": \"50000\"\n          }\n        }\n      ]\n    },\n\n    {\n      \"Name\": \"SFMainPolicy\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"system-background\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"SFRenderEnginePolicy\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"cpuset\",\n            \"Path\": \"system-background\"\n          }\n        }\n      ]\n    },\n\n    {\n      \"Name\": \"LowMemoryUsage\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"MemSoftLimit\",\n            \"Value\": \"16M\"\n          }\n        },\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"MemSwappiness\",\n            \"Value\": \"150\"\n\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"HighMemoryUsage\",\n      \"Actions\": [\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"MemSoftLimit\",\n            \"Value\": \"512M\"\n          }\n        },\n        {\n          \"Name\": \"SetAttribute\",\n          \"Params\":\n          {\n            \"Name\": \"MemSwappiness\",\n            \"Value\": \"100\"\n          }\n        }\n      ]\n    },\n    {\n      \"Name\": \"SystemMemoryProcess\",\n      \"Actions\": [\n        {\n          \"Name\": \"JoinCgroup\",\n          \"Params\":\n          {\n            \"Controller\": \"memory\",\n            \"Path\": \"system\"\n          }\n        }\n      ]\n    }\n  ],\n\n  \"AggregateProfiles\": [\n    {\n      \"Name\": \"SCHED_SP_DEFAULT\",\n      \"Profiles\": [ \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"SCHED_SP_BACKGROUND\",\n      \"Profiles\": [ \"HighEnergySaving\", \"LowIoPriority\", \"TimerSlackHigh\" ]\n    },\n    {\n      \"Name\": \"SCHED_SP_FOREGROUND\",\n      \"Profiles\": [ \"HighPerformance\", \"HighIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"SCHED_SP_FOREGROUND_WINDOW\",\n      \"Profiles\": [ \"HighPerformanceWI\", \"HighIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"SCHED_SP_TOP_APP\",\n      \"Profiles\": [ \"MaxPerformance\", \"MaxIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"SCHED_SP_SYSTEM\",\n      \"Profiles\": [ \"ServicePerformance\", \"LowIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"SCHED_SP_COMPUTE\",\n      \"Profiles\": [ \"HighPerformance\", \"ProcessCapacityHigh\", \"LowIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"SCHED_SP_RT_APP\",\n      \"Profiles\": [ \"RealtimePerformance\", \"MaxIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"CPUSET_SP_DEFAULT\",\n      \"Profiles\": [ \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"CPUSET_SP_BACKGROUND\",\n      \"Profiles\": [ \"HighEnergySaving\", \"ProcessCapacityLow\", \"LowIoPriority\", \"TimerSlackHigh\" ]\n    },\n    {\n      \"Name\": \"CPUSET_SP_FOREGROUND\",\n      \"Profiles\": [ \"HighPerformance\", \"ProcessCapacityHigh\", \"HighIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"CPUSET_SP_FOREGROUND_WINDOW\",\n      \"Profiles\": [ \"HighPerformanceWI\", \"ProcessCapacityHighWI\", \"HighIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"CPUSET_SP_TOP_APP\",\n      \"Profiles\": [ \"MaxPerformance\", \"ProcessCapacityMax\", \"MaxIoPriority\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"CPUSET_SP_SYSTEM\",\n      \"Profiles\": [ \"ServiceCapacityLow\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"CPUSET_SP_RESTRICTED\",\n      \"Profiles\": [ \"ServiceCapacityRestricted\", \"TimerSlackNormal\" ]\n    },\n    {\n      \"Name\": \"Dex2OatBootComplete\",\n      \"Profiles\": [ \"Dex2oatPerformance\", \"LowIoPriority\", \"TimerSlackHigh\" ]\n    },\n    {\n      \"Name\": \"Dex2OatBackground\",\n      \"Profiles\": [ \"Dex2OatBootComplete\" ]\n    },\n    {\n      \"Name\": \"OtaProfiles\",\n      \"Profiles\": [ \"ServiceCapacityLow\", \"LowIoPriority\", \"HighEnergySaving\" ]\n    },\n    {\n      \"Name\": \"InputPolicy\",\n      \"Profiles\": [ \"RealTimeInputScheduling\", \"MaxPerformance\", \"ProcessCapacityMax\", \"TimerSlackNormal\" ]\n    }\n  ]\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/task_profiles.proto",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nsyntax = \"proto3\";\n\npackage android.profiles;\n\n// Next: 4\nmessage TaskProfiles {\n    repeated Attribute attributes = 1 [json_name = \"Attributes\"];\n    repeated Profile profiles = 2 [json_name = \"Profiles\"];\n    repeated AggregateProfiles aggregateprofiles = 3 [json_name = \"AggregateProfiles\"];\n}\n\n// Next: 6\nmessage Attribute {\n    string name = 1 [json_name = \"Name\"];\n    string controller = 2 [json_name = \"Controller\"];\n    string file = 3 [json_name = \"File\"];\n    string filev2 = 4 [json_name = \"FileV2\"];\n    string optional = 5 [json_name = \"Optional\"];\n}\n\n// Next: 3\nmessage Profile {\n    string name = 1 [json_name = \"Name\"];\n    repeated Action actions = 2 [json_name = \"Actions\"];\n}\n\n// Next: 3\nmessage Action {\n    string name = 1 [json_name = \"Name\"];\n    map<string, string> params = 2 [json_name = \"Params\"];\n}\n\n// Next: 3\nmessage AggregateProfiles {\n    string name = 1 [json_name = \"Name\"];\n    repeated string profiles = 2 [json_name = \"Profiles\"];\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/task_profiles_test.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <gmock/gmock.h>\n#include <jsonpb/json_schema_test.h>\n\n#include \"task_profiles.pb.h\"\n\nnamespace android {\nnamespace profiles {\n\nclass TaskProfilesTest : public jsonpb::JsonSchemaTest {\n  public:\n    void SetUp() override {\n        JsonSchemaTest::SetUp();\n        task_profiles_ = static_cast<TaskProfiles*>(message());\n    }\n    TaskProfiles* task_profiles_;\n};\n\nTEST_P(TaskProfilesTest, AttributeRequiredFields) {\n    for (int i = 0; i < task_profiles_->attributes_size(); ++i) {\n        auto&& attribute = task_profiles_->attributes(i);\n        EXPECT_FALSE(attribute.name().empty())\n                << \"No name for attribute #\" << i << \" in \" << file_path_;\n        EXPECT_FALSE(attribute.controller().empty())\n                << \"No controller for attribute #\" << i << \" in \" << file_path_;\n        EXPECT_FALSE(attribute.file().empty())\n                << \"No file for attribute #\" << i << \" in \" << file_path_;\n    }\n}\n\nTEST_P(TaskProfilesTest, ProfileRequiredFields) {\n    for (int profile_idx = 0; profile_idx < task_profiles_->profiles_size(); ++profile_idx) {\n        auto&& profile = task_profiles_->profiles(profile_idx);\n        EXPECT_FALSE(profile.name().empty())\n                << \"No name for profile #\" << profile_idx << \" in \" << file_path_;\n        for (int action_idx = 0; action_idx < profile.actions_size(); ++action_idx) {\n            auto&& action = profile.actions(action_idx);\n            EXPECT_FALSE(action.name().empty())\n                    << \"No name for profiles[\" << profile_idx << \"].actions[\" << action_idx\n                    << \"] in \" << file_path_;\n        }\n    }\n}\n\n}  // namespace profiles\n}  // namespace android\n"
  },
  {
    "path": "libprocessgroup/profiles/test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n#include <jsonpb/json_schema_test.h>\n\n#include \"cgroups_test.h\"\n#include \"task_profiles_test.h\"\n\nusing namespace ::android::jsonpb;\nusing ::android::base::GetExecutableDirectory;\n\nnamespace android {\nnamespace profiles {\n\ntemplate <typename T>\nJsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {\n    return jsonpb::MakeTestParam<T>(GetExecutableDirectory() + path);\n}\n\n// Test suite instantiations\nINSTANTIATE_TEST_SUITE_P(Cgroups, JsonSchemaTest,\n                         ::testing::Values(MakeTestParam<Cgroups>(\"/cgroups.json\"),\n                                           MakeTestParam<Cgroups>(\"/cgroups.recovery.json\"),\n                                           MakeTestParam<TaskProfiles>(\"/task_profiles.json\")));\nINSTANTIATE_TEST_SUITE_P(Cgroups, CgroupsTest,\n                         ::testing::Values(MakeTestParam<Cgroups>(\"/cgroups.json\"),\n                                           MakeTestParam<Cgroups>(\"/cgroups.recovery.json\")));\nINSTANTIATE_TEST_SUITE_P(TaskProfiles, TaskProfilesTest,\n                         ::testing::Values(MakeTestParam<TaskProfiles>(\"/task_profiles.json\")));\n\n}  // namespace profiles\n}  // namespace android\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "libprocessgroup/profiles/test_vendor.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/file.h>\n#include <gtest/gtest.h>\n#include <jsonpb/json_schema_test.h>\n\n#include \"cgroups_test.h\"\n#include \"task_profiles_test.h\"\n\nusing ::android::base::GetExecutableDirectory;\nusing namespace ::android::jsonpb;\n\nnamespace android {\nnamespace profiles {\n\nstatic constexpr const char* kVendorCgroups = \"/vendor/etc/cgroups.json\";\nstatic constexpr const char* kVendorTaskProfiles = \"/vendor/etc/task_profiles.json\";\n\ntemplate <typename T>\nclass TestConfig : public JsonSchemaTestConfig {\n  public:\n    TestConfig(const std::string& path) : file_path_(path){};\n    std::unique_ptr<google::protobuf::Message> CreateMessage() const override {\n        return std::make_unique<T>();\n    }\n    std::string file_path() const override { return file_path_; }\n    bool optional() const override {\n        // Ignore when vendor JSON files are missing.\n        return true;\n    }\n\n  private:\n    std::string file_path_;\n};\n\ntemplate <typename T>\nJsonSchemaTestConfigFactory MakeTestParam(const std::string& path) {\n    return [path]() { return std::make_unique<TestConfig<T>>(path); };\n}\n\nINSTANTIATE_TEST_SUITE_P(VendorCgroups, JsonSchemaTest,\n                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));\nINSTANTIATE_TEST_SUITE_P(VendorCgroups, CgroupsTest,\n                         ::testing::Values(MakeTestParam<Cgroups>(kVendorCgroups)));\n\nINSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, JsonSchemaTest,\n                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));\nINSTANTIATE_TEST_SUITE_P(VendorTaskProfiles, TaskProfilesTest,\n                         ::testing::Values(MakeTestParam<TaskProfiles>(kVendorTaskProfiles)));\n\n}  // namespace profiles\n}  // namespace android\n\nint main(int argc, char** argv) {\n    ::testing::InitGoogleTest(&argc, argv);\n    return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "libprocessgroup/sched_policy.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <processgroup/sched_policy.h>\n\n#define LOG_TAG \"SchedPolicy\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include <android-base/threads.h>\n#include <cgroup_map.h>\n#include <processgroup/processgroup.h>\n\nusing android::base::GetThreadId;\n\n/* Re-map SP_DEFAULT to the system default policy, and leave other values unchanged.\n * Call this any place a SchedPolicy is used as an input parameter.\n * Returns the possibly re-mapped policy.\n */\nstatic inline SchedPolicy _policy(SchedPolicy p) {\n    return p == SP_DEFAULT ? SP_SYSTEM_DEFAULT : p;\n}\n\n#if defined(__ANDROID__)\n\nint set_cpuset_policy(pid_t tid, SchedPolicy policy) {\n    if (tid == 0) {\n        tid = GetThreadId();\n    }\n    policy = _policy(policy);\n\n    switch (policy) {\n        case SP_BACKGROUND:\n            return SetTaskProfiles(tid, {\"CPUSET_SP_BACKGROUND\"}, true) ? 0 : -1;\n        case SP_FOREGROUND:\n        case SP_AUDIO_APP:\n        case SP_AUDIO_SYS:\n            return SetTaskProfiles(tid, {\"CPUSET_SP_FOREGROUND\"}, true) ? 0 : -1;\n        case SP_TOP_APP:\n            return SetTaskProfiles(tid, {\"CPUSET_SP_TOP_APP\"}, true) ? 0 : -1;\n        case SP_SYSTEM:\n            return SetTaskProfiles(tid, {\"CPUSET_SP_SYSTEM\"}, true) ? 0 : -1;\n        case SP_RESTRICTED:\n            return SetTaskProfiles(tid, {\"CPUSET_SP_RESTRICTED\"}, true) ? 0 : -1;\n        case SP_FOREGROUND_WINDOW:\n            return SetTaskProfiles(tid, {\"CPUSET_SP_FOREGROUND_WINDOW\"}, true) ? 0 : -1;\n        default:\n            break;\n    }\n\n    return 0;\n}\n\nint set_sched_policy(pid_t tid, SchedPolicy policy) {\n    if (tid == 0) {\n        tid = GetThreadId();\n    }\n    policy = _policy(policy);\n\n#if POLICY_DEBUG\n    char statfile[64];\n    char statline[1024];\n    char thread_name[255];\n\n    snprintf(statfile, sizeof(statfile), \"/proc/%d/stat\", tid);\n    memset(thread_name, 0, sizeof(thread_name));\n\n    unique_fd fd(TEMP_FAILURE_RETRY(open(statfile, O_RDONLY | O_CLOEXEC)));\n    if (fd >= 0) {\n        int rc = read(fd, statline, 1023);\n        statline[rc] = 0;\n        char* p = statline;\n        char* q;\n\n        for (p = statline; *p != '('; p++)\n            ;\n        p++;\n        for (q = p; *q != ')'; q++)\n            ;\n\n        strncpy(thread_name, p, (q - p));\n    }\n    switch (policy) {\n        case SP_BACKGROUND:\n            SLOGD(\"vvv tid %d (%s)\", tid, thread_name);\n            break;\n        case SP_FOREGROUND:\n        case SP_AUDIO_APP:\n        case SP_AUDIO_SYS:\n        case SP_TOP_APP:\n            SLOGD(\"^^^ tid %d (%s)\", tid, thread_name);\n            break;\n        case SP_SYSTEM:\n            SLOGD(\"/// tid %d (%s)\", tid, thread_name);\n            break;\n        case SP_RT_APP:\n            SLOGD(\"RT  tid %d (%s)\", tid, thread_name);\n            break;\n        case SP_FOREGROUND_WINDOW:\n            SLOGD(\"WI  tid %d (%s)\", tid, thread_name);\n            break;\n        default:\n            SLOGD(\"??? tid %d (%s)\", tid, thread_name);\n            break;\n    }\n#endif\n\n    switch (policy) {\n        case SP_BACKGROUND:\n            return SetTaskProfiles(tid, {\"SCHED_SP_BACKGROUND\"}, true) ? 0 : -1;\n        case SP_FOREGROUND:\n        case SP_AUDIO_APP:\n        case SP_AUDIO_SYS:\n            return SetTaskProfiles(tid, {\"SCHED_SP_FOREGROUND\"}, true) ? 0 : -1;\n        case SP_TOP_APP:\n            return SetTaskProfiles(tid, {\"SCHED_SP_TOP_APP\"}, true) ? 0 : -1;\n        case SP_SYSTEM:\n            return SetTaskProfiles(tid, {\"SCHED_SP_SYSTEM\"}, true) ? 0 : -1;\n        case SP_RT_APP:\n            return SetTaskProfiles(tid, {\"SCHED_SP_RT_APP\"}, true) ? 0 : -1;\n        case SP_FOREGROUND_WINDOW:\n            return SetTaskProfiles(tid, {\"SCHED_SP_FOREGROUND_WINDOW\"}, true) ? 0 : -1;\n        default:\n            return SetTaskProfiles(tid, {\"SCHED_SP_DEFAULT\"}, true) ? 0 : -1;\n    }\n\n    return 0;\n}\n\nbool cpusets_enabled() {\n    static bool enabled = (CgroupMap::GetInstance().FindController(\"cpuset\").IsUsable());\n    return enabled;\n}\n\nstatic bool cpuctl_enabled() {\n    return (CgroupMap::GetInstance().FindController(\"cpu\").IsUsable());\n}\n\nstatic int getCGroupSubsys(pid_t tid, const char* subsys, std::string& subgroup) {\n    auto controller = CgroupMap::GetInstance().FindController(subsys);\n\n    if (!controller.IsUsable()) return -1;\n\n    if (!controller.GetTaskGroup(tid, &subgroup))\n        return -1;\n\n    return 0;\n}\n\nstatic int get_sched_policy_from_group(const std::string& group, SchedPolicy* policy) {\n    if (group.empty()) {\n        *policy = SP_FOREGROUND;\n    } else if (group == \"foreground\") {\n        *policy = SP_FOREGROUND;\n    } else if (group == \"system-background\") {\n        *policy = SP_SYSTEM;\n    } else if (group == \"background\") {\n        *policy = SP_BACKGROUND;\n    } else if (group == \"top-app\") {\n        *policy = SP_TOP_APP;\n    } else if (group == \"restricted\") {\n        *policy = SP_RESTRICTED;\n    } else if (group == \"foreground_window\") {\n        *policy = SP_FOREGROUND_WINDOW;\n    } else {\n        errno = ERANGE;\n        return -1;\n    }\n    return 0;\n}\n\nint get_sched_policy(pid_t tid, SchedPolicy* policy) {\n    if (tid == 0) {\n        tid = GetThreadId();\n    }\n\n    std::string group;\n    if (cpuctl_enabled()) {\n        if (getCGroupSubsys(tid, \"cpu\", group) < 0) {\n            LOG(ERROR) << \"Failed to find cpu cgroup for tid \" << tid;\n            return -1;\n        }\n        // Wipe invalid group to fallback to cpuset\n        if (!group.empty()) {\n            if (get_sched_policy_from_group(group, policy) < 0) {\n                group.clear();\n            } else {\n                return 0;\n            }\n        }\n    }\n\n    if (cpusets_enabled() && getCGroupSubsys(tid, \"cpuset\", group) < 0) {\n        LOG(ERROR) << \"Failed to find cpuset cgroup for tid \" << tid;\n        return -1;\n    }\n    return get_sched_policy_from_group(group, policy);\n}\n\n#else\n\n/* Stubs for non-Android targets. */\n\nint set_sched_policy(int, SchedPolicy) {\n    return 0;\n}\n\nint get_sched_policy(int, SchedPolicy* policy) {\n    *policy = SP_SYSTEM_DEFAULT;\n    return 0;\n}\n\n#endif\n\nconst char* get_sched_policy_name(SchedPolicy policy) {\n    policy = _policy(policy);\n    static const char* const kSchedPolicyNames[] = {\n            [SP_BACKGROUND] = \"bg\", [SP_FOREGROUND] = \"fg\", [SP_SYSTEM] = \"  \",\n            [SP_AUDIO_APP] = \"aa\",  [SP_AUDIO_SYS] = \"as\",  [SP_TOP_APP] = \"ta\",\n            [SP_RT_APP] = \"rt\",     [SP_RESTRICTED] = \"rs\", [SP_FOREGROUND_WINDOW] = \"wi\",\n    };\n    static_assert(arraysize(kSchedPolicyNames) == SP_CNT, \"missing name\");\n    if (policy < SP_BACKGROUND || policy >= SP_CNT) {\n        return nullptr;\n    }\n    return kSchedPolicyNames[policy];\n}\n\nconst char* get_cpuset_policy_profile_name(SchedPolicy policy) {\n    /*\n     *  cpuset profile array for:\n     *  SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),\n     *  SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),\n     *  SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7),\n     *  SP_FOREGROUND_WINDOW(8)\n     *  index is policy + 1\n     *  this need keep in sync with SchedPolicy enum\n     */\n    static constexpr const char* kCpusetProfiles[SP_CNT + 1] = {\n            \"CPUSET_SP_DEFAULT\",      \"CPUSET_SP_BACKGROUND\", \"CPUSET_SP_FOREGROUND\",\n            \"CPUSET_SP_SYSTEM\",       \"CPUSET_SP_FOREGROUND\", \"CPUSET_SP_FOREGROUND\",\n            \"CPUSET_SP_TOP_APP\",      \"CPUSET_SP_DEFAULT\",    \"CPUSET_SP_RESTRICTED\",\n            \"CPUSET_SP_FOREGROUND_WINDOW\"};\n    if (policy < SP_DEFAULT || policy >= SP_CNT) {\n        return nullptr;\n    }\n    return kCpusetProfiles[policy + 1];\n}\n\nconst char* get_sched_policy_profile_name(SchedPolicy policy) {\n    /*\n     *  sched profile array for:\n     *  SP_DEFAULT(-1), SP_BACKGROUND(0), SP_FOREGROUND(1),\n     *  SP_SYSTEM(2), SP_AUDIO_APP(3), SP_AUDIO_SYS(4),\n     *  SP_TOP_APP(5), SP_RT_APP(6), SP_RESTRICTED(7),\n     *  SP_FOREGROUND_WINDOW(8)\n     *  index is policy + 1\n     *  this need keep in sync with SchedPolicy enum\n     */\n    static constexpr const char* kSchedProfiles[SP_CNT + 1] = {\n            \"SCHED_SP_DEFAULT\",      \"SCHED_SP_BACKGROUND\", \"SCHED_SP_FOREGROUND\",\n            \"SCHED_SP_SYSTEM\",       \"SCHED_SP_FOREGROUND\", \"SCHED_SP_FOREGROUND\",\n            \"SCHED_SP_TOP_APP\",      \"SCHED_SP_RT_APP\",     \"SCHED_SP_DEFAULT\",\n            \"SCHED_SP_FOREGROUND_WINDOW\"};\n    if (policy < SP_DEFAULT || policy >= SP_CNT) {\n        return nullptr;\n    }\n    return kSchedProfiles[policy + 1];\n}\n"
  },
  {
    "path": "libprocessgroup/setup/Android.bp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_shared {\n    name: \"libprocessgroup_setup\",\n    recovery_available: true,\n    srcs: [\n        \"cgroup_map_write.cpp\",\n    ],\n    export_include_dirs: [\n        \"include\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libjsoncpp\",\n    ],\n    static_libs: [\n        \"libprocessgroup_util\",\n    ],\n    header_libs: [\n        \"libprocessgroup_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libprocessgroup_headers\",\n    ],\n    defaults: [\"libprocessgroup_build_flags_cc\"],\n}\n"
  },
  {
    "path": "libprocessgroup/setup/cgroup_descriptor.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n\n#include <sys/stat.h>\n\n#include <processgroup/cgroup_controller.h>\n\n// Complete controller description for mounting cgroups\nclass CgroupDescriptor {\n  public:\n    CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,\n                     mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags,\n                     uint32_t max_activation_depth);\n\n    const CgroupController* controller() const { return &controller_; }\n    mode_t mode() const { return mode_; }\n    std::string uid() const { return uid_; }\n    std::string gid() const { return gid_; }\n\n    void set_mounted(bool mounted);\n\n  private:\n    CgroupController controller_;\n    mode_t mode_ = 0;\n    std::string uid_;\n    std::string gid_;\n};\n"
  },
  {
    "path": "libprocessgroup/setup/cgroup_map_write.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//#define LOG_NDEBUG 0\n#define LOG_TAG \"libprocessgroup\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <grp.h>\n#include <pwd.h>\n#include <sys/mount.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n#include <processgroup/cgroup_descriptor.h>\n#include <processgroup/processgroup.h>\n#include <processgroup/setup.h>\n#include <processgroup/util.h>\n\n#include \"../build_flags.h\"\n#include \"../internal.h\"\n\nstatic constexpr const char* CGROUPS_DESC_FILE = \"/etc/cgroups.json\";\nstatic constexpr const char* CGROUPS_DESC_VENDOR_FILE = \"/vendor/etc/cgroups.json\";\n\nstatic constexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = \"/etc/task_profiles/cgroups_%u.json\";\n\nstatic bool ChangeDirModeAndOwner(const std::string& path, mode_t mode, const std::string& uid,\n                                  const std::string& gid, bool permissive_mode = false) {\n    uid_t pw_uid = -1;\n    gid_t gr_gid = -1;\n\n    if (!uid.empty()) {\n        passwd* uid_pwd = getpwnam(uid.c_str());\n        if (!uid_pwd) {\n            PLOG(ERROR) << \"Unable to decode UID for '\" << uid << \"'\";\n            return false;\n        }\n\n        pw_uid = uid_pwd->pw_uid;\n        gr_gid = -1;\n\n        if (!gid.empty()) {\n            group* gid_pwd = getgrnam(gid.c_str());\n            if (!gid_pwd) {\n                PLOG(ERROR) << \"Unable to decode GID for '\" << gid << \"'\";\n                return false;\n            }\n            gr_gid = gid_pwd->gr_gid;\n        }\n    }\n\n    auto dir = std::unique_ptr<DIR, decltype(&closedir)>(opendir(path.c_str()), closedir);\n\n    if (dir == NULL) {\n        PLOG(ERROR) << \"opendir failed for \" << path;\n        return false;\n    }\n\n    struct dirent* dir_entry;\n    while ((dir_entry = readdir(dir.get()))) {\n        if (!strcmp(\"..\", dir_entry->d_name)) {\n            continue;\n        }\n\n        std::string file_path = path + \"/\" + dir_entry->d_name;\n\n        if (pw_uid != -1 && lchown(file_path.c_str(), pw_uid, gr_gid) < 0) {\n            PLOG(ERROR) << \"lchown() failed for \" << file_path;\n            return false;\n        }\n\n        if (fchmodat(AT_FDCWD, file_path.c_str(), mode, AT_SYMLINK_NOFOLLOW) != 0 &&\n            (errno != EROFS || !permissive_mode)) {\n            PLOG(ERROR) << \"fchmodat() failed for \" << path;\n            return false;\n        }\n    }\n\n    return true;\n}\n\nstatic bool Mkdir(const std::string& path, mode_t mode, const std::string& uid,\n                  const std::string& gid) {\n    bool permissive_mode = false;\n\n    if (mode == 0) {\n        /* Allow chmod to fail */\n        permissive_mode = true;\n        mode = 0755;\n    }\n\n    if (mkdir(path.c_str(), mode) != 0) {\n        // /acct is a special case when the directory already exists\n        if (errno != EEXIST) {\n            PLOG(ERROR) << \"mkdir() failed for \" << path;\n            return false;\n        } else {\n            permissive_mode = true;\n        }\n    }\n\n    if (uid.empty() && permissive_mode) {\n        return true;\n    }\n\n    if (!ChangeDirModeAndOwner(path, mode, uid, gid, permissive_mode)) {\n        PLOG(ERROR) << \"change of ownership or mode failed for \" << path;\n        return false;\n    }\n\n    return true;\n}\n\n// To avoid issues in sdk_mac build\n#if defined(__ANDROID__)\n\nstatic bool IsOptionalController(const CgroupController* controller) {\n    return controller->flags() & CGROUPRC_CONTROLLER_FLAG_OPTIONAL;\n}\n\nstatic bool MountV2CgroupController(const CgroupDescriptor& descriptor) {\n    const CgroupController* controller = descriptor.controller();\n\n    // /sys/fs/cgroup is created by cgroup2 with specific selinux permissions,\n    // try to create again in case the mount point is changed\n    if (!Mkdir(controller->path(), 0, \"\", \"\")) {\n        LOG(ERROR) << \"Failed to create directory for \" << controller->name() << \" cgroup\";\n        return false;\n    }\n\n    // The memory_recursiveprot mount option has been introduced by kernel commit\n    // 8a931f801340 (\"mm: memcontrol: recursive memory.low protection\"; v5.7). Try first to\n    // mount with that option enabled. If mounting fails because the kernel is too old,\n    // retry without that mount option.\n    if (mount(\"none\", controller->path(), \"cgroup2\", MS_NODEV | MS_NOEXEC | MS_NOSUID,\n              \"memory_recursiveprot\") < 0) {\n        LOG(INFO) << \"Mounting memcg with memory_recursiveprot failed. Retrying without.\";\n        if (mount(\"none\", controller->path(), \"cgroup2\", MS_NODEV | MS_NOEXEC | MS_NOSUID,\n                  nullptr) < 0) {\n            PLOG(ERROR) << \"Failed to mount cgroup v2\";\n            return IsOptionalController(controller);\n        }\n    }\n\n    // selinux permissions change after mounting, so it's ok to change mode and owner now\n    if (!ChangeDirModeAndOwner(controller->path(), descriptor.mode(), descriptor.uid(),\n                               descriptor.gid())) {\n        PLOG(ERROR) << \"Change of ownership or mode failed for controller \" << controller->name();\n        return IsOptionalController(controller);\n    }\n\n    return true;\n}\n\nstatic bool ActivateV2CgroupController(const CgroupDescriptor& descriptor) {\n    const CgroupController* controller = descriptor.controller();\n\n    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {\n        LOG(ERROR) << \"Failed to create directory for \" << controller->name() << \" cgroup\";\n        return false;\n    }\n\n    return ::ActivateControllers(controller->path(), {{controller->name(), descriptor}});\n}\n\nstatic bool MountV1CgroupController(const CgroupDescriptor& descriptor) {\n    const CgroupController* controller = descriptor.controller();\n\n    // mkdir <path> [mode] [owner] [group]\n    if (!Mkdir(controller->path(), descriptor.mode(), descriptor.uid(), descriptor.gid())) {\n        LOG(ERROR) << \"Failed to create directory for \" << controller->name() << \" cgroup\";\n        return false;\n    }\n\n    // Unfortunately historically cpuset controller was mounted using a mount command\n    // different from all other controllers. This results in controller attributes not\n    // to be prepended with controller name. For example this way instead of\n    // /dev/cpuset/cpuset.cpus the attribute becomes /dev/cpuset/cpus which is what\n    // the system currently expects.\n    int res;\n    if (!strcmp(controller->name(), \"cpuset\")) {\n        // mount cpuset none /dev/cpuset nodev noexec nosuid\n        res = mount(\"none\", controller->path(), controller->name(),\n                    MS_NODEV | MS_NOEXEC | MS_NOSUID, nullptr);\n    } else {\n        // mount cgroup none <path> nodev noexec nosuid <controller>\n        res = mount(\"none\", controller->path(), \"cgroup\", MS_NODEV | MS_NOEXEC | MS_NOSUID,\n                    controller->name());\n    }\n    if (res != 0) {\n        if (IsOptionalController(controller)) {\n            PLOG(INFO) << \"Failed to mount optional controller \" << controller->name();\n            return true;\n        }\n        PLOG(ERROR) << \"Failed to mount controller \" << controller->name();\n        return false;\n    }\n    return true;\n}\n\nstatic bool SetupCgroup(const CgroupDescriptor& descriptor) {\n    const CgroupController* controller = descriptor.controller();\n\n    if (controller->version() == 2) {\n        if (controller->name() == CGROUPV2_HIERARCHY_NAME) {\n            return MountV2CgroupController(descriptor);\n        } else {\n            return ActivateV2CgroupController(descriptor);\n        }\n    } else {\n        return MountV1CgroupController(descriptor);\n    }\n}\n\n#else\n\n// Stubs for non-Android targets.\nstatic bool SetupCgroup(const CgroupDescriptor&) {\n    return false;\n}\n\n#endif\n\nCgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,\n                                   const std::string& path, mode_t mode, const std::string& uid,\n                                   const std::string& gid, uint32_t flags,\n                                   uint32_t max_activation_depth)\n    : controller_(version, flags, name, path, max_activation_depth),\n      mode_(mode),\n      uid_(uid),\n      gid_(gid) {}\n\nvoid CgroupDescriptor::set_mounted(bool mounted) {\n    uint32_t flags = controller_.flags();\n    if (mounted) {\n        flags |= CGROUPRC_CONTROLLER_FLAG_MOUNTED;\n    } else {\n        flags &= ~CGROUPRC_CONTROLLER_FLAG_MOUNTED;\n    }\n    controller_.set_flags(flags);\n}\n\nstatic bool CreateV2SubHierarchy(const std::string& path, const CgroupDescriptorMap& descriptors) {\n    const auto cgv2_iter = descriptors.find(CGROUPV2_HIERARCHY_NAME);\n    if (cgv2_iter == descriptors.end()) return false;\n    const CgroupDescriptor cgv2_descriptor = cgv2_iter->second;\n\n    if (!Mkdir(path, cgv2_descriptor.mode(), cgv2_descriptor.uid(), cgv2_descriptor.gid())) {\n        PLOG(ERROR) << \"Failed to create directory for \" << path;\n        return false;\n    }\n\n    // Activate all v2 controllers in path so they can be activated in\n    // children as they are created.\n    return ::ActivateControllers(path, descriptors);\n}\n\nbool CgroupSetup() {\n    CgroupDescriptorMap descriptors;\n\n    if (getpid() != 1) {\n        LOG(ERROR) << \"Cgroup setup can be done only by init process\";\n        return false;\n    }\n\n    // load cgroups.json file\n    if (!ReadDescriptors(&descriptors)) {\n        LOG(ERROR) << \"Failed to load cgroup description file\";\n        return false;\n    }\n\n    // setup cgroups\n    for (auto& [name, descriptor] : descriptors) {\n        if (descriptor.controller()->flags() & CGROUPRC_CONTROLLER_FLAG_MOUNTED) {\n            LOG(WARNING) << \"Attempt to call CgroupSetup() more than once\";\n            return true;\n        }\n\n        if (!SetupCgroup(descriptor)) {\n            // issue a warning and proceed with the next cgroup\n            LOG(WARNING) << \"Failed to setup \" << name << \" cgroup\";\n        }\n    }\n\n    // System / app isolation.\n    // This really belongs in early-init in init.rc, but we cannot use the flag there.\n    if (android::libprocessgroup_flags::cgroup_v2_sys_app_isolation()) {\n        const auto it = descriptors.find(CGROUPV2_HIERARCHY_NAME);\n        const std::string cgroup_v2_root = (it == descriptors.end())\n                                                   ? CGROUP_V2_ROOT_DEFAULT\n                                                   : it->second.controller()->path();\n\n        LOG(INFO) << \"Using system/app isolation under: \" << cgroup_v2_root;\n        if (!CreateV2SubHierarchy(cgroup_v2_root + \"/apps\", descriptors) ||\n            !CreateV2SubHierarchy(cgroup_v2_root + \"/system\", descriptors)) {\n            return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "libprocessgroup/setup/include/processgroup/setup.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nbool CgroupSetup();\n"
  },
  {
    "path": "libprocessgroup/task_profiles.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//#define LOG_NDEBUG 0\n#define LOG_TAG \"libprocessgroup\"\n\n#include <task_profiles.h>\n\n#include <map>\n#include <string>\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <sched.h>\n#include <sys/resource.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <android-base/threads.h>\n\n#include <build_flags.h>\n\n#include <cutils/android_filesystem_config.h>\n\n#include <json/reader.h>\n#include <json/value.h>\n\nusing android::base::GetThreadId;\nusing android::base::GetUintProperty;\nusing android::base::StringPrintf;\nusing android::base::StringReplace;\nusing android::base::unique_fd;\nusing android::base::WriteStringToFile;\n\nstatic constexpr const char* TASK_PROFILE_DB_FILE = \"/etc/task_profiles.json\";\nstatic constexpr const char* TASK_PROFILE_DB_VENDOR_FILE = \"/vendor/etc/task_profiles.json\";\n\nstatic constexpr const char* TEMPLATE_TASK_PROFILE_API_FILE =\n        \"/etc/task_profiles/task_profiles_%u.json\";\nnamespace {\n\nclass FdCacheHelper {\n  public:\n    enum FdState {\n        FDS_INACCESSIBLE = -1,\n        FDS_APP_DEPENDENT = -2,\n        FDS_NOT_CACHED = -3,\n    };\n\n    static void Cache(const std::string& path, android::base::unique_fd& fd);\n\n    static void Drop(android::base::unique_fd& fd);\n\n    static void Init(const std::string& path, android::base::unique_fd& fd);\n\n    static bool IsCached(const android::base::unique_fd& fd) { return fd > FDS_INACCESSIBLE; }\n\n  private:\n    static bool IsAppDependentPath(const std::string& path);\n};\n\nvoid FdCacheHelper::Init(const std::string& path, android::base::unique_fd& fd) {\n    // file descriptors for app-dependent paths can't be cached\n    if (IsAppDependentPath(path)) {\n        // file descriptor is not cached\n        fd.reset(FDS_APP_DEPENDENT);\n        return;\n    }\n    // file descriptor can be cached later on request\n    fd.reset(FDS_NOT_CACHED);\n}\n\nvoid FdCacheHelper::Cache(const std::string& path, android::base::unique_fd& fd) {\n    if (fd != FDS_NOT_CACHED) {\n        return;\n    }\n\n    if (access(path.c_str(), W_OK) != 0) {\n        // file is not accessible\n        fd.reset(FDS_INACCESSIBLE);\n        return;\n    }\n\n    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));\n    if (tmp_fd < 0) {\n        PLOG(ERROR) << \"Failed to cache fd '\" << path << \"'\";\n        fd.reset(FDS_INACCESSIBLE);\n        return;\n    }\n\n    fd = std::move(tmp_fd);\n}\n\nvoid FdCacheHelper::Drop(android::base::unique_fd& fd) {\n    if (fd == FDS_NOT_CACHED) {\n        return;\n    }\n\n    fd.reset(FDS_NOT_CACHED);\n}\n\nbool FdCacheHelper::IsAppDependentPath(const std::string& path) {\n    return path.find(\"<uid>\", 0) != std::string::npos || path.find(\"<pid>\", 0) != std::string::npos;\n}\n\n}  // namespace\n\nIProfileAttribute::~IProfileAttribute() = default;\n\nconst std::string& ProfileAttribute::file_name() const {\n    if (controller()->version() == 2 && !file_v2_name_.empty()) return file_v2_name_;\n    return file_name_;\n}\n\nvoid ProfileAttribute::Reset(const CgroupControllerWrapper& controller,\n                             const std::string& file_name, const std::string& file_v2_name) {\n    controller_ = controller;\n    file_name_ = file_name;\n    file_v2_name_ = file_v2_name;\n}\n\nstatic bool isSystemApp(uid_t uid) {\n    return uid < AID_APP_START;\n}\n\nstd::string ConvertUidToPath(const char* root_cgroup_path, uid_t uid, bool v2_path) {\n    if (android::libprocessgroup_flags::cgroup_v2_sys_app_isolation() && v2_path) {\n        if (isSystemApp(uid))\n            return StringPrintf(\"%s/system/uid_%u\", root_cgroup_path, uid);\n        else\n            return StringPrintf(\"%s/apps/uid_%u\", root_cgroup_path, uid);\n    }\n    return StringPrintf(\"%s/uid_%u\", root_cgroup_path, uid);\n}\n\nstd::string ConvertUidPidToPath(const char* root_cgroup_path, uid_t uid, pid_t pid, bool v2_path) {\n    const std::string uid_path = ConvertUidToPath(root_cgroup_path, uid, v2_path);\n    return StringPrintf(\"%s/pid_%d\", uid_path.c_str(), pid);\n}\n\nbool ProfileAttribute::GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const {\n    if (controller()->version() == 2) {\n        const std::string cgroup_path = ConvertUidPidToPath(controller()->path(), uid, pid, true);\n        *path = cgroup_path + \"/\" + file_name();\n        return true;\n    }\n    return GetPathForTask(pid, path);\n}\n\nbool ProfileAttribute::GetPathForTask(pid_t tid, std::string* path) const {\n    std::string subgroup;\n    if (!controller()->GetTaskGroup(tid, &subgroup)) {\n        return false;\n    }\n\n    if (path == nullptr) {\n        return true;\n    }\n\n    if (subgroup.empty()) {\n        *path = StringPrintf(\"%s/%s\", controller()->path(), file_name().c_str());\n    } else {\n        *path = StringPrintf(\"%s/%s/%s\", controller()->path(), subgroup.c_str(),\n                             file_name().c_str());\n    }\n    return true;\n}\n\n// NOTE: This function is for cgroup v2 only\nbool ProfileAttribute::GetPathForUID(uid_t uid, std::string* path) const {\n    if (path == nullptr) {\n        return true;\n    }\n\n    const std::string cgroup_path = ConvertUidToPath(controller()->path(), uid, true);\n    *path = cgroup_path + \"/\" + file_name();\n    return true;\n}\n\nbool SetTimerSlackAction::ExecuteForTask(pid_t tid) const {\n    const auto file = StringPrintf(\"/proc/%d/timerslack_ns\", tid);\n    if (!WriteStringToFile(std::to_string(slack_), file)) {\n        if (errno == ENOENT) {\n            // This happens when process is already dead\n            return true;\n        }\n        PLOG(ERROR) << \"set_timerslack_ns write failed\";\n        return false;\n    }\n\n    return true;\n}\n\nbool SetAttributeAction::WriteValueToFile(const std::string& path) const {\n    if (!WriteStringToFile(value_, path)) {\n        if (access(path.c_str(), F_OK) < 0) {\n            if (optional_) {\n                return true;\n            } else {\n                LOG(ERROR) << \"No such cgroup attribute: \" << path;\n                return false;\n            }\n        }\n        // The PLOG() statement below uses the error code stored in `errno` by\n        // WriteStringToFile() because access() only overwrites `errno` if it fails\n        // and because this code is only reached if the access() function returns 0.\n        PLOG(ERROR) << \"Failed to write '\" << value_ << \"' to \" << path;\n        return false;\n    }\n\n    return true;\n}\n\nbool SetAttributeAction::ExecuteForProcess(uid_t uid, pid_t pid) const {\n    std::string path;\n\n    if (!attribute_->GetPathForProcess(uid, pid, &path)) {\n        LOG(ERROR) << \"Failed to find cgroup for uid \" << uid << \" pid \" << pid;\n        return false;\n    }\n\n    return WriteValueToFile(path);\n}\n\nbool SetAttributeAction::ExecuteForTask(pid_t tid) const {\n    std::string path;\n\n    if (!attribute_->GetPathForTask(tid, &path)) {\n        LOG(ERROR) << \"Failed to find cgroup for tid \" << tid;\n        return false;\n    }\n\n    return WriteValueToFile(path);\n}\n\nbool SetAttributeAction::ExecuteForUID(uid_t uid) const {\n    std::string path;\n\n    if (!attribute_->GetPathForUID(uid, &path)) {\n        LOG(ERROR) << \"Failed to find cgroup for uid \" << uid;\n        return false;\n    }\n\n    if (!WriteStringToFile(value_, path)) {\n        if (access(path.c_str(), F_OK) < 0) {\n            if (optional_) {\n                return true;\n            } else {\n                LOG(ERROR) << \"No such cgroup attribute: \" << path;\n                return false;\n            }\n        }\n        PLOG(ERROR) << \"Failed to write '\" << value_ << \"' to \" << path;\n        return false;\n    }\n    return true;\n}\n\nbool SetAttributeAction::IsValidForProcess(uid_t, pid_t pid) const {\n    return IsValidForTask(pid);\n}\n\nbool SetAttributeAction::IsValidForTask(pid_t tid) const {\n    std::string path;\n\n    if (!attribute_->GetPathForTask(tid, &path)) {\n        return false;\n    }\n\n    if (!access(path.c_str(), W_OK)) {\n        // operation will succeed\n        return true;\n    }\n\n    if (!access(path.c_str(), F_OK)) {\n        // file exists but not writable\n        return false;\n    }\n\n    // file does not exist, ignore if optional\n    return optional_;\n}\n\nSetCgroupAction::SetCgroupAction(const CgroupControllerWrapper& c, const std::string& p)\n    : controller_(c), path_(p) {\n    FdCacheHelper::Init(controller_.GetTasksFilePath(path_), fd_[ProfileAction::RCT_TASK]);\n    // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them\n    FdCacheHelper::Init(controller_.GetProcsFilePath(path_, 0, 0), fd_[ProfileAction::RCT_PROCESS]);\n}\n\nbool SetCgroupAction::AddTidToCgroup(pid_t tid, int fd, ResourceCacheType cache_type) const {\n    if (tid <= 0) {\n        return true;\n    }\n\n    std::string value = std::to_string(tid);\n\n    if (TEMP_FAILURE_RETRY(write(fd, value.c_str(), value.length())) == value.length()) {\n        return true;\n    }\n\n    // If the thread is in the process of exiting, don't flag an error\n    if (errno == ESRCH) {\n        return true;\n    }\n\n    const char* controller_name = controller()->name();\n    // ENOSPC is returned when cpuset cgroup that we are joining has no online cpus\n    if (errno == ENOSPC && !strcmp(controller_name, \"cpuset\")) {\n        // This is an abnormal case happening only in testing, so report it only once\n        static bool empty_cpuset_reported = false;\n\n        if (empty_cpuset_reported) {\n            return true;\n        }\n\n        LOG(ERROR) << \"Failed to add task '\" << value\n                   << \"' into cpuset because all cpus in that cpuset are offline\";\n        empty_cpuset_reported = true;\n    } else {\n        PLOG(ERROR) << \"AddTidToCgroup failed to write '\" << value << \"'; path=\" << path_ << \"; \"\n                    << (cache_type == RCT_TASK ? \"task\" : \"process\");\n    }\n\n    return false;\n}\n\nProfileAction::CacheUseResult SetCgroupAction::UseCachedFd(ResourceCacheType cache_type,\n                                                           int id) const {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    if (FdCacheHelper::IsCached(fd_[cache_type])) {\n        // fd is cached, reuse it\n        if (!AddTidToCgroup(id, fd_[cache_type], cache_type)) {\n            LOG(ERROR) << \"Failed to add task into cgroup\";\n            return ProfileAction::FAIL;\n        }\n        return ProfileAction::SUCCESS;\n    }\n\n    if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {\n        // no permissions to access the file, ignore\n        return ProfileAction::SUCCESS;\n    }\n\n    if (cache_type == ResourceCacheType::RCT_TASK &&\n        fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {\n        // application-dependent path can't be used with tid\n        LOG(ERROR) << Name() << \": application profile can't be applied to a thread\";\n        return ProfileAction::FAIL;\n    }\n\n    return ProfileAction::UNUSED;\n}\n\nbool SetCgroupAction::ExecuteForProcess(uid_t uid, pid_t pid) const {\n    CacheUseResult result = UseCachedFd(ProfileAction::RCT_PROCESS, pid);\n    if (result != ProfileAction::UNUSED) {\n        return result == ProfileAction::SUCCESS;\n    }\n\n    // fd was not cached or cached fd can't be used\n    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);\n    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(procs_path.c_str(), O_WRONLY | O_CLOEXEC)));\n    if (tmp_fd < 0) {\n        PLOG(WARNING) << Name() << \"::\" << __func__ << \": failed to open \" << procs_path;\n        return false;\n    }\n    if (!AddTidToCgroup(pid, tmp_fd, RCT_PROCESS)) {\n        LOG(ERROR) << \"Failed to add task into cgroup\";\n        return false;\n    }\n\n    return true;\n}\n\nbool SetCgroupAction::ExecuteForTask(pid_t tid) const {\n    CacheUseResult result = UseCachedFd(ProfileAction::RCT_TASK, tid);\n    if (result != ProfileAction::UNUSED) {\n        return result == ProfileAction::SUCCESS;\n    }\n\n    // fd was not cached or cached fd can't be used\n    std::string tasks_path = controller()->GetTasksFilePath(path_);\n    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(tasks_path.c_str(), O_WRONLY | O_CLOEXEC)));\n    if (tmp_fd < 0) {\n        PLOG(WARNING) << Name() << \"::\" << __func__ << \": failed to open \" << tasks_path;\n        return false;\n    }\n    if (!AddTidToCgroup(tid, tmp_fd, RCT_TASK)) {\n        LOG(ERROR) << \"Failed to add task into cgroup\";\n        return false;\n    }\n\n    return true;\n}\n\nvoid SetCgroupAction::EnableResourceCaching(ResourceCacheType cache_type) {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    // Return early to prevent unnecessary calls to controller_.Get{Tasks|Procs}FilePath() which\n    // include regex evaluations\n    if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {\n        return;\n    }\n    switch (cache_type) {\n        case (ProfileAction::RCT_TASK):\n            FdCacheHelper::Cache(controller_.GetTasksFilePath(path_), fd_[cache_type]);\n            break;\n        case (ProfileAction::RCT_PROCESS):\n            // uid and pid don't matter because IsAppDependentPath ensures the path doesn't use them\n            FdCacheHelper::Cache(controller_.GetProcsFilePath(path_, 0, 0), fd_[cache_type]);\n            break;\n        default:\n            LOG(ERROR) << \"Invalid cache type is specified!\";\n            break;\n    }\n}\n\nvoid SetCgroupAction::DropResourceCaching(ResourceCacheType cache_type) {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    FdCacheHelper::Drop(fd_[cache_type]);\n}\n\nbool SetCgroupAction::IsValidForProcess(uid_t uid, pid_t pid) const {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {\n        return true;\n    }\n\n    if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {\n        return false;\n    }\n\n    std::string procs_path = controller()->GetProcsFilePath(path_, uid, pid);\n    return access(procs_path.c_str(), W_OK) == 0;\n}\n\nbool SetCgroupAction::IsValidForTask(int) const {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {\n        return true;\n    }\n\n    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {\n        return false;\n    }\n\n    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {\n        // application-dependent path can't be used with tid\n        return false;\n    }\n\n    std::string tasks_path = controller()->GetTasksFilePath(path_);\n    return access(tasks_path.c_str(), W_OK) == 0;\n}\n\nWriteFileAction::WriteFileAction(const std::string& task_path, const std::string& proc_path,\n                                 const std::string& value, bool logfailures)\n    : task_path_(task_path), proc_path_(proc_path), value_(value), logfailures_(logfailures) {\n    FdCacheHelper::Init(task_path_, fd_[ProfileAction::RCT_TASK]);\n    if (!proc_path_.empty()) FdCacheHelper::Init(proc_path_, fd_[ProfileAction::RCT_PROCESS]);\n}\n\nbool WriteFileAction::WriteValueToFile(const std::string& value_, ResourceCacheType cache_type,\n                                       uid_t uid, pid_t pid, bool logfailures) const {\n    std::string value(value_);\n\n    value = StringReplace(value, \"<uid>\", std::to_string(uid), true);\n    value = StringReplace(value, \"<pid>\", std::to_string(pid), true);\n\n    CacheUseResult result = UseCachedFd(cache_type, value);\n\n    if (result != ProfileAction::UNUSED) {\n        return result == ProfileAction::SUCCESS;\n    }\n\n    std::string path;\n    if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {\n        path = task_path_;\n    } else {\n        path = proc_path_;\n    }\n\n    // Use WriteStringToFd instead of WriteStringToFile because the latter will open file with\n    // O_TRUNC which causes kernfs_mutex contention\n    unique_fd tmp_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_WRONLY | O_CLOEXEC)));\n\n    if (tmp_fd < 0) {\n        if (logfailures) PLOG(WARNING) << Name() << \"::\" << __func__ << \": failed to open \" << path;\n        return false;\n    }\n\n    if (!WriteStringToFd(value, tmp_fd)) {\n        if (logfailures) PLOG(ERROR) << \"Failed to write '\" << value << \"' to \" << path;\n        return false;\n    }\n\n    return true;\n}\n\nProfileAction::CacheUseResult WriteFileAction::UseCachedFd(ResourceCacheType cache_type,\n                                                           const std::string& value) const {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    if (FdCacheHelper::IsCached(fd_[cache_type])) {\n        // fd is cached, reuse it\n        bool ret = WriteStringToFd(value, fd_[cache_type]);\n\n        if (!ret && logfailures_) {\n            if (cache_type == ProfileAction::RCT_TASK || proc_path_.empty()) {\n                PLOG(ERROR) << \"Failed to write '\" << value << \"' to \" << task_path_;\n            } else {\n                PLOG(ERROR) << \"Failed to write '\" << value << \"' to \" << proc_path_;\n            }\n        }\n        return ret ? ProfileAction::SUCCESS : ProfileAction::FAIL;\n    }\n\n    if (fd_[cache_type] == FdCacheHelper::FDS_INACCESSIBLE) {\n        // no permissions to access the file, ignore\n        return ProfileAction::SUCCESS;\n    }\n\n    if (cache_type == ResourceCacheType::RCT_TASK &&\n        fd_[cache_type] == FdCacheHelper::FDS_APP_DEPENDENT) {\n        // application-dependent path can't be used with tid\n        LOG(ERROR) << Name() << \": application profile can't be applied to a thread\";\n        return ProfileAction::FAIL;\n    }\n    return ProfileAction::UNUSED;\n}\n\nbool WriteFileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {\n    if (!proc_path_.empty()) {\n        return WriteValueToFile(value_, ProfileAction::RCT_PROCESS, uid, pid, logfailures_);\n    }\n\n    DIR* d;\n    struct dirent* de;\n    char proc_path[255];\n    pid_t t_pid;\n\n    sprintf(proc_path, \"/proc/%d/task\", pid);\n    if (!(d = opendir(proc_path))) {\n        return false;\n    }\n\n    while ((de = readdir(d))) {\n        if (de->d_name[0] == '.') {\n            continue;\n        }\n\n        t_pid = atoi(de->d_name);\n\n        if (!t_pid) {\n            continue;\n        }\n\n        WriteValueToFile(value_, ProfileAction::RCT_TASK, uid, t_pid, logfailures_);\n    }\n\n    closedir(d);\n\n    return true;\n}\n\nbool WriteFileAction::ExecuteForTask(pid_t tid) const {\n    return WriteValueToFile(value_, ProfileAction::RCT_TASK, getuid(), tid, logfailures_);\n}\n\nvoid WriteFileAction::EnableResourceCaching(ResourceCacheType cache_type) {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    if (fd_[cache_type] != FdCacheHelper::FDS_NOT_CACHED) {\n        return;\n    }\n    switch (cache_type) {\n        case (ProfileAction::RCT_TASK):\n            FdCacheHelper::Cache(task_path_, fd_[cache_type]);\n            break;\n        case (ProfileAction::RCT_PROCESS):\n            if (!proc_path_.empty()) FdCacheHelper::Cache(proc_path_, fd_[cache_type]);\n            break;\n        default:\n            LOG(ERROR) << \"Invalid cache type is specified!\";\n            break;\n    }\n}\n\nvoid WriteFileAction::DropResourceCaching(ResourceCacheType cache_type) {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    FdCacheHelper::Drop(fd_[cache_type]);\n}\n\nbool WriteFileAction::IsValidForProcess(uid_t, pid_t) const {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_PROCESS])) {\n        return true;\n    }\n\n    if (fd_[ProfileAction::RCT_PROCESS] == FdCacheHelper::FDS_INACCESSIBLE) {\n        return false;\n    }\n\n    return access(proc_path_.empty() ? task_path_.c_str() : proc_path_.c_str(), W_OK) == 0;\n}\n\nbool WriteFileAction::IsValidForTask(int) const {\n    std::lock_guard<std::mutex> lock(fd_mutex_);\n    if (FdCacheHelper::IsCached(fd_[ProfileAction::RCT_TASK])) {\n        return true;\n    }\n\n    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_INACCESSIBLE) {\n        return false;\n    }\n\n    if (fd_[ProfileAction::RCT_TASK] == FdCacheHelper::FDS_APP_DEPENDENT) {\n        // application-dependent path can't be used with tid\n        return false;\n    }\n\n    return access(task_path_.c_str(), W_OK) == 0;\n}\n\nbool SetSchedulerPolicyAction::isNormalPolicy(int policy) {\n    return policy == SCHED_OTHER || policy == SCHED_BATCH || policy == SCHED_IDLE;\n}\n\nbool SetSchedulerPolicyAction::toPriority(int policy, int virtual_priority, int& priority_out) {\n    constexpr int VIRTUAL_PRIORITY_MIN = 1;\n    constexpr int VIRTUAL_PRIORITY_MAX = 99;\n\n    if (virtual_priority < VIRTUAL_PRIORITY_MIN || virtual_priority > VIRTUAL_PRIORITY_MAX) {\n        LOG(WARNING) << \"SetSchedulerPolicy: invalid priority (\" << virtual_priority\n                     << \") for policy (\" << policy << \")\";\n        return false;\n    }\n\n    const int min = sched_get_priority_min(policy);\n    if (min == -1) {\n        PLOG(ERROR) << \"SetSchedulerPolicy: Cannot get min sched priority for policy \" << policy;\n        return false;\n    }\n\n    const int max = sched_get_priority_max(policy);\n    if (max == -1) {\n        PLOG(ERROR) << \"SetSchedulerPolicy: Cannot get max sched priority for policy \" << policy;\n        return false;\n    }\n\n    priority_out = min + (virtual_priority - VIRTUAL_PRIORITY_MIN) * (max - min) /\n        (VIRTUAL_PRIORITY_MAX - VIRTUAL_PRIORITY_MIN);\n\n    return true;\n}\n\nbool SetSchedulerPolicyAction::ExecuteForTask(pid_t tid) const {\n    struct sched_param param = {};\n    param.sched_priority = isNormalPolicy(policy_) ? 0 : *priority_or_nice_;\n    if (sched_setscheduler(tid, policy_, &param) == -1) {\n        PLOG(WARNING) << \"SetSchedulerPolicy: Failed to apply scheduler policy (\" << policy_\n                      << \") with priority (\" << *priority_or_nice_ << \") to tid \" << tid;\n        return false;\n    }\n\n    if (isNormalPolicy(policy_) && priority_or_nice_ &&\n        setpriority(PRIO_PROCESS, tid, *priority_or_nice_) == -1) {\n        PLOG(WARNING) << \"SetSchedulerPolicy: Failed to apply nice (\" << *priority_or_nice_\n                      << \") to tid \" << tid;\n        return false;\n    }\n\n    return true;\n}\n\nbool ApplyProfileAction::ExecuteForProcess(uid_t uid, pid_t pid) const {\n    for (const auto& profile : profiles_) {\n        profile->ExecuteForProcess(uid, pid);\n    }\n    return true;\n}\n\nbool ApplyProfileAction::ExecuteForTask(pid_t tid) const {\n    for (const auto& profile : profiles_) {\n        profile->ExecuteForTask(tid);\n    }\n    return true;\n}\n\nvoid ApplyProfileAction::EnableResourceCaching(ResourceCacheType cache_type) {\n    for (const auto& profile : profiles_) {\n        profile->EnableResourceCaching(cache_type);\n    }\n}\n\nvoid ApplyProfileAction::DropResourceCaching(ResourceCacheType cache_type) {\n    for (const auto& profile : profiles_) {\n        profile->DropResourceCaching(cache_type);\n    }\n}\n\nbool ApplyProfileAction::IsValidForProcess(uid_t uid, pid_t pid) const {\n    for (const auto& profile : profiles_) {\n        if (!profile->IsValidForProcess(uid, pid)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nbool ApplyProfileAction::IsValidForTask(pid_t tid) const {\n    for (const auto& profile : profiles_) {\n        if (!profile->IsValidForTask(tid)) {\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid TaskProfile::MoveTo(TaskProfile* profile) {\n    profile->elements_ = std::move(elements_);\n    profile->res_cached_ = res_cached_;\n}\n\nbool TaskProfile::ExecuteForProcess(uid_t uid, pid_t pid) const {\n    for (const auto& element : elements_) {\n        if (!element->ExecuteForProcess(uid, pid)) {\n            LOG(VERBOSE) << \"Applying profile action \" << element->Name() << \" failed\";\n            return false;\n        }\n    }\n    return true;\n}\n\nbool TaskProfile::ExecuteForTask(pid_t tid) const {\n    if (tid == 0) {\n        tid = GetThreadId();\n    }\n    for (const auto& element : elements_) {\n        if (!element->ExecuteForTask(tid)) {\n            LOG(VERBOSE) << \"Applying profile action \" << element->Name() << \" failed\";\n            return false;\n        }\n    }\n    return true;\n}\n\nbool TaskProfile::ExecuteForUID(uid_t uid) const {\n    for (const auto& element : elements_) {\n        if (!element->ExecuteForUID(uid)) {\n            LOG(VERBOSE) << \"Applying profile action \" << element->Name() << \" failed\";\n            return false;\n        }\n    }\n    return true;\n}\n\nvoid TaskProfile::EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) {\n    if (res_cached_) {\n        return;\n    }\n\n    for (auto& element : elements_) {\n        element->EnableResourceCaching(cache_type);\n    }\n\n    res_cached_ = true;\n}\n\nvoid TaskProfile::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) {\n    if (!res_cached_) {\n        return;\n    }\n\n    for (auto& element : elements_) {\n        element->DropResourceCaching(cache_type);\n    }\n\n    res_cached_ = false;\n}\n\nbool TaskProfile::IsValidForProcess(uid_t uid, pid_t pid) const {\n    for (const auto& element : elements_) {\n        if (!element->IsValidForProcess(uid, pid)) return false;\n    }\n    return true;\n}\n\nbool TaskProfile::IsValidForTask(pid_t tid) const {\n    for (const auto& element : elements_) {\n        if (!element->IsValidForTask(tid)) return false;\n    }\n    return true;\n}\n\nvoid TaskProfiles::DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const {\n    for (auto& iter : profiles_) {\n        iter.second->DropResourceCaching(cache_type);\n    }\n}\n\nTaskProfiles& TaskProfiles::GetInstance() {\n    // Deliberately leak this object to avoid a race between destruction on\n    // process exit and concurrent access from another thread.\n    static auto* instance = new TaskProfiles;\n    return *instance;\n}\n\nTaskProfiles::TaskProfiles() {\n    // load system task profiles\n    if (!Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_FILE)) {\n        LOG(ERROR) << \"Loading \" << TASK_PROFILE_DB_FILE << \" for [\" << getpid() << \"] failed\";\n    }\n\n    // load API-level specific system task profiles if available\n    unsigned int api_level = GetUintProperty<unsigned int>(\"ro.product.first_api_level\", 0);\n    if (api_level > 0) {\n        std::string api_profiles_path =\n                android::base::StringPrintf(TEMPLATE_TASK_PROFILE_API_FILE, api_level);\n        if (!access(api_profiles_path.c_str(), F_OK) || errno != ENOENT) {\n            if (!Load(CgroupMap::GetInstance(), api_profiles_path)) {\n                LOG(ERROR) << \"Loading \" << api_profiles_path << \" for [\" << getpid() << \"] failed\";\n            }\n        }\n    }\n\n    // load vendor task profiles if the file exists\n    if (!access(TASK_PROFILE_DB_VENDOR_FILE, F_OK) &&\n        !Load(CgroupMap::GetInstance(), TASK_PROFILE_DB_VENDOR_FILE)) {\n        LOG(ERROR) << \"Loading \" << TASK_PROFILE_DB_VENDOR_FILE << \" for [\" << getpid()\n                   << \"] failed\";\n    }\n}\n\nbool TaskProfiles::Load(const CgroupMap& cg_map, const std::string& file_name) {\n    std::string json_doc;\n\n    if (!android::base::ReadFileToString(file_name, &json_doc)) {\n        LOG(ERROR) << \"Failed to read task profiles from \" << file_name;\n        return false;\n    }\n\n    Json::CharReaderBuilder builder;\n    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());\n    Json::Value root;\n    std::string errorMessage;\n    if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {\n        LOG(ERROR) << \"Failed to parse task profiles: \" << errorMessage;\n        return false;\n    }\n\n    const Json::Value& attr = root[\"Attributes\"];\n    for (Json::Value::ArrayIndex i = 0; i < attr.size(); ++i) {\n        std::string name = attr[i][\"Name\"].asString();\n        std::string controller_name = attr[i][\"Controller\"].asString();\n        std::string file_attr = attr[i][\"File\"].asString();\n        std::string file_v2_attr = attr[i][\"FileV2\"].asString();\n\n        if (!file_v2_attr.empty() && file_attr.empty()) {\n            LOG(ERROR) << \"Attribute \" << name << \" has FileV2 but no File property\";\n            return false;\n        }\n\n        auto controller = cg_map.FindController(controller_name);\n        if (controller.HasValue()) {\n            auto iter = attributes_.find(name);\n            if (iter == attributes_.end()) {\n                attributes_[name] =\n                        std::make_unique<ProfileAttribute>(controller, file_attr, file_v2_attr);\n            } else {\n                iter->second->Reset(controller, file_attr, file_v2_attr);\n            }\n        } else {\n            LOG(WARNING) << \"Controller \" << controller_name << \" is not found\";\n        }\n    }\n\n    const Json::Value& profiles_val = root[\"Profiles\"];\n    for (Json::Value::ArrayIndex i = 0; i < profiles_val.size(); ++i) {\n        const Json::Value& profile_val = profiles_val[i];\n\n        std::string profile_name = profile_val[\"Name\"].asString();\n        const Json::Value& actions = profile_val[\"Actions\"];\n        auto profile = std::make_shared<TaskProfile>(profile_name);\n\n        for (Json::Value::ArrayIndex act_idx = 0; act_idx < actions.size(); ++act_idx) {\n            const Json::Value& action_val = actions[act_idx];\n            std::string action_name = action_val[\"Name\"].asString();\n            const Json::Value& params_val = action_val[\"Params\"];\n            if (action_name == \"JoinCgroup\") {\n                std::string controller_name = params_val[\"Controller\"].asString();\n                std::string path = params_val[\"Path\"].asString();\n\n                auto controller = cg_map.FindController(controller_name);\n                if (controller.HasValue()) {\n                    if (controller.version() == 1) {\n                        profile->Add(std::make_unique<SetCgroupAction>(controller, path));\n                    } else {\n                        LOG(WARNING) << \"A JoinCgroup action in the \" << profile_name\n                                     << \" profile is used for controller \" << controller_name\n                                     << \" in the cgroup v2 hierarchy and will be ignored\";\n                    }\n                } else {\n                    LOG(WARNING) << \"JoinCgroup: controller \" << controller_name << \" is not found\";\n                }\n            } else if (action_name == \"SetTimerSlack\") {\n                const std::string slack_string = params_val[\"Slack\"].asString();\n                if (long slack; android::base::ParseInt(slack_string, &slack) && slack >= 0) {\n                    profile->Add(std::make_unique<SetTimerSlackAction>(slack));\n                } else {\n                    LOG(WARNING) << \"SetTimerSlack: invalid parameter: \" << slack_string;\n                }\n            } else if (action_name == \"SetAttribute\") {\n                std::string attr_name = params_val[\"Name\"].asString();\n                std::string attr_value = params_val[\"Value\"].asString();\n                bool optional = strcmp(params_val[\"Optional\"].asString().c_str(), \"true\") == 0;\n\n                auto iter = attributes_.find(attr_name);\n                if (iter != attributes_.end()) {\n                    profile->Add(std::make_unique<SetAttributeAction>(iter->second.get(),\n                                                                      attr_value, optional));\n                } else {\n                    LOG(WARNING) << \"SetAttribute: unknown attribute: \" << attr_name;\n                }\n            } else if (action_name == \"WriteFile\") {\n                std::string attr_filepath = params_val[\"FilePath\"].asString();\n                std::string attr_procfilepath = params_val[\"ProcFilePath\"].asString();\n                std::string attr_value = params_val[\"Value\"].asString();\n                // FilePath and Value are mandatory\n                if (!attr_filepath.empty() && !attr_value.empty()) {\n                    std::string attr_logfailures = params_val[\"LogFailures\"].asString();\n                    bool logfailures = attr_logfailures.empty() || attr_logfailures == \"true\";\n                    profile->Add(std::make_unique<WriteFileAction>(attr_filepath, attr_procfilepath,\n                                                                   attr_value, logfailures));\n                } else if (attr_filepath.empty()) {\n                    LOG(WARNING) << \"WriteFile: invalid parameter: \"\n                                 << \"empty filepath\";\n                } else if (attr_value.empty()) {\n                    LOG(WARNING) << \"WriteFile: invalid parameter: \"\n                                 << \"empty value\";\n                }\n            } else if (action_name == \"SetSchedulerPolicy\") {\n                const std::map<std::string, int> POLICY_MAP = {\n                    {\"SCHED_OTHER\", SCHED_OTHER},\n                    {\"SCHED_BATCH\", SCHED_BATCH},\n                    {\"SCHED_IDLE\", SCHED_IDLE},\n                    {\"SCHED_FIFO\", SCHED_FIFO},\n                    {\"SCHED_RR\", SCHED_RR},\n                };\n                const std::string policy_str = params_val[\"Policy\"].asString();\n\n                const auto it = POLICY_MAP.find(policy_str);\n                if (it == POLICY_MAP.end()) {\n                    LOG(WARNING) << \"SetSchedulerPolicy: invalid policy \" << policy_str;\n                    continue;\n                }\n\n                const int policy = it->second;\n\n                if (SetSchedulerPolicyAction::isNormalPolicy(policy)) {\n                    if (params_val.isMember(\"Priority\")) {\n                        LOG(WARNING) << \"SetSchedulerPolicy: Normal policies (\" << policy_str\n                                     << \") use Nice values, not Priority values\";\n                    }\n\n                    if (params_val.isMember(\"Nice\")) {\n                        // If present, this optional value will be passed in an additional syscall\n                        // to setpriority(), since the sched_priority value must be 0 for calls to\n                        // sched_setscheduler() with \"normal\" policies.\n                        const std::string nice_string = params_val[\"Nice\"].asString();\n                        int nice;\n                        if (!android::base::ParseInt(nice_string, &nice)) {\n                            LOG(FATAL) << \"Invalid nice value specified: \" << nice_string;\n                        }\n                        const int LINUX_MIN_NICE = -20;\n                        const int LINUX_MAX_NICE = 19;\n                        if (nice < LINUX_MIN_NICE || nice > LINUX_MAX_NICE) {\n                            LOG(WARNING) << \"SetSchedulerPolicy: Provided nice (\" << nice\n                                         << \") appears out of range.\";\n                        }\n                        profile->Add(std::make_unique<SetSchedulerPolicyAction>(policy, nice));\n                    } else {\n                        profile->Add(std::make_unique<SetSchedulerPolicyAction>(policy));\n                    }\n                } else {\n                    if (params_val.isMember(\"Nice\")) {\n                        LOG(WARNING) << \"SetSchedulerPolicy: Real-time policies (\" << policy_str\n                                     << \") use Priority values, not Nice values\";\n                    }\n\n                    // This is a \"virtual priority\" as described by `man 2 sched_get_priority_min`\n                    // that will be mapped onto the following range for the provided policy:\n                    // [sched_get_priority_min(), sched_get_priority_max()]\n\n                    const std::string priority_string = params_val[\"Priority\"].asString();\n                    if (long virtual_priority;\n                        android::base::ParseInt(priority_string, &virtual_priority) &&\n                        virtual_priority > 0) {\n                        int priority;\n                        if (SetSchedulerPolicyAction::toPriority(policy, virtual_priority,\n                                                                 priority)) {\n                            profile->Add(\n                                    std::make_unique<SetSchedulerPolicyAction>(policy, priority));\n                        }\n                    } else {\n                        LOG(WARNING) << \"Invalid priority value: \" << priority_string;\n                    }\n                }\n            } else {\n                LOG(WARNING) << \"Unknown profile action: \" << action_name;\n            }\n        }\n        auto iter = profiles_.find(profile_name);\n        if (iter == profiles_.end()) {\n            profiles_[profile_name] = profile;\n        } else {\n            // Move the content rather that replace the profile because old profile might be\n            // referenced from an aggregate profile if vendor overrides task profiles\n            profile->MoveTo(iter->second.get());\n            profile.reset();\n        }\n    }\n\n    const Json::Value& aggregateprofiles_val = root[\"AggregateProfiles\"];\n    for (Json::Value::ArrayIndex i = 0; i < aggregateprofiles_val.size(); ++i) {\n        const Json::Value& aggregateprofile_val = aggregateprofiles_val[i];\n\n        std::string aggregateprofile_name = aggregateprofile_val[\"Name\"].asString();\n        const Json::Value& aggregateprofiles = aggregateprofile_val[\"Profiles\"];\n        std::vector<std::shared_ptr<TaskProfile>> profiles;\n        bool ret = true;\n\n        for (Json::Value::ArrayIndex pf_idx = 0; pf_idx < aggregateprofiles.size(); ++pf_idx) {\n            std::string profile_name = aggregateprofiles[pf_idx].asString();\n\n            if (profile_name == aggregateprofile_name) {\n                LOG(WARNING) << \"AggregateProfiles: recursive profile name: \" << profile_name;\n                ret = false;\n                break;\n            } else if (profiles_.find(profile_name) == profiles_.end()) {\n                LOG(WARNING) << \"AggregateProfiles: undefined profile name: \" << profile_name;\n                ret = false;\n                break;\n            } else {\n                profiles.push_back(profiles_[profile_name]);\n            }\n        }\n        if (ret) {\n            auto profile = std::make_shared<TaskProfile>(aggregateprofile_name);\n            profile->Add(std::make_unique<ApplyProfileAction>(profiles));\n            profiles_[aggregateprofile_name] = profile;\n        }\n    }\n\n    return true;\n}\n\nTaskProfile* TaskProfiles::GetProfile(std::string_view name) const {\n    auto iter = profiles_.find(name);\n\n    if (iter != profiles_.end()) {\n        return iter->second.get();\n    }\n    return nullptr;\n}\n\nconst IProfileAttribute* TaskProfiles::GetAttribute(std::string_view name) const {\n    auto iter = attributes_.find(name);\n\n    if (iter != attributes_.end()) {\n        return iter->second.get();\n    }\n    return nullptr;\n}\n\ntemplate <typename T>\nbool TaskProfiles::SetUserProfiles(uid_t uid, std::span<const T> profiles, bool use_fd_cache) {\n    for (const auto& name : profiles) {\n        TaskProfile* profile = GetProfile(name);\n        if (profile != nullptr) {\n            if (use_fd_cache) {\n                profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);\n            }\n            if (!profile->ExecuteForUID(uid)) {\n                PLOG(WARNING) << \"Failed to apply \" << name << \" process profile\";\n            }\n        } else {\n            PLOG(WARNING) << \"Failed to find \" << name << \"process profile\";\n        }\n    }\n    return true;\n}\n\ntemplate <typename T>\nbool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles,\n                                      bool use_fd_cache) {\n    bool success = true;\n    for (const auto& name : profiles) {\n        TaskProfile* profile = GetProfile(name);\n        if (profile != nullptr) {\n            if (use_fd_cache) {\n                profile->EnableResourceCaching(ProfileAction::RCT_PROCESS);\n            }\n            if (!profile->ExecuteForProcess(uid, pid)) {\n                LOG(WARNING) << \"Failed to apply \" << name << \" process profile\";\n                success = false;\n            }\n        } else {\n            LOG(WARNING) << \"Failed to find \" << name << \" process profile\";\n            success = false;\n        }\n    }\n    return success;\n}\n\ntemplate <typename T>\nbool TaskProfiles::SetTaskProfiles(pid_t tid, std::span<const T> profiles, bool use_fd_cache) {\n    bool success = true;\n    for (const auto& name : profiles) {\n        TaskProfile* profile = GetProfile(name);\n        if (profile != nullptr) {\n            if (use_fd_cache) {\n                profile->EnableResourceCaching(ProfileAction::RCT_TASK);\n            }\n            if (!profile->ExecuteForTask(tid)) {\n                LOG(WARNING) << \"Failed to apply \" << name << \" task profile\";\n                success = false;\n            }\n        } else {\n            LOG(WARNING) << \"Failed to find \" << name << \" task profile\";\n            success = false;\n        }\n    }\n    return success;\n}\n\ntemplate bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,\n                                               std::span<const std::string> profiles,\n                                               bool use_fd_cache);\ntemplate bool TaskProfiles::SetProcessProfiles(uid_t uid, pid_t pid,\n                                               std::span<const std::string_view> profiles,\n                                               bool use_fd_cache);\ntemplate bool TaskProfiles::SetTaskProfiles(pid_t tid, std::span<const std::string> profiles,\n                                            bool use_fd_cache);\ntemplate bool TaskProfiles::SetTaskProfiles(pid_t tid, std::span<const std::string_view> profiles,\n                                            bool use_fd_cache);\ntemplate bool TaskProfiles::SetUserProfiles(uid_t uid, std::span<const std::string> profiles,\n                                            bool use_fd_cache);\n"
  },
  {
    "path": "libprocessgroup/task_profiles.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <sys/types.h>\n\n#include <map>\n#include <memory>\n#include <mutex>\n#include <optional>\n#include <span>\n#include <string>\n#include <string_view>\n#include <vector>\n\n#include <android-base/unique_fd.h>\n#include <cgroup_map.h>\n\nclass IProfileAttribute {\n  public:\n    virtual ~IProfileAttribute() = 0;\n    virtual void Reset(const CgroupControllerWrapper& controller, const std::string& file_name,\n                       const std::string& file_v2_name) = 0;\n    virtual const CgroupControllerWrapper* controller() const = 0;\n    virtual const std::string& file_name() const = 0;\n    virtual bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const = 0;\n    virtual bool GetPathForTask(pid_t tid, std::string* path) const = 0;\n    virtual bool GetPathForUID(uid_t uid, std::string* path) const = 0;\n};\n\nclass ProfileAttribute : public IProfileAttribute {\n  public:\n    // Cgroup attributes may have different names in the v1 and v2 hierarchies. If `file_v2_name` is\n    // not empty, `file_name` is the name for the v1 hierarchy and `file_v2_name` is the name for\n    // the v2 hierarchy. If `file_v2_name` is empty, `file_name` is used for both hierarchies.\n    ProfileAttribute(const CgroupControllerWrapper& controller, const std::string& file_name,\n                     const std::string& file_v2_name)\n        : controller_(controller), file_name_(file_name), file_v2_name_(file_v2_name) {}\n    ~ProfileAttribute() = default;\n\n    const CgroupControllerWrapper* controller() const override { return &controller_; }\n    const std::string& file_name() const override;\n    void Reset(const CgroupControllerWrapper& controller, const std::string& file_name,\n               const std::string& file_v2_name) override;\n\n    bool GetPathForProcess(uid_t uid, pid_t pid, std::string* path) const override;\n    bool GetPathForTask(pid_t tid, std::string* path) const override;\n    bool GetPathForUID(uid_t uid, std::string* path) const override;\n\n  private:\n    CgroupControllerWrapper controller_;\n    std::string file_name_;\n    std::string file_v2_name_;\n};\n\n// Abstract profile element\nclass ProfileAction {\n  public:\n    enum ResourceCacheType { RCT_TASK = 0, RCT_PROCESS, RCT_COUNT };\n\n    virtual ~ProfileAction() {}\n\n    virtual const char* Name() const = 0;\n\n    // Default implementations will fail\n    virtual bool ExecuteForProcess(uid_t, pid_t) const { return false; }\n    virtual bool ExecuteForTask(pid_t) const { return false; }\n    virtual bool ExecuteForUID(uid_t) const { return false; }\n\n    virtual void EnableResourceCaching(ResourceCacheType) {}\n    virtual void DropResourceCaching(ResourceCacheType) {}\n    virtual bool IsValidForProcess(uid_t, pid_t) const { return false; }\n    virtual bool IsValidForTask(pid_t) const { return false; }\n\n  protected:\n    enum CacheUseResult { SUCCESS, FAIL, UNUSED };\n};\n\n// Profile actions\nclass SetTimerSlackAction : public ProfileAction {\n  public:\n    SetTimerSlackAction(unsigned long slack) noexcept : slack_(slack) {}\n\n    const char* Name() const override { return \"SetTimerSlack\"; }\n    bool ExecuteForTask(pid_t tid) const override;\n    bool IsValidForProcess(uid_t, pid_t) const override { return true; }\n    bool IsValidForTask(pid_t) const override { return true; }\n\n  private:\n    unsigned long slack_;\n};\n\n// Set attribute profile element\nclass SetAttributeAction : public ProfileAction {\n  public:\n    SetAttributeAction(const IProfileAttribute* attribute, const std::string& value, bool optional)\n        : attribute_(attribute), value_(value), optional_(optional) {}\n\n    const char* Name() const override { return \"SetAttribute\"; }\n    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;\n    bool ExecuteForTask(pid_t tid) const override;\n    bool ExecuteForUID(uid_t uid) const override;\n    bool IsValidForProcess(uid_t uid, pid_t pid) const override;\n    bool IsValidForTask(pid_t tid) const override;\n\n  private:\n    const IProfileAttribute* attribute_;\n    std::string value_;\n    bool optional_;\n\n    bool WriteValueToFile(const std::string& path) const;\n};\n\n// Set cgroup profile element\nclass SetCgroupAction : public ProfileAction {\n  public:\n    SetCgroupAction(const CgroupControllerWrapper& c, const std::string& p);\n\n    const char* Name() const override { return \"SetCgroup\"; }\n    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;\n    bool ExecuteForTask(pid_t tid) const override;\n    void EnableResourceCaching(ResourceCacheType cache_type) override;\n    void DropResourceCaching(ResourceCacheType cache_type) override;\n    bool IsValidForProcess(uid_t uid, pid_t pid) const override;\n    bool IsValidForTask(pid_t tid) const override;\n\n    const CgroupControllerWrapper* controller() const { return &controller_; }\n\n  private:\n    CgroupControllerWrapper controller_;\n    std::string path_;\n    android::base::unique_fd fd_[ProfileAction::RCT_COUNT];\n    mutable std::mutex fd_mutex_;\n\n    bool AddTidToCgroup(pid_t tid, int fd, ResourceCacheType cache_type) const;\n    CacheUseResult UseCachedFd(ResourceCacheType cache_type, int id) const;\n};\n\n// Write to file action\nclass WriteFileAction : public ProfileAction {\n  public:\n    WriteFileAction(const std::string& task_path, const std::string& proc_path,\n                    const std::string& value, bool logfailures);\n\n    const char* Name() const override { return \"WriteFile\"; }\n    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;\n    bool ExecuteForTask(pid_t tid) const override;\n    void EnableResourceCaching(ResourceCacheType cache_type) override;\n    void DropResourceCaching(ResourceCacheType cache_type) override;\n    bool IsValidForProcess(uid_t uid, pid_t pid) const override;\n    bool IsValidForTask(pid_t tid) const override;\n\n  private:\n    std::string task_path_, proc_path_, value_;\n    bool logfailures_;\n    android::base::unique_fd fd_[ProfileAction::RCT_COUNT];\n    mutable std::mutex fd_mutex_;\n\n    bool WriteValueToFile(const std::string& value, ResourceCacheType cache_type, uid_t uid,\n                          pid_t pid, bool logfailures) const;\n    CacheUseResult UseCachedFd(ResourceCacheType cache_type, const std::string& value) const;\n};\n\n// Set scheduler policy action\nclass SetSchedulerPolicyAction : public ProfileAction {\n  public:\n    SetSchedulerPolicyAction(int policy)\n        : policy_(policy) {}\n    SetSchedulerPolicyAction(int policy, int priority_or_nice)\n        : policy_(policy), priority_or_nice_(priority_or_nice) {}\n\n    const char* Name() const override { return \"SetSchedulerPolicy\"; }\n    bool ExecuteForTask(pid_t tid) const override;\n\n    static bool isNormalPolicy(int policy);\n    static bool toPriority(int policy, int virtual_priority, int& priority_out);\n\n  private:\n    int policy_;\n    std::optional<int> priority_or_nice_;\n};\n\nclass TaskProfile {\n  public:\n    TaskProfile(const std::string& name) : name_(name), res_cached_(false) {}\n\n    const std::string& Name() const { return name_; }\n    void Add(std::unique_ptr<ProfileAction> e) { elements_.push_back(std::move(e)); }\n    void MoveTo(TaskProfile* profile);\n\n    bool ExecuteForProcess(uid_t uid, pid_t pid) const;\n    bool ExecuteForTask(pid_t tid) const;\n    bool ExecuteForUID(uid_t uid) const;\n    void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type);\n    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type);\n    bool IsValidForProcess(uid_t uid, pid_t pid) const;\n    bool IsValidForTask(pid_t tid) const;\n\n  private:\n    const std::string name_;\n    bool res_cached_;\n    std::vector<std::unique_ptr<ProfileAction>> elements_;\n};\n\n// Set aggregate profile element\nclass ApplyProfileAction : public ProfileAction {\n  public:\n    ApplyProfileAction(const std::vector<std::shared_ptr<TaskProfile>>& profiles)\n        : profiles_(profiles) {}\n\n    const char* Name() const override { return \"ApplyProfileAction\"; }\n    bool ExecuteForProcess(uid_t uid, pid_t pid) const override;\n    bool ExecuteForTask(pid_t tid) const override;\n    void EnableResourceCaching(ProfileAction::ResourceCacheType cache_type) override;\n    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) override;\n    bool IsValidForProcess(uid_t uid, pid_t pid) const override;\n    bool IsValidForTask(pid_t tid) const override;\n\n  private:\n    std::vector<std::shared_ptr<TaskProfile>> profiles_;\n};\n\nclass TaskProfiles {\n  public:\n    // Should be used by all users\n    static TaskProfiles& GetInstance();\n\n    TaskProfile* GetProfile(std::string_view name) const;\n    const IProfileAttribute* GetAttribute(std::string_view name) const;\n    void DropResourceCaching(ProfileAction::ResourceCacheType cache_type) const;\n    template <typename T>\n    bool SetProcessProfiles(uid_t uid, pid_t pid, std::span<const T> profiles, bool use_fd_cache);\n    template <typename T>\n    bool SetTaskProfiles(pid_t tid, std::span<const T> profiles, bool use_fd_cache);\n    template <typename T>\n    bool SetUserProfiles(uid_t uid, std::span<const T> profiles, bool use_fd_cache);\n\n  private:\n    TaskProfiles();\n\n    bool Load(const CgroupMap& cg_map, const std::string& file_name);\n\n    std::map<std::string, std::shared_ptr<TaskProfile>, std::less<>> profiles_;\n    std::map<std::string, std::unique_ptr<IProfileAttribute>, std::less<>> attributes_;\n};\n\nstd::string ConvertUidToPath(const char* root_cgroup_path, uid_t uid, bool v2_path);\nstd::string ConvertUidPidToPath(const char* root_cgroup_path, uid_t uid, pid_t pid, bool v2_path);\n"
  },
  {
    "path": "libprocessgroup/task_profiles_test.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"task_profiles.h\"\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <gtest/gtest.h>\n#include <mntent.h>\n#include <processgroup/processgroup.h>\n#include <stdio.h>\n#include <unistd.h>\n\n#include <fstream>\n\nusing ::android::base::ERROR;\nusing ::android::base::LogFunction;\nusing ::android::base::LogId;\nusing ::android::base::LogSeverity;\nusing ::android::base::SetLogger;\nusing ::android::base::Split;\nusing ::android::base::VERBOSE;\nusing ::testing::TestWithParam;\nusing ::testing::Values;\n\nnamespace {\n\nbool IsCgroupV2MountedRw() {\n    std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent(\"/proc/mounts\", \"re\"), endmntent);\n    if (!mnts) {\n        LOG(ERROR) << \"Failed to open /proc/mounts\";\n        return false;\n    }\n    struct mntent* mnt;\n    while ((mnt = getmntent(mnts.get()))) {\n        if (strcmp(mnt->mnt_type, \"cgroup2\") != 0) {\n            continue;\n        }\n        const std::vector<std::string> options = Split(mnt->mnt_opts, \",\");\n        return std::count(options.begin(), options.end(), \"ro\") == 0;\n    }\n    return false;\n}\n\nclass ScopedLogCapturer {\n  public:\n    struct log_args {\n        LogId log_buffer_id;\n        LogSeverity severity;\n        std::string tag;\n        std::string file;\n        unsigned int line;\n        std::string message;\n    };\n\n    // Constructor. Installs a new logger and saves the currently active logger.\n    ScopedLogCapturer() {\n        saved_severity_ = SetMinimumLogSeverity(android::base::VERBOSE);\n        saved_logger_ = SetLogger([this](LogId log_buffer_id, LogSeverity severity, const char* tag,\n                                         const char* file, unsigned int line, const char* message) {\n            if (saved_logger_) {\n                saved_logger_(log_buffer_id, severity, tag, file, line, message);\n            }\n            log_.emplace_back(log_args{.log_buffer_id = log_buffer_id,\n                                       .severity = severity,\n                                       .tag = tag,\n                                       .file = file,\n                                       .line = line,\n                                       .message = message});\n        });\n    }\n    // Destructor. Restores the original logger and log level.\n    ~ScopedLogCapturer() {\n        SetLogger(std::move(saved_logger_));\n        SetMinimumLogSeverity(saved_severity_);\n    }\n    ScopedLogCapturer(const ScopedLogCapturer&) = delete;\n    ScopedLogCapturer& operator=(const ScopedLogCapturer&) = delete;\n    // Returns the logged lines.\n    const std::vector<log_args>& Log() const { return log_; }\n\n  private:\n    LogSeverity saved_severity_;\n    LogFunction saved_logger_;\n    std::vector<log_args> log_;\n};\n\n// cgroup attribute at the top level of the cgroup hierarchy.\nclass ProfileAttributeMock : public IProfileAttribute {\n  public:\n    ProfileAttributeMock(const std::string& file_name) : file_name_(file_name) {}\n    ~ProfileAttributeMock() override = default;\n    void Reset(const CgroupControllerWrapper&, const std::string&, const std::string&) override {\n        CHECK(false);\n    }\n    const CgroupControllerWrapper* controller() const override {\n        CHECK(false);\n        return {};\n    }\n    const std::string& file_name() const override { return file_name_; }\n    bool GetPathForProcess(uid_t, pid_t pid, std::string* path) const override {\n        return GetPathForTask(pid, path);\n    }\n    bool GetPathForTask(int, std::string* path) const override {\n#ifdef __ANDROID__\n        CHECK(CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, path));\n        CHECK_GT(path->length(), 0);\n        if (path->rbegin()[0] != '/') {\n            *path += \"/\";\n        }\n#else\n        // Not Android.\n        *path = \"/sys/fs/cgroup/\";\n#endif\n        *path += file_name_;\n        return true;\n    };\n\n    bool GetPathForUID(uid_t, std::string*) const override { return false; }\n\n  private:\n    const std::string file_name_;\n};\n\nstruct TestParam {\n    const char* attr_name;\n    const char* attr_value;\n    bool optional_attr;\n    bool result;\n    LogSeverity log_severity;\n    const char* log_prefix;\n    const char* log_suffix;\n};\n\nclass SetAttributeFixture : public TestWithParam<TestParam> {\n  public:\n    ~SetAttributeFixture() = default;\n};\n\nTEST_P(SetAttributeFixture, SetAttribute) {\n    // Treehugger runs host tests inside a container either without cgroupv2\n    // support or with the cgroup filesystem mounted read-only.\n    if (!IsCgroupV2MountedRw()) {\n        GTEST_SKIP();\n        return;\n    }\n    const TestParam params = GetParam();\n    ScopedLogCapturer captured_log;\n    ProfileAttributeMock pa(params.attr_name);\n    SetAttributeAction a(&pa, params.attr_value, params.optional_attr);\n    EXPECT_EQ(a.ExecuteForProcess(getuid(), getpid()), params.result);\n    auto log = captured_log.Log();\n    if (params.log_prefix || params.log_suffix) {\n        ASSERT_EQ(log.size(), 1);\n        EXPECT_EQ(log[0].severity, params.log_severity);\n        if (params.log_prefix) {\n            EXPECT_EQ(log[0].message.find(params.log_prefix), 0);\n        }\n        if (params.log_suffix) {\n            EXPECT_NE(log[0].message.find(params.log_suffix), std::string::npos);\n        }\n    } else {\n        ASSERT_EQ(log.size(), 0);\n    }\n}\n\nclass TaskProfileFixture : public TestWithParam<TestParam> {\n  public:\n    ~TaskProfileFixture() = default;\n};\n\nTEST_P(TaskProfileFixture, TaskProfile) {\n    // Treehugger runs host tests inside a container without cgroupv2 support.\n    if (!IsCgroupV2MountedRw()) {\n        GTEST_SKIP();\n        return;\n    }\n    const TestParam params = GetParam();\n    ProfileAttributeMock pa(params.attr_name);\n    // Test simple profile with one action\n    std::shared_ptr<TaskProfile> tp = std::make_shared<TaskProfile>(\"test_profile\");\n    tp->Add(std::make_unique<SetAttributeAction>(&pa, params.attr_value, params.optional_attr));\n    EXPECT_EQ(tp->IsValidForProcess(getuid(), getpid()), params.result);\n    EXPECT_EQ(tp->IsValidForTask(getpid()), params.result);\n    // Test aggregate profile\n    TaskProfile tp2(\"meta_profile\");\n    std::vector<std::shared_ptr<TaskProfile>> profiles = {tp};\n    tp2.Add(std::make_unique<ApplyProfileAction>(profiles));\n    EXPECT_EQ(tp2.IsValidForProcess(getuid(), getpid()), params.result);\n    EXPECT_EQ(tp2.IsValidForTask(getpid()), params.result);\n}\n\n// Test the four combinations of optional_attr {false, true} and cgroup attribute { does not exist,\n// exists }.\nINSTANTIATE_TEST_SUITE_P(\n        SetAttributeTestSuite, SetAttributeFixture,\n        Values(\n                // Test that attempting to write into a non-existing cgroup attribute fails and also\n                // that an error message is logged.\n                TestParam{.attr_name = \"no-such-attribute\",\n                          .attr_value = \".\",\n                          .optional_attr = false,\n                          .result = false,\n                          .log_severity = ERROR,\n                          .log_prefix = \"No such cgroup attribute\"},\n                // Test that attempting to write into an optional non-existing cgroup attribute\n                // results in the return value 'true' and also that no messages are logged.\n                TestParam{.attr_name = \"no-such-attribute\",\n                          .attr_value = \".\",\n                          .optional_attr = true,\n                          .result = true},\n                // Test that attempting to write an invalid value into an existing optional cgroup\n                // attribute fails and also that it causes an error\n                // message to be logged.\n                TestParam{.attr_name = \"cgroup.procs\",\n                          .attr_value = \"-1\",\n                          .optional_attr = true,\n                          .result = false,\n                          .log_severity = ERROR,\n                          .log_prefix = \"Failed to write\",\n                          .log_suffix = geteuid() == 0 ? \"Invalid argument\" : \"Permission denied\"},\n                // Test that attempting to write into an existing optional read-only cgroup\n                // attribute fails and also that it causes an error message to be logged.\n                TestParam{\n                        .attr_name = \"cgroup.controllers\",\n                        .attr_value = \".\",\n                        .optional_attr = false,\n                        .result = false,\n                        .log_severity = ERROR,\n                        .log_prefix = \"Failed to write\",\n                        .log_suffix = geteuid() == 0 ? \"Invalid argument\" : \"Permission denied\"}));\n\n// Test TaskProfile IsValid calls.\nINSTANTIATE_TEST_SUITE_P(\n        TaskProfileTestSuite, TaskProfileFixture,\n        Values(\n                // Test operating on non-existing cgroup attribute fails.\n                TestParam{.attr_name = \"no-such-attribute\",\n                          .attr_value = \".\",\n                          .optional_attr = false,\n                          .result = false},\n                // Test operating on optional non-existing cgroup attribute succeeds.\n                TestParam{.attr_name = \"no-such-attribute\",\n                          .attr_value = \".\",\n                          .optional_attr = true,\n                          .result = true},\n                // Test operating on existing cgroup attribute succeeds.\n                TestParam{.attr_name = \"cgroup.procs\",\n                          .attr_value = \".\",\n                          .optional_attr = false,\n                          .result = true},\n                // Test operating on optional existing cgroup attribute succeeds.\n                TestParam{.attr_name = \"cgroup.procs\",\n                          .attr_value = \".\",\n                          .optional_attr = true,\n                          .result = true}));\n}  // namespace\n"
  },
  {
    "path": "libprocessgroup/tools/Android.bp",
    "content": "// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"settaskprofile\",\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    srcs: [\"settaskprofile.cpp\"],\n    shared_libs: [\n        \"libprocessgroup\",\n    ],\n}\n"
  },
  {
    "path": "libprocessgroup/tools/settaskprofile.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdlib.h>\n\n#include <iostream>\n\n#include <processgroup/processgroup.h>\n\n[[noreturn]] static void usage(int exit_status) {\n    std::cerr << \"Usage: \" << getprogname() << \" <tid> <profile> [... profileN]\" << std::endl\n              << \"    tid      Thread ID to apply the profiles to.\" << std::endl\n              << \"    profile  Name of the profile to apply.\" << std::endl\n              << \"Applies listed profiles to the thread with specified ID.\" << std::endl\n              << \"Profiles are applied in the order specified in the command line.\" << std::endl\n              << \"If applying a profile fails, remaining profiles are ignored.\" << std::endl;\n    exit(exit_status);\n}\n\nint main(int argc, char* argv[]) {\n    if (argc < 3) {\n        usage(EXIT_FAILURE);\n    }\n\n    int tid = atoi(argv[1]);\n    if (tid == 0) {\n        std::cerr << \"Invalid thread id\" << std::endl;\n        exit(EXIT_FAILURE);\n    }\n\n    for (int i = 2; i < argc; i++) {\n        if (!SetTaskProfiles(tid, {argv[i]})) {\n            std::cerr << \"Failed to apply \" << argv[i] << \" profile\" << std::endl;\n            exit(EXIT_FAILURE);\n        }\n        std::cout << \"Profile \" << argv[i] << \" is applied successfully!\" << std::endl;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "libprocessgroup/util/Android.bp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_team: \"trendy_team_android_kernel\",\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libprocessgroup_util\",\n    cpp_std: \"gnu++23\",\n    vendor_available: true,\n    product_available: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    host_supported: true,\n    native_bridge_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"30\",\n    export_include_dirs: [\n        \"include\",\n    ],\n    srcs: [\n        \"cgroup_controller.cpp\",\n        \"cgroup_descriptor.cpp\",\n        \"util.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    static_libs: [\n        \"libjsoncpp\",\n    ],\n}\n\ncc_test {\n    name: \"libprocessgroup_util_test\",\n    static_libs: [\"libprocessgroup_util\"],\n    srcs: [\"tests/util.cpp\"],\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "libprocessgroup/util/OWNERS",
    "content": "# Bug component: 1293033\nsurenb@google.com\ntjmercier@google.com\n"
  },
  {
    "path": "libprocessgroup/util/TEST_MAPPING",
    "content": "{\n  \"postsubmit\": [\n    {\n      \"name\": \"libprocessgroup_util_test\"\n    }\n  ]\n}"
  },
  {
    "path": "libprocessgroup/util/cgroup_controller.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <processgroup/cgroup_controller.h>\n\n#include <cstring>\n\nCgroupController::CgroupController(uint32_t version, uint32_t flags, const std::string& name,\n                                   const std::string& path, uint32_t max_activation_depth)\n    : version_(version), flags_(flags), max_activation_depth_(max_activation_depth) {\n    // strlcpy isn't available on host. Although there is an implementation\n    // in licutils, libcutils itself depends on libcgrouprc_format, causing\n    // a circular dependency.\n    strncpy(name_, name.c_str(), sizeof(name_) - 1);\n    name_[sizeof(name_) - 1] = '\\0';\n    strncpy(path_, path.c_str(), sizeof(path_) - 1);\n    path_[sizeof(path_) - 1] = '\\0';\n}\n\nuint32_t CgroupController::version() const {\n    return version_;\n}\n\nuint32_t CgroupController::flags() const {\n    return flags_;\n}\n\nuint32_t CgroupController::max_activation_depth() const {\n    return max_activation_depth_;\n}\n\nconst char* CgroupController::name() const {\n    return name_;\n}\n\nconst char* CgroupController::path() const {\n    return path_;\n}\n\nvoid CgroupController::set_flags(uint32_t flags) {\n    flags_ = flags;\n}"
  },
  {
    "path": "libprocessgroup/util/cgroup_descriptor.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <processgroup/cgroup_descriptor.h>\n\n#include <processgroup/util.h>  // For flag values\n\nCgroupDescriptor::CgroupDescriptor(uint32_t version, const std::string& name,\n                                   const std::string& path, mode_t mode, const std::string& uid,\n                                   const std::string& gid, uint32_t flags,\n                                   uint32_t max_activation_depth)\n    : controller_(version, flags, name, path, max_activation_depth),\n      mode_(mode),\n      uid_(uid),\n      gid_(gid) {}\n\nvoid CgroupDescriptor::set_mounted(bool mounted) {\n    uint32_t flags = controller_.flags();\n    if (mounted) {\n        flags |= CGROUPRC_CONTROLLER_FLAG_MOUNTED;\n    } else {\n        flags &= ~CGROUPRC_CONTROLLER_FLAG_MOUNTED;\n    }\n    controller_.set_flags(flags);\n}\n"
  },
  {
    "path": "libprocessgroup/util/include/processgroup/cgroup_controller.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <string>\n\n// Minimal controller description\nstruct CgroupController {\n  public:\n    CgroupController() = default;\n    CgroupController(uint32_t version, uint32_t flags, const std::string& name,\n                     const std::string& path, uint32_t max_activation_depth);\n\n    uint32_t version() const;\n    uint32_t flags() const;\n    uint32_t max_activation_depth() const;\n    const char* name() const;\n    const char* path() const;\n\n    void set_flags(uint32_t flags);\n\n  private:\n    static constexpr size_t CGROUP_NAME_BUF_SZ = 16;\n    static constexpr size_t CGROUP_PATH_BUF_SZ = 32;\n\n    uint32_t version_ = 0;\n    uint32_t flags_ = 0;\n    uint32_t max_activation_depth_ = UINT32_MAX;\n    char name_[CGROUP_NAME_BUF_SZ] = {};\n    char path_[CGROUP_PATH_BUF_SZ] = {};\n};"
  },
  {
    "path": "libprocessgroup/util/include/processgroup/cgroup_descriptor.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n\n#include <sys/stat.h>\n\n#include <processgroup/cgroup_controller.h>\n\n// Complete controller description for mounting cgroups\nclass CgroupDescriptor {\n  public:\n    CgroupDescriptor(uint32_t version, const std::string& name, const std::string& path,\n                     mode_t mode, const std::string& uid, const std::string& gid, uint32_t flags,\n                     uint32_t max_activation_depth);\n\n    const CgroupController* controller() const { return &controller_; }\n    mode_t mode() const { return mode_; }\n    std::string uid() const { return uid_; }\n    std::string gid() const { return gid_; }\n\n    void set_mounted(bool mounted);\n\n  private:\n    CgroupController controller_;\n    mode_t mode_ = 0;\n    std::string uid_;\n    std::string gid_;\n};\n"
  },
  {
    "path": "libprocessgroup/util/include/processgroup/util.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <map>\n#include <string>\n\n#include \"cgroup_descriptor.h\"\n\n// Duplicated from cgrouprc.h. Don't depend on libcgrouprc here.\n#define CGROUPRC_CONTROLLER_FLAG_MOUNTED 0x1\n#define CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION 0x2\n#define CGROUPRC_CONTROLLER_FLAG_OPTIONAL 0x4\n\nunsigned int GetCgroupDepth(const std::string& controller_root, const std::string& cgroup_path);\n\nusing CgroupControllerName = std::string;\nusing CgroupDescriptorMap = std::map<CgroupControllerName, CgroupDescriptor>;\nbool ReadDescriptors(CgroupDescriptorMap* descriptors);\n\nbool ActivateControllers(const std::string& path, const CgroupDescriptorMap& descriptors);\n"
  },
  {
    "path": "libprocessgroup/util/tests/util.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <processgroup/util.h>\n\n#include \"gtest/gtest.h\"\n\nTEST(EmptyInputs, bothEmpty) {\n    EXPECT_EQ(GetCgroupDepth({}, {}), 0);\n}\n\nTEST(EmptyInputs, rootEmpty) {\n    EXPECT_EQ(GetCgroupDepth({}, \"foo\"), 0);\n}\n\nTEST(EmptyInputs, pathEmpty) {\n    EXPECT_EQ(GetCgroupDepth(\"foo\", {}), 0);\n}\n\nTEST(InvalidInputs, pathNotInRoot) {\n    EXPECT_EQ(GetCgroupDepth(\"foo\", \"bar\"), 0);\n}\n\nTEST(InvalidInputs, rootLargerThanPath) {\n    EXPECT_EQ(GetCgroupDepth(\"/a/long/path\", \"/short\"), 0);\n}\n\nTEST(InvalidInputs, pathLargerThanRoot) {\n    EXPECT_EQ(GetCgroupDepth(\"/short\", \"/a/long/path\"), 0);\n}\n\nTEST(InvalidInputs, missingSeparator) {\n    EXPECT_EQ(GetCgroupDepth(\"/controller/root\", \"/controller/rootcgroup\"), 0);\n}\n\nTEST(ExtraSeparators, root) {\n    EXPECT_EQ(GetCgroupDepth(\"///sys/fs/cgroup\", \"/sys/fs/cgroup/a/b/c\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys///fs/cgroup\", \"/sys/fs/cgroup/a/b/c\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs///cgroup\", \"/sys/fs/cgroup/a/b/c\"), 3);\n\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"///sys/fs/cgroup/a/b/c\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys///fs/cgroup/a/b/c\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs///cgroup/a/b/c\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup///a/b/c\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/a///b/c\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/a/b///c\"), 3);\n}\n\nTEST(SeparatorEndings, rootEndsInSeparator) {\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup/\", \"/sys/fs/cgroup/a/b\"), 2);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup///\", \"/sys/fs/cgroup/a/b\"), 2);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup/\", \"/sys/fs/cgroup/a/b/\"), 2);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup///\", \"/sys/fs/cgroup/a/b/\"), 2);\n}\n\nTEST(SeparatorEndings, pathEndsInSeparator) {\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/a/b/\"), 2);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/a/b///\"), 2);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup/\", \"/sys/fs/cgroup/a/b/\"), 2);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup/\", \"/sys/fs/cgroup/a/b///\"), 2);\n}\n\nTEST(ValidInputs, rootHasZeroDepth) {\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup\"), 0);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup/\", \"/sys/fs/cgroup\"), 0);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/\"), 0);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup/\", \"/sys/fs/cgroup/\"), 0);\n}\n\nTEST(ValidInputs, atLeastDepth10) {\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/a/b/c/d/e/f/g/h/i/j\"), 10);\n}\n\nTEST(ValidInputs, androidCgroupNames) {\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/system/uid_0/pid_1000\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/uid_0/pid_1000\"), 2);\n\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/apps/uid_100000/pid_1000\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/uid_100000/pid_1000\"), 2);\n\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/apps\"), 1);\n    EXPECT_EQ(GetCgroupDepth(\"/sys/fs/cgroup\", \"/sys/fs/cgroup/system\"), 1);\n}\n\nTEST(ValidInputs, androidCgroupNames_nonDefaultRoot) {\n    EXPECT_EQ(GetCgroupDepth(\"/custom/root\", \"/custom/root/system/uid_0/pid_1000\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/custom/root\", \"/custom/root/uid_0/pid_1000\"), 2);\n\n    EXPECT_EQ(GetCgroupDepth(\"/custom/root\", \"/custom/root/apps/uid_100000/pid_1000\"), 3);\n    EXPECT_EQ(GetCgroupDepth(\"/custom/root\", \"/custom/root/uid_100000/pid_1000\"), 2);\n\n    EXPECT_EQ(GetCgroupDepth(\"/custom/root\", \"/custom/root/apps\"), 1);\n    EXPECT_EQ(GetCgroupDepth(\"/custom/root\", \"/custom/root/system\"), 1);\n}\n"
  },
  {
    "path": "libprocessgroup/util/util.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <processgroup/util.h>\n\n#include <algorithm>\n#include <iterator>\n#include <optional>\n#include <string_view>\n\n#include <mntent.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <json/reader.h>\n#include <json/value.h>\n\n#include \"../build_flags.h\"\n#include \"../internal.h\"\n\nusing android::base::GetUintProperty;\n\nnamespace {\n\nconstexpr const char* CGROUPS_DESC_FILE = \"/etc/cgroups.json\";\nconstexpr const char* CGROUPS_DESC_VENDOR_FILE = \"/vendor/etc/cgroups.json\";\nconstexpr const char* TEMPLATE_CGROUPS_DESC_API_FILE = \"/etc/task_profiles/cgroups_%u.json\";\n\n// This should match the publicly declared value in processgroup.h,\n// but we don't want this library to depend on libprocessgroup.\nconstexpr std::string CGROUPV2_HIERARCHY_NAME_INTERNAL = \"cgroup2\";\n\nconst char SEP = '/';\n\nstd::string DeduplicateAndTrimSeparators(const std::string& path) {\n    bool lastWasSep = false;\n    std::string ret;\n\n    std::copy_if(path.begin(), path.end(), std::back_inserter(ret), [&lastWasSep](char c) {\n        if (lastWasSep) {\n            if (c == SEP) return false;\n            lastWasSep = false;\n        } else if (c == SEP) {\n            lastWasSep = true;\n        }\n        return true;\n    });\n\n    if (ret.length() > 1 && ret.back() == SEP) ret.pop_back();\n\n    return ret;\n}\n\nvoid MergeCgroupToDescriptors(CgroupDescriptorMap* descriptors, const Json::Value& cgroup,\n                              const std::string& name, const std::string& root_path,\n                              int cgroups_version) {\n    const std::string cgroup_path = cgroup[\"Path\"].asString();\n    std::string path;\n\n    if (!root_path.empty()) {\n        path = root_path;\n        if (cgroup_path != \".\") {\n            path += \"/\";\n            path += cgroup_path;\n        }\n    } else {\n        path = cgroup_path;\n    }\n\n    uint32_t controller_flags = 0;\n\n    if (cgroup[\"NeedsActivation\"].isBool() && cgroup[\"NeedsActivation\"].asBool()) {\n        controller_flags |= CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION;\n    }\n\n    if (cgroup[\"Optional\"].isBool() && cgroup[\"Optional\"].asBool()) {\n        controller_flags |= CGROUPRC_CONTROLLER_FLAG_OPTIONAL;\n    }\n\n    uint32_t max_activation_depth = UINT32_MAX;\n    if (cgroup.isMember(\"MaxActivationDepth\")) {\n        max_activation_depth = cgroup[\"MaxActivationDepth\"].asUInt();\n    }\n\n    CgroupDescriptor descriptor(\n            cgroups_version, name, path, std::strtoul(cgroup[\"Mode\"].asString().c_str(), 0, 8),\n            cgroup[\"UID\"].asString(), cgroup[\"GID\"].asString(), controller_flags,\n            max_activation_depth);\n\n    auto iter = descriptors->find(name);\n    if (iter == descriptors->end()) {\n        descriptors->emplace(name, descriptor);\n    } else {\n        iter->second = descriptor;\n    }\n}\n\nbool ReadDescriptorsFromFile(const std::string& file_name, CgroupDescriptorMap* descriptors) {\n    std::vector<CgroupDescriptor> result;\n    std::string json_doc;\n\n    if (!android::base::ReadFileToString(file_name, &json_doc)) {\n        PLOG(ERROR) << \"Failed to read task profiles from \" << file_name;\n        return false;\n    }\n\n    Json::CharReaderBuilder builder;\n    std::unique_ptr<Json::CharReader> reader(builder.newCharReader());\n    Json::Value root;\n    std::string errorMessage;\n    if (!reader->parse(&*json_doc.begin(), &*json_doc.end(), &root, &errorMessage)) {\n        LOG(ERROR) << \"Failed to parse cgroups description: \" << errorMessage;\n        return false;\n    }\n\n    if (root.isMember(\"Cgroups\")) {\n        const Json::Value& cgroups = root[\"Cgroups\"];\n        for (Json::Value::ArrayIndex i = 0; i < cgroups.size(); ++i) {\n            std::string name = cgroups[i][\"Controller\"].asString();\n            MergeCgroupToDescriptors(descriptors, cgroups[i], name, \"\", 1);\n        }\n    }\n\n    std::string root_path;\n    if (root.isMember(\"Cgroups2\")) {\n        const Json::Value& cgroups2 = root[\"Cgroups2\"];\n        root_path = cgroups2[\"Path\"].asString();\n        MergeCgroupToDescriptors(descriptors, cgroups2, CGROUPV2_HIERARCHY_NAME_INTERNAL, \"\", 2);\n\n        const Json::Value& childGroups = cgroups2[\"Controllers\"];\n        for (Json::Value::ArrayIndex i = 0; i < childGroups.size(); ++i) {\n            std::string name = childGroups[i][\"Controller\"].asString();\n            MergeCgroupToDescriptors(descriptors, childGroups[i], name, root_path, 2);\n        }\n    }\n\n    return true;\n}\n\nusing MountDir = std::string;\nusing MountOpts = std::string;\nstatic std::optional<std::map<MountDir, MountOpts>> ReadCgroupV1Mounts() {\n    FILE* fp = setmntent(\"/proc/mounts\", \"r\");\n    if (fp == nullptr) {\n        PLOG(ERROR) << \"Failed to read mounts\";\n        return std::nullopt;\n    }\n\n    std::map<MountDir, MountOpts> mounts;\n    const std::string_view CGROUP_V1_TYPE = \"cgroup\";\n    for (mntent* mentry = getmntent(fp); mentry != nullptr; mentry = getmntent(fp)) {\n        if (mentry->mnt_type && CGROUP_V1_TYPE == mentry->mnt_type &&\n            mentry->mnt_dir && mentry->mnt_opts) {\n            mounts[mentry->mnt_dir] = mentry->mnt_opts;\n        }\n    }\n    endmntent(fp);\n\n    return mounts;\n}\n\n}  // anonymous namespace\n\n\nunsigned int GetCgroupDepth(const std::string& controller_root, const std::string& cgroup_path) {\n    const std::string deduped_root = DeduplicateAndTrimSeparators(controller_root);\n    const std::string deduped_path = DeduplicateAndTrimSeparators(cgroup_path);\n\n    if (deduped_root.empty() || deduped_path.empty() || !deduped_path.starts_with(deduped_root))\n        return 0;\n\n    return std::count(deduped_path.begin() + deduped_root.size(), deduped_path.end(), SEP);\n}\n\nbool ReadDescriptors(CgroupDescriptorMap* descriptors) {\n    // load system cgroup descriptors\n    if (!ReadDescriptorsFromFile(CGROUPS_DESC_FILE, descriptors)) {\n        return false;\n    }\n\n    // load API-level specific system cgroups descriptors if available\n    unsigned int api_level = GetUintProperty<unsigned int>(\"ro.product.first_api_level\", 0);\n    if (api_level > 0) {\n        std::string api_cgroups_path =\n                android::base::StringPrintf(TEMPLATE_CGROUPS_DESC_API_FILE, api_level);\n        if (!access(api_cgroups_path.c_str(), F_OK) || errno != ENOENT) {\n            if (!ReadDescriptorsFromFile(api_cgroups_path, descriptors)) {\n                return false;\n            }\n        }\n    }\n\n    // load vendor cgroup descriptors if the file exists\n    if (!access(CGROUPS_DESC_VENDOR_FILE, F_OK) &&\n        !ReadDescriptorsFromFile(CGROUPS_DESC_VENDOR_FILE, descriptors)) {\n        return false;\n    }\n\n    // check for v1 mount/usability status\n    std::optional<std::map<MountDir, MountOpts>> v1Mounts;\n    for (auto& [name, descriptor] : *descriptors) {\n        const CgroupController* const controller = descriptor.controller();\n\n        if (controller->version() != 1) continue;\n\n        // Read only once, and only if we have at least one v1 controller\n        if (!v1Mounts) {\n            v1Mounts = ReadCgroupV1Mounts();\n            if (!v1Mounts) return false;\n        }\n\n        if (const auto it = v1Mounts->find(controller->path()); it != v1Mounts->end()) {\n            if (it->second.contains(controller->name())) descriptor.set_mounted(true);\n        }\n    }\n\n    return true;\n}\n\nbool ActivateControllers(const std::string& path, const CgroupDescriptorMap& descriptors) {\n    for (const auto& [name, descriptor] : descriptors) {\n        const uint32_t flags = descriptor.controller()->flags();\n        const uint32_t max_activation_depth = descriptor.controller()->max_activation_depth();\n        const unsigned int depth = GetCgroupDepth(descriptor.controller()->path(), path);\n\n        if (flags & CGROUPRC_CONTROLLER_FLAG_NEEDS_ACTIVATION && depth < max_activation_depth) {\n            std::string str(\"+\");\n            str.append(descriptor.controller()->name());\n            if (!android::base::WriteStringToFile(str, path + \"/cgroup.subtree_control\")) {\n                if (flags & CGROUPRC_CONTROLLER_FLAG_OPTIONAL) {\n                    PLOG(WARNING) << \"Activation of cgroup controller \" << str\n                                  << \" failed in path \" << path;\n                } else {\n                    return false;\n                }\n            }\n        }\n    }\n    return true;\n}\n\n"
  },
  {
    "path": "libprocessgroup/vts/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test {\n    name: \"vts_libprocessgroup\",\n    srcs: [\"vts_libprocessgroup.cpp\"],\n    shared_libs: [\"libbase\"],\n    static_libs: [\"libgmock\"],\n    require_root: true,\n    test_suites: [\n        \"general-tests\",\n        \"vts\",\n    ],\n}\n"
  },
  {
    "path": "libprocessgroup/vts/vts_libprocessgroup.cpp",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <cerrno>\n#include <cstdio>\n#include <filesystem>\n#include <iostream>\n#include <optional>\n#include <random>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/strings.h>\nusing android::base::ReadFileToString;\nusing android::base::Split;\nusing android::base::WriteStringToFile;\n\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n\nnamespace {\n\nconst std::string CGROUP_V2_ROOT_PATH = \"/sys/fs/cgroup\";\n\nstd::optional<bool> isMemcgV2Enabled() {\n    if (std::string proc_cgroups; ReadFileToString(\"/proc/cgroups\", &proc_cgroups)) {\n        const std::vector<std::string> lines = Split(proc_cgroups, \"\\n\");\n        for (const std::string& line : lines) {\n            if (line.starts_with(\"memory\")) {\n                const bool enabled = line.back() == '1';\n                if (!enabled) return false;\n\n                const std::vector<std::string> memcg_tokens = Split(line, \"\\t\");\n                return memcg_tokens[1] == \"0\";  // 0 == default hierarchy == v2\n            }\n        }\n        // We know for sure it's not enabled, either because it is mounted as v1 (cgroups.json\n        // override) which would be detected above, or because it was intentionally disabled via\n        // kernel command line (cgroup_disable=memory), or because it's not built in to the kernel\n        // (CONFIG_MEMCG is not set).\n        return false;\n    }\n\n    // Problems accessing /proc/cgroups (sepolicy?) Try checking the root cgroup.controllers file.\n    perror(\"Warning: Could not read /proc/cgroups\");\n    if (std::string controllers;\n        ReadFileToString(CGROUP_V2_ROOT_PATH + \"/cgroup.controllers\", &controllers)) {\n        return controllers.find(\"memory\") != std::string::npos;\n    }\n\n    std::cerr << \"Error: Could not read \" << CGROUP_V2_ROOT_PATH\n              << \"/cgroup.controllers: \" << std::strerror(errno) << std::endl;\n    return std::nullopt;\n}\n\nstd::optional<bool> checkRootSubtreeState() {\n    if (std::string controllers;\n        ReadFileToString(CGROUP_V2_ROOT_PATH + \"/cgroup.subtree_control\", &controllers)) {\n        return controllers.find(\"memory\") != std::string::npos;\n    }\n    std::cerr << \"Error: Could not read \" << CGROUP_V2_ROOT_PATH\n              << \"/cgroup.subtree_control: \" << std::strerror(errno) << std::endl;\n    return std::nullopt;\n}\n\n}  // anonymous namespace\n\nclass MemcgV2SubdirTest : public testing::Test {\n  protected:\n    std::optional<std::string> mRandDir;\n\n    void SetUp() override {\n        std::optional<bool> memcgV2Enabled = isMemcgV2Enabled();\n        ASSERT_NE(memcgV2Enabled, std::nullopt);\n        if (!*memcgV2Enabled) GTEST_SKIP() << \"Memcg v2 not enabled\";\n\n        mRootSubtreeState = checkRootSubtreeState();\n        ASSERT_NE(mRootSubtreeState, std::nullopt);\n\n        if (!*mRootSubtreeState) {\n            ASSERT_TRUE(\n                    WriteStringToFile(\"+memory\", CGROUP_V2_ROOT_PATH + \"/cgroup.subtree_control\"))\n                    << \"Could not enable memcg under root: \" << std::strerror(errno);\n        }\n\n        // Make a new, temporary, randomly-named v2 cgroup in which we will attempt to activate\n        // memcg\n        std::random_device rd;\n        std::uniform_int_distribution dist(static_cast<int>('A'), static_cast<int>('Z'));\n        std::string randName = CGROUP_V2_ROOT_PATH + \"/vts_libprocessgroup.\";\n        for (int i = 0; i < 10; ++i) randName.append(1, static_cast<char>(dist(rd)));\n        ASSERT_TRUE(std::filesystem::create_directory(randName));\n        mRandDir = randName;  // For cleanup in TearDown\n\n        std::string subtree_controllers;\n        ASSERT_TRUE(ReadFileToString(*mRandDir + \"/cgroup.controllers\", &subtree_controllers));\n        ASSERT_NE(subtree_controllers.find(\"memory\"), std::string::npos)\n                << \"Memcg was not activated in child cgroup\";\n    }\n\n    void TearDown() override {\n        if (mRandDir) {\n            if (!std::filesystem::remove(*mRandDir)) {\n                std::cerr << \"Could not remove temporary memcg v2 test directory\" << std::endl;\n            }\n        }\n\n        if (!*mRootSubtreeState) {\n            if (!WriteStringToFile(\"-memory\", CGROUP_V2_ROOT_PATH + \"/cgroup.subtree_control\")) {\n                std::cerr << \"Could not disable memcg under root: \" << std::strerror(errno)\n                          << std::endl;\n            }\n        }\n    }\n\n  private:\n    std::optional<bool> mRootSubtreeState;\n};\n\n\nTEST_F(MemcgV2SubdirTest, CanActivateMemcgV2Subtree) {\n    ASSERT_TRUE(WriteStringToFile(\"+memory\", *mRandDir + \"/cgroup.subtree_control\"))\n            << \"Could not enable memcg under child cgroup subtree\";\n}\n"
  },
  {
    "path": "libsparse/Android.bp",
    "content": "// Copyright 2010 The Android Open Source Project\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libsparse\",\n    host_supported: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    unique_host_soname: true,\n    vendor_available: true,\n    srcs: [\n        \"backed_block.cpp\",\n        \"output_file.cpp\",\n        \"sparse.cpp\",\n        \"sparse_crc32.cpp\",\n        \"sparse_err.cpp\",\n        \"sparse_read.cpp\",\n    ],\n    cflags: [\"-Werror\"],\n    local_include_dirs: [\"include\"],\n    export_include_dirs: [\"include\"],\n    shared_libs: [\n        \"libz\",\n        \"libbase\",\n    ],\n    target: {\n        darwin: {\n            enabled: true,\n        },\n        windows: {\n            enabled: true,\n        },\n    },\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.virt\",\n    ],\n}\n\ncc_binary_host {\n    name: \"simg2img\",\n    srcs: [\n        \"simg2img.cpp\",\n        \"sparse_crc32.cpp\",\n    ],\n    static_libs: [\n        \"libsparse\",\n        \"libz\",\n        \"libbase\",\n    ],\n\n    cflags: [\"-Werror\"],\n    target: {\n        darwin: {\n            enabled: true,\n        },\n    },\n}\n\ncc_binary_host {\n    name: \"img2simg\",\n    srcs: [\"img2simg.cpp\"],\n    static_libs: [\n        \"libsparse\",\n        \"libz\",\n        \"libbase\",\n    ],\n\n    cflags: [\"-Werror\"],\n}\n\ncc_binary_host {\n    name: \"append2simg\",\n    srcs: [\"append2simg.cpp\"],\n    static_libs: [\n        \"libsparse\",\n        \"libz\",\n        \"libbase\",\n    ],\n\n    cflags: [\"-Werror\"],\n}\n\npython_binary_host {\n    name: \"simg_dump\",\n    main: \"simg_dump.py\",\n    srcs: [\"simg_dump.py\"],\n}\n\ncc_fuzz {\n    name: \"sparse_fuzzer\",\n    host_supported: true,\n    srcs: [\n        \"sparse_fuzzer.cpp\",\n    ],\n    static_libs: [\n        \"libsparse\",\n        \"libbase\",\n        \"libz\",\n        \"liblog\",\n    ],\n}\n"
  },
  {
    "path": "libsparse/OWNERS",
    "content": "ccross@google.com\n"
  },
  {
    "path": "libsparse/append2simg.cpp",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define _FILE_OFFSET_BITS 64\n#define _LARGEFILE64_SOURCE 1\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <sparse/sparse.h>\n#include \"backed_block.h\"\n#include \"sparse_file.h\"\n\n#ifndef O_BINARY\n#define O_BINARY 0\n#endif\n\n#if defined(__APPLE__) && defined(__MACH__)\n#define lseek64 lseek\n#endif\n#if defined(__APPLE__) && defined(__MACH__)\n#define lseek64 lseek\n#define off64_t off_t\n#endif\n\nvoid usage() {\n  fprintf(stderr, \"Usage: append2simg <output> <input>\\n\");\n}\n\nint main(int argc, char* argv[]) {\n  int output;\n  int output_block;\n  char* output_path;\n  struct sparse_file* sparse_output;\n\n  int input;\n  char* input_path;\n  off64_t input_len;\n\n  int tmp_fd;\n  char* tmp_path;\n\n  int ret;\n\n  if (argc == 3) {\n    output_path = argv[1];\n    input_path = argv[2];\n  } else {\n    usage();\n    exit(EXIT_FAILURE);\n  }\n\n  ret = asprintf(&tmp_path, \"%s.append2simg\", output_path);\n  if (ret < 0) {\n    fprintf(stderr, \"Couldn't allocate filename\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  output = open(output_path, O_RDWR | O_BINARY);\n  if (output < 0) {\n    fprintf(stderr, \"Couldn't open output file (%s)\\n\", strerror(errno));\n    exit(EXIT_FAILURE);\n  }\n\n  sparse_output = sparse_file_import_auto(output, false, true);\n  if (!sparse_output) {\n    fprintf(stderr, \"Couldn't import output file\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  input = open(input_path, O_RDONLY | O_BINARY);\n  if (input < 0) {\n    fprintf(stderr, \"Couldn't open input file (%s)\\n\", strerror(errno));\n    exit(EXIT_FAILURE);\n  }\n\n  input_len = lseek64(input, 0, SEEK_END);\n  if (input_len < 0) {\n    fprintf(stderr, \"Couldn't get input file length (%s)\\n\", strerror(errno));\n    exit(EXIT_FAILURE);\n  } else if (input_len % sparse_output->block_size) {\n    fprintf(stderr, \"Input file is not a multiple of the output file's block size\");\n    exit(EXIT_FAILURE);\n  }\n  lseek64(input, 0, SEEK_SET);\n\n  output_block = sparse_output->len / sparse_output->block_size;\n  if (sparse_file_add_fd(sparse_output, input, 0, input_len, output_block) < 0) {\n    fprintf(stderr, \"Couldn't add input file\\n\");\n    exit(EXIT_FAILURE);\n  }\n  sparse_output->len += input_len;\n\n  tmp_fd = open(tmp_path, O_WRONLY | O_CREAT | O_BINARY, 0664);\n  if (tmp_fd < 0) {\n    fprintf(stderr, \"Couldn't open temporary file (%s)\\n\", strerror(errno));\n    exit(EXIT_FAILURE);\n  }\n\n  lseek64(output, 0, SEEK_SET);\n  if (sparse_file_write(sparse_output, tmp_fd, false, true, false) < 0) {\n    fprintf(stderr, \"Failed to write sparse file\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  sparse_file_destroy(sparse_output);\n  close(tmp_fd);\n  close(output);\n  close(input);\n\n  ret = rename(tmp_path, output_path);\n  if (ret < 0) {\n    fprintf(stderr, \"Failed to rename temporary file (%s)\\n\", strerror(errno));\n    exit(EXIT_FAILURE);\n  }\n\n  free(tmp_path);\n\n  exit(EXIT_SUCCESS);\n}\n"
  },
  {
    "path": "libsparse/backed_block.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <assert.h>\n#include <errno.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"backed_block.h\"\n#include \"sparse_defs.h\"\n\nstruct backed_block {\n  unsigned int block;\n  uint64_t len;\n  enum backed_block_type type;\n  union {\n    struct {\n      void* data;\n    } data;\n    struct {\n      char* filename;\n      int64_t offset;\n    } file;\n    struct {\n      int fd;\n      int64_t offset;\n    } fd;\n    struct {\n      uint32_t val;\n    } fill;\n  };\n  struct backed_block* next;\n};\n\nstruct backed_block_list {\n  struct backed_block* data_blocks;\n  struct backed_block* last_used;\n  unsigned int block_size;\n};\n\nstruct backed_block* backed_block_iter_new(struct backed_block_list* bbl) {\n  return bbl->data_blocks;\n}\n\nstruct backed_block* backed_block_iter_next(struct backed_block* bb) {\n  return bb->next;\n}\n\nuint64_t backed_block_len(struct backed_block* bb) {\n  return bb->len;\n}\n\nunsigned int backed_block_block(struct backed_block* bb) {\n  return bb->block;\n}\n\nvoid* backed_block_data(struct backed_block* bb) {\n  assert(bb->type == BACKED_BLOCK_DATA);\n  return bb->data.data;\n}\n\nconst char* backed_block_filename(struct backed_block* bb) {\n  assert(bb->type == BACKED_BLOCK_FILE);\n  return bb->file.filename;\n}\n\nint backed_block_fd(struct backed_block* bb) {\n  assert(bb->type == BACKED_BLOCK_FD);\n  return bb->fd.fd;\n}\n\nint64_t backed_block_file_offset(struct backed_block* bb) {\n  assert(bb->type == BACKED_BLOCK_FILE || bb->type == BACKED_BLOCK_FD);\n  if (bb->type == BACKED_BLOCK_FILE) {\n    return bb->file.offset;\n  } else { /* bb->type == BACKED_BLOCK_FD */\n    return bb->fd.offset;\n  }\n}\n\nuint32_t backed_block_fill_val(struct backed_block* bb) {\n  assert(bb->type == BACKED_BLOCK_FILL);\n  return bb->fill.val;\n}\n\nenum backed_block_type backed_block_type(struct backed_block* bb) {\n  return bb->type;\n}\n\nvoid backed_block_destroy(struct backed_block* bb) {\n  if (bb->type == BACKED_BLOCK_FILE) {\n    free(bb->file.filename);\n  }\n\n  free(bb);\n}\n\nstruct backed_block_list* backed_block_list_new(unsigned int block_size) {\n  struct backed_block_list* b =\n      reinterpret_cast<backed_block_list*>(calloc(sizeof(struct backed_block_list), 1));\n  b->block_size = block_size;\n  return b;\n}\n\nvoid backed_block_list_destroy(struct backed_block_list* bbl) {\n  if (bbl->data_blocks) {\n    struct backed_block* bb = bbl->data_blocks;\n    while (bb) {\n      struct backed_block* next = bb->next;\n      backed_block_destroy(bb);\n      bb = next;\n    }\n  }\n\n  free(bbl);\n}\n\nvoid backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,\n                            struct backed_block* start, struct backed_block* end) {\n  struct backed_block* bb;\n\n  if (start == nullptr) {\n    start = from->data_blocks;\n  }\n\n  if (!end) {\n    for (end = start; end && end->next; end = end->next)\n      ;\n  }\n\n  if (start == nullptr || end == nullptr) {\n    return;\n  }\n\n  from->last_used = nullptr;\n  to->last_used = nullptr;\n  if (from->data_blocks == start) {\n    from->data_blocks = end->next;\n  } else {\n    for (bb = from->data_blocks; bb; bb = bb->next) {\n      if (bb->next == start) {\n        bb->next = end->next;\n        break;\n      }\n    }\n  }\n\n  if (!to->data_blocks) {\n    to->data_blocks = start;\n    end->next = nullptr;\n  } else {\n    for (bb = to->data_blocks; bb; bb = bb->next) {\n      if (!bb->next || bb->next->block > start->block) {\n        end->next = bb->next;\n        bb->next = start;\n        break;\n      }\n    }\n  }\n}\n\n/* may free b */\nstatic int merge_bb(struct backed_block_list* bbl, struct backed_block* a, struct backed_block* b) {\n  unsigned int block_len;\n\n  /* Block doesn't exist (possible if one block is the last block) */\n  if (!a || !b) {\n    return -EINVAL;\n  }\n\n  assert(a->block < b->block);\n\n  /* Blocks are of different types */\n  if (a->type != b->type) {\n    return -EINVAL;\n  }\n\n  /* Blocks are not adjacent */\n  block_len = a->len / bbl->block_size; /* rounds down */\n  if (a->block + block_len != b->block) {\n    return -EINVAL;\n  }\n\n  switch (a->type) {\n    case BACKED_BLOCK_DATA:\n      /* Don't support merging data for now */\n      return -EINVAL;\n    case BACKED_BLOCK_FILL:\n      if (a->fill.val != b->fill.val) {\n        return -EINVAL;\n      }\n      break;\n    case BACKED_BLOCK_FILE:\n      /* Already make sure b->type is BACKED_BLOCK_FILE */\n      if (strcmp(a->file.filename, b->file.filename) || a->file.offset + a->len != b->file.offset) {\n        return -EINVAL;\n      }\n      break;\n    case BACKED_BLOCK_FD:\n      if (a->fd.fd != b->fd.fd || a->fd.offset + a->len != b->fd.offset) {\n        return -EINVAL;\n      }\n      break;\n  }\n\n  /* Blocks are compatible and adjacent, with a before b.  Merge b into a,\n   * and free b */\n  a->len += b->len;\n  a->next = b->next;\n\n  backed_block_destroy(b);\n\n  return 0;\n}\n\nstatic int queue_bb(struct backed_block_list* bbl, struct backed_block* new_bb) {\n  struct backed_block* bb;\n\n  if (bbl->data_blocks == nullptr) {\n    bbl->data_blocks = new_bb;\n    return 0;\n  }\n\n  if (bbl->data_blocks->block > new_bb->block) {\n    new_bb->next = bbl->data_blocks;\n    bbl->data_blocks = new_bb;\n    return 0;\n  }\n\n  /* Optimization: blocks are mostly queued in sequence, so save the\n     pointer to the last bb that was added, and start searching from\n     there if the next block number is higher */\n  if (bbl->last_used && new_bb->block > bbl->last_used->block)\n    bb = bbl->last_used;\n  else\n    bb = bbl->data_blocks;\n  bbl->last_used = new_bb;\n\n  for (; bb->next && bb->next->block < new_bb->block; bb = bb->next)\n    ;\n\n  if (bb->next == nullptr) {\n    bb->next = new_bb;\n  } else {\n    new_bb->next = bb->next;\n    bb->next = new_bb;\n  }\n\n  merge_bb(bbl, new_bb, new_bb->next);\n  if (!merge_bb(bbl, bb, new_bb)) {\n    /* new_bb destroyed, point to retained as last_used */\n    bbl->last_used = bb;\n  }\n\n  return 0;\n}\n\n/* Queues a fill block of memory to be written to the specified data blocks */\nint backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,\n                          unsigned int block) {\n  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));\n  if (bb == nullptr) {\n    return -ENOMEM;\n  }\n\n  bb->block = block;\n  bb->len = len;\n  bb->type = BACKED_BLOCK_FILL;\n  bb->fill.val = fill_val;\n  bb->next = nullptr;\n\n  return queue_bb(bbl, bb);\n}\n\n/* Queues a block of memory to be written to the specified data blocks */\nint backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,\n                          unsigned int block) {\n  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));\n  if (bb == nullptr) {\n    return -ENOMEM;\n  }\n\n  bb->block = block;\n  bb->len = len;\n  bb->type = BACKED_BLOCK_DATA;\n  bb->data.data = data;\n  bb->next = nullptr;\n\n  return queue_bb(bbl, bb);\n}\n\n/* Queues a chunk of a file on disk to be written to the specified data blocks */\nint backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,\n                          uint64_t len, unsigned int block) {\n  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));\n  if (bb == nullptr) {\n    return -ENOMEM;\n  }\n\n  bb->block = block;\n  bb->len = len;\n  bb->type = BACKED_BLOCK_FILE;\n  bb->file.filename = strdup(filename);\n  if (!bb->file.filename) {\n    free(bb);\n    return -ENOMEM;\n  }\n  bb->file.offset = offset;\n  bb->next = nullptr;\n\n  return queue_bb(bbl, bb);\n}\n\n/* Queues a chunk of a fd to be written to the specified data blocks */\nint backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,\n                        unsigned int block) {\n  struct backed_block* bb = reinterpret_cast<backed_block*>(calloc(1, sizeof(struct backed_block)));\n  if (bb == nullptr) {\n    return -ENOMEM;\n  }\n\n  bb->block = block;\n  bb->len = len;\n  bb->type = BACKED_BLOCK_FD;\n  bb->fd.fd = fd;\n  bb->fd.offset = offset;\n  bb->next = nullptr;\n\n  return queue_bb(bbl, bb);\n}\n\nint backed_block_split(struct backed_block_list* bbl, struct backed_block* bb,\n                       unsigned int max_len) {\n  struct backed_block* new_bb;\n\n  max_len = ALIGN_DOWN(max_len, bbl->block_size);\n\n  if (bb->len <= max_len) {\n    return 0;\n  }\n\n  new_bb = reinterpret_cast<backed_block*>(malloc(sizeof(struct backed_block)));\n  if (new_bb == nullptr) {\n    return -ENOMEM;\n  }\n\n  *new_bb = *bb;\n\n  new_bb->len = bb->len - max_len;\n  new_bb->block = bb->block + max_len / bbl->block_size;\n  new_bb->next = bb->next;\n\n  switch (bb->type) {\n    case BACKED_BLOCK_DATA:\n      new_bb->data.data = (char*)bb->data.data + max_len;\n      break;\n    case BACKED_BLOCK_FILE:\n      new_bb->file.filename = strdup(bb->file.filename);\n      if (!new_bb->file.filename) {\n        free(new_bb);\n        return -ENOMEM;\n      }\n      new_bb->file.offset += max_len;\n      break;\n    case BACKED_BLOCK_FD:\n      new_bb->fd.offset += max_len;\n      break;\n    case BACKED_BLOCK_FILL:\n      break;\n  }\n\n  bb->next = new_bb;\n  bb->len = max_len;\n  return 0;\n}\n"
  },
  {
    "path": "libsparse/backed_block.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _BACKED_BLOCK_H_\n#define _BACKED_BLOCK_H_\n\n#include <stdint.h>\n\nstruct backed_block_list;\nstruct backed_block;\n\nenum backed_block_type {\n  BACKED_BLOCK_DATA,\n  BACKED_BLOCK_FILE,\n  BACKED_BLOCK_FD,\n  BACKED_BLOCK_FILL,\n};\n\nint backed_block_add_data(struct backed_block_list* bbl, void* data, uint64_t len,\n                          unsigned int block);\nint backed_block_add_fill(struct backed_block_list* bbl, unsigned int fill_val, uint64_t len,\n                          unsigned int block);\nint backed_block_add_file(struct backed_block_list* bbl, const char* filename, int64_t offset,\n                          uint64_t len, unsigned int block);\nint backed_block_add_fd(struct backed_block_list* bbl, int fd, int64_t offset, uint64_t len,\n                        unsigned int block);\n\nstruct backed_block* backed_block_iter_new(struct backed_block_list* bbl);\nstruct backed_block* backed_block_iter_next(struct backed_block* bb);\nuint64_t backed_block_len(struct backed_block* bb);\nunsigned int backed_block_block(struct backed_block* bb);\nvoid* backed_block_data(struct backed_block* bb);\nconst char* backed_block_filename(struct backed_block* bb);\nint backed_block_fd(struct backed_block* bb);\nint64_t backed_block_file_offset(struct backed_block* bb);\nuint32_t backed_block_fill_val(struct backed_block* bb);\nenum backed_block_type backed_block_type(struct backed_block* bb);\nint backed_block_split(struct backed_block_list* bbl, struct backed_block* bb, unsigned int max_len);\n\nstruct backed_block* backed_block_iter_new(struct backed_block_list* bbl);\nstruct backed_block* backed_block_iter_next(struct backed_block* bb);\n\nstruct backed_block_list* backed_block_list_new(unsigned int block_size);\nvoid backed_block_list_destroy(struct backed_block_list* bbl);\n\nvoid backed_block_list_move(struct backed_block_list* from, struct backed_block_list* to,\n                            struct backed_block* start, struct backed_block* end);\n\n#endif\n"
  },
  {
    "path": "libsparse/defs.h",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSPARSE_DEFS_H_\n\n#ifndef __unused\n#define __unused __attribute__((__unused__))\n#endif\n\n#endif /* _LIBSPARSE_DEFS_H_ */\n"
  },
  {
    "path": "libsparse/img2simg.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define _FILE_OFFSET_BITS 64\n#define _LARGEFILE64_SOURCE 1\n\n#include <fcntl.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <sparse/sparse.h>\n\n#ifndef O_BINARY\n#define O_BINARY 0\n#endif\n\n#if defined(__APPLE__) && defined(__MACH__)\n#define lseek64 lseek\n#define off64_t off_t\n#endif\n\nvoid usage() {\n  fprintf(stderr, \"Usage: img2simg [-s] <raw_image_file> <sparse_image_file> [<block_size>]\\n\");\n}\n\nint main(int argc, char* argv[]) {\n  char *arg_in;\n  char *arg_out;\n  enum sparse_read_mode mode = SPARSE_READ_MODE_NORMAL;\n  int extra;\n  int in;\n  int opt;\n  int out;\n  int ret;\n  struct sparse_file* s;\n  unsigned int block_size = 4096;\n  off64_t len;\n\n  while ((opt = getopt(argc, argv, \"s\")) != -1) {\n    switch (opt) {\n      case 's':\n        mode = SPARSE_READ_MODE_HOLE;\n        break;\n      default:\n        usage();\n        exit(EXIT_FAILURE);\n    }\n  }\n\n  extra = argc - optind;\n  if (extra < 2 || extra > 3) {\n    usage();\n    exit(EXIT_FAILURE);\n  }\n\n  if (extra == 3) {\n    block_size = atoi(argv[optind + 2]);\n  }\n\n  if (block_size < 1024 || block_size % 4 != 0) {\n    usage();\n    exit(EXIT_FAILURE);\n  }\n\n  arg_in = argv[optind];\n  if (strcmp(arg_in, \"-\") == 0) {\n    in = STDIN_FILENO;\n  } else {\n    in = open(arg_in, O_RDONLY | O_BINARY);\n    if (in < 0) {\n      fprintf(stderr, \"Cannot open input file %s\\n\", arg_in);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  arg_out = argv[optind + 1];\n  if (strcmp(arg_out, \"-\") == 0) {\n    out = STDOUT_FILENO;\n  } else {\n    out = open(arg_out, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);\n    if (out < 0) {\n      fprintf(stderr, \"Cannot open output file %s\\n\", arg_out);\n      exit(EXIT_FAILURE);\n    }\n  }\n\n  len = lseek64(in, 0, SEEK_END);\n  lseek64(in, 0, SEEK_SET);\n\n  s = sparse_file_new(block_size, len);\n  if (!s) {\n    fprintf(stderr, \"Failed to create sparse file\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  sparse_file_verbose(s);\n  ret = sparse_file_read(s, in, mode, false);\n  if (ret) {\n    fprintf(stderr, \"Failed to read file\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  ret = sparse_file_write(s, out, false, true, false);\n  if (ret) {\n    fprintf(stderr, \"Failed to write sparse file\\n\");\n    exit(EXIT_FAILURE);\n  }\n\n  close(in);\n  close(out);\n\n  exit(EXIT_SUCCESS);\n}\n"
  },
  {
    "path": "libsparse/include/sparse/sparse.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSPARSE_SPARSE_H_\n#define _LIBSPARSE_SPARSE_H_\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\nstruct sparse_file;\n\n// The callbacks in sparse_file_callback() and sparse_file_foreach_chunk() take\n// size_t as the length type (was `int` in past). This allows clients to keep\n// their codes compatibile with both versions as needed.\n#define\tSPARSE_CALLBACK_USES_SIZE_T\n\n/**\n * sparse_file_new - create a new sparse file cookie\n *\n * @block_size - minimum size of a chunk\n * @len - size of the expanded sparse file.\n *\n * Creates a new sparse_file cookie that can be used to associate data\n * blocks.  Can later be written to a file with a variety of options.\n * block_size specifies the minimum size of a chunk in the file.  The maximum\n * size of the file is 2**32 * block_size (16TB for 4k block size).\n *\n * Returns the sparse file cookie, or NULL on error.\n */\nstruct sparse_file *sparse_file_new(unsigned int block_size, int64_t len);\n\n/**\n * sparse_file_destroy - destroy a sparse file cookie\n *\n * @s - sparse file cookie\n *\n * Destroys a sparse file cookie.  After destroy, all memory passed in to\n * sparse_file_add_data can be freed by the caller\n */\nvoid sparse_file_destroy(struct sparse_file *s);\n\n/**\n * sparse_file_add_data - associate a data chunk with a sparse file\n *\n * @s - sparse file cookie\n * @data - pointer to data block\n * @len - length of the data block\n * @block - offset in blocks into the sparse file to place the data chunk\n *\n * Associates a data chunk with a sparse file cookie.  The region\n * [block * block_size : block * block_size + len) must not already be used in\n * the sparse file. If len is not a multiple of the block size the data\n * will be padded with zeros.\n *\n * The data pointer must remain valid until the sparse file is closed or the\n * data block is removed from the sparse file.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block);\n\n/**\n * sparse_file_add_fill - associate a fill chunk with a sparse file\n *\n * @s - sparse file cookie\n * @fill_val - 32 bit fill data\n * @len - length of the fill block\n * @block - offset in blocks into the sparse file to place the fill chunk\n *\n * Associates a chunk filled with fill_val with a sparse file cookie.\n * The region [block * block_size : block * block_size + len) must not already\n * be used in the sparse file. If len is not a multiple of the block size the\n * data will be padded with zeros.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,\n                         unsigned int block);\n\n/**\n * sparse_file_add_file - associate a chunk of a file with a sparse file\n *\n * @s - sparse file cookie\n * @filename - filename of the file to be copied\n * @file_offset - offset into the copied file\n * @len - length of the copied block\n * @block - offset in blocks into the sparse file to place the file chunk\n *\n * Associates a chunk of an existing file with a sparse file cookie.\n * The region [block * block_size : block * block_size + len) must not already\n * be used in the sparse file. If len is not a multiple of the block size the\n * data will be padded with zeros.\n *\n * Allows adding large amounts of data to a sparse file without needing to keep\n * it all mapped.  File size is limited by available virtual address space,\n * exceptionally large files may need to be added in multiple chunks.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,\n                         uint64_t len, unsigned int block);\n\n/**\n * sparse_file_add_file - associate a chunk of a file with a sparse file\n *\n * @s - sparse file cookie\n * @filename - filename of the file to be copied\n * @file_offset - offset into the copied file\n * @len - length of the copied block\n * @block - offset in blocks into the sparse file to place the file chunk\n *\n * Associates a chunk of an existing fd with a sparse file cookie.\n * The region [block * block_size : block * block_size + len) must not already\n * be used in the sparse file. If len is not a multiple of the block size the\n * data will be padded with zeros.\n *\n * Allows adding large amounts of data to a sparse file without needing to keep\n * it all mapped.  File size is limited by available virtual address space,\n * exceptionally large files may need to be added in multiple chunks.\n *\n * The fd must remain open until the sparse file is closed or the fd block is\n * removed from the sparse file.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,\n                       unsigned int block);\n\n/**\n * sparse_file_write - write a sparse file to a file\n *\n * @s - sparse file cookie\n * @fd - file descriptor to write to\n * @gz - write a gzipped file\n * @sparse - write in the Android sparse file format\n * @crc - append a crc chunk\n *\n * Writes a sparse file to a file.  If gz is true, the data will be passed\n * through zlib.  If sparse is true, the file will be written in the Android\n * sparse file format.  If sparse is false, the file will be written by seeking\n * over unused chunks, producing a smaller file if the filesystem supports\n * sparse files.  If crc is true, the crc of the expanded data will be\n * calculated and appended in a crc chunk.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_write(struct sparse_file *s, int fd, bool gz, bool sparse,\n\t\tbool crc);\n\n/**\n * sparse_file_len - return the length of a sparse file if written to disk\n *\n * @s - sparse file cookie\n * @sparse - write in the Android sparse file format\n * @crc - append a crc chunk\n *\n * Returns the size a sparse file would be on disk if it were written in the\n * specified format.  If sparse is true, this is the size of the data in the\n * sparse format.  If sparse is false, this is the size of the normal\n * non-sparse file.\n */\nint64_t sparse_file_len(struct sparse_file *s, bool sparse, bool crc);\n\n/**\n * sparse_file_block_size\n *\n * @s - sparse file cookie\n */\nunsigned int sparse_file_block_size(struct sparse_file *s);\n\n/**\n * sparse_file_callback - call a callback for blocks in sparse file\n *\n * @s - sparse file cookie\n * @sparse - write in the Android sparse file format\n * @crc - append a crc chunk\n * @write - function to call for each block\n * @priv - value that will be passed as the first argument to write\n *\n * Writes a sparse file by calling a callback function.  If sparse is true, the\n * file will be written in the Android sparse file format.  If crc is true, the\n * crc of the expanded data will be calculated and appended in a crc chunk.\n * The callback 'write' will be called with data and length for each data,\n * and with data==NULL to skip over a region (only used for non-sparse format).\n * The callback should return negative on error, 0 on success.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_callback(struct sparse_file *s, bool sparse, bool crc,\n\t\tint (*write)(void *priv, const void *data, size_t len), void *priv);\n\n/**\n * sparse_file_foreach_chunk - call a callback for data blocks in sparse file\n *\n * @s - sparse file cookie\n * @sparse - write in the Android sparse file format\n * @crc - append a crc chunk\n * @write - function to call for each block\n * @priv - value that will be passed as the first argument to write\n *\n * The function has the same behavior as 'sparse_file_callback', except it only\n * iterates on blocks that contain data.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_foreach_chunk(struct sparse_file *s, bool sparse, bool crc,\n\tint (*write)(void *priv, const void *data, size_t len, unsigned int block,\n\t\t     unsigned int nr_blocks),\n\tvoid *priv);\n\n/**\n * enum sparse_read_mode - The method to use when reading in files\n * @SPARSE_READ_MODE_NORMAL: The input is a regular file. Constant chunks of\n *                           data (including holes) will be be converted to\n *                           fill chunks.\n * @SPARSE_READ_MODE_SPARSE: The input is an Android sparse file.\n * @SPARSE_READ_MODE_HOLE: The input is a regular file. Holes will be converted\n *                         to \"don't care\" chunks. Other constant chunks will\n *                         be converted to fill chunks.\n */\nenum sparse_read_mode {\n\tSPARSE_READ_MODE_NORMAL = false,\n\tSPARSE_READ_MODE_SPARSE = true,\n\tSPARSE_READ_MODE_HOLE,\n};\n\n/**\n * sparse_file_read - read a file into a sparse file cookie\n *\n * @s - sparse file cookie\n * @fd - file descriptor to read from\n * @mode - mode to use when reading the input file\n * @crc - verify the crc of a file in the Android sparse file format\n *\n * Reads a file into a sparse file cookie. If @mode is\n * %SPARSE_READ_MODE_SPARSE, the file is assumed to be in the Android sparse\n * file format. If @mode is %SPARSE_READ_MODE_NORMAL, the file will be sparsed\n * by looking for block aligned chunks of all zeros or another 32 bit value. If\n * @mode is %SPARSE_READ_MODE_HOLE, the file will be sparsed like\n * %SPARSE_READ_MODE_NORMAL, but holes in the file will be converted to \"don't\n * care\" chunks. If crc is true, the crc of the sparse file will be verified.\n *\n * Returns 0 on success, negative errno on error.\n */\nint sparse_file_read(struct sparse_file *s, int fd, enum sparse_read_mode mode, bool crc);\n\n/**\n * sparse_file_import - import an existing sparse file\n *\n * @fd - file descriptor to read from\n * @verbose - print verbose errors while reading the sparse file\n * @crc - verify the crc of a file in the Android sparse file format\n *\n * Reads an existing sparse file into a sparse file cookie, recreating the same\n * sparse cookie that was used to write it.  If verbose is true, prints verbose\n * errors when the sparse file is formatted incorrectly.\n *\n * Returns a new sparse file cookie on success, NULL on error.\n */\nstruct sparse_file *sparse_file_import(int fd, bool verbose, bool crc);\n\n/**\n * sparse_file_import_buf - import an existing sparse file from a buffer\n *\n * @buf - buffer to read from\n * @len - length of buffer\n * @verbose - print verbose errors while reading the sparse file\n * @crc - verify the crc of a file in the Android sparse file format\n *\n * Reads existing sparse file data into a sparse file cookie, recreating the same\n * sparse cookie that was used to write it.  If verbose is true, prints verbose\n * errors when the sparse file is formatted incorrectly.\n *\n * Returns a new sparse file cookie on success, NULL on error.\n */\nstruct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc);\n\n/**\n * sparse_file_import_auto - import an existing sparse or normal file\n *\n * @fd - file descriptor to read from\n * @crc - verify the crc of a file in the Android sparse file format\n * @verbose - whether to use verbose logging\n *\n * Reads an existing sparse or normal file into a sparse file cookie.\n * Attempts to determine if the file is sparse or not by looking for the sparse\n * file magic number in the first 4 bytes.  If the file is not sparse, the file\n * will be sparsed by looking for block aligned chunks of all zeros or another\n * 32 bit value.  If crc is true, the crc of the sparse file will be verified.\n *\n * Returns a new sparse file cookie on success, NULL on error.\n */\nstruct sparse_file *sparse_file_import_auto(int fd, bool crc, bool verbose);\n\n/** sparse_file_resparse - rechunk an existing sparse file into smaller files\n *\n * @in_s - sparse file cookie of the existing sparse file\n * @max_len - maximum file size\n * @out_s - array of sparse file cookies\n * @out_s_count - size of out_s array\n *\n * Splits chunks of an existing sparse file into smaller sparse files such that\n * each sparse file is less than max_len.  Returns the number of sparse_files\n * that would have been written to out_s if out_s were big enough.\n */\nint sparse_file_resparse(struct sparse_file *in_s, unsigned int max_len,\n\t\tstruct sparse_file **out_s, int out_s_count);\n\n/**\n * sparse_file_verbose - set a sparse file cookie to print verbose errors\n *\n * @s - sparse file cookie\n *\n * Print verbose sparse file errors whenever using the sparse file cookie.\n */\nvoid sparse_file_verbose(struct sparse_file *s);\n\n/**\n * sparse_print_verbose - function called to print verbose errors\n *\n * By default, verbose errors will print to standard error.\n * sparse_print_verbose may be overridden to log verbose errors somewhere else.\n *\n */\nextern void (*sparse_print_verbose)(const char *fmt, ...);\n\n#ifdef\t__cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libsparse/output_file.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define _FILE_OFFSET_BITS 64\n#define _LARGEFILE64_SOURCE 1\n\n#include <algorithm>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <limits.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <zlib.h>\n\n#include \"defs.h\"\n#include \"output_file.h\"\n#include \"sparse_crc32.h\"\n#include \"sparse_format.h\"\n\n#include <android-base/mapped_file.h>\n\n#ifndef _WIN32\n#define O_BINARY 0\n#else\n#define ftruncate64 ftruncate\n#endif\n\n#if defined(__APPLE__) && defined(__MACH__)\n#define lseek64 lseek\n#define ftruncate64 ftruncate\n#define off64_t off_t\n#endif\n\n#define SPARSE_HEADER_MAJOR_VER 1\n#define SPARSE_HEADER_MINOR_VER 0\n#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))\n#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))\n\n#define FILL_ZERO_BUFSIZE (2 * 1024 * 1024)\n\n#define container_of(inner, outer_t, elem) ((outer_t*)((char*)(inner)-offsetof(outer_t, elem)))\n\nstatic constexpr size_t kMaxMmapSize = 256 * 1024 * 1024;\n\nstruct output_file_ops {\n  int (*open)(struct output_file*, int fd);\n  int (*skip)(struct output_file*, int64_t);\n  int (*pad)(struct output_file*, int64_t);\n  int (*write)(struct output_file*, void*, size_t);\n  void (*close)(struct output_file*);\n};\n\nstruct sparse_file_ops {\n  int (*write_data_chunk)(struct output_file* out, uint64_t len, void* data);\n  int (*write_fill_chunk)(struct output_file* out, uint64_t len, uint32_t fill_val);\n  int (*write_skip_chunk)(struct output_file* out, uint64_t len);\n  int (*write_end_chunk)(struct output_file* out);\n  int (*write_fd_chunk)(struct output_file* out, uint64_t len, int fd, int64_t offset);\n};\n\nstruct output_file {\n  int64_t cur_out_ptr;\n  unsigned int chunk_cnt;\n  uint32_t crc32;\n  struct output_file_ops* ops;\n  struct sparse_file_ops* sparse_ops;\n  int use_crc;\n  unsigned int block_size;\n  int64_t len;\n  char* zero_buf;\n  uint32_t* fill_buf;\n  char* buf;\n};\n\nstruct output_file_gz {\n  struct output_file out;\n  gzFile gz_fd;\n};\n\n#define to_output_file_gz(_o) container_of((_o), struct output_file_gz, out)\n\nstruct output_file_normal {\n  struct output_file out;\n  int fd;\n};\n\n#define to_output_file_normal(_o) container_of((_o), struct output_file_normal, out)\n\nstruct output_file_callback {\n  struct output_file out;\n  void* priv;\n  int (*write)(void* priv, const void* buf, size_t len);\n};\n\n#define to_output_file_callback(_o) container_of((_o), struct output_file_callback, out)\n\nstatic int file_open(struct output_file* out, int fd) {\n  struct output_file_normal* outn = to_output_file_normal(out);\n\n  outn->fd = fd;\n  return 0;\n}\n\nstatic int file_skip(struct output_file* out, int64_t cnt) {\n  off64_t ret;\n  struct output_file_normal* outn = to_output_file_normal(out);\n\n  ret = lseek64(outn->fd, cnt, SEEK_CUR);\n  if (ret < 0) {\n    error_errno(\"lseek64\");\n    return -1;\n  }\n  return 0;\n}\n\nstatic int file_pad(struct output_file* out, int64_t len) {\n  int ret;\n  struct output_file_normal* outn = to_output_file_normal(out);\n\n  ret = ftruncate64(outn->fd, len);\n  if (ret < 0) {\n    return -errno;\n  }\n\n  return 0;\n}\n\nstatic int file_write(struct output_file* out, void* data, size_t len) {\n  ssize_t ret;\n  struct output_file_normal* outn = to_output_file_normal(out);\n\n  while (len > 0) {\n    ret = write(outn->fd, data, len);\n    if (ret < 0) {\n      if (errno == EINTR) {\n        continue;\n      }\n      error_errno(\"write\");\n      return -1;\n    }\n\n    data = (char*)data + ret;\n    len -= ret;\n  }\n\n  return 0;\n}\n\nstatic void file_close(struct output_file* out) {\n  struct output_file_normal* outn = to_output_file_normal(out);\n\n  free(outn);\n}\n\nstatic struct output_file_ops file_ops = {\n    .open = file_open,\n    .skip = file_skip,\n    .pad = file_pad,\n    .write = file_write,\n    .close = file_close,\n};\n\nstatic int gz_file_open(struct output_file* out, int fd) {\n  struct output_file_gz* outgz = to_output_file_gz(out);\n\n  outgz->gz_fd = gzdopen(fd, \"wb9\");\n  if (!outgz->gz_fd) {\n    error_errno(\"gzopen\");\n    return -errno;\n  }\n\n  return 0;\n}\n\nstatic int gz_file_skip(struct output_file* out, int64_t cnt) {\n  off64_t ret;\n  struct output_file_gz* outgz = to_output_file_gz(out);\n\n  ret = gzseek(outgz->gz_fd, cnt, SEEK_CUR);\n  if (ret < 0) {\n    error_errno(\"gzseek\");\n    return -1;\n  }\n  return 0;\n}\n\nstatic int gz_file_pad(struct output_file* out, int64_t len) {\n  off64_t ret;\n  struct output_file_gz* outgz = to_output_file_gz(out);\n\n  ret = gztell(outgz->gz_fd);\n  if (ret < 0) {\n    return -1;\n  }\n\n  if (ret >= len) {\n    return 0;\n  }\n\n  ret = gzseek(outgz->gz_fd, len - 1, SEEK_SET);\n  if (ret < 0) {\n    return -1;\n  }\n\n  gzwrite(outgz->gz_fd, \"\", 1);\n\n  return 0;\n}\n\nstatic int gz_file_write(struct output_file* out, void* data, size_t len) {\n  int ret;\n  struct output_file_gz* outgz = to_output_file_gz(out);\n\n  while (len > 0) {\n    ret = gzwrite(outgz->gz_fd, data, std::min<unsigned int>(len, (unsigned int)INT_MAX));\n    if (ret == 0) {\n      error(\"gzwrite %s\", gzerror(outgz->gz_fd, nullptr));\n      return -1;\n    }\n    len -= ret;\n    data = (char*)data + ret;\n  }\n\n  return 0;\n}\n\nstatic void gz_file_close(struct output_file* out) {\n  struct output_file_gz* outgz = to_output_file_gz(out);\n\n  gzclose(outgz->gz_fd);\n  free(outgz);\n}\n\nstatic struct output_file_ops gz_file_ops = {\n    .open = gz_file_open,\n    .skip = gz_file_skip,\n    .pad = gz_file_pad,\n    .write = gz_file_write,\n    .close = gz_file_close,\n};\n\nstatic int callback_file_open(struct output_file* out __unused, int fd __unused) {\n  return 0;\n}\n\nstatic int callback_file_skip(struct output_file* out, int64_t off) {\n  struct output_file_callback* outc = to_output_file_callback(out);\n  int to_write;\n  int ret;\n\n  while (off > 0) {\n    to_write = std::min(off, (int64_t)INT_MAX);\n    ret = outc->write(outc->priv, nullptr, to_write);\n    if (ret < 0) {\n      return ret;\n    }\n    off -= to_write;\n  }\n\n  return 0;\n}\n\nstatic int callback_file_pad(struct output_file* out __unused, int64_t len __unused) {\n  return -1;\n}\n\nstatic int callback_file_write(struct output_file* out, void* data, size_t len) {\n  struct output_file_callback* outc = to_output_file_callback(out);\n\n  return outc->write(outc->priv, data, len);\n}\n\nstatic void callback_file_close(struct output_file* out) {\n  struct output_file_callback* outc = to_output_file_callback(out);\n\n  free(outc);\n}\n\nstatic struct output_file_ops callback_file_ops = {\n    .open = callback_file_open,\n    .skip = callback_file_skip,\n    .pad = callback_file_pad,\n    .write = callback_file_write,\n    .close = callback_file_close,\n};\n\nint read_all(int fd, void* buf, size_t len) {\n  size_t total = 0;\n  int ret;\n  char* ptr = reinterpret_cast<char*>(buf);\n\n  while (total < len) {\n    ret = read(fd, ptr, len - total);\n\n    if (ret < 0) return -errno;\n\n    if (ret == 0) return -EINVAL;\n\n    ptr += ret;\n    total += ret;\n  }\n\n  return 0;\n}\n\ntemplate <typename T>\nstatic bool write_fd_chunk_range(int fd, int64_t offset, uint64_t len, T callback) {\n  uint64_t bytes_written = 0;\n  int64_t current_offset = offset;\n  while (bytes_written < len) {\n    size_t mmap_size = std::min(static_cast<uint64_t>(kMaxMmapSize), len - bytes_written);\n    auto m = android::base::MappedFile::FromFd(fd, current_offset, mmap_size, PROT_READ);\n    if (!m) {\n      error(\"failed to mmap region of length %zu\", mmap_size);\n      return false;\n    }\n    if (!callback(m->data(), mmap_size)) {\n      return false;\n    }\n    bytes_written += mmap_size;\n    current_offset += mmap_size;\n  }\n  return true;\n}\n\nstatic int write_sparse_skip_chunk(struct output_file* out, uint64_t skip_len) {\n  chunk_header_t chunk_header;\n  int ret;\n\n  if (skip_len % out->block_size) {\n    error(\"don't care size %\" PRIi64 \" is not a multiple of the block size %u\", skip_len,\n          out->block_size);\n    return -1;\n  }\n\n  /* We are skipping data, so emit a don't care chunk. */\n  chunk_header.chunk_type = CHUNK_TYPE_DONT_CARE;\n  chunk_header.reserved1 = 0;\n  chunk_header.chunk_sz = skip_len / out->block_size;\n  chunk_header.total_sz = CHUNK_HEADER_LEN;\n  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));\n  if (ret < 0) return -1;\n\n  out->cur_out_ptr += skip_len;\n  out->chunk_cnt++;\n\n  return 0;\n}\n\nstatic int write_sparse_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {\n  chunk_header_t chunk_header;\n  uint64_t rnd_up_len;\n  int count;\n  int ret;\n\n  /* Round up the fill length to a multiple of the block size */\n  rnd_up_len = ALIGN(len, out->block_size);\n\n  /* Finally we can safely emit a chunk of data */\n  chunk_header.chunk_type = CHUNK_TYPE_FILL;\n  chunk_header.reserved1 = 0;\n  chunk_header.chunk_sz = rnd_up_len / out->block_size;\n  chunk_header.total_sz = CHUNK_HEADER_LEN + sizeof(fill_val);\n  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));\n\n  if (ret < 0) return -1;\n  ret = out->ops->write(out, &fill_val, sizeof(fill_val));\n  if (ret < 0) return -1;\n\n  if (out->use_crc) {\n    count = out->block_size / sizeof(uint32_t);\n    while (count--) out->crc32 = sparse_crc32(out->crc32, &fill_val, sizeof(uint32_t));\n  }\n\n  out->cur_out_ptr += rnd_up_len;\n  out->chunk_cnt++;\n\n  return 0;\n}\n\nstatic int write_sparse_data_chunk(struct output_file* out, uint64_t len, void* data) {\n  chunk_header_t chunk_header;\n  uint64_t rnd_up_len, zero_len;\n  int ret;\n\n  /* Round up the data length to a multiple of the block size */\n  rnd_up_len = ALIGN(len, out->block_size);\n  zero_len = rnd_up_len - len;\n\n  /* Finally we can safely emit a chunk of data */\n  chunk_header.chunk_type = CHUNK_TYPE_RAW;\n  chunk_header.reserved1 = 0;\n  chunk_header.chunk_sz = rnd_up_len / out->block_size;\n  chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;\n  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));\n\n  if (ret < 0) return -1;\n  ret = out->ops->write(out, data, len);\n  if (ret < 0) return -1;\n  if (zero_len) {\n    uint64_t len = zero_len;\n    uint64_t write_len;\n    while (len) {\n      write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);\n      ret = out->ops->write(out, out->zero_buf, write_len);\n      if (ret < 0) {\n        return ret;\n      }\n      len -= write_len;\n    }\n  }\n\n  if (out->use_crc) {\n    out->crc32 = sparse_crc32(out->crc32, data, len);\n    if (zero_len) {\n      uint64_t len = zero_len;\n      uint64_t write_len;\n      while (len) {\n        write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);\n        out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);\n        len -= write_len;\n      }\n    }\n  }\n\n  out->cur_out_ptr += rnd_up_len;\n  out->chunk_cnt++;\n\n  return 0;\n}\n\nstatic int write_sparse_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {\n  chunk_header_t chunk_header;\n  uint64_t rnd_up_len, zero_len;\n  int ret;\n\n  /* Round up the data length to a multiple of the block size */\n  rnd_up_len = ALIGN(len, out->block_size);\n  zero_len = rnd_up_len - len;\n\n  /* Finally we can safely emit a chunk of data */\n  chunk_header.chunk_type = CHUNK_TYPE_RAW;\n  chunk_header.reserved1 = 0;\n  chunk_header.chunk_sz = rnd_up_len / out->block_size;\n  chunk_header.total_sz = CHUNK_HEADER_LEN + rnd_up_len;\n  ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));\n\n  if (ret < 0) return -1;\n  bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {\n    ret = out->ops->write(out, data, size);\n    if (ret < 0) return false;\n    if (out->use_crc) {\n      out->crc32 = sparse_crc32(out->crc32, data, size);\n    }\n    return true;\n  });\n  if (!ok) return -1;\n  if (zero_len) {\n    uint64_t len = zero_len;\n    uint64_t write_len;\n    while (len) {\n      write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);\n      ret = out->ops->write(out, out->zero_buf, write_len);\n      if (ret < 0) {\n        return ret;\n      }\n      len -= write_len;\n    }\n\n    if (out->use_crc) {\n      uint64_t len = zero_len;\n      uint64_t write_len;\n      while (len) {\n        write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);\n        out->crc32 = sparse_crc32(out->crc32, out->zero_buf, write_len);\n        len -= write_len;\n      }\n    }\n  }\n\n  out->cur_out_ptr += rnd_up_len;\n  out->chunk_cnt++;\n\n  return 0;\n}\n\nint write_sparse_end_chunk(struct output_file* out) {\n  chunk_header_t chunk_header;\n  int ret;\n\n  if (out->use_crc) {\n    chunk_header.chunk_type = CHUNK_TYPE_CRC32;\n    chunk_header.reserved1 = 0;\n    chunk_header.chunk_sz = 0;\n    chunk_header.total_sz = CHUNK_HEADER_LEN + 4;\n\n    ret = out->ops->write(out, &chunk_header, sizeof(chunk_header));\n    if (ret < 0) {\n      return ret;\n    }\n    out->ops->write(out, &out->crc32, 4);\n    if (ret < 0) {\n      return ret;\n    }\n\n    out->chunk_cnt++;\n  }\n\n  return 0;\n}\n\nstatic struct sparse_file_ops sparse_file_ops = {\n    .write_data_chunk = write_sparse_data_chunk,\n    .write_fill_chunk = write_sparse_fill_chunk,\n    .write_skip_chunk = write_sparse_skip_chunk,\n    .write_end_chunk = write_sparse_end_chunk,\n    .write_fd_chunk = write_sparse_fd_chunk,\n};\n\nstatic int write_normal_data_chunk(struct output_file* out, uint64_t len, void* data) {\n  int ret;\n  uint64_t rnd_up_len = ALIGN(len, out->block_size);\n\n  ret = out->ops->write(out, data, len);\n  if (ret < 0) {\n    return ret;\n  }\n\n  if (rnd_up_len > len) {\n    ret = out->ops->skip(out, rnd_up_len - len);\n  }\n\n  return ret;\n}\n\nstatic int write_normal_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {\n  int ret;\n  unsigned int i;\n  uint64_t write_len;\n\n  /* Initialize fill_buf with the fill_val */\n  for (i = 0; i < FILL_ZERO_BUFSIZE / sizeof(uint32_t); i++) {\n    out->fill_buf[i] = fill_val;\n  }\n\n  while (len) {\n    write_len = std::min(len, (uint64_t)FILL_ZERO_BUFSIZE);\n    ret = out->ops->write(out, out->fill_buf, write_len);\n    if (ret < 0) {\n      return ret;\n    }\n\n    len -= write_len;\n  }\n\n  return 0;\n}\n\nstatic int write_normal_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {\n  int ret;\n  uint64_t rnd_up_len = ALIGN(len, out->block_size);\n\n  bool ok = write_fd_chunk_range(fd, offset, len, [&ret, out](char* data, size_t size) -> bool {\n    ret = out->ops->write(out, data, size);\n    return ret >= 0;\n  });\n  if (!ok) return ret;\n\n  if (rnd_up_len > len) {\n    ret = out->ops->skip(out, rnd_up_len - len);\n  }\n\n  return ret;\n}\n\nstatic int write_normal_skip_chunk(struct output_file* out, uint64_t len) {\n  return out->ops->skip(out, len);\n}\n\nint write_normal_end_chunk(struct output_file* out) {\n  return out->ops->pad(out, out->len);\n}\n\nstatic struct sparse_file_ops normal_file_ops = {\n    .write_data_chunk = write_normal_data_chunk,\n    .write_fill_chunk = write_normal_fill_chunk,\n    .write_skip_chunk = write_normal_skip_chunk,\n    .write_end_chunk = write_normal_end_chunk,\n    .write_fd_chunk = write_normal_fd_chunk,\n};\n\nvoid output_file_close(struct output_file* out) {\n  out->sparse_ops->write_end_chunk(out);\n  free(out->zero_buf);\n  free(out->fill_buf);\n  out->zero_buf = nullptr;\n  out->fill_buf = nullptr;\n  out->ops->close(out);\n}\n\nstatic int output_file_init(struct output_file* out, int block_size, int64_t len, bool sparse,\n                            int chunks, bool crc) {\n  int ret;\n\n  out->len = len;\n  out->block_size = block_size;\n  out->cur_out_ptr = 0LL;\n  out->chunk_cnt = 0;\n  out->crc32 = 0;\n  out->use_crc = crc;\n\n  // don't use sparse format block size as it can takes up to 32GB\n  out->zero_buf = reinterpret_cast<char*>(calloc(FILL_ZERO_BUFSIZE, 1));\n  if (!out->zero_buf) {\n    error_errno(\"malloc zero_buf\");\n    return -ENOMEM;\n  }\n\n  // don't use sparse format block size as it can takes up to 32GB\n  out->fill_buf = reinterpret_cast<uint32_t*>(calloc(FILL_ZERO_BUFSIZE, 1));\n  if (!out->fill_buf) {\n    error_errno(\"malloc fill_buf\");\n    ret = -ENOMEM;\n    goto err_fill_buf;\n  }\n\n  if (sparse) {\n    out->sparse_ops = &sparse_file_ops;\n  } else {\n    out->sparse_ops = &normal_file_ops;\n  }\n\n  if (sparse) {\n    sparse_header_t sparse_header = {\n        .magic = SPARSE_HEADER_MAGIC,\n        .major_version = SPARSE_HEADER_MAJOR_VER,\n        .minor_version = SPARSE_HEADER_MINOR_VER,\n        .file_hdr_sz = SPARSE_HEADER_LEN,\n        .chunk_hdr_sz = CHUNK_HEADER_LEN,\n        .blk_sz = out->block_size,\n        .total_blks = static_cast<unsigned>(DIV_ROUND_UP(out->len, out->block_size)),\n        .total_chunks = static_cast<unsigned>(chunks),\n        .image_checksum = 0};\n\n    if (out->use_crc) {\n      sparse_header.total_chunks++;\n    }\n\n    ret = out->ops->write(out, &sparse_header, sizeof(sparse_header));\n    if (ret < 0) {\n      goto err_write;\n    }\n  }\n\n  return 0;\n\nerr_write:\n  free(out->fill_buf);\nerr_fill_buf:\n  free(out->zero_buf);\n  return ret;\n}\n\nstatic struct output_file* output_file_new_gz(void) {\n  struct output_file_gz* outgz =\n      reinterpret_cast<struct output_file_gz*>(calloc(1, sizeof(struct output_file_gz)));\n  if (!outgz) {\n    error_errno(\"malloc struct outgz\");\n    return nullptr;\n  }\n\n  outgz->out.ops = &gz_file_ops;\n\n  return &outgz->out;\n}\n\nstatic struct output_file* output_file_new_normal(void) {\n  struct output_file_normal* outn =\n      reinterpret_cast<struct output_file_normal*>(calloc(1, sizeof(struct output_file_normal)));\n  if (!outn) {\n    error_errno(\"malloc struct outn\");\n    return nullptr;\n  }\n\n  outn->out.ops = &file_ops;\n\n  return &outn->out;\n}\n\nstruct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,\n                                              unsigned int block_size, int64_t len, int gz __unused,\n                                              int sparse, int chunks, int crc) {\n  int ret;\n  struct output_file_callback* outc;\n\n  outc =\n      reinterpret_cast<struct output_file_callback*>(calloc(1, sizeof(struct output_file_callback)));\n  if (!outc) {\n    error_errno(\"malloc struct outc\");\n    return nullptr;\n  }\n\n  outc->out.ops = &callback_file_ops;\n  outc->priv = priv;\n  outc->write = write;\n\n  ret = output_file_init(&outc->out, block_size, len, sparse, chunks, crc);\n  if (ret < 0) {\n    free(outc);\n    return nullptr;\n  }\n\n  return &outc->out;\n}\n\nstruct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,\n                                        int sparse, int chunks, int crc) {\n  int ret;\n  struct output_file* out;\n\n  if (gz) {\n    out = output_file_new_gz();\n  } else {\n    out = output_file_new_normal();\n  }\n  if (!out) {\n    return nullptr;\n  }\n\n  out->ops->open(out, fd);\n\n  ret = output_file_init(out, block_size, len, sparse, chunks, crc);\n  if (ret < 0) {\n    free(out);\n    return nullptr;\n  }\n\n  return out;\n}\n\n/* Write a contiguous region of data blocks from a memory buffer */\nint write_data_chunk(struct output_file* out, uint64_t len, void* data) {\n  return out->sparse_ops->write_data_chunk(out, len, data);\n}\n\n/* Write a contiguous region of data blocks with a fill value */\nint write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val) {\n  return out->sparse_ops->write_fill_chunk(out, len, fill_val);\n}\n\nint write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset) {\n  return out->sparse_ops->write_fd_chunk(out, len, fd, offset);\n}\n\n/* Write a contiguous region of data blocks from a file */\nint write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset) {\n  int ret;\n\n  int file_fd = open(file, O_RDONLY | O_BINARY);\n  if (file_fd < 0) {\n    return -errno;\n  }\n\n  ret = write_fd_chunk(out, len, file_fd, offset);\n\n  close(file_fd);\n\n  return ret;\n}\n\nint write_skip_chunk(struct output_file* out, uint64_t len) {\n  return out->sparse_ops->write_skip_chunk(out, len);\n}\n"
  },
  {
    "path": "libsparse/output_file.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _OUTPUT_FILE_H_\n#define _OUTPUT_FILE_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <sparse/sparse.h>\n\nstruct output_file;\n\nstruct output_file* output_file_open_fd(int fd, unsigned int block_size, int64_t len, int gz,\n                                        int sparse, int chunks, int crc);\nstruct output_file* output_file_open_callback(int (*write)(void*, const void*, size_t), void* priv,\n                                              unsigned int block_size, int64_t len, int gz,\n                                              int sparse, int chunks, int crc);\nint write_data_chunk(struct output_file* out, uint64_t len, void* data);\nint write_fill_chunk(struct output_file* out, uint64_t len, uint32_t fill_val);\nint write_file_chunk(struct output_file* out, uint64_t len, const char* file, int64_t offset);\nint write_fd_chunk(struct output_file* out, uint64_t len, int fd, int64_t offset);\nint write_skip_chunk(struct output_file* out, uint64_t len);\nvoid output_file_close(struct output_file* out);\n\nint read_all(int fd, void* buf, size_t len);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libsparse/simg2img.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sparse/sparse.h>\n\n#include <fcntl.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#ifndef O_BINARY\n#define O_BINARY 0\n#endif\n\nvoid usage() {\n  fprintf(stderr, \"Usage: simg2img <sparse_image_files> <raw_image_file>\\n\");\n}\n\nint main(int argc, char* argv[]) {\n  int in;\n  int out;\n  int i;\n  struct sparse_file* s;\n\n  if (argc < 3) {\n    usage();\n    exit(EXIT_FAILURE);\n  }\n\n  out = open(argv[argc - 1], O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0664);\n  if (out < 0) {\n    fprintf(stderr, \"Cannot open output file %s\\n\", argv[argc - 1]);\n    exit(EXIT_FAILURE);\n  }\n\n  for (i = 1; i < argc - 1; i++) {\n    if (strcmp(argv[i], \"-\") == 0) {\n      in = STDIN_FILENO;\n    } else {\n      in = open(argv[i], O_RDONLY | O_BINARY);\n      if (in < 0) {\n        fprintf(stderr, \"Cannot open input file %s\\n\", argv[i]);\n        exit(EXIT_FAILURE);\n      }\n    }\n\n    s = sparse_file_import(in, true, false);\n    if (!s) {\n      fprintf(stderr, \"Failed to read sparse file\\n\");\n      exit(EXIT_FAILURE);\n    }\n\n    if (lseek(out, 0, SEEK_SET) == -1) {\n      perror(\"lseek failed\");\n      exit(EXIT_FAILURE);\n    }\n\n    if (sparse_file_write(s, out, false, false, false) < 0) {\n      fprintf(stderr, \"Cannot write output file\\n\");\n      exit(EXIT_FAILURE);\n    }\n    sparse_file_destroy(s);\n    close(in);\n  }\n\n  close(out);\n\n  exit(EXIT_SUCCESS);\n}\n"
  },
  {
    "path": "libsparse/simg_dump.py",
    "content": "#! /usr/bin/env python3\n\n# Copyright (C) 2012 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport csv\nimport getopt\nimport hashlib\nimport posixpath\nimport signal\nimport struct\nimport sys\n\n\ndef usage(argv0):\n  print(\"\"\"\nUsage: %s [-v] [-s] [-c <filename>] sparse_image_file ...\n -v             verbose output\n -s             show sha1sum of data blocks\n -c <filename>  save .csv file of blocks\n\"\"\" % (argv0))\n  sys.exit(2)\n\n\ndef main():\n  signal.signal(signal.SIGPIPE, signal.SIG_DFL)\n\n  me = posixpath.basename(sys.argv[0])\n\n  # Parse the command line\n  verbose = 0                   # -v\n  showhash = 0                  # -s\n  csvfilename = None            # -c\n  try:\n    opts, args = getopt.getopt(sys.argv[1:],\n                               \"vsc:\",\n                               [\"verbose\", \"showhash\", \"csvfile\"])\n  except getopt.GetoptError as e:\n    print(e)\n    usage(me)\n  for o, a in opts:\n    if o in (\"-v\", \"--verbose\"):\n      verbose += 1\n    elif o in (\"-s\", \"--showhash\"):\n      showhash = True\n    elif o in (\"-c\", \"--csvfile\"):\n      csvfilename = a\n    else:\n      print(\"Unrecognized option \\\"%s\\\"\" % (o))\n      usage(me)\n\n  if not args:\n    print(\"No sparse_image_file specified\")\n    usage(me)\n\n  if csvfilename:\n    csvfile = open(csvfilename, \"wb\")\n    csvwriter = csv.writer(csvfile)\n\n  output = verbose or csvfilename or showhash\n\n  for path in args:\n    FH = open(path, \"rb\")\n    header_bin = FH.read(28)\n    header = struct.unpack(\"<I4H4I\", header_bin)\n\n    magic = header[0]\n    major_version = header[1]\n    minor_version = header[2]\n    file_hdr_sz = header[3]\n    chunk_hdr_sz = header[4]\n    blk_sz = header[5]\n    total_blks = header[6]\n    total_chunks = header[7]\n    image_checksum = header[8]\n\n    if magic != 0xED26FF3A:\n      print(\"%s: %s: Magic should be 0xED26FF3A but is 0x%08X\"\n            % (me, path, magic))\n      continue\n    if major_version != 1 or minor_version != 0:\n      print(\"%s: %s: I only know about version 1.0, but this is version %u.%u\"\n            % (me, path, major_version, minor_version))\n      continue\n    if file_hdr_sz != 28:\n      print(\"%s: %s: The file header size was expected to be 28, but is %u.\"\n            % (me, path, file_hdr_sz))\n      continue\n    if chunk_hdr_sz != 12:\n      print(\"%s: %s: The chunk header size was expected to be 12, but is %u.\"\n            % (me, path, chunk_hdr_sz))\n      continue\n\n    print(\"%s: Total of %u %u-byte output blocks in %u input chunks.\"\n          % (path, total_blks, blk_sz, total_chunks))\n\n    if image_checksum != 0:\n      print(\"checksum=0x%08X\" % (image_checksum))\n\n    if not output:\n      continue\n\n    if verbose > 0:\n      print(\"            input_bytes      output_blocks\")\n      print(\"chunk    offset     number  offset  number\")\n\n    if csvfilename:\n      csvwriter.writerow([\"chunk\", \"input offset\", \"input bytes\",\n                          \"output offset\", \"output blocks\", \"type\", \"hash\"])\n\n    offset = 0\n    for i in range(1, total_chunks + 1):\n      header_bin = FH.read(12)\n      header = struct.unpack(\"<2H2I\", header_bin)\n      chunk_type = header[0]\n      chunk_sz = header[2]\n      total_sz = header[3]\n      data_sz = total_sz - 12\n      curhash = \"\"\n      curtype = \"\"\n      curpos = FH.tell()\n\n      if verbose > 0:\n        print(\"%4u %10u %10u %7u %7u\" % (i, curpos, data_sz, offset, chunk_sz),\n              end=\" \")\n\n      if chunk_type == 0xCAC1:\n        if data_sz != (chunk_sz * blk_sz):\n          print(\"Raw chunk input size (%u) does not match output size (%u)\"\n                % (data_sz, chunk_sz * blk_sz))\n          break\n        else:\n          curtype = \"Raw data\"\n          data = FH.read(data_sz)\n          if showhash:\n            h = hashlib.sha1()\n            h.update(data)\n            curhash = h.hexdigest()\n      elif chunk_type == 0xCAC2:\n        if data_sz != 4:\n          print(\"Fill chunk should have 4 bytes of fill, but this has %u\"\n                % (data_sz))\n          break\n        else:\n          fill_bin = FH.read(4)\n          fill = struct.unpack(\"<I\", fill_bin)\n          curtype = format(\"Fill with 0x%08X\" % (fill))\n          if showhash:\n            h = hashlib.sha1()\n            data = fill_bin * (blk_sz // 4);\n            for block in range(chunk_sz):\n              h.update(data)\n            curhash = h.hexdigest()\n      elif chunk_type == 0xCAC3:\n        if data_sz != 0:\n          print(\"Don't care chunk input size is non-zero (%u)\" % (data_sz))\n          break\n        else:\n          curtype = \"Don't care\"\n      elif chunk_type == 0xCAC4:\n        if data_sz != 4:\n          print(\"CRC32 chunk should have 4 bytes of CRC, but this has %u\"\n                % (data_sz))\n          break\n        else:\n          crc_bin = FH.read(4)\n          crc = struct.unpack(\"<I\", crc_bin)\n          curtype = format(\"Unverified CRC32 0x%08X\" % (crc))\n      else:\n        print(\"Unknown chunk type 0x%04X\" % (chunk_type))\n        break\n\n      if verbose > 0:\n        print(\"%-18s\" % (curtype), end=\" \")\n\n        if verbose > 1:\n          header = struct.unpack(\"<12B\", header_bin)\n          print(\" (%02X%02X %02X%02X %02X%02X%02X%02X %02X%02X%02X%02X)\"\n                % (header[0], header[1], header[2], header[3],\n                   header[4], header[5], header[6], header[7],\n                   header[8], header[9], header[10], header[11]), end=\" \")\n\n        print(curhash)\n\n      if csvfilename:\n        csvwriter.writerow([i, curpos, data_sz, offset, chunk_sz, curtype,\n                            curhash])\n\n      offset += chunk_sz\n\n    if verbose > 0:\n      print(\"     %10u            %7u         End\" % (FH.tell(), offset))\n\n    if total_blks != offset:\n      print(\"The header said we should have %u output blocks, but we saw %u\"\n            % (total_blks, offset))\n\n    junk_len = len(FH.read())\n    if junk_len:\n      print(\"There were %u bytes of extra data at the end of the file.\"\n            % (junk_len))\n\n  if csvfilename:\n    csvfile.close()\n\n  sys.exit(0)\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "libsparse/sparse.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <assert.h>\n#include <stdlib.h>\n\n#include <sparse/sparse.h>\n\n#include \"defs.h\"\n#include \"sparse_file.h\"\n\n#include \"backed_block.h\"\n#include \"output_file.h\"\n#include \"sparse_defs.h\"\n#include \"sparse_format.h\"\n\nstruct sparse_file* sparse_file_new(unsigned int block_size, int64_t len) {\n  struct sparse_file* s = reinterpret_cast<sparse_file*>(calloc(sizeof(struct sparse_file), 1));\n  if (!s) {\n    return nullptr;\n  }\n\n  s->backed_block_list = backed_block_list_new(block_size);\n  if (!s->backed_block_list) {\n    free(s);\n    return nullptr;\n  }\n\n  s->block_size = block_size;\n  s->len = len;\n\n  return s;\n}\n\nvoid sparse_file_destroy(struct sparse_file* s) {\n  backed_block_list_destroy(s->backed_block_list);\n  free(s);\n}\n\nint sparse_file_add_data(struct sparse_file* s, void* data, uint64_t len, unsigned int block) {\n  return backed_block_add_data(s->backed_block_list, data, len, block);\n}\n\nint sparse_file_add_fill(struct sparse_file* s, uint32_t fill_val, uint64_t len,\n                         unsigned int block) {\n  return backed_block_add_fill(s->backed_block_list, fill_val, len, block);\n}\n\nint sparse_file_add_file(struct sparse_file* s, const char* filename, int64_t file_offset,\n                         uint64_t len, unsigned int block) {\n  return backed_block_add_file(s->backed_block_list, filename, file_offset, len, block);\n}\n\nint sparse_file_add_fd(struct sparse_file* s, int fd, int64_t file_offset, uint64_t len,\n                       unsigned int block) {\n  return backed_block_add_fd(s->backed_block_list, fd, file_offset, len, block);\n}\nunsigned int sparse_count_chunks(struct sparse_file* s) {\n  struct backed_block* bb;\n  unsigned int last_block = 0;\n  unsigned int chunks = 0;\n\n  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {\n    if (backed_block_block(bb) > last_block) {\n      /* If there is a gap between chunks, add a skip chunk */\n      chunks++;\n    }\n    chunks++;\n    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);\n  }\n  if (last_block < DIV_ROUND_UP(s->len, s->block_size)) {\n    chunks++;\n  }\n\n  return chunks;\n}\n\nstatic int sparse_file_write_block(struct output_file* out, struct backed_block* bb) {\n  int ret = -EINVAL;\n\n  switch (backed_block_type(bb)) {\n    case BACKED_BLOCK_DATA:\n      ret = write_data_chunk(out, backed_block_len(bb), backed_block_data(bb));\n      break;\n    case BACKED_BLOCK_FILE:\n      ret = write_file_chunk(out, backed_block_len(bb), backed_block_filename(bb),\n                             backed_block_file_offset(bb));\n      break;\n    case BACKED_BLOCK_FD:\n      ret = write_fd_chunk(out, backed_block_len(bb), backed_block_fd(bb),\n                           backed_block_file_offset(bb));\n      break;\n    case BACKED_BLOCK_FILL:\n      ret = write_fill_chunk(out, backed_block_len(bb), backed_block_fill_val(bb));\n      break;\n  }\n\n  return ret;\n}\n\nstatic int write_all_blocks(struct sparse_file* s, struct output_file* out) {\n  struct backed_block* bb;\n  unsigned int last_block = 0;\n  int64_t pad;\n  int ret = 0;\n\n  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {\n    if (backed_block_block(bb) > last_block) {\n      unsigned int blocks = backed_block_block(bb) - last_block;\n      write_skip_chunk(out, (int64_t)blocks * s->block_size);\n    }\n    ret = sparse_file_write_block(out, bb);\n    if (ret) return ret;\n    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), s->block_size);\n  }\n\n  pad = s->len - (int64_t)last_block * s->block_size;\n  assert(pad >= 0);\n  if (pad > 0) {\n    write_skip_chunk(out, pad);\n  }\n\n  return 0;\n}\n\n/*\n * This is a workaround for 32-bit Windows: Limit the block size to 64 MB before\n * fastboot executable binary for windows 64-bit is released (b/156057250).\n */\n#define MAX_BACKED_BLOCK_SIZE ((unsigned int) (64UL << 20))\n\nint sparse_file_write(struct sparse_file* s, int fd, bool gz, bool sparse, bool crc) {\n  struct backed_block* bb;\n  int ret;\n  int chunks;\n  struct output_file* out;\n\n  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {\n    ret = backed_block_split(s->backed_block_list, bb, MAX_BACKED_BLOCK_SIZE);\n    if (ret) return ret;\n  }\n\n  chunks = sparse_count_chunks(s);\n  out = output_file_open_fd(fd, s->block_size, s->len, gz, sparse, chunks, crc);\n\n  if (!out) return -ENOMEM;\n\n  ret = write_all_blocks(s, out);\n\n  output_file_close(out);\n\n  return ret;\n}\n\nint sparse_file_callback(struct sparse_file* s, bool sparse, bool crc,\n                         int (*write)(void* priv, const void* data, size_t len), void* priv) {\n  int ret;\n  int chunks;\n  struct output_file* out;\n\n  chunks = sparse_count_chunks(s);\n  out = output_file_open_callback(write, priv, s->block_size, s->len, false, sparse, chunks, crc);\n\n  if (!out) return -ENOMEM;\n\n  ret = write_all_blocks(s, out);\n\n  output_file_close(out);\n\n  return ret;\n}\n\nstruct chunk_data {\n  void* priv;\n  unsigned int block;\n  unsigned int nr_blocks;\n  int (*write)(void* priv, const void* data, size_t len, unsigned int block, unsigned int nr_blocks);\n};\n\nstatic int foreach_chunk_write(void* priv, const void* data, size_t len) {\n  struct chunk_data* chk = reinterpret_cast<chunk_data*>(priv);\n\n  return chk->write(chk->priv, data, len, chk->block, chk->nr_blocks);\n}\n\nint sparse_file_foreach_chunk(struct sparse_file* s, bool sparse, bool crc,\n                              int (*write)(void* priv, const void* data, size_t len,\n                                           unsigned int block, unsigned int nr_blocks),\n                              void* priv) {\n  int ret = 0;\n  int chunks;\n  struct chunk_data chk;\n  struct output_file* out;\n  struct backed_block* bb;\n\n  chk.priv = priv;\n  chk.write = write;\n  chk.block = chk.nr_blocks = 0;\n  chunks = sparse_count_chunks(s);\n  out = output_file_open_callback(foreach_chunk_write, &chk, s->block_size, s->len, false, sparse,\n                                  chunks, crc);\n\n  if (!out) return -ENOMEM;\n\n  for (bb = backed_block_iter_new(s->backed_block_list); bb; bb = backed_block_iter_next(bb)) {\n    chk.block = backed_block_block(bb);\n    chk.nr_blocks = (backed_block_len(bb) - 1) / s->block_size + 1;\n    ret = sparse_file_write_block(out, bb);\n    if (ret) return ret;\n  }\n\n  output_file_close(out);\n\n  return ret;\n}\n\nstatic int out_counter_write(void* priv, const void* data __unused, size_t len) {\n  int64_t* count = reinterpret_cast<int64_t*>(priv);\n  *count += len;\n  return 0;\n}\n\nint64_t sparse_file_len(struct sparse_file* s, bool sparse, bool crc) {\n  int ret;\n  int chunks = sparse_count_chunks(s);\n  int64_t count = 0;\n  struct output_file* out;\n\n  out = output_file_open_callback(out_counter_write, &count, s->block_size, s->len, false, sparse,\n                                  chunks, crc);\n  if (!out) {\n    return -1;\n  }\n\n  ret = write_all_blocks(s, out);\n\n  output_file_close(out);\n\n  if (ret < 0) {\n    return -1;\n  }\n\n  return count;\n}\n\nunsigned int sparse_file_block_size(struct sparse_file* s) {\n  return s->block_size;\n}\n\nstatic int move_chunks_up_to_len(struct sparse_file* from, struct sparse_file* to, unsigned int len,\n                                 backed_block** out_bb) {\n  int64_t count = 0;\n  struct output_file* out_counter;\n  struct backed_block* last_bb = nullptr;\n  struct backed_block* bb;\n  struct backed_block* start;\n  unsigned int last_block = 0;\n  int64_t file_len = 0;\n  int ret;\n\n  /*\n   * overhead is sparse file header, the potential end skip\n   * chunk and crc chunk.\n   */\n  int overhead = sizeof(sparse_header_t) + 2 * sizeof(chunk_header_t) + sizeof(uint32_t);\n  len -= overhead;\n\n  start = backed_block_iter_new(from->backed_block_list);\n  out_counter = output_file_open_callback(out_counter_write, &count, to->block_size, to->len, false,\n                                          true, 0, false);\n  if (!out_counter) {\n    return -1;\n  }\n\n  for (bb = start; bb; bb = backed_block_iter_next(bb)) {\n    count = 0;\n    if (backed_block_block(bb) > last_block) count += sizeof(chunk_header_t);\n    last_block = backed_block_block(bb) + DIV_ROUND_UP(backed_block_len(bb), to->block_size);\n\n    /* will call out_counter_write to update count */\n    ret = sparse_file_write_block(out_counter, bb);\n    if (ret) {\n      bb = nullptr;\n      goto out;\n    }\n    if (file_len + count > len) {\n      /*\n       * If the remaining available size is more than 1/8th of the\n       * requested size, split the chunk.  Results in sparse files that\n       * are at least 7/8ths of the requested size\n       */\n      file_len += sizeof(chunk_header_t);\n      if (!last_bb || (len - file_len > (len / 8))) {\n        backed_block_split(from->backed_block_list, bb, len - file_len);\n        last_bb = bb;\n      }\n      goto move;\n    }\n    file_len += count;\n    last_bb = bb;\n  }\n\nmove:\n  backed_block_list_move(from->backed_block_list, to->backed_block_list, start, last_bb);\n\nout:\n  output_file_close(out_counter);\n\n  *out_bb = bb;\n  return 0;\n}\n\nint sparse_file_resparse(struct sparse_file* in_s, unsigned int max_len, struct sparse_file** out_s,\n                         int out_s_count) {\n  struct backed_block* bb;\n  struct sparse_file* s;\n  struct sparse_file* tmp;\n  int c = 0;\n\n  tmp = sparse_file_new(in_s->block_size, in_s->len);\n  if (!tmp) {\n    return -ENOMEM;\n  }\n\n  do {\n    s = sparse_file_new(in_s->block_size, in_s->len);\n\n    if (move_chunks_up_to_len(in_s, s, max_len, &bb) < 0) {\n      sparse_file_destroy(s);\n      for (int i = 0; i < c && i < out_s_count; i++) {\n        sparse_file_destroy(out_s[i]);\n        out_s[i] = nullptr;\n      }\n      sparse_file_destroy(tmp);\n      return -1;\n    }\n\n    if (c < out_s_count) {\n      out_s[c] = s;\n    } else {\n      backed_block_list_move(s->backed_block_list, tmp->backed_block_list, nullptr, nullptr);\n      sparse_file_destroy(s);\n    }\n    c++;\n  } while (bb);\n\n  backed_block_list_move(tmp->backed_block_list, in_s->backed_block_list, nullptr, nullptr);\n\n  sparse_file_destroy(tmp);\n\n  return c;\n}\n\nvoid sparse_file_verbose(struct sparse_file* s) {\n  s->verbose = true;\n}\n"
  },
  {
    "path": "libsparse/sparse_crc32.cpp",
    "content": "/*-\n *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or\n *  code or tables extracted from it, as desired without restriction.\n */\n\n/*\n *  First, the polynomial itself and its table of feedback terms.  The\n *  polynomial is\n *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0\n *\n *  Note that we take it \"backwards\" and put the highest-order term in\n *  the lowest-order bit.  The X^32 term is \"implied\"; the LSB is the\n *  X^31 term, etc.  The X^0 term (usually shown as \"+1\") results in\n *  the MSB being 1\n *\n *  Note that the usual hardware shift register implementation, which\n *  is what we're using (we're merely optimizing it by doing eight-bit\n *  chunks at a time) shifts bits into the lowest-order term.  In our\n *  implementation, that means shifting towards the right.  Why do we\n *  do it this way?  Because the calculated CRC must be transmitted in\n *  order from highest-order term to lowest-order term.  UARTs transmit\n *  characters in order from LSB to MSB.  By storing the CRC this way\n *  we hand it to the UART in the order low-byte to high-byte; the UART\n *  sends each low-bit to hight-bit; and the result is transmission bit\n *  by bit from highest- to lowest-order term without requiring any bit\n *  shuffling on our part.  Reception works similarly\n *\n *  The feedback terms table consists of 256, 32-bit entries.  Notes\n *\n *      The table can be generated at runtime if desired; code to do so\n *      is shown later.  It might not be obvious, but the feedback\n *      terms simply represent the results of eight shift/xor opera\n *      tions for all combinations of data and CRC register values\n *\n *      The values must be right-shifted by eight bits by the \"updcrc\n *      logic; the shift must be unsigned (bring in zeroes).  On some\n *      hardware you could probably optimize the shift in assembler by\n *      using byte-swap instructions\n *      polynomial $edb88320\n *\n *\n * CRC32 code derived from work by Gary S. Brown.\n */\n\n/* Code taken from FreeBSD 8 */\n#include <stdint.h>\n#include <stdio.h>\n\nstatic uint32_t crc32_tab[] = {\n    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,\n    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,\n    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,\n    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\n    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,\n    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\n    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,\n    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,\n    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,\n    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,\n    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,\n    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,\n    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,\n    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,\n    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,\n    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,\n    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,\n    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,\n    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,\n    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,\n    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,\n    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d};\n\n/*\n * A function that calculates the CRC-32 based on the table above is\n * given below for documentation purposes. An equivalent implementation\n * of this function that's actually used in the kernel can be found\n * in sys/libkern.h, where it can be inlined.\n */\n\nuint32_t sparse_crc32(uint32_t crc_in, const void* buf, size_t size) {\n  const uint8_t* p = reinterpret_cast<const uint8_t*>(buf);\n  uint32_t crc;\n\n  crc = crc_in ^ ~0U;\n  while (size--) crc = crc32_tab[(crc ^ *p++) & 0xFF] ^ (crc >> 8);\n  return crc ^ ~0U;\n}\n"
  },
  {
    "path": "libsparse/sparse_crc32.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSPARSE_SPARSE_CRC32_H_\n#define _LIBSPARSE_SPARSE_CRC32_H_\n\n#include <stdint.h>\n\nuint32_t sparse_crc32(uint32_t crc, const void* buf, size_t size);\n\n#endif\n"
  },
  {
    "path": "libsparse/sparse_defs.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSPARSE_SPARSE_DEFS_\n#define _LIBSPARSE_SPARSE_DEFS_\n\n#include <errno.h>\n#include <stdio.h>\n\n#define __le64 u64\n#define __le32 u32\n#define __le16 u16\n\n#define __be64 u64\n#define __be32 u32\n#define __be16 u16\n\n#define __u64 u64\n#define __u32 u32\n#define __u16 u16\n#define __u8 u8\n\ntypedef unsigned long long u64;\ntypedef signed long long s64;\ntypedef unsigned int u32;\ntypedef unsigned short int u16;\ntypedef unsigned char u8;\n\n#define DIV_ROUND_UP(x, y) (((x) + (y)-1) / (y))\n#define ALIGN(x, y) ((y)*DIV_ROUND_UP((x), (y)))\n#define ALIGN_DOWN(x, y) ((y) * ((x) / (y)))\n\n#define error(fmt, args...)                                    \\\n  do {                                                         \\\n    fprintf(stderr, \"error: %s: \" fmt \"\\n\", __func__, ##args); \\\n  } while (0)\n#define error_errno(s, args...) error(s \": %s\", ##args, strerror(errno))\n\n#endif\n"
  },
  {
    "path": "libsparse/sparse_err.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sparse/sparse.h>\n\n#include <stdarg.h>\n#include <stdio.h>\n#include <unistd.h>\n\nvoid sparse_default_print(const char* fmt, ...) {\n  va_list argp;\n\n  va_start(argp, fmt);\n  vfprintf(stderr, fmt, argp);\n  va_end(argp);\n}\n\nvoid (*sparse_print_error)(const char* fmt, ...) = sparse_default_print;\nvoid (*sparse_print_verbose)(const char* fmt, ...) = sparse_default_print;\n"
  },
  {
    "path": "libsparse/sparse_file.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSPARSE_SPARSE_FILE_H_\n#define _LIBSPARSE_SPARSE_FILE_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <sparse/sparse.h>\n\nstruct sparse_file {\n  unsigned int block_size;\n  int64_t len;\n  bool verbose;\n\n  struct backed_block_list* backed_block_list;\n  struct output_file* out;\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* _LIBSPARSE_SPARSE_FILE_H_ */\n"
  },
  {
    "path": "libsparse/sparse_format.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSPARSE_SPARSE_FORMAT_H_\n#define _LIBSPARSE_SPARSE_FORMAT_H_\n#include \"sparse_defs.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef struct sparse_header {\n  __le32 magic;          /* 0xed26ff3a */\n  __le16 major_version;  /* (0x1) - reject images with higher major versions */\n  __le16 minor_version;  /* (0x0) - allow images with higer minor versions */\n  __le16 file_hdr_sz;    /* 28 bytes for first revision of the file format */\n  __le16 chunk_hdr_sz;   /* 12 bytes for first revision of the file format */\n  __le32 blk_sz;         /* block size in bytes, must be a multiple of 4 (4096) */\n  __le32 total_blks;     /* total blocks in the non-sparse output image */\n  __le32 total_chunks;   /* total chunks in the sparse input image */\n  __le32 image_checksum; /* CRC32 checksum of the original data, counting \"don't care\" */\n                         /* as 0. Standard 802.3 polynomial, use a Public Domain */\n                         /* table implementation */\n} sparse_header_t;\n\n#define SPARSE_HEADER_MAGIC 0xed26ff3a\n\n#define CHUNK_TYPE_RAW 0xCAC1\n#define CHUNK_TYPE_FILL 0xCAC2\n#define CHUNK_TYPE_DONT_CARE 0xCAC3\n#define CHUNK_TYPE_CRC32 0xCAC4\n\ntypedef struct chunk_header {\n  __le16 chunk_type; /* 0xCAC1 -> raw; 0xCAC2 -> fill; 0xCAC3 -> don't care */\n  __le16 reserved1;\n  __le32 chunk_sz; /* in blocks in output image */\n  __le32 total_sz; /* in bytes of chunk input file including chunk header and data */\n} chunk_header_t;\n\n/* Following a Raw or Fill or CRC32 chunk is data.\n *  For a Raw chunk, it's the data in chunk_sz * blk_sz.\n *  For a Fill chunk, it's 4 bytes of the fill data.\n *  For a CRC32 chunk, it's 4 bytes of CRC32\n */\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "libsparse/sparse_fuzzer.cpp",
    "content": "#include \"include/sparse/sparse.h\"\n\nstatic volatile int count;\n\nint WriteCallback(void* priv  __attribute__((__unused__)), const void* data, size_t len) {\n  if (!data) {\n    return 0;\n  }\n  if (len == 0) {\n    return 0;\n  }\n\n  const char* p = (const char*)data;\n  // Just to make sure the data is accessible\n  // We only check the head and tail to save time\n  count += *p;\n  count += *(p+len-1);\n  return 0;\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n  struct sparse_file* file = sparse_file_import_buf((char*)data, size, true, false);\n  if (!file) {\n      return 0;\n  }\n  int32_t result = sparse_file_callback(file, false, false, WriteCallback, nullptr);\n  sparse_file_destroy(file);\n  return result;\n}\n"
  },
  {
    "path": "libsparse/sparse_read.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define _FILE_OFFSET_BITS 64\n#define _LARGEFILE64_SOURCE 1\n\n#include <fcntl.h>\n#include <inttypes.h>\n#include <stdarg.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n#include <algorithm>\n#include <string>\n\n#include <sparse/sparse.h>\n\n#include \"android-base/stringprintf.h\"\n#include \"defs.h\"\n#include \"output_file.h\"\n#include \"sparse_crc32.h\"\n#include \"sparse_file.h\"\n#include \"sparse_format.h\"\n\n#if defined(__APPLE__) && defined(__MACH__)\n#define lseek64 lseek\n#define off64_t off_t\n#endif\n\n#define SPARSE_HEADER_MAJOR_VER 1\n#define SPARSE_HEADER_LEN (sizeof(sparse_header_t))\n#define CHUNK_HEADER_LEN (sizeof(chunk_header_t))\n\nstatic constexpr int64_t COPY_BUF_SIZE = 1024 * 1024;\nstatic char* copybuf;\n\nstatic std::string ErrorString(int err) {\n  if (err == -EOVERFLOW) return \"EOF while reading file\";\n  if (err == -EINVAL) return \"Invalid sparse file format\";\n  if (err == -ENOMEM) return \"Failed allocation while reading file\";\n  return android::base::StringPrintf(\"Unknown error %d\", err);\n}\n\nclass SparseFileSource {\n public:\n  /* Seeks the source ahead by the given offset.\n   * Return 0 if successful. */\n  virtual int Seek(int64_t offset) = 0;\n\n  /* Return the current offset. */\n  virtual int64_t GetOffset() = 0;\n\n  /* Rewind to beginning. Return 0 if successful. */\n  virtual int Rewind() = 0;\n\n  /* Adds the given length from the current offset of the source to the file at the given block.\n   * Return 0 if successful. */\n  virtual int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) = 0;\n\n  /* Get data of fixed size from the current offset and seek len bytes. Return 0 if successful. */\n  virtual int ReadValue(void* ptr, int len) = 0;\n\n  /* Find the crc32 of the next len bytes and seek ahead len bytes. Return 0 if successful. */\n  virtual int GetCrc32(uint32_t* crc32, int64_t len) = 0;\n\n  virtual ~SparseFileSource(){};\n};\n\nclass SparseFileFdSource : public SparseFileSource {\n private:\n  int fd;\n\n public:\n  SparseFileFdSource(int fd) : fd(fd) {}\n  ~SparseFileFdSource() override {}\n\n  int Seek(int64_t off) override {\n    return lseek64(fd, off, SEEK_CUR) != -1 ? 0 : -errno;\n  }\n\n  int64_t GetOffset() override { return lseek64(fd, 0, SEEK_CUR); }\n\n  int Rewind() override {\n    return lseek64(fd, 0, SEEK_SET) == 0 ? 0 : -errno;\n  }\n\n  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {\n    return sparse_file_add_fd(s, fd, GetOffset(), len, block);\n  }\n\n  int ReadValue(void* ptr, int len) override { return read_all(fd, ptr, len); }\n\n  int GetCrc32(uint32_t* crc32, int64_t len) override {\n    int chunk;\n    int ret;\n    while (len) {\n      chunk = std::min(len, COPY_BUF_SIZE);\n      ret = read_all(fd, copybuf, chunk);\n      if (ret < 0) {\n        return ret;\n      }\n      *crc32 = sparse_crc32(*crc32, copybuf, chunk);\n      len -= chunk;\n    }\n    return 0;\n  }\n};\n\nclass SparseFileBufSource : public SparseFileSource {\n private:\n  char* buf_start;\n  char* buf_end;\n  char* buf;\n  int64_t offset;\n\n  int AccessOkay(int64_t len) {\n    if (len <= 0) return -EINVAL;\n    if (buf < buf_start) return -EOVERFLOW;\n    if (buf >= buf_end) return -EOVERFLOW;\n    if (len > buf_end - buf) return -EOVERFLOW;\n\n    return 0;\n  }\n\n public:\n  SparseFileBufSource(char* buf, uint64_t len) {\n    this->buf = buf;\n    this->offset = 0;\n    this->buf_start = buf;\n    this->buf_end = buf + len;\n  }\n  ~SparseFileBufSource() override {}\n\n  int Seek(int64_t off) override {\n    int ret = AccessOkay(off);\n    if (ret < 0) {\n      return ret;\n    }\n    buf += off;\n    offset += off;\n    return 0;\n  }\n\n  int64_t GetOffset() override { return offset; }\n\n  int Rewind() override {\n    buf = buf_start;\n    offset = 0;\n    return 0;\n  }\n\n  int AddToSparseFile(struct sparse_file* s, int64_t len, unsigned int block) override {\n    int ret = AccessOkay(len);\n    if (ret < 0) {\n      return ret;\n    }\n    return sparse_file_add_data(s, buf, len, block);\n  }\n\n  int ReadValue(void* ptr, int len) override {\n    int ret = AccessOkay(len);\n    if (ret < 0) {\n      return ret;\n    }\n    memcpy(ptr, buf, len);\n    buf += len;\n    offset += len;\n    return 0;\n  }\n\n  int GetCrc32(uint32_t* crc32, int64_t len) override {\n    int ret = AccessOkay(len);\n    if (ret < 0) {\n      return ret;\n    }\n    *crc32 = sparse_crc32(*crc32, buf, len);\n    buf += len;\n    offset += len;\n    return 0;\n  }\n};\n\nstatic void verbose_error(bool verbose, int err, const char* fmt, ...) {\n  if (!verbose) return;\n\n  std::string msg = ErrorString(err);\n  if (fmt) {\n    msg += \" at \";\n    va_list argp;\n    va_start(argp, fmt);\n    android::base::StringAppendV(&msg, fmt, argp);\n    va_end(argp);\n  }\n  sparse_print_verbose(\"%s\\n\", msg.c_str());\n}\n\nstatic int process_raw_chunk(struct sparse_file* s, unsigned int chunk_size,\n                             SparseFileSource* source, unsigned int blocks, unsigned int block,\n                             uint32_t* crc32) {\n  int ret;\n  int64_t len = (int64_t)blocks * s->block_size;\n\n  if (chunk_size % s->block_size != 0) {\n    return -EINVAL;\n  }\n\n  if (chunk_size / s->block_size != blocks) {\n    return -EINVAL;\n  }\n\n  ret = source->AddToSparseFile(s, len, block);\n  if (ret < 0) {\n    return ret;\n  }\n\n  if (crc32) {\n    ret = source->GetCrc32(crc32, len);\n    if (ret < 0) {\n      return ret;\n    }\n  } else {\n    ret = source->Seek(len);\n    if (ret < 0) {\n      return ret;\n    }\n  }\n\n  return 0;\n}\n\nstatic int process_fill_chunk(struct sparse_file* s, unsigned int chunk_size,\n                              SparseFileSource* source, unsigned int blocks, unsigned int block,\n                              uint32_t* crc32) {\n  int ret;\n  int chunk;\n  int64_t len = (int64_t)blocks * s->block_size;\n  uint32_t fill_val;\n  uint32_t* fillbuf;\n  unsigned int i;\n\n  if (chunk_size != sizeof(fill_val)) {\n    return -EINVAL;\n  }\n\n  ret = source->ReadValue(&fill_val, sizeof(fill_val));\n  if (ret < 0) {\n    return ret;\n  }\n\n  ret = sparse_file_add_fill(s, fill_val, len, block);\n  if (ret < 0) {\n    return ret;\n  }\n\n  if (crc32) {\n    /* Fill copy_buf with the fill value */\n    fillbuf = (uint32_t*)copybuf;\n    for (i = 0; i < (COPY_BUF_SIZE / sizeof(fill_val)); i++) {\n      fillbuf[i] = fill_val;\n    }\n\n    while (len) {\n      chunk = std::min(len, COPY_BUF_SIZE);\n      *crc32 = sparse_crc32(*crc32, copybuf, chunk);\n      len -= chunk;\n    }\n  }\n\n  return 0;\n}\n\nstatic int process_skip_chunk(struct sparse_file* s, unsigned int chunk_size,\n                              SparseFileSource* source __unused, unsigned int blocks,\n                              unsigned int block __unused, uint32_t* crc32) {\n  if (chunk_size != 0) {\n    return -EINVAL;\n  }\n\n  if (crc32) {\n    int64_t len = (int64_t)blocks * s->block_size;\n    memset(copybuf, 0, COPY_BUF_SIZE);\n\n    while (len) {\n      int chunk = std::min(len, COPY_BUF_SIZE);\n      *crc32 = sparse_crc32(*crc32, copybuf, chunk);\n      len -= chunk;\n    }\n  }\n\n  return 0;\n}\n\nstatic int process_crc32_chunk(SparseFileSource* source, unsigned int chunk_size, uint32_t* crc32) {\n  uint32_t file_crc32;\n\n  if (chunk_size != sizeof(file_crc32)) {\n    return -EINVAL;\n  }\n\n  int ret = source->ReadValue(&file_crc32, sizeof(file_crc32));\n  if (ret < 0) {\n    return ret;\n  }\n\n  if (crc32 != nullptr && file_crc32 != *crc32) {\n    return -EINVAL;\n  }\n\n  return 0;\n}\n\nstatic int process_chunk(struct sparse_file* s, SparseFileSource* source, unsigned int chunk_hdr_sz,\n                         chunk_header_t* chunk_header, unsigned int cur_block, uint32_t* crc_ptr) {\n  int ret;\n  unsigned int chunk_data_size;\n  int64_t offset = source->GetOffset();\n\n  chunk_data_size = chunk_header->total_sz - chunk_hdr_sz;\n\n  switch (chunk_header->chunk_type) {\n    case CHUNK_TYPE_RAW:\n      ret =\n          process_raw_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block, crc_ptr);\n      if (ret < 0) {\n        verbose_error(s->verbose, ret, \"data block at %\" PRId64, offset);\n        return ret;\n      }\n      return chunk_header->chunk_sz;\n    case CHUNK_TYPE_FILL:\n      ret = process_fill_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,\n                               crc_ptr);\n      if (ret < 0) {\n        verbose_error(s->verbose, ret, \"fill block at %\" PRId64, offset);\n        return ret;\n      }\n      return chunk_header->chunk_sz;\n    case CHUNK_TYPE_DONT_CARE:\n      ret = process_skip_chunk(s, chunk_data_size, source, chunk_header->chunk_sz, cur_block,\n                               crc_ptr);\n      if (chunk_data_size != 0) {\n        if (ret < 0) {\n          verbose_error(s->verbose, ret, \"skip block at %\" PRId64, offset);\n          return ret;\n        }\n      }\n      return chunk_header->chunk_sz;\n    case CHUNK_TYPE_CRC32:\n      ret = process_crc32_chunk(source, chunk_data_size, crc_ptr);\n      if (ret < 0) {\n        verbose_error(s->verbose, -EINVAL, \"crc block at %\" PRId64, offset);\n        return ret;\n      }\n      return 0;\n    default:\n      verbose_error(s->verbose, -EINVAL, \"unknown block %04X at %\" PRId64, chunk_header->chunk_type,\n                    offset);\n  }\n\n  return 0;\n}\n\nstatic int sparse_file_read_sparse(struct sparse_file* s, SparseFileSource* source, bool crc) {\n  int ret;\n  unsigned int i;\n  sparse_header_t sparse_header;\n  chunk_header_t chunk_header;\n  uint32_t crc32 = 0;\n  uint32_t* crc_ptr = nullptr;\n  unsigned int cur_block = 0;\n\n  if (!copybuf) {\n    copybuf = (char*)malloc(COPY_BUF_SIZE);\n  }\n\n  if (!copybuf) {\n    return -ENOMEM;\n  }\n\n  if (crc) {\n    crc_ptr = &crc32;\n  }\n\n  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));\n  if (ret < 0) {\n    return ret;\n  }\n\n  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {\n    return -EINVAL;\n  }\n\n  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {\n    return -EINVAL;\n  }\n\n  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {\n    return -EINVAL;\n  }\n\n  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header)) {\n    return -EINVAL;\n  }\n\n  if (sparse_header.file_hdr_sz > SPARSE_HEADER_LEN) {\n    /* Skip the remaining bytes in a header that is longer than\n     * we expected.\n     */\n    ret = source->Seek(sparse_header.file_hdr_sz - SPARSE_HEADER_LEN);\n    if (ret < 0) {\n      return ret;\n    }\n  }\n\n  for (i = 0; i < sparse_header.total_chunks; i++) {\n    ret = source->ReadValue(&chunk_header, sizeof(chunk_header));\n    if (ret < 0) {\n      return ret;\n    }\n\n    if (sparse_header.chunk_hdr_sz > CHUNK_HEADER_LEN) {\n      /* Skip the remaining bytes in a header that is longer than\n       * we expected.\n       */\n      ret = source->Seek(sparse_header.chunk_hdr_sz - CHUNK_HEADER_LEN);\n      if (ret < 0) {\n        return ret;\n      }\n    }\n\n    ret = process_chunk(s, source, sparse_header.chunk_hdr_sz, &chunk_header, cur_block, crc_ptr);\n    if (ret < 0) {\n      return ret;\n    }\n\n    cur_block += ret;\n  }\n\n  if (sparse_header.total_blks != cur_block) {\n    return -EINVAL;\n  }\n\n  return 0;\n}\n\nstatic int do_sparse_file_read_normal(struct sparse_file* s, int fd, uint32_t* buf, int64_t offset,\n                                      int64_t remain) {\n  int ret;\n  unsigned int block = offset / s->block_size;\n  unsigned int to_read;\n  unsigned int i;\n  bool sparse_block;\n\n  if (!buf) {\n    return -ENOMEM;\n  }\n\n  while (remain > 0) {\n    to_read = std::min(remain, (int64_t)(s->block_size));\n    ret = read_all(fd, buf, to_read);\n    if (ret < 0) {\n      error(\"failed to read sparse file\");\n      return ret;\n    }\n\n    if (to_read == s->block_size) {\n      sparse_block = true;\n      for (i = 1; i < s->block_size / sizeof(uint32_t); i++) {\n        if (buf[0] != buf[i]) {\n          sparse_block = false;\n          break;\n        }\n      }\n    } else {\n      sparse_block = false;\n    }\n\n    if (sparse_block) {\n      /* TODO: add flag to use skip instead of fill for buf[0] == 0 */\n      sparse_file_add_fill(s, buf[0], to_read, block);\n    } else {\n      sparse_file_add_fd(s, fd, offset, to_read, block);\n    }\n\n    remain -= to_read;\n    offset += to_read;\n    block++;\n  }\n\n  return 0;\n}\n\nstatic int sparse_file_read_normal(struct sparse_file* s, int fd) {\n  int ret;\n  uint32_t* buf = (uint32_t*)malloc(s->block_size);\n\n  if (!buf)\n    return -ENOMEM;\n\n  ret = do_sparse_file_read_normal(s, fd, buf, 0, s->len);\n  free(buf);\n  return ret;\n}\n\n#ifdef __linux__\nstatic int sparse_file_read_hole(struct sparse_file* s, int fd) {\n  int ret;\n  uint32_t* buf = (uint32_t*)malloc(s->block_size);\n  int64_t end = 0;\n  int64_t start = 0;\n\n  if (!buf) {\n    return -ENOMEM;\n  }\n\n  do {\n    start = lseek(fd, end, SEEK_DATA);\n    if (start < 0) {\n      if (errno == ENXIO)\n        /* The rest of the file is a hole */\n        break;\n\n      error(\"could not seek to data\");\n      free(buf);\n      return -errno;\n    } else if (start > s->len) {\n      break;\n    }\n\n    end = lseek(fd, start, SEEK_HOLE);\n    if (end < 0) {\n      error(\"could not seek to end\");\n      free(buf);\n      return -errno;\n    }\n    end = std::min(end, s->len);\n\n    start = ALIGN_DOWN(start, s->block_size);\n    end = ALIGN(end, s->block_size);\n    if (lseek(fd, start, SEEK_SET) < 0) {\n      free(buf);\n      return -errno;\n    }\n\n    ret = do_sparse_file_read_normal(s, fd, buf, start, end - start);\n    if (ret) {\n      free(buf);\n      return ret;\n    }\n  } while (end < s->len);\n\n  free(buf);\n  return 0;\n}\n#else\nstatic int sparse_file_read_hole(struct sparse_file* s __unused, int fd __unused) {\n  return -ENOTSUP;\n}\n#endif\n\nint sparse_file_read(struct sparse_file* s, int fd, enum sparse_read_mode mode, bool crc) {\n  if (crc && mode != SPARSE_READ_MODE_SPARSE) {\n    return -EINVAL;\n  }\n\n  switch (mode) {\n    case SPARSE_READ_MODE_SPARSE: {\n      SparseFileFdSource source(fd);\n      return sparse_file_read_sparse(s, &source, crc);\n    }\n    case SPARSE_READ_MODE_NORMAL:\n      return sparse_file_read_normal(s, fd);\n    case SPARSE_READ_MODE_HOLE:\n      return sparse_file_read_hole(s, fd);\n    default:\n      return -EINVAL;\n  }\n}\n\nstatic struct sparse_file* sparse_file_import_source(SparseFileSource* source, bool verbose,\n                                                     bool crc) {\n  int ret;\n  sparse_header_t sparse_header;\n  int64_t len;\n  struct sparse_file* s;\n\n  ret = source->ReadValue(&sparse_header, sizeof(sparse_header));\n  if (ret < 0) {\n    verbose_error(verbose, ret, \"header\");\n    return nullptr;\n  }\n\n  if (sparse_header.magic != SPARSE_HEADER_MAGIC) {\n    verbose_error(verbose, -EINVAL, \"header magic\");\n    return nullptr;\n  }\n\n  if (sparse_header.major_version != SPARSE_HEADER_MAJOR_VER) {\n    verbose_error(verbose, -EINVAL, \"header major version\");\n    return nullptr;\n  }\n\n  if (sparse_header.file_hdr_sz < SPARSE_HEADER_LEN) {\n    return nullptr;\n  }\n\n  if (sparse_header.chunk_hdr_sz < sizeof(chunk_header_t)) {\n    return nullptr;\n  }\n\n  if (!sparse_header.blk_sz || (sparse_header.blk_sz % 4)) {\n    return nullptr;\n  }\n\n  if (!sparse_header.total_blks) {\n    return nullptr;\n  }\n\n  len = (int64_t)sparse_header.total_blks * sparse_header.blk_sz;\n  s = sparse_file_new(sparse_header.blk_sz, len);\n  if (!s) {\n    verbose_error(verbose, -EINVAL, nullptr);\n    return nullptr;\n  }\n\n  ret = source->Rewind();\n  if (ret < 0) {\n    verbose_error(verbose, ret, \"seeking\");\n    sparse_file_destroy(s);\n    return nullptr;\n  }\n\n  s->verbose = verbose;\n\n  ret = sparse_file_read_sparse(s, source, crc);\n  if (ret < 0) {\n    sparse_file_destroy(s);\n    return nullptr;\n  }\n\n  return s;\n}\n\nstruct sparse_file* sparse_file_import(int fd, bool verbose, bool crc) {\n  SparseFileFdSource source(fd);\n  return sparse_file_import_source(&source, verbose, crc);\n}\n\nstruct sparse_file* sparse_file_import_buf(char* buf, size_t len, bool verbose, bool crc) {\n  SparseFileBufSource source(buf, len);\n  return sparse_file_import_source(&source, verbose, crc);\n}\n\nstruct sparse_file* sparse_file_import_auto(int fd, bool crc, bool verbose) {\n  struct sparse_file* s;\n  int64_t len;\n  int ret;\n\n  s = sparse_file_import(fd, false, crc);\n  if (s) {\n    return s;\n  }\n\n  len = lseek64(fd, 0, SEEK_END);\n  if (len < 0) {\n    return nullptr;\n  }\n\n  lseek64(fd, 0, SEEK_SET);\n\n  s = sparse_file_new(4096, len);\n  if (!s) {\n    return nullptr;\n  }\n  if (verbose) {\n    sparse_file_verbose(s);\n  }\n\n  ret = sparse_file_read_normal(s, fd);\n  if (ret < 0) {\n    sparse_file_destroy(s);\n    return nullptr;\n  }\n\n  return s;\n}\n"
  },
  {
    "path": "libstats/OWNERS",
    "content": "jeffreyhuang@google.com\nmonicamwang@google.com\nmuhammadq@google.com\nrayhdez@google.com\nsharaienko@google.com\nsinghtejinder@google.com\ntsaichristine@google.com\nyaochen@google.com\n"
  },
  {
    "path": "libstats/bootstrap/Android.bp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n// =========================================================================\n// Native library that provide a client to StatsBootstrapAtomService.\n// This library should only be used by processes that start in the bootstrap namespace.\n// All other clients should use libstatssocket, provided by the statsd apex.\n// =========================================================================\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libstatsbootstrap_defaults\",\n    srcs: [\n        \"BootstrapClientInternal.cpp\",\n        \"StatsBootstrapAtomClient.cpp\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbinder\",\n        \"libutils\",\n        \"android.os.statsbootstrap_aidl-cpp\",\n    ],\n}\n\ncc_library {\n    name: \"libstatsbootstrap\",\n    defaults: [\"libstatsbootstrap_defaults\"],\n    export_include_dirs: [\"include\"],\n}\n\n\n"
  },
  {
    "path": "libstats/bootstrap/BootstrapClientInternal.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"BootstrapClientInternal.h\"\n\n#include <binder/IServiceManager.h>\n\nnamespace android {\nnamespace os {\nnamespace stats {\n\nsp<BootstrapClientInternal> BootstrapClientInternal::getInstance() {\n    static sp<BootstrapClientInternal> client = new BootstrapClientInternal();\n    return client;\n}\n\nsp<IStatsBootstrapAtomService> BootstrapClientInternal::getServiceNonBlocking() {\n    std::lock_guard<std::mutex> lock(mLock);\n    if (mService != nullptr) {\n        return mService;\n    }\n    connectNonBlockingLocked();\n    return mService;\n}\n\nvoid BootstrapClientInternal::binderDied(const wp<IBinder>&) {\n    std::lock_guard<std::mutex> lock(mLock);\n    mService = nullptr;\n    connectNonBlockingLocked();\n}\n\nvoid BootstrapClientInternal::connectNonBlockingLocked() {\n    const String16 name(\"statsbootstrap\");\n    mService =\n            interface_cast<IStatsBootstrapAtomService>(defaultServiceManager()->checkService(name));\n    if (mService != nullptr) {\n        // Set up binder death.\n        IInterface::asBinder(mService)->linkToDeath(this);\n    }\n}\n\n}  // namespace stats\n}  // namespace os\n}  // namespace android"
  },
  {
    "path": "libstats/bootstrap/BootstrapClientInternal.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android/os/IStatsBootstrapAtomService.h>\n\n#include <mutex>\n\nnamespace android {\nnamespace os {\nnamespace stats {\n\nclass BootstrapClientInternal : public IBinder::DeathRecipient {\n  public:\n    static sp<BootstrapClientInternal> getInstance();\n    void binderDied(const wp<IBinder>& who) override;\n    sp<IStatsBootstrapAtomService> getServiceNonBlocking();\n\n  private:\n    BootstrapClientInternal() {}\n    void connectNonBlockingLocked();\n\n    mutable std::mutex mLock;\n    sp<IStatsBootstrapAtomService> mService;\n};\n\n}  // namespace stats\n}  // namespace os\n}  // namespace android\n"
  },
  {
    "path": "libstats/bootstrap/StatsBootstrapAtomClient.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"include/StatsBootstrapAtomClient.h\"\n\n#include <android/os/IStatsBootstrapAtomService.h>\n\n#include \"BootstrapClientInternal.h\"\n\nnamespace android {\nnamespace os {\nnamespace stats {\n\nbool StatsBootstrapAtomClient::reportBootstrapAtom(const StatsBootstrapAtom& atom) {\n    sp<IStatsBootstrapAtomService> service =\n            BootstrapClientInternal::getInstance()->getServiceNonBlocking();\n    if (service == nullptr) {\n        return false;\n    }\n    return service->reportBootstrapAtom(atom).isOk();\n}\n\n}  // namespace stats\n}  // namespace os\n}  // namespace android"
  },
  {
    "path": "libstats/bootstrap/include/StatsBootstrapAtomClient.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <android/os/StatsBootstrapAtom.h>\n\nnamespace android {\nnamespace os {\nnamespace stats {\n\nclass StatsBootstrapAtomClient {\n  public:\n    static bool reportBootstrapAtom(const StatsBootstrapAtom& atom);\n};\n\n}  // namespace stats\n}  // namespace os\n}  // namespace android"
  },
  {
    "path": "libstats/expresslog/.clang-format",
    "content": "BasedOnStyle: Google\nAllowShortIfStatementsOnASingleLine: true\nAllowShortFunctionsOnASingleLine: false\nAllowShortLoopsOnASingleLine: true\nBinPackArguments: true\nBinPackParameters: true\nColumnLimit: 100\nCommentPragmas: NOLINT:.*\nContinuationIndentWidth: 8\nDerivePointerAlignment: false\nIndentWidth: 4\nPointerAlignment: Left\nTabWidth: 4\nAccessModifierOffset: -4\nIncludeCategories:\n  - Regex:    '^\"Log\\.h\"'\n    Priority:    -1\n"
  },
  {
    "path": "libstats/expresslog/Android.bp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n    default_team: \"trendy_team_android_telemetry_client_infra\",\n}\n\ncc_defaults {\n    name: \"expresslog_defaults\",\n    srcs: [\n        \"Counter.cpp\",\n        \"Histogram.cpp\",\n    ],\n}\n\ncc_library {\n    name: \"libexpresslog\",\n    host_supported: true,\n    defaults: [\"expresslog_defaults\"],\n    cflags: [\n        \"-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    header_libs: [\n        \"libtextclassifier_hash_headers\",\n    ],\n    static_libs: [\n        \"libstatslog_express\",\n        \"libtextclassifier_hash_static\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n        \"libstatssocket\",\n    ],\n    export_include_dirs: [\"include\"],\n    min_sdk_version: \"33\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.bt\",\n    ],\n}\n\ngenrule {\n    name: \"statslog_express.h\",\n    tools: [\"stats-log-api-gen\"],\n    cmd: \"$(location stats-log-api-gen) --header $(genDir)/statslog_express.h --module expresslog --namespace android,expresslog\",\n    out: [\n        \"statslog_express.h\",\n    ],\n}\n\ngenrule {\n    name: \"statslog_express.cpp\",\n    tools: [\"stats-log-api-gen\"],\n    cmd: \"$(location stats-log-api-gen) --cpp $(genDir)/statslog_express.cpp --module expresslog --namespace android,expresslog --importHeader statslog_express.h\",\n    out: [\n        \"statslog_express.cpp\",\n    ],\n}\n\ncc_library_static {\n    name: \"libstatslog_express\",\n    host_supported: true,\n    generated_sources: [\"statslog_express.cpp\"],\n    generated_headers: [\"statslog_express.h\"],\n    export_generated_headers: [\"statslog_express.h\"],\n    shared_libs: [\n        \"libstatssocket\",\n    ],\n    min_sdk_version: \"33\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.bt\",\n    ],\n}\n\ncc_test {\n    name: \"expresslog_test\",\n    defaults: [\"expresslog_defaults\"],\n    test_suites: [\n        \"general-tests\",\n    ],\n    srcs: [\n        \"tests/Histogram_test.cpp\",\n    ],\n    local_include_dirs: [\n        \"include\",\n    ],\n    cflags: [\n        \"-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash\",\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Wunused\",\n        \"-Wpedantic\",\n        \"-Werror\",\n    ],\n    header_libs: [\n        \"libtextclassifier_hash_headers\",\n    ],\n    static_libs: [\n        \"libgmock\",\n        \"libbase\",\n        \"liblog\",\n        \"libstatslog_express\",\n        \"libtextclassifier_hash_static\",\n    ],\n    shared_libs: [\n        \"libstatssocket\",\n    ],\n}\n"
  },
  {
    "path": "libstats/expresslog/Counter.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"include/Counter.h\"\n\n#include <statslog_express.h>\n#include <string.h>\n#include <utils/hash/farmhash.h>\n\nnamespace android {\nnamespace expresslog {\n\nvoid Counter::logIncrement(const char* metricName, int64_t amount) {\n    const int64_t metricIdHash = farmhash::Fingerprint64(metricName, strlen(metricName));\n    stats_write(EXPRESS_EVENT_REPORTED, metricIdHash, amount);\n}\n\nvoid Counter::logIncrementWithUid(const char* metricName, int32_t uid, int64_t amount) {\n    const int64_t metricIdHash = farmhash::Fingerprint64(metricName, strlen(metricName));\n    stats_write(EXPRESS_UID_EVENT_REPORTED, metricIdHash, amount, uid);\n}\n\n}  // namespace expresslog\n}  // namespace android\n"
  },
  {
    "path": "libstats/expresslog/Histogram.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"include/Histogram.h\"\n\n#define LOG_TAG \"tex\"\n\n#include <log/log.h>\n#include <statslog_express.h>\n#include <string.h>\n#include <utils/hash/farmhash.h>\n\nnamespace android {\nnamespace expresslog {\n\nstd::shared_ptr<Histogram::UniformOptions> Histogram::UniformOptions::create(\n        int binCount, float minValue, float exclusiveMaxValue) {\n    if (binCount < 1) {\n        ALOGE(\"Bin count should be positive number\");\n        return nullptr;\n    }\n\n    if (exclusiveMaxValue <= minValue) {\n        ALOGE(\"Bins range invalid (maxValue < minValue)\");\n        return nullptr;\n    }\n\n    return std::shared_ptr<UniformOptions>(\n            new UniformOptions(binCount, minValue, exclusiveMaxValue));\n}\n\nHistogram::UniformOptions::UniformOptions(int binCount, float minValue, float exclusiveMaxValue)\n    :  // Implicitly add 2 for the extra undeflow & overflow bins\n      mBinCount(binCount + 2),\n      mMinValue(minValue),\n      mExclusiveMaxValue(exclusiveMaxValue),\n      mBinSize((exclusiveMaxValue - minValue) / binCount) {\n}\n\nint Histogram::UniformOptions::getBinForSample(float sample) const {\n    if (sample < mMinValue) {\n        // goes to underflow\n        return 0;\n    } else if (sample >= mExclusiveMaxValue) {\n        // goes to overflow\n        return mBinCount - 1;\n    }\n    return (int)((sample - mMinValue) / mBinSize + 1);\n}\n\nHistogram::Histogram(const char* metricName, std::shared_ptr<BinOptions> binOptions)\n    : mMetricIdHash(farmhash::Fingerprint64(metricName, strlen(metricName))),\n      mBinOptions(std::move(binOptions)) {\n}\n\nvoid Histogram::logSample(float sample) const {\n    const int binIndex = mBinOptions->getBinForSample(sample);\n    stats_write(EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash, /*count*/ 1, binIndex);\n}\n\nvoid Histogram::logSampleWithUid(int32_t uid, float sample) const {\n    const int binIndex = mBinOptions->getBinForSample(sample);\n    stats_write(EXPRESS_UID_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash, /*count*/ 1, binIndex, uid);\n}\n\n}  // namespace expresslog\n}  // namespace android\n"
  },
  {
    "path": "libstats/expresslog/include/Counter.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n#include <stdint.h>\n\nnamespace android {\nnamespace expresslog {\n\n/** Counter encapsulates StatsD write API calls */\nclass Counter final {\npublic:\n    static void logIncrement(const char* metricId, int64_t amount = 1);\n\n    static void logIncrementWithUid(const char* metricId, int32_t uid, int64_t amount = 1);\n};\n\n}  // namespace expresslog\n}  // namespace android\n"
  },
  {
    "path": "libstats/expresslog/include/Histogram.h",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n#include <stdint.h>\n\n#include <memory>\n\nnamespace android {\nnamespace expresslog {\n\n/** Histogram encapsulates StatsD write API calls */\nclass Histogram final {\npublic:\n    class BinOptions {\n    public:\n        virtual ~BinOptions() = default;\n        /**\n         * Returns bins count to be used by a Histogram\n         *\n         * @return bins count used to initialize Options, including overflow & underflow bins\n         */\n        virtual int getBinsCount() const = 0;\n\n        /**\n         * @return zero based index\n         * Calculates bin index for the input sample value\n         * index == 0 stands for underflow\n         * index == getBinsCount() - 1 stands for overflow\n         */\n        virtual int getBinForSample(float sample) const = 0;\n    };\n\n    /** Used by Histogram to map data sample to corresponding bin for uniform bins */\n    class UniformOptions : public BinOptions {\n    public:\n        static std::shared_ptr<UniformOptions> create(int binCount, float minValue,\n                                                      float exclusiveMaxValue);\n\n        int getBinsCount() const override {\n            return mBinCount;\n        }\n\n        int getBinForSample(float sample) const override;\n\n    private:\n        UniformOptions(int binCount, float minValue, float exclusiveMaxValue);\n\n        const int mBinCount;\n        const float mMinValue;\n        const float mExclusiveMaxValue;\n        const float mBinSize;\n    };\n\n    Histogram(const char* metricName, std::shared_ptr<BinOptions> binOptions);\n\n    /**\n     * Logs increment sample count for automatically calculated bin\n     */\n    void logSample(float sample) const;\n\n    /**\n     * Logs increment sample count for automatically calculated bin with uid\n     */\n    void logSampleWithUid(int32_t uid, float sample) const;\n\nprivate:\n    const int64_t mMetricIdHash;\n    const std::shared_ptr<BinOptions> mBinOptions;\n};\n\n}  // namespace expresslog\n}  // namespace android\n"
  },
  {
    "path": "libstats/expresslog/tests/Histogram_test.cpp",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"Histogram.h\"\n\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace expresslog {\n\n#ifdef __ANDROID__\nTEST(UniformOptions, getBinsCount) {\n    const std::shared_ptr<Histogram::UniformOptions> options1(\n            Histogram::UniformOptions::create(1, 100, 1000));\n    ASSERT_EQ(3, options1->getBinsCount());\n\n    const std::shared_ptr<Histogram::UniformOptions> options10(\n            Histogram::UniformOptions::create(10, 100, 1000));\n    ASSERT_EQ(12, options10->getBinsCount());\n}\n\nTEST(UniformOptions, constructZeroBinsCount) {\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(0, 100, 1000));\n    ASSERT_EQ(nullptr, options);\n}\n\nTEST(UniformOptions, constructNegativeBinsCount) {\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(-1, 100, 1000));\n    ASSERT_EQ(nullptr, options);\n}\n\nTEST(UniformOptions, constructMaxValueLessThanMinValue) {\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(10, 1000, 100));\n    ASSERT_EQ(nullptr, options);\n}\n\nTEST(UniformOptions, testBinIndexForRangeEqual1) {\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(10, 1, 11));\n    for (int i = 0, bins = options->getBinsCount(); i < bins; i++) {\n        ASSERT_EQ(i, options->getBinForSample(i));\n    }\n}\n\nTEST(UniformOptions, testBinIndexForRangeEqual2) {\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(10, 1, 21));\n    for (int i = 0, bins = options->getBinsCount(); i < bins; i++) {\n        ASSERT_EQ(i, options->getBinForSample(i * 2));\n        ASSERT_EQ(i, options->getBinForSample(i * 2 - 1));\n    }\n}\n\nTEST(UniformOptions, testBinIndexForRangeEqual5) {\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(2, 0, 10));\n    ASSERT_EQ(4, options->getBinsCount());\n    for (int i = 0; i < 2; i++) {\n        for (int sample = 0; sample < 5; sample++) {\n            ASSERT_EQ(i + 1, options->getBinForSample(i * 5 + sample));\n        }\n    }\n}\n\nTEST(UniformOptions, testBinIndexForRangeEqual10) {\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(10, 1, 101));\n    ASSERT_EQ(0, options->getBinForSample(0));\n    ASSERT_EQ(options->getBinsCount() - 2, options->getBinForSample(100));\n    ASSERT_EQ(options->getBinsCount() - 1, options->getBinForSample(101));\n\n    const float binSize = (101 - 1) / 10.f;\n    for (int i = 1, bins = options->getBinsCount() - 1; i < bins; i++) {\n        ASSERT_EQ(i, options->getBinForSample(i * binSize));\n    }\n}\n\nTEST(UniformOptions, testBinIndexForRangeEqual90) {\n    const int binCount = 10;\n    const int minValue = 100;\n    const int maxValue = 100000;\n\n    const std::shared_ptr<Histogram::UniformOptions> options(\n            Histogram::UniformOptions::create(binCount, minValue, maxValue));\n\n    // logging underflow sample\n    ASSERT_EQ(0, options->getBinForSample(minValue - 1));\n\n    // logging overflow sample\n    ASSERT_EQ(binCount + 1, options->getBinForSample(maxValue));\n    ASSERT_EQ(binCount + 1, options->getBinForSample(maxValue + 1));\n\n    // logging min edge sample\n    ASSERT_EQ(1, options->getBinForSample(minValue));\n\n    // logging max edge sample\n    ASSERT_EQ(binCount, options->getBinForSample(maxValue - 1));\n\n    // logging single valid sample per bin\n    const int binSize = (maxValue - minValue) / binCount;\n\n    for (int i = 0; i < binCount; i++) {\n        ASSERT_EQ(i + 1, options->getBinForSample(minValue + binSize * i));\n    }\n}\n\n#else\nGTEST_LOG_(INFO) << \"This test does nothing.\\n\";\n#endif\n\n}  // namespace expresslog\n}  // namespace android\n"
  },
  {
    "path": "libstats/pull_lazy/Android.bp",
    "content": "// Lazy loading version of libstatspull that can be used by code\n// that is running before the statsd APEX is mounted and\n// libstatspull.so is available.\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libstatspull_lazy\",\n    header_libs: [\n        \"libstatspull_headers\",\n        \"libstatssocket_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libstatspull_headers\",\n    ],\n    apex_available: [\"//apex_available:platform\"],\n    srcs: [\"libstatspull_lazy.cpp\"],\n}\n\ncc_test {\n    name: \"libstatspull_lazy_test\",\n    srcs: [\n        \"tests/libstatspull_lazy_test.cpp\",\n    ],\n    static_libs: [\n        \"libstatspull_lazy\",\n        \"libstatssocket_lazy\",\n    ],\n    shared_libs: [\"liblog\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    test_suites: [\"device-tests\"],\n    test_config: \"libstatspull_lazy_test.xml\",\n    // TODO(b/153588990): Remove when the build system properly separates.\n    // 32bit and 64bit architectures.\n    compile_multilib: \"both\",\n    multilib: {\n        lib64: {\n            suffix: \"64\",\n        },\n        lib32: {\n            suffix: \"32\",\n        },\n    },\n}\n"
  },
  {
    "path": "libstats/pull_lazy/TEST_MAPPING",
    "content": "{\n  \"presubmit\" : [\n    {\n      \"name\" : \"libstatspull_lazy_test\"\n    }\n  ],\n  \"hwasan-presubmit\" : [\n    {\n      \"name\" : \"libstatspull_lazy_test\"\n    }\n  ]\n}"
  },
  {
    "path": "libstats/pull_lazy/libstatspull_lazy.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libstatspull_lazy.h\"\n\n#include <mutex>\n\n#include <dlfcn.h>\n#include <stdatomic.h>\n\n#include \"log/log.h\"\n\n#include \"stats_pull_atom_callback.h\"\n\n// This file provides a lazy interface to libstatspull.so to address early boot dependencies.\n// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and\n// libstatspull.so is in the statsd APEX.\n\n// Method pointers to libstatspull methods are held in an array which simplifies checking\n// all pointers are initialized.\nenum MethodIndex {\n    // PullAtomMetadata APIs in stats_pull_atom_callback.h.\n    k_AStatsManager_PullAtomMetadata_obtain,\n    k_AStatsManager_PullAtomMetadata_release,\n    k_AStatsManager_PullAtomMetadata_setCoolDownMillis,\n    k_AStatsManager_PullAtomMetadata_getCoolDownMillis,\n    k_AStatsManager_PullAtomMetadata_setTimeoutMillis,\n    k_AStatsManager_PullAtomMetadata_getTimeoutMillis,\n    k_AStatsManager_PullAtomMetadata_setAdditiveFields,\n    k_AStatsManager_PullAtomMetadata_getNumAdditiveFields,\n    k_AStatsManager_PullAtomMetadata_getAdditiveFields,\n\n    // AStatsEventList APIs in stats_pull_atom_callback.h\n    k_AStatsEventList_addStatsEvent,\n\n    // PullAtomCallback APIs in stats_pull_atom_callback.h\n    k_AStatsManager_setPullAtomCallback,\n    k_AStatsManager_clearPullAtomCallback,\n\n    // Marker for count of methods\n    k_MethodCount\n};\n\n// Table of methods pointers in libstatspull APIs.\nstatic void* g_Methods[k_MethodCount];\n\n//\n// Libstatspull lazy loading.\n//\n\nstatic atomic_bool gPreventLibstatspullLoading = false;  // Allows tests to block loading.\n\nvoid PreventLibstatspullLazyLoadingForTests() {\n    gPreventLibstatspullLoading.store(true);\n}\n\nstatic void* LoadLibstatspull(int dlopen_flags) {\n    if (gPreventLibstatspullLoading.load()) {\n        return nullptr;\n    }\n    return dlopen(\"libstatspull.so\", dlopen_flags);\n}\n\n//\n// Initialization and symbol binding.\n\nstatic void BindSymbol(void* handle, const char* name, enum MethodIndex index) {\n    void* symbol = dlsym(handle, name);\n    LOG_ALWAYS_FATAL_IF(symbol == nullptr, \"Failed to find symbol '%s' in libstatspull.so: %s\",\n                        name, dlerror());\n    g_Methods[index] = symbol;\n}\n\nstatic void InitializeOnce() {\n    void* handle = LoadLibstatspull(RTLD_NOW);\n    LOG_ALWAYS_FATAL_IF(handle == nullptr, \"Failed to load libstatspull.so: %s\", dlerror());\n\n#undef BIND_SYMBOL\n#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name);\n    // PullAtomMetadata APIs in stats_pull_atom_callback.h.\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_obtain);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_release);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_setCoolDownMillis);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getCoolDownMillis);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_setTimeoutMillis);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getTimeoutMillis);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_setAdditiveFields);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getNumAdditiveFields);\n    BIND_SYMBOL(AStatsManager_PullAtomMetadata_getAdditiveFields);\n\n    // AStatsEventList APIs in stats_pull_atom_callback.h\n    BIND_SYMBOL(AStatsEventList_addStatsEvent);\n\n    // PullAtomCallback APIs in stats_pull_atom_callback.h\n    BIND_SYMBOL(AStatsManager_setPullAtomCallback);\n    BIND_SYMBOL(AStatsManager_clearPullAtomCallback);\n\n#undef BIND_SYMBOL\n\n    // Check every symbol is bound.\n    for (int i = 0; i < k_MethodCount; ++i) {\n        LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr,\n                            \"Uninitialized method in libstatspull_lazy at index: %d\", i);\n    }\n}\n\nstatic void EnsureInitialized() {\n    static std::once_flag initialize_flag;\n    std::call_once(initialize_flag, InitializeOnce);\n}\n\n#define INVOKE_METHOD(name, args...)                            \\\n    do {                                                        \\\n        EnsureInitialized();                                    \\\n        void* method = g_Methods[k_##name];                     \\\n        return reinterpret_cast<decltype(&name)>(method)(args); \\\n    } while (0)\n\n//\n// Forwarding for methods in stats_pull_atom_callback.h.\n//\n\nAStatsManager_PullAtomMetadata* AStatsManager_PullAtomMetadata_obtain() {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_obtain);\n}\n\nvoid AStatsManager_PullAtomMetadata_release(AStatsManager_PullAtomMetadata* metadata) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_release, metadata);\n}\n\nvoid AStatsManager_PullAtomMetadata_setCoolDownMillis(AStatsManager_PullAtomMetadata* metadata,\n                                                      int64_t cool_down_millis) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_setCoolDownMillis, metadata, cool_down_millis);\n}\n\nint64_t AStatsManager_PullAtomMetadata_getCoolDownMillis(AStatsManager_PullAtomMetadata* metadata) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getCoolDownMillis, metadata);\n}\n\nvoid AStatsManager_PullAtomMetadata_setTimeoutMillis(AStatsManager_PullAtomMetadata* metadata,\n                                                     int64_t timeout_millis) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_setTimeoutMillis, metadata, timeout_millis);\n}\n\nint64_t AStatsManager_PullAtomMetadata_getTimeoutMillis(AStatsManager_PullAtomMetadata* metadata) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getTimeoutMillis, metadata);\n}\n\nvoid AStatsManager_PullAtomMetadata_setAdditiveFields(AStatsManager_PullAtomMetadata* metadata,\n                                                      int32_t* additive_fields,\n                                                      int32_t num_fields) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_setAdditiveFields, metadata, additive_fields,\n                  num_fields);\n}\n\nint32_t AStatsManager_PullAtomMetadata_getNumAdditiveFields(\n        AStatsManager_PullAtomMetadata* metadata) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getNumAdditiveFields, metadata);\n}\n\nvoid AStatsManager_PullAtomMetadata_getAdditiveFields(AStatsManager_PullAtomMetadata* metadata,\n                                                      int32_t* fields) {\n    INVOKE_METHOD(AStatsManager_PullAtomMetadata_getAdditiveFields, metadata, fields);\n}\n\nAStatsEvent* AStatsEventList_addStatsEvent(AStatsEventList* pull_data) {\n    INVOKE_METHOD(AStatsEventList_addStatsEvent, pull_data);\n}\n\nvoid AStatsManager_setPullAtomCallback(int32_t atom_tag, AStatsManager_PullAtomMetadata* metadata,\n                                       AStatsManager_PullAtomCallback callback, void* cookie) {\n    INVOKE_METHOD(AStatsManager_setPullAtomCallback, atom_tag, metadata, callback, cookie);\n}\n\nvoid AStatsManager_clearPullAtomCallback(int32_t atom_tag) {\n    INVOKE_METHOD(AStatsManager_clearPullAtomCallback, atom_tag);\n}\n"
  },
  {
    "path": "libstats/pull_lazy/libstatspull_lazy.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nextern \"C\" void PreventLibstatspullLazyLoadingForTests();"
  },
  {
    "path": "libstats/pull_lazy/libstatspull_lazy_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2021 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Runs libstatspull_lazy_test.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-native\" />\n    <option name=\"test-suite-tag\" value=\"mts\" />\n\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\"/>\n\n    <target_preparer class=\"com.android.compatibility.common.tradefed.targetprep.FilePusher\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"libstatspull_lazy_test->/data/local/tmp/libstatspull_lazy_test\" />\n        <option name=\"append-bitness\" value=\"true\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        <option name=\"native-test-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"module-name\" value=\"libstatspull_lazy_test\" />\n    </test>\n\n    <object type=\"module_controller\" class=\"com.android.tradefed.testtype.suite.module.MainlineTestModuleController\">\n        <option name=\"mainline-module-package-name\" value=\"com.google.android.os.statsd\" />\n    </object>\n</configuration>"
  },
  {
    "path": "libstats/pull_lazy/tests/libstatspull_lazy_test.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../libstatspull_lazy.h\"\n\n#include <gtest/gtest.h>\n\n#include \"stats_pull_atom_callback.h\"\n//#include \"stats_event.h\"\n\n// The tests here are just for the case when libstatspull.so cannot be loaded by\n// libstatspull_lazy.\nclass LibstatspullLazyTest : public ::testing::Test {\n  protected:\n    virtual void SetUp() {\n        ::testing::Test::SetUp();\n        PreventLibstatspullLazyLoadingForTests();\n    }\n};\n\nstatic const char* kLoadFailed = \"Failed to load libstatspull.so\";\n\nTEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomMetadata) {\n    AStatsManager_PullAtomMetadata* metadata = NULL;\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_obtain(), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_release(metadata), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_setCoolDownMillis(metadata, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getCoolDownMillis(metadata), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_setTimeoutMillis(metadata, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getTimeoutMillis(metadata), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_setAdditiveFields(metadata, NULL, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getNumAdditiveFields(metadata), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_PullAtomMetadata_getAdditiveFields(metadata, NULL), kLoadFailed);\n}\n\nTEST_F(LibstatspullLazyTest, NoLibstatspullForAStatsEventList) {\n    AStatsEventList* event_list = NULL;\n    EXPECT_DEATH(AStatsEventList_addStatsEvent(event_list), kLoadFailed);\n}\n\nTEST_F(LibstatspullLazyTest, NoLibstatspullForPullAtomCallback) {\n    AStatsManager_PullAtomCallback callback = NULL;\n    EXPECT_DEATH(AStatsManager_setPullAtomCallback(0, NULL, callback, NULL), kLoadFailed);\n    EXPECT_DEATH(AStatsManager_clearPullAtomCallback(0), kLoadFailed);\n}"
  },
  {
    "path": "libstats/pull_rust/Android.bp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_bindgen {\n    name: \"libstatspull_bindgen\",\n    wrapper_src: \"statslog.h\",\n    crate_name: \"statspull_bindgen\",\n    visibility: [\n        \"//frameworks/proto_logging/stats/stats_log_api_gen\",\n        \"//packages/modules:__subpackages__\",\n    ],\n    source_stem: \"bindings\",\n    bindgen_flags: [\n        \"--allowlist-function=AStatsEventList_addStatsEvent\",\n        \"--allowlist-function=AStatsEvent_.*\",\n        \"--allowlist-function=AStatsManager_.*\",\n        \"--allowlist-var=AStatsManager_.*\",\n    ],\n    target: {\n        android: {\n            shared_libs: [\n                \"libstatspull\",\n                \"libstatssocket\",\n            ],\n        },\n        host: {\n            static_libs: [\n                \"libstatspull\",\n                \"libstatssocket\",\n            ],\n        },\n    },\n    min_sdk_version: \"apex_inherit\",\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.resolv\",\n        \"com.android.virt\",\n    ],\n}\n\nrust_library {\n    name: \"libstatspull_rust\",\n    crate_name: \"statspull_rust\",\n    srcs: [\"stats_pull.rs\"],\n    rustlibs: [\n        \"liblog_rust\",\n        \"libstatslog_rust_header\",\n        \"libstatspull_bindgen\",\n    ],\n}\n\nrust_test {\n    name: \"libstatspull_bindgen_test\",\n    srcs: [\":libstatspull_bindgen\"],\n    crate_name: \"statspull_bindgen_test\",\n    test_suites: [\"general-tests\"],\n    auto_gen_config: true,\n    clippy_lints: \"none\",\n    lints: \"none\",\n}\n"
  },
  {
    "path": "libstats/pull_rust/stats_pull.rs",
    "content": "// Copyright 2021, The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! A Rust interface for the StatsD pull API.\n\nuse statslog_rust_header::{Atoms, Stat, StatsError};\nuse statspull_bindgen::*;\nuse std::collections::HashMap;\nuse std::convert::TryInto;\nuse std::os::raw::c_void;\nuse std::sync::{LazyLock, Mutex};\n\n/// The return value of callbacks.\npub type StatsPullResult = Vec<Box<dyn Stat>>;\n\n/// A wrapper for AStatsManager_PullAtomMetadata.\n/// It calls AStatsManager_PullAtomMetadata_release on drop.\npub struct Metadata {\n    metadata: *mut AStatsManager_PullAtomMetadata,\n}\n\nimpl Metadata {\n    /// Calls AStatsManager_PullAtomMetadata_obtain.\n    pub fn new() -> Self {\n        // Safety: We panic if the memory allocation fails.\n        let metadata = unsafe { AStatsManager_PullAtomMetadata_obtain() };\n        if metadata.is_null() {\n            panic!(\"Cannot obtain pull atom metadata.\");\n        } else {\n            Metadata { metadata }\n        }\n    }\n\n    /// Calls AStatsManager_PullAtomMetadata_setCoolDownMillis.\n    pub fn set_cooldown_millis(&mut self, cooldown_millis: i64) {\n        // Safety: Metadata::new ensures that self.metadata is a valid object.\n        unsafe { AStatsManager_PullAtomMetadata_setCoolDownMillis(self.metadata, cooldown_millis) }\n    }\n\n    /// Calls AStatsManager_PullAtomMetadata_getCoolDownMillis.\n    pub fn get_cooldown_millis(&self) -> i64 {\n        // Safety: Metadata::new ensures that self.metadata is a valid object.\n        unsafe { AStatsManager_PullAtomMetadata_getCoolDownMillis(self.metadata) }\n    }\n\n    /// Calls AStatsManager_PullAtomMetadata_setTimeoutMillis.\n    pub fn set_timeout_millis(&mut self, timeout_millis: i64) {\n        // Safety: Metadata::new ensures that self.metadata is a valid object.\n        unsafe { AStatsManager_PullAtomMetadata_setTimeoutMillis(self.metadata, timeout_millis) }\n    }\n\n    /// Calls AStatsManager_PullAtomMetadata_getTimeoutMillis.\n    pub fn get_timeout_millis(&self) -> i64 {\n        // Safety: Metadata::new ensures that self.metadata is a valid object.\n        unsafe { AStatsManager_PullAtomMetadata_getTimeoutMillis(self.metadata) }\n    }\n\n    /// Calls AStatsManager_PullAtomMetadata_setAdditiveFields.\n    pub fn set_additive_fields(&mut self, additive_fields: &mut [i32]) {\n        // Safety: Metadata::new ensures that self.metadata is a valid object.\n        unsafe {\n            AStatsManager_PullAtomMetadata_setAdditiveFields(\n                self.metadata,\n                additive_fields.as_mut_ptr(),\n                additive_fields.len().try_into().expect(\"Cannot convert length to i32\"),\n            )\n        }\n    }\n\n    /// Calls AStatsManager_PullAtomMetadata_getAdditiveFields.\n    pub fn get_additive_fields(&self) -> Vec<i32> {\n        // Safety: Metadata::new ensures that self.metadata is a valid object.\n        // We call getNumAdditiveFields to ensure we pass getAdditiveFields a large enough array.\n        unsafe {\n            let num_fields = AStatsManager_PullAtomMetadata_getNumAdditiveFields(self.metadata)\n                .try_into()\n                .expect(\"Cannot convert num additive fields to usize\");\n            let mut fields = vec![0; num_fields];\n            AStatsManager_PullAtomMetadata_getAdditiveFields(self.metadata, fields.as_mut_ptr());\n            fields\n        }\n    }\n}\n\nimpl Drop for Metadata {\n    fn drop(&mut self) {\n        // Safety: Metadata::new ensures that self.metadata is a valid object.\n        unsafe { AStatsManager_PullAtomMetadata_release(self.metadata) }\n    }\n}\n\nimpl Default for Metadata {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nstatic COOKIES: LazyLock<Mutex<HashMap<i32, fn() -> StatsPullResult>>> =\n    LazyLock::new(|| Mutex::new(HashMap::new()));\n\n/// # Safety\n///\n/// `data` must be a valid pointer with no aliases.\nunsafe extern \"C\" fn callback_wrapper(\n    atom_tag: i32,\n    data: *mut AStatsEventList,\n    _cookie: *mut c_void,\n) -> AStatsManager_PullAtomCallbackReturn {\n    if !data.is_null() {\n        let map = COOKIES.lock().unwrap();\n        let cb = map.get(&atom_tag);\n        match cb {\n            None => log::error!(\"No callback found for {}\", atom_tag),\n            Some(cb) => {\n                let stats = cb();\n                let result = stats\n                    .iter()\n                    // Safety: The caller promises that `data` is valid and unaliased.\n                    .map(|stat| stat.add_astats_event(unsafe { &mut *data }))\n                    .collect::<Result<Vec<()>, StatsError>>();\n                match result {\n                    Ok(_) => {\n                        return AStatsManager_PULL_SUCCESS as AStatsManager_PullAtomCallbackReturn\n                    }\n                    _ => log::error!(\"Error adding astats events: {:?}\", result),\n                }\n            }\n        }\n    }\n    AStatsManager_PULL_SKIP as AStatsManager_PullAtomCallbackReturn\n}\n\n/// Rust wrapper for AStatsManager_setPullAtomCallback.\npub fn set_pull_atom_callback(\n    atom: Atoms,\n    metadata: Option<&Metadata>,\n    callback: fn() -> StatsPullResult,\n) {\n    COOKIES.lock().unwrap().insert(atom as i32, callback);\n    let metadata_raw = match metadata {\n        Some(m) => m.metadata,\n        None => std::ptr::null_mut(),\n    };\n    // Safety: We pass a valid function as the callback.\n    unsafe {\n        AStatsManager_setPullAtomCallback(\n            atom as i32,\n            metadata_raw,\n            Some(callback_wrapper),\n            std::ptr::null_mut(),\n        );\n    }\n}\n\n/// Rust wrapper for AStatsManager_clearPullAtomCallback.\npub fn clear_pull_atom_callback(atom: Atoms) {\n    COOKIES.lock().unwrap().remove(&(atom as i32));\n    // Safety: No memory allocations.\n    unsafe { AStatsManager_clearPullAtomCallback(atom as i32) }\n}\n"
  },
  {
    "path": "libstats/pull_rust/statslog.h",
    "content": "#pragma once\n\n#include \"stats_pull_atom_callback.h\"\n"
  },
  {
    "path": "libstats/push_compat/Android.bp",
    "content": "//\n// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n// =========================================================================\n// Native library that toggles between the old and new statsd socket\n// protocols. This library should only be used by DNS resolver or other\n// native modules on Q that log pushed atoms to statsd.\n// =========================================================================\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libstatspush_compat_defaults\",\n    srcs: [\n        \"statsd_writer.cpp\",\n        \"stats_event_list.c\",\n        \"StatsEventCompat.cpp\"\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-DWRITE_TO_STATSD=1\",\n        \"-DWRITE_TO_LOGD=0\",\n    ],\n    header_libs: [\n        \"libcutils_headers\",\n        \"libstatssocket_headers\",\n    ],\n    static_libs: [\n        \"libbase\",\n    ],\n    shared_libs: [\n        \"liblog\",\n    ],\n}\n\ncc_library {\n    name: \"libstatspush_compat\",\n    defaults: [\"libstatspush_compat_defaults\"],\n    export_include_dirs: [\"include\"],\n    export_header_lib_headers: [\n        \"libstatssocket_headers\",\n    ],\n    header_libs: [\"libgtest_prod_headers\"],\n    apex_available: [\"com.android.resolv\"],\n    min_sdk_version: \"29\",\n}\n\ncc_test {\n    name: \"libstatspush_compat_test\",\n    defaults: [\"libstatspush_compat_defaults\"],\n    test_suites: [\"device_tests\"],\n    srcs: [\n        \"tests/StatsEventCompat_test.cpp\",\n    ],\n    static_libs: [\"libgmock\"],\n}\n"
  },
  {
    "path": "libstats/push_compat/StatsEventCompat.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"include/StatsEventCompat.h\"\n\n#include <chrono>\n\n#include <android-base/chrono_utils.h>\n#include <android-base/properties.h>\n#include <android/api-level.h>\n#include <android/log.h>\n#include <dlfcn.h>\n\nusing android::base::boot_clock;\nusing android::base::GetProperty;\n\nconst static int kStatsEventTag = 1937006964;\nconst bool StatsEventCompat::mPlatformAtLeastR =\n        android_get_device_api_level() >= __ANDROID_API_R__;\n\n// initializations of static class variables\nbool StatsEventCompat::mAttemptedLoad = false;\nstd::mutex StatsEventCompat::mLoadLock;\nAStatsEventApi StatsEventCompat::mAStatsEventApi;\n\nstatic int64_t elapsedRealtimeNano() {\n    return std::chrono::time_point_cast<std::chrono::nanoseconds>(boot_clock::now())\n            .time_since_epoch()\n            .count();\n}\n\nStatsEventCompat::StatsEventCompat() : mEventQ(kStatsEventTag) {\n    // guard loading because StatsEventCompat might be called from multithreaded\n    // environment\n    {\n        std::lock_guard<std::mutex> lg(mLoadLock);\n        if (!mAttemptedLoad && mPlatformAtLeastR) {\n            void* handle = dlopen(\"libstatssocket.so\", RTLD_NOW);\n            if (handle) {\n                initializeApiTableLocked(handle);\n            } else {\n                ALOGE(\"dlopen failed: %s\\n\", dlerror());\n            }\n        }\n        mAttemptedLoad = true;\n    }\n\n    if (useRSchema()) {\n        mEventR = mAStatsEventApi.obtain();\n    } else if (useQSchema()) {\n        mEventQ << elapsedRealtimeNano();\n    }\n}\n\nStatsEventCompat::~StatsEventCompat() {\n    if (useRSchema()) mAStatsEventApi.release(mEventR);\n}\n\n// Populates the AStatsEventApi struct by calling dlsym to find the address of\n// each API function.\nvoid StatsEventCompat::initializeApiTableLocked(void* handle) {\n    mAStatsEventApi.obtain = (AStatsEvent* (*)())dlsym(handle, \"AStatsEvent_obtain\");\n    mAStatsEventApi.build = (void (*)(AStatsEvent*))dlsym(handle, \"AStatsEvent_build\");\n    mAStatsEventApi.write = (int (*)(AStatsEvent*))dlsym(handle, \"AStatsEvent_write\");\n    mAStatsEventApi.release = (void (*)(AStatsEvent*))dlsym(handle, \"AStatsEvent_release\");\n    mAStatsEventApi.setAtomId =\n            (void (*)(AStatsEvent*, uint32_t))dlsym(handle, \"AStatsEvent_setAtomId\");\n    mAStatsEventApi.writeInt32 =\n            (void (*)(AStatsEvent*, int32_t))dlsym(handle, \"AStatsEvent_writeInt32\");\n    mAStatsEventApi.writeInt64 =\n            (void (*)(AStatsEvent*, int64_t))dlsym(handle, \"AStatsEvent_writeInt64\");\n    mAStatsEventApi.writeFloat =\n            (void (*)(AStatsEvent*, float))dlsym(handle, \"AStatsEvent_writeFloat\");\n    mAStatsEventApi.writeBool =\n            (void (*)(AStatsEvent*, bool))dlsym(handle, \"AStatsEvent_writeBool\");\n    mAStatsEventApi.writeByteArray = (void (*)(AStatsEvent*, const uint8_t*, size_t))dlsym(\n            handle, \"AStatsEvent_writeByteArray\");\n    mAStatsEventApi.writeString =\n            (void (*)(AStatsEvent*, const char*))dlsym(handle, \"AStatsEvent_writeString\");\n    mAStatsEventApi.writeAttributionChain =\n            (void (*)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t))dlsym(\n                    handle, \"AStatsEvent_writeAttributionChain\");\n    mAStatsEventApi.addBoolAnnotation =\n            (void (*)(AStatsEvent*, uint8_t, bool))dlsym(handle, \"AStatsEvent_addBoolAnnotation\");\n    mAStatsEventApi.addInt32Annotation = (void (*)(AStatsEvent*, uint8_t, int32_t))dlsym(\n            handle, \"AStatsEvent_addInt32Annotation\");\n\n    mAStatsEventApi.initialized = true;\n}\n\nvoid StatsEventCompat::setAtomId(int32_t atomId) {\n    if (useRSchema()) {\n        mAStatsEventApi.setAtomId(mEventR, (uint32_t)atomId);\n    } else if (useQSchema()) {\n        mEventQ << atomId;\n    }\n}\n\nvoid StatsEventCompat::writeInt32(int32_t value) {\n    if (useRSchema()) {\n        mAStatsEventApi.writeInt32(mEventR, value);\n    } else if (useQSchema()) {\n        mEventQ << value;\n    }\n}\n\nvoid StatsEventCompat::writeInt64(int64_t value) {\n    if (useRSchema()) {\n        mAStatsEventApi.writeInt64(mEventR, value);\n    } else if (useQSchema()) {\n        mEventQ << value;\n    }\n}\n\nvoid StatsEventCompat::writeFloat(float value) {\n    if (useRSchema()) {\n        mAStatsEventApi.writeFloat(mEventR, value);\n    } else if (useQSchema()) {\n        mEventQ << value;\n    }\n}\n\nvoid StatsEventCompat::writeBool(bool value) {\n    if (useRSchema()) {\n        mAStatsEventApi.writeBool(mEventR, value);\n    } else if (useQSchema()) {\n        mEventQ << value;\n    }\n}\n\nvoid StatsEventCompat::writeByteArray(const char* buffer, size_t length) {\n    if (useRSchema()) {\n        mAStatsEventApi.writeByteArray(mEventR, reinterpret_cast<const uint8_t*>(buffer), length);\n    } else if (useQSchema()) {\n        mEventQ.AppendCharArray(buffer, length);\n    }\n}\n\nvoid StatsEventCompat::writeString(const char* value) {\n    if (value == nullptr) value = \"\";\n\n    if (useRSchema()) {\n        mAStatsEventApi.writeString(mEventR, value);\n    } else if (useQSchema()) {\n        mEventQ << value;\n    }\n}\n\nvoid StatsEventCompat::writeAttributionChain(const int32_t* uids, size_t numUids,\n                                             const vector<const char*>& tags) {\n    if (useRSchema()) {\n        mAStatsEventApi.writeAttributionChain(mEventR, (const uint32_t*)uids, tags.data(),\n                                              (uint8_t)numUids);\n    } else if (useQSchema()) {\n        mEventQ.begin();\n        for (size_t i = 0; i < numUids; i++) {\n            mEventQ.begin();\n            mEventQ << uids[i];\n            const char* tag = tags[i] ? tags[i] : \"\";\n            mEventQ << tag;\n            mEventQ.end();\n        }\n        mEventQ.end();\n    }\n}\n\nvoid StatsEventCompat::writeKeyValuePairs(const map<int, int32_t>& int32Map,\n                                          const map<int, int64_t>& int64Map,\n                                          const map<int, const char*>& stringMap,\n                                          const map<int, float>& floatMap) {\n    // AStatsEvent does not support key value pairs.\n    if (useQSchema()) {\n        mEventQ.begin();\n        writeKeyValuePairMap(int32Map);\n        writeKeyValuePairMap(int64Map);\n        writeKeyValuePairMap(stringMap);\n        writeKeyValuePairMap(floatMap);\n        mEventQ.end();\n    }\n}\n\ntemplate <class T>\nvoid StatsEventCompat::writeKeyValuePairMap(const map<int, T>& keyValuePairMap) {\n    for (const auto& it : keyValuePairMap) {\n        mEventQ.begin();\n        mEventQ << it.first;\n        mEventQ << it.second;\n        mEventQ.end();\n    }\n}\n\n// explicitly specify which types we're going to use\ntemplate void StatsEventCompat::writeKeyValuePairMap<int32_t>(const map<int, int32_t>&);\ntemplate void StatsEventCompat::writeKeyValuePairMap<int64_t>(const map<int, int64_t>&);\ntemplate void StatsEventCompat::writeKeyValuePairMap<float>(const map<int, float>&);\ntemplate void StatsEventCompat::writeKeyValuePairMap<const char*>(const map<int, const char*>&);\n\nvoid StatsEventCompat::addBoolAnnotation(uint8_t annotationId, bool value) {\n    if (useRSchema()) {\n        mAStatsEventApi.addBoolAnnotation(mEventR, annotationId, value);\n    }\n    // Don't do anything if on Q.\n}\n\nvoid StatsEventCompat::addInt32Annotation(uint8_t annotationId, int32_t value) {\n    if (useRSchema()) {\n        mAStatsEventApi.addInt32Annotation(mEventR, annotationId, value);\n    }\n    // Don't do anything if on Q.\n}\n\nint StatsEventCompat::writeToSocket() {\n    if (useRSchema()) {\n        return mAStatsEventApi.write(mEventR);\n    }\n\n    if (useQSchema()) return mEventQ.write(LOG_ID_STATS);\n\n    // We reach here only if we're on R, but libstatssocket was unable to\n    // be loaded using dlopen.\n    return -ENOLINK;\n}\n\nbool StatsEventCompat::useRSchema() {\n    return mPlatformAtLeastR && mAStatsEventApi.initialized;\n}\n\nbool StatsEventCompat::useQSchema() {\n    return !mPlatformAtLeastR;\n}\n"
  },
  {
    "path": "libstats/push_compat/include/StatsEventCompat.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <gtest/gtest_prod.h>\n#include <map>\n#include <mutex>\n#include <vector>\n#include \"stats_event.h\"\n#include \"stats_event_list.h\"\n\nusing std::map;\nusing std::vector;\n\nstruct AStatsEventApi {\n    // Indicates whether the below function pointers have been set using dlsym.\n    bool initialized = false;\n\n    AStatsEvent* (*obtain)(void);\n    void (*build)(AStatsEvent*);\n    int (*write)(AStatsEvent*);\n    void (*release)(AStatsEvent*);\n    void (*setAtomId)(AStatsEvent*, uint32_t);\n    void (*writeInt32)(AStatsEvent*, int32_t);\n    void (*writeInt64)(AStatsEvent*, int64_t);\n    void (*writeFloat)(AStatsEvent*, float);\n    void (*writeBool)(AStatsEvent*, bool);\n    void (*writeByteArray)(AStatsEvent*, const uint8_t*, size_t);\n    void (*writeString)(AStatsEvent*, const char*);\n    void (*writeAttributionChain)(AStatsEvent*, const uint32_t*, const char* const*, uint8_t);\n    void (*addBoolAnnotation)(AStatsEvent*, uint8_t, bool);\n    void (*addInt32Annotation)(AStatsEvent*, uint8_t, int32_t);\n};\n\nclass StatsEventCompat {\n  public:\n    StatsEventCompat();\n    ~StatsEventCompat();\n\n    void setAtomId(int32_t atomId);\n    void writeInt32(int32_t value);\n    void writeInt64(int64_t value);\n    void writeFloat(float value);\n    void writeBool(bool value);\n    void writeByteArray(const char* buffer, size_t length);\n    void writeString(const char* value);\n\n    // Pre-condition: numUids == tags.size()\n    void writeAttributionChain(const int32_t* uids, size_t numUids,\n                               const vector<const char*>& tags);\n\n    void writeKeyValuePairs(const map<int, int32_t>& int32Map, const map<int, int64_t>& int64Map,\n                            const map<int, const char*>& stringMap,\n                            const map<int, float>& floatMap);\n\n    void addBoolAnnotation(uint8_t annotationId, bool value);\n    void addInt32Annotation(uint8_t annotationId, int32_t value);\n\n    int writeToSocket();\n\n  private:\n    // static member variables\n    const static bool mPlatformAtLeastR;\n    static bool mAttemptedLoad;\n    static std::mutex mLoadLock;\n    static AStatsEventApi mAStatsEventApi;\n\n    // non-static member variables\n    AStatsEvent* mEventR = nullptr;\n    stats_event_list mEventQ;\n\n    template <class T>\n    void writeKeyValuePairMap(const map<int, T>& keyValuePairMap);\n\n    void initializeApiTableLocked(void* handle);\n    bool useRSchema();\n    bool useQSchema();\n\n    FRIEND_TEST(StatsEventCompatTest, TestDynamicLoading);\n};\n"
  },
  {
    "path": "libstats/push_compat/include/stats_event_list.h",
    "content": "/*\n * Copyright (C) 2018, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <log/log_event_list.h>\n#include <sys/uio.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\nvoid reset_log_context(android_log_context ctx);\nint write_to_logger(android_log_context context, log_id_t id);\nvoid note_log_drop(int error, int atom_tag);\nvoid stats_log_close();\nint android_log_write_char_array(android_log_context ctx, const char* value, size_t len);\nextern int (*write_to_statsd)(struct iovec* vec, size_t nr);\n\n#ifdef __cplusplus\n}\n#endif\n\n#ifdef __cplusplus\n/**\n * A copy of android_log_event_list class.\n *\n * android_log_event_list is going to be deprecated soon, so copy it here to\n * avoid creating dependency on upstream code. TODO(b/78304629): Rewrite this\n * code.\n */\nclass stats_event_list {\n  private:\n    android_log_context ctx;\n    int ret;\n\n    stats_event_list(const stats_event_list&) = delete;\n    void operator=(const stats_event_list&) = delete;\n\n  public:\n    explicit stats_event_list(int tag) : ret(0) {\n        ctx = create_android_logger(static_cast<uint32_t>(tag));\n    }\n    ~stats_event_list() { android_log_destroy(&ctx); }\n\n    int close() {\n        int retval = android_log_destroy(&ctx);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return retval;\n    }\n\n    /* To allow above C calls to use this class as parameter */\n    operator android_log_context() const { return ctx; }\n\n    /* return errors or transmit status */\n    int status() const { return ret; }\n\n    int begin() {\n        int retval = android_log_write_list_begin(ctx);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret;\n    }\n    int end() {\n        int retval = android_log_write_list_end(ctx);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret;\n    }\n\n    stats_event_list& operator<<(int32_t value) {\n        int retval = android_log_write_int32(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    stats_event_list& operator<<(uint32_t value) {\n        int retval = android_log_write_int32(ctx, static_cast<int32_t>(value));\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    stats_event_list& operator<<(bool value) {\n        int retval = android_log_write_int32(ctx, value ? 1 : 0);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    stats_event_list& operator<<(int64_t value) {\n        int retval = android_log_write_int64(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    stats_event_list& operator<<(uint64_t value) {\n        int retval = android_log_write_int64(ctx, static_cast<int64_t>(value));\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    stats_event_list& operator<<(const char* value) {\n        int retval = android_log_write_string8(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    stats_event_list& operator<<(const std::string& value) {\n        int retval = android_log_write_string8_len(ctx, value.data(), value.length());\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    stats_event_list& operator<<(float value) {\n        int retval = android_log_write_float32(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return *this;\n    }\n\n    int write(log_id_t id = LOG_ID_EVENTS) {\n        /* facilitate -EBUSY retry */\n        if ((ret == -EBUSY) || (ret > 0)) {\n            ret = 0;\n        }\n        int retval = write_to_logger(ctx, id);\n        /* existing errors trump transmission errors */\n        if (!ret) {\n            ret = retval;\n        }\n        return ret;\n    }\n\n    /*\n     * Append<Type> methods removes any integer promotion\n     * confusion, and adds access to string with length.\n     * Append methods are also added for all types for\n     * convenience.\n     */\n\n    bool AppendInt(int32_t value) {\n        int retval = android_log_write_int32(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret >= 0;\n    }\n\n    bool AppendLong(int64_t value) {\n        int retval = android_log_write_int64(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret >= 0;\n    }\n\n    bool AppendString(const char* value) {\n        int retval = android_log_write_string8(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret >= 0;\n    }\n\n    bool AppendString(const char* value, size_t len) {\n        int retval = android_log_write_string8_len(ctx, value, len);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret >= 0;\n    }\n\n    bool AppendString(const std::string& value) {\n        int retval = android_log_write_string8_len(ctx, value.data(), value.length());\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret;\n    }\n\n    bool Append(const std::string& value) {\n        int retval = android_log_write_string8_len(ctx, value.data(), value.length());\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret;\n    }\n\n    bool AppendFloat(float value) {\n        int retval = android_log_write_float32(ctx, value);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret >= 0;\n    }\n\n    template <typename Tvalue>\n    bool Append(Tvalue value) {\n        *this << value;\n        return ret >= 0;\n    }\n\n    bool Append(const char* value, size_t len) {\n        int retval = android_log_write_string8_len(ctx, value, len);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret >= 0;\n    }\n\n    bool AppendCharArray(const char* value, size_t len) {\n        int retval = android_log_write_char_array(ctx, value, len);\n        if (retval < 0) {\n            ret = retval;\n        }\n        return ret >= 0;\n    }\n};\n\n#endif\n"
  },
  {
    "path": "libstats/push_compat/stats_event_list.c",
    "content": "/*\n * Copyright (C) 2018, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"include/stats_event_list.h\"\n\n#include <string.h>\n#include <sys/time.h>\n#include \"statsd_writer.h\"\n\n#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))\n\ntypedef struct {\n    uint32_t tag;\n    unsigned pos;                                    /* Read/write position into buffer */\n    unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */\n    unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */\n    unsigned list_nest_depth;\n    unsigned len; /* Length or raw buffer. */\n    bool overflow;\n    bool list_stop; /* next call decrement list_nest_depth and issue a stop */\n    enum {\n        kAndroidLoggerRead = 1,\n        kAndroidLoggerWrite = 2,\n    } read_write_flag;\n    uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];\n} android_log_context_internal;\n\nextern struct android_log_transport_write statsdLoggerWrite;\n\nstatic int __write_to_statsd_init(struct iovec* vec, size_t nr);\nint (*write_to_statsd)(struct iovec* vec, size_t nr) = __write_to_statsd_init;\n\n// Similar to create_android_logger(), but instead of allocation a new buffer,\n// this function resets the buffer for resuse.\nvoid reset_log_context(android_log_context ctx) {\n    if (!ctx) {\n        return;\n    }\n    android_log_context_internal* context = (android_log_context_internal*)(ctx);\n    uint32_t tag = context->tag;\n    memset(context, 0, sizeof(android_log_context_internal));\n\n    context->tag = tag;\n    context->read_write_flag = kAndroidLoggerWrite;\n    size_t needed = sizeof(uint8_t) + sizeof(uint8_t);\n    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {\n        context->overflow = true;\n    }\n    /* Everything is a list */\n    context->storage[context->pos + 0] = EVENT_TYPE_LIST;\n    context->list[0] = context->pos + 1;\n    context->pos += needed;\n}\n\nint stats_write_list(android_log_context ctx) {\n    android_log_context_internal* context;\n    const char* msg;\n    ssize_t len;\n\n    context = (android_log_context_internal*)(ctx);\n    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {\n        return -EBADF;\n    }\n\n    if (context->list_nest_depth) {\n        return -EIO;\n    }\n\n    /* NB: if there was overflow, then log is truncated. Nothing reported */\n    context->storage[1] = context->count[0];\n    len = context->len = context->pos;\n    msg = (const char*)context->storage;\n    /* it's not a list */\n    if (context->count[0] <= 1) {\n        len -= sizeof(uint8_t) + sizeof(uint8_t);\n        if (len < 0) {\n            len = 0;\n        }\n        msg += sizeof(uint8_t) + sizeof(uint8_t);\n    }\n\n    struct iovec vec[2];\n    vec[0].iov_base = &context->tag;\n    vec[0].iov_len = sizeof(context->tag);\n    vec[1].iov_base = (void*)msg;\n    vec[1].iov_len = len;\n    return write_to_statsd(vec, 2);\n}\n\nint write_to_logger(android_log_context ctx, log_id_t id) {\n    int retValue = 0;\n\n    if (WRITE_TO_LOGD) {\n        retValue = android_log_write_list(ctx, id);\n    }\n\n    if (WRITE_TO_STATSD) {\n        // log_event_list's cast operator is overloaded.\n        int ret = stats_write_list(ctx);\n        // In debugging phase, we may write to both logd and statsd. Prefer to\n        // return statsd socket write error code here.\n        if (ret < 0) {\n            retValue = ret;\n        }\n    }\n\n    return retValue;\n}\n\nvoid note_log_drop(int error, int tag) {\n    statsdLoggerWrite.noteDrop(error, tag);\n}\n\nvoid stats_log_close() {\n    statsd_writer_init_lock();\n    write_to_statsd = __write_to_statsd_init;\n    if (statsdLoggerWrite.close) {\n        (*statsdLoggerWrite.close)();\n    }\n    statsd_writer_init_unlock();\n}\n\n/* log_init_lock assumed */\nstatic int __write_to_statsd_initialize_locked() {\n    if (!statsdLoggerWrite.open || ((*statsdLoggerWrite.open)() < 0)) {\n        if (statsdLoggerWrite.close) {\n            (*statsdLoggerWrite.close)();\n            return -ENODEV;\n        }\n    }\n    return 1;\n}\n\nstatic int __write_to_stats_daemon(struct iovec* vec, size_t nr) {\n    int save_errno;\n    struct timespec ts;\n    size_t len, i;\n\n    for (len = i = 0; i < nr; ++i) {\n        len += vec[i].iov_len;\n    }\n    if (!len) {\n        return -EINVAL;\n    }\n\n    save_errno = errno;\n#if defined(__ANDROID__)\n    clock_gettime(CLOCK_REALTIME, &ts);\n#else\n    struct timeval tv;\n    gettimeofday(&tv, NULL);\n    ts.tv_sec = tv.tv_sec;\n    ts.tv_nsec = tv.tv_usec * 1000;\n#endif\n\n    int ret = (int)(*statsdLoggerWrite.write)(&ts, vec, nr);\n    errno = save_errno;\n    return ret;\n}\n\nstatic int __write_to_statsd_init(struct iovec* vec, size_t nr) {\n    int ret, save_errno = errno;\n\n    statsd_writer_init_lock();\n\n    if (write_to_statsd == __write_to_statsd_init) {\n        ret = __write_to_statsd_initialize_locked();\n        if (ret < 0) {\n            statsd_writer_init_unlock();\n            errno = save_errno;\n            return ret;\n        }\n\n        write_to_statsd = __write_to_stats_daemon;\n    }\n\n    statsd_writer_init_unlock();\n\n    ret = write_to_statsd(vec, nr);\n    errno = save_errno;\n    return ret;\n}\n\nstatic inline void copy4LE(uint8_t* buf, uint32_t val) {\n    buf[0] = val & 0xFF;\n    buf[1] = (val >> 8) & 0xFF;\n    buf[2] = (val >> 16) & 0xFF;\n    buf[3] = (val >> 24) & 0xFF;\n}\n\n// Note: this function differs from android_log_write_string8_len in that the length passed in\n// should be treated as actual length and not max length.\nint android_log_write_char_array(android_log_context ctx, const char* value, size_t actual_len) {\n    size_t needed;\n    ssize_t len = actual_len;\n    android_log_context_internal* context;\n\n    context = (android_log_context_internal*)ctx;\n    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {\n        return -EBADF;\n    }\n    if (context->overflow) {\n        return -EIO;\n    }\n    if (!value) {\n        value = \"\";\n        len = 0;\n    }\n    needed = sizeof(uint8_t) + sizeof(int32_t) + len;\n    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {\n        /* Truncate string for delivery */\n        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);\n        if (len <= 0) {\n            context->overflow = true;\n            return -EIO;\n        }\n    }\n    context->count[context->list_nest_depth]++;\n    context->storage[context->pos + 0] = EVENT_TYPE_STRING;\n    copy4LE(&context->storage[context->pos + 1], len);\n    if (len) {\n        memcpy(&context->storage[context->pos + 5], value, len);\n    }\n    context->pos += needed;\n    return len;\n}\n"
  },
  {
    "path": "libstats/push_compat/statsd_writer.cpp",
    "content": "/*\n * Copyright (C) 2018, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include \"statsd_writer.h\"\n\n#include <android-base/threads.h>\n#include <cutils/fs.h>\n#include <cutils/sockets.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <poll.h>\n#include <private/android_filesystem_config.h>\n#include <private/android_logger.h>\n#include <stdarg.h>\n#include <stdatomic.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/uio.h>\n#include <sys/un.h>\n#include <time.h>\n#include <unistd.h>\n\nstatic pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;\nstatic atomic_int dropped = 0;\nstatic atomic_int log_error = 0;\nstatic atomic_int atom_tag = 0;\n\nvoid statsd_writer_init_lock() {\n    /*\n     * If we trigger a signal handler in the middle of locked activity and the\n     * signal handler logs a message, we could get into a deadlock state.\n     */\n    pthread_mutex_lock(&log_init_lock);\n}\n\nint statd_writer_trylock() {\n    return pthread_mutex_trylock(&log_init_lock);\n}\n\nvoid statsd_writer_init_unlock() {\n    pthread_mutex_unlock(&log_init_lock);\n}\n\nstatic int statsdAvailable();\nstatic int statsdOpen();\nstatic void statsdClose();\nstatic int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr);\nstatic void statsdNoteDrop(int error, int tag);\n\nstruct android_log_transport_write statsdLoggerWrite = {\n        .name = \"statsd\",\n        .sock = -EBADF,\n        .available = statsdAvailable,\n        .open = statsdOpen,\n        .close = statsdClose,\n        .write = statsdWrite,\n        .noteDrop = statsdNoteDrop,\n};\n\n/* log_init_lock assumed */\nstatic int statsdOpen() {\n    int i, ret = 0;\n\n    i = atomic_load(&statsdLoggerWrite.sock);\n    if (i < 0) {\n        int flags = SOCK_DGRAM;\n#ifdef SOCK_CLOEXEC\n        flags |= SOCK_CLOEXEC;\n#endif\n#ifdef SOCK_NONBLOCK\n        flags |= SOCK_NONBLOCK;\n#endif\n        int sock = TEMP_FAILURE_RETRY(socket(PF_UNIX, flags, 0));\n        if (sock < 0) {\n            ret = -errno;\n        } else {\n            int sndbuf = 1 * 1024 * 1024;  // set max send buffer size 1MB\n            socklen_t bufLen = sizeof(sndbuf);\n            // SO_RCVBUF does not have an effect on unix domain socket, but SO_SNDBUF does.\n            // Proceed to connect even setsockopt fails.\n            setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &sndbuf, bufLen);\n            struct sockaddr_un un;\n            memset(&un, 0, sizeof(struct sockaddr_un));\n            un.sun_family = AF_UNIX;\n            strcpy(un.sun_path, \"/dev/socket/statsdw\");\n\n            if (TEMP_FAILURE_RETRY(\n                        connect(sock, (struct sockaddr*)&un, sizeof(struct sockaddr_un))) < 0) {\n                ret = -errno;\n                switch (ret) {\n                    case -ENOTCONN:\n                    case -ECONNREFUSED:\n                    case -ENOENT:\n                        i = atomic_exchange(&statsdLoggerWrite.sock, ret);\n                        break;\n                    default:\n                        break;\n                }\n                close(sock);\n            } else {\n                ret = atomic_exchange(&statsdLoggerWrite.sock, sock);\n                if ((ret >= 0) && (ret != sock)) {\n                    close(ret);\n                }\n                ret = 0;\n            }\n        }\n    }\n\n    return ret;\n}\n\nstatic void __statsdClose(int negative_errno) {\n    int sock = atomic_exchange(&statsdLoggerWrite.sock, negative_errno);\n    if (sock >= 0) {\n        close(sock);\n    }\n}\n\nstatic void statsdClose() {\n    __statsdClose(-EBADF);\n}\n\nstatic int statsdAvailable() {\n    if (atomic_load(&statsdLoggerWrite.sock) < 0) {\n        if (access(\"/dev/socket/statsdw\", W_OK) == 0) {\n            return 0;\n        }\n        return -EBADF;\n    }\n    return 1;\n}\n\nstatic void statsdNoteDrop(int error, int tag) {\n    atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);\n    atomic_exchange_explicit(&log_error, error, memory_order_relaxed);\n    atomic_exchange_explicit(&atom_tag, tag, memory_order_relaxed);\n}\n\nstatic int statsdWrite(struct timespec* ts, struct iovec* vec, size_t nr) {\n    ssize_t ret;\n    int sock;\n    static const unsigned headerLength = 1;\n    struct iovec newVec[nr + headerLength];\n    android_log_header_t header;\n    size_t i, payloadSize;\n\n    sock = atomic_load(&statsdLoggerWrite.sock);\n    if (sock < 0) switch (sock) {\n            case -ENOTCONN:\n            case -ECONNREFUSED:\n            case -ENOENT:\n                break;\n            default:\n                return -EBADF;\n        }\n    /*\n     *  struct {\n     *      // what we provide to socket\n     *      android_log_header_t header;\n     *      // caller provides\n     *      union {\n     *          struct {\n     *              char     prio;\n     *              char     payload[];\n     *          } string;\n     *          struct {\n     *              uint32_t tag\n     *              char     payload[];\n     *          } binary;\n     *      };\n     *  };\n     */\n\n    header.tid = android::base::GetThreadId();\n    header.realtime.tv_sec = ts->tv_sec;\n    header.realtime.tv_nsec = ts->tv_nsec;\n\n    newVec[0].iov_base = (unsigned char*)&header;\n    newVec[0].iov_len = sizeof(header);\n\n    // If we dropped events before, try to tell statsd.\n    if (sock >= 0) {\n        int32_t snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);\n        if (snapshot) {\n            android_log_event_long_t buffer;\n            header.id = LOG_ID_STATS;\n            // store the last log error in the tag field. This tag field is not used by statsd.\n            buffer.header.tag = atomic_load(&log_error);\n            buffer.payload.type = EVENT_TYPE_LONG;\n            // format:\n            // |atom_tag|dropped_count|\n            int64_t composed_long = atomic_load(&atom_tag);\n            // Send 2 int32's via an int64.\n            composed_long = ((composed_long << 32) | ((int64_t)snapshot));\n            buffer.payload.data = composed_long;\n\n            newVec[headerLength].iov_base = &buffer;\n            newVec[headerLength].iov_len = sizeof(buffer);\n\n            ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));\n            if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {\n                atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);\n            }\n        }\n    }\n\n    header.id = LOG_ID_STATS;\n\n    for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {\n        newVec[i].iov_base = vec[i - headerLength].iov_base;\n        payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;\n\n        if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {\n            newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;\n            if (newVec[i].iov_len) {\n                ++i;\n            }\n            break;\n        }\n    }\n\n    /*\n     * The write below could be lost, but will never block.\n     *\n     * ENOTCONN occurs if statsd has died.\n     * ENOENT occurs if statsd is not running and socket is missing.\n     * ECONNREFUSED occurs if we can not reconnect to statsd.\n     * EAGAIN occurs if statsd is overloaded.\n     */\n    if (sock < 0) {\n        ret = sock;\n    } else {\n        ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));\n        if (ret < 0) {\n            ret = -errno;\n        }\n    }\n    switch (ret) {\n        case -ENOTCONN:\n        case -ECONNREFUSED:\n        case -ENOENT:\n            if (statd_writer_trylock()) {\n                return ret; /* in a signal handler? try again when less stressed\n                             */\n            }\n            __statsdClose(ret);\n            ret = statsdOpen();\n            statsd_writer_init_unlock();\n\n            if (ret < 0) {\n                return ret;\n            }\n\n            ret = TEMP_FAILURE_RETRY(writev(atomic_load(&statsdLoggerWrite.sock), newVec, i));\n            if (ret < 0) {\n                ret = -errno;\n            }\n            break;\n        default:\n            break;\n    }\n\n    if (ret > (ssize_t)sizeof(header)) {\n        ret -= sizeof(header);\n    }\n\n    return ret;\n}\n"
  },
  {
    "path": "libstats/push_compat/statsd_writer.h",
    "content": "/*\n * Copyright (C) 2018, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_STATS_LOG_STATS_WRITER_H\n#define ANDROID_STATS_LOG_STATS_WRITER_H\n\n#include <pthread.h>\n#include <stdatomic.h>\n#include <sys/socket.h>\n\n__BEGIN_DECLS\n\n/**\n * Internal lock should not be exposed. This is bad design.\n * TODO: rewrite it in c++ code and encapsulate the functionality in a\n * StatsdWriter class.\n */\nvoid statsd_writer_init_lock();\nint statsd_writer_init_trylock();\nvoid statsd_writer_init_unlock();\n\nstruct android_log_transport_write {\n    const char* name; /* human name to describe the transport */\n    atomic_int sock;\n    int (*available)(); /* Does not cause resources to be taken */\n    int (*open)();      /* can be called multiple times, reusing current resources */\n    void (*close)();    /* free up resources */\n    /* write log to transport, returns number of bytes propagated, or -errno */\n    int (*write)(struct timespec* ts, struct iovec* vec, size_t nr);\n    /* note one log drop */\n    void (*noteDrop)(int error, int tag);\n};\n\n__END_DECLS\n\n#endif  // ANDROID_STATS_LOG_STATS_WRITER_H\n"
  },
  {
    "path": "libstats/push_compat/tests/StatsEventCompat_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"include/StatsEventCompat.h\"\n#include <android-base/properties.h>\n#include <android/api-level.h>\n#include <gtest/gtest.h>\n\nusing android::base::GetProperty;\n\nconst static bool mPlatformAtLeastR = android_get_device_api_level() >= __ANDROID_API_R__;\n\nTEST(StatsEventCompatTest, TestDynamicLoading) {\n    StatsEventCompat event;\n    EXPECT_EQ(mPlatformAtLeastR, event.useRSchema());\n}\n"
  },
  {
    "path": "libstats/socket_lazy/Android.bp",
    "content": "// Lazy loading version of libstatssocket that can be used by code\n// that is running before the statsd APEX is mounted and\n// libstatssocket.so is available.\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libstatssocket_lazy\",\n    local_include_dirs: [\n        \"include\",\n    ],\n    export_include_dirs: [\n        \"include\",\n    ],\n    header_libs: [\n        \"libstatssocket_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libstatssocket_headers\",\n    ],\n    apex_available: [\"//apex_available:platform\"],\n    srcs: [\"libstatssocket_lazy.cpp\"],\n}\n\ncc_test {\n    name: \"libstatssocket_lazy_test\",\n    srcs: [\n        \"tests/libstatssocket_lazy_test.cpp\",\n    ],\n    static_libs: [\"libstatssocket_lazy\"],\n    shared_libs: [\"liblog\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    test_suites: [\n        \"device-tests\",\n    ],\n    test_config: \"libstatssocket_lazy_test.xml\",\n    // TODO(b/153588990): Remove when the build system properly separates.\n    // 32bit and 64bit architectures.\n    compile_multilib: \"both\",\n    multilib: {\n        lib64: {\n            suffix: \"64\",\n        },\n        lib32: {\n            suffix: \"32\",\n        },\n    },\n}\n"
  },
  {
    "path": "libstats/socket_lazy/TEST_MAPPING",
    "content": "{\n  \"presubmit\" : [\n    {\n      \"name\" : \"libstatssocket_lazy_test\"\n    }\n  ],\n  \"hwasan-presubmit\" : [\n    {\n      \"name\" : \"libstatssocket_lazy_test\"\n    }\n  ]\n}"
  },
  {
    "path": "libstats/socket_lazy/include/statssocket_lazy.h",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nnamespace android::statssocket::lazy {\n\n// See if libstatssocket.so is available. Early processes relying on _lazy might not have access\n// to libstatssocket.so when they start before the StatsD APEX is available.\nbool IsAvailable();\n\n}  // namespace android::statssocket::lazy\n"
  },
  {
    "path": "libstats/socket_lazy/libstatssocket_lazy.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"libstatssocket_lazy.h\"\n\n#include <mutex>\n\n#include <dlfcn.h>\n#include <stdatomic.h>\n\n#include \"log/log.h\"\n\n#include <stats_event.h>\n#include <stats_socket.h>\n\n#include \"statssocket_lazy.h\"\n\n// This file provides a lazy interface to libstatssocket.so to address early boot dependencies.\n// Specifically bootanimation, surfaceflinger, and lmkd run before the statsd APEX is loaded and\n// libstatssocket.so is in the statsd APEX.\n\n// Method pointers to libstatssocket methods are held in an array which simplifies checking\n// all pointers are initialized.\nenum MethodIndex {\n    // Stats Event APIs in stats_event.h.\n    k_AStatsEvent_obtain,\n    k_AStatsEvent_build,\n    k_AStatsEvent_write,\n    k_AStatsEvent_release,\n    k_AStatsEvent_setAtomId,\n    k_AStatsEvent_writeInt32,\n    k_AStatsEvent_writeInt64,\n    k_AStatsEvent_writeFloat,\n    k_AStatsEvent_writeBool,\n    k_AStatsEvent_writeByteArray,\n    k_AStatsEvent_writeString,\n    k_AStatsEvent_writeStringArray,\n    k_AStatsEvent_writeAttributionChain,\n    k_AStatsEvent_addBoolAnnotation,\n    k_AStatsEvent_addInt32Annotation,\n\n    // Stats Socket APIs in stats_socket.h.\n    k_AStatsSocket_close,\n\n    // Marker for count of methods\n    k_MethodCount\n};\n\n// Table of methods pointers in libstatssocket APIs.\nstatic void* g_Methods[k_MethodCount];\n\n//\n// Libstatssocket lazy loading.\n//\n\nstatic atomic_bool gPreventLibstatssocketLoading = false;  // Allows tests to block loading.\n\nvoid PreventLibstatssocketLazyLoadingForTests() {\n    gPreventLibstatssocketLoading.store(true);\n}\n\nstatic void* LoadLibstatssocket(int dlopen_flags) {\n    if (gPreventLibstatssocketLoading.load()) {\n        return nullptr;\n    }\n    return dlopen(\"libstatssocket.so\", dlopen_flags);\n}\n\nnamespace android::statssocket::lazy {\nbool IsAvailable() {\n    static const void* handle = LoadLibstatssocket(RTLD_NOW);\n    return handle != nullptr;\n}\n}  // namespace android::statssocket::lazy\n\n//\n// Initialization and symbol binding.\n\nstatic void BindSymbol(void* handle, const char* name, enum MethodIndex index) {\n    void* symbol = dlsym(handle, name);\n    LOG_ALWAYS_FATAL_IF(symbol == nullptr, \"Failed to find symbol '%s' in libstatssocket.so: %s\",\n                        name, dlerror());\n    g_Methods[index] = symbol;\n}\n\nstatic void InitializeOnce() {\n    void* handle = LoadLibstatssocket(RTLD_NOW);\n    LOG_ALWAYS_FATAL_IF(handle == nullptr, \"Failed to load libstatssocket.so: %s\", dlerror());\n\n#undef BIND_SYMBOL\n#define BIND_SYMBOL(name) BindSymbol(handle, #name, k_##name);\n    // Methods in stats_event.h.\n    BIND_SYMBOL(AStatsEvent_obtain);\n    BIND_SYMBOL(AStatsEvent_build);\n    BIND_SYMBOL(AStatsEvent_write);\n    BIND_SYMBOL(AStatsEvent_release);\n    BIND_SYMBOL(AStatsEvent_setAtomId);\n    BIND_SYMBOL(AStatsEvent_writeInt32);\n    BIND_SYMBOL(AStatsEvent_writeInt64);\n    BIND_SYMBOL(AStatsEvent_writeFloat);\n    BIND_SYMBOL(AStatsEvent_writeBool);\n    BIND_SYMBOL(AStatsEvent_writeByteArray);\n    BIND_SYMBOL(AStatsEvent_writeString);\n    BIND_SYMBOL(AStatsEvent_writeStringArray);\n    BIND_SYMBOL(AStatsEvent_writeAttributionChain);\n    BIND_SYMBOL(AStatsEvent_addBoolAnnotation);\n    BIND_SYMBOL(AStatsEvent_addInt32Annotation);\n\n    // Methods in stats_socket.h.\n    BIND_SYMBOL(AStatsSocket_close);\n#undef BIND_SYMBOL\n\n    // Check every symbol is bound.\n    for (int i = 0; i < k_MethodCount; ++i) {\n        LOG_ALWAYS_FATAL_IF(g_Methods[i] == nullptr,\n                            \"Uninitialized method in libstatssocket_lazy at index: %d\", i);\n    }\n}\n\nstatic void EnsureInitialized() {\n    static std::once_flag initialize_flag;\n    std::call_once(initialize_flag, InitializeOnce);\n}\n\n#define INVOKE_METHOD(name, args...)                            \\\n    do {                                                        \\\n        EnsureInitialized();                                    \\\n        void* method = g_Methods[k_##name];                     \\\n        return reinterpret_cast<decltype(&name)>(method)(args); \\\n    } while (0)\n\n//\n// Forwarding for methods in stats_event.h.\n//\n\nAStatsEvent* AStatsEvent_obtain() {\n    INVOKE_METHOD(AStatsEvent_obtain);\n}\n\nvoid AStatsEvent_build(AStatsEvent* event) {\n    INVOKE_METHOD(AStatsEvent_build, event);\n}\n\nint AStatsEvent_write(AStatsEvent* event) {\n    INVOKE_METHOD(AStatsEvent_write, event);\n}\n\nvoid AStatsEvent_release(AStatsEvent* event) {\n    INVOKE_METHOD(AStatsEvent_release, event);\n}\n\nvoid AStatsEvent_setAtomId(AStatsEvent* event, uint32_t atomId) {\n    INVOKE_METHOD(AStatsEvent_setAtomId, event, atomId);\n}\n\nvoid AStatsEvent_writeInt32(AStatsEvent* event, int32_t value) {\n    INVOKE_METHOD(AStatsEvent_writeInt32, event, value);\n}\n\nvoid AStatsEvent_writeInt64(AStatsEvent* event, int64_t value) {\n    INVOKE_METHOD(AStatsEvent_writeInt64, event, value);\n}\n\nvoid AStatsEvent_writeFloat(AStatsEvent* event, float value) {\n    INVOKE_METHOD(AStatsEvent_writeFloat, event, value);\n}\n\nvoid AStatsEvent_writeBool(AStatsEvent* event, bool value) {\n    INVOKE_METHOD(AStatsEvent_writeBool, event, value);\n}\n\nvoid AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {\n    INVOKE_METHOD(AStatsEvent_writeByteArray, event, buf, numBytes);\n}\n\nvoid AStatsEvent_writeString(AStatsEvent* event, const char* value) {\n    INVOKE_METHOD(AStatsEvent_writeString, event, value);\n}\n\nvoid AStatsEvent_writeStringArray(AStatsEvent* event, const char* const* elements,\n                                  size_t numElements) {\n    INVOKE_METHOD(AStatsEvent_writeStringArray, event, elements, numElements);\n}\n\nvoid AStatsEvent_writeAttributionChain(AStatsEvent* event, const uint32_t* uids,\n                                       const char* const* tags, uint8_t numNodes) {\n    INVOKE_METHOD(AStatsEvent_writeAttributionChain, event, uids, tags, numNodes);\n}\n\nvoid AStatsEvent_addBoolAnnotation(AStatsEvent* event, uint8_t annotationId, bool value) {\n    INVOKE_METHOD(AStatsEvent_addBoolAnnotation, event, annotationId, value);\n}\n\nvoid AStatsEvent_addInt32Annotation(AStatsEvent* event, uint8_t annotationId, int32_t value) {\n    INVOKE_METHOD(AStatsEvent_addInt32Annotation, event, annotationId, value);\n}\n\n//\n// Forwarding for methods in stats_socket.h.\n//\n\nvoid AStatsSocket_close() {\n    INVOKE_METHOD(AStatsSocket_close);\n}\n"
  },
  {
    "path": "libstats/socket_lazy/libstatssocket_lazy.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\nextern \"C\" void PreventLibstatssocketLazyLoadingForTests();"
  },
  {
    "path": "libstats/socket_lazy/libstatssocket_lazy_test.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2021 The Android Open Source Project\n\n     Licensed under the Apache License, Version 2.0 (the \"License\");\n     you may not use this file except in compliance with the License.\n     You may obtain a copy of the License at\n\n          http://www.apache.org/licenses/LICENSE-2.0\n\n     Unless required by applicable law or agreed to in writing, software\n     distributed under the License is distributed on an \"AS IS\" BASIS,\n     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n     See the License for the specific language governing permissions and\n     limitations under the License.\n-->\n<configuration description=\"Runs libstatssocket_lazy_test.\">\n    <option name=\"test-suite-tag\" value=\"apct\" />\n    <option name=\"test-suite-tag\" value=\"apct-native\" />\n    <option name=\"test-suite-tag\" value=\"mts\" />\n\n    <target_preparer class=\"com.android.tradefed.targetprep.RootTargetPreparer\"/>\n\n    <target_preparer class=\"com.android.compatibility.common.tradefed.targetprep.FilePusher\">\n        <option name=\"cleanup\" value=\"true\" />\n        <option name=\"push\" value=\"libstatssocket_lazy_test->/data/local/tmp/libstatssocket_lazy_test\" />\n        <option name=\"append-bitness\" value=\"true\" />\n    </target_preparer>\n\n    <test class=\"com.android.tradefed.testtype.GTest\" >\n        <option name=\"native-test-device-path\" value=\"/data/local/tmp\" />\n        <option name=\"module-name\" value=\"libstatssocket_lazy_test\" />\n    </test>\n\n    <object type=\"module_controller\" class=\"com.android.tradefed.testtype.suite.module.MainlineTestModuleController\">\n        <option name=\"mainline-module-package-name\" value=\"com.google.android.os.statsd\" />\n    </object>\n</configuration>"
  },
  {
    "path": "libstats/socket_lazy/tests/libstatssocket_lazy_test.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"../libstatssocket_lazy.h\"\n\n#include <gtest/gtest.h>\n\n#include \"stats_event.h\"\n#include \"stats_socket.h\"\n\n#include \"statssocket_lazy.h\"\n\n// The tests here are just for the case when libstatssocket.so cannot be loaded by\n// libstatssocket_lazy.\nclass LibstatssocketLazyTest : public ::testing::Test {\n  protected:\n    virtual void SetUp() {\n        ::testing::Test::SetUp();\n        PreventLibstatssocketLazyLoadingForTests();\n    }\n};\n\nstatic const char* kLoadFailed = \"Failed to load libstatssocket.so\";\n\nTEST_F(LibstatssocketLazyTest, NoLibstatssocketForStatsEvent) {\n    AStatsEvent* event = NULL;\n    EXPECT_DEATH(AStatsEvent_obtain(), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_build(event), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_write(event), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_release(event), kLoadFailed);\n\n    EXPECT_DEATH(AStatsEvent_setAtomId(event, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeInt32(event, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeInt64(event, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeFloat(event, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeBool(event, false), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeByteArray(event, NULL, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeString(event, NULL), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeStringArray(event, NULL, 0), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_writeAttributionChain(event, NULL, NULL, 0), kLoadFailed);\n\n    EXPECT_DEATH(AStatsEvent_addBoolAnnotation(event, 0, false), kLoadFailed);\n    EXPECT_DEATH(AStatsEvent_addInt32Annotation(event, 0, 0), kLoadFailed);\n}\n\nTEST_F(LibstatssocketLazyTest, NoLibstatssocketForStatsSocket) {\n    EXPECT_DEATH(AStatsSocket_close(), kLoadFailed);\n}\n\nTEST_F(LibstatssocketLazyTest, IsAvailableFalse) {\n    EXPECT_FALSE(android::statssocket::lazy::IsAvailable());\n}\n"
  },
  {
    "path": "libsuspend/Android.bp",
    "content": "// Copyright 2012 The Android Open Source Project\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libsuspend\",\n    vendor_available: true,\n    srcs: [\n        \"autosuspend.c\",\n        \"autosuspend_wakeup_count.cpp\",\n    ],\n    export_include_dirs: [\"include\"],\n    local_include_dirs: [\"include\"],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n        \"libcutils\",\n    ],\n    cflags: [\n        \"-Werror\",\n        // \"-DLOG_NDEBUG=0\",\n    ],\n}\n"
  },
  {
    "path": "libsuspend/autosuspend.c",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"libsuspend\"\n\n#include <stdbool.h>\n\n#include <log/log.h>\n\n#include <suspend/autosuspend.h>\n\n#include \"autosuspend_ops.h\"\n\nstatic struct autosuspend_ops* autosuspend_ops = NULL;\nstatic bool autosuspend_enabled;\n\nstatic int autosuspend_init(void) {\n    if (autosuspend_ops != NULL) {\n        return 0;\n    }\n\n    autosuspend_ops = autosuspend_wakeup_count_init();\n    if (autosuspend_ops == NULL) {\n        ALOGE(\"failed to initialize autosuspend\");\n        return -1;\n    }\n\n    ALOGV(\"autosuspend initialized\");\n    return 0;\n}\n\nint autosuspend_enable(void) {\n    int ret;\n\n    ret = autosuspend_init();\n    if (ret) {\n        return ret;\n    }\n\n    ALOGV(\"autosuspend_enable\");\n\n    if (autosuspend_enabled) {\n        return 0;\n    }\n\n    ret = autosuspend_ops->enable();\n    if (ret) {\n        return ret;\n    }\n\n    autosuspend_enabled = true;\n    return 0;\n}\n\nint autosuspend_disable(void) {\n    int ret;\n\n    ret = autosuspend_init();\n    if (ret) {\n        return ret;\n    }\n\n    ALOGV(\"autosuspend_disable\");\n\n    if (!autosuspend_enabled) {\n        return 0;\n    }\n\n    ret = autosuspend_ops->disable();\n    if (ret) {\n        return ret;\n    }\n\n    autosuspend_enabled = false;\n    return 0;\n}\n\nint autosuspend_force_suspend(int timeout_ms) {\n    int ret;\n\n    ret = autosuspend_init();\n    if (ret) {\n        return ret;\n    }\n\n    ALOGV(\"autosuspend_force_suspend\");\n\n    return autosuspend_ops->force_suspend(timeout_ms);\n}\n\nvoid autosuspend_set_wakeup_callback(void (*func)(bool success)) {\n    int ret;\n\n    ret = autosuspend_init();\n    if (ret) {\n        return;\n    }\n\n    ALOGV(\"set_wakeup_callback\");\n\n    autosuspend_ops->set_wakeup_callback(func);\n}\n"
  },
  {
    "path": "libsuspend/autosuspend_ops.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSUSPEND_AUTOSUSPEND_OPS_H_\n#define _LIBSUSPEND_AUTOSUSPEND_OPS_H_\n\nstruct autosuspend_ops {\n    int (*enable)(void);\n    int (*disable)(void);\n    int (*force_suspend)(int timeout_ms);\n    void (*set_wakeup_callback)(void (*func)(bool success));\n};\n\n__BEGIN_DECLS\nstruct autosuspend_ops *autosuspend_wakeup_count_init(void);\n__END_DECLS\n\n#endif\n"
  },
  {
    "path": "libsuspend/autosuspend_wakeup_count.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"libsuspend\"\n//#define LOG_NDEBUG 0\n\n#include <fcntl.h>\n#include <pthread.h>\n#include <semaphore.h>\n#include <stdbool.h>\n#include <stddef.h>\n#include <string.h>\n#include <sys/param.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n\n#include \"autosuspend_ops.h\"\n\n#define BASE_SLEEP_TIME 100000\n#define MAX_SLEEP_TIME 60000000\n\nstatic int state_fd = -1;\nstatic int wakeup_count_fd;\n\nusing android::base::ReadFdToString;\nusing android::base::Trim;\nusing android::base::WriteStringToFd;\n\nstatic pthread_t suspend_thread;\nstatic sem_t suspend_lockout;\nstatic constexpr char sleep_state[] = \"mem\";\nstatic void (*wakeup_func)(bool success) = NULL;\nstatic int sleep_time = BASE_SLEEP_TIME;\nstatic constexpr char sys_power_state[] = \"/sys/power/state\";\nstatic constexpr char sys_power_wakeup_count[] = \"/sys/power/wakeup_count\";\nstatic bool autosuspend_is_init = false;\n\nstatic void update_sleep_time(bool success) {\n    if (success) {\n        sleep_time = BASE_SLEEP_TIME;\n        return;\n    }\n    // double sleep time after each failure up to one minute\n    sleep_time = MIN(sleep_time * 2, MAX_SLEEP_TIME);\n}\n\nstatic void* suspend_thread_func(void* arg __attribute__((unused))) {\n    bool success = true;\n\n    while (true) {\n        update_sleep_time(success);\n        usleep(sleep_time);\n        success = false;\n        LOG(VERBOSE) << \"read wakeup_count\";\n        lseek(wakeup_count_fd, 0, SEEK_SET);\n        std::string wakeup_count;\n        if (!ReadFdToString(wakeup_count_fd, &wakeup_count)) {\n            PLOG(ERROR) << \"error reading from \" << sys_power_wakeup_count;\n            continue;\n        }\n\n        wakeup_count = Trim(wakeup_count);\n        if (wakeup_count.empty()) {\n            LOG(ERROR) << \"empty wakeup count\";\n            continue;\n        }\n\n        LOG(VERBOSE) << \"wait\";\n        int ret = sem_wait(&suspend_lockout);\n        if (ret < 0) {\n            PLOG(ERROR) << \"error waiting on semaphore\";\n            continue;\n        }\n\n        LOG(VERBOSE) << \"write \" << wakeup_count << \" to wakeup_count\";\n        if (WriteStringToFd(wakeup_count, wakeup_count_fd)) {\n            LOG(VERBOSE) << \"write \" << sleep_state << \" to \" << sys_power_state;\n            success = WriteStringToFd(sleep_state, state_fd);\n\n            void (*func)(bool success) = wakeup_func;\n            if (func != NULL) {\n                (*func)(success);\n            }\n        } else {\n            PLOG(ERROR) << \"error writing to \" << sys_power_wakeup_count;\n        }\n\n        LOG(VERBOSE) << \"release sem\";\n        ret = sem_post(&suspend_lockout);\n        if (ret < 0) {\n            PLOG(ERROR) << \"error releasing semaphore\";\n        }\n    }\n    return NULL;\n}\n\nstatic int init_state_fd(void) {\n    if (state_fd >= 0) {\n        return 0;\n    }\n\n    int fd = TEMP_FAILURE_RETRY(open(sys_power_state, O_CLOEXEC | O_RDWR));\n    if (fd < 0) {\n        PLOG(ERROR) << \"error opening \" << sys_power_state;\n        return -1;\n    }\n\n    state_fd = fd;\n    LOG(INFO) << \"init_state_fd success\";\n    return 0;\n}\n\nstatic int autosuspend_init(void) {\n    if (autosuspend_is_init) {\n        return 0;\n    }\n\n    int ret = init_state_fd();\n    if (ret < 0) {\n        return -1;\n    }\n\n    wakeup_count_fd = TEMP_FAILURE_RETRY(open(sys_power_wakeup_count, O_CLOEXEC | O_RDWR));\n    if (wakeup_count_fd < 0) {\n        PLOG(ERROR) << \"error opening \" << sys_power_wakeup_count;\n        goto err_open_wakeup_count;\n    }\n\n    ret = sem_init(&suspend_lockout, 0, 0);\n    if (ret < 0) {\n        PLOG(ERROR) << \"error creating suspend_lockout semaphore\";\n        goto err_sem_init;\n    }\n\n    ret = pthread_create(&suspend_thread, NULL, suspend_thread_func, NULL);\n    if (ret) {\n        LOG(ERROR) << \"error creating thread: \" << strerror(ret);\n        goto err_pthread_create;\n    }\n\n    LOG(VERBOSE) << \"autosuspend_init success\";\n    autosuspend_is_init = true;\n    return 0;\n\nerr_pthread_create:\n    sem_destroy(&suspend_lockout);\nerr_sem_init:\n    close(wakeup_count_fd);\nerr_open_wakeup_count:\n    return -1;\n}\n\nstatic int autosuspend_wakeup_count_enable(void) {\n    LOG(VERBOSE) << \"autosuspend_wakeup_count_enable\";\n\n    int ret = autosuspend_init();\n    if (ret < 0) {\n        LOG(ERROR) << \"autosuspend_init failed\";\n        return ret;\n    }\n\n    ret = sem_post(&suspend_lockout);\n    if (ret < 0) {\n        PLOG(ERROR) << \"error changing semaphore\";\n    }\n\n    LOG(VERBOSE) << \"autosuspend_wakeup_count_enable done\";\n\n    return ret;\n}\n\nstatic int autosuspend_wakeup_count_disable(void) {\n    LOG(VERBOSE) << \"autosuspend_wakeup_count_disable\";\n\n    if (!autosuspend_is_init) {\n        return 0;  // always successful if no thread is running yet\n    }\n\n    int ret = sem_wait(&suspend_lockout);\n\n    if (ret < 0) {\n        PLOG(ERROR) << \"error changing semaphore\";\n    }\n\n    LOG(VERBOSE) << \"autosuspend_wakeup_count_disable done\";\n\n    return ret;\n}\n\nstatic int force_suspend(int timeout_ms) {\n    LOG(VERBOSE) << \"force_suspend called with timeout: \" << timeout_ms;\n\n    int ret = init_state_fd();\n    if (ret < 0) {\n        return ret;\n    }\n\n    return WriteStringToFd(sleep_state, state_fd) ? 0 : -1;\n}\n\nstatic void autosuspend_set_wakeup_callback(void (*func)(bool success)) {\n    if (wakeup_func != NULL) {\n        LOG(ERROR) << \"duplicate wakeup callback applied, keeping original\";\n        return;\n    }\n    wakeup_func = func;\n}\n\nstruct autosuspend_ops autosuspend_wakeup_count_ops = {\n    .enable = autosuspend_wakeup_count_enable,\n    .disable = autosuspend_wakeup_count_disable,\n    .force_suspend = force_suspend,\n    .set_wakeup_callback = autosuspend_set_wakeup_callback,\n};\n\nstruct autosuspend_ops* autosuspend_wakeup_count_init(void) {\n    return &autosuspend_wakeup_count_ops;\n}\n"
  },
  {
    "path": "libsuspend/include/suspend/autosuspend.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBSUSPEND_AUTOSUSPEND_H_\n#define _LIBSUSPEND_AUTOSUSPEND_H_\n\n#include <sys/cdefs.h>\n#include <stdbool.h>\n\n__BEGIN_DECLS\n\n/*\n * autosuspend_enable\n *\n * Turn on autosuspend in the kernel, allowing it to enter suspend if no\n * wakelocks/wakeup_sources are held.\n *\n *\n *\n * Returns 0 on success, -1 if autosuspend was not enabled.\n */\nint autosuspend_enable(void);\n\n/*\n * autosuspend_disable\n *\n * Turn off autosuspend in the kernel, preventing suspend and synchronizing\n * with any in-progress resume.\n *\n * Returns 0 on success, -1 if autosuspend was not disabled.\n */\nint autosuspend_disable(void);\n\n/*\n * force_suspend\n *\n * Forces suspend to happen.  timeout_ms is used to give system a chance to suspend gracefully.\n * When timeout expires, suspend will be forced via mem --> /sys/power/state.  timeout_ms of 0\n * will force suspend immediately.\n *\n * Returns 0 if system suspended, -1 if suspend did not occur.\n */\nint autosuspend_force_suspend(int timeout_ms);\n\n/*\n * set_wakeup_callback\n *\n * Set a function to be called each time the device returns from suspend.\n * success is true if the suspend was sucessful and false if the suspend\n * aborted due to some reason.\n */\nvoid autosuspend_set_wakeup_callback(void (*func)(bool success));\n\n__END_DECLS\n\n#endif\n"
  },
  {
    "path": "libsync/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"system_core_libsync_license\"],\n}\n\n// Added automatically by a large-scale-change\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_libsync_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\nndk_headers {\n    name: \"libsync_headers\",\n    from: \"include/ndk\",\n    to: \"android\",\n    srcs: [\"include/ndk/sync.h\"],\n    license: \"NOTICE\",\n}\n\nndk_library {\n    name: \"libsync\",\n    symbol_file: \"libsync.map.txt\",\n    first_version: \"26\",\n}\n\ncc_defaults {\n    name: \"libsync_defaults\",\n    srcs: [\"sync.c\"],\n    local_include_dirs: [\"include\"],\n    export_include_dirs: [\"include\"],\n    cflags: [\"-Werror\"],\n}\n\ncc_library {\n    name: \"libsync\",\n    recovery_available: true,\n    native_bridge_supported: true,\n    defaults: [\"libsync_defaults\"],\n    llndk: {\n        symbol_file: \"libsync.map.txt\",\n    },\n    stubs: {\n        symbol_file: \"libsync.map.txt\",\n        versions: [\n            \"26\",\n        ],\n    },\n}\n\ncc_test {\n    name: \"sync-unit-tests\",\n    shared_libs: [\"libsync\"],\n    srcs: [\"tests/sync_test.cpp\"],\n    cflags: [\n        \"-g\",\n        \"-Wall\",\n        \"-Werror\",\n        \"-Wno-missing-field-initializers\",\n        \"-Wno-sign-compare\",\n    ],\n}\n"
  },
  {
    "path": "libsync/NOTICE",
    "content": "\n   Copyright (c) 2012-2017, The Android Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "libsync/OWNERS",
    "content": "chrisforbes@google.com\njessehall@google.com\n"
  },
  {
    "path": "libsync/include/android/sync.h",
    "content": "/*\n *  sync.h\n *\n *   Copyright 2012 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef __SYS_CORE_SYNC_H\n#define __SYS_CORE_SYNC_H\n\n/* This file contains the legacy sync interface used by Android platform and\n * device code. The direct contents will be removed over time as code\n * transitions to using the updated interface in ndk/sync.h. When this file is\n * empty other than the ndk/sync.h include, that file will be renamed to\n * replace this one.\n *\n * New code should continue to include this file (#include <android/sync.h>)\n * instead of ndk/sync.h so the eventual rename is seamless, but should only\n * use the things declared in ndk/sync.h.\n *\n * This file used to be called sync/sync.h, but we renamed to that both the\n * platform and NDK call it android/sync.h. A symlink from the old name to this\n * one exists temporarily to avoid having to change all sync clients\n * simultaneously. It will be removed when they've been updated, and probably\n * after this change has been delivered to AOSP so that integrations don't\n * break builds.\n */\n\n#include \"../ndk/sync.h\"\n\n__BEGIN_DECLS\n\n/* timeout in msecs */\nint sync_wait(int fd, int timeout);\n\n__END_DECLS\n\n#endif /* __SYS_CORE_SYNC_H */\n"
  },
  {
    "path": "libsync/include/ndk/sync.h",
    "content": "/*\n *  Copyright 2017 The Android Open Source Project\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n/**\n * @addtogroup Sync\n * @{\n */\n\n/**\n * @file sync.h\n */\n\n#ifndef ANDROID_SYNC_H\n#define ANDROID_SYNC_H\n\n#include <stdint.h>\n#include <sys/cdefs.h>\n\n#include <linux/sync_file.h>\n\n__BEGIN_DECLS\n\n/* Fences indicate the status of an asynchronous task. They are initially\n * in unsignaled state (0), and make a one-time transition to either signaled\n * (1) or error (< 0) state. A sync file is a collection of one or more fences;\n * the sync file's status is error if any of its fences are in error state,\n * signaled if all of the child fences are signaled, or unsignaled otherwise.\n *\n * Sync files are created by various device APIs in response to submitting\n * tasks to the device. Standard file descriptor lifetime syscalls like dup()\n * and close() are used to manage sync file lifetime.\n *\n * The poll(), ppoll(), or select() syscalls can be used to wait for the sync\n * file to change status, or (with a timeout of zero) to check its status.\n *\n * The functions below provide a few additional sync-specific operations.\n */\n\n/**\n * Merge two sync files.\n *\n * This produces a new sync file with the given name which has the union of the\n * two original sync file's fences; redundant fences may be removed.\n *\n * If one of the input sync files is signaled or invalid, then this function\n * may behave like dup(): the new file descriptor refers to the valid/unsignaled\n * sync file with its original name, rather than a new sync file.\n *\n * The original fences remain valid, and the caller is responsible for closing\n * them.\n *\n * Available since API level 26.\n */\nint32_t sync_merge(const char* name, int32_t fd1, int32_t fd2) __INTRODUCED_IN(26);\n\n/**\n * Retrieve detailed information about a sync file and its fences.\n *\n * The returned sync_file_info must be freed by calling sync_file_info_free().\n *\n * Available since API level 26.\n */\nstruct sync_file_info* sync_file_info(int32_t fd) __INTRODUCED_IN(26);\n\n/**\n * Get the array of fence infos from the sync file's info.\n *\n * The returned array is owned by the parent sync file info, and has\n * info->num_fences entries.\n *\n * Available since API level 26.\n */\nstatic inline struct sync_fence_info* sync_get_fence_info(const struct sync_file_info* info) {\n// This header should compile in C, but some C++ projects enable\n// warnings-as-error for C-style casts.\n#pragma GCC diagnostic push\n#pragma GCC diagnostic ignored \"-Wold-style-cast\"\n    return (struct sync_fence_info *)(uintptr_t)(info->sync_fence_info);\n#pragma GCC diagnostic pop\n}\n\n/**\n * Free a struct sync_file_info structure\n *\n * Available since API level 26.\n */\nvoid sync_file_info_free(struct sync_file_info* info) __INTRODUCED_IN(26);\n\n__END_DECLS\n\n#endif /* ANDROID_SYNC_H */\n\n/** @} */\n"
  },
  {
    "path": "libsync/libsync.map.txt",
    "content": "#\n# Copyright 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n#  Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nLIBSYNC {\n  global:\n    sync_merge; # introduced=26\n    sync_file_info; # introduced=26\n    sync_file_info_free; # introduced=26\n    sync_wait; # llndk systemapi\n    sync_fence_info; # llndk\n    sync_pt_info; # llndk\n    sync_fence_info_free; # llndk\n  local:\n    *;\n};\n"
  },
  {
    "path": "libsync/sw_sync.h",
    "content": "/*\n *  sw_sync.h\n *\n *   Copyright 2013 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#ifndef __SYS_CORE_SW_SYNC_H\n#define __SYS_CORE_SW_SYNC_H\n\n__BEGIN_DECLS\n\n/*\n * sw_sync is mainly intended for testing and should not be compiled into\n * production kernels\n */\n\nint sw_sync_timeline_create(void);\nint sw_sync_timeline_inc(int fd, unsigned count);\nint sw_sync_fence_create(int fd, const char *name, unsigned value);\n\n__END_DECLS\n\n#endif /* __SYS_CORE_SW_SYNC_H */\n"
  },
  {
    "path": "libsync/sync.c",
    "content": "/*\n *  sync.c\n *\n *   Copyright 2012 Google, Inc\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\");\n *  you may not use this file except in compliance with the License.\n *  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n *  Unless required by applicable law or agreed to in writing, software\n *  distributed under the License is distributed on an \"AS IS\" BASIS,\n *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n *  See the License for the specific language governing permissions and\n *  limitations under the License.\n */\n\n#include <errno.h>\n#include <fcntl.h>\n#include <malloc.h>\n#include <poll.h>\n#include <stdatomic.h>\n#include <stdint.h>\n#include <string.h>\n\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n\n#include <android/sync.h>\n\n/* Prototypes for deprecated functions that used to be declared in the legacy\n * android/sync.h. They've been moved here to make sure new code does not use\n * them, but the functions are still defined to avoid breaking existing\n * binaries. Eventually they can be removed altogether.\n */\nstruct sync_fence_info_data {\n    uint32_t len;\n    char name[32];\n    int32_t status;\n    uint8_t pt_info[0];\n};\nstruct sync_pt_info {\n    uint32_t len;\n    char obj_name[32];\n    char driver_name[32];\n    int32_t status;\n    uint64_t timestamp_ns;\n    uint8_t driver_data[0];\n};\nstruct sync_fence_info_data* sync_fence_info(int fd);\nstruct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);\nvoid sync_fence_info_free(struct sync_fence_info_data* info);\n\n/* Legacy Sync API */\n\nstruct sync_legacy_merge_data {\n int32_t fd2;\n char name[32];\n int32_t fence;\n};\n\n/**\n * DOC: SYNC_IOC_MERGE - merge two fences\n *\n * Takes a struct sync_merge_data.  Creates a new fence containing copies of\n * the sync_pts in both the calling fd and sync_merge_data.fd2.  Returns the\n * new fence's fd in sync_merge_data.fence\n *\n * This is the legacy version of the Sync API before the de-stage that happened\n * on Linux kernel 4.7.\n */\n#define SYNC_IOC_LEGACY_MERGE   _IOWR(SYNC_IOC_MAGIC, 1, \\\n    struct sync_legacy_merge_data)\n\n/**\n * DOC: SYNC_IOC_LEGACY_FENCE_INFO - get detailed information on a fence\n *\n * Takes a struct sync_fence_info_data with extra space allocated for pt_info.\n * Caller should write the size of the buffer into len.  On return, len is\n * updated to reflect the total size of the sync_fence_info_data including\n * pt_info.\n *\n * pt_info is a buffer containing sync_pt_infos for every sync_pt in the fence.\n * To iterate over the sync_pt_infos, use the sync_pt_info.len field.\n *\n * This is the legacy version of the Sync API before the de-stage that happened\n * on Linux kernel 4.7.\n */\n#define SYNC_IOC_LEGACY_FENCE_INFO  _IOWR(SYNC_IOC_MAGIC, 2,\\\n    struct sync_fence_info_data)\n\n/* SW Sync API */\n\nstruct sw_sync_create_fence_data {\n  __u32 value;\n  char name[32];\n  __s32 fence;\n};\n\n#define SW_SYNC_IOC_MAGIC 'W'\n#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0, struct sw_sync_create_fence_data)\n#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)\n\n// ---------------------------------------------------------------------------\n// Support for caching the sync uapi version.\n//\n// This library supports both legacy (android/staging) uapi and modern\n// (mainline) sync uapi. Library calls first try one uapi, and if that fails,\n// try the other. Since any given kernel only supports one uapi version, after\n// the first successful syscall we know what the kernel supports and can skip\n// trying the other.\n\nenum uapi_version {\n    UAPI_UNKNOWN,\n    UAPI_MODERN,\n    UAPI_LEGACY\n};\nstatic atomic_int g_uapi_version = UAPI_UNKNOWN;\n\n// ---------------------------------------------------------------------------\n\nint sync_wait(int fd, int timeout)\n{\n    struct pollfd fds;\n    int ret;\n\n    if (fd < 0) {\n        errno = EINVAL;\n        return -1;\n    }\n\n    fds.fd = fd;\n    fds.events = POLLIN;\n\n    do {\n        ret = poll(&fds, 1, timeout);\n        if (ret > 0) {\n            if (fds.revents & (POLLERR | POLLNVAL)) {\n                errno = EINVAL;\n                return -1;\n            }\n            return 0;\n        } else if (ret == 0) {\n            errno = ETIME;\n            return -1;\n        }\n    } while (ret == -1 && (errno == EINTR || errno == EAGAIN));\n\n    return ret;\n}\n\nstatic int legacy_sync_merge(const char *name, int fd1, int fd2)\n{\n    struct sync_legacy_merge_data data;\n    int ret;\n\n    data.fd2 = fd2;\n    strlcpy(data.name, name, sizeof(data.name));\n    ret = ioctl(fd1, SYNC_IOC_LEGACY_MERGE, &data);\n    if (ret < 0)\n        return ret;\n    return data.fence;\n}\n\nstatic int modern_sync_merge(const char *name, int fd1, int fd2)\n{\n    struct sync_merge_data data;\n    int ret;\n\n    data.fd2 = fd2;\n    strlcpy(data.name, name, sizeof(data.name));\n    data.flags = 0;\n    data.pad = 0;\n\n    ret = ioctl(fd1, SYNC_IOC_MERGE, &data);\n    if (ret < 0)\n        return ret;\n    return data.fence;\n}\n\nint sync_merge(const char *name, int fd1, int fd2)\n{\n    int uapi;\n    int ret;\n\n    uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);\n\n    if (uapi == UAPI_MODERN || uapi == UAPI_UNKNOWN) {\n        ret = modern_sync_merge(name, fd1, fd2);\n        if (ret >= 0 || errno != ENOTTY) {\n            if (ret >= 0 && uapi == UAPI_UNKNOWN) {\n                atomic_store_explicit(&g_uapi_version, UAPI_MODERN,\n                                      memory_order_release);\n            }\n            return ret;\n        }\n    }\n\n    ret = legacy_sync_merge(name, fd1, fd2);\n    if (ret >= 0 && uapi == UAPI_UNKNOWN) {\n        atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,\n                              memory_order_release);\n    }\n    return ret;\n}\n\nstatic struct sync_fence_info_data *legacy_sync_fence_info(int fd)\n{\n    struct sync_fence_info_data *legacy_info;\n    struct sync_pt_info *legacy_pt_info;\n    int err;\n\n    legacy_info = malloc(4096);\n    if (legacy_info == NULL)\n        return NULL;\n\n    legacy_info->len = 4096;\n    err = ioctl(fd, SYNC_IOC_LEGACY_FENCE_INFO, legacy_info);\n    if (err < 0) {\n        free(legacy_info);\n        return NULL;\n    }\n    return legacy_info;\n}\n\nstatic struct sync_file_info *modern_sync_file_info(int fd)\n{\n    struct sync_file_info local_info;\n    struct sync_file_info *info;\n    int err;\n\n    memset(&local_info, 0, sizeof(local_info));\n    err = ioctl(fd, SYNC_IOC_FILE_INFO, &local_info);\n    if (err < 0)\n        return NULL;\n\n    info = calloc(1, sizeof(struct sync_file_info) +\n                  local_info.num_fences * sizeof(struct sync_fence_info));\n    if (!info)\n        return NULL;\n\n    info->num_fences = local_info.num_fences;\n    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);\n\n    err = ioctl(fd, SYNC_IOC_FILE_INFO, info);\n    if (err < 0) {\n        free(info);\n        return NULL;\n    }\n\n    return info;\n}\n\nstatic struct sync_fence_info_data *sync_file_info_to_legacy_fence_info(\n    const struct sync_file_info *info)\n{\n    struct sync_fence_info_data *legacy_info;\n    struct sync_pt_info *legacy_pt_info;\n    const struct sync_fence_info *fence_info = sync_get_fence_info(info);\n    const uint32_t num_fences = info->num_fences;\n\n    legacy_info = malloc(4096);\n    if (legacy_info == NULL)\n        return NULL;\n    legacy_info->len = sizeof(*legacy_info) +\n                        num_fences * sizeof(struct sync_pt_info);\n    strlcpy(legacy_info->name, info->name, sizeof(legacy_info->name));\n    legacy_info->status = info->status;\n\n    legacy_pt_info = (struct sync_pt_info *)legacy_info->pt_info;\n    for (uint32_t i = 0; i < num_fences; i++) {\n        legacy_pt_info[i].len = sizeof(*legacy_pt_info);\n        strlcpy(legacy_pt_info[i].obj_name, fence_info[i].obj_name,\n                sizeof(legacy_pt_info->obj_name));\n        strlcpy(legacy_pt_info[i].driver_name, fence_info[i].driver_name,\n                sizeof(legacy_pt_info->driver_name));\n        legacy_pt_info[i].status = fence_info[i].status;\n        legacy_pt_info[i].timestamp_ns = fence_info[i].timestamp_ns;\n    }\n\n    return legacy_info;\n}\n\nstatic struct sync_file_info* legacy_fence_info_to_sync_file_info(\n                                    struct sync_fence_info_data *legacy_info)\n{\n    struct sync_file_info *info;\n    struct sync_pt_info *pt;\n    struct sync_fence_info *fence;\n    size_t num_fences;\n    int err;\n\n    pt = NULL;\n    num_fences = 0;\n    while ((pt = sync_pt_info(legacy_info, pt)) != NULL)\n        num_fences++;\n\n    info = calloc(1, sizeof(struct sync_file_info) +\n                     num_fences * sizeof(struct sync_fence_info));\n    if (!info) {\n        return NULL;\n    }\n    info->sync_fence_info = (__u64)(uintptr_t)(info + 1);\n\n    strlcpy(info->name, legacy_info->name, sizeof(info->name));\n    info->status = legacy_info->status;\n    info->num_fences = num_fences;\n\n    pt = NULL;\n    fence = sync_get_fence_info(info);\n    while ((pt = sync_pt_info(legacy_info, pt)) != NULL) {\n        strlcpy(fence->obj_name, pt->obj_name, sizeof(fence->obj_name));\n        strlcpy(fence->driver_name, pt->driver_name,\n                sizeof(fence->driver_name));\n        fence->status = pt->status;\n        fence->timestamp_ns = pt->timestamp_ns;\n        fence++;\n    }\n\n    return info;\n}\n\nstruct sync_fence_info_data *sync_fence_info(int fd)\n{\n    struct sync_fence_info_data *legacy_info;\n    int uapi;\n\n    uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);\n\n    if (uapi == UAPI_LEGACY || uapi == UAPI_UNKNOWN) {\n        legacy_info = legacy_sync_fence_info(fd);\n        if (legacy_info || errno != ENOTTY) {\n            if (legacy_info && uapi == UAPI_UNKNOWN) {\n                atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,\n                                      memory_order_release);\n            }\n            return legacy_info;\n        }\n    }\n\n    struct sync_file_info* file_info;\n    file_info = modern_sync_file_info(fd);\n    if (!file_info)\n        return NULL;\n    if (uapi == UAPI_UNKNOWN) {\n        atomic_store_explicit(&g_uapi_version, UAPI_MODERN,\n                              memory_order_release);\n    }\n    legacy_info = sync_file_info_to_legacy_fence_info(file_info);\n    sync_file_info_free(file_info);\n    return legacy_info;\n}\n\nstruct sync_file_info* sync_file_info(int32_t fd)\n{\n    struct sync_file_info *info;\n    int uapi;\n\n    uapi = atomic_load_explicit(&g_uapi_version, memory_order_acquire);\n\n    if (uapi == UAPI_MODERN || uapi == UAPI_UNKNOWN) {\n        info = modern_sync_file_info(fd);\n        if (info || errno != ENOTTY) {\n            if (info && uapi == UAPI_UNKNOWN) {\n                atomic_store_explicit(&g_uapi_version, UAPI_MODERN,\n                                      memory_order_release);\n            }\n            return info;\n        }\n    }\n\n    struct sync_fence_info_data *legacy_info;\n    legacy_info = legacy_sync_fence_info(fd);\n    if (!legacy_info)\n        return NULL;\n    if (uapi == UAPI_UNKNOWN) {\n        atomic_store_explicit(&g_uapi_version, UAPI_LEGACY,\n                              memory_order_release);\n    }\n    info = legacy_fence_info_to_sync_file_info(legacy_info);\n    sync_fence_info_free(legacy_info);\n    return info;\n}\n\nstruct sync_pt_info *sync_pt_info(struct sync_fence_info_data *info,\n                                  struct sync_pt_info *itr)\n{\n    if (itr == NULL)\n        itr = (struct sync_pt_info *) info->pt_info;\n    else\n        itr = (struct sync_pt_info *) ((__u8 *)itr + itr->len);\n\n    if ((__u8 *)itr - (__u8 *)info >= (int)info->len)\n        return NULL;\n\n    return itr;\n}\n\nvoid sync_fence_info_free(struct sync_fence_info_data *info)\n{\n    free(info);\n}\n\nvoid sync_file_info_free(struct sync_file_info *info)\n{\n    free(info);\n}\n\n\nint sw_sync_timeline_create(void)\n{\n    int ret;\n\n    ret = open(\"/sys/kernel/debug/sync/sw_sync\", O_RDWR);\n    if (ret < 0)\n        ret = open(\"/dev/sw_sync\", O_RDWR);\n\n    return ret;\n}\n\nint sw_sync_timeline_inc(int fd, unsigned count)\n{\n    __u32 arg = count;\n\n    return ioctl(fd, SW_SYNC_IOC_INC, &arg);\n}\n\nint sw_sync_fence_create(int fd, const char *name, unsigned value)\n{\n    struct sw_sync_create_fence_data data;\n    int err;\n\n    data.value = value;\n    strlcpy(data.name, name, sizeof(data.name));\n\n    err = ioctl(fd, SW_SYNC_IOC_CREATE_FENCE, &data);\n    if (err < 0)\n        return err;\n\n    return data.fence;\n}\n"
  },
  {
    "path": "libsync/tests/sync_test.cpp",
    "content": "#include <gtest/gtest.h>\n#include <android/sync.h>\n#include <sw_sync.h>\n#include <fcntl.h>\n#include <vector>\n#include <string>\n#include <cassert>\n#include <iostream>\n#include <unistd.h>\n#include <thread>\n#include <poll.h>\n#include <mutex>\n#include <algorithm>\n#include <tuple>\n#include <random>\n#include <unordered_map>\n\n/* These deprecated declarations were in the legacy android/sync.h. They've been removed to\n * encourage code to move to the modern equivalents. But they are still implemented in libsync.so\n * to avoid breaking existing binaries; as long as that's true we should keep testing them here.\n * That means making local copies of the declarations.\n */\nextern \"C\" {\n\nstruct sync_fence_info_data {\n    uint32_t len;\n    char name[32];\n    int32_t status;\n    uint8_t pt_info[0];\n};\n\nstruct sync_pt_info {\n    uint32_t len;\n    char obj_name[32];\n    char driver_name[32];\n    int32_t status;\n    uint64_t timestamp_ns;\n    uint8_t driver_data[0];\n};\n\nstruct sync_fence_info_data* sync_fence_info(int fd);\nstruct sync_pt_info* sync_pt_info(struct sync_fence_info_data* info, struct sync_pt_info* itr);\nvoid sync_fence_info_free(struct sync_fence_info_data* info);\n\n}  // extern \"C\"\n\n// TODO: better stress tests?\n// Handle more than 64 fd's simultaneously, i.e. fix sync_fence_info's 4k limit.\n// Handle wraparound in timelines like nvidia.\n\nusing namespace std;\n\nnamespace {\n\n// C++ wrapper class for sync timeline.\nclass SyncTimeline {\n    int m_fd = -1;\n    bool m_fdInitialized = false;\npublic:\n    SyncTimeline(const SyncTimeline &) = delete;\n    SyncTimeline& operator=(SyncTimeline&) = delete;\n    SyncTimeline() noexcept {\n        int fd = sw_sync_timeline_create();\n        if (fd == -1)\n            return;\n        m_fdInitialized = true;\n        m_fd = fd;\n    }\n    void destroy() {\n        if (m_fdInitialized) {\n            close(m_fd);\n            m_fd = -1;\n            m_fdInitialized = false;\n        }\n    }\n    ~SyncTimeline() {\n        destroy();\n    }\n    bool isValid() const {\n        if (m_fdInitialized) {\n            int status = fcntl(m_fd, F_GETFD, 0);\n            if (status >= 0)\n                return true;\n            else\n                return false;\n        }\n        else {\n            return false;\n        }\n    }\n    int getFd() const {\n        return m_fd;\n    }\n    int inc(int val = 1) {\n        return sw_sync_timeline_inc(m_fd, val);\n    }\n};\n\nstruct SyncPointInfo {\n    std::string driverName;\n    std::string objectName;\n    uint64_t timeStampNs;\n    int status; // 1 sig, 0 active, neg is err\n};\n\n// Wrapper class for sync fence.\nclass SyncFence {\n    int m_fd = -1;\n    bool m_fdInitialized = false;\n    static int s_fenceCount;\n\n    void setFd(int fd) {\n        m_fd = fd;\n        m_fdInitialized = true;\n    }\n    void clearFd() {\n        m_fd = -1;\n        m_fdInitialized = false;\n    }\npublic:\n    bool isValid() const {\n        if (m_fdInitialized) {\n            int status = fcntl(m_fd, F_GETFD, 0);\n            if (status >= 0)\n                return true;\n            else\n                return false;\n        }\n        else {\n            return false;\n        }\n    }\n    SyncFence& operator=(SyncFence &&rhs) noexcept {\n        destroy();\n        if (rhs.isValid()) {\n            setFd(rhs.getFd());\n            rhs.clearFd();\n        }\n        return *this;\n    }\n    SyncFence(SyncFence &&fence) noexcept {\n        if (fence.isValid()) {\n            setFd(fence.getFd());\n            fence.clearFd();\n        }\n    }\n    SyncFence(const SyncFence &fence) noexcept {\n        // This is ok, as sync fences are immutable after construction, so a dup\n        // is basically the same thing as a copy.\n        if (fence.isValid()) {\n            int fd = dup(fence.getFd());\n            if (fd == -1)\n                return;\n            setFd(fd);\n        }\n    }\n    SyncFence(const SyncTimeline &timeline,\n              int value,\n              const char *name = nullptr) noexcept {\n        std::string autoName = \"allocFence\";\n        autoName += s_fenceCount;\n        s_fenceCount++;\n        int fd = sw_sync_fence_create(timeline.getFd(), name ? name : autoName.c_str(), value);\n        if (fd == -1)\n            return;\n        setFd(fd);\n    }\n    SyncFence(const SyncFence &a, const SyncFence &b, const char *name = nullptr) noexcept {\n        std::string autoName = \"mergeFence\";\n        autoName += s_fenceCount;\n        s_fenceCount++;\n        int fd = sync_merge(name ? name : autoName.c_str(), a.getFd(), b.getFd());\n        if (fd == -1)\n            return;\n        setFd(fd);\n    }\n    SyncFence(const vector<SyncFence> &sources) noexcept {\n        assert(sources.size());\n        SyncFence temp(*begin(sources));\n        for (auto itr = ++begin(sources); itr != end(sources); ++itr) {\n            temp = SyncFence(*itr, temp);\n        }\n        if (temp.isValid()) {\n            setFd(temp.getFd());\n            temp.clearFd();\n        }\n    }\n    void destroy() {\n        if (isValid()) {\n            close(m_fd);\n            clearFd();\n        }\n    }\n    ~SyncFence() {\n        destroy();\n    }\n    int getFd() const {\n        return m_fd;\n    }\n    int wait(int timeout = -1) {\n        return sync_wait(m_fd, timeout);\n    }\n    vector<SyncPointInfo> getInfo() const {\n        vector<SyncPointInfo> fenceInfo;\n        struct sync_file_info *info = sync_file_info(getFd());\n        if (!info) {\n            return fenceInfo;\n        }\n        const auto fences = sync_get_fence_info(info);\n        for (uint32_t i = 0; i < info->num_fences; i++) {\n            fenceInfo.push_back(SyncPointInfo{\n                fences[i].driver_name,\n                fences[i].obj_name,\n                fences[i].timestamp_ns,\n                fences[i].status});\n        }\n        sync_file_info_free(info);\n        return fenceInfo;\n    }\n    int getSize() const {\n        return getInfo().size();\n    }\n    int getSignaledCount() const {\n        return countWithStatus(1);\n    }\n    int getActiveCount() const {\n        return countWithStatus(0);\n    }\n    int getErrorCount() const {\n        return countWithStatus(-1);\n    }\nprivate:\n    int countWithStatus(int status) const {\n        int count = 0;\n        for (auto &info : getInfo()) {\n            if (info.status == status) {\n                count++;\n            }\n        }\n        return count;\n    }\n};\n\nstatic void CheckModernLegacyInfoMatch(const SyncFence& f) {\n    struct sync_file_info* modern = sync_file_info(f.getFd());\n    struct sync_fence_info_data* legacy = sync_fence_info(f.getFd());\n\n    ASSERT_TRUE(modern != NULL);\n    ASSERT_TRUE(legacy != NULL);\n\n    EXPECT_STREQ(modern->name, legacy->name);\n    EXPECT_EQ(modern->status, legacy->status);\n\n    uint32_t fenceIdx = 0;\n    struct sync_pt_info* pt = sync_pt_info(legacy, NULL);\n    const struct sync_fence_info* fences = sync_get_fence_info(modern);\n    while (fenceIdx < modern->num_fences && pt != NULL) {\n        EXPECT_STREQ(fences[fenceIdx].obj_name, pt->obj_name);\n        EXPECT_STREQ(fences[fenceIdx].driver_name, pt->driver_name);\n        EXPECT_EQ(fences[fenceIdx].status, pt->status);\n        EXPECT_EQ(fences[fenceIdx].timestamp_ns, pt->timestamp_ns);\n\n        fenceIdx++;\n        pt = sync_pt_info(legacy, pt);\n    }\n    EXPECT_EQ(fenceIdx, modern->num_fences);\n    EXPECT_EQ(NULL, pt);\n}\n\nint SyncFence::s_fenceCount = 0;\n\nTEST(AllocTest, Timeline) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n}\n\nTEST(AllocTest, Fence) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    SyncFence fence(timeline, 1);\n    ASSERT_TRUE(fence.isValid());\n    CheckModernLegacyInfoMatch(fence);\n}\n\nTEST(AllocTest, FenceNegative) {\n    int timeline = sw_sync_timeline_create();\n    ASSERT_GT(timeline, 0);\n\n    // bad fd.\n    ASSERT_LT(sw_sync_fence_create(-1, \"fence\", 1), 0);\n\n    // No name - segfaults in user space.\n    // Maybe we should be friendlier here?\n    /*\n    ASSERT_LT(sw_sync_fence_create(timeline, nullptr, 1), 0);\n    */\n    close(timeline);\n}\n\nTEST(FenceTest, OneTimelineWait) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    SyncFence fence(timeline, 5);\n    ASSERT_TRUE(fence.isValid());\n\n    // Wait on fence until timeout.\n    ASSERT_EQ(fence.wait(0), -1);\n    ASSERT_EQ(errno, ETIME);\n\n    // Advance timeline from 0 -> 1\n    ASSERT_EQ(timeline.inc(1), 0);\n\n    // Wait on fence until timeout.\n    ASSERT_EQ(fence.wait(0), -1);\n    ASSERT_EQ(errno, ETIME);\n\n    // Signal the fence.\n    ASSERT_EQ(timeline.inc(4), 0);\n\n    // Wait successfully.\n    ASSERT_EQ(fence.wait(0), 0);\n\n    // Go even futher, and confirm wait still succeeds.\n    ASSERT_EQ(timeline.inc(10), 0);\n    ASSERT_EQ(fence.wait(0), 0);\n}\n\nTEST(FenceTest, OneTimelinePoll) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    SyncFence fence(timeline, 100);\n    ASSERT_TRUE(fence.isValid());\n\n    fd_set set;\n    FD_ZERO(&set);\n    FD_SET(fence.getFd(), &set);\n\n    // Poll the fence, and wait till timeout.\n    timeval time = {0};\n    ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 0);\n\n    // Advance the timeline.\n    timeline.inc(100);\n    timeline.inc(100);\n\n    // Select should return that the fd is read for reading.\n    FD_ZERO(&set);\n    FD_SET(fence.getFd(), &set);\n\n    ASSERT_EQ(select(fence.getFd() + 1, &set, nullptr, nullptr, &time), 1);\n    ASSERT_TRUE(FD_ISSET(fence.getFd(), &set));\n}\n\nTEST(FenceTest, OneTimelineMerge) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    // create fence a,b,c and then merge them all into fence d.\n    SyncFence a(timeline, 1), b(timeline, 2), c(timeline, 3);\n    ASSERT_TRUE(a.isValid());\n    ASSERT_TRUE(b.isValid());\n    ASSERT_TRUE(c.isValid());\n\n    SyncFence d({a,b,c});\n    ASSERT_TRUE(d.isValid());\n\n    // confirm all fences have one active point (even d).\n    ASSERT_EQ(a.getActiveCount(), 1);\n    ASSERT_EQ(b.getActiveCount(), 1);\n    ASSERT_EQ(c.getActiveCount(), 1);\n    ASSERT_EQ(d.getActiveCount(), 1);\n\n    // confirm that d is not signaled until the max of a,b,c\n    timeline.inc(1);\n    ASSERT_EQ(a.getSignaledCount(), 1);\n    ASSERT_EQ(d.getActiveCount(), 1);\n    CheckModernLegacyInfoMatch(a);\n    CheckModernLegacyInfoMatch(d);\n\n    timeline.inc(1);\n    ASSERT_EQ(b.getSignaledCount(), 1);\n    ASSERT_EQ(d.getActiveCount(), 1);\n    CheckModernLegacyInfoMatch(b);\n    CheckModernLegacyInfoMatch(d);\n\n    timeline.inc(1);\n    ASSERT_EQ(c.getSignaledCount(), 1);\n    ASSERT_EQ(d.getActiveCount(), 0);\n    ASSERT_EQ(d.getSignaledCount(), 1);\n    CheckModernLegacyInfoMatch(c);\n    CheckModernLegacyInfoMatch(d);\n}\n\nTEST(FenceTest, MergeSameFence) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    SyncFence fence(timeline, 5);\n    ASSERT_TRUE(fence.isValid());\n\n    SyncFence selfMergeFence(fence, fence);\n    ASSERT_TRUE(selfMergeFence.isValid());\n\n    ASSERT_EQ(selfMergeFence.getSignaledCount(), 0);\n    CheckModernLegacyInfoMatch(selfMergeFence);\n\n    timeline.inc(5);\n    ASSERT_EQ(selfMergeFence.getSignaledCount(), 1);\n    CheckModernLegacyInfoMatch(selfMergeFence);\n}\n\nTEST(FenceTest, PollOnDestroyedTimeline) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    SyncFence fenceSig(timeline, 100);\n    SyncFence fenceKill(timeline, 200);\n\n    // Spawn a thread to wait on a fence when the timeline is killed.\n    thread waitThread{\n        [&]() {\n            ASSERT_EQ(timeline.inc(100), 0);\n\n            // Wait on the fd.\n            struct pollfd fds;\n            fds.fd = fenceKill.getFd();\n            fds.events = POLLIN | POLLERR;\n            ASSERT_EQ(poll(&fds, 1, 0), 0);\n        }\n    };\n\n    // Wait for the thread to spool up.\n    fenceSig.wait();\n\n    // Kill the timeline.\n    timeline.destroy();\n\n    // wait for the thread to clean up.\n    waitThread.join();\n}\n\nTEST(FenceTest, MultiTimelineWait) {\n    SyncTimeline timelineA, timelineB, timelineC;\n\n    SyncFence fenceA(timelineA, 5);\n    SyncFence fenceB(timelineB, 5);\n    SyncFence fenceC(timelineC, 5);\n\n    // Make a larger fence using 3 other fences from different timelines.\n    SyncFence mergedFence({fenceA, fenceB, fenceC});\n    ASSERT_TRUE(mergedFence.isValid());\n\n    // Confirm fence isn't signaled\n    ASSERT_EQ(mergedFence.getActiveCount(), 3);\n    ASSERT_EQ(mergedFence.wait(0), -1);\n    ASSERT_EQ(errno, ETIME);\n\n    timelineA.inc(5);\n    ASSERT_EQ(mergedFence.getActiveCount(), 2);\n    ASSERT_EQ(mergedFence.getSignaledCount(), 1);\n    CheckModernLegacyInfoMatch(mergedFence);\n\n    timelineB.inc(5);\n    ASSERT_EQ(mergedFence.getActiveCount(), 1);\n    ASSERT_EQ(mergedFence.getSignaledCount(), 2);\n    CheckModernLegacyInfoMatch(mergedFence);\n\n    timelineC.inc(5);\n    ASSERT_EQ(mergedFence.getActiveCount(), 0);\n    ASSERT_EQ(mergedFence.getSignaledCount(), 3);\n    CheckModernLegacyInfoMatch(mergedFence);\n\n    // confirm you can successfully wait.\n    ASSERT_EQ(mergedFence.wait(100), 0);\n}\n\nTEST(FenceTest, GetInfoActive) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    SyncFence fence(timeline, 1);\n    ASSERT_TRUE(fence.isValid());\n\n    vector<SyncPointInfo> info = fence.getInfo();\n    ASSERT_EQ(info.size(), 1);\n\n    ASSERT_FALSE(info[0].driverName.empty());\n    ASSERT_FALSE(info[0].objectName.empty());\n    ASSERT_EQ(info[0].timeStampNs, 0);\n    ASSERT_EQ(info[0].status, 0);\n}\n\nTEST(FenceTest, GetInfoSignaled) {\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    SyncFence fence(timeline, 1);\n    ASSERT_TRUE(fence.isValid());\n\n    ASSERT_EQ(timeline.inc(1), 0);\n    ASSERT_EQ(fence.wait(), 0);\n\n    vector<SyncPointInfo> info = fence.getInfo();\n    ASSERT_EQ(info.size(), 1);\n\n    ASSERT_FALSE(info[0].driverName.empty());\n    ASSERT_FALSE(info[0].objectName.empty());\n    ASSERT_GT(info[0].timeStampNs, 0);\n    ASSERT_EQ(info[0].status, 1);\n}\n\nTEST(StressTest, TwoThreadsSharedTimeline) {\n    const int iterations = 1 << 16;\n    int counter = 0;\n    SyncTimeline timeline;\n    ASSERT_TRUE(timeline.isValid());\n\n    // Use a single timeline to synchronize two threads\n    // hammmering on the same counter.\n    auto threadMain = [&](int threadId) {\n        for (int i = 0; i < iterations; i++) {\n            SyncFence fence(timeline, i * 2 + threadId);\n            ASSERT_TRUE(fence.isValid());\n\n            // Wait on the prior thread to complete.\n            ASSERT_EQ(fence.wait(), 0);\n\n            // Confirm the previous thread's writes are visible and then inc.\n            ASSERT_EQ(counter, i * 2 + threadId);\n            counter++;\n\n            // Kick off the other thread.\n            ASSERT_EQ(timeline.inc(), 0);\n        }\n    };\n\n    thread a{threadMain, 0};\n    thread b{threadMain, 1};\n    a.join();\n    b.join();\n\n    // make sure the threads did not trample on one another.\n    ASSERT_EQ(counter, iterations * 2);\n}\n\nclass ConsumerStressTest : public ::testing::TestWithParam<int> {};\n\nTEST_P(ConsumerStressTest, MultiProducerSingleConsumer) {\n    mutex lock;\n    int counter = 0;\n    int iterations = 1 << 12;\n\n    vector<SyncTimeline> producerTimelines(GetParam());\n    vector<thread> threads;\n    SyncTimeline consumerTimeline;\n\n    // Producer threads run this lambda.\n    auto threadMain = [&](int threadId) {\n        for (int i = 0; i < iterations; i++) {\n            SyncFence fence(consumerTimeline, i);\n            ASSERT_TRUE(fence.isValid());\n\n            // Wait for the consumer to finish. Use alternate\n            // means of waiting on the fence.\n            if ((iterations + threadId) % 8 != 0) {\n                ASSERT_EQ(fence.wait(), 0);\n            }\n            else {\n                while (fence.getSignaledCount() != 1) {\n                    ASSERT_EQ(fence.getErrorCount(), 0);\n                }\n            }\n\n            // Every producer increments the counter, the consumer checks + erases it.\n            lock.lock();\n            counter++;\n            lock.unlock();\n\n            ASSERT_EQ(producerTimelines[threadId].inc(), 0);\n        }\n    };\n\n    for (int i = 0; i < GetParam(); i++) {\n        threads.push_back(thread{threadMain, i});\n    }\n\n    // Consumer thread runs this loop.\n    for (int i = 1; i <= iterations; i++) {\n        // Create a fence representing all producers final timelines.\n        vector<SyncFence> fences;\n        for (auto& timeline : producerTimelines) {\n            fences.push_back(SyncFence(timeline, i));\n        }\n        SyncFence mergeFence(fences);\n        ASSERT_TRUE(mergeFence.isValid());\n\n        // Make sure we see an increment from every producer thread. Vary\n        // the means by which we wait.\n        if (iterations % 8 != 0) {\n            ASSERT_EQ(mergeFence.wait(), 0);\n        }\n        else {\n            while (mergeFence.getSignaledCount() != mergeFence.getSize()) {\n                ASSERT_EQ(mergeFence.getErrorCount(), 0);\n            }\n        }\n        ASSERT_EQ(counter, GetParam()*i);\n\n        // Release the producer threads.\n        ASSERT_EQ(consumerTimeline.inc(), 0);\n    }\n\n    for_each(begin(threads), end(threads), [](thread& thread) { thread.join(); });\n}\nINSTANTIATE_TEST_CASE_P(\n    ParameterizedStressTest,\n    ConsumerStressTest,\n    ::testing::Values(2,4,16));\n\nclass MergeStressTest : public ::testing::TestWithParam<tuple<int, int>> {};\n\ntemplate <typename K, typename V> using dict = unordered_map<K,V>;\n\nTEST_P(MergeStressTest, RandomMerge) {\n    int timelineCount = get<0>(GetParam());\n    int mergeCount = get<1>(GetParam());\n\n    vector<SyncTimeline> timelines(timelineCount);\n\n    default_random_engine generator;\n    uniform_int_distribution<int> timelineDist(0, timelines.size()-1);\n    uniform_int_distribution<int> syncPointDist(0, numeric_limits<int>::max());\n\n    SyncFence fence(timelines[0], 0);\n    ASSERT_TRUE(fence.isValid());\n\n    unordered_map<int, int> fenceMap;\n    fenceMap.insert(make_pair(0, 0));\n\n    // Randomly create syncpoints out of a fixed set of timelines, and merge them together.\n    for (int i = 0; i < mergeCount; i++) {\n\n        // Generate syncpoint.\n        int timelineOffset = timelineDist(generator);\n        const SyncTimeline& timeline = timelines[timelineOffset];\n        int syncPoint = syncPointDist(generator);\n\n        // Keep track of the latest syncpoint in each timeline.\n        auto itr = fenceMap.find(timelineOffset);\n        if (itr == end(fenceMap)) {\n            fenceMap.insert(make_pair(timelineOffset, syncPoint));\n        }\n        else {\n            int oldSyncPoint = itr->second;\n            fenceMap.erase(itr);\n            fenceMap.insert(make_pair(timelineOffset, max(syncPoint, oldSyncPoint)));\n        }\n\n        // Merge.\n        fence = SyncFence(fence, SyncFence(timeline, syncPoint));\n        ASSERT_TRUE(fence.isValid());\n        CheckModernLegacyInfoMatch(fence);\n    }\n\n    // Confirm our map matches the fence.\n    ASSERT_EQ(fence.getSize(), fenceMap.size());\n\n    // Trigger the merged fence.\n    for (auto& item: fenceMap) {\n        ASSERT_EQ(fence.wait(0), -1);\n        ASSERT_EQ(errno, ETIME);\n\n        // Increment the timeline to the last syncpoint.\n        timelines[item.first].inc(item.second);\n    }\n\n    // Check that the fence is triggered.\n    ASSERT_EQ(fence.wait(0), 0);\n}\n\nINSTANTIATE_TEST_CASE_P(\n    ParameterizedMergeStressTest,\n    MergeStressTest,\n    ::testing::Combine(::testing::Values(16,32), ::testing::Values(32, 1024, 1024*32)));\n\n}\n\n"
  },
  {
    "path": "libsystem/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_headers {\n    name: \"libsystem_headers\",\n    vendor_available: true,\n    product_available: true,\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    host_supported: true,\n    native_bridge_supported: true,\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n    min_sdk_version: \"apex_inherit\",\n    export_include_dirs: [\"include\"],\n\n    target: {\n        linux_bionic: {\n            enabled: true,\n        },\n        windows: {\n            enabled: true,\n        },\n    },\n}\n"
  },
  {
    "path": "libsystem/OWNERS",
    "content": "# graphics/composer\nadyabr@google.com\n\n# camera\netalvala@google.com\n"
  },
  {
    "path": "libsystem/include/system/camera.h",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H\n#define SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H\n\n#include <stdint.h>\n#include <sys/cdefs.h>\n#include <sys/types.h>\n#include <cutils/native_handle.h>\n#include <hardware/hardware.h>\n#include <hardware/gralloc.h>\n\n__BEGIN_DECLS\n\n/**\n * A set of bit masks for specifying how the received preview frames are\n * handled before the previewCallback() call.\n *\n * The least significant 3 bits of an \"int\" value are used for this purpose:\n *\n * ..... 0 0 0\n *       ^ ^ ^\n *       | | |---------> determine whether the callback is enabled or not\n *       | |-----------> determine whether the callback is one-shot or not\n *       |-------------> determine whether the frame is copied out or not\n *\n * WARNING: When a frame is sent directly without copying, it is the frame\n * receiver's responsiblity to make sure that the frame data won't get\n * corrupted by subsequent preview frames filled by the camera. This flag is\n * recommended only when copying out data brings significant performance price\n * and the handling/processing of the received frame data is always faster than\n * the preview frame rate so that data corruption won't occur.\n *\n * For instance,\n * 1. 0x00 disables the callback. In this case, copy out and one shot bits\n *    are ignored.\n * 2. 0x01 enables a callback without copying out the received frames. A\n *    typical use case is the Camcorder application to avoid making costly\n *    frame copies.\n * 3. 0x05 is enabling a callback with frame copied out repeatedly. A typical\n *    use case is the Camera application.\n * 4. 0x07 is enabling a callback with frame copied out only once. A typical\n *    use case is the Barcode scanner application.\n */\n\nenum {\n    CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK = 0x01,\n    CAMERA_FRAME_CALLBACK_FLAG_ONE_SHOT_MASK = 0x02,\n    CAMERA_FRAME_CALLBACK_FLAG_COPY_OUT_MASK = 0x04,\n    /** Typical use cases */\n    CAMERA_FRAME_CALLBACK_FLAG_NOOP = 0x00,\n    CAMERA_FRAME_CALLBACK_FLAG_CAMCORDER = 0x01,\n    CAMERA_FRAME_CALLBACK_FLAG_CAMERA = 0x05,\n    CAMERA_FRAME_CALLBACK_FLAG_BARCODE_SCANNER = 0x07\n};\n\n/** msgType in notifyCallback and dataCallback functions */\nenum {\n    CAMERA_MSG_ERROR = 0x0001,            // notifyCallback\n    CAMERA_MSG_SHUTTER = 0x0002,          // notifyCallback\n    CAMERA_MSG_FOCUS = 0x0004,            // notifyCallback\n    CAMERA_MSG_ZOOM = 0x0008,             // notifyCallback\n    CAMERA_MSG_PREVIEW_FRAME = 0x0010,    // dataCallback\n    CAMERA_MSG_VIDEO_FRAME = 0x0020,      // data_timestamp_callback\n    CAMERA_MSG_POSTVIEW_FRAME = 0x0040,   // dataCallback\n    CAMERA_MSG_RAW_IMAGE = 0x0080,        // dataCallback\n    CAMERA_MSG_COMPRESSED_IMAGE = 0x0100, // dataCallback\n    CAMERA_MSG_RAW_IMAGE_NOTIFY = 0x0200, // dataCallback\n    // Preview frame metadata. This can be combined with\n    // CAMERA_MSG_PREVIEW_FRAME in dataCallback. For example, the apps can\n    // request FRAME and METADATA. Or the apps can request only FRAME or only\n    // METADATA.\n    CAMERA_MSG_PREVIEW_METADATA = 0x0400, // dataCallback\n    // Notify on autofocus start and stop. This is useful in continuous\n    // autofocus - FOCUS_MODE_CONTINUOUS_VIDEO and FOCUS_MODE_CONTINUOUS_PICTURE.\n    CAMERA_MSG_FOCUS_MOVE = 0x0800,       // notifyCallback\n    CAMERA_MSG_ALL_MSGS = 0xFFFF\n};\n\n/** cmdType in sendCommand functions */\nenum {\n    CAMERA_CMD_START_SMOOTH_ZOOM = 1,\n    CAMERA_CMD_STOP_SMOOTH_ZOOM = 2,\n\n    /**\n     * Set the clockwise rotation of preview display (setPreviewDisplay) in\n     * degrees. This affects the preview frames and the picture displayed after\n     * snapshot. This method is useful for portrait mode applications. Note\n     * that preview display of front-facing cameras is flipped horizontally\n     * before the rotation, that is, the image is reflected along the central\n     * vertical axis of the camera sensor. So the users can see themselves as\n     * looking into a mirror.\n     *\n     * This does not affect the order of byte array of\n     * CAMERA_MSG_PREVIEW_FRAME, CAMERA_MSG_VIDEO_FRAME,\n     * CAMERA_MSG_POSTVIEW_FRAME, CAMERA_MSG_RAW_IMAGE, or\n     * CAMERA_MSG_COMPRESSED_IMAGE. This is allowed to be set during preview\n     * since API level 14.\n     */\n    CAMERA_CMD_SET_DISPLAY_ORIENTATION = 3,\n\n    /**\n     * cmdType to disable/enable shutter sound. In sendCommand passing arg1 =\n     * 0 will disable, while passing arg1 = 1 will enable the shutter sound.\n     */\n    CAMERA_CMD_ENABLE_SHUTTER_SOUND = 4,\n\n    /* cmdType to play recording sound */\n    CAMERA_CMD_PLAY_RECORDING_SOUND = 5,\n\n    /**\n     * Start the face detection. This should be called after preview is started.\n     * The camera will notify the listener of CAMERA_MSG_FACE and the detected\n     * faces in the preview frame. The detected faces may be the same as the\n     * previous ones. Apps should call CAMERA_CMD_STOP_FACE_DETECTION to stop\n     * the face detection. This method is supported if CameraParameters\n     * KEY_MAX_NUM_HW_DETECTED_FACES or KEY_MAX_NUM_SW_DETECTED_FACES is\n     * bigger than 0. Hardware and software face detection should not be running\n     * at the same time. If the face detection has started, apps should not send\n     * this again.\n     *\n     * In hardware face detection mode, CameraParameters KEY_WHITE_BALANCE,\n     * KEY_FOCUS_AREAS and KEY_METERING_AREAS have no effect.\n     *\n     * arg1 is the face detection type. It can be CAMERA_FACE_DETECTION_HW or\n     * CAMERA_FACE_DETECTION_SW. If the type of face detection requested is not\n     * supported, the HAL must return BAD_VALUE.\n     */\n    CAMERA_CMD_START_FACE_DETECTION = 6,\n\n    /**\n     * Stop the face detection.\n     */\n    CAMERA_CMD_STOP_FACE_DETECTION = 7,\n\n    /**\n     * Enable/disable focus move callback (CAMERA_MSG_FOCUS_MOVE). Passing\n     * arg1 = 0 will disable, while passing arg1 = 1 will enable the callback.\n     */\n    CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG = 8,\n\n    /**\n     * Ping camera service to see if camera hardware is released.\n     *\n     * When any camera method returns error, the client can use ping command\n     * to see if the camera has been taken away by other clients. If the result\n     * is OK, it means the camera hardware is not released. If the result\n     * is not OK, the camera has been released and the existing client\n     * can silently finish itself or show a dialog.\n     */\n    CAMERA_CMD_PING = 9,\n\n    /**\n     * Configure the number of video buffers used for recording. The intended\n     * video buffer count for recording is passed as arg1, which must be\n     * greater than 0. This command must be sent before recording is started.\n     * This command returns INVALID_OPERATION error if it is sent after video\n     * recording is started, or the command is not supported at all. This\n     * command also returns a BAD_VALUE error if the intended video buffer\n     * count is non-positive or too big to be realized.\n     */\n    CAMERA_CMD_SET_VIDEO_BUFFER_COUNT = 10,\n\n    /**\n     * Configure an explicit format to use for video recording metadata mode.\n     * This can be used to switch the format from the\n     * default IMPLEMENTATION_DEFINED gralloc format to some other\n     * device-supported format, and the default dataspace from the BT_709 color\n     * space to some other device-supported dataspace. arg1 is the HAL pixel\n     * format, and arg2 is the HAL dataSpace. This command returns\n     * INVALID_OPERATION error if it is sent after video recording is started,\n     * or the command is not supported at all.\n     *\n     * If the gralloc format is set to a format other than\n     * IMPLEMENTATION_DEFINED, then HALv3 devices will use gralloc usage flags\n     * of SW_READ_OFTEN.\n     */\n    CAMERA_CMD_SET_VIDEO_FORMAT = 11\n};\n\n/** camera fatal errors */\nenum {\n    CAMERA_ERROR_UNKNOWN = 1,\n    /**\n     * Camera was released because another client has connected to the camera.\n     * The original client should call Camera::disconnect immediately after\n     * getting this notification. Otherwise, the camera will be released by\n     * camera service in a short time. The client should not call any method\n     * (except disconnect and sending CAMERA_CMD_PING) after getting this.\n     */\n    CAMERA_ERROR_RELEASED = 2,\n\n    /**\n     * Camera was released because device policy change or the client application\n     * is going to background. The client should call Camera::disconnect\n     * immediately after getting this notification. Otherwise, the camera will be\n     * released by camera service in a short time. The client should not call any\n     * method (except disconnect and sending CAMERA_CMD_PING) after getting this.\n     */\n    CAMERA_ERROR_DISABLED = 3,\n    CAMERA_ERROR_SERVER_DIED = 100\n};\n\nenum {\n    /** The facing of the camera is opposite to that of the screen. */\n    CAMERA_FACING_BACK = 0,\n    /** The facing of the camera is the same as that of the screen. */\n    CAMERA_FACING_FRONT = 1,\n    /**\n     * The facing of the camera is not fixed relative to the screen.\n     * The cameras with this facing are external cameras, e.g. USB cameras.\n     */\n    CAMERA_FACING_EXTERNAL = 2\n};\n\nenum {\n    /** Hardware face detection. It does not use much CPU. */\n    CAMERA_FACE_DETECTION_HW = 0,\n    /**\n     * Software face detection. It uses some CPU. Applications must use\n     * Camera.setPreviewTexture for preview in this mode.\n     */\n    CAMERA_FACE_DETECTION_SW = 1\n};\n\n/**\n * The information of a face from camera face detection.\n */\ntypedef struct camera_face {\n    /**\n     * Bounds of the face [left, top, right, bottom]. (-1000, -1000) represents\n     * the top-left of the camera field of view, and (1000, 1000) represents the\n     * bottom-right of the field of view. The width and height cannot be 0 or\n     * negative. This is supported by both hardware and software face detection.\n     *\n     * The direction is relative to the sensor orientation, that is, what the\n     * sensor sees. The direction is not affected by the rotation or mirroring\n     * of CAMERA_CMD_SET_DISPLAY_ORIENTATION.\n     */\n    int32_t rect[4];\n\n    /**\n     * The confidence level of the face. The range is 1 to 100. 100 is the\n     * highest confidence. This is supported by both hardware and software\n     * face detection.\n     */\n    int32_t score;\n\n    /**\n     * An unique id per face while the face is visible to the tracker. If\n     * the face leaves the field-of-view and comes back, it will get a new\n     * id. If the value is 0, id is not supported.\n     */\n    int32_t id;\n\n    /**\n     * The coordinates of the center of the left eye. The range is -1000 to\n     * 1000. -2000, -2000 if this is not supported.\n     */\n    int32_t left_eye[2];\n\n    /**\n     * The coordinates of the center of the right eye. The range is -1000 to\n     * 1000. -2000, -2000 if this is not supported.\n     */\n    int32_t right_eye[2];\n\n    /**\n     * The coordinates of the center of the mouth. The range is -1000 to 1000.\n     * -2000, -2000 if this is not supported.\n     */\n    int32_t mouth[2];\n\n} camera_face_t;\n\n/**\n * The metadata of the frame data.\n */\ntypedef struct camera_frame_metadata {\n    /**\n     * The number of detected faces in the frame.\n     */\n    int32_t number_of_faces;\n\n    /**\n     * An array of the detected faces. The length is number_of_faces.\n     */\n    camera_face_t *faces;\n} camera_frame_metadata_t;\n\n__END_DECLS\n\n#endif /* SYSTEM_CORE_INCLUDE_ANDROID_CAMERA_H */\n"
  },
  {
    "path": "libsystem/include/system/graphics-base-v1.0.h",
    "content": "// This file is autogenerated by hidl-gen. Do not edit manually.\n// Source: android.hardware.graphics.common@1.0\n// Location: hardware/interfaces/graphics/common/1.0/\n\n#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_\n#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    HAL_PIXEL_FORMAT_RGBA_8888 = 1,\n    HAL_PIXEL_FORMAT_RGBX_8888 = 2,\n    HAL_PIXEL_FORMAT_RGB_888 = 3,\n    HAL_PIXEL_FORMAT_RGB_565 = 4,\n    HAL_PIXEL_FORMAT_BGRA_8888 = 5,\n    HAL_PIXEL_FORMAT_YCBCR_422_SP = 16,\n    HAL_PIXEL_FORMAT_YCRCB_420_SP = 17,\n    HAL_PIXEL_FORMAT_YCBCR_422_I = 20,\n    HAL_PIXEL_FORMAT_RGBA_FP16 = 22,\n    HAL_PIXEL_FORMAT_RAW16 = 32,\n    HAL_PIXEL_FORMAT_BLOB = 33,\n    HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 34,\n    HAL_PIXEL_FORMAT_YCBCR_420_888 = 35,\n    HAL_PIXEL_FORMAT_RAW_OPAQUE = 36,\n    HAL_PIXEL_FORMAT_RAW10 = 37,\n    HAL_PIXEL_FORMAT_RAW12 = 38,\n    HAL_PIXEL_FORMAT_RGBA_1010102 = 43,\n    HAL_PIXEL_FORMAT_Y8 = 538982489,\n    HAL_PIXEL_FORMAT_Y16 = 540422489,\n    HAL_PIXEL_FORMAT_YV12 = 842094169,\n} android_pixel_format_t;\n\ntypedef enum {\n    HAL_TRANSFORM_FLIP_H = 1,   // (1 << 0)\n    HAL_TRANSFORM_FLIP_V = 2,   // (1 << 1)\n    HAL_TRANSFORM_ROT_90 = 4,   // (1 << 2)\n    HAL_TRANSFORM_ROT_180 = 3,  // (FLIP_H | FLIP_V)\n    HAL_TRANSFORM_ROT_270 = 7,  // ((FLIP_H | FLIP_V) | ROT_90)\n} android_transform_t;\n\ntypedef enum {\n    HAL_DATASPACE_UNKNOWN = 0,\n    HAL_DATASPACE_ARBITRARY = 1,\n    HAL_DATASPACE_STANDARD_SHIFT = 16,\n    HAL_DATASPACE_STANDARD_MASK = 4128768,                      // (63 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_UNSPECIFIED = 0,                     // (0 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT709 = 65536,                       // (1 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT601_625 = 131072,                  // (2 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608,       // (3 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT601_525 = 262144,                  // (4 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680,       // (5 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT2020 = 393216,                     // (6 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752,  // (7 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_BT470M = 524288,                     // (8 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_FILM = 589824,                       // (9 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_DCI_P3 = 655360,                     // (10 << STANDARD_SHIFT)\n    HAL_DATASPACE_STANDARD_ADOBE_RGB = 720896,                  // (11 << STANDARD_SHIFT)\n    HAL_DATASPACE_TRANSFER_SHIFT = 22,\n    HAL_DATASPACE_TRANSFER_MASK = 130023424,       // (31 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_UNSPECIFIED = 0,        // (0 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_LINEAR = 4194304,       // (1 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_SRGB = 8388608,         // (2 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_SMPTE_170M = 12582912,  // (3 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_GAMMA2_2 = 16777216,    // (4 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_GAMMA2_6 = 20971520,    // (5 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_GAMMA2_8 = 25165824,    // (6 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_ST2084 = 29360128,      // (7 << TRANSFER_SHIFT)\n    HAL_DATASPACE_TRANSFER_HLG = 33554432,         // (8 << TRANSFER_SHIFT)\n    HAL_DATASPACE_RANGE_SHIFT = 27,\n    HAL_DATASPACE_RANGE_MASK = 939524096,      // (7 << RANGE_SHIFT)\n    HAL_DATASPACE_RANGE_UNSPECIFIED = 0,       // (0 << RANGE_SHIFT)\n    HAL_DATASPACE_RANGE_FULL = 134217728,      // (1 << RANGE_SHIFT)\n    HAL_DATASPACE_RANGE_LIMITED = 268435456,   // (2 << RANGE_SHIFT)\n    HAL_DATASPACE_RANGE_EXTENDED = 402653184,  // (3 << RANGE_SHIFT)\n    HAL_DATASPACE_SRGB_LINEAR = 512,\n    HAL_DATASPACE_V0_SRGB_LINEAR = 138477568,  // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)\n    HAL_DATASPACE_V0_SCRGB_LINEAR =\n        406913024,  // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)\n    HAL_DATASPACE_SRGB = 513,\n    HAL_DATASPACE_V0_SRGB = 142671872,   // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)\n    HAL_DATASPACE_V0_SCRGB = 411107328,  // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)\n    HAL_DATASPACE_JFIF = 257,\n    HAL_DATASPACE_V0_JFIF = 146931712,  // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)\n    HAL_DATASPACE_BT601_625 = 258,\n    HAL_DATASPACE_V0_BT601_625 =\n        281149440,  // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)\n    HAL_DATASPACE_BT601_525 = 259,\n    HAL_DATASPACE_V0_BT601_525 =\n        281280512,  // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)\n    HAL_DATASPACE_BT709 = 260,\n    HAL_DATASPACE_V0_BT709 = 281083904,  // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)\n    HAL_DATASPACE_DCI_P3_LINEAR = 139067392,  // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)\n    HAL_DATASPACE_DCI_P3 = 155844608,  // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)\n    HAL_DATASPACE_DISPLAY_P3_LINEAR =\n        139067392,                         // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)\n    HAL_DATASPACE_DISPLAY_P3 = 143261696,  // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)\n    HAL_DATASPACE_ADOBE_RGB = 151715840,  // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)\n    HAL_DATASPACE_BT2020_LINEAR = 138805248,  // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)\n    HAL_DATASPACE_BT2020 = 147193856,     // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)\n    HAL_DATASPACE_BT2020_PQ = 163971072,  // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)\n    HAL_DATASPACE_DEPTH = 4096,\n    HAL_DATASPACE_SENSOR = 4097,\n} android_dataspace_t;\n\ntypedef enum {\n    HAL_COLOR_MODE_NATIVE = 0,\n    HAL_COLOR_MODE_STANDARD_BT601_625 = 1,\n    HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED = 2,\n    HAL_COLOR_MODE_STANDARD_BT601_525 = 3,\n    HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED = 4,\n    HAL_COLOR_MODE_STANDARD_BT709 = 5,\n    HAL_COLOR_MODE_DCI_P3 = 6,\n    HAL_COLOR_MODE_SRGB = 7,\n    HAL_COLOR_MODE_ADOBE_RGB = 8,\n    HAL_COLOR_MODE_DISPLAY_P3 = 9,\n} android_color_mode_t;\n\ntypedef enum {\n    HAL_COLOR_TRANSFORM_IDENTITY = 0,\n    HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX = 1,\n    HAL_COLOR_TRANSFORM_VALUE_INVERSE = 2,\n    HAL_COLOR_TRANSFORM_GRAYSCALE = 3,\n    HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA = 4,\n    HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA = 5,\n    HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA = 6,\n} android_color_transform_t;\n\ntypedef enum {\n    HAL_HDR_DOLBY_VISION = 1,\n    HAL_HDR_HDR10 = 2,\n    HAL_HDR_HLG = 3,\n} android_hdr_t;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_\n"
  },
  {
    "path": "libsystem/include/system/graphics-base-v1.1.h",
    "content": "// This file is autogenerated by hidl-gen. Do not edit manually.\n// Source: android.hardware.graphics.common@1.1\n// Location: hardware/interfaces/graphics/common/1.1/\n\n#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_\n#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    HAL_PIXEL_FORMAT_DEPTH_16 = 48,\n    HAL_PIXEL_FORMAT_DEPTH_24 = 49,\n    HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = 50,\n    HAL_PIXEL_FORMAT_DEPTH_32F = 51,\n    HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8 = 52,\n    HAL_PIXEL_FORMAT_STENCIL_8 = 53,\n    HAL_PIXEL_FORMAT_YCBCR_P010 = 54,\n} android_pixel_format_v1_1_t;\n\ntypedef enum {\n    HAL_DATASPACE_BT2020_ITU =\n        281411584,  // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)\n    HAL_DATASPACE_BT2020_ITU_PQ =\n        298188800,  // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_LIMITED)\n    HAL_DATASPACE_BT2020_ITU_HLG = 302383104,  // ((STANDARD_BT2020 | TRANSFER_HLG) | RANGE_LIMITED)\n    HAL_DATASPACE_BT2020_HLG = 168165376,      // ((STANDARD_BT2020 | TRANSFER_HLG) | RANGE_FULL)\n} android_dataspace_v1_1_t;\n\ntypedef enum {\n    HAL_COLOR_MODE_BT2020 = 10,\n    HAL_COLOR_MODE_BT2100_PQ = 11,\n    HAL_COLOR_MODE_BT2100_HLG = 12,\n} android_color_mode_v1_1_t;\n\ntypedef enum {\n    HAL_RENDER_INTENT_COLORIMETRIC = 0,\n    HAL_RENDER_INTENT_ENHANCE = 1,\n    HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC = 2,\n    HAL_RENDER_INTENT_TONE_MAP_ENHANCE = 3,\n} android_render_intent_v1_1_t;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_1_EXPORTED_CONSTANTS_H_\n"
  },
  {
    "path": "libsystem/include/system/graphics-base-v1.2.h",
    "content": "// This file is autogenerated by hidl-gen. Do not edit manually.\n// Source: android.hardware.graphics.common@1.2\n// Location: hardware/interfaces/graphics/common/1.2/\n\n#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_\n#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef enum {\n    HAL_HDR_HDR10_PLUS = 4,\n} android_hdr_v1_2_t;\n\ntypedef enum {\n    HAL_DATASPACE_DISPLAY_BT2020 = 142999552 /* STANDARD_BT2020 | TRANSFER_SRGB | RANGE_FULL */,\n    HAL_DATASPACE_DYNAMIC_DEPTH = 4098 /* 0x1002 */,\n    HAL_DATASPACE_JPEG_APP_SEGMENTS = 4099 /* 0x1003 */,\n    HAL_DATASPACE_HEIF = 4100 /* 0x1004 */,\n} android_dataspace_v1_2_t;\n\ntypedef enum {\n    HAL_COLOR_MODE_DISPLAY_BT2020 = 13,\n} android_color_mode_v1_2_t;\n\ntypedef enum {\n    HAL_PIXEL_FORMAT_HSV_888 = 55 /* 0x37 */,\n} android_pixel_format_v1_2_t;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_2_EXPORTED_CONSTANTS_H_\n"
  },
  {
    "path": "libsystem/include/system/graphics-base.h",
    "content": "#ifndef SYSTEM_CORE_GRAPHICS_BASE_H_\n#define SYSTEM_CORE_GRAPHICS_BASE_H_\n\n#include \"graphics-base-v1.0.h\"\n#include \"graphics-base-v1.1.h\"\n#include \"graphics-base-v1.2.h\"\n\n#endif  // SYSTEM_CORE_GRAPHICS_BASE_H_\n"
  },
  {
    "path": "libsystem/include/system/graphics-sw.h",
    "content": "#ifndef SYSTEM_CORE_GRAPHICS_SW_H_\n#define SYSTEM_CORE_GRAPHICS_SW_H_\n\n/* Software formats not in the HAL definitions. */\ntypedef enum {\n    HAL_PIXEL_FORMAT_YCBCR_422_888 = 39,   // 0x27\n    HAL_PIXEL_FORMAT_YCBCR_444_888 = 40,   // 0x28\n    HAL_PIXEL_FORMAT_FLEX_RGB_888 = 41,    // 0x29\n    HAL_PIXEL_FORMAT_FLEX_RGBA_8888 = 42,  // 0x2A\n} android_pixel_format_sw_t;\n\n/* for compatibility */\n#define HAL_PIXEL_FORMAT_YCbCr_422_888 HAL_PIXEL_FORMAT_YCBCR_422_888\n#define HAL_PIXEL_FORMAT_YCbCr_444_888 HAL_PIXEL_FORMAT_YCBCR_444_888\n\n#endif  // SYSTEM_CORE_GRAPHICS_SW_H_\n"
  },
  {
    "path": "libsystem/include/system/graphics.h",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H\n#define SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H\n\n#include <stddef.h>\n#include <stdint.h>\n\n/*\n * Some of the enums are now defined in HIDL in hardware/interfaces and are\n * generated.\n */\n#include \"graphics-base.h\"\n#include \"graphics-sw.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/* for compatibility */\n#define HAL_PIXEL_FORMAT_YCbCr_420_888 HAL_PIXEL_FORMAT_YCBCR_420_888\n#define HAL_PIXEL_FORMAT_YCbCr_422_SP HAL_PIXEL_FORMAT_YCBCR_422_SP\n#define HAL_PIXEL_FORMAT_YCrCb_420_SP HAL_PIXEL_FORMAT_YCRCB_420_SP\n#define HAL_PIXEL_FORMAT_YCbCr_422_I HAL_PIXEL_FORMAT_YCBCR_422_I\ntypedef android_pixel_format_t android_pixel_format;\ntypedef android_transform_t android_transform;\ntypedef android_dataspace_t android_dataspace;\ntypedef android_color_mode_t android_color_mode;\ntypedef android_color_transform_t android_color_transform;\ntypedef android_hdr_t android_hdr;\n\n/*\n * If the HAL needs to create service threads to handle graphics related\n * tasks, these threads need to run at HAL_PRIORITY_URGENT_DISPLAY priority\n * if they can block the main rendering thread in any way.\n *\n * the priority of the current thread can be set with:\n *\n *      #include <sys/resource.h>\n *      setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);\n *\n */\n\n#define HAL_PRIORITY_URGENT_DISPLAY     (-8)\n\n/*\n * Structure for describing YCbCr formats for consumption by applications.\n * This is used with HAL_PIXEL_FORMAT_YCbCr_*.\n *\n * Buffer chroma subsampling is defined in the format.\n * e.g. HAL_PIXEL_FORMAT_YCbCr_420_888 has subsampling 4:2:0.\n *\n * Buffers must have a byte aligned channel depth or a byte aligned packed\n * channel depth (e.g. 10 bits packed into 16 bits for\n * HAL_PIXEL_FORMAT_YCbCr_P010).\n *\n * y, cb, and cr point to the first byte of their respective planes.\n *\n * Stride describes the distance in bytes from the first value of one row of\n * the image to the first value of the next row.  It includes the width of the\n * image plus padding.\n * ystride is the stride of the luma plane.\n * cstride is the stride of the chroma planes.\n *\n * chroma_step is the distance in bytes from one chroma pixel value to the\n * next.  This is `2 * channel depth` bytes for semiplanar (because chroma\n * values are interleaved) and `1 * channel depth` bytes for planar.\n */\n\nstruct android_ycbcr {\n    void *y;\n    void *cb;\n    void *cr;\n    size_t ystride;\n    size_t cstride;\n    size_t chroma_step;\n\n    /** reserved for future use, set to 0 by gralloc's (*lock_ycbcr)() */\n    uint32_t reserved[8];\n};\n\n/*\n * Structures for describing flexible YUVA/RGBA formats for consumption by\n * applications. Such flexible formats contain a plane for each component (e.g.\n * red, green, blue), where each plane is laid out in a grid-like pattern\n * occupying unique byte addresses and with consistent byte offsets between\n * neighboring pixels.\n *\n * The android_flex_layout structure is used with any pixel format that can be\n * represented by it, such as:\n *  - HAL_PIXEL_FORMAT_YCbCr_*_888\n *  - HAL_PIXEL_FORMAT_FLEX_RGB*_888\n *  - HAL_PIXEL_FORMAT_RGB[AX]_888[8],BGRA_8888,RGB_888\n *  - HAL_PIXEL_FORMAT_YV12,Y8,Y16,YCbCr_422_SP/I,YCrCb_420_SP\n *  - even implementation defined formats that can be represented by\n *    the structures\n *\n * Vertical increment (aka. row increment or stride) describes the distance in\n * bytes from the first pixel of one row to the first pixel of the next row\n * (below) for the component plane. This can be negative.\n *\n * Horizontal increment (aka. column or pixel increment) describes the distance\n * in bytes from one pixel to the next pixel (to the right) on the same row for\n * the component plane. This can be negative.\n *\n * Each plane can be subsampled either vertically or horizontally by\n * a power-of-two factor.\n *\n * The bit-depth of each component can be arbitrary, as long as the pixels are\n * laid out on whole bytes, in native byte-order, using the most significant\n * bits of each unit.\n */\n\ntypedef enum android_flex_component {\n    /* luma */\n    FLEX_COMPONENT_Y = 1 << 0,\n    /* chroma blue */\n    FLEX_COMPONENT_Cb = 1 << 1,\n    /* chroma red */\n    FLEX_COMPONENT_Cr = 1 << 2,\n\n    /* red */\n    FLEX_COMPONENT_R = 1 << 10,\n    /* green */\n    FLEX_COMPONENT_G = 1 << 11,\n    /* blue */\n    FLEX_COMPONENT_B = 1 << 12,\n\n    /* alpha */\n    FLEX_COMPONENT_A = 1 << 30,\n} android_flex_component_t;\n\ntypedef struct android_flex_plane {\n    /* pointer to the first byte of the top-left pixel of the plane. */\n    uint8_t *top_left;\n\n    android_flex_component_t component;\n\n    /* bits allocated for the component in each pixel. Must be a positive\n       multiple of 8. */\n    int32_t bits_per_component;\n    /* number of the most significant bits used in the format for this\n       component. Must be between 1 and bits_per_component, inclusive. */\n    int32_t bits_used;\n\n    /* horizontal increment */\n    int32_t h_increment;\n    /* vertical increment */\n    int32_t v_increment;\n    /* horizontal subsampling. Must be a positive power of 2. */\n    int32_t h_subsampling;\n    /* vertical subsampling. Must be a positive power of 2. */\n    int32_t v_subsampling;\n} android_flex_plane_t;\n\ntypedef enum android_flex_format {\n    /* not a flexible format */\n    FLEX_FORMAT_INVALID = 0x0,\n    FLEX_FORMAT_Y = FLEX_COMPONENT_Y,\n    FLEX_FORMAT_YCbCr = FLEX_COMPONENT_Y | FLEX_COMPONENT_Cb | FLEX_COMPONENT_Cr,\n    FLEX_FORMAT_YCbCrA = FLEX_FORMAT_YCbCr | FLEX_COMPONENT_A,\n    FLEX_FORMAT_RGB = FLEX_COMPONENT_R | FLEX_COMPONENT_G | FLEX_COMPONENT_B,\n    FLEX_FORMAT_RGBA = FLEX_FORMAT_RGB | FLEX_COMPONENT_A,\n} android_flex_format_t;\n\ntypedef struct android_flex_layout {\n    /* the kind of flexible format */\n    android_flex_format_t format;\n\n    /* number of planes; 0 for FLEX_FORMAT_INVALID */\n    uint32_t num_planes;\n    /* a plane for each component; ordered in increasing component value order.\n       E.g. FLEX_FORMAT_RGBA maps 0 -> R, 1 -> G, etc.\n       Can be NULL for FLEX_FORMAT_INVALID */\n    android_flex_plane_t *planes;\n} android_flex_layout_t;\n\n/**\n * Structure used to define depth point clouds for format HAL_PIXEL_FORMAT_BLOB\n * with dataSpace value of HAL_DATASPACE_DEPTH.\n * When locking a native buffer of the above format and dataSpace value,\n * the vaddr pointer can be cast to this structure.\n *\n * A variable-length list of (x,y,z, confidence) 3D points, as floats.  (x, y,\n * z) represents a measured point's position, with the coordinate system defined\n * by the data source.  Confidence represents the estimated likelihood that this\n * measurement is correct. It is between 0.f and 1.f, inclusive, with 1.f ==\n * 100% confidence.\n *\n * num_points is the number of points in the list\n *\n * xyz_points is the flexible array of floating-point values.\n *   It contains (num_points) * 4 floats.\n *\n *   For example:\n *     android_depth_points d = get_depth_buffer();\n *     struct {\n *       float x; float y; float z; float confidence;\n *     } firstPoint, lastPoint;\n *\n *     firstPoint.x = d.xyzc_points[0];\n *     firstPoint.y = d.xyzc_points[1];\n *     firstPoint.z = d.xyzc_points[2];\n *     firstPoint.confidence = d.xyzc_points[3];\n *     lastPoint.x = d.xyzc_points[(d.num_points - 1) * 4 + 0];\n *     lastPoint.y = d.xyzc_points[(d.num_points - 1) * 4 + 1];\n *     lastPoint.z = d.xyzc_points[(d.num_points - 1) * 4 + 2];\n *     lastPoint.confidence = d.xyzc_points[(d.num_points - 1) * 4 + 3];\n */\n\nstruct android_depth_points {\n    uint32_t num_points;\n\n    /** reserved for future use, set to 0 by gralloc's (*lock)() */\n    uint32_t reserved[8];\n\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wc99-extensions\"\n#endif\n    float xyzc_points[];\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n};\n\n/**\n  * These structures are used to define the reference display's\n  * capabilities for HDR content. Display engine can use this\n  * to better tone map content to user's display.\n  * Color is defined in CIE XYZ coordinates\n  */\nstruct android_xy_color {\n    float x;\n    float y;\n};\n\nstruct android_smpte2086_metadata {\n    struct android_xy_color displayPrimaryRed;\n    struct android_xy_color displayPrimaryGreen;\n    struct android_xy_color displayPrimaryBlue;\n    struct android_xy_color whitePoint;\n    float maxLuminance;\n    float minLuminance;\n};\n\nstruct android_cta861_3_metadata {\n    float maxContentLightLevel;\n    float maxFrameAverageLightLevel;\n};\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif /* SYSTEM_CORE_INCLUDE_ANDROID_GRAPHICS_H */\n"
  },
  {
    "path": "libsystem/include/system/radio.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_RADIO_H\n#define ANDROID_RADIO_H\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <sys/cdefs.h>\n#include <sys/types.h>\n\n\n#define RADIO_NUM_BANDS_MAX     16\n#define RADIO_NUM_SPACINGS_MAX  16\n#define RADIO_STRING_LEN_MAX    128\n\n/*\n * Radio hardware module class. A given radio hardware module HAL is of one class\n * only. The platform can not have more than one hardware module of each class.\n * Current version of the framework only supports RADIO_CLASS_AM_FM.\n */\ntypedef enum {\n    RADIO_CLASS_AM_FM = 0,  /* FM (including HD radio) and AM */\n    RADIO_CLASS_SAT   = 1,  /* Satellite Radio */\n    RADIO_CLASS_DT    = 2,  /* Digital Radio (DAB) */\n} radio_class_t;\n\n/* value for field \"type\" of radio band described in struct radio_hal_band_config */\ntypedef enum {\n    RADIO_BAND_AM     = 0,  /* Amplitude Modulation band: LW, MW, SW */\n    RADIO_BAND_FM     = 1,  /* Frequency Modulation band: FM */\n    RADIO_BAND_FM_HD  = 2,  /* FM HD Radio / DRM (IBOC) */\n    RADIO_BAND_AM_HD  = 3,  /* AM HD Radio / DRM (IBOC) */\n} radio_band_t;\n\n/* RDS variant implemented. A struct radio_hal_fm_band_config can list none or several. */\nenum {\n    RADIO_RDS_NONE   = 0x0,\n    RADIO_RDS_WORLD  = 0x01,\n    RADIO_RDS_US     = 0x02,\n};\ntypedef unsigned int radio_rds_t;\n\n/* FM deemphasis variant implemented. A struct radio_hal_fm_band_config can list one or more. */\nenum {\n    RADIO_DEEMPHASIS_50   = 0x1,\n    RADIO_DEEMPHASIS_75   = 0x2,\n};\ntypedef unsigned int radio_deemphasis_t;\n\n/* Region a particular radio band configuration corresponds to. Not used at the HAL.\n * Derived by the framework when converting the band descriptors retrieved from the HAL to\n * individual band descriptors for each supported region. */\ntypedef enum {\n    RADIO_REGION_NONE  = -1,\n    RADIO_REGION_ITU_1 = 0,\n    RADIO_REGION_ITU_2 = 1,\n    RADIO_REGION_OIRT  = 2,\n    RADIO_REGION_JAPAN = 3,\n    RADIO_REGION_KOREA = 4,\n} radio_region_t;\n\n/* scanning direction for scan() and step() tuner APIs */\ntypedef enum {\n    RADIO_DIRECTION_UP,\n    RADIO_DIRECTION_DOWN\n} radio_direction_t;\n\n/* unique handle allocated to a radio module */\ntypedef uint32_t radio_handle_t;\n\n/* Opaque meta data structure used by radio meta data API (see system/radio_metadata.h) */\ntypedef struct radio_metadata radio_metadata_t;\n\n\n/* Additional attributes for an FM band configuration */\ntypedef struct radio_hal_fm_band_config {\n    radio_deemphasis_t  deemphasis; /* deemphasis variant */\n    bool                stereo;     /* stereo supported */\n    radio_rds_t         rds;        /* RDS variants supported */\n    bool                ta;         /* Traffic Announcement supported */\n    bool                af;         /* Alternate Frequency supported */\n    bool                ea;         /* Emergency announcements supported */\n} radio_hal_fm_band_config_t;\n\n/* Additional attributes for an AM band configuration */\ntypedef struct radio_hal_am_band_config {\n    bool                stereo;     /* stereo supported */\n} radio_hal_am_band_config_t;\n\n/* Radio band configuration. Describes a given band supported by the radio module.\n * The HAL can expose only one band per type with the the maximum range supported and all options.\n * THe framework will derive the actual regions were this module can operate and expose separate\n * band configurations for applications to chose from. */\ntypedef struct radio_hal_band_config {\n    radio_band_t type;\n    bool         antenna_connected;\n    uint32_t     lower_limit;\n    uint32_t     upper_limit;\n    uint32_t     num_spacings;\n    uint32_t     spacings[RADIO_NUM_SPACINGS_MAX];\n    union {\n        radio_hal_fm_band_config_t fm;\n        radio_hal_am_band_config_t am;\n    };\n} radio_hal_band_config_t;\n\n/* Used internally by the framework to represent a band for s specific region */\ntypedef struct radio_band_config {\n    radio_region_t  region;\n    radio_hal_band_config_t band;\n} radio_band_config_t;\n\n\n/* Exposes properties of a given hardware radio module.\n * NOTE: current framework implementation supports only one audio source (num_audio_sources = 1).\n * The source corresponds to AUDIO_DEVICE_IN_FM_TUNER.\n * If more than one tuner is supported (num_tuners > 1), only one can be connected to the audio\n * source. */\ntypedef struct radio_hal_properties {\n    radio_class_t   class_id;   /* Class of this module. E.g RADIO_CLASS_AM_FM */\n    char            implementor[RADIO_STRING_LEN_MAX];  /* implementor name */\n    char            product[RADIO_STRING_LEN_MAX];  /* product name */\n    char            version[RADIO_STRING_LEN_MAX];  /* product version */\n    char            serial[RADIO_STRING_LEN_MAX];  /* serial number (for subscription services) */\n    uint32_t        num_tuners;     /* number of tuners controllable independently */\n    uint32_t        num_audio_sources; /* number of audio sources driven simultaneously */\n    bool            supports_capture; /* the hardware supports capture of audio source audio HAL */\n    uint32_t        num_bands;      /* number of band descriptors */\n    radio_hal_band_config_t bands[RADIO_NUM_BANDS_MAX]; /* band descriptors */\n} radio_hal_properties_t;\n\n/* Used internally by the framework. Same information as in struct radio_hal_properties plus a\n * unique handle and one band configuration per region. */\ntypedef struct radio_properties {\n    radio_handle_t      handle;\n    radio_class_t       class_id;\n    char                implementor[RADIO_STRING_LEN_MAX];\n    char                product[RADIO_STRING_LEN_MAX];\n    char                version[RADIO_STRING_LEN_MAX];\n    char                serial[RADIO_STRING_LEN_MAX];\n    uint32_t            num_tuners;\n    uint32_t            num_audio_sources;\n    bool                supports_capture;\n    uint32_t            num_bands;\n    radio_band_config_t bands[RADIO_NUM_BANDS_MAX];\n} radio_properties_t;\n\n/* Radio program information. Returned by the HAL with event RADIO_EVENT_TUNED.\n * Contains information on currently tuned channel.\n */\ntypedef struct radio_program_info {\n    uint32_t         channel;   /* current channel. (e.g kHz for band type RADIO_BAND_FM) */\n    uint32_t         sub_channel; /* current sub channel. (used for RADIO_BAND_FM_HD) */\n    bool             tuned;     /* tuned to a program or not */\n    bool             stereo;    /* program is stereo or not */\n    bool             digital;   /* digital program or not (e.g HD Radio program) */\n    uint32_t         signal_strength; /* signal strength from 0 to 100 */\n                                /* meta data (e.g PTY, song title ...), must not be NULL */\n    __attribute__((aligned(8))) radio_metadata_t *metadata;\n} radio_program_info_t;\n\n\n/* Events sent to the framework via the HAL callback. An event can notify the completion of an\n * asynchronous command (configuration, tune, scan ...) or a spontaneous change (antenna connection,\n * failure, AF switching, meta data reception... */\nenum {\n    RADIO_EVENT_HW_FAILURE  = 0,  /* hardware module failure. Requires reopening the tuner */\n    RADIO_EVENT_CONFIG      = 1,  /* configuration change completed */\n    RADIO_EVENT_ANTENNA     = 2,  /* Antenna connected, disconnected */\n    RADIO_EVENT_TUNED       = 3,  /* tune, step, scan completed */\n    RADIO_EVENT_METADATA    = 4,  /* New meta data received */\n    RADIO_EVENT_TA          = 5,  /* Traffic announcement start or stop */\n    RADIO_EVENT_AF_SWITCH   = 6,  /* Switch to Alternate Frequency */\n    RADIO_EVENT_EA          = 7,  /* Emergency announcement start or stop */\n    // begin framework only events\n    RADIO_EVENT_CONTROL     = 100, /* loss/gain of tuner control */\n    RADIO_EVENT_SERVER_DIED = 101, /* radio service died */\n};\ntypedef unsigned int radio_event_type_t;\n\n/* Event passed to the framework by the HAL callback */\ntypedef struct radio_hal_event {\n    radio_event_type_t  type;       /* event type */\n    int32_t             status;     /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */\n    union {\n        /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA, RADIO_EVENT_EA */\n        bool                    on;\n        radio_hal_band_config_t config; /* RADIO_EVENT_CONFIG */\n        radio_program_info_t    info;   /* RADIO_EVENT_TUNED, RADIO_EVENT_AF_SWITCH */\n        radio_metadata_t        *metadata; /* RADIO_EVENT_METADATA */\n    };\n} radio_hal_event_t;\n\n/* Used internally by the framework. Same information as in struct radio_hal_event */\ntypedef struct radio_event {\n    radio_event_type_t  type;\n    int32_t             status;\n    union {\n        bool                    on;\n        radio_band_config_t     config;\n        radio_program_info_t    info;\n                                /* meta data (e.g PTY, song title ...), must not be NULL */\n        __attribute__((aligned(8))) radio_metadata_t *metadata;\n    };\n} radio_event_t;\n\n\nstatic inline\nradio_rds_t radio_rds_for_region(bool rds, radio_region_t region) {\n    if (!rds)\n        return RADIO_RDS_NONE;\n    switch(region) {\n        case RADIO_REGION_ITU_1:\n        case RADIO_REGION_OIRT:\n        case RADIO_REGION_JAPAN:\n        case RADIO_REGION_KOREA:\n            return RADIO_RDS_WORLD;\n        case RADIO_REGION_ITU_2:\n            return RADIO_RDS_US;\n        default:\n            return RADIO_REGION_NONE;\n    }\n}\n\nstatic inline\nradio_deemphasis_t radio_demephasis_for_region(radio_region_t region) {\n    switch(region) {\n        case RADIO_REGION_KOREA:\n        case RADIO_REGION_ITU_2:\n            return RADIO_DEEMPHASIS_75;\n        case RADIO_REGION_ITU_1:\n        case RADIO_REGION_OIRT:\n        case RADIO_REGION_JAPAN:\n        default:\n            return RADIO_DEEMPHASIS_50;\n    }\n}\n\n#endif  // ANDROID_RADIO_H\n"
  },
  {
    "path": "libsystem/include/system/thread_defs.h",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_THREAD_DEFS_H\n#define ANDROID_THREAD_DEFS_H\n\n#include \"graphics.h\"\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\nenum {\n    /*\n     * ***********************************************\n     * ** Keep in sync with android.os.Process.java **\n     * ***********************************************\n     *\n     * This maps directly to the \"nice\" priorities we use in Android.\n     * A thread priority should be chosen inverse-proportionally to\n     * the amount of work the thread is expected to do. The more work\n     * a thread will do, the less favorable priority it should get so that\n     * it doesn't starve the system. Threads not behaving properly might\n     * be \"punished\" by the kernel.\n     * Use the levels below when appropriate. Intermediate values are\n     * acceptable, preferably use the {MORE|LESS}_FAVORABLE constants below.\n     */\n    ANDROID_PRIORITY_LOWEST         =  19,\n\n    /* use for background tasks */\n    ANDROID_PRIORITY_BACKGROUND     =  10,\n\n    /* most threads run at normal priority */\n    ANDROID_PRIORITY_NORMAL         =   0,\n\n    /* threads currently running a UI that the user is interacting with */\n    ANDROID_PRIORITY_FOREGROUND     =  -2,\n\n    /* the main UI thread has a slightly more favorable priority */\n    ANDROID_PRIORITY_DISPLAY        =  -4,\n\n    /* ui service treads might want to run at a urgent display (uncommon) */\n    ANDROID_PRIORITY_URGENT_DISPLAY =  HAL_PRIORITY_URGENT_DISPLAY,\n\n    /* all normal video threads */\n    ANDROID_PRIORITY_VIDEO          = -10,\n\n    /* all normal audio threads */\n    ANDROID_PRIORITY_AUDIO          = -16,\n\n    /* service audio threads (uncommon) */\n    ANDROID_PRIORITY_URGENT_AUDIO   = -19,\n\n    /* should never be used in practice. regular process might not\n     * be allowed to use this level */\n    ANDROID_PRIORITY_HIGHEST        = -20,\n\n    ANDROID_PRIORITY_DEFAULT        = ANDROID_PRIORITY_NORMAL,\n    ANDROID_PRIORITY_MORE_FAVORABLE = -1,\n    ANDROID_PRIORITY_LESS_FAVORABLE = +1,\n};\n\n#if defined(__cplusplus)\n}\n#endif\n\n#endif /* ANDROID_THREAD_DEFS_H */\n"
  },
  {
    "path": "libsysutils/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libsysutils\",\n    vendor_available: true,\n\n    srcs: [\n        \"src/SocketListener.cpp\",\n        \"src/FrameworkListener.cpp\",\n        \"src/NetlinkListener.cpp\",\n        \"src/NetlinkEvent.cpp\",\n        \"src/FrameworkCommand.cpp\",\n        \"src/SocketClient.cpp\",\n        \"src/ServiceManager.cpp\",\n    ],\n\n    logtags: [\"EventLogTags.logtags\"],\n\n    cflags: [\"-Werror\"],\n\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"liblog\",\n    ],\n\n    header_libs: [\n        \"bpf_headers\",\n    ],\n\n    export_include_dirs: [\"include\"],\n\n    apex_available: [\n        \"//apex_available:anyapex\",\n        \"//apex_available:platform\",\n    ],\n    min_sdk_version: \"apex_inherit\",\n}\n\ncc_test {\n    name: \"libsysutils_tests\",\n    test_suites: [\"device-tests\"],\n    srcs: [\n        \"src/SocketListener_test.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"libsysutils\",\n    ],\n}\n"
  },
  {
    "path": "libsysutils/EventLogTags.logtags",
    "content": "# See system/logging/logcat/event.logtags for a description of the format of this file.\n\n# FrameworkListener dispatchCommand overflow\n78001 exp_det_dispatchCommand_overflow\n65537 exp_det_netlink_failure (uid|1)\n"
  },
  {
    "path": "libsysutils/include/sysutils/FrameworkCommand.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef __FRAMEWORK_CMD_HANDLER_H\n#define __FRAMEWORK_CMD_HANDLER_H\n\nclass SocketClient;\n\nclass FrameworkCommand { \nprivate:\n    const char *mCommand;\n\npublic:\n\n    FrameworkCommand(const char *cmd);\n    virtual ~FrameworkCommand() { }\n\n    virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;\n\n    const char* getCommand() const { return mCommand; }\n};\n\n#endif\n"
  },
  {
    "path": "libsysutils/include/sysutils/FrameworkListener.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _FRAMEWORKSOCKETLISTENER_H\n#define _FRAMEWORKSOCKETLISTENER_H\n\n#include \"SocketListener.h\"\n\n#include <vector>\n\nclass FrameworkCommand;\nclass SocketClient;\n\nclass FrameworkListener : public SocketListener {\npublic:\n    static const int CMD_ARGS_MAX = 26;\n\n    /* 1 out of errorRate will be dropped */\n    int errorRate;\n\nprivate:\n    int mCommandCount;\n    bool mWithSeq;\n    std::vector<FrameworkCommand*> mCommands;\n    bool mSkipToNextNullByte;\n\npublic:\n    FrameworkListener(const char *socketName);\n    FrameworkListener(const char *socketName, bool withSeq);\n    FrameworkListener(int sock);\n    ~FrameworkListener() override {}\n\n  protected:\n    void registerCmd(FrameworkCommand *cmd);\n    bool onDataAvailable(SocketClient* c) override;\n\n  private:\n    void dispatchCommand(SocketClient *c, char *data);\n    void init(const char *socketName, bool withSeq);\n};\n#endif\n"
  },
  {
    "path": "libsysutils/include/sysutils/NetlinkEvent.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _NETLINKEVENT_H\n#define _NETLINKEVENT_H\n\n#include <sysutils/NetlinkListener.h>\n\n#define NL_PARAMS_MAX 32\n\nclass NetlinkEvent {\npublic:\n    enum class Action {\n        kUnknown = 0,\n        kAdd = 1,\n        kRemove = 2,\n        kChange = 3,\n        kLinkUp = 4,\n        kLinkDown = 5,\n        kAddressUpdated = 6,\n        kAddressRemoved = 7,\n        kRdnss = 8,\n        kRouteUpdated = 9,\n        kRouteRemoved = 10,\n    };\n\nprivate:\n    int  mSeq;\n    char *mPath;\n    Action mAction;\n    char *mSubsystem;\n    char *mParams[NL_PARAMS_MAX];\n\npublic:\n    NetlinkEvent();\n    virtual ~NetlinkEvent();\n\n    bool decode(char *buffer, int size, int format = NetlinkListener::NETLINK_FORMAT_ASCII);\n    const char *findParam(const char *paramName);\n\n    const char *getSubsystem() { return mSubsystem; }\n    Action getAction() { return mAction; }\n\n    void dump();\n\n protected:\n    bool parseBinaryNetlinkMessage(char *buffer, int size);\n    bool parseAsciiNetlinkMessage(char *buffer, int size);\n    bool parseIfInfoMessage(const struct nlmsghdr *nh);\n    bool parseIfAddrMessage(const struct nlmsghdr *nh);\n    bool parseUlogPacketMessage(const struct nlmsghdr *nh);\n    bool parseNfPacketMessage(struct nlmsghdr *nh);\n    bool parseRtMessage(const struct nlmsghdr *nh);\n    bool parseNdUserOptMessage(const struct nlmsghdr *nh);\n    struct nlattr* findNlAttr(const nlmsghdr* nl, size_t hdrlen, uint16_t attr);\n};\n\n#endif\n"
  },
  {
    "path": "libsysutils/include/sysutils/NetlinkListener.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _NETLINKLISTENER_H\n#define _NETLINKLISTENER_H\n\n#include \"SocketListener.h\"\n\nclass NetlinkEvent;\n\nclass NetlinkListener : public SocketListener {\n    char mBuffer[64 * 1024] __attribute__((aligned(4)));\n    int mFormat;\n\npublic:\n    static const int NETLINK_FORMAT_ASCII = 0;\n    static const int NETLINK_FORMAT_BINARY = 1;\n    static const int NETLINK_FORMAT_BINARY_UNICAST = 2;\n\n#if 1\n    /* temporary version until we can get Motorola to update their\n     * ril.so.  Their prebuilt ril.so is using this private class\n     * so changing the NetlinkListener() constructor breaks their ril.\n     */\n    NetlinkListener(int socket);\n    NetlinkListener(int socket, int format);\n#else\n    NetlinkListener(int socket, int format = NETLINK_FORMAT_ASCII);\n#endif\n    virtual ~NetlinkListener() {}\n\nprotected:\n    virtual bool onDataAvailable(SocketClient *cli);\n    virtual void onEvent(NetlinkEvent *evt) = 0;\n};\n\n#endif\n"
  },
  {
    "path": "libsysutils/include/sysutils/OWNERS",
    "content": "include ../../src/OWNERS\n\n"
  },
  {
    "path": "libsysutils/include/sysutils/ServiceManager.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _SERVICE_MANAGER_H\n#define _SERVICE_MANAGER_H\n\nclass ServiceManager {\npublic:\n    ServiceManager();\n    virtual ~ServiceManager() {}\n\n    int start(const char *name);\n    int stop(const char *name);\n    bool isRunning(const char *name);\n};\n\n#endif\n"
  },
  {
    "path": "libsysutils/include/sysutils/SocketClient.h",
    "content": "#ifndef _SOCKET_CLIENT_H\n#define _SOCKET_CLIENT_H\n\n#include <pthread.h>\n#include <cutils/atomic.h>\n#include <sys/types.h>\n#include <sys/uio.h>\n\nclass SocketClient {\n    int             mSocket;\n    bool            mSocketOwned;\n    pthread_mutex_t mWriteMutex;\n\n    // Peer process ID\n    pid_t mPid;\n\n    // Peer user ID\n    uid_t mUid;\n\n    // Peer group ID\n    gid_t mGid;\n\n    // Reference count (starts at 1)\n    pthread_mutex_t mRefCountMutex;\n    int mRefCount;\n\n    int mCmdNum;\n\n    bool mUseCmdNum;\n\npublic:\n    SocketClient(int sock, bool owned);\n    SocketClient(int sock, bool owned, bool useCmdNum);\n    virtual ~SocketClient();\n\n    int getSocket() const { return mSocket; }\n    pid_t getPid() const { return mPid; }\n    uid_t getUid() const { return mUid; }\n    gid_t getGid() const { return mGid; }\n    void setCmdNum(int cmdNum) {\n        android_atomic_release_store(cmdNum, &mCmdNum);\n    }\n    int getCmdNum() { return mCmdNum; }\n\n    // Send null-terminated C strings:\n    int sendMsg(int code, const char *msg, bool addErrno);\n    int sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum);\n    int sendMsg(const char *msg);\n\n    // Provides a mechanism to send a response code to the client.\n    // Sends the code and a null character.\n    int sendCode(int code);\n\n    // Provides a mechanism to send binary data to client.\n    // Sends the code and a null character, followed by 4 bytes of\n    // big-endian length, and the data.\n    int sendBinaryMsg(int code, const void *data, int len);\n\n    // Sending binary data:\n    int sendData(const void *data, int len);\n    // iovec contents not preserved through call\n    int sendDatav(struct iovec *iov, int iovcnt);\n\n    // Optional reference counting.  Reference count starts at 1.  If\n    // it's decremented to 0, it deletes itself.\n    // SocketListener creates a SocketClient (at refcount 1) and calls\n    // decRef() when it's done with the client.\n    void incRef();\n    bool decRef(); // returns true at 0 (but note: SocketClient already deleted)\n\n    // return a new string in quotes with '\\\\' and '\\\"' escaped for \"my arg\"\n    // transmissions\n    static char *quoteArg(const char *arg);\n\nprivate:\n    void init(int socket, bool owned, bool useCmdNum);\n\n    // Sending binary data. The caller should make sure this is protected\n    // from multiple threads entering simultaneously.\n    // returns 0 if successful, -1 if there is a 0 byte write or if any\n    // other error occurred (use errno to get the error)\n    int sendDataLockedv(struct iovec *iov, int iovcnt);\n};\n\n#endif\n"
  },
  {
    "path": "libsysutils/include/sysutils/SocketClientCommand.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _SOCKETCLIENTCOMMAND_H\n#define _SOCKETCLIENTCOMMAND_H\n\n#include <sysutils/SocketClient.h>\n\nclass SocketClientCommand {\npublic:\n    virtual ~SocketClientCommand() { }\n    virtual void runSocketCommand(SocketClient *client) = 0;\n};\n\n#endif\n"
  },
  {
    "path": "libsysutils/include/sysutils/SocketListener.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _SOCKETLISTENER_H\n#define _SOCKETLISTENER_H\n\n#include <pthread.h>\n\n#include <unordered_map>\n#include <vector>\n\n#include <sysutils/SocketClient.h>\n#include \"SocketClientCommand.h\"\n\nclass SocketListener {\n    bool                    mListen;\n    const char              *mSocketName;\n    int                     mSock;\n    std::unordered_map<int, SocketClient*> mClients;\n    pthread_mutex_t         mClientsLock;\n    int                     mCtrlPipe[2];\n    pthread_t               mThread;\n    bool                    mUseCmdNum;\n\npublic:\n    SocketListener(const char *socketName, bool listen);\n    SocketListener(const char *socketName, bool listen, bool useCmdNum);\n    SocketListener(int socketFd, bool listen);\n\n    virtual ~SocketListener();\n    int startListener();\n    int startListener(int backlog);\n    int stopListener();\n\n    void sendBroadcast(int code, const char *msg, bool addErrno);\n\n    void runOnEachSocket(SocketClientCommand *command);\n\n    bool release(SocketClient *c) { return release(c, true); }\n\nprotected:\n    virtual bool onDataAvailable(SocketClient *c) = 0;\n\nprivate:\n    static void *threadStart(void *obj);\n\n    // Add all clients to a separate list, so we don't have to hold the lock\n    // while processing it.\n    std::vector<SocketClient*> snapshotClients();\n\n    bool release(SocketClient *c, bool wakeup);\n    void runListener();\n    void init(const char *socketName, int socketFd, bool listen, bool useCmdNum);\n};\n#endif\n"
  },
  {
    "path": "libsysutils/src/FrameworkCommand.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"FrameworkCommand\"\n\n#include <errno.h>\n\n#include <log/log.h>\n#include <sysutils/FrameworkCommand.h>\n\n#define UNUSED __attribute__((unused))\n\nFrameworkCommand::FrameworkCommand(const char *cmd) {\n    mCommand = cmd;\n}\n\nint FrameworkCommand::runCommand(SocketClient *c UNUSED, int argc UNUSED,\n                                 char **argv UNUSED) {\n    SLOGW(\"Command %s has no run handler!\", getCommand());\n    errno = ENOSYS;\n    return -1;\n}\n"
  },
  {
    "path": "libsysutils/src/FrameworkListener.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"FrameworkListener\"\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <log/log.h>\n#include <sysutils/FrameworkCommand.h>\n#include <sysutils/FrameworkListener.h>\n#include <sysutils/SocketClient.h>\n\nstatic const int CMD_BUF_SIZE = 4096;\n\nFrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :\n                            SocketListener(socketName, true, withSeq) {\n    init(socketName, withSeq);\n}\n\nFrameworkListener::FrameworkListener(const char *socketName) :\n                            SocketListener(socketName, true, false) {\n    init(socketName, false);\n}\n\nFrameworkListener::FrameworkListener(int sock) :\n                            SocketListener(sock, true) {\n    init(nullptr, false);\n}\n\nvoid FrameworkListener::init(const char* /*socketName*/, bool withSeq) {\n    errorRate = 0;\n    mCommandCount = 0;\n    mWithSeq = withSeq;\n    mSkipToNextNullByte = false;\n}\n\nbool FrameworkListener::onDataAvailable(SocketClient *c) {\n    char buffer[CMD_BUF_SIZE];\n    int len;\n\n    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));\n    if (len < 0) {\n        SLOGE(\"read() failed (%s)\", strerror(errno));\n        return false;\n    } else if (!len) {\n        return false;\n    } else if (buffer[len-1] != '\\0') {\n        SLOGW(\"String is not zero-terminated\");\n        android_errorWriteLog(0x534e4554, \"29831647\");\n        c->sendMsg(500, \"Command too large for buffer\", false);\n        mSkipToNextNullByte = true;\n        return true;\n    }\n\n    int offset = 0;\n    int i;\n\n    for (i = 0; i < len; i++) {\n        if (buffer[i] == '\\0') {\n            /* IMPORTANT: dispatchCommand() expects a zero-terminated string */\n            if (mSkipToNextNullByte) {\n                mSkipToNextNullByte = false;\n            } else {\n                dispatchCommand(c, buffer + offset);\n            }\n            offset = i + 1;\n        }\n    }\n\n    mSkipToNextNullByte = false;\n    return true;\n}\n\nvoid FrameworkListener::registerCmd(FrameworkCommand *cmd) {\n    mCommands.push_back(cmd);\n}\n\nvoid FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {\n    int argc = 0;\n    char *argv[FrameworkListener::CMD_ARGS_MAX];\n    char tmp[CMD_BUF_SIZE];\n    char *p = data;\n    char *q = tmp;\n    char *qlimit = tmp + sizeof(tmp) - 1;\n    bool esc = false;\n    bool quote = false;\n    bool haveCmdNum = !mWithSeq;\n\n    memset(argv, 0, sizeof(argv));\n    memset(tmp, 0, sizeof(tmp));\n    while(*p) {\n        if (*p == '\\\\') {\n            if (esc) {\n                if (q >= qlimit)\n                    goto overflow;\n                *q++ = '\\\\';\n                esc = false;\n            } else\n                esc = true;\n            p++;\n            continue;\n        } else if (esc) {\n            if (*p == '\"') {\n                if (q >= qlimit)\n                    goto overflow;\n                *q++ = '\"';\n            } else if (*p == '\\\\') {\n                if (q >= qlimit)\n                    goto overflow;\n                *q++ = '\\\\';\n            } else {\n                cli->sendMsg(500, \"Unsupported escape sequence\", false);\n                goto out;\n            }\n            p++;\n            esc = false;\n            continue;\n        }\n\n        if (*p == '\"') {\n            if (quote)\n                quote = false;\n            else\n                quote = true;\n            p++;\n            continue;\n        }\n\n        if (q >= qlimit)\n            goto overflow;\n        *q = *p++;\n        if (!quote && *q == ' ') {\n            *q = '\\0';\n            if (!haveCmdNum) {\n                char *endptr;\n                int cmdNum = (int)strtol(tmp, &endptr, 0);\n                if (endptr == nullptr || *endptr != '\\0') {\n                    cli->sendMsg(500, \"Invalid sequence number\", false);\n                    goto out;\n                }\n                cli->setCmdNum(cmdNum);\n                haveCmdNum = true;\n            } else {\n                if (argc >= CMD_ARGS_MAX)\n                    goto overflow;\n                argv[argc++] = strdup(tmp);\n            }\n            memset(tmp, 0, sizeof(tmp));\n            q = tmp;\n            continue;\n        }\n        q++;\n    }\n\n    *q = '\\0';\n    if (argc >= CMD_ARGS_MAX)\n        goto overflow;\n    argv[argc++] = strdup(tmp);\n#if 0\n    for (int k = 0; k < argc; k++) {\n        SLOGD(\"arg[%d] = '%s'\", k, argv[k]);\n    }\n#endif\n\n    if (quote) {\n        cli->sendMsg(500, \"Unclosed quotes error\", false);\n        goto out;\n    }\n\n    if (errorRate && (++mCommandCount % errorRate == 0)) {\n        /* ignore this command - let the timeout handler handle it */\n        SLOGE(\"Faking a timeout\");\n        goto out;\n    }\n\n    for (auto* c : mCommands) {\n        if (!strcmp(argv[0], c->getCommand())) {\n            if (c->runCommand(cli, argc, argv)) {\n                SLOGW(\"Handler '%s' error (%s)\", c->getCommand(), strerror(errno));\n            }\n            goto out;\n        }\n    }\n    cli->sendMsg(500, \"Command not recognized\", false);\nout:\n    int j;\n    for (j = 0; j < argc; j++)\n        free(argv[j]);\n    return;\n\noverflow:\n    cli->sendMsg(500, \"Command too long\", false);\n    goto out;\n}\n"
  },
  {
    "path": "libsysutils/src/NetlinkEvent.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"NetlinkEvent\"\n\n#include <arpa/inet.h>\n#include <limits.h>\n#include <linux/genetlink.h>\n#include <linux/if.h>\n#include <linux/if_addr.h>\n#include <linux/if_link.h>\n#include <linux/netfilter/nfnetlink.h>\n#include <linux/netfilter/nfnetlink_log.h>\n#include <linux/netlink.h>\n#include <linux/rtnetlink.h>\n#include <net/if.h>\n#include <netinet/icmp6.h>\n#include <netinet/in.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/personality.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <sys/utsname.h>\n\n#include <android-base/parseint.h>\n#include <log/log.h>\n#include <sysutils/NetlinkEvent.h>\n\nusing android::base::ParseInt;\n\n// 'long' on a 32-bit kernel is 32-bits with 32-bit alignment,\n// and on a 64-bit kernel is 64-bits with 64-bit alignment,\n// while 'long long' is always 64-bit it may have 32-bit aligment (x86 structs).\ntypedef long long __attribute__((__aligned__(8))) long64;\ntypedef unsigned long long __attribute__((__aligned__(8))) ulong64;\nstatic_assert(sizeof(long64) == 8);\nstatic_assert(sizeof(ulong64) == 8);\n\n// From kernel's net/netfilter/xt_quota2.c\n// It got there from deprecated ipt_ULOG.h to parse QLOG_NL_EVENT.\nconstexpr int LOCAL_QLOG_NL_EVENT = 112;\nconstexpr int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;\n\nconstexpr int ULOG_MAC_LEN = 80;\nconstexpr int ULOG_PREFIX_LEN = 32;\n\n// This structure layout assumes we're running on a 64-bit kernel.\ntypedef struct {\n    ulong64 mark;  // kernel: unsigned long\n    long64 timestamp_sec;  // kernel: long\n    long64 timestamp_usec;  // kernel: long\n    unsigned int hook;\n    char indev_name[IFNAMSIZ];\n    char outdev_name[IFNAMSIZ];\n    ulong64 data_len;  // kernel: size_t, a.k.a. unsigned long\n    char prefix[ULOG_PREFIX_LEN];\n    unsigned char mac_len;\n    unsigned char mac[ULOG_MAC_LEN];\n    unsigned char payload[0];\n} ulog_packet_msg_t;\n\n// In practice, for both x86 and arm, we have\nstatic_assert(sizeof(ulog_packet_msg_t) == 192);\n\n/******************************************************************************/\n\nNetlinkEvent::NetlinkEvent() {\n    mAction = Action::kUnknown;\n    memset(mParams, 0, sizeof(mParams));\n    mPath = nullptr;\n    mSubsystem = nullptr;\n}\n\nNetlinkEvent::~NetlinkEvent() {\n    free(mPath);\n    free(mSubsystem);\n    for (auto param : mParams) {\n        free(param);\n    }\n}\n\nvoid NetlinkEvent::dump() {\n    int i;\n\n    for (i = 0; i < NL_PARAMS_MAX; i++) {\n        if (!mParams[i])\n            break;\n        SLOGD(\"NL param '%s'\\n\", mParams[i]);\n    }\n}\n\n/*\n * Returns the message name for a message in the NETLINK_ROUTE family, or NULL\n * if parsing that message is not supported.\n */\nstatic const char *rtMessageName(int type) {\n#define NL_EVENT_RTM_NAME(rtm) case rtm: return #rtm;\n    switch (type) {\n        NL_EVENT_RTM_NAME(RTM_NEWLINK);\n        NL_EVENT_RTM_NAME(RTM_DELLINK);\n        NL_EVENT_RTM_NAME(RTM_NEWADDR);\n        NL_EVENT_RTM_NAME(RTM_DELADDR);\n        NL_EVENT_RTM_NAME(RTM_NEWROUTE);\n        NL_EVENT_RTM_NAME(RTM_DELROUTE);\n        NL_EVENT_RTM_NAME(RTM_NEWNDUSEROPT);\n        NL_EVENT_RTM_NAME(LOCAL_QLOG_NL_EVENT);\n        NL_EVENT_RTM_NAME(LOCAL_NFLOG_PACKET);\n        default:\n            return nullptr;\n    }\n#undef NL_EVENT_RTM_NAME\n}\n\n/*\n * Checks that a binary NETLINK_ROUTE message is long enough for a payload of\n * size bytes.\n */\nstatic bool checkRtNetlinkLength(const struct nlmsghdr *nh, size_t size) {\n    if (nh->nlmsg_len < NLMSG_LENGTH(size)) {\n        SLOGE(\"Got a short %s message\\n\", rtMessageName(nh->nlmsg_type));\n        return false;\n    }\n    return true;\n}\n\n/*\n * Utility function to log errors.\n */\nstatic bool maybeLogDuplicateAttribute(bool isDup,\n                                       const char *attributeName,\n                                       const char *messageName) {\n    if (isDup) {\n        SLOGE(\"Multiple %s attributes in %s, ignoring\\n\", attributeName, messageName);\n        return true;\n    }\n    return false;\n}\n\n/*\n * Parse a RTM_NEWLINK message.\n */\nbool NetlinkEvent::parseIfInfoMessage(const struct nlmsghdr *nh) {\n    struct ifinfomsg *ifi = (struct ifinfomsg *) NLMSG_DATA(nh);\n    if (!checkRtNetlinkLength(nh, sizeof(*ifi)))\n        return false;\n\n    if ((ifi->ifi_flags & IFF_LOOPBACK) != 0) {\n        return false;\n    }\n\n    int len = IFLA_PAYLOAD(nh);\n    struct rtattr *rta;\n    for (rta = IFLA_RTA(ifi); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n        switch(rta->rta_type) {\n            case IFLA_IFNAME:\n                asprintf(&mParams[0], \"INTERFACE=%s\", (char *) RTA_DATA(rta));\n                // We can get the interface change information from sysfs update\n                // already. But in case we missed those message when devices start.\n                // We do a update again when received a kLinkUp event. To make\n                // the message consistent, use IFINDEX here as well since sysfs\n                // uses IFINDEX.\n                asprintf(&mParams[1], \"IFINDEX=%d\", ifi->ifi_index);\n                mAction = (ifi->ifi_flags & IFF_LOWER_UP) ? Action::kLinkUp :\n                                                            Action::kLinkDown;\n                mSubsystem = strdup(\"net\");\n                return true;\n        }\n    }\n\n    return false;\n}\n\n/*\n * Parse a RTM_NEWADDR or RTM_DELADDR message.\n */\nbool NetlinkEvent::parseIfAddrMessage(const struct nlmsghdr *nh) {\n    struct ifaddrmsg *ifaddr = (struct ifaddrmsg *) NLMSG_DATA(nh);\n    struct ifa_cacheinfo *cacheinfo = nullptr;\n    char addrstr[INET6_ADDRSTRLEN] = \"\";\n    char ifname[IFNAMSIZ] = \"\";\n    uint32_t flags;\n\n    if (!checkRtNetlinkLength(nh, sizeof(*ifaddr)))\n        return false;\n\n    int type = nh->nlmsg_type;\n    if (type != RTM_NEWADDR && type != RTM_DELADDR) {\n        SLOGE(\"parseIfAddrMessage on incorrect message type 0x%x\\n\", type);\n        return false;\n    }\n\n    // For log messages.\n    const char *msgtype = rtMessageName(type);\n\n    // First 8 bits of flags. In practice will always be overridden when parsing IFA_FLAGS below.\n    flags = ifaddr->ifa_flags;\n\n    struct rtattr *rta;\n    int len = IFA_PAYLOAD(nh);\n    for (rta = IFA_RTA(ifaddr); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n        if (rta->rta_type == IFA_ADDRESS) {\n            // Only look at the first address, because we only support notifying\n            // one change at a time.\n            if (maybeLogDuplicateAttribute(*addrstr != '\\0', \"IFA_ADDRESS\", msgtype))\n                continue;\n\n            // Convert the IP address to a string.\n            if (ifaddr->ifa_family == AF_INET) {\n                struct in_addr *addr4 = (struct in_addr *) RTA_DATA(rta);\n                if (RTA_PAYLOAD(rta) < sizeof(*addr4)) {\n                    SLOGE(\"Short IPv4 address (%zu bytes) in %s\",\n                          RTA_PAYLOAD(rta), msgtype);\n                    continue;\n                }\n                inet_ntop(AF_INET, addr4, addrstr, sizeof(addrstr));\n            } else if (ifaddr->ifa_family == AF_INET6) {\n                struct in6_addr *addr6 = (struct in6_addr *) RTA_DATA(rta);\n                if (RTA_PAYLOAD(rta) < sizeof(*addr6)) {\n                    SLOGE(\"Short IPv6 address (%zu bytes) in %s\",\n                          RTA_PAYLOAD(rta), msgtype);\n                    continue;\n                }\n                inet_ntop(AF_INET6, addr6, addrstr, sizeof(addrstr));\n            } else {\n                SLOGE(\"Unknown address family %d\\n\", ifaddr->ifa_family);\n                continue;\n            }\n\n            // Find the interface name.\n            if (!if_indextoname(ifaddr->ifa_index, ifname)) {\n                SLOGD(\"Unknown ifindex %d in %s\", ifaddr->ifa_index, msgtype);\n            }\n\n        } else if (rta->rta_type == IFA_CACHEINFO) {\n            // Address lifetime information.\n            if (maybeLogDuplicateAttribute(cacheinfo, \"IFA_CACHEINFO\", msgtype))\n                continue;\n\n            if (RTA_PAYLOAD(rta) < sizeof(*cacheinfo)) {\n                SLOGE(\"Short IFA_CACHEINFO (%zu vs. %zu bytes) in %s\",\n                      RTA_PAYLOAD(rta), sizeof(cacheinfo), msgtype);\n                continue;\n            }\n\n            cacheinfo = (struct ifa_cacheinfo *) RTA_DATA(rta);\n\n        } else if (rta->rta_type == IFA_FLAGS) {\n            flags = *(uint32_t*)RTA_DATA(rta);\n        }\n    }\n\n    if (addrstr[0] == '\\0') {\n        SLOGE(\"No IFA_ADDRESS in %s\\n\", msgtype);\n        return false;\n    }\n\n    // Fill in netlink event information.\n    mAction = (type == RTM_NEWADDR) ? Action::kAddressUpdated :\n                                      Action::kAddressRemoved;\n    mSubsystem = strdup(\"net\");\n    asprintf(&mParams[0], \"ADDRESS=%s/%d\", addrstr, ifaddr->ifa_prefixlen);\n    asprintf(&mParams[1], \"INTERFACE=%s\", ifname);\n    asprintf(&mParams[2], \"FLAGS=%u\", flags);\n    asprintf(&mParams[3], \"SCOPE=%u\", ifaddr->ifa_scope);\n    asprintf(&mParams[4], \"IFINDEX=%u\", ifaddr->ifa_index);\n\n    if (cacheinfo) {\n        asprintf(&mParams[5], \"PREFERRED=%u\", cacheinfo->ifa_prefered);\n        asprintf(&mParams[6], \"VALID=%u\", cacheinfo->ifa_valid);\n        asprintf(&mParams[7], \"CSTAMP=%u\", cacheinfo->cstamp);\n        asprintf(&mParams[8], \"TSTAMP=%u\", cacheinfo->tstamp);\n    }\n\n    return true;\n}\n\n/*\n * Parse a QLOG_NL_EVENT message.\n */\nbool NetlinkEvent::parseUlogPacketMessage(const struct nlmsghdr *nh) {\n    ulog_packet_msg_t* pm = (ulog_packet_msg_t*)NLMSG_DATA(nh);\n    if (!checkRtNetlinkLength(nh, sizeof(*pm))) return false;\n\n    const char* alert = pm->prefix;\n    const char* devname = pm->indev_name[0] ? pm->indev_name : pm->outdev_name;\n\n    asprintf(&mParams[0], \"ALERT_NAME=%s\", alert);\n    asprintf(&mParams[1], \"INTERFACE=%s\", devname);\n    mSubsystem = strdup(\"qlog\");\n    mAction = Action::kChange;\n    return true;\n}\n\nstatic size_t nlAttrLen(const nlattr* nla) {\n    return nla->nla_len - NLA_HDRLEN;\n}\n\nstatic const uint8_t* nlAttrData(const nlattr* nla) {\n    return reinterpret_cast<const uint8_t*>(nla) + NLA_HDRLEN;\n}\n\nstatic uint32_t nlAttrU32(const nlattr* nla) {\n    return *reinterpret_cast<const uint32_t*>(nlAttrData(nla));\n}\n\n/*\n * Parse a LOCAL_NFLOG_PACKET message.\n */\nbool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) {\n    int uid = -1;\n    int len = 0;\n    char* raw = nullptr;\n\n    struct nlattr* uid_attr = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_UID);\n    if (uid_attr) {\n        uid = ntohl(nlAttrU32(uid_attr));\n    }\n\n    struct nlattr* payload = findNlAttr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);\n    if (payload) {\n        /* First 256 bytes is plenty */\n        len = nlAttrLen(payload);\n        if (len > 256) len = 256;\n        raw = (char*)nlAttrData(payload);\n    }\n\n    size_t hexSize = 5 + (len * 2);\n    char* hex = (char*)calloc(1, hexSize);\n    strlcpy(hex, \"HEX=\", hexSize);\n    for (int i = 0; i < len; i++) {\n        hex[4 + (i * 2)] = \"0123456789abcdef\"[(raw[i] >> 4) & 0xf];\n        hex[5 + (i * 2)] = \"0123456789abcdef\"[raw[i] & 0xf];\n    }\n\n    asprintf(&mParams[0], \"UID=%d\", uid);\n    mParams[1] = hex;\n    mSubsystem = strdup(\"strict\");\n    mAction = Action::kChange;\n    return true;\n}\n\n/*\n * Parse a RTM_NEWROUTE or RTM_DELROUTE message.\n */\nbool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) {\n    uint8_t type = nh->nlmsg_type;\n    const char *msgname = rtMessageName(type);\n\n    if (type != RTM_NEWROUTE && type != RTM_DELROUTE) {\n        SLOGE(\"%s: incorrect message type %d (%s)\\n\", __func__, type, msgname);\n        return false;\n    }\n\n    struct rtmsg *rtm = (struct rtmsg *) NLMSG_DATA(nh);\n    if (!checkRtNetlinkLength(nh, sizeof(*rtm)))\n        return false;\n\n    if (// Ignore static routes we've set up ourselves.\n        (rtm->rtm_protocol != RTPROT_KERNEL &&\n         rtm->rtm_protocol != RTPROT_RA) ||\n        // We're only interested in global unicast routes.\n        (rtm->rtm_scope != RT_SCOPE_UNIVERSE) ||\n        (rtm->rtm_type != RTN_UNICAST) ||\n        // We don't support source routing.\n        (rtm->rtm_src_len != 0) ||\n        // Cloned routes aren't real routes.\n        (rtm->rtm_flags & RTM_F_CLONED)) {\n        return false;\n    }\n\n    int family = rtm->rtm_family;\n    int prefixLength = rtm->rtm_dst_len;\n\n    // Currently we only support: destination, (one) next hop, ifindex.\n    char dst[INET6_ADDRSTRLEN] = \"\";\n    char gw[INET6_ADDRSTRLEN] = \"\";\n    char dev[IFNAMSIZ] = \"\";\n\n    size_t len = RTM_PAYLOAD(nh);\n    struct rtattr *rta;\n    for (rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {\n        switch (rta->rta_type) {\n            case RTA_DST:\n                if (maybeLogDuplicateAttribute(*dst, \"RTA_DST\", msgname))\n                    continue;\n                if (!inet_ntop(family, RTA_DATA(rta), dst, sizeof(dst)))\n                    return false;\n                continue;\n            case RTA_GATEWAY:\n                if (maybeLogDuplicateAttribute(*gw, \"RTA_GATEWAY\", msgname))\n                    continue;\n                if (!inet_ntop(family, RTA_DATA(rta), gw, sizeof(gw)))\n                    return false;\n                continue;\n            case RTA_OIF:\n                if (maybeLogDuplicateAttribute(*dev, \"RTA_OIF\", msgname))\n                    continue;\n                if (!if_indextoname(* (int *) RTA_DATA(rta), dev))\n                    return false;\n                continue;\n            default:\n                continue;\n        }\n    }\n\n   // If there's no RTA_DST attribute, then:\n   // - If the prefix length is zero, it's the default route.\n   // - If the prefix length is nonzero, there's something we don't understand.\n   //   Ignore the event.\n   if (!*dst && !prefixLength) {\n        if (family == AF_INET) {\n            strncpy(dst, \"0.0.0.0\", sizeof(dst));\n        } else if (family == AF_INET6) {\n            strncpy(dst, \"::\", sizeof(dst));\n        }\n    }\n\n    // A useful route must have a destination and at least either a gateway or\n    // an interface.\n    if (!*dst || (!*gw && !*dev))\n        return false;\n\n    // Fill in netlink event information.\n    mAction = (type == RTM_NEWROUTE) ? Action::kRouteUpdated :\n                                       Action::kRouteRemoved;\n    mSubsystem = strdup(\"net\");\n    asprintf(&mParams[0], \"ROUTE=%s/%d\", dst, prefixLength);\n    asprintf(&mParams[1], \"GATEWAY=%s\", (*gw) ? gw : \"\");\n    asprintf(&mParams[2], \"INTERFACE=%s\", (*dev) ? dev : \"\");\n\n    return true;\n}\n\n/*\n * Parse a RTM_NEWNDUSEROPT message.\n */\nbool NetlinkEvent::parseNdUserOptMessage(const struct nlmsghdr *nh) {\n    struct nduseroptmsg *msg = (struct nduseroptmsg *) NLMSG_DATA(nh);\n    if (!checkRtNetlinkLength(nh, sizeof(*msg)))\n        return false;\n\n    // Check the length is valid.\n    int len = NLMSG_PAYLOAD(nh, sizeof(*msg));\n    if (msg->nduseropt_opts_len > len) {\n        SLOGE(\"RTM_NEWNDUSEROPT invalid length %d > %d\\n\",\n              msg->nduseropt_opts_len, len);\n        return false;\n    }\n    len = msg->nduseropt_opts_len;\n\n    // Check address family and packet type.\n    if (msg->nduseropt_family != AF_INET6) {\n        SLOGE(\"RTM_NEWNDUSEROPT message for unknown family %d\\n\",\n              msg->nduseropt_family);\n        return false;\n    }\n\n    if (msg->nduseropt_icmp_type != ND_ROUTER_ADVERT ||\n        msg->nduseropt_icmp_code != 0) {\n        SLOGE(\"RTM_NEWNDUSEROPT message for unknown ICMPv6 type/code %d/%d\\n\",\n              msg->nduseropt_icmp_type, msg->nduseropt_icmp_code);\n        return false;\n    }\n\n    // Find the interface name.\n    char ifname[IFNAMSIZ];\n    if (!if_indextoname(msg->nduseropt_ifindex, ifname)) {\n        SLOGE(\"RTM_NEWNDUSEROPT on unknown ifindex %d\\n\",\n              msg->nduseropt_ifindex);\n        return false;\n    }\n\n    // The kernel sends a separate netlink message for each ND option in the RA.\n    // So only parse the first ND option in the message.\n    struct nd_opt_hdr *opthdr = (struct nd_opt_hdr *) (msg + 1);\n\n    // The length is in multiples of 8 octets.\n    uint16_t optlen = opthdr->nd_opt_len;\n    if (optlen * 8 > len) {\n        SLOGE(\"Invalid option length %d > %d for ND option %d\\n\",\n              optlen * 8, len, opthdr->nd_opt_type);\n        return false;\n    }\n\n    if (opthdr->nd_opt_type == ND_OPT_RDNSS) {\n        // DNS Servers (RFC 6106).\n        // Each address takes up 2*8 octets, and the header takes up 8 octets.\n        // So for a valid option with one or more addresses, optlen must be\n        // odd and greater than 1.\n        if ((optlen < 3) || !(optlen & 0x1)) {\n            SLOGE(\"Invalid optlen %d for RDNSS option\\n\", optlen);\n            return false;\n        }\n        const int numaddrs = (optlen - 1) / 2;\n\n        // Find the lifetime.\n        struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;\n        const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);\n\n        // Construct a comma-separated string of DNS addresses.\n        // Reserve sufficient space for an IPv6 link-local address: all but the\n        // last address are followed by ','; the last is followed by '\\0'.\n        static const size_t kMaxSingleAddressLength =\n                INET6_ADDRSTRLEN + strlen(\"%\") + IFNAMSIZ + strlen(\",\");\n        const size_t bufsize = numaddrs * kMaxSingleAddressLength;\n        char *buf = (char *) malloc(bufsize);\n        if (!buf) {\n            SLOGE(\"RDNSS option: out of memory\\n\");\n            return false;\n        }\n\n        struct in6_addr *addrs = (struct in6_addr *) (rndss_opt + 1);\n        size_t pos = 0;\n        for (int i = 0; i < numaddrs; i++) {\n            if (i > 0) {\n                buf[pos++] = ',';\n            }\n            inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);\n            pos += strlen(buf + pos);\n            if (IN6_IS_ADDR_LINKLOCAL(addrs + i)) {\n                buf[pos++] = '%';\n                pos += strlcpy(buf + pos, ifname, bufsize - pos);\n            }\n        }\n        buf[pos] = '\\0';\n\n        mAction = Action::kRdnss;\n        mSubsystem = strdup(\"net\");\n        asprintf(&mParams[0], \"INTERFACE=%s\", ifname);\n        asprintf(&mParams[1], \"LIFETIME=%u\", lifetime);\n        asprintf(&mParams[2], \"SERVERS=%s\", buf);\n        free(buf);\n    } else if (opthdr->nd_opt_type == ND_OPT_DNSSL) {\n        // TODO: support DNSSL.\n    } else if (opthdr->nd_opt_type == ND_OPT_CAPTIVE_PORTAL) {\n        // TODO: support CAPTIVE PORTAL.\n    } else if (opthdr->nd_opt_type == ND_OPT_PREF64) {\n        // TODO: support PREF64.\n    } else {\n        SLOGD(\"Unknown ND option type %d\\n\", opthdr->nd_opt_type);\n        return false;\n    }\n\n    return true;\n}\n\n/*\n * Parse a binary message from a NETLINK_ROUTE netlink socket.\n *\n * Note that this function can only parse one message, because the message's\n * content has to be stored in the class's member variables (mAction,\n * mSubsystem, etc.). Invalid or unrecognized messages are skipped, but if\n * there are multiple valid messages in the buffer, only the first one will be\n * returned.\n *\n * TODO: consider only ever looking at the first message.\n */\nbool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {\n    struct nlmsghdr *nh;\n\n    for (nh = (struct nlmsghdr *) buffer;\n         NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);\n         nh = NLMSG_NEXT(nh, size)) {\n\n        if (!rtMessageName(nh->nlmsg_type)) {\n            SLOGD(\"Unexpected netlink message type %d\\n\", nh->nlmsg_type);\n            continue;\n        }\n\n        if (nh->nlmsg_type == RTM_NEWLINK) {\n            if (parseIfInfoMessage(nh))\n                return true;\n\n        } else if (nh->nlmsg_type == LOCAL_QLOG_NL_EVENT) {\n            if (parseUlogPacketMessage(nh))\n                return true;\n\n        } else if (nh->nlmsg_type == RTM_NEWADDR ||\n                   nh->nlmsg_type == RTM_DELADDR) {\n            if (parseIfAddrMessage(nh))\n                return true;\n\n        } else if (nh->nlmsg_type == RTM_NEWROUTE ||\n                   nh->nlmsg_type == RTM_DELROUTE) {\n            if (parseRtMessage(nh))\n                return true;\n\n        } else if (nh->nlmsg_type == RTM_NEWNDUSEROPT) {\n            if (parseNdUserOptMessage(nh))\n                return true;\n\n        } else if (nh->nlmsg_type == LOCAL_NFLOG_PACKET) {\n            if (parseNfPacketMessage(nh))\n                return true;\n\n        }\n    }\n\n    return false;\n}\n\n/* If the string between 'str' and 'end' begins with 'prefixlen' characters\n * from the 'prefix' array, then return 'str + prefixlen', otherwise return\n * NULL.\n */\nstatic const char*\nhas_prefix(const char* str, const char* end, const char* prefix, size_t prefixlen)\n{\n    if ((end - str) >= (ptrdiff_t)prefixlen &&\n        (prefixlen == 0 || !memcmp(str, prefix, prefixlen))) {\n        return str + prefixlen;\n    } else {\n        return nullptr;\n    }\n}\n\n/* Same as strlen(x) for constant string literals ONLY */\n#define CONST_STRLEN(x)  (sizeof(x)-1)\n\n/* Convenience macro to call has_prefix with a constant string literal  */\n#define HAS_CONST_PREFIX(str,end,prefix)  has_prefix((str),(end),prefix,CONST_STRLEN(prefix))\n\n\n/*\n * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT\n * netlink socket.\n */\nbool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {\n    const char *s = buffer;\n    const char *end;\n    int param_idx = 0;\n    int first = 1;\n\n    if (size == 0)\n        return false;\n\n    /* Ensure the buffer is zero-terminated, the code below depends on this */\n    buffer[size-1] = '\\0';\n\n    end = s + size;\n    while (s < end) {\n        if (first) {\n            const char *p;\n            /* buffer is 0-terminated, no need to check p < end */\n            for (p = s; *p != '@'; p++) {\n                if (!*p) { /* no '@', should not happen */\n                    return false;\n                }\n            }\n            mPath = strdup(p+1);\n            first = 0;\n        } else {\n            const char* a;\n            if ((a = HAS_CONST_PREFIX(s, end, \"ACTION=\")) != nullptr) {\n                if (!strcmp(a, \"add\"))\n                    mAction = Action::kAdd;\n                else if (!strcmp(a, \"remove\"))\n                    mAction = Action::kRemove;\n                else if (!strcmp(a, \"change\"))\n                    mAction = Action::kChange;\n            } else if ((a = HAS_CONST_PREFIX(s, end, \"SEQNUM=\")) != nullptr) {\n                if (!ParseInt(a, &mSeq)) {\n                    SLOGE(\"NetlinkEvent::parseAsciiNetlinkMessage: failed to parse SEQNUM=%s\", a);\n                }\n            } else if ((a = HAS_CONST_PREFIX(s, end, \"SUBSYSTEM=\")) != nullptr) {\n                mSubsystem = strdup(a);\n            } else if (param_idx < NL_PARAMS_MAX) {\n                mParams[param_idx++] = strdup(s);\n            }\n        }\n        s += strlen(s) + 1;\n    }\n    return true;\n}\n\nbool NetlinkEvent::decode(char *buffer, int size, int format) {\n    if (format == NetlinkListener::NETLINK_FORMAT_BINARY\n            || format == NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST) {\n        return parseBinaryNetlinkMessage(buffer, size);\n    } else {\n        return parseAsciiNetlinkMessage(buffer, size);\n    }\n}\n\nconst char *NetlinkEvent::findParam(const char *paramName) {\n    size_t len = strlen(paramName);\n    for (int i = 0; i < NL_PARAMS_MAX && mParams[i] != nullptr; ++i) {\n        const char *ptr = mParams[i] + len;\n        if (!strncmp(mParams[i], paramName, len) && *ptr == '=')\n            return ++ptr;\n    }\n\n    SLOGE(\"NetlinkEvent::FindParam(): Parameter '%s' not found\", paramName);\n    return nullptr;\n}\n\nnlattr* NetlinkEvent::findNlAttr(const nlmsghdr* nh, size_t hdrlen, uint16_t attr) {\n    if (nh == nullptr || NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen) > SSIZE_MAX) {\n        return nullptr;\n    }\n\n    // Skip header, padding, and family header.\n    const ssize_t NLA_START = NLMSG_HDRLEN + NLMSG_ALIGN(hdrlen);\n    ssize_t left = nh->nlmsg_len - NLA_START;\n    uint8_t* hdr = ((uint8_t*)nh) + NLA_START;\n\n    while (left >= NLA_HDRLEN) {\n        nlattr* nla = (nlattr*)hdr;\n        if (nla->nla_type == attr) {\n            return nla;\n        }\n\n        hdr += NLA_ALIGN(nla->nla_len);\n        left -= NLA_ALIGN(nla->nla_len);\n    }\n\n    return nullptr;\n}\n"
  },
  {
    "path": "libsysutils/src/NetlinkListener.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"NetlinkListener\"\n\n#include <errno.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <linux/netlink.h> /* out of order because must follow sys/socket.h */\n\n#include <cutils/uevent.h>\n#include <log/log.h>\n#include <sysutils/NetlinkEvent.h>\n\n#if 1\n/* temporary version until we can get Motorola to update their\n * ril.so.  Their prebuilt ril.so is using this private class\n * so changing the NetlinkListener() constructor breaks their ril.\n */\nNetlinkListener::NetlinkListener(int socket) :\n                            SocketListener(socket, false) {\n    mFormat = NETLINK_FORMAT_ASCII;\n}\n#endif\n\nNetlinkListener::NetlinkListener(int socket, int format) :\n                            SocketListener(socket, false), mFormat(format) {\n}\n\nbool NetlinkListener::onDataAvailable(SocketClient *cli)\n{\n    int socket = cli->getSocket();\n    ssize_t count;\n    uid_t uid = -1;\n\n    bool require_group = true;\n    if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {\n        require_group = false;\n    }\n\n    count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,\n            mBuffer, sizeof(mBuffer), require_group, &uid));\n    if (count < 0) {\n        SLOGE(\"recvmsg failed (%s)\", strerror(errno));\n        return false;\n    }\n\n    NetlinkEvent *evt = new NetlinkEvent();\n    if (evt->decode(mBuffer, count, mFormat)) {\n        onEvent(evt);\n    } else if (mFormat != NETLINK_FORMAT_BINARY) {\n        // Don't complain if parseBinaryNetlinkMessage returns false. That can\n        // just mean that the buffer contained no messages we're interested in.\n        SLOGE(\"Error decoding NetlinkEvent\");\n    }\n\n    delete evt;\n    return true;\n}\n"
  },
  {
    "path": "libsysutils/src/OWNERS",
    "content": "per-file OWNERS,Netlink* = file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking\n"
  },
  {
    "path": "libsysutils/src/ServiceManager.cpp",
    "content": "/*\n * Copyright (C) 2009-2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Service\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <string.h>\n#include <sys/system_properties.h>\n#include <unistd.h>\n\n#include <android-base/properties.h>\n#include <android-base/stringprintf.h>\n#include <log/log.h>\n#include <sysutils/ServiceManager.h>\n\nServiceManager::ServiceManager() {\n}\n\n// The length of a service name should not exceed SERVICE_NAME_MAX. Starting\n// a service is done by writing its name to the \"ctl.start\" system property\n// and stopping a service is done by writing its name to \"ctl.stop\". If a\n// service name is too long to fit in a property, you won't be able to start\n// or stop it.\nstatic constexpr size_t SERVICE_NAME_MAX = PROP_VALUE_MAX;\n\n/* The maximum amount of time to wait for a service to start or stop,\n * in micro-seconds (really an approximation) */\n#define  SLEEP_MAX_USEC     2000000  /* 2 seconds */\n\n/* The minimal sleeping interval between checking for the service's state\n * when looping for SLEEP_MAX_USEC */\n#define  SLEEP_MIN_USEC      200000  /* 200 msec */\n\nint ServiceManager::start(const char *name) {\n    if (strlen(name) > SERVICE_NAME_MAX) {\n        SLOGE(\"Service name '%s' is too long\", name);\n        return 0;\n    }\n\n    if (isRunning(name)) {\n        SLOGW(\"Service '%s' is already running\", name);\n        return 0;\n    }\n\n    SLOGD(\"Starting service '%s'\", name);\n    android::base::SetProperty(\"ctl.start\", name);\n\n    int count = SLEEP_MAX_USEC;\n    while(count > 0) {\n        usleep(SLEEP_MIN_USEC);\n        count -= SLEEP_MIN_USEC;\n        if (isRunning(name))\n            break;\n    }\n    if (count <= 0) {\n        SLOGW(\"Timed out waiting for service '%s' to start\", name);\n        errno = ETIMEDOUT;\n        return -1;\n    }\n    SLOGD(\"Sucessfully started '%s'\", name);\n    return 0;\n}\n\nint ServiceManager::stop(const char *name) {\n    if (strlen(name) > SERVICE_NAME_MAX) {\n        SLOGE(\"Service name '%s' is too long\", name);\n        return 0;\n    }\n\n    if (!isRunning(name)) {\n        SLOGW(\"Service '%s' is already stopped\", name);\n        return 0;\n    }\n\n    SLOGD(\"Stopping service '%s'\", name);\n    android::base::SetProperty(\"ctl.stop\", name);\n\n    int count = SLEEP_MAX_USEC;\n    while(count > 0) {\n        usleep(SLEEP_MIN_USEC);\n        count -= SLEEP_MIN_USEC;\n        if (!isRunning(name))\n            break;\n    }\n\n    if (count <= 0) {\n        SLOGW(\"Timed out waiting for service '%s' to stop\", name);\n        errno = ETIMEDOUT;\n        return -1;\n    }\n    SLOGD(\"Successfully stopped '%s'\", name);\n    return 0;\n}\n\nbool ServiceManager::isRunning(const char *name) {\n    std::string property_name = android::base::StringPrintf(\"init.svc.%s\", name);\n    return (android::base::GetProperty(property_name, \"\") == \"running\");\n}\n"
  },
  {
    "path": "libsysutils/src/SocketClient.cpp",
    "content": "/*\n * Copyright (C) 2009-2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"SocketClient\"\n\n#include <alloca.h>\n#include <arpa/inet.h>\n#include <errno.h>\n#include <malloc.h>\n#include <pthread.h>\n#include <signal.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/macros.h>\n#include <log/log.h>\n#include <sysutils/SocketClient.h>\n\nSocketClient::SocketClient(int socket, bool owned) {\n    init(socket, owned, false);\n}\n\nSocketClient::SocketClient(int socket, bool owned, bool useCmdNum) {\n    init(socket, owned, useCmdNum);\n}\n\nvoid SocketClient::init(int socket, bool owned, bool useCmdNum) {\n    mSocket = socket;\n    mSocketOwned = owned;\n    mUseCmdNum = useCmdNum;\n    pthread_mutex_init(&mWriteMutex, nullptr);\n    pthread_mutex_init(&mRefCountMutex, nullptr);\n    mPid = -1;\n    mUid = -1;\n    mGid = -1;\n    mRefCount = 1;\n    mCmdNum = 0;\n\n    struct ucred creds;\n    socklen_t szCreds = sizeof(creds);\n    memset(&creds, 0, szCreds);\n\n    int err = getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);\n    if (err == 0) {\n        mPid = creds.pid;\n        mUid = creds.uid;\n        mGid = creds.gid;\n    }\n}\n\nSocketClient::~SocketClient() {\n    if (mSocketOwned) {\n        close(mSocket);\n    }\n}\n\nint SocketClient::sendMsg(int code, const char *msg, bool addErrno) {\n    return sendMsg(code, msg, addErrno, mUseCmdNum);\n}\n\nint SocketClient::sendMsg(int code, const char *msg, bool addErrno, bool useCmdNum) {\n    char *buf;\n    int ret = 0;\n\n    if (addErrno) {\n        if (useCmdNum) {\n            ret = asprintf(&buf, \"%d %d %s (%s)\", code, getCmdNum(), msg, strerror(errno));\n        } else {\n            ret = asprintf(&buf, \"%d %s (%s)\", code, msg, strerror(errno));\n        }\n    } else {\n        if (useCmdNum) {\n            ret = asprintf(&buf, \"%d %d %s\", code, getCmdNum(), msg);\n        } else {\n            ret = asprintf(&buf, \"%d %s\", code, msg);\n        }\n    }\n    // Send the zero-terminated message\n    if (ret != -1) {\n        ret = sendMsg(buf);\n        free(buf);\n    }\n    return ret;\n}\n\n// send 3-digit code, null, binary-length, binary data\nint SocketClient::sendBinaryMsg(int code, const void *data, int len) {\n\n    // 4 bytes for the code & null + 4 bytes for the len\n    char buf[8];\n    // Write the code\n    snprintf(buf, 4, \"%.3d\", code);\n    // Write the len\n    uint32_t tmp = htonl(len);\n    memcpy(buf + 4, &tmp, sizeof(uint32_t));\n\n    struct iovec vec[2];\n    vec[0].iov_base = (void *) buf;\n    vec[0].iov_len = sizeof(buf);\n    vec[1].iov_base = (void *) data;\n    vec[1].iov_len = len;\n\n    pthread_mutex_lock(&mWriteMutex);\n    int result = sendDataLockedv(vec, (len > 0) ? 2 : 1);\n    pthread_mutex_unlock(&mWriteMutex);\n\n    return result;\n}\n\n// Sends the code (c-string null-terminated).\nint SocketClient::sendCode(int code) {\n    char buf[4];\n    snprintf(buf, sizeof(buf), \"%.3d\", code);\n    return sendData(buf, sizeof(buf));\n}\n\nchar *SocketClient::quoteArg(const char *arg) {\n    int len = strlen(arg);\n    char *result = (char *)malloc(len * 2 + 3);\n    char *current = result;\n    const char *end = arg + len;\n    char *oldresult;\n\n    if(result == nullptr) {\n        SLOGW(\"malloc error (%s)\", strerror(errno));\n        return nullptr;\n    }\n\n    *(current++) = '\"';\n    while (arg < end) {\n        switch (*arg) {\n        case '\\\\':\n        case '\"':\n            *(current++) = '\\\\';\n            FALLTHROUGH_INTENDED;\n        default:\n            *(current++) = *(arg++);\n        }\n    }\n    *(current++) = '\"';\n    *(current++) = '\\0';\n    oldresult = result; // save pointer in case realloc fails\n    result = (char *)realloc(result, current-result);\n    return result ? result : oldresult;\n}\n\n\nint SocketClient::sendMsg(const char *msg) {\n    // Send the message including null character\n    if (sendData(msg, strlen(msg) + 1) != 0) {\n        SLOGW(\"Unable to send msg '%s'\", msg);\n        return -1;\n    }\n    return 0;\n}\n\nint SocketClient::sendData(const void *data, int len) {\n    struct iovec vec[1];\n    vec[0].iov_base = (void *) data;\n    vec[0].iov_len = len;\n\n    pthread_mutex_lock(&mWriteMutex);\n    int rc = sendDataLockedv(vec, 1);\n    pthread_mutex_unlock(&mWriteMutex);\n\n    return rc;\n}\n\nint SocketClient::sendDatav(struct iovec *iov, int iovcnt) {\n    pthread_mutex_lock(&mWriteMutex);\n    int rc = sendDataLockedv(iov, iovcnt);\n    pthread_mutex_unlock(&mWriteMutex);\n\n    return rc;\n}\n\nint SocketClient::sendDataLockedv(struct iovec *iov, int iovcnt) {\n\n    if (mSocket < 0) {\n        errno = EHOSTUNREACH;\n        return -1;\n    }\n\n    if (iovcnt <= 0) {\n        return 0;\n    }\n\n    int current = 0;\n\n    for (;;) {\n        ssize_t rc = TEMP_FAILURE_RETRY(writev(mSocket, iov + current, iovcnt - current));\n\n        if (rc == 0) {\n            errno = EIO;\n            SLOGW(\"0 length write :(\");\n            return -1;\n        } else if (rc < 0) {\n            SLOGW(\"write error (%s)\", strerror(errno));\n            return -1;\n        }\n\n        size_t written = rc;\n        while (current < iovcnt && written >= iov[current].iov_len) {\n            written -= iov[current].iov_len;\n            current++;\n        }\n        if (current == iovcnt) {\n            return 0;\n        }\n        iov[current].iov_base = (char*)iov[current].iov_base + written;\n        iov[current].iov_len -= written;\n    }\n}\n\nvoid SocketClient::incRef() {\n    pthread_mutex_lock(&mRefCountMutex);\n    mRefCount++;\n    pthread_mutex_unlock(&mRefCountMutex);\n}\n\nbool SocketClient::decRef() {\n    bool deleteSelf = false;\n    pthread_mutex_lock(&mRefCountMutex);\n    mRefCount--;\n    if (mRefCount == 0) {\n        deleteSelf = true;\n    } else if (mRefCount < 0) {\n        SLOGE(\"SocketClient refcount went negative!\");\n    }\n    pthread_mutex_unlock(&mRefCountMutex);\n    if (deleteSelf) {\n        delete this;\n    }\n    return deleteSelf;\n}\n"
  },
  {
    "path": "libsysutils/src/SocketListener.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"SocketListener\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/poll.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <sys/types.h>\n#include <sys/un.h>\n#include <unistd.h>\n\n#include <vector>\n\n#include <cutils/sockets.h>\n#include <log/log.h>\n#include <sysutils/SocketListener.h>\n#include <sysutils/SocketClient.h>\n\n#define CtrlPipe_Shutdown 0\n#define CtrlPipe_Wakeup   1\n\nSocketListener::SocketListener(const char *socketName, bool listen) {\n    init(socketName, -1, listen, false);\n}\n\nSocketListener::SocketListener(int socketFd, bool listen) {\n    init(nullptr, socketFd, listen, false);\n}\n\nSocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {\n    init(socketName, -1, listen, useCmdNum);\n}\n\nvoid SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {\n    mListen = listen;\n    mSocketName = socketName;\n    mSock = socketFd;\n    mUseCmdNum = useCmdNum;\n    pthread_mutex_init(&mClientsLock, nullptr);\n}\n\nSocketListener::~SocketListener() {\n    if (mSocketName && mSock > -1)\n        close(mSock);\n\n    if (mCtrlPipe[0] != -1) {\n        close(mCtrlPipe[0]);\n        close(mCtrlPipe[1]);\n    }\n    for (auto pair : mClients) {\n        pair.second->decRef();\n    }\n}\n\nint SocketListener::startListener() {\n    return startListener(4);\n}\n\nint SocketListener::startListener(int backlog) {\n\n    if (!mSocketName && mSock == -1) {\n        SLOGE(\"Failed to start unbound listener\");\n        errno = EINVAL;\n        return -1;\n    } else if (mSocketName) {\n        if ((mSock = android_get_control_socket(mSocketName)) < 0) {\n            SLOGE(\"Obtaining file descriptor socket '%s' failed: %s\",\n                 mSocketName, strerror(errno));\n            return -1;\n        }\n        SLOGV(\"got mSock = %d for %s\", mSock, mSocketName);\n        fcntl(mSock, F_SETFD, FD_CLOEXEC);\n    }\n\n    if (mListen && listen(mSock, backlog) < 0) {\n        SLOGE(\"Unable to listen on socket (%s)\", strerror(errno));\n        return -1;\n    } else if (!mListen)\n        mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);\n\n    if (pipe2(mCtrlPipe, O_CLOEXEC)) {\n        SLOGE(\"pipe failed (%s)\", strerror(errno));\n        return -1;\n    }\n\n    if (pthread_create(&mThread, nullptr, SocketListener::threadStart, this)) {\n        SLOGE(\"pthread_create (%s)\", strerror(errno));\n        return -1;\n    }\n\n    return 0;\n}\n\nint SocketListener::stopListener() {\n    char c = CtrlPipe_Shutdown;\n    int  rc;\n\n    rc = TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &c, 1));\n    if (rc != 1) {\n        SLOGE(\"Error writing to control pipe (%s)\", strerror(errno));\n        return -1;\n    }\n\n    void *ret;\n    if (pthread_join(mThread, &ret)) {\n        SLOGE(\"Error joining to listener thread (%s)\", strerror(errno));\n        return -1;\n    }\n    close(mCtrlPipe[0]);\n    close(mCtrlPipe[1]);\n    mCtrlPipe[0] = -1;\n    mCtrlPipe[1] = -1;\n\n    if (mSocketName && mSock > -1) {\n        close(mSock);\n        mSock = -1;\n    }\n\n    for (auto pair : mClients) {\n        delete pair.second;\n    }\n    mClients.clear();\n    return 0;\n}\n\nvoid *SocketListener::threadStart(void *obj) {\n    SocketListener *me = reinterpret_cast<SocketListener *>(obj);\n\n    me->runListener();\n    pthread_exit(nullptr);\n    return nullptr;\n}\n\nvoid SocketListener::runListener() {\n    while (true) {\n        std::vector<pollfd> fds;\n\n        pthread_mutex_lock(&mClientsLock);\n        fds.reserve(2 + mClients.size());\n        fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN});\n        if (mListen) fds.push_back({.fd = mSock, .events = POLLIN});\n        for (auto pair : mClients) {\n            // NB: calling out to an other object with mClientsLock held (safe)\n            const int fd = pair.second->getSocket();\n            if (fd != pair.first) SLOGE(\"fd mismatch: %d != %d\", fd, pair.first);\n            fds.push_back({.fd = fd, .events = POLLIN});\n        }\n        pthread_mutex_unlock(&mClientsLock);\n\n        SLOGV(\"mListen=%d, mSocketName=%s\", mListen, mSocketName);\n        int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1));\n        if (rc < 0) {\n            SLOGE(\"poll failed (%s) mListen=%d\", strerror(errno), mListen);\n            sleep(1);\n            continue;\n        }\n\n        if (fds[0].revents & (POLLIN | POLLERR)) {\n            char c = CtrlPipe_Shutdown;\n            TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));\n            if (c == CtrlPipe_Shutdown) {\n                break;\n            }\n            continue;\n        }\n        if (mListen && (fds[1].revents & (POLLIN | POLLERR))) {\n            int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));\n            if (c < 0) {\n                SLOGE(\"accept failed (%s)\", strerror(errno));\n                sleep(1);\n                continue;\n            }\n            pthread_mutex_lock(&mClientsLock);\n            mClients[c] = new SocketClient(c, true, mUseCmdNum);\n            pthread_mutex_unlock(&mClientsLock);\n        }\n\n        // Add all active clients to the pending list first, so we can release\n        // the lock before invoking the callbacks.\n        std::vector<SocketClient*> pending;\n        pthread_mutex_lock(&mClientsLock);\n        const int size = fds.size();\n        for (int i = mListen ? 2 : 1; i < size; ++i) {\n            const struct pollfd& p = fds[i];\n            if (p.revents & (POLLIN | POLLERR)) {\n                auto it = mClients.find(p.fd);\n                if (it == mClients.end()) {\n                    SLOGE(\"fd vanished: %d\", p.fd);\n                    continue;\n                }\n                SocketClient* c = it->second;\n                pending.push_back(c);\n                c->incRef();\n            }\n        }\n        pthread_mutex_unlock(&mClientsLock);\n\n        for (SocketClient* c : pending) {\n            // Process it, if false is returned, remove from the map\n            SLOGV(\"processing fd %d\", c->getSocket());\n            if (!onDataAvailable(c)) {\n                release(c, false);\n            }\n            c->decRef();\n        }\n    }\n}\n\nbool SocketListener::release(SocketClient* c, bool wakeup) {\n    bool ret = false;\n    /* if our sockets are connection-based, remove and destroy it */\n    if (mListen && c) {\n        /* Remove the client from our map */\n        SLOGV(\"going to zap %d for %s\", c->getSocket(), mSocketName);\n        pthread_mutex_lock(&mClientsLock);\n        ret = (mClients.erase(c->getSocket()) != 0);\n        pthread_mutex_unlock(&mClientsLock);\n        if (ret) {\n            ret = c->decRef();\n            if (wakeup) {\n                char b = CtrlPipe_Wakeup;\n                TEMP_FAILURE_RETRY(write(mCtrlPipe[1], &b, 1));\n            }\n        }\n    }\n    return ret;\n}\n\nstd::vector<SocketClient*> SocketListener::snapshotClients() {\n    std::vector<SocketClient*> clients;\n    pthread_mutex_lock(&mClientsLock);\n    clients.reserve(mClients.size());\n    for (auto pair : mClients) {\n        SocketClient* c = pair.second;\n        c->incRef();\n        clients.push_back(c);\n    }\n    pthread_mutex_unlock(&mClientsLock);\n\n    return clients;\n}\n\nvoid SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {\n    for (SocketClient* c : snapshotClients()) {\n        // broadcasts are unsolicited and should not include a cmd number\n        if (c->sendMsg(code, msg, addErrno, false)) {\n            SLOGW(\"Error sending broadcast (%s)\", strerror(errno));\n        }\n        c->decRef();\n    }\n}\n\nvoid SocketListener::runOnEachSocket(SocketClientCommand *command) {\n    for (SocketClient* c : snapshotClients()) {\n        command->runSocketCommand(c);\n        c->decRef();\n    }\n}\n"
  },
  {
    "path": "libsysutils/src/SocketListener_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sysutils/FrameworkCommand.h>\n#include <sysutils/FrameworkListener.h>\n\n#include <poll.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n\n#include <algorithm>\n#include <memory>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <android-base/unique_fd.h>\n#include <cutils/sockets.h>\n#include <gtest/gtest.h>\n\nusing android::base::unique_fd;\n\nnamespace {\n\nstd::string testSocketPath() {\n    const testing::TestInfo* const test_info =\n            testing::UnitTest::GetInstance()->current_test_info();\n    return std::string(ANDROID_SOCKET_DIR \"/\") + std::string(test_info->test_case_name()) +\n           std::string(\".\") + std::string(test_info->name());\n}\n\nunique_fd serverSocket(const std::string& path) {\n    unlink(path.c_str());\n\n    unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));\n    EXPECT_GE(fd.get(), 0);\n\n    struct sockaddr_un addr = {.sun_family = AF_UNIX};\n    strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));\n\n    EXPECT_EQ(bind(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0)\n            << \"bind() to \" << path << \" failed: \" << strerror(errno);\n    EXPECT_EQ(android_get_control_socket(path.c_str()), -1);\n\n    return fd;\n}\n\nunique_fd clientSocket(const std::string& path) {\n    unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));\n    EXPECT_GE(fd.get(), 0);\n\n    struct sockaddr_un addr = {.sun_family = AF_UNIX};\n    strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));\n\n    EXPECT_EQ(0, connect(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))\n            << \"connect() to \" << path << \" failed: \" << strerror(errno);\n\n    return fd;\n}\n\nvoid sendCmd(int fd, const char* cmd) {\n    EXPECT_TRUE(android::base::WriteFully(fd, cmd, strlen(cmd) + 1))\n            << \"write() to socket failed: \" << strerror(errno);\n}\n\nstd::string recvReply(int fd) {\n    pollfd fds = {.fd = fd, .events = POLLIN};\n    int poll_events = poll(&fds, 1, -1);\n    EXPECT_EQ(1, poll_events);\n\n    // Technically, this one-shot read() is incorrect: we should keep on\n    // reading the socket until we get a \\0. But this is also how\n    // FrameworkListener::onDataAvailable() reads, and it works because\n    // replies are always send with a single write() call, and clients\n    // always read replies before queueing the next command.\n    char buf[1024];\n    ssize_t len = read(fd, buf, sizeof(buf));\n    EXPECT_GE(len, 0) << \"read() from socket failed: \" << strerror(errno);\n    return len > 0 ? std::string(buf, buf + len) : \"\";\n}\n\n// Test command which echoes back all its arguments as a comma-separated list.\n// Always returns error code 42\n//\n// TODO: enable testing replies with addErrno=true and useCmdNum=true\nclass TestCommand : public FrameworkCommand {\n  public:\n    TestCommand() : FrameworkCommand(\"test\") {}\n    ~TestCommand() override {}\n\n    int runCommand(SocketClient* cli, int argc, char** argv) {\n        std::vector<std::string> args(argv, argv + argc);\n        std::string reply = android::base::Join(args, ',');\n        cli->sendMsg(42, reply.c_str(), /*addErrno=*/false, /*useCmdNum=*/false);\n        return 0;\n    }\n};\n\n// A test listener with a single command.\nclass TestListener : public FrameworkListener {\n  public:\n    TestListener(int fd) : FrameworkListener(fd) {\n        registerCmd(new TestCommand);  // Leaked :-(\n    }\n};\n\n}  // unnamed namespace\n\nclass FrameworkListenerTest : public testing::Test {\n  public:\n    FrameworkListenerTest() {\n        mSocketPath = testSocketPath();\n        mSserverFd = serverSocket(mSocketPath);\n        mListener = std::make_unique<TestListener>(mSserverFd.get());\n        EXPECT_EQ(0, mListener->startListener());\n    }\n\n    ~FrameworkListenerTest() override {\n        EXPECT_EQ(0, mListener->stopListener());\n\n        // Wouldn't it be cool if unique_fd had an option for taking care of this?\n        unlink(mSocketPath.c_str());\n    }\n\n    void testCommand(const char* command, const char* expected) {\n        unique_fd client_fd = clientSocket(mSocketPath);\n        sendCmd(client_fd.get(), command);\n\n        std::string reply = recvReply(client_fd.get());\n        EXPECT_EQ(std::string(expected) + '\\0', reply);\n    }\n\n  protected:\n    std::string mSocketPath;\n    unique_fd mSserverFd;\n    std::unique_ptr<TestListener> mListener;\n};\n\nTEST_F(FrameworkListenerTest, DoesNothing) {\n    // Let the test harness start and stop a FrameworkListener\n    // without sending any commands through it.\n}\n\nTEST_F(FrameworkListenerTest, DispatchesValidCommands) {\n    testCommand(\"test\", \"42 test\");\n    testCommand(\"test arg1 arg2\", \"42 test,arg1,arg2\");\n    testCommand(\"test \\\"arg1 still_arg1\\\" arg2\", \"42 test,arg1 still_arg1,arg2\");\n    testCommand(\"test \\\"escaped quote: '\\\\\\\"'\\\"\", \"42 test,escaped quote: '\\\"'\");\n\n    // Perhaps this behavior was unintended, but would be good to detect any\n    // changes, in case anyone depends on it.\n    testCommand(\"test   \", \"42 test,,,\");\n}\n\nTEST_F(FrameworkListenerTest, RejectsInvalidCommands) {\n    testCommand(\"unknown arg1 arg2\", \"500 Command not recognized\");\n    testCommand(\"test \\\"arg1 arg2\", \"500 Unclosed quotes error\");\n    testCommand(\"test \\\\a\", \"500 Unsupported escape sequence\");\n}\n\nTEST_F(FrameworkListenerTest, MultipleClients) {\n    unique_fd client1 = clientSocket(mSocketPath);\n    unique_fd client2 = clientSocket(mSocketPath);\n    sendCmd(client1.get(), \"test 1\");\n    sendCmd(client2.get(), \"test 2\");\n\n    EXPECT_EQ(std::string(\"42 test,2\") + '\\0', recvReply(client2.get()));\n    EXPECT_EQ(std::string(\"42 test,1\") + '\\0', recvReply(client1.get()));\n}\n"
  },
  {
    "path": "libusbhost/Android.bp",
    "content": "//\n// Copyright (C) 2010 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libusbhost\",\n    vendor_available: true,\n    host_supported: true,\n    srcs: [\"usbhost.c\"],\n    cflags: [\"-Werror\"],\n    export_include_dirs: [\"include\"],\n    target: {\n        android: {\n            header_libs: [\"jni_headers\"],\n            shared_libs: [\"liblog\"],\n            srcs: [\"usbhost_jni.cpp\"],\n        },\n        darwin: {\n            enabled: false,\n        },\n    },\n}\n"
  },
  {
    "path": "libusbhost/include/usbhost/usbhost.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __USB_HOST_H\n#define __USB_HOST_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h>\n#include <stdint.h>\n\n#include <linux/version.h>\n#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 20)\n#include <linux/usb/ch9.h>\n#else\n#include <linux/usb_ch9.h>\n#endif\n\nstruct usb_host_context;\nstruct usb_endpoint_descriptor;\n\nstruct usb_descriptor_iter {\n    unsigned char*  config;\n    unsigned char*  config_end;\n    unsigned char*  curr_desc;\n};\n\nstruct usb_request\n{\n    struct usb_device *dev;\n    void* buffer;\n    int buffer_length;\n    int actual_length;\n    int max_packet_size;\n    void *private_data; /* struct usbdevfs_urb* */\n    int endpoint;\n    void *client_data;  /* free for use by client */\n};\n\n/* Callback for notification when new USB devices are attached.\n * Return true to exit from usb_host_run.\n */\ntypedef int (* usb_device_added_cb)(const char *dev_name, void *client_data);\n\n/* Callback for notification when USB devices are removed.\n * Return true to exit from usb_host_run.\n */\ntypedef int (* usb_device_removed_cb)(const char *dev_name, void *client_data);\n\n/* Callback indicating that initial device discovery is done.\n * Return true to exit from usb_host_run.\n */\ntypedef int (* usb_discovery_done_cb)(void *client_data);\n\n/* Call this to initialize the USB host library. */\nstruct usb_host_context *usb_host_init(void);\n\n/* Call this to cleanup the USB host library. */\nvoid usb_host_cleanup(struct usb_host_context *context);\n\n/* Call this to get the inotify file descriptor. */\nint usb_host_get_fd(struct usb_host_context *context);\n\n/* Call this to initialize the usb host context. */\nint usb_host_load(struct usb_host_context *context,\n                  usb_device_added_cb added_cb,\n                  usb_device_removed_cb removed_cb,\n                  usb_discovery_done_cb discovery_done_cb,\n                  void *client_data);\n\n/* Call this to read and handle occuring usb event. */\nint usb_host_read_event(struct usb_host_context *context);\n\n/* Call this to monitor the USB bus for new and removed devices.\n * This is intended to be called from a dedicated thread,\n * as it will not return until one of the callbacks returns true.\n * added_cb will be called immediately for each existing USB device,\n * and subsequently each time a new device is added.\n * removed_cb is called when USB devices are removed from the bus.\n * discovery_done_cb is called after the initial discovery of already\n * connected devices is complete.\n */\nvoid usb_host_run(struct usb_host_context *context,\n                  usb_device_added_cb added_cb,\n                  usb_device_removed_cb removed_cb,\n                  usb_discovery_done_cb discovery_done_cb,\n                  void *client_data);\n\n/* Creates a usb_device object for a USB device */\nstruct usb_device *usb_device_open(const char *dev_name);\n\n/* Releases all resources associated with the USB device */\nvoid usb_device_close(struct usb_device *device);\n\n/* Creates a usb_device object for already open USB device */\nstruct usb_device *usb_device_new(const char *dev_name, int fd);\n\n/* Returns the file descriptor for the usb_device */\nint usb_device_get_fd(struct usb_device *device);\n\n/* Returns the name for the USB device, which is the same as\n * the dev_name passed to usb_device_open()\n */\nconst char* usb_device_get_name(struct usb_device *device);\n\n/* Returns a unique ID for the device.\n *Currently this is generated from the dev_name path.\n */\nint usb_device_get_unique_id(struct usb_device *device);\n\n/* Returns a unique ID for the device name.\n * Currently this is generated from the device path.\n */\nint usb_device_get_unique_id_from_name(const char* name);\n\n/* Returns the device name for the unique ID.\n * Call free() to deallocate the returned string */\nchar* usb_device_get_name_from_unique_id(int id);\n\n/* Returns the USB vendor ID from the device descriptor for the USB device */\nuint16_t usb_device_get_vendor_id(struct usb_device *device);\n\n/* Returns the USB product ID from the device descriptor for the USB device */\nuint16_t usb_device_get_product_id(struct usb_device *device);\n\n/* Returns a pointer to device descriptor */\nconst struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device *device);\n\n/* Returns a USB descriptor string for the given string ID.\n * Return value: < 0 on error.  0 on success.\n * The string is returned in ucs2_out in USB-native UCS-2 encoding.\n *\n * parameters:\n *  id - the string descriptor index.\n *  timeout - in milliseconds (see Documentation/driver-api/usb/usb.rst)\n *  ucs2_out - Must point to null on call.\n *             Will be filled in with a buffer on success.\n *             If this is non-null on return, it must be free()d.\n *  response_size - size, in bytes, of ucs-2 string in ucs2_out.\n *                  The size isn't guaranteed to include null termination.\n * Call free() to free the result when you are done with it.\n */\nint usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,\n                               size_t* response_size);\n\n/* Returns the length in bytes read into the raw descriptors array */\nsize_t usb_device_get_descriptors_length(const struct usb_device* device);\n\n/* Returns a pointer to the raw descriptors array */\nconst unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device);\n\n/* Returns a USB descriptor string for the given string ID.\n * Used to implement usb_device_get_manufacturer_name,\n * usb_device_get_product_name and usb_device_get_serial.\n * Returns ascii - non ascii characters will be replaced with '?'.\n * Call free() to free the result when you are done with it.\n */\nchar* usb_device_get_string(struct usb_device *device, int id, int timeout);\n\n/* Returns the manufacturer name for the USB device.\n * Call free() to free the result when you are done with it.\n */\nchar* usb_device_get_manufacturer_name(struct usb_device *device, int timeout);\n\n/* Returns the product name for the USB device.\n * Call free() to free the result when you are done with it.\n */\nchar* usb_device_get_product_name(struct usb_device *device, int timeout);\n\n/* Returns the version number for the USB device.\n */\nint usb_device_get_version(struct usb_device *device);\n\n/* Returns the USB serial number for the USB device.\n * Call free() to free the result when you are done with it.\n */\nchar* usb_device_get_serial(struct usb_device *device, int timeout);\n\n/* Returns true if we have write access to the USB device,\n * and false if we only have access to the USB device configuration.\n */\nint usb_device_is_writeable(struct usb_device *device);\n\n/* Initializes a usb_descriptor_iter, which can be used to iterate through all\n * the USB descriptors for a USB device.\n */\nvoid usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter);\n\n/* Returns the next USB descriptor for a device, or NULL if we have reached the\n * end of the list.\n */\nstruct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter);\n\n/* Claims the specified interface of a USB device */\nint usb_device_claim_interface(struct usb_device *device, unsigned int interface);\n\n/* Releases the specified interface of a USB device */\nint usb_device_release_interface(struct usb_device *device, unsigned int interface);\n\n/* Requests the kernel to connect or disconnect its driver for the specified interface.\n * This can be used to ask the kernel to disconnect its driver for a device\n * so usb_device_claim_interface can claim it instead.\n */\nint usb_device_connect_kernel_driver(struct usb_device *device,\n        unsigned int interface, int connect);\n\n/* Sets the current configuration for the device to the specified configuration */\nint usb_device_set_configuration(struct usb_device *device, int configuration);\n\n/* Sets the specified interface of a USB device */\nint usb_device_set_interface(struct usb_device *device, unsigned int interface,\n                            unsigned int alt_setting);\n\n/* Sends a control message to the specified device on endpoint zero */\nint usb_device_control_transfer(struct usb_device *device,\n                            int requestType,\n                            int request,\n                            int value,\n                            int index,\n                            void* buffer,\n                            int length,\n                            unsigned int timeout);\n\n/* Reads or writes on a bulk endpoint.\n * Returns number of bytes transferred, or negative value for error.\n */\nint usb_device_bulk_transfer(struct usb_device *device,\n                            int endpoint,\n                            void* buffer,\n                            unsigned int length,\n                            unsigned int timeout);\n\n/** Reset USB bus for the device */\nint usb_device_reset(struct usb_device *device);\n\n/* Creates a new usb_request. */\nstruct usb_request *usb_request_new(struct usb_device *dev,\n        const struct usb_endpoint_descriptor *ep_desc);\n\n/* Releases all resources associated with the request */\nvoid usb_request_free(struct usb_request *req);\n\n/* Submits a read or write request on the specified device */\nint usb_request_queue(struct usb_request *req);\n\n /* Waits for the results of a previous usb_request_queue operation. timeoutMillis == -1 requests\n  * to wait forever.\n  * Returns a usb_request, or NULL for error.\n  */\nstruct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis);\n\n/* Cancels a pending usb_request_queue() operation. */\nint usb_request_cancel(struct usb_request *req);\n\n#ifdef __cplusplus\n}\n#endif\n#endif /* __USB_HOST_H */\n"
  },
  {
    "path": "libusbhost/include/usbhost/usbhost_jni.h",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <jni.h>\n\n/**\n * Reads USB descriptors from `fd`.\n *\n * Returns a byte[] on success,\n * or returns NULL and logs an appropriate error on failure.\n */\njbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd);\n"
  },
  {
    "path": "libusbhost/usbhost.c",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE\n#endif\n\n#include <usbhost/usbhost.h>\n\n#include \"usbhost_private.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <string.h>\n#include <stddef.h>\n\n#include <sys/ioctl.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <sys/inotify.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <errno.h>\n#include <ctype.h>\n#include <poll.h>\n\n#include <linux/usbdevice_fs.h>\n\n// #define DEBUG 1\n#if defined(DEBUG)\n#if defined(__BIONIC__)\n#define D ALOGD\n#else\n#define D printf\n#endif\n#else\n#define D(...)\n#endif\n\n#define DEV_DIR             \"/dev\"\n#define DEV_BUS_DIR         DEV_DIR \"/bus\"\n#define USB_FS_DIR          DEV_BUS_DIR \"/usb\"\n#define USB_FS_ID_SCANNER   USB_FS_DIR \"/%d/%d\"\n#define USB_FS_ID_FORMAT    USB_FS_DIR \"/%03d/%03d\"\n\n// Some devices fail to send string descriptors if we attempt reading > 255 bytes\n#define MAX_STRING_DESCRIPTOR_LENGTH    255\n\n#define MAX_USBFS_WD_COUNT      10\n\nstruct usb_host_context {\n    int                         fd;\n    usb_device_added_cb         cb_added;\n    usb_device_removed_cb       cb_removed;\n    void                        *data;\n    int                         wds[MAX_USBFS_WD_COUNT];\n    int                         wdd;\n    int                         wddbus;\n};\n\nstruct usb_device {\n    char dev_name[64];\n    unsigned char desc[MAX_DESCRIPTORS_LENGTH];\n    int desc_length;\n    int fd;\n    int writeable;\n};\n\nstatic inline int badname(const char *name)\n{\n    while(*name) {\n        if(!isdigit(*name++)) return 1;\n    }\n    return 0;\n}\n\nstatic int find_existing_devices_bus(char *busname,\n                                     usb_device_added_cb added_cb,\n                                     void *client_data)\n{\n    char devname[32];\n    DIR *devdir;\n    struct dirent *de;\n    int done = 0;\n\n    devdir = opendir(busname);\n    if(devdir == 0) return 0;\n\n    while ((de = readdir(devdir)) && !done) {\n        if(badname(de->d_name)) continue;\n\n        snprintf(devname, sizeof(devname), \"%s/%s\", busname, de->d_name);\n        done = added_cb(devname, client_data);\n    } // end of devdir while\n    closedir(devdir);\n\n    return done;\n}\n\n/* returns true if one of the callbacks indicates we are done */\nstatic int find_existing_devices(usb_device_added_cb added_cb,\n                                  void *client_data)\n{\n    char busname[32];\n    DIR *busdir;\n    struct dirent *de;\n    int done = 0;\n\n    busdir = opendir(USB_FS_DIR);\n    if(busdir == 0) return 0;\n\n    while ((de = readdir(busdir)) != 0 && !done) {\n        if(badname(de->d_name)) continue;\n\n        snprintf(busname, sizeof(busname), USB_FS_DIR \"/%s\", de->d_name);\n        done = find_existing_devices_bus(busname, added_cb,\n                                         client_data);\n    } //end of busdir while\n    closedir(busdir);\n\n    return done;\n}\n\nstatic void watch_existing_subdirs(struct usb_host_context *context,\n                                   int *wds, int wd_count)\n{\n    char path[100];\n    int i, ret;\n\n    wds[0] = inotify_add_watch(context->fd, USB_FS_DIR, IN_CREATE | IN_DELETE);\n    if (wds[0] < 0)\n        return;\n\n    /* watch existing subdirectories of USB_FS_DIR */\n    for (i = 1; i < wd_count; i++) {\n        snprintf(path, sizeof(path), USB_FS_DIR \"/%03d\", i);\n        ret = inotify_add_watch(context->fd, path, IN_CREATE | IN_DELETE);\n        if (ret >= 0)\n            wds[i] = ret;\n    }\n}\n\nstruct usb_host_context *usb_host_init()\n{\n    struct usb_host_context *context = calloc(1, sizeof(struct usb_host_context));\n    if (!context) {\n        fprintf(stderr, \"out of memory in usb_host_context\\n\");\n        return NULL;\n    }\n    context->fd = inotify_init();\n    if (context->fd < 0) {\n        fprintf(stderr, \"inotify_init failed\\n\");\n        free(context);\n        return NULL;\n    }\n    return context;\n}\n\nvoid usb_host_cleanup(struct usb_host_context *context)\n{\n    close(context->fd);\n    free(context);\n}\n\nint usb_host_get_fd(struct usb_host_context *context)\n{\n    return context->fd;\n} /* usb_host_get_fd() */\n\nint usb_host_load(struct usb_host_context *context,\n                  usb_device_added_cb added_cb,\n                  usb_device_removed_cb removed_cb,\n                  usb_discovery_done_cb discovery_done_cb,\n                  void *client_data)\n{\n    int done = 0;\n    int i;\n\n    context->cb_added = added_cb;\n    context->cb_removed = removed_cb;\n    context->data = client_data;\n\n    D(\"Created device discovery thread\\n\");\n\n    /* watch for files added and deleted within USB_FS_DIR */\n    context->wddbus = -1;\n    for (i = 0; i < MAX_USBFS_WD_COUNT; i++)\n        context->wds[i] = -1;\n\n    /* watch the root for new subdirectories */\n    context->wdd = inotify_add_watch(context->fd, DEV_DIR, IN_CREATE | IN_DELETE);\n    if (context->wdd < 0) {\n        fprintf(stderr, \"inotify_add_watch failed\\n\");\n        if (discovery_done_cb)\n            discovery_done_cb(client_data);\n        return done;\n    }\n\n    watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);\n\n    /* check for existing devices first, after we have inotify set up */\n    done = find_existing_devices(added_cb, client_data);\n    if (discovery_done_cb)\n        done |= discovery_done_cb(client_data);\n\n    return done;\n} /* usb_host_load() */\n\nint usb_host_read_event(struct usb_host_context *context)\n{\n    struct inotify_event* event;\n    char event_buf[512];\n    char path[100];\n    int i, ret, done = 0;\n    int offset = 0;\n    int wd;\n\n    ret = read(context->fd, event_buf, sizeof(event_buf));\n    if (ret >= (int)sizeof(struct inotify_event)) {\n        while (offset < ret && !done) {\n            event = (struct inotify_event*)&event_buf[offset];\n            done = 0;\n            wd = event->wd;\n            if (wd == context->wdd) {\n                if ((event->mask & IN_CREATE) && !strcmp(event->name, \"bus\")) {\n                    context->wddbus = inotify_add_watch(context->fd, DEV_BUS_DIR, IN_CREATE | IN_DELETE);\n                    if (context->wddbus < 0) {\n                        done = 1;\n                    } else {\n                        watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);\n                        done = find_existing_devices(context->cb_added, context->data);\n                    }\n                }\n            } else if (wd == context->wddbus) {\n                if ((event->mask & IN_CREATE) && !strcmp(event->name, \"usb\")) {\n                    watch_existing_subdirs(context, context->wds, MAX_USBFS_WD_COUNT);\n                    done = find_existing_devices(context->cb_added, context->data);\n                } else if ((event->mask & IN_DELETE) && !strcmp(event->name, \"usb\")) {\n                    for (i = 0; i < MAX_USBFS_WD_COUNT; i++) {\n                        if (context->wds[i] >= 0) {\n                            inotify_rm_watch(context->fd, context->wds[i]);\n                            context->wds[i] = -1;\n                        }\n                    }\n                }\n            } else if (wd == context->wds[0]) {\n                i = atoi(event->name);\n                snprintf(path, sizeof(path), USB_FS_DIR \"/%s\", event->name);\n                D(\"%s subdirectory %s: index: %d\\n\", (event->mask & IN_CREATE) ?\n                        \"new\" : \"gone\", path, i);\n                if (i > 0 && i < MAX_USBFS_WD_COUNT) {\n                    int local_ret = 0;\n                    if (event->mask & IN_CREATE) {\n                        local_ret = inotify_add_watch(context->fd, path,\n                                IN_CREATE | IN_DELETE);\n                        if (local_ret >= 0)\n                            context->wds[i] = local_ret;\n                        done = find_existing_devices_bus(path, context->cb_added,\n                                context->data);\n                    } else if (event->mask & IN_DELETE) {\n                        inotify_rm_watch(context->fd, context->wds[i]);\n                        context->wds[i] = -1;\n                    }\n                }\n            } else {\n                for (i = 1; (i < MAX_USBFS_WD_COUNT) && !done; i++) {\n                    if (wd == context->wds[i]) {\n                        snprintf(path, sizeof(path), USB_FS_DIR \"/%03d/%s\", i, event->name);\n                        if (event->mask == IN_CREATE) {\n                            D(\"new device %s\\n\", path);\n                            done = context->cb_added(path, context->data);\n                        } else if (event->mask == IN_DELETE) {\n                            D(\"gone device %s\\n\", path);\n                            done = context->cb_removed(path, context->data);\n                        }\n                    }\n                }\n            }\n\n            offset += sizeof(struct inotify_event) + event->len;\n        }\n    }\n\n    return done;\n} /* usb_host_read_event() */\n\nvoid usb_host_run(struct usb_host_context *context,\n                  usb_device_added_cb added_cb,\n                  usb_device_removed_cb removed_cb,\n                  usb_discovery_done_cb discovery_done_cb,\n                  void *client_data)\n{\n    int done;\n\n    done = usb_host_load(context, added_cb, removed_cb, discovery_done_cb, client_data);\n\n    while (!done) {\n\n        done = usb_host_read_event(context);\n    }\n} /* usb_host_run() */\n\nstruct usb_device *usb_device_open(const char *dev_name)\n{\n    int fd, attempts, writeable = 1;\n    const int SLEEP_BETWEEN_ATTEMPTS_US = 100000; /* 100 ms */\n    const int64_t MAX_ATTEMPTS = 10;              /* 1s */\n    D(\"usb_device_open %s\\n\", dev_name);\n\n    /* Hack around waiting for permissions to be set on the USB device node.\n     * Should really be a timeout instead of attempt count, and should REALLY\n     * be triggered by the perm change via inotify rather than polling.\n     */\n    for (attempts = 0; attempts < MAX_ATTEMPTS; ++attempts) {\n        if (access(dev_name, R_OK | W_OK) == 0) {\n            writeable = 1;\n            break;\n        } else {\n            if (access(dev_name, R_OK) == 0) {\n                /* double check that write permission didn't just come along too! */\n                writeable = (access(dev_name, R_OK | W_OK) == 0);\n                break;\n            }\n        }\n        /* not writeable or readable - sleep and try again. */\n        D(\"usb_device_open no access sleeping\\n\");\n        usleep(SLEEP_BETWEEN_ATTEMPTS_US);\n    }\n\n    if (writeable) {\n        fd = open(dev_name, O_RDWR);\n    } else {\n        fd = open(dev_name, O_RDONLY);\n    }\n    D(\"usb_device_open open returned %d writeable %d errno %d\\n\", fd, writeable, errno);\n    if (fd < 0) return NULL;\n\n    struct usb_device* result = usb_device_new(dev_name, fd);\n    if (result)\n        result->writeable = writeable;\n    return result;\n}\n\nvoid usb_device_close(struct usb_device *device)\n{\n    close(device->fd);\n    free(device);\n}\n\nstruct usb_device *usb_device_new(const char *dev_name, int fd)\n{\n    struct usb_device *device = calloc(1, sizeof(struct usb_device));\n    int length;\n\n    D(\"usb_device_new %s fd: %d\\n\", dev_name, fd);\n\n    if (lseek(fd, 0, SEEK_SET) != 0)\n        goto failed;\n    length = read(fd, device->desc, sizeof(device->desc));\n    D(\"usb_device_new read returned %d errno %d\\n\", length, errno);\n    if (length < 0)\n        goto failed;\n\n    strncpy(device->dev_name, dev_name, sizeof(device->dev_name) - 1);\n    device->fd = fd;\n    device->desc_length = length;\n    // assume we are writeable, since usb_device_get_fd will only return writeable fds\n    device->writeable = 1;\n    return device;\n\nfailed:\n    // TODO It would be more appropriate to have callers do this\n    // since this function doesn't \"own\" this file descriptor.\n    close(fd);\n    free(device);\n    return NULL;\n}\n\nstatic int usb_device_reopen_writeable(struct usb_device *device)\n{\n    if (device->writeable)\n        return 1;\n\n    int fd = open(device->dev_name, O_RDWR);\n    if (fd >= 0) {\n        close(device->fd);\n        device->fd = fd;\n        device->writeable = 1;\n        return 1;\n    }\n    D(\"usb_device_reopen_writeable failed errno %d\\n\", errno);\n    return 0;\n}\n\nint usb_device_get_fd(struct usb_device *device)\n{\n    if (!usb_device_reopen_writeable(device))\n        return -1;\n    return device->fd;\n}\n\nconst char* usb_device_get_name(struct usb_device *device)\n{\n    return device->dev_name;\n}\n\nint usb_device_get_unique_id(struct usb_device *device)\n{\n    int bus = 0, dev = 0;\n    sscanf(device->dev_name, USB_FS_ID_SCANNER, &bus, &dev);\n    return bus * 1000 + dev;\n}\n\nint usb_device_get_unique_id_from_name(const char* name)\n{\n    int bus = 0, dev = 0;\n    sscanf(name, USB_FS_ID_SCANNER, &bus, &dev);\n    return bus * 1000 + dev;\n}\n\nchar* usb_device_get_name_from_unique_id(int id)\n{\n    int bus = id / 1000;\n    int dev = id % 1000;\n    char* result = (char *)calloc(1, strlen(USB_FS_ID_FORMAT));\n    snprintf(result, strlen(USB_FS_ID_FORMAT) - 1, USB_FS_ID_FORMAT, bus, dev);\n    return result;\n}\n\nuint16_t usb_device_get_vendor_id(struct usb_device *device)\n{\n    struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;\n    return __le16_to_cpu(desc->idVendor);\n}\n\nuint16_t usb_device_get_product_id(struct usb_device *device)\n{\n    struct usb_device_descriptor* desc = (struct usb_device_descriptor*)device->desc;\n    return __le16_to_cpu(desc->idProduct);\n}\n\nconst struct usb_device_descriptor* usb_device_get_device_descriptor(struct usb_device* device) {\n    return (struct usb_device_descriptor*)device->desc;\n}\n\nsize_t usb_device_get_descriptors_length(const struct usb_device* device) {\n    return device->desc_length;\n}\n\nconst unsigned char* usb_device_get_raw_descriptors(const struct usb_device* device) {\n    return device->desc;\n}\n\n/* Returns a USB descriptor string for the given string ID.\n * Return value: < 0 on error.  0 on success.\n * The string is returned in ucs2_out in USB-native UCS-2 encoding.\n *\n * parameters:\n *  id - the string descriptor index.\n *  timeout - in milliseconds (see Documentation/driver-api/usb/usb.rst)\n *  ucs2_out - Must point to null on call.\n *             Will be filled in with a buffer on success.\n *             If this is non-null on return, it must be free()d.\n *  response_size - size, in bytes, of ucs-2 string in ucs2_out.\n *                  The size isn't guaranteed to include null termination.\n * Call free() to free the result when you are done with it.\n */\nint usb_device_get_string_ucs2(struct usb_device* device, int id, int timeout, void** ucs2_out,\n                               size_t* response_size) {\n    __u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];\n    char response[MAX_STRING_DESCRIPTOR_LENGTH];\n    int result;\n    int languageCount = 0;\n\n    if (id == 0) return -1;\n    if (*ucs2_out != NULL) return -1;\n\n    memset(languages, 0, sizeof(languages));\n\n    // read list of supported languages\n    result = usb_device_control_transfer(device,\n            USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,\n            (USB_DT_STRING << 8) | 0, 0, languages, sizeof(languages),\n            timeout);\n    if (result > 0)\n        languageCount = (result - 2) / 2;\n\n    for (int i = 1; i <= languageCount; i++) {\n        memset(response, 0, sizeof(response));\n\n        result = usb_device_control_transfer(\n            device, USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE, USB_REQ_GET_DESCRIPTOR,\n            (USB_DT_STRING << 8) | id, languages[i], response, sizeof(response), timeout);\n        if (result >= 2) {  // string contents begin at offset 2.\n            int descriptor_len = result - 2;\n            char* out = malloc(descriptor_len + 3);\n            if (out == NULL) {\n                return -1;\n            }\n            memcpy(out, response + 2, descriptor_len);\n            // trail with three additional NULLs, so that there's guaranteed\n            // to be a UCS-2 NULL character beyond whatever USB returned.\n            // The returned string length is still just what USB returned.\n            memset(out + descriptor_len, '\\0', 3);\n            *ucs2_out = (void*)out;\n            *response_size = descriptor_len;\n            return 0;\n        }\n    }\n    return -1;\n}\n\n/* Warning: previously this blindly returned the lower 8 bits of\n * every UCS-2 character in a USB descriptor.  Now it will replace\n * values > 127 with ascii '?'.\n */\nchar* usb_device_get_string(struct usb_device* device, int id, int timeout) {\n    char* ascii_string = NULL;\n    size_t raw_string_len = 0;\n    size_t i;\n    if (usb_device_get_string_ucs2(device, id, timeout, (void**)&ascii_string, &raw_string_len) < 0)\n        return NULL;\n    if (ascii_string == NULL) return NULL;\n    for (i = 0; i < raw_string_len / 2; ++i) {\n        // wire format for USB is always little-endian.\n        char lower = ascii_string[2 * i];\n        char upper = ascii_string[2 * i + 1];\n        if (upper || (lower & 0x80)) {\n            ascii_string[i] = '?';\n        } else {\n            ascii_string[i] = lower;\n        }\n    }\n    ascii_string[i] = '\\0';\n    return ascii_string;\n}\n\nchar* usb_device_get_manufacturer_name(struct usb_device *device, int timeout)\n{\n    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;\n    return usb_device_get_string(device, desc->iManufacturer, timeout);\n}\n\nchar* usb_device_get_product_name(struct usb_device *device, int timeout)\n{\n    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;\n    return usb_device_get_string(device, desc->iProduct, timeout);\n}\n\nint usb_device_get_version(struct usb_device *device)\n{\n    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;\n    return desc->bcdUSB;\n}\n\nchar* usb_device_get_serial(struct usb_device *device, int timeout)\n{\n    struct usb_device_descriptor *desc = (struct usb_device_descriptor *)device->desc;\n    return usb_device_get_string(device, desc->iSerialNumber, timeout);\n}\n\nint usb_device_is_writeable(struct usb_device *device)\n{\n    return device->writeable;\n}\n\nvoid usb_descriptor_iter_init(struct usb_device *device, struct usb_descriptor_iter *iter)\n{\n    iter->config = device->desc;\n    iter->config_end = device->desc + device->desc_length;\n    iter->curr_desc = device->desc;\n}\n\nstruct usb_descriptor_header *usb_descriptor_iter_next(struct usb_descriptor_iter *iter)\n{\n    struct usb_descriptor_header* next;\n    if (iter->curr_desc >= iter->config_end)\n        return NULL;\n    next = (struct usb_descriptor_header*)iter->curr_desc;\n    // Corrupt descriptor with zero length, cannot continue iterating\n    if (next->bLength == 0) {\n       D(\"usb_descriptor_iter_next got zero length USB descriptor, ending iteration\\n\");\n       return NULL;\n    }\n    iter->curr_desc += next->bLength;\n    return next;\n}\n\nint usb_device_claim_interface(struct usb_device *device, unsigned int interface)\n{\n    return ioctl(device->fd, USBDEVFS_CLAIMINTERFACE, &interface);\n}\n\nint usb_device_release_interface(struct usb_device *device, unsigned int interface)\n{\n    return ioctl(device->fd, USBDEVFS_RELEASEINTERFACE, &interface);\n}\n\nint usb_device_connect_kernel_driver(struct usb_device *device,\n        unsigned int interface, int connect)\n{\n    struct usbdevfs_ioctl ctl;\n\n    ctl.ifno = interface;\n    ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT);\n    ctl.data = NULL;\n    return ioctl(device->fd, USBDEVFS_IOCTL, &ctl);\n}\n\nint usb_device_set_configuration(struct usb_device *device, int configuration)\n{\n    return ioctl(device->fd, USBDEVFS_SETCONFIGURATION, &configuration);\n}\n\nint usb_device_set_interface(struct usb_device *device, unsigned int interface,\n                            unsigned int alt_setting)\n{\n    struct usbdevfs_setinterface ctl;\n\n    ctl.interface = interface;\n    ctl.altsetting = alt_setting;\n    return ioctl(device->fd, USBDEVFS_SETINTERFACE, &ctl);\n}\n\nint usb_device_control_transfer(struct usb_device *device,\n                            int requestType,\n                            int request,\n                            int value,\n                            int index,\n                            void* buffer,\n                            int length,\n                            unsigned int timeout)\n{\n    struct usbdevfs_ctrltransfer  ctrl;\n\n    // this usually requires read/write permission\n    if (!usb_device_reopen_writeable(device))\n        return -1;\n\n    memset(&ctrl, 0, sizeof(ctrl));\n    ctrl.bRequestType = requestType;\n    ctrl.bRequest = request;\n    ctrl.wValue = value;\n    ctrl.wIndex = index;\n    ctrl.wLength = length;\n    ctrl.data = buffer;\n    ctrl.timeout = timeout;\n    return ioctl(device->fd, USBDEVFS_CONTROL, &ctrl);\n}\n\nint usb_device_bulk_transfer(struct usb_device *device,\n                            int endpoint,\n                            void* buffer,\n                            unsigned int length,\n                            unsigned int timeout)\n{\n    struct usbdevfs_bulktransfer  ctrl;\n\n    memset(&ctrl, 0, sizeof(ctrl));\n    ctrl.ep = endpoint;\n    ctrl.len = length;\n    ctrl.data = buffer;\n    ctrl.timeout = timeout;\n    return ioctl(device->fd, USBDEVFS_BULK, &ctrl);\n}\n\nint usb_device_reset(struct usb_device *device)\n{\n    return ioctl(device->fd, USBDEVFS_RESET);\n}\n\nstruct usb_request *usb_request_new(struct usb_device *dev,\n        const struct usb_endpoint_descriptor *ep_desc)\n{\n    struct usbdevfs_urb *urb = calloc(1, sizeof(struct usbdevfs_urb));\n    if (!urb)\n        return NULL;\n\n    if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK)\n        urb->type = USBDEVFS_URB_TYPE_BULK;\n    else if ((ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)\n        urb->type = USBDEVFS_URB_TYPE_INTERRUPT;\n    else {\n        D(\"Unsupported endpoint type %d\", ep_desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);\n        free(urb);\n        return NULL;\n    }\n    urb->endpoint = ep_desc->bEndpointAddress;\n\n    struct usb_request *req = calloc(1, sizeof(struct usb_request));\n    if (!req) {\n        free(urb);\n        return NULL;\n    }\n\n    req->dev = dev;\n    req->max_packet_size = __le16_to_cpu(ep_desc->wMaxPacketSize);\n    req->private_data = urb;\n    req->endpoint = urb->endpoint;\n    urb->usercontext = req;\n\n    return req;\n}\n\nvoid usb_request_free(struct usb_request *req)\n{\n    free(req->private_data);\n    free(req);\n}\n\nint usb_request_queue(struct usb_request *req)\n{\n    struct usbdevfs_urb *urb = (struct usbdevfs_urb*)req->private_data;\n    int res;\n\n    urb->status = -1;\n    urb->buffer = req->buffer;\n    urb->buffer_length = req->buffer_length;\n\n    do {\n        res = ioctl(req->dev->fd, USBDEVFS_SUBMITURB, urb);\n    } while((res < 0) && (errno == EINTR));\n\n    return res;\n}\n\nstruct usb_request *usb_request_wait(struct usb_device *dev, int timeoutMillis)\n{\n    // Poll until a request becomes available if there is a timeout\n    if (timeoutMillis > 0) {\n        struct pollfd p = {.fd = dev->fd, .events = POLLOUT, .revents = 0};\n\n        int res = poll(&p, 1, timeoutMillis);\n\n        if (res != 1 || p.revents != POLLOUT) {\n            D(\"[ poll - event %d, error %d]\\n\", p.revents, errno);\n            return NULL;\n        }\n    }\n\n    // Read the request. This should usually succeed as we polled before, but it can fail e.g. when\n    // two threads are reading usb requests at the same time and only a single request is available.\n    struct usbdevfs_urb *urb = NULL;\n    int res = TEMP_FAILURE_RETRY(ioctl(dev->fd, timeoutMillis == -1 ? USBDEVFS_REAPURB :\n                                       USBDEVFS_REAPURBNDELAY, &urb));\n    D(\"%s returned %d\\n\", timeoutMillis == -1 ? \"USBDEVFS_REAPURB\" : \"USBDEVFS_REAPURBNDELAY\", res);\n\n    if (res < 0) {\n        D(\"[ reap urb - error %d]\\n\", errno);\n        return NULL;\n    } else {\n        D(\"[ urb @%p status = %d, actual = %d ]\\n\", urb, urb->status, urb->actual_length);\n\n        struct usb_request *req = (struct usb_request*)urb->usercontext;\n        req->actual_length = urb->actual_length;\n\n        return req;\n    }\n}\n\nint usb_request_cancel(struct usb_request *req)\n{\n    struct usbdevfs_urb *urb = ((struct usbdevfs_urb*)req->private_data);\n    return ioctl(req->dev->fd, USBDEVFS_DISCARDURB, urb);\n}\n"
  },
  {
    "path": "libusbhost/usbhost_jni.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <usbhost/usbhost_jni.h>\n\n#include \"usbhost_private.h\"\n\n#include <errno.h>\n#include <string.h>\n#include <unistd.h>\n\njbyteArray usb_jni_read_descriptors(JNIEnv* env, int fd) {\n    if (TEMP_FAILURE_RETRY(lseek(fd, 0, SEEK_SET)) == -1) {\n        ALOGE(\"usb_jni_read_descriptors(%d): lseek() failed: %s\", fd, strerror(errno));\n        return NULL;\n    }\n\n    jbyte buf[MAX_DESCRIPTORS_LENGTH];\n    ssize_t n = TEMP_FAILURE_RETRY(read(fd, buf, sizeof(buf)));\n    if (n == -1) {\n        ALOGE(\"usb_jni_read_descriptors: read failed: %s\", strerror(errno));\n        return NULL;\n    }\n\n    jbyteArray result = env->NewByteArray(n);\n    if (result) env->SetByteArrayRegion(result, 0, n, buf);\n    return result;\n}\n"
  },
  {
    "path": "libusbhost/usbhost_private.h",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#define LOG_TAG \"usbhost\"\n#include <log/log.h>\n\n// Somewhat arbitrary: Sony has reported needing more than 4KiB (but less\n// than 8KiB), and some frameworks code had 16KiB without any explanation,\n// so we went with the largest of those.\n#define MAX_DESCRIPTORS_LENGTH (16 * 1024)\n"
  },
  {
    "path": "libutils/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"system_core_libutils_license\"],\n}\n\nlicense {\n    name: \"system_core_libutils_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\ncc_library_headers {\n    name: \"libutils_headers\",\n    vendor_available: true,\n    product_available: true,\n    recovery_available: true,\n    vendor_ramdisk_available: true,\n    host_supported: true,\n    native_bridge_supported: true,\n    defaults: [\n        \"apex-lowest-min-sdk-version\",\n    ],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"//apex_available:anyapex\",\n    ],\n\n    header_libs: [\n        \"libbase_headers\",\n        \"libcutils_headers\",\n        \"liblog_headers\",\n        \"libsystem_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libbase_headers\",\n        \"libcutils_headers\",\n        \"liblog_headers\",\n        \"libsystem_headers\",\n    ],\n    export_include_dirs: [\"include\"],\n\n    target: {\n        linux_bionic: {\n            enabled: true,\n        },\n        windows: {\n            enabled: true,\n        },\n    },\n}\n\ncc_defaults {\n    name: \"libutils_defaults_nodeps\",\n    vendor_available: true,\n    product_available: true,\n    recovery_available: true,\n    host_supported: true,\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-Wno-exit-time-destructors\",\n        \"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION\",\n    ],\n\n    sanitize: {\n        misc_undefined: [\"integer\"],\n    },\n\n    target: {\n        android: {\n            cflags: [\"-fvisibility=protected\"],\n\n            shared_libs: [\n                \"libvndksupport\",\n            ],\n\n            sanitize: {\n                misc_undefined: [\"integer\"],\n            },\n        },\n\n        recovery: {\n            exclude_shared_libs: [\"libvndksupport\"],\n        },\n\n        linux_bionic: {\n            enabled: true,\n        },\n\n        darwin: {\n            cflags: [\"-Wno-unused-parameter\"],\n        },\n\n        windows: {\n            cflags: [\n                // Under MinGW, ctype.h doesn't need multi-byte support\n                \"-DMB_CUR_MAX=1\",\n                \"-Wno-unused-private-field\",\n            ],\n\n            enabled: true,\n        },\n    },\n    fuzz_config: {\n        cc: [\"smoreland@google.com\"],\n    },\n}\n\ncc_defaults {\n    name: \"libutils_defaults\",\n    defaults: [\n        \"libutils_defaults_nodeps\",\n    ],\n\n    shared_libs: [\n        \"libcutils\",\n        \"liblog\",\n    ],\n}\n\ncc_defaults {\n    name: \"libutils_impl_defaults\",\n    defaults: [\n        \"libutils_defaults\",\n        \"apex-lowest-min-sdk-version\",\n    ],\n    native_bridge_supported: true,\n\n    whole_static_libs: [\"libutils_binder\"],\n\n    header_libs: [\n        \"libbase_headers\",\n        \"libutils_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libutils_headers\",\n    ],\n\n    srcs: [\n        \"FileMap.cpp\",\n        \"JenkinsHash.cpp\",\n        \"LightRefBase.cpp\",\n        \"NativeHandle.cpp\",\n        \"Printer.cpp\",\n        \"StopWatch.cpp\",\n        \"SystemClock.cpp\",\n        \"Threads.cpp\",\n        \"Timers.cpp\",\n        \"Tokenizer.cpp\",\n        \"misc.cpp\",\n    ],\n\n    target: {\n        android: {\n            srcs: [\n                \"Trace.cpp\",\n            ],\n        },\n        linux: {\n            header_libs: [\"libbase_headers\"],\n            srcs: [\n                \"Looper.cpp\",\n            ],\n        },\n    },\n\n    apex_available: [\n        \"//apex_available:anyapex\",\n        \"//apex_available:platform\",\n    ],\n\n    afdo: true,\n}\n\ncc_library {\n    name: \"libutils\",\n    defaults: [\"libutils_impl_defaults\"],\n\n    double_loadable: true,\n\n    target: {\n        product: {\n            header_abi_checker: {\n                enabled: true,\n                // AFDO affects weak symbols.\n                diff_flags: [\"-allow-adding-removing-weak-symbols\"],\n                ref_dump_dirs: [\"abi-dumps\"],\n            },\n        },\n        vendor: {\n            header_abi_checker: {\n                enabled: true,\n                // AFDO affects weak symbols.\n                diff_flags: [\"-allow-adding-removing-weak-symbols\"],\n                ref_dump_dirs: [\"abi-dumps\"],\n            },\n        },\n    },\n}\n\ncc_library {\n    name: \"libutils_test_compile\",\n    defaults: [\"libutils_impl_defaults\"],\n\n    cflags: [\n        \"-DDEBUG_CALLBACKS=1\",\n        \"-DDEBUG_POLL_AND_WAKE=1\",\n        \"-DDEBUG_REFS=1\",\n        \"-DDEBUG_TOKENIZER=1\",\n    ],\n\n    visibility: [\":__subpackages__\"],\n}\n\ncc_library {\n    name: \"libutilscallstack\",\n    defaults: [\"libutils_defaults\"],\n    // TODO(b/153609531): remove when no longer needed.\n    native_bridge_supported: true,\n    min_sdk_version: \"29\",\n    double_loadable: true,\n\n    header_libs: [\n        \"libbase_headers\",\n        \"libutils_headers\",\n    ],\n    export_header_lib_headers: [\n        \"libutils_headers\",\n    ],\n\n    srcs: [\n        \"CallStack.cpp\",\n    ],\n\n    shared_libs: [\n        \"libutils\",\n        \"libunwindstack\",\n    ],\n\n    target: {\n        linux: {\n            srcs: [\n                \"ProcessCallStack.cpp\",\n            ],\n        },\n        darwin: {\n            enabled: false,\n        },\n        windows: {\n            enabled: false,\n        },\n    },\n}\n\ncc_defaults {\n    name: \"libutils_fuzz_defaults\",\n    host_supported: true,\n    shared_libs: [\n        \"libutils\",\n        \"libbase\",\n        \"liblog\",\n    ],\n    fuzz_config: {\n        cc: [\n            \"smoreland@google.com\",\n        ],\n        componentid: 128577,\n        description: \"The fuzzer targets the APIs of libutils\",\n        vector: \"local_no_privileges_required\",\n        service_privilege: \"privileged\",\n        users: \"multi_user\",\n        fuzzed_code_usage: \"shipped\",\n    },\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_bitset\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"BitSet_fuzz.cpp\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_filemap\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"FileMap_fuzz.cpp\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_printer\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"Printer_fuzz.cpp\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_callstack\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"CallStack_fuzz.cpp\"],\n    shared_libs: [\n        \"libutilscallstack\",\n    ],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_process_callstack\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"ProcessCallStack_fuzz.cpp\"],\n    shared_libs: [\n        \"libutilscallstack\",\n    ],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_lrucache\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"LruCache_fuzz.cpp\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_looper\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"Looper_fuzz.cpp\"],\n}\n\ncc_test {\n    name: \"libutils_test\",\n    host_supported: true,\n\n    srcs: [\n        \"BitSet_test.cpp\",\n        \"CallStack_test.cpp\",\n        \"FileMap_test.cpp\",\n        \"LruCache_test.cpp\",\n        \"Mutex_test.cpp\",\n        \"Singleton_test.cpp\",\n        \"Timers_test.cpp\",\n    ],\n\n    target: {\n        android: {\n            srcs: [\n                \"SystemClock_test.cpp\",\n            ],\n            shared_libs: [\n                \"libbase\",\n                \"libcutils\",\n                \"liblog\",\n                \"liblzma\",\n                \"libunwindstack\",\n                \"libutils\",\n                \"libutilscallstack\",\n                \"libz\",\n            ],\n        },\n        linux: {\n            srcs: [\n                \"Looper_test.cpp\",\n            ],\n        },\n        host: {\n            static_libs: [\n                \"libbase\",\n                \"liblog\",\n                \"liblzma\",\n                \"libunwindstack_no_dex\",\n                \"libutils\",\n                \"libutilscallstack\",\n                \"libz\",\n            ],\n        },\n    },\n\n    data_libs: [\n        \"libutils_test_singleton1\",\n        \"libutils_test_singleton2\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n        \"-Wthread-safety\",\n    ],\n\n    test_suites: [\"device-tests\"],\n}\n\ncc_test_library {\n    name: \"libutils_test_singleton1\",\n    host_supported: true,\n    installable: false,\n    srcs: [\"Singleton_test1.cpp\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    header_libs: [\"libutils_headers\"],\n}\n\ncc_test_library {\n    name: \"libutils_test_singleton2\",\n    host_supported: true,\n    installable: false,\n    srcs: [\"Singleton_test2.cpp\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\"libutils_test_singleton1\"],\n    header_libs: [\"libutils_headers\"],\n}\n"
  },
  {
    "path": "libutils/BitSet_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <functional>\n\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/BitSet.h\"\nstatic constexpr uint8_t MAX_OPERATIONS = 50;\n\n// We need to handle both 32 and 64 bit bitsets, so we use a function template\n// here. Sadly, std::function can't be generic, so we generate a vector of\n// std::functions using this function.\ntemplate <typename T>\nstd::vector<std::function<void(T, uint32_t)>> getOperationsForType() {\n    return {\n            [](T bs, uint32_t val) -> void { bs.markBit(val); },\n            [](T bs, uint32_t val) -> void { bs.valueForBit(val); },\n            [](T bs, uint32_t val) -> void { bs.hasBit(val); },\n            [](T bs, uint32_t val) -> void { bs.clearBit(val); },\n            [](T bs, uint32_t val) -> void { bs.getIndexOfBit(val); },\n            [](T bs, uint32_t) -> void { bs.clearFirstMarkedBit(); },\n            [](T bs, uint32_t) -> void { bs.markFirstUnmarkedBit(); },\n            [](T bs, uint32_t) -> void { bs.clearLastMarkedBit(); },\n            [](T bs, uint32_t) -> void { bs.clear(); },\n            [](T bs, uint32_t) -> void { bs.count(); },\n            [](T bs, uint32_t) -> void { bs.isEmpty(); },\n            [](T bs, uint32_t) -> void { bs.isFull(); },\n            [](T bs, uint32_t) -> void { bs.firstMarkedBit(); },\n            [](T bs, uint32_t) -> void { bs.lastMarkedBit(); },\n    };\n}\n\n// Our operations for 32 and 64 bit bitsets\nstatic const std::vector<std::function<void(android::BitSet32, uint32_t)>> thirtyTwoBitOps =\n        getOperationsForType<android::BitSet32>();\nstatic const std::vector<std::function<void(android::BitSet64, uint32_t)>> sixtyFourBitOps =\n        getOperationsForType<android::BitSet64>();\n\nvoid runOperationFor32Bit(android::BitSet32 bs, uint32_t bit, uint8_t operation) {\n    thirtyTwoBitOps[operation](bs, bit);\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    uint32_t thirty_two_base = dataProvider.ConsumeIntegral<uint32_t>();\n    uint64_t sixty_four_base = dataProvider.ConsumeIntegral<uint64_t>();\n    android::BitSet32 b1 = android::BitSet32(thirty_two_base);\n    android::BitSet64 b2 = android::BitSet64(sixty_four_base);\n\n    size_t opsRun = 0;\n    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {\n        uint32_t bit = dataProvider.ConsumeIntegral<uint32_t>();\n        uint8_t op = dataProvider.ConsumeIntegral<uint8_t>();\n        thirtyTwoBitOps[op % thirtyTwoBitOps.size()](b1, bit);\n        sixtyFourBitOps[op % sixtyFourBitOps.size()](b2, bit);\n    }\n    return 0;\n}\n"
  },
  {
    "path": "libutils/BitSet_test.cpp",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"BitSet_test\"\n\n#include <unistd.h>\n\n#include <android/log.h>\n#include <gtest/gtest.h>\n#include <utils/BitSet.h>\n\nnamespace android {\n\nclass BitSet32Test : public testing::Test {\nprotected:\n    BitSet32 b1;\n    BitSet32 b2;\n    virtual void TearDown() {\n        b1.clear();\n        b2.clear();\n    }\n};\n\n\nTEST_F(BitSet32Test, BitWiseOr) {\n    b1.markBit(2);\n    b2.markBit(4);\n\n    BitSet32 tmp = b1 | b2;\n    EXPECT_EQ(tmp.count(), 2u);\n    EXPECT_TRUE(tmp.hasBit(2) && tmp.hasBit(4));\n    // Check that the operator is symmetric\n    EXPECT_TRUE((b2 | b1) == (b1 | b2));\n\n    b1 |= b2;\n    EXPECT_EQ(b1.count(), 2u);\n    EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4));\n    EXPECT_TRUE(b2.hasBit(4) && b2.count() == 1u);\n}\nTEST_F(BitSet32Test, BitWiseAnd_Disjoint) {\n    b1.markBit(2);\n    b1.markBit(4);\n    b1.markBit(6);\n\n    BitSet32 tmp = b1 & b2;\n    EXPECT_TRUE(tmp.isEmpty());\n    // Check that the operator is symmetric\n    EXPECT_TRUE((b2 & b1) == (b1 & b2));\n\n    b2 &= b1;\n    EXPECT_TRUE(b2.isEmpty());\n    EXPECT_EQ(b1.count(), 3u);\n    EXPECT_TRUE(b1.hasBit(2) && b1.hasBit(4) && b1.hasBit(6));\n}\n\nTEST_F(BitSet32Test, BitWiseAnd_NonDisjoint) {\n    b1.markBit(2);\n    b1.markBit(4);\n    b1.markBit(6);\n    b2.markBit(3);\n    b2.markBit(6);\n    b2.markBit(9);\n\n    BitSet32 tmp = b1 & b2;\n    EXPECT_EQ(tmp.count(), 1u);\n    EXPECT_TRUE(tmp.hasBit(6));\n    // Check that the operator is symmetric\n    EXPECT_TRUE((b2 & b1) == (b1 & b2));\n\n    b1 &= b2;\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_EQ(b2.count(), 3u);\n    EXPECT_TRUE(b2.hasBit(3) && b2.hasBit(6) && b2.hasBit(9));\n}\n\nTEST_F(BitSet32Test, MarkFirstUnmarkedBit) {\n    b1.markBit(1);\n\n    b1.markFirstUnmarkedBit();\n    EXPECT_EQ(b1.count(), 2u);\n    EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1));\n\n    b1.markFirstUnmarkedBit();\n    EXPECT_EQ(b1.count(), 3u);\n    EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2));\n}\n\nTEST_F(BitSet32Test, ClearFirstMarkedBit) {\n    b1.markBit(0);\n    b1.markBit(10);\n\n    b1.clearFirstMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(10));\n\n    b1.markBit(30);\n    b1.clearFirstMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(30));\n}\n\nTEST_F(BitSet32Test, ClearLastMarkedBit) {\n    b1.markBit(10);\n    b1.markBit(31);\n\n    b1.clearLastMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(10));\n\n    b1.markBit(5);\n    b1.clearLastMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(5));\n}\n\nTEST_F(BitSet32Test, FillAndClear) {\n    EXPECT_TRUE(b1.isEmpty());\n    for (size_t i = 0; i < 32; i++) {\n        b1.markFirstUnmarkedBit();\n    }\n    EXPECT_TRUE(b1.isFull());\n    b1.clear();\n    EXPECT_TRUE(b1.isEmpty());\n}\n\nTEST_F(BitSet32Test, GetIndexOfBit) {\n    b1.markBit(1);\n    b1.markBit(4);\n    EXPECT_EQ(0U, b1.getIndexOfBit(1));\n    EXPECT_EQ(1U, b1.getIndexOfBit(4));\n    b1.markFirstUnmarkedBit();\n    EXPECT_EQ(1U, b1.getIndexOfBit(1));\n    EXPECT_EQ(2U, b1.getIndexOfBit(4));\n}\n\nclass BitSet64Test : public testing::Test {\nprotected:\n    BitSet64 b1;\n    BitSet64 b2;\n    virtual void TearDown() {\n        b1.clear();\n        b2.clear();\n    }\n};\n\n\nTEST_F(BitSet64Test, BitWiseOr) {\n    b1.markBit(20);\n    b2.markBit(40);\n\n    BitSet64 tmp = b1 | b2;\n    EXPECT_EQ(tmp.count(), 2u);\n    EXPECT_TRUE(tmp.hasBit(20) && tmp.hasBit(40));\n    // Check that the operator is symmetric\n    EXPECT_TRUE((b2 | b1) == (b1 | b2));\n\n    b1 |= b2;\n    EXPECT_EQ(b1.count(), 2u);\n    EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40));\n    EXPECT_TRUE(b2.hasBit(40) && b2.count() == 1u);\n}\nTEST_F(BitSet64Test, BitWiseAnd_Disjoint) {\n    b1.markBit(20);\n    b1.markBit(40);\n    b1.markBit(60);\n\n    BitSet64 tmp = b1 & b2;\n    EXPECT_TRUE(tmp.isEmpty());\n    // Check that the operator is symmetric\n    EXPECT_TRUE((b2 & b1) == (b1 & b2));\n\n    b2 &= b1;\n    EXPECT_TRUE(b2.isEmpty());\n    EXPECT_EQ(b1.count(), 3u);\n    EXPECT_TRUE(b1.hasBit(20) && b1.hasBit(40) && b1.hasBit(60));\n}\n\nTEST_F(BitSet64Test, BitWiseAnd_NonDisjoint) {\n    b1.markBit(20);\n    b1.markBit(40);\n    b1.markBit(60);\n    b2.markBit(30);\n    b2.markBit(60);\n    b2.markBit(63);\n\n    BitSet64 tmp = b1 & b2;\n    EXPECT_EQ(tmp.count(), 1u);\n    EXPECT_TRUE(tmp.hasBit(60));\n    // Check that the operator is symmetric\n    EXPECT_TRUE((b2 & b1) == (b1 & b2));\n\n    b1 &= b2;\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_EQ(b2.count(), 3u);\n    EXPECT_TRUE(b2.hasBit(30) && b2.hasBit(60) && b2.hasBit(63));\n}\n\nTEST_F(BitSet64Test, MarkFirstUnmarkedBit) {\n    b1.markBit(1);\n\n    b1.markFirstUnmarkedBit();\n    EXPECT_EQ(b1.count(), 2u);\n    EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1));\n\n    b1.markFirstUnmarkedBit();\n    EXPECT_EQ(b1.count(), 3u);\n    EXPECT_TRUE(b1.hasBit(0) && b1.hasBit(1) && b1.hasBit(2));\n}\n\nTEST_F(BitSet64Test, ClearFirstMarkedBit) {\n    b1.markBit(0);\n    b1.markBit(10);\n\n    b1.clearFirstMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(10));\n\n    b1.markBit(50);\n    b1.clearFirstMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(50));\n}\n\nTEST_F(BitSet64Test, ClearLastMarkedBit) {\n    b1.markBit(10);\n    b1.markBit(63);\n\n    b1.clearLastMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(10));\n\n    b1.markBit(5);\n    b1.clearLastMarkedBit();\n    EXPECT_EQ(b1.count(), 1u);\n    EXPECT_TRUE(b1.hasBit(5));\n}\n\nTEST_F(BitSet64Test, FillAndClear) {\n    EXPECT_TRUE(b1.isEmpty());\n    for (size_t i = 0; i < 64; i++) {\n        b1.markFirstUnmarkedBit();\n    }\n    EXPECT_TRUE(b1.isFull());\n    b1.clear();\n    EXPECT_TRUE(b1.isEmpty());\n}\n\nTEST_F(BitSet64Test, GetIndexOfBit) {\n    b1.markBit(10);\n    b1.markBit(40);\n    EXPECT_EQ(0U, b1.getIndexOfBit(10));\n    EXPECT_EQ(1U, b1.getIndexOfBit(40));\n    b1.markFirstUnmarkedBit();\n    EXPECT_EQ(1U, b1.getIndexOfBit(10));\n    EXPECT_EQ(2U, b1.getIndexOfBit(40));\n}\n\n} // namespace android\n"
  },
  {
    "path": "libutils/CallStack.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"CallStack\"\n\n#include <utils/Printer.h>\n#include <utils/Errors.h>\n#include <log/log.h>\n\n#include <unwindstack/AndroidUnwinder.h>\n\n#define CALLSTACK_WEAK  // Don't generate weak definitions.\n#include <utils/CallStack.h>\n\nnamespace android {\n\nCallStack::CallStack() {\n}\n\nCallStack::CallStack(const char* logtag, int32_t ignoreDepth) {\n    this->update(ignoreDepth+1);\n    this->log(logtag);\n}\n\nCallStack::~CallStack() {\n}\n\nvoid CallStack::update(int32_t ignoreDepth, pid_t tid) {\n    if (ignoreDepth < 0) {\n        ignoreDepth = 0;\n    }\n\n    mFrameLines.clear();\n\n    unwindstack::AndroidLocalUnwinder unwinder;\n    unwindstack::AndroidUnwinderData data;\n    std::optional<pid_t> tid_val;\n    if (tid != -1) {\n        tid_val = tid;\n    }\n    if (!unwinder.Unwind(tid_val, data)) {\n        ALOGW(\"%s: Failed to unwind callstack: %s\", __FUNCTION__, data.GetErrorString().c_str());\n    }\n    for (size_t i = ignoreDepth; i < data.frames.size(); i++) {\n        auto& frame = data.frames[i];\n        frame.num -= ignoreDepth;\n        mFrameLines.push_back(String8(unwinder.FormatFrame(frame).c_str()));\n    }\n}\n\nvoid CallStack::log(const char* logtag, android_LogPriority priority, const char* prefix) const {\n    LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);\n    print(printer);\n}\n\nvoid CallStack::dump(int fd, int indent, const char* prefix) const {\n    FdPrinter printer(fd, indent, prefix);\n    print(printer);\n}\n\nString8 CallStack::toString(const char* prefix) const {\n    String8 str;\n\n    String8Printer printer(&str, prefix);\n    print(printer);\n\n    return str;\n}\n\nvoid CallStack::print(Printer& printer) const {\n    for (size_t i = 0; i < mFrameLines.size(); i++) {\n        printer.printLine(mFrameLines[i].c_str());\n    }\n}\n\n// The following four functions may be used via weak symbol references from libutils.\n// Clients assume that if any of these symbols are available, then deleteStack() is.\n\n// Apple and Windows does not support this, so only compile on other platforms.\n#if !defined(__APPLE__) && !defined(_WIN32)\n\nCallStack::CallStackUPtr CallStack::getCurrentInternal(int ignoreDepth) {\n    CallStack::CallStackUPtr stack(new CallStack());\n    stack->update(ignoreDepth + 1);\n    return stack;\n}\n\nvoid CallStack::logStackInternal(const char* logtag, const CallStack* stack,\n                                 android_LogPriority priority) {\n    stack->log(logtag, priority);\n}\n\nString8 CallStack::stackToStringInternal(const char* prefix, const CallStack* stack) {\n    return stack->toString(prefix);\n}\n\nvoid CallStack::deleteStack(CallStack* stack) {\n    delete stack;\n}\n\n#endif  // !defined(__APPLE__) && !defined(_WIN32)\n\n}; // namespace android\n"
  },
  {
    "path": "libutils/CallStack_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <memory.h>\n\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/CallStack.h\"\n\nstatic constexpr int MAX_STRING_SIZE = 500;\nstatic constexpr int MAX_IGNORE_DEPTH = 200;\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    size_t ignoreDepth = dataProvider.ConsumeIntegralInRange<size_t>(0, MAX_IGNORE_DEPTH);\n    int logPriority = dataProvider.ConsumeIntegral<int>();\n    pid_t tid = dataProvider.ConsumeIntegral<pid_t>();\n    std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);\n    std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STRING_SIZE);\n\n    const char* logTagChars = logTag.c_str();\n    const char* prefixChars = prefix.c_str();\n\n    android::CallStack::CallStackUPtr callStack = android::CallStack::getCurrent(ignoreDepth);\n    android::CallStack* callstackPtr = callStack.get();\n    android::CallStack::logStack(logTagChars, callstackPtr,\n                                 static_cast<android_LogPriority>(logPriority));\n    android::CallStack::stackToString(prefixChars);\n\n    callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);\n    callstackPtr->clear();\n    callstackPtr->getCurrent(ignoreDepth);\n    callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);\n    callstackPtr->update(ignoreDepth, tid);\n    callstackPtr->log(logTagChars, static_cast<android_LogPriority>(logPriority), prefixChars);\n\n    return 0;\n}\n"
  },
  {
    "path": "libutils/CallStack_test.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <unistd.h>\n\n#include <thread>\n\n#include <android-base/test_utils.h>\n#include <android-base/threads.h>\n#include <gtest/gtest.h>\n#include <utils/CallStack.h>\n\n#if defined(__ANDROID__)\n#include <log/log.h>\n#include <log/log_read.h>\n#endif\n\n__attribute__((__noinline__)) extern \"C\" void CurrentCaller(android::String8& backtrace) {\n    android::CallStack cs;\n    cs.update();\n    backtrace = cs.toString();\n}\n\nTEST(CallStackTest, current_backtrace) {\n    android::String8 backtrace;\n    CurrentCaller(backtrace);\n\n    ASSERT_NE(-1, backtrace.find(\"(CurrentCaller\")) << \"Full backtrace:\\n\" << backtrace;\n}\n\n__attribute__((__noinline__)) extern \"C\" void ThreadBusyWait(std::atomic<pid_t>* tid,\n                                                             volatile bool* done) {\n    *tid = android::base::GetThreadId();\n    while (!*done) {\n    }\n}\n\nTEST(CallStackTest, thread_backtrace) {\n    // Use a volatile to avoid any problems unwinding since sometimes\n    // accessing a std::atomic does not include unwind data at every\n    // instruction and leads to failed unwinds.\n    volatile bool done = false;\n    std::atomic<pid_t> tid = -1;\n    std::thread thread([&tid, &done]() { ThreadBusyWait(&tid, &done); });\n\n    while (tid == -1) {\n    }\n\n    android::CallStack cs;\n    cs.update(0, tid);\n\n    done = true;\n    thread.join();\n\n    ASSERT_NE(-1, cs.toString().find(\"(ThreadBusyWait\")) << \"Full backtrace:\\n\" << cs.toString();\n}\n\n#if defined(__ANDROID__)\nTEST(CallStackTest, log_stack) {\n    android::CallStack::logStack(\"callstack_test\");\n    auto logger_list = android_logger_list_open(android_name_to_log_id(\"main\"),\n                                                ANDROID_LOG_NONBLOCK,\n                                                INT_MAX /* tail */, getpid());\n    ASSERT_NE(nullptr, logger_list);\n    std::string log;\n    while (true) {\n        log_msg log_msg;\n        auto ret = android_logger_list_read(logger_list, &log_msg);\n        if (ret == -EAGAIN) {\n            break;\n        }\n        ASSERT_GT(ret, 0);\n        if (log_msg.msg() == nullptr) {\n            continue;\n        }\n        // First get the tag.\n        char* msg = &log_msg.msg()[1];\n        if (std::string(msg) != \"callstack_test\") {\n            continue;\n        }\n        // Now move past the tag.\n        msg = &msg[strlen(msg) + 1];\n        log += msg;\n        log += '\\n';\n    }\n    ASSERT_NE(\"\", log) << \"No messages found in the log from the test.\";\n    // Look for a backtrace line such as:\n    //   #00 pc 00000000000536e4  libutils_test (testing::Test::Run()+436)\n    ASSERT_MATCH(log, \"#\\\\d+ pc \\\\d+\");\n    android_logger_list_close(logger_list);\n}\n#endif\n"
  },
  {
    "path": "libutils/CleanSpec.mk",
    "content": "# Copyright (C) 2012 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# If you don't need to do a full clean build but would like to touch\n# a file or delete some intermediate files, add a clean step to the end\n# of the list.  These steps will only be run once, if they haven't been\n# run before.\n#\n# E.g.:\n#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)\n#     $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)\n#\n# Always use \"touch -c\" and \"rm -f\" or \"rm -rf\" to gracefully deal with\n# files that are missing or have been moved.\n#\n# Use $(PRODUCT_OUT) to get to the \"out/target/product/blah/\" directory.\n# Use $(OUT_DIR) to refer to the \"out\" directory.\n#\n# If you need to re-do something that's already mentioned, just copy\n# the command and add it to the bottom of the list.  E.g., if a change\n# that you made last week required touching a file and a change you\n# made today requires touching the same file, just copy the old\n# touch step and add it to the end of the list.\n#\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n\n# For example:\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)\n#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)\n#$(call add-clean-step, find $(OUT_DIR) -type f -name \"IGTalkSession*\" -print0 | xargs -0 rm -f)\n#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)\n\n# ************************************************\n# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST\n# ************************************************\n$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/libutils_intermediates/import_includes)\n$(call add-clean-step, rm -rf $(HOST_OUT)/obj/STATIC_LIBRARIES/lib64utils_intermediates/import_includes)\n"
  },
  {
    "path": "libutils/FileMap.cpp",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Shared file mapping class.\n//\n\n#define LOG_TAG \"filemap\"\n\n#include <utils/FileMap.h>\n#include <log/log.h>\n\n#if defined(__MINGW32__) && !defined(__USE_MINGW_ANSI_STDIO)\n# define PRId32 \"I32d\"\n# define PRIx32 \"I32x\"\n# define PRId64 \"I64d\"\n#else\n#include <inttypes.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n\n#if !defined(__MINGW32__)\n#include <sys/mman.h>\n#endif\n\n#include <string.h>\n#include <memory.h>\n#include <errno.h>\n#include <assert.h>\n\nusing namespace android;\n\n/*static*/ long FileMap::mPageSize = -1;\n\n// Constructor.  Create an empty object.\nFileMap::FileMap(void)\n    : mFileName(nullptr),\n      mBasePtr(nullptr),\n      mBaseLength(0),\n      mDataPtr(nullptr),\n      mDataLength(0)\n#if defined(__MINGW32__)\n      ,\n      mFileHandle(INVALID_HANDLE_VALUE),\n      mFileMapping(NULL)\n#endif\n{\n}\n\n// Move Constructor.\nFileMap::FileMap(FileMap&& other) noexcept\n    : mFileName(other.mFileName),\n      mBasePtr(other.mBasePtr),\n      mBaseLength(other.mBaseLength),\n      mDataOffset(other.mDataOffset),\n      mDataPtr(other.mDataPtr),\n      mDataLength(other.mDataLength)\n#if defined(__MINGW32__)\n      ,\n      mFileHandle(other.mFileHandle),\n      mFileMapping(other.mFileMapping)\n#endif\n{\n    other.mFileName = nullptr;\n    other.mBasePtr = nullptr;\n    other.mDataPtr = nullptr;\n#if defined(__MINGW32__)\n    other.mFileHandle = INVALID_HANDLE_VALUE;\n    other.mFileMapping = NULL;\n#endif\n}\n\n// Move assign operator.\nFileMap& FileMap::operator=(FileMap&& other) noexcept {\n    mFileName = other.mFileName;\n    mBasePtr = other.mBasePtr;\n    mBaseLength = other.mBaseLength;\n    mDataOffset = other.mDataOffset;\n    mDataPtr = other.mDataPtr;\n    mDataLength = other.mDataLength;\n    other.mFileName = nullptr;\n    other.mBasePtr = nullptr;\n    other.mDataPtr = nullptr;\n#if defined(__MINGW32__)\n    mFileHandle = other.mFileHandle;\n    mFileMapping = other.mFileMapping;\n    other.mFileHandle = INVALID_HANDLE_VALUE;\n    other.mFileMapping = NULL;\n#endif\n    return *this;\n}\n\n// Destructor.\nFileMap::~FileMap(void)\n{\n    if (mFileName != nullptr) {\n        free(mFileName);\n    }\n#if defined(__MINGW32__)\n    if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) {\n        ALOGD(\"UnmapViewOfFile(%p) failed, error = %lu\\n\", mBasePtr,\n              GetLastError() );\n    }\n    if (mFileMapping != NULL) {\n        CloseHandle(mFileMapping);\n    }\n#else\n    if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) {\n        ALOGD(\"munmap(%p, %zu) failed\\n\", mBasePtr, mBaseLength);\n    }\n#endif\n}\n\n\n// Create a new mapping on an open file.\n//\n// Closing the file descriptor does not unmap the pages, so we don't\n// claim ownership of the fd.\n//\n// Returns \"false\" on failure.\nbool FileMap::create(const char* origFileName, int fd, off64_t offset, size_t length,\n        bool readOnly)\n{\n#if defined(__MINGW32__)\n    int     adjust;\n    off64_t adjOffset;\n    size_t  adjLength;\n\n    if (mPageSize == -1) {\n        SYSTEM_INFO  si;\n\n        GetSystemInfo( &si );\n        mPageSize = si.dwAllocationGranularity;\n    }\n\n    DWORD  protect = readOnly ? PAGE_READONLY : PAGE_READWRITE;\n\n    mFileHandle  = (HANDLE) _get_osfhandle(fd);\n    mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL);\n    if (mFileMapping == NULL) {\n        ALOGE(\"CreateFileMapping(%p, %lx) failed with error %lu\\n\",\n              mFileHandle, protect, GetLastError() );\n        return false;\n    }\n\n    adjust    = offset % mPageSize;\n    adjOffset = offset - adjust;\n    adjLength = length + adjust;\n\n    mBasePtr = MapViewOfFile( mFileMapping,\n                              readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS,\n                              0,\n                              (DWORD)(adjOffset),\n                              adjLength );\n    if (mBasePtr == NULL) {\n        ALOGE(\"MapViewOfFile(%\" PRId64 \", %zu) failed with error %lu\\n\",\n              adjOffset, adjLength, GetLastError() );\n        CloseHandle(mFileMapping);\n        mFileMapping = NULL;\n        return false;\n    }\n#else // !defined(__MINGW32__)\n    assert(fd >= 0);\n    assert(offset >= 0);\n    assert(length > 0);\n\n    // init on first use\n    if (mPageSize == -1) {\n        mPageSize = sysconf(_SC_PAGESIZE);\n        if (mPageSize == -1) {\n            ALOGE(\"could not get _SC_PAGESIZE\\n\");\n            return false;\n        }\n    }\n\n    int adjust = offset % mPageSize;\n    off64_t adjOffset = offset - adjust;\n    size_t adjLength;\n    if (__builtin_add_overflow(length, adjust, &adjLength)) {\n        ALOGE(\"adjusted length overflow: length %zu adjust %d\", length, adjust);\n        return false;\n    }\n\n    int flags = MAP_SHARED;\n    int prot = PROT_READ;\n    if (!readOnly) prot |= PROT_WRITE;\n\n    void* ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset);\n    if (ptr == MAP_FAILED) {\n        if (errno == EINVAL && length == 0) {\n            ptr = nullptr;\n            adjust = 0;\n        } else {\n            ALOGE(\"mmap(%lld,%zu) failed: %s\\n\", (long long)adjOffset, adjLength, strerror(errno));\n            return false;\n        }\n    }\n    mBasePtr = ptr;\n#endif // !defined(__MINGW32__)\n\n    mFileName = origFileName != nullptr ? strdup(origFileName) : nullptr;\n    mBaseLength = adjLength;\n    mDataOffset = offset;\n    mDataPtr = (char*) mBasePtr + adjust;\n    mDataLength = length;\n\n    ALOGV(\"MAP: base %p/%zu data %p/%zu\\n\",\n        mBasePtr, mBaseLength, mDataPtr, mDataLength);\n\n    return true;\n}\n\n// Provide guidance to the system.\n#if !defined(_WIN32)\nint FileMap::advise(MapAdvice advice)\n{\n    int cc, sysAdvice;\n\n    switch (advice) {\n        case NORMAL:        sysAdvice = MADV_NORMAL;        break;\n        case RANDOM:        sysAdvice = MADV_RANDOM;        break;\n        case SEQUENTIAL:    sysAdvice = MADV_SEQUENTIAL;    break;\n        case WILLNEED:      sysAdvice = MADV_WILLNEED;      break;\n        case DONTNEED:      sysAdvice = MADV_DONTNEED;      break;\n        default:\n                            assert(false);\n                            return -1;\n    }\n\n    cc = madvise(mBasePtr, mBaseLength, sysAdvice);\n    if (cc != 0)\n        ALOGW(\"madvise(%d) failed: %s\\n\", sysAdvice, strerror(errno));\n    return cc;\n}\n\n#else\nint FileMap::advise(MapAdvice /* advice */)\n{\n    return -1;\n}\n#endif\n"
  },
  {
    "path": "libutils/FileMap_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <iostream>\n\n#include \"android-base/file.h\"\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/FileMap.h\"\n\nstatic constexpr uint16_t MAX_STR_SIZE = 256;\nstatic constexpr uint8_t MAX_FILENAME_SIZE = 32;\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    TemporaryFile tf;\n    // Generate file contents\n    std::string contents = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);\n    // If we have string contents, dump them into the file.\n    // Otherwise, just leave it as an empty file.\n    if (contents.length() > 0) {\n        const char* bytes = contents.c_str();\n        android::base::WriteStringToFd(bytes, tf.fd);\n    }\n    android::FileMap m;\n    // Generate create() params\n    std::string orig_name = dataProvider.ConsumeRandomLengthString(MAX_FILENAME_SIZE);\n    size_t length = dataProvider.ConsumeIntegralInRange<size_t>(1, SIZE_MAX);\n    off64_t offset = dataProvider.ConsumeIntegralInRange<off64_t>(1, INT64_MAX);\n    bool read_only = dataProvider.ConsumeBool();\n    m.create(orig_name.c_str(), tf.fd, offset, length, read_only);\n    m.getDataOffset();\n    m.getFileName();\n    m.getDataLength();\n    m.getDataPtr();\n    int enum_index = dataProvider.ConsumeIntegral<int>();\n    m.advise(static_cast<android::FileMap::MapAdvice>(enum_index));\n    return 0;\n}\n"
  },
  {
    "path": "libutils/FileMap_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"utils/FileMap.h\"\n\n#include <gtest/gtest.h>\n\n#include \"android-base/file.h\"\n\nTEST(FileMap, zero_length_mapping) {\n    // http://b/119818070 \"app crashes when reading asset of zero length\".\n    // mmap fails with EINVAL for a zero length region.\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n\n    android::FileMap m;\n    ASSERT_TRUE(m.create(\"test\", tf.fd, 4096, 0, true));\n    ASSERT_STREQ(\"test\", m.getFileName());\n    ASSERT_EQ(0u, m.getDataLength());\n    ASSERT_EQ(4096, m.getDataOffset());\n}\n\nTEST(FileMap, large_offset) {\n    // Make sure that an offset > INT32_MAX will not fail the create\n    // function. See http://b/155662887.\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n\n    off64_t offset = INT32_MAX + 1024LL;\n\n    // Make the temporary file large enough to pass the mmap.\n    ASSERT_EQ(offset, lseek64(tf.fd, offset, SEEK_SET));\n    char value = 0;\n    ASSERT_EQ(1, write(tf.fd, &value, 1));\n\n    android::FileMap m;\n    ASSERT_TRUE(m.create(\"test\", tf.fd, offset, 0, true));\n    ASSERT_STREQ(\"test\", m.getFileName());\n    ASSERT_EQ(0u, m.getDataLength());\n    ASSERT_EQ(offset, m.getDataOffset());\n}\n\nTEST(FileMap, offset_overflow) {\n    // Make sure that an end that overflows SIZE_MAX will not abort.\n    // See http://b/156997193.\n    TemporaryFile tf;\n    ASSERT_TRUE(tf.fd != -1);\n\n    off64_t offset = 200;\n    size_t length = SIZE_MAX;\n\n    android::FileMap m;\n    ASSERT_FALSE(m.create(\"test\", tf.fd, offset, length, true));\n}\n"
  },
  {
    "path": "libutils/JenkinsHash.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* Implementation of Jenkins one-at-a-time hash function. These choices are\n * optimized for code size and portability, rather than raw speed. But speed\n * should still be quite good.\n **/\n\n#include <stdlib.h>\n#include <utils/JenkinsHash.h>\n\nnamespace android {\n\n#ifdef __clang__\n__attribute__((no_sanitize(\"integer\")))\n#endif\nhash_t JenkinsHashWhiten(uint32_t hash) {\n    hash += (hash << 3);\n    hash ^= (hash >> 11);\n    hash += (hash << 15);\n    return hash;\n}\n\nuint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) {\n    if (size > UINT32_MAX) {\n        abort();\n    }\n    hash = JenkinsHashMix(hash, (uint32_t)size);\n    size_t i;\n    for (i = 0; i < (size & -4); i += 4) {\n        uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24);\n        hash = JenkinsHashMix(hash, data);\n    }\n    if (size & 3) {\n        uint32_t data = bytes[i];\n        data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0;\n        data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0;\n        hash = JenkinsHashMix(hash, data);\n    }\n    return hash;\n}\n\nuint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) {\n    if (size > UINT32_MAX) {\n        abort();\n    }\n    hash = JenkinsHashMix(hash, (uint32_t)size);\n    size_t i;\n    for (i = 0; i < (size & -2); i += 2) {\n        uint32_t data = shorts[i] | (shorts[i+1] << 16);\n        hash = JenkinsHashMix(hash, data);\n    }\n    if (size & 1) {\n        uint32_t data = shorts[i];\n        hash = JenkinsHashMix(hash, data);\n    }\n    return hash;\n}\n\n}\n\n"
  },
  {
    "path": "libutils/LightRefBase.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"LightRefBase\"\n\n#include <utils/LightRefBase.h>\n\n#include <log/log.h>\n\nnamespace android {\n\nvoid LightRefBase_reportIncStrongRequireStrongFailed(const void* thiz) {\n    LOG_ALWAYS_FATAL(\"incStrongRequireStrong() called on %p which isn't already owned\", thiz);\n}\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/Looper.cpp",
    "content": "//\n// Copyright 2010 The Android Open Source Project\n//\n// A looper implementation based on epoll().\n//\n#define LOG_TAG \"Looper\"\n\n//#define LOG_NDEBUG 0\n\n// Debugs poll and wake interactions.\n#ifndef DEBUG_POLL_AND_WAKE\n#define DEBUG_POLL_AND_WAKE 0\n#endif\n\n// Debugs callback registration and invocation.\n#ifndef DEBUG_CALLBACKS\n#define DEBUG_CALLBACKS 0\n#endif\n\n#include <utils/Looper.h>\n\n#include <sys/eventfd.h>\n#include <cinttypes>\n\nnamespace android {\n\nnamespace {\n\nconstexpr uint64_t WAKE_EVENT_FD_SEQ = 1;\n\nepoll_event createEpollEvent(uint32_t events, uint64_t seq) {\n    return {.events = events, .data = {.u64 = seq}};\n}\n\n}  // namespace\n\n// --- WeakMessageHandler ---\n\nWeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :\n        mHandler(handler) {\n}\n\nWeakMessageHandler::~WeakMessageHandler() {\n}\n\nvoid WeakMessageHandler::handleMessage(const Message& message) {\n    sp<MessageHandler> handler = mHandler.promote();\n    if (handler != nullptr) {\n        handler->handleMessage(message);\n    }\n}\n\n\n// --- SimpleLooperCallback ---\n\nSimpleLooperCallback::SimpleLooperCallback(Looper_callbackFunc callback) :\n        mCallback(callback) {\n}\n\nSimpleLooperCallback::~SimpleLooperCallback() {\n}\n\nint SimpleLooperCallback::handleEvent(int fd, int events, void* data) {\n    return mCallback(fd, events, data);\n}\n\n\n// --- Looper ---\n\n// Maximum number of file descriptors for which to retrieve poll events each iteration.\nstatic const int EPOLL_MAX_EVENTS = 16;\n\nthread_local static sp<Looper> gThreadLocalLooper;\n\nLooper::Looper(bool allowNonCallbacks)\n    : mAllowNonCallbacks(allowNonCallbacks),\n      mSendingMessage(false),\n      mPolling(false),\n      mEpollRebuildRequired(false),\n      mNextRequestSeq(WAKE_EVENT_FD_SEQ + 1),\n      mResponseIndex(0),\n      mNextMessageUptime(LLONG_MAX) {\n    mWakeEventFd.reset(eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC));\n    LOG_ALWAYS_FATAL_IF(mWakeEventFd.get() < 0, \"Could not make wake event fd: %s\", strerror(errno));\n\n    AutoMutex _l(mLock);\n    rebuildEpollLocked();\n}\n\nLooper::~Looper() {\n}\n\nvoid Looper::setForThread(const sp<Looper>& looper) {\n    gThreadLocalLooper = looper;\n}\n\nsp<Looper> Looper::getForThread() {\n    return gThreadLocalLooper;\n}\n\nsp<Looper> Looper::prepare(int opts) {\n    bool allowNonCallbacks = opts & PREPARE_ALLOW_NON_CALLBACKS;\n    sp<Looper> looper = Looper::getForThread();\n    if (looper == nullptr) {\n        looper = sp<Looper>::make(allowNonCallbacks);\n        Looper::setForThread(looper);\n    }\n    if (looper->getAllowNonCallbacks() != allowNonCallbacks) {\n        ALOGW(\"Looper already prepared for this thread with a different value for the \"\n                \"LOOPER_PREPARE_ALLOW_NON_CALLBACKS option.\");\n    }\n    return looper;\n}\n\nbool Looper::getAllowNonCallbacks() const {\n    return mAllowNonCallbacks;\n}\n\nvoid Looper::rebuildEpollLocked() {\n    // Close old epoll instance if we have one.\n    if (mEpollFd >= 0) {\n#if DEBUG_CALLBACKS\n        ALOGD(\"%p ~ rebuildEpollLocked - rebuilding epoll set\", this);\n#endif\n        mEpollFd.reset();\n    }\n\n    // Allocate the new epoll instance and register the WakeEventFd.\n    mEpollFd.reset(epoll_create1(EPOLL_CLOEXEC));\n    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, \"Could not create epoll instance: %s\", strerror(errno));\n\n    epoll_event wakeEvent = createEpollEvent(EPOLLIN, WAKE_EVENT_FD_SEQ);\n    int result = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, mWakeEventFd.get(), &wakeEvent);\n    LOG_ALWAYS_FATAL_IF(result != 0, \"Could not add wake event fd to epoll instance: %s\",\n                        strerror(errno));\n\n    for (const auto& [seq, request] : mRequests) {\n        epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);\n\n        int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, request.fd, &eventItem);\n        if (epollResult < 0) {\n            ALOGE(\"Error adding epoll events for fd %d while rebuilding epoll set: %s\",\n                  request.fd, strerror(errno));\n        }\n    }\n}\n\nvoid Looper::scheduleEpollRebuildLocked() {\n    if (!mEpollRebuildRequired) {\n#if DEBUG_CALLBACKS\n        ALOGD(\"%p ~ scheduleEpollRebuildLocked - scheduling epoll set rebuild\", this);\n#endif\n        mEpollRebuildRequired = true;\n        wake();\n    }\n}\n\nint Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {\n    int result = 0;\n    for (;;) {\n        while (mResponseIndex < mResponses.size()) {\n            const Response& response = mResponses.itemAt(mResponseIndex++);\n            int ident = response.request.ident;\n            if (ident >= 0) {\n                int fd = response.request.fd;\n                int events = response.events;\n                void* data = response.request.data;\n#if DEBUG_POLL_AND_WAKE\n                ALOGD(\"%p ~ pollOnce - returning signalled identifier %d: \"\n                        \"fd=%d, events=0x%x, data=%p\",\n                        this, ident, fd, events, data);\n#endif\n                if (outFd != nullptr) *outFd = fd;\n                if (outEvents != nullptr) *outEvents = events;\n                if (outData != nullptr) *outData = data;\n                return ident;\n            }\n        }\n\n        if (result != 0) {\n#if DEBUG_POLL_AND_WAKE\n            ALOGD(\"%p ~ pollOnce - returning result %d\", this, result);\n#endif\n            if (outFd != nullptr) *outFd = 0;\n            if (outEvents != nullptr) *outEvents = 0;\n            if (outData != nullptr) *outData = nullptr;\n            return result;\n        }\n\n        result = pollInner(timeoutMillis);\n    }\n}\n\nint Looper::pollInner(int timeoutMillis) {\n#if DEBUG_POLL_AND_WAKE\n    ALOGD(\"%p ~ pollOnce - waiting: timeoutMillis=%d\", this, timeoutMillis);\n#endif\n\n    // Adjust the timeout based on when the next message is due.\n    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {\n        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n        int messageTimeoutMillis = toMillisecondTimeoutDelay(now, mNextMessageUptime);\n        if (messageTimeoutMillis >= 0\n                && (timeoutMillis < 0 || messageTimeoutMillis < timeoutMillis)) {\n            timeoutMillis = messageTimeoutMillis;\n        }\n#if DEBUG_POLL_AND_WAKE\n        ALOGD(\"%p ~ pollOnce - next message in %\" PRId64 \"ns, adjusted timeout: timeoutMillis=%d\",\n                this, mNextMessageUptime - now, timeoutMillis);\n#endif\n    }\n\n    // Poll.\n    int result = POLL_WAKE;\n    mResponses.clear();\n    mResponseIndex = 0;\n\n    // We are about to idle.\n    mPolling = true;\n\n    struct epoll_event eventItems[EPOLL_MAX_EVENTS];\n    int eventCount = epoll_wait(mEpollFd.get(), eventItems, EPOLL_MAX_EVENTS, timeoutMillis);\n\n    // No longer idling.\n    mPolling = false;\n\n    // Acquire lock.\n    mLock.lock();\n\n    // Rebuild epoll set if needed.\n    if (mEpollRebuildRequired) {\n        mEpollRebuildRequired = false;\n        rebuildEpollLocked();\n        goto Done;\n    }\n\n    // Check for poll error.\n    if (eventCount < 0) {\n        if (errno == EINTR) {\n            goto Done;\n        }\n        ALOGW(\"Poll failed with an unexpected error: %s\", strerror(errno));\n        result = POLL_ERROR;\n        goto Done;\n    }\n\n    // Check for poll timeout.\n    if (eventCount == 0) {\n#if DEBUG_POLL_AND_WAKE\n        ALOGD(\"%p ~ pollOnce - timeout\", this);\n#endif\n        result = POLL_TIMEOUT;\n        goto Done;\n    }\n\n    // Handle all events.\n#if DEBUG_POLL_AND_WAKE\n    ALOGD(\"%p ~ pollOnce - handling events from %d fds\", this, eventCount);\n#endif\n\n    for (int i = 0; i < eventCount; i++) {\n        const SequenceNumber seq = eventItems[i].data.u64;\n        uint32_t epollEvents = eventItems[i].events;\n        if (seq == WAKE_EVENT_FD_SEQ) {\n            if (epollEvents & EPOLLIN) {\n                awoken();\n            } else {\n                ALOGW(\"Ignoring unexpected epoll events 0x%x on wake event fd.\", epollEvents);\n            }\n        } else {\n            const auto& request_it = mRequests.find(seq);\n            if (request_it != mRequests.end()) {\n                const auto& request = request_it->second;\n                int events = 0;\n                if (epollEvents & EPOLLIN) events |= EVENT_INPUT;\n                if (epollEvents & EPOLLOUT) events |= EVENT_OUTPUT;\n                if (epollEvents & EPOLLERR) events |= EVENT_ERROR;\n                if (epollEvents & EPOLLHUP) events |= EVENT_HANGUP;\n                mResponses.push({.seq = seq, .events = events, .request = request});\n            } else {\n                ALOGW(\"Ignoring unexpected epoll events 0x%x for sequence number %\" PRIu64\n                      \" that is no longer registered.\",\n                      epollEvents, seq);\n            }\n        }\n    }\nDone: ;\n\n    // Invoke pending message callbacks.\n    mNextMessageUptime = LLONG_MAX;\n    while (mMessageEnvelopes.size() != 0) {\n        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);\n        if (messageEnvelope.uptime <= now) {\n            // Remove the envelope from the list.\n            // We keep a strong reference to the handler until the call to handleMessage\n            // finishes.  Then we drop it so that the handler can be deleted *before*\n            // we reacquire our lock.\n            { // obtain handler\n                sp<MessageHandler> handler = messageEnvelope.handler;\n                Message message = messageEnvelope.message;\n                mMessageEnvelopes.removeAt(0);\n                mSendingMessage = true;\n                mLock.unlock();\n\n#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS\n                ALOGD(\"%p ~ pollOnce - sending message: handler=%p, what=%d\",\n                        this, handler.get(), message.what);\n#endif\n                handler->handleMessage(message);\n            } // release handler\n\n            mLock.lock();\n            mSendingMessage = false;\n            result = POLL_CALLBACK;\n        } else {\n            // The last message left at the head of the queue determines the next wakeup time.\n            mNextMessageUptime = messageEnvelope.uptime;\n            break;\n        }\n    }\n\n    // Release lock.\n    mLock.unlock();\n\n    // Invoke all response callbacks.\n    for (size_t i = 0; i < mResponses.size(); i++) {\n        Response& response = mResponses.editItemAt(i);\n        if (response.request.ident == POLL_CALLBACK) {\n            int fd = response.request.fd;\n            int events = response.events;\n            void* data = response.request.data;\n#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS\n            ALOGD(\"%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p\",\n                    this, response.request.callback.get(), fd, events, data);\n#endif\n            // Invoke the callback.  Note that the file descriptor may be closed by\n            // the callback (and potentially even reused) before the function returns so\n            // we need to be a little careful when removing the file descriptor afterwards.\n            int callbackResult = response.request.callback->handleEvent(fd, events, data);\n            if (callbackResult == 0) {\n                AutoMutex _l(mLock);\n                removeSequenceNumberLocked(response.seq);\n            }\n\n            // Clear the callback reference in the response structure promptly because we\n            // will not clear the response vector itself until the next poll.\n            response.request.callback.clear();\n            result = POLL_CALLBACK;\n        }\n    }\n    return result;\n}\n\nint Looper::pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData) {\n    if (timeoutMillis <= 0) {\n        int result;\n        do {\n            result = pollOnce(timeoutMillis, outFd, outEvents, outData);\n        } while (result == POLL_CALLBACK);\n        return result;\n    } else {\n        nsecs_t endTime = systemTime(SYSTEM_TIME_MONOTONIC)\n                + milliseconds_to_nanoseconds(timeoutMillis);\n\n        for (;;) {\n            int result = pollOnce(timeoutMillis, outFd, outEvents, outData);\n            if (result != POLL_CALLBACK) {\n                return result;\n            }\n\n            nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n            timeoutMillis = toMillisecondTimeoutDelay(now, endTime);\n            if (timeoutMillis == 0) {\n                return POLL_TIMEOUT;\n            }\n        }\n    }\n}\n\nvoid Looper::wake() {\n#if DEBUG_POLL_AND_WAKE\n    ALOGD(\"%p ~ wake\", this);\n#endif\n\n    uint64_t inc = 1;\n    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd.get(), &inc, sizeof(uint64_t)));\n    if (nWrite != sizeof(uint64_t)) {\n        if (errno != EAGAIN) {\n            LOG_ALWAYS_FATAL(\"Could not write wake signal to fd %d (returned %zd): %s\",\n                             mWakeEventFd.get(), nWrite, strerror(errno));\n        }\n    }\n}\n\nvoid Looper::awoken() {\n#if DEBUG_POLL_AND_WAKE\n    ALOGD(\"%p ~ awoken\", this);\n#endif\n\n    uint64_t counter;\n    TEMP_FAILURE_RETRY(read(mWakeEventFd.get(), &counter, sizeof(uint64_t)));\n}\n\nint Looper::addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data) {\n    sp<SimpleLooperCallback> looperCallback;\n    if (callback) {\n        looperCallback = sp<SimpleLooperCallback>::make(callback);\n    }\n    return addFd(fd, ident, events, looperCallback, data);\n}\n\nint Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {\n#if DEBUG_CALLBACKS\n    ALOGD(\"%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p\", this, fd, ident,\n            events, callback.get(), data);\n#endif\n\n    if (!callback.get()) {\n        if (! mAllowNonCallbacks) {\n            ALOGE(\"Invalid attempt to set NULL callback but not allowed for this looper.\");\n            return -1;\n        }\n\n        if (ident < 0) {\n            ALOGE(\"Invalid attempt to set NULL callback with ident < 0.\");\n            return -1;\n        }\n    } else {\n        ident = POLL_CALLBACK;\n    }\n\n    { // acquire lock\n        AutoMutex _l(mLock);\n        // There is a sequence number reserved for the WakeEventFd.\n        if (mNextRequestSeq == WAKE_EVENT_FD_SEQ) mNextRequestSeq++;\n        const SequenceNumber seq = mNextRequestSeq++;\n\n        Request request;\n        request.fd = fd;\n        request.ident = ident;\n        request.events = events;\n        request.callback = callback;\n        request.data = data;\n\n        epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);\n        auto seq_it = mSequenceNumberByFd.find(fd);\n        if (seq_it == mSequenceNumberByFd.end()) {\n            int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);\n            if (epollResult < 0) {\n                ALOGE(\"Error adding epoll events for fd %d: %s\", fd, strerror(errno));\n                return -1;\n            }\n            mRequests.emplace(seq, request);\n            mSequenceNumberByFd.emplace(fd, seq);\n        } else {\n            int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem);\n            if (epollResult < 0) {\n                if (errno == ENOENT) {\n                    // Tolerate ENOENT because it means that an older file descriptor was\n                    // closed before its callback was unregistered and meanwhile a new\n                    // file descriptor with the same number has been created and is now\n                    // being registered for the first time.  This error may occur naturally\n                    // when a callback has the side-effect of closing the file descriptor\n                    // before returning and unregistering itself.  Callback sequence number\n                    // checks further ensure that the race is benign.\n                    //\n                    // Unfortunately due to kernel limitations we need to rebuild the epoll\n                    // set from scratch because it may contain an old file handle that we are\n                    // now unable to remove since its file descriptor is no longer valid.\n                    // No such problem would have occurred if we were using the poll system\n                    // call instead, but that approach carries other disadvantages.\n#if DEBUG_CALLBACKS\n                    ALOGD(\"%p ~ addFd - EPOLL_CTL_MOD failed due to file descriptor \"\n                            \"being recycled, falling back on EPOLL_CTL_ADD: %s\",\n                            this, strerror(errno));\n#endif\n                    epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_ADD, fd, &eventItem);\n                    if (epollResult < 0) {\n                        ALOGE(\"Error modifying or adding epoll events for fd %d: %s\",\n                                fd, strerror(errno));\n                        return -1;\n                    }\n                    scheduleEpollRebuildLocked();\n                } else {\n                    ALOGE(\"Error modifying epoll events for fd %d: %s\", fd, strerror(errno));\n                    return -1;\n                }\n            }\n            const SequenceNumber oldSeq = seq_it->second;\n            mRequests.erase(oldSeq);\n            mRequests.emplace(seq, request);\n            seq_it->second = seq;\n        }\n    } // release lock\n    return 1;\n}\n\nbool Looper::getFdStateDebug(int fd, int* ident, int* events, sp<LooperCallback>* cb, void** data) {\n    AutoMutex _l(mLock);\n    if (auto seqNumIt = mSequenceNumberByFd.find(fd); seqNumIt != mSequenceNumberByFd.cend()) {\n        if (auto reqIt = mRequests.find(seqNumIt->second); reqIt != mRequests.cend()) {\n            const Request& request = reqIt->second;\n            if (ident) *ident = request.ident;\n            if (events) *events = request.events;\n            if (cb) *cb = request.callback;\n            if (data) *data = request.data;\n            return true;\n        }\n    }\n    return false;\n}\n\nint Looper::removeFd(int fd) {\n    AutoMutex _l(mLock);\n    const auto& it = mSequenceNumberByFd.find(fd);\n    if (it == mSequenceNumberByFd.end()) {\n        return 0;\n    }\n    return removeSequenceNumberLocked(it->second);\n}\n\nint Looper::repoll(int fd) {\n    AutoMutex _l(mLock);\n    const auto& it = mSequenceNumberByFd.find(fd);\n    if (it == mSequenceNumberByFd.end()) {\n        return 0;\n    }\n\n    const auto& request_it = mRequests.find(it->second);\n    if (request_it == mRequests.end()) {\n        return 0;\n    }\n    const auto& [seq, request] = *request_it;\n\n    LOG_ALWAYS_FATAL_IF(\n            fd != request.fd,\n            \"Looper has inconsistent data structure. When looking up FD %d found FD %d.\", fd,\n            request_it->second.fd);\n\n    epoll_event eventItem = createEpollEvent(request.getEpollEvents(), seq);\n    if (epoll_ctl(mEpollFd.get(), EPOLL_CTL_MOD, fd, &eventItem) == -1) return 0;\n\n    return 1;  // success\n}\n\nint Looper::removeSequenceNumberLocked(SequenceNumber seq) {\n#if DEBUG_CALLBACKS\n    ALOGD(\"%p ~ removeFd - seq=%\" PRIu64, this, seq);\n#endif\n\n    const auto& request_it = mRequests.find(seq);\n    if (request_it == mRequests.end()) {\n        return 0;\n    }\n    const int fd = request_it->second.fd;\n\n    // Always remove the FD from the request map even if an error occurs while\n    // updating the epoll set so that we avoid accidentally leaking callbacks.\n    mRequests.erase(request_it);\n    mSequenceNumberByFd.erase(fd);\n\n    int epollResult = epoll_ctl(mEpollFd.get(), EPOLL_CTL_DEL, fd, nullptr);\n    if (epollResult < 0) {\n        if (errno == EBADF || errno == ENOENT) {\n            // Tolerate EBADF or ENOENT because it means that the file descriptor was closed\n            // before its callback was unregistered. This error may occur naturally when a\n            // callback has the side-effect of closing the file descriptor before returning and\n            // unregistering itself.\n            //\n            // Unfortunately due to kernel limitations we need to rebuild the epoll\n            // set from scratch because it may contain an old file handle that we are\n            // now unable to remove since its file descriptor is no longer valid.\n            // No such problem would have occurred if we were using the poll system\n            // call instead, but that approach carries other disadvantages.\n#if DEBUG_CALLBACKS\n            ALOGD(\"%p ~ removeFd - EPOLL_CTL_DEL failed due to file descriptor \"\n                  \"being closed: %s\",\n                  this, strerror(errno));\n#endif\n            scheduleEpollRebuildLocked();\n        } else {\n            // Some other error occurred.  This is really weird because it means\n            // our list of callbacks got out of sync with the epoll set somehow.\n            // We defensively rebuild the epoll set to avoid getting spurious\n            // notifications with nowhere to go.\n            ALOGE(\"Error removing epoll events for fd %d: %s\", fd, strerror(errno));\n            scheduleEpollRebuildLocked();\n            return -1;\n        }\n    }\n    return 1;\n}\n\nvoid Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {\n    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n    sendMessageAtTime(now, handler, message);\n}\n\nvoid Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,\n        const Message& message) {\n    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n    sendMessageAtTime(now + uptimeDelay, handler, message);\n}\n\nvoid Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,\n        const Message& message) {\n#if DEBUG_CALLBACKS\n    ALOGD(\"%p ~ sendMessageAtTime - uptime=%\" PRId64 \", handler=%p, what=%d\",\n            this, uptime, handler.get(), message.what);\n#endif\n\n    size_t i = 0;\n    { // acquire lock\n        AutoMutex _l(mLock);\n\n        size_t messageCount = mMessageEnvelopes.size();\n        while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {\n            i += 1;\n        }\n\n        MessageEnvelope messageEnvelope(uptime, handler, message);\n        mMessageEnvelopes.insertAt(messageEnvelope, i, 1);\n\n        // Optimization: If the Looper is currently sending a message, then we can skip\n        // the call to wake() because the next thing the Looper will do after processing\n        // messages is to decide when the next wakeup time should be.  In fact, it does\n        // not even matter whether this code is running on the Looper thread.\n        if (mSendingMessage) {\n            return;\n        }\n    } // release lock\n\n    // Wake the poll loop only when we enqueue a new message at the head.\n    if (i == 0) {\n        wake();\n    }\n}\n\nvoid Looper::removeMessages(const sp<MessageHandler>& handler) {\n#if DEBUG_CALLBACKS\n    ALOGD(\"%p ~ removeMessages - handler=%p\", this, handler.get());\n#endif\n\n    { // acquire lock\n        AutoMutex _l(mLock);\n\n        for (size_t i = mMessageEnvelopes.size(); i != 0; ) {\n            const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);\n            if (messageEnvelope.handler == handler) {\n                mMessageEnvelopes.removeAt(i);\n            }\n        }\n    } // release lock\n}\n\nvoid Looper::removeMessages(const sp<MessageHandler>& handler, int what) {\n#if DEBUG_CALLBACKS\n    ALOGD(\"%p ~ removeMessages - handler=%p, what=%d\", this, handler.get(), what);\n#endif\n\n    { // acquire lock\n        AutoMutex _l(mLock);\n\n        for (size_t i = mMessageEnvelopes.size(); i != 0; ) {\n            const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);\n            if (messageEnvelope.handler == handler\n                    && messageEnvelope.message.what == what) {\n                mMessageEnvelopes.removeAt(i);\n            }\n        }\n    } // release lock\n}\n\nbool Looper::isPolling() const {\n    return mPolling;\n}\n\nuint32_t Looper::Request::getEpollEvents() const {\n    uint32_t epollEvents = 0;\n    if (events & EVENT_INPUT) epollEvents |= EPOLLIN;\n    if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;\n    return epollEvents;\n}\n\nMessageHandler::~MessageHandler() { }\n\nLooperCallback::~LooperCallback() { }\n\n} // namespace android\n"
  },
  {
    "path": "libutils/Looper_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/select.h>\n\n#include <iostream>\n\n#include <utils/Looper.h>\n\n#include \"Looper_test_pipe.h\"\n#include \"fuzzer/FuzzedDataProvider.h\"\n\nusing android::Looper;\nusing android::sp;\n\n// We don't want this to bog down fuzzing\nstatic constexpr int MAX_POLL_DELAY = 50;\nstatic constexpr int MAX_OPERATIONS = 500;\n\nvoid doNothing() {}\nvoid* doNothingPointer = reinterpret_cast<void*>(doNothing);\n\nstatic int noopCallback(int, int, void*) {\n    return 0;\n}\n\nstd::vector<std::function<void(FuzzedDataProvider*, sp<Looper>, Pipe)>> operations = {\n        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {\n            looper->pollOnce(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));\n        },\n        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe) -> void {\n            looper->pollAll(dataProvider->ConsumeIntegralInRange<int>(0, MAX_POLL_DELAY));\n        },\n        // events and callback are nullptr\n        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {\n            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),\n                          dataProvider->ConsumeIntegral<int>(), nullptr, nullptr);\n        },\n        // Events is nullptr\n        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {\n            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),\n                          dataProvider->ConsumeIntegral<int>(), noopCallback, nullptr);\n        },\n        // callback is nullptr\n        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {\n            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),\n                          dataProvider->ConsumeIntegral<int>(), nullptr, doNothingPointer);\n        },\n        // callback and events both set\n        [](FuzzedDataProvider* dataProvider, sp<Looper> looper, Pipe pipeObj) -> void {\n            looper->addFd(pipeObj.receiveFd, dataProvider->ConsumeIntegral<int>(),\n                          dataProvider->ConsumeIntegral<int>(), noopCallback, doNothingPointer);\n        },\n\n        [](FuzzedDataProvider*, sp<Looper> looper, Pipe) -> void { looper->wake(); },\n        [](FuzzedDataProvider*, sp<Looper>, Pipe pipeObj) -> void { pipeObj.writeSignal(); }};\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    Pipe pipeObj;\n    FuzzedDataProvider dataProvider(data, size);\n    sp<Looper> looper = new Looper(dataProvider.ConsumeBool());\n\n    size_t opsRun = 0;\n    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {\n        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);\n        operations[op](&dataProvider, looper, pipeObj);\n    }\n    // Clear our pointer\n    looper.clear();\n    return 0;\n}\n"
  },
  {
    "path": "libutils/Looper_test.cpp",
    "content": "//\n// Copyright 2010 The Android Open Source Project\n//\n\n#include <gtest/gtest.h>\n#include <time.h>\n#include <unistd.h>\n#include <utils/Looper.h>\n#include <utils/StopWatch.h>\n#include <utils/Timers.h>\n#include <thread>\n#include <unordered_map>\n#include <utility>\n#include \"Looper_test_pipe.h\"\n\n#include <utils/threads.h>\n\n// b/141212746 - increased for virtual platforms with higher volatility\n// # of milliseconds to fudge stopwatch measurements\n#define TIMING_TOLERANCE_MS 100\n\nnamespace android {\n\nenum {\n    MSG_TEST1 = 1,\n    MSG_TEST2 = 2,\n    MSG_TEST3 = 3,\n    MSG_TEST4 = 4,\n};\n\nclass DelayedTask : public Thread {\n    int mDelayMillis;\n\npublic:\n    explicit DelayedTask(int delayMillis) : mDelayMillis(delayMillis) { }\n\nprotected:\n    virtual ~DelayedTask() { }\n\n    virtual void doTask() = 0;\n\n    virtual bool threadLoop() {\n        usleep(mDelayMillis * 1000);\n        doTask();\n        return false;\n    }\n};\n\nclass DelayedWake : public DelayedTask {\n    sp<Looper> mLooper;\n\npublic:\n    DelayedWake(int delayMillis, const sp<Looper> looper) :\n        DelayedTask(delayMillis), mLooper(looper) {\n    }\n\nprotected:\n    virtual void doTask() {\n        mLooper->wake();\n    }\n};\n\nclass DelayedWriteSignal : public DelayedTask {\n    Pipe* mPipe;\n\npublic:\n    DelayedWriteSignal(int delayMillis, Pipe* pipe) :\n        DelayedTask(delayMillis), mPipe(pipe) {\n    }\n\nprotected:\n    virtual void doTask() {\n        mPipe->writeSignal();\n    }\n};\n\nclass CallbackHandler {\npublic:\n    void setCallback(const sp<Looper>& looper, int fd, int events) {\n        looper->addFd(fd, 0, events, staticHandler, this);\n    }\n\nprotected:\n    virtual ~CallbackHandler() { }\n\n    virtual int handler(int fd, int events) = 0;\n\nprivate:\n    static int staticHandler(int fd, int events, void* data) {\n        return static_cast<CallbackHandler*>(data)->handler(fd, events);\n    }\n};\n\nclass StubCallbackHandler : public CallbackHandler {\npublic:\n    int nextResult;\n    int callbackCount;\n\n    int fd;\n    int events;\n\n    explicit StubCallbackHandler(int nextResult) : nextResult(nextResult),\n            callbackCount(0), fd(-1), events(-1) {\n    }\n\nprotected:\n    virtual int handler(int fd, int events) {\n        callbackCount += 1;\n        this->fd = fd;\n        this->events = events;\n        return nextResult;\n    }\n};\n\nclass StubMessageHandler : public MessageHandler {\npublic:\n    Vector<Message> messages;\n\n    virtual void handleMessage(const Message& message) {\n        messages.push(message);\n    }\n};\n\nclass LooperTest : public testing::Test {\nprotected:\n    sp<Looper> mLooper;\n\n    virtual void SetUp() {\n        mLooper = new Looper(true);\n    }\n\n    virtual void TearDown() {\n        mLooper.clear();\n    }\n};\n\n\nTEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNotAwoken_WaitsForTimeout) {\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. equal timeout\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be LOOPER_POLL_TIMEOUT\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenBeforeWaiting_ImmediatelyReturns) {\n    mLooper->wake();\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(1000);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because wake() was called before waiting\";\n    EXPECT_EQ(Looper::POLL_WAKE, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because loop was awoken\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndAwokenWhileWaiting_PromptlyReturns) {\n    sp<DelayedWake> delayedWake = new DelayedWake(100, mLooper);\n    delayedWake->run(\"LooperTest\");\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(1000);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. equal wake delay\";\n    EXPECT_EQ(Looper::POLL_WAKE, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because loop was awoken\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoRegisteredFDs_ImmediatelyReturns) {\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(0);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should be approx. zero\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndNoSignalledFDs_ImmediatelyReturns) {\n    Pipe pipe;\n    StubCallbackHandler handler(true);\n\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(0);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should be approx. zero\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT\";\n    EXPECT_EQ(0, handler.callbackCount)\n            << \"callback should not have been invoked because FD was not signalled\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenZeroTimeoutAndSignalledFD_ImmediatelyInvokesCallbackAndReturns) {\n    Pipe pipe;\n    StubCallbackHandler handler(true);\n\n    ASSERT_EQ(OK, pipe.writeSignal());\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(0);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should be approx. zero\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because FD was signalled\";\n    EXPECT_EQ(1, handler.callbackCount)\n            << \"callback should be invoked exactly once\";\n    EXPECT_EQ(pipe.receiveFd, handler.fd)\n            << \"callback should have received pipe fd as parameter\";\n    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)\n            << \"callback should have received Looper::EVENT_INPUT as events\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndNoSignalledFDs_WaitsForTimeoutAndReturns) {\n    Pipe pipe;\n    StubCallbackHandler handler(true);\n\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. equal timeout\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT\";\n    EXPECT_EQ(0, handler.callbackCount)\n            << \"callback should not have been invoked because FD was not signalled\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDBeforeWaiting_ImmediatelyInvokesCallbackAndReturns) {\n    Pipe pipe;\n    StubCallbackHandler handler(true);\n\n    pipe.writeSignal();\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    ASSERT_EQ(OK, pipe.readSignal())\n            << \"signal should actually have been written\";\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should be approx. zero\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because FD was signalled\";\n    EXPECT_EQ(1, handler.callbackCount)\n            << \"callback should be invoked exactly once\";\n    EXPECT_EQ(pipe.receiveFd, handler.fd)\n            << \"callback should have received pipe fd as parameter\";\n    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)\n            << \"callback should have received Looper::EVENT_INPUT as events\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenNonZeroTimeoutAndSignalledFDWhileWaiting_PromptlyInvokesCallbackAndReturns) {\n    Pipe pipe;\n    StubCallbackHandler handler(true);\n    sp<DelayedWriteSignal> delayedWriteSignal = new DelayedWriteSignal(100, & pipe);\n\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n    delayedWriteSignal->run(\"LooperTest\");\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(1000);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    ASSERT_EQ(OK, pipe.readSignal())\n            << \"signal should actually have been written\";\n    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. equal signal delay\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because FD was signalled\";\n    EXPECT_EQ(1, handler.callbackCount)\n            << \"callback should be invoked exactly once\";\n    EXPECT_EQ(pipe.receiveFd, handler.fd)\n            << \"callback should have received pipe fd as parameter\";\n    EXPECT_EQ(Looper::EVENT_INPUT, handler.events)\n            << \"callback should have received Looper::EVENT_INPUT as events\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenCallbackAddedThenRemoved_CallbackShouldNotBeInvoked) {\n    Pipe pipe;\n    StubCallbackHandler handler(true);\n\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n    pipe.writeSignal(); // would cause FD to be considered signalled\n    mLooper->removeFd(pipe.receiveFd);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    ASSERT_EQ(OK, pipe.readSignal())\n            << \"signal should actually have been written\";\n    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. equal timeout because FD was no longer registered\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT\";\n    EXPECT_EQ(0, handler.callbackCount)\n            << \"callback should not be invoked\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenCallbackReturnsFalse_CallbackShouldNotBeInvokedAgainLater) {\n    Pipe pipe;\n    StubCallbackHandler handler(false);\n\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n\n    // First loop: Callback is registered and FD is signalled.\n    pipe.writeSignal();\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(0);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    ASSERT_EQ(OK, pipe.readSignal())\n            << \"signal should actually have been written\";\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. equal zero because FD was already signalled\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because FD was signalled\";\n    EXPECT_EQ(1, handler.callbackCount)\n            << \"callback should be invoked\";\n\n    // Second loop: Callback is no longer registered and FD is signalled.\n    pipe.writeSignal();\n\n    stopWatch.reset();\n    result = mLooper->pollOnce(0);\n    elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    ASSERT_EQ(OK, pipe.readSignal())\n            << \"signal should actually have been written\";\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. equal zero because timeout was zero\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT\";\n    EXPECT_EQ(1, handler.callbackCount)\n            << \"callback should not be invoked this time\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenNonCallbackFdIsSignalled_ReturnsIdent) {\n    const int expectedIdent = 5;\n    void* expectedData = this;\n\n    Pipe pipe;\n\n    pipe.writeSignal();\n    mLooper->addFd(pipe.receiveFd, expectedIdent, Looper::EVENT_INPUT, nullptr, expectedData);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int fd;\n    int events;\n    void* data;\n    int result = mLooper->pollOnce(100, &fd, &events, &data);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    ASSERT_EQ(OK, pipe.readSignal())\n            << \"signal should actually have been written\";\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should be approx. zero\";\n    EXPECT_EQ(expectedIdent, result)\n            << \"pollOnce result should be the ident of the FD that was signalled\";\n    EXPECT_EQ(pipe.receiveFd, fd)\n            << \"pollOnce should have returned the received pipe fd\";\n    EXPECT_EQ(Looper::EVENT_INPUT, events)\n            << \"pollOnce should have returned Looper::EVENT_INPUT as events\";\n    EXPECT_EQ(expectedData, data)\n            << \"pollOnce should have returned the data\";\n}\n\nTEST_F(LooperTest, AddFd_WhenCallbackAdded_ReturnsOne) {\n    Pipe pipe;\n    int result = mLooper->addFd(pipe.receiveFd, 0, Looper::EVENT_INPUT, nullptr, nullptr);\n\n    EXPECT_EQ(1, result)\n            << \"addFd should return 1 because FD was added\";\n}\n\nTEST_F(LooperTest, AddFd_WhenIdentIsNegativeAndCallbackIsNull_ReturnsError) {\n    Pipe pipe;\n    int result = mLooper->addFd(pipe.receiveFd, -1, Looper::EVENT_INPUT, nullptr, nullptr);\n\n    EXPECT_EQ(-1, result)\n            << \"addFd should return -1 because arguments were invalid\";\n}\n\nTEST_F(LooperTest, AddFd_WhenNoCallbackAndAllowNonCallbacksIsFalse_ReturnsError) {\n    Pipe pipe;\n    sp<Looper> looper = new Looper(false /*allowNonCallbacks*/);\n    int result = looper->addFd(pipe.receiveFd, 0, 0, nullptr, nullptr);\n\n    EXPECT_EQ(-1, result)\n            << \"addFd should return -1 because arguments were invalid\";\n}\n\nclass LooperCallbackStub final : public LooperCallback {\n  public:\n    LooperCallbackStub(std::function<int()> callback) : mCallback{callback} {}\n\n    int handleEvent(int /*fd*/, int /*events*/, void* /*data*/) override { return mCallback(); }\n\n  private:\n    std::function<int()> mCallback;\n};\n\nTEST_F(LooperTest, getFdStateDebug_WhenFdIsInRequests_ReturnsTrue) {\n    Pipe pipe;\n    const int fd = pipe.receiveFd;\n    constexpr int expectedIdent{Looper::POLL_CALLBACK};\n    sp<LooperCallback> expectedCallback =\n            sp<LooperCallbackStub>::make([]() constexpr -> int { return 0; });\n    void* expectedData = this;\n\n    EXPECT_EQ(1, mLooper->addFd(fd, expectedIdent, Looper::EVENT_INPUT, expectedCallback,\n                                expectedData));\n\n    int ident;\n    int events;\n    sp<LooperCallback> callback;\n    void* data;\n\n    EXPECT_TRUE(mLooper->getFdStateDebug(fd, &ident, &events, &callback, &data));\n\n    EXPECT_EQ(ident, expectedIdent);\n    EXPECT_EQ(events, Looper::EVENT_INPUT);\n    EXPECT_EQ(callback, expectedCallback);\n    EXPECT_EQ(data, expectedData);\n}\n\nTEST_F(LooperTest, getFdStateDebug_WhenFdIsNotInRequests_ReturnsFalse) {\n    Pipe pipe;\n    const int notAddedFd = pipe.receiveFd;\n\n    int ident;\n    int events;\n    sp<LooperCallback> callback;\n    void* data;\n\n    EXPECT_FALSE(mLooper->getFdStateDebug(notAddedFd, &ident, &events, &callback, &data));\n}\n\nTEST_F(LooperTest, RemoveFd_WhenCallbackNotAdded_ReturnsZero) {\n    int result = mLooper->removeFd(1);\n\n    EXPECT_EQ(0, result)\n            << \"removeFd should return 0 because FD not registered\";\n}\n\nTEST_F(LooperTest, RemoveFd_WhenCallbackAddedThenRemovedTwice_ReturnsOnceFirstTimeAndReturnsZeroSecondTime) {\n    Pipe pipe;\n    StubCallbackHandler handler(false);\n    handler.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n\n    // First time.\n    int result = mLooper->removeFd(pipe.receiveFd);\n\n    EXPECT_EQ(1, result)\n            << \"removeFd should return 1 first time because FD was registered\";\n\n    // Second time.\n    result = mLooper->removeFd(pipe.receiveFd);\n\n    EXPECT_EQ(0, result)\n            << \"removeFd should return 0 second time because FD was no longer registered\";\n}\n\nTEST_F(LooperTest, PollOnce_WhenCallbackAddedTwice_OnlySecondCallbackShouldBeInvoked) {\n    Pipe pipe;\n    StubCallbackHandler handler1(true);\n    StubCallbackHandler handler2(true);\n\n    handler1.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT);\n    handler2.setCallback(mLooper, pipe.receiveFd, Looper::EVENT_INPUT); // replace it\n    pipe.writeSignal(); // would cause FD to be considered signalled\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    ASSERT_EQ(OK, pipe.readSignal())\n            << \"signal should actually have been written\";\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because FD was already signalled\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because FD was signalled\";\n    EXPECT_EQ(0, handler1.callbackCount)\n            << \"original handler callback should not be invoked because it was replaced\";\n    EXPECT_EQ(1, handler2.callbackCount)\n            << \"replacement handler callback should be invoked\";\n}\n\nTEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) {\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessage(handler, Message(MSG_TEST1));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was already sent\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n    EXPECT_EQ(size_t(1), handler->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)\n            << \"handled message\";\n}\n\nTEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) {\n    sp<StubMessageHandler> handler1 = new StubMessageHandler();\n    sp<StubMessageHandler> handler2 = new StubMessageHandler();\n    mLooper->sendMessage(handler1, Message(MSG_TEST1));\n    mLooper->sendMessage(handler2, Message(MSG_TEST2));\n    mLooper->sendMessage(handler1, Message(MSG_TEST3));\n    mLooper->sendMessage(handler1, Message(MSG_TEST4));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(1000);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was already sent\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n    EXPECT_EQ(size_t(3), handler1->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST3, handler1->messages[1].what)\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST4, handler1->messages[2].what)\n            << \"handled message\";\n    EXPECT_EQ(size_t(1), handler2->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST2, handler2->messages[0].what)\n            << \"handled message\";\n}\n\nTEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(1000);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"first poll should end quickly because next message timeout was computed\";\n    EXPECT_EQ(Looper::POLL_WAKE, result)\n            << \"pollOnce result should be Looper::POLL_WAKE due to wakeup\";\n    EXPECT_EQ(size_t(0), handler->messages.size())\n            << \"no message handled yet\";\n\n    result = mLooper->pollOnce(1000);\n    elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_EQ(size_t(1), handler->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)\n            << \"handled message\";\n    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"second poll should end around the time of the delayed message dispatch\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n\n    result = mLooper->pollOnce(100);\n    elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"third poll should timeout\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left\";\n}\n\nTEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was already sent\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n    EXPECT_EQ(size_t(1), handler->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)\n            << \"handled message\";\n}\n\nTEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was already sent\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n    EXPECT_EQ(size_t(1), handler->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)\n            << \"handled message\";\n}\n\nTEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {\n    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(1000);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"first poll should end quickly because next message timeout was computed\";\n    EXPECT_EQ(Looper::POLL_WAKE, result)\n            << \"pollOnce result should be Looper::POLL_WAKE due to wakeup\";\n    EXPECT_EQ(size_t(0), handler->messages.size())\n            << \"no message handled yet\";\n\n    result = mLooper->pollOnce(1000);\n    elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_EQ(size_t(1), handler->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)\n            << \"handled message\";\n    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"second poll should end around the time of the delayed message dispatch\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n\n    result = mLooper->pollOnce(100);\n    elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"third poll should timeout\";\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT because there were no messages left\";\n}\n\nTEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {\n    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was already sent\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n    EXPECT_EQ(size_t(1), handler->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)\n            << \"handled message\";\n}\n\nTEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {\n    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1));\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(100);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was already sent\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because message was sent\";\n    EXPECT_EQ(size_t(1), handler->messages.size())\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)\n            << \"handled message\";\n}\n\nTEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) {\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessage(handler, Message(MSG_TEST1));\n    mLooper->sendMessage(handler, Message(MSG_TEST2));\n    mLooper->sendMessage(handler, Message(MSG_TEST3));\n    mLooper->removeMessages(handler);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(0);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was sent so looper was awoken\";\n    EXPECT_EQ(Looper::POLL_WAKE, result)\n            << \"pollOnce result should be Looper::POLL_WAKE because looper was awoken\";\n    EXPECT_EQ(size_t(0), handler->messages.size())\n            << \"no messages to handle\";\n\n    result = mLooper->pollOnce(0);\n\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do\";\n    EXPECT_EQ(size_t(0), handler->messages.size())\n            << \"no messages to handle\";\n}\n\nTEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) {\n    sp<StubMessageHandler> handler = new StubMessageHandler();\n    mLooper->sendMessage(handler, Message(MSG_TEST1));\n    mLooper->sendMessage(handler, Message(MSG_TEST2));\n    mLooper->sendMessage(handler, Message(MSG_TEST3));\n    mLooper->sendMessage(handler, Message(MSG_TEST4));\n    mLooper->removeMessages(handler, MSG_TEST3);\n    mLooper->removeMessages(handler, MSG_TEST1);\n\n    StopWatch stopWatch(\"pollOnce\");\n    int result = mLooper->pollOnce(0);\n    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());\n\n    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)\n            << \"elapsed time should approx. zero because message was sent so looper was awoken\";\n    EXPECT_EQ(Looper::POLL_CALLBACK, result)\n            << \"pollOnce result should be Looper::POLL_CALLBACK because two messages were sent\";\n    EXPECT_EQ(size_t(2), handler->messages.size())\n            << \"no messages to handle\";\n    EXPECT_EQ(MSG_TEST2, handler->messages[0].what)\n            << \"handled message\";\n    EXPECT_EQ(MSG_TEST4, handler->messages[1].what)\n            << \"handled message\";\n\n    result = mLooper->pollOnce(0);\n\n    EXPECT_EQ(Looper::POLL_TIMEOUT, result)\n            << \"pollOnce result should be Looper::POLL_TIMEOUT because there was nothing to do\";\n    EXPECT_EQ(size_t(2), handler->messages.size())\n            << \"no more messages to handle\";\n}\n\nclass LooperEventCallback : public LooperCallback {\n  public:\n    using Callback = std::function<int(int fd, int events)>;\n    explicit LooperEventCallback(Callback callback) : mCallback(std::move(callback)) {}\n    int handleEvent(int fd, int events, void* /*data*/) override { return mCallback(fd, events); }\n\n  private:\n    Callback mCallback;\n};\n\n// A utility class that allows for pipes to be added and removed from the looper, and polls the\n// looper from a different thread.\nclass ThreadedLooperUtil {\n  public:\n    explicit ThreadedLooperUtil(const sp<Looper>& looper) : mLooper(looper), mRunning(true) {\n        mThread = std::thread([this]() {\n            while (mRunning) {\n                static constexpr std::chrono::milliseconds POLL_TIMEOUT(500);\n                mLooper->pollOnce(POLL_TIMEOUT.count());\n            }\n        });\n    }\n\n    ~ThreadedLooperUtil() {\n        mRunning = false;\n        mThread.join();\n    }\n\n    // Create a new pipe, and return the write end of the pipe and the id used to track the pipe.\n    // The read end of the pipe is added to the looper.\n    std::pair<int /*id*/, base::unique_fd> createPipe() {\n        int pipeFd[2];\n        if (pipe(pipeFd)) {\n            ADD_FAILURE() << \"pipe() failed.\";\n            return {};\n        }\n        const int readFd = pipeFd[0];\n        const int writeFd = pipeFd[1];\n\n        int id;\n        {  // acquire lock\n            std::scoped_lock l(mLock);\n\n            id = mNextId++;\n            mFds.emplace(id, readFd);\n\n            auto removeCallback = [this, id, readFd](int fd, int events) {\n                EXPECT_EQ(readFd, fd) << \"Received callback for incorrect fd.\";\n                if ((events & Looper::EVENT_HANGUP) == 0) {\n                    return 1;  // Not a hangup, keep the callback.\n                }\n                removePipe(id);\n                return 0;  // Remove the callback.\n            };\n\n            mLooper->addFd(readFd, 0, Looper::EVENT_INPUT,\n                           new LooperEventCallback(std::move(removeCallback)), nullptr);\n        }  // release lock\n\n        return {id, base::unique_fd(writeFd)};\n    }\n\n    // Remove the pipe with the given id.\n    void removePipe(int id) {\n        std::scoped_lock l(mLock);\n        if (mFds.find(id) == mFds.end()) {\n            return;\n        }\n        mLooper->removeFd(mFds[id].get());\n        mFds.erase(id);\n    }\n\n    // Check if the pipe with the given id exists and has not been removed.\n    bool hasPipe(int id) {\n        std::scoped_lock l(mLock);\n        return mFds.find(id) != mFds.end();\n    }\n\n  private:\n    sp<Looper> mLooper;\n    std::atomic<bool> mRunning;\n    std::thread mThread;\n\n    std::mutex mLock;\n    std::unordered_map<int, base::unique_fd> mFds GUARDED_BY(mLock);\n    int mNextId GUARDED_BY(mLock) = 0;\n};\n\nTEST_F(LooperTest, MultiThreaded_NoUnexpectedFdRemoval) {\n    ThreadedLooperUtil util(mLooper);\n\n    // Iterate repeatedly to try to recreate a flaky instance.\n    for (int i = 0; i < 1000; i++) {\n        auto [firstPipeId, firstPipeFd] = util.createPipe();\n        const int firstFdNumber = firstPipeFd.get();\n\n        // Close the first pipe's fd, causing a fd hangup.\n        firstPipeFd.reset();\n\n        // Request to remove the pipe from this test thread. This causes a race for pipe removal\n        // between the hangup in the looper's thread and this remove request from the test thread.\n        util.removePipe(firstPipeId);\n\n        // Create the second pipe. Since the fds for the first pipe are closed, this pipe should\n        // have the same fd numbers as the first pipe because the lowest unused fd number is used.\n        const auto [secondPipeId, fd] = util.createPipe();\n        EXPECT_EQ(firstFdNumber, fd.get())\n                << \"The first and second fds must match for the purposes of this test.\";\n\n        // Wait for unexpected hangup to occur.\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n\n        ASSERT_TRUE(util.hasPipe(secondPipeId)) << \"The second pipe was removed unexpectedly.\";\n\n        util.removePipe(secondPipeId);\n    }\n    SUCCEED() << \"No unexpectedly removed fds.\";\n}\n\n} // namespace android\n"
  },
  {
    "path": "libutils/Looper_test_pipe.h",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <unistd.h>\n/**\n * A pipe class for use when testing or fuzzing Looper\n */\nclass Pipe {\n  public:\n    int sendFd;\n    int receiveFd;\n\n    Pipe() {\n        int fds[2];\n        ::pipe(fds);\n\n        receiveFd = fds[0];\n        sendFd = fds[1];\n    }\n\n    ~Pipe() {\n        if (sendFd != -1) {\n            ::close(sendFd);\n        }\n\n        if (receiveFd != -1) {\n            ::close(receiveFd);\n        }\n    }\n\n    android::status_t writeSignal() {\n        ssize_t nWritten = ::write(sendFd, \"*\", 1);\n        return nWritten == 1 ? 0 : -errno;\n    }\n\n    android::status_t readSignal() {\n        char buf[1];\n        ssize_t nRead = ::read(receiveFd, buf, 1);\n        return nRead == 1 ? 0 : nRead == 0 ? -EPIPE : -errno;\n    }\n};\n"
  },
  {
    "path": "libutils/LruCache_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <functional>\n\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/LruCache.h\"\n#include \"utils/StrongPointer.h\"\n\ntypedef android::LruCache<size_t, size_t> FuzzCache;\n\nstatic constexpr uint32_t MAX_CACHE_ENTRIES = 800;\n\nclass NoopRemovedCallback : public android::OnEntryRemoved<size_t, size_t> {\n  public:\n    void operator()(size_t&, size_t&) {\n        // noop\n    }\n};\n\nstatic NoopRemovedCallback callback;\n\nstatic const std::vector<std::function<void(FuzzedDataProvider*, FuzzCache*)>> operations = {\n        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->removeOldest(); },\n        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->peekOldestValue(); },\n        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->clear(); },\n        [](FuzzedDataProvider*, FuzzCache* cache) -> void { cache->size(); },\n        [](FuzzedDataProvider*, FuzzCache* cache) -> void {\n            android::LruCache<size_t, size_t>::Iterator iter(*cache);\n            while (iter.next()) {\n                iter.key();\n                iter.value();\n            }\n        },\n        [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {\n            size_t key = dataProvider->ConsumeIntegral<size_t>();\n            size_t val = dataProvider->ConsumeIntegral<size_t>();\n            cache->put(key, val);\n        },\n        [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {\n            size_t key = dataProvider->ConsumeIntegral<size_t>();\n            cache->get(key);\n        },\n        [](FuzzedDataProvider* dataProvider, FuzzCache* cache) -> void {\n            size_t key = dataProvider->ConsumeIntegral<size_t>();\n            cache->remove(key);\n        },\n        [](FuzzedDataProvider*, FuzzCache* cache) -> void {\n            cache->setOnEntryRemovedListener(&callback);\n        }};\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    FuzzCache cache(MAX_CACHE_ENTRIES);\n    while (dataProvider.remaining_bytes() > 0) {\n        uint8_t op = dataProvider.ConsumeIntegral<uint8_t>() % operations.size();\n        operations[op](&dataProvider, &cache);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "libutils/LruCache_test.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdlib.h>\n\n#include <android/log.h>\n#include <gtest/gtest.h>\n#include <utils/JenkinsHash.h>\n#include <utils/LruCache.h>\n\nnamespace {\n\ntypedef int SimpleKey;\ntypedef const char* StringValue;\n\nstruct ComplexKey {\n    int k;\n\n    explicit ComplexKey() : k(0) { instanceCount += 1; }\n\n    explicit ComplexKey(int k) : k(k) {\n        instanceCount += 1;\n    }\n\n    ComplexKey(const ComplexKey& other) : k(other.k) {\n        instanceCount += 1;\n    }\n\n    ~ComplexKey() {\n        instanceCount -= 1;\n    }\n\n    bool operator ==(const ComplexKey& other) const {\n        return k == other.k;\n    }\n\n    bool operator !=(const ComplexKey& other) const {\n        return k != other.k;\n    }\n\n    static ssize_t instanceCount;\n};\n\nssize_t ComplexKey::instanceCount = 0;\n\nstruct ComplexValue {\n    int v;\n\n    explicit ComplexValue() : v(0) { instanceCount += 1; }\n\n    explicit ComplexValue(int v) : v(v) {\n        instanceCount += 1;\n    }\n\n    ComplexValue(const ComplexValue& other) : v(other.v) {\n        instanceCount += 1;\n    }\n\n    ~ComplexValue() {\n        instanceCount -= 1;\n    }\n\n    static ssize_t instanceCount;\n};\n\nssize_t ComplexValue::instanceCount = 0;\n\nstruct KeyWithPointer {\n    int *ptr;\n    bool operator ==(const KeyWithPointer& other) const {\n        return *ptr == *other.ptr;\n    }\n};\n\nstruct KeyFailsOnCopy : public ComplexKey {\n    public:\n      KeyFailsOnCopy() : ComplexKey() {}\n      KeyFailsOnCopy(const KeyFailsOnCopy& key) : ComplexKey(key) { ADD_FAILURE(); }\n      KeyFailsOnCopy(int key) : ComplexKey(key) {}\n};\n\n} // namespace\n\n\nnamespace android {\n\ntypedef LruCache<ComplexKey, ComplexValue> ComplexCache;\n\ntemplate<> inline android::hash_t hash_type(const ComplexKey& value) {\n    return hash_type(value.k);\n}\n\ntemplate<> inline android::hash_t hash_type(const KeyWithPointer& value) {\n    return hash_type(*value.ptr);\n}\n\ntemplate<> inline android::hash_t hash_type(const KeyFailsOnCopy& value) {\n    return hash_type<ComplexKey>(value);\n}\n\nclass EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> {\npublic:\n    EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(nullptr) { }\n    ~EntryRemovedCallback() {}\n    void operator()(SimpleKey& k, StringValue& v) {\n        callbackCount += 1;\n        lastKey = k;\n        lastValue = v;\n    }\n    ssize_t callbackCount;\n    SimpleKey lastKey;\n    StringValue lastValue;\n};\n\nclass InvalidateKeyCallback : public OnEntryRemoved<KeyWithPointer, StringValue> {\npublic:\n    void operator()(KeyWithPointer& k, StringValue&) {\n        delete k.ptr;\n        k.ptr = nullptr;\n    }\n};\n\nclass LruCacheTest : public testing::Test {\nprotected:\n    virtual void SetUp() {\n        ComplexKey::instanceCount = 0;\n        ComplexValue::instanceCount = 0;\n    }\n\n    virtual void TearDown() {\n        ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0));\n    }\n\n    void assertInstanceCount(ssize_t keys, ssize_t values) {\n        if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) {\n            FAIL() << \"Expected \" << keys << \" keys and \" << values << \" values \"\n                    \"but there were actually \" << ComplexKey::instanceCount << \" keys and \"\n                    << ComplexValue::instanceCount << \" values\";\n        }\n    }\n};\n\nTEST_F(LruCacheTest, Empty) {\n    LruCache<SimpleKey, StringValue> cache(100);\n\n    EXPECT_EQ(nullptr, cache.get(0));\n    EXPECT_EQ(0u, cache.size());\n}\n\nTEST_F(LruCacheTest, Simple) {\n    LruCache<SimpleKey, StringValue> cache(100);\n\n    cache.put(1, \"one\");\n    cache.put(2, \"two\");\n    cache.put(3, \"three\");\n    EXPECT_STREQ(\"one\", cache.get(1));\n    EXPECT_STREQ(\"two\", cache.get(2));\n    EXPECT_STREQ(\"three\", cache.get(3));\n    EXPECT_EQ(3u, cache.size());\n}\n\nTEST_F(LruCacheTest, MaxCapacity) {\n    LruCache<SimpleKey, StringValue> cache(2);\n\n    cache.put(1, \"one\");\n    cache.put(2, \"two\");\n    cache.put(3, \"three\");\n    EXPECT_EQ(nullptr, cache.get(1));\n    EXPECT_STREQ(\"two\", cache.get(2));\n    EXPECT_STREQ(\"three\", cache.get(3));\n    EXPECT_EQ(2u, cache.size());\n}\n\nTEST_F(LruCacheTest, RemoveLru) {\n    LruCache<SimpleKey, StringValue> cache(100);\n\n    cache.put(1, \"one\");\n    cache.put(2, \"two\");\n    cache.put(3, \"three\");\n    cache.removeOldest();\n    EXPECT_EQ(nullptr, cache.get(1));\n    EXPECT_STREQ(\"two\", cache.get(2));\n    EXPECT_STREQ(\"three\", cache.get(3));\n    EXPECT_EQ(2u, cache.size());\n}\n\nTEST_F(LruCacheTest, GetUpdatesLru) {\n    LruCache<SimpleKey, StringValue> cache(100);\n\n    cache.put(1, \"one\");\n    cache.put(2, \"two\");\n    cache.put(3, \"three\");\n    EXPECT_STREQ(\"one\", cache.get(1));\n    cache.removeOldest();\n    EXPECT_STREQ(\"one\", cache.get(1));\n    EXPECT_EQ(nullptr, cache.get(2));\n    EXPECT_STREQ(\"three\", cache.get(3));\n    EXPECT_EQ(2u, cache.size());\n}\n\nuint32_t hash_int(int x) {\n    return JenkinsHashWhiten(JenkinsHashMix(0, x));\n}\n\nTEST_F(LruCacheTest, StressTest) {\n    const size_t kCacheSize = 512;\n    LruCache<SimpleKey, StringValue> cache(512);\n    const size_t kNumKeys = 16 * 1024;\n    const size_t kNumIters = 100000;\n    char* strings[kNumKeys];\n\n    for (size_t i = 0; i < kNumKeys; i++) {\n        strings[i] = (char *)malloc(16);\n        sprintf(strings[i], \"%zu\", i);\n    }\n\n    srandom(12345);\n    int hitCount = 0;\n    for (size_t i = 0; i < kNumIters; i++) {\n        int index = random() % kNumKeys;\n        uint32_t key = hash_int(index);\n        const char *val = cache.get(key);\n        if (val != nullptr) {\n            EXPECT_EQ(strings[index], val);\n            hitCount++;\n        } else {\n            cache.put(key, strings[index]);\n        }\n    }\n    size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys;\n    EXPECT_LT(int(expectedHitCount * 0.9), hitCount);\n    EXPECT_GT(int(expectedHitCount * 1.1), hitCount);\n    EXPECT_EQ(kCacheSize, cache.size());\n\n    for (size_t i = 0; i < kNumKeys; i++) {\n        free((void *)strings[i]);\n    }\n}\n\nTEST_F(LruCacheTest, NoLeak) {\n    ComplexCache cache(100);\n\n    cache.put(ComplexKey(0), ComplexValue(0));\n    cache.put(ComplexKey(1), ComplexValue(1));\n    EXPECT_EQ(2U, cache.size());\n    assertInstanceCount(2, 3);  // the member mNullValue counts as an instance\n}\n\nTEST_F(LruCacheTest, Clear) {\n    ComplexCache cache(100);\n\n    cache.put(ComplexKey(0), ComplexValue(0));\n    cache.put(ComplexKey(1), ComplexValue(1));\n    EXPECT_EQ(2U, cache.size());\n    assertInstanceCount(2, 3);\n    cache.clear();\n    assertInstanceCount(0, 1);\n}\n\nTEST_F(LruCacheTest, ClearNoDoubleFree) {\n    {\n        ComplexCache cache(100);\n\n        cache.put(ComplexKey(0), ComplexValue(0));\n        cache.put(ComplexKey(1), ComplexValue(1));\n        EXPECT_EQ(2U, cache.size());\n        assertInstanceCount(2, 3);\n        cache.removeOldest();\n        cache.clear();\n        assertInstanceCount(0, 1);\n    }\n    assertInstanceCount(0, 0);\n}\n\nTEST_F(LruCacheTest, ClearReuseOk) {\n    ComplexCache cache(100);\n\n    cache.put(ComplexKey(0), ComplexValue(0));\n    cache.put(ComplexKey(1), ComplexValue(1));\n    EXPECT_EQ(2U, cache.size());\n    assertInstanceCount(2, 3);\n    cache.clear();\n    assertInstanceCount(0, 1);\n    cache.put(ComplexKey(0), ComplexValue(0));\n    cache.put(ComplexKey(1), ComplexValue(1));\n    EXPECT_EQ(2U, cache.size());\n    assertInstanceCount(2, 3);\n}\n\nTEST_F(LruCacheTest, Callback) {\n    EntryRemovedCallback callback;\n    LruCache<SimpleKey, StringValue> cache(100);\n    cache.setOnEntryRemovedListener(&callback);\n\n    cache.put(1, \"one\");\n    cache.put(2, \"two\");\n    cache.put(3, \"three\");\n    EXPECT_EQ(3U, cache.size());\n    cache.removeOldest();\n    EXPECT_EQ(1, callback.callbackCount);\n    EXPECT_EQ(1, callback.lastKey);\n    EXPECT_STREQ(\"one\", callback.lastValue);\n}\n\nTEST_F(LruCacheTest, CallbackOnClear) {\n    EntryRemovedCallback callback;\n    LruCache<SimpleKey, StringValue> cache(100);\n    cache.setOnEntryRemovedListener(&callback);\n\n    cache.put(1, \"one\");\n    cache.put(2, \"two\");\n    cache.put(3, \"three\");\n    EXPECT_EQ(3U, cache.size());\n    cache.clear();\n    EXPECT_EQ(3, callback.callbackCount);\n}\n\nTEST_F(LruCacheTest, CallbackRemovesKeyWorksOK) {\n    InvalidateKeyCallback callback;\n    LruCache<KeyWithPointer, StringValue> cache(1);\n    cache.setOnEntryRemovedListener(&callback);\n    KeyWithPointer key1;\n    key1.ptr = new int(1);\n    KeyWithPointer key2;\n    key2.ptr = new int(2);\n\n    cache.put(key1, \"one\");\n    // As the size of the cache is 1, the put will call the callback.\n    // Make sure everything goes smoothly even if the callback invalidates\n    // the key (b/24785286)\n    cache.put(key2, \"two\");\n    EXPECT_EQ(1U, cache.size());\n    EXPECT_STREQ(\"two\", cache.get(key2));\n    cache.clear();\n}\n\nTEST_F(LruCacheTest, IteratorCheck) {\n    LruCache<int, int> cache(100);\n\n    cache.put(1, 4);\n    cache.put(2, 5);\n    cache.put(3, 6);\n    EXPECT_EQ(3U, cache.size());\n\n    LruCache<int, int>::Iterator it(cache);\n    std::unordered_set<int> returnedValues;\n    while (it.next()) {\n        int v = it.value();\n        // Check we haven't seen the value before.\n        EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());\n        returnedValues.insert(v);\n    }\n    EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);\n}\n\nTEST_F(LruCacheTest, EmptyCacheIterator) {\n    // Check that nothing crashes...\n    LruCache<int, int> cache(100);\n\n    LruCache<int, int>::Iterator it(cache);\n    std::unordered_set<int> returnedValues;\n    while (it.next()) {\n        returnedValues.insert(it.value());\n    }\n    EXPECT_EQ(std::unordered_set<int>(), returnedValues);\n}\n\nTEST_F(LruCacheTest, OneElementCacheIterator) {\n    // Check that nothing crashes...\n    LruCache<int, int> cache(100);\n    cache.put(1, 2);\n\n    LruCache<int, int>::Iterator it(cache);\n    std::unordered_set<int> returnedValues;\n    while (it.next()) {\n        returnedValues.insert(it.value());\n    }\n    EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);\n}\n\nTEST_F(LruCacheTest, OneElementCacheRemove) {\n    LruCache<int, int> cache(100);\n    cache.put(1, 2);\n\n    cache.remove(1);\n\n    LruCache<int, int>::Iterator it(cache);\n    std::unordered_set<int> returnedValues;\n    while (it.next()) {\n        returnedValues.insert(it.value());\n    }\n    EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);\n}\n\nTEST_F(LruCacheTest, Remove) {\n    LruCache<int, int> cache(100);\n    cache.put(1, 4);\n    cache.put(2, 5);\n    cache.put(3, 6);\n\n    cache.remove(2);\n\n    LruCache<int, int>::Iterator it(cache);\n    std::unordered_set<int> returnedValues;\n    while (it.next()) {\n        returnedValues.insert(it.value());\n    }\n    EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);\n}\n\nTEST_F(LruCacheTest, RemoveYoungest) {\n    LruCache<int, int> cache(100);\n    cache.put(1, 4);\n    cache.put(2, 5);\n    cache.put(3, 6);\n\n    cache.remove(3);\n\n    LruCache<int, int>::Iterator it(cache);\n    std::unordered_set<int> returnedValues;\n    while (it.next()) {\n        returnedValues.insert(it.value());\n    }\n    EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);\n}\n\nTEST_F(LruCacheTest, RemoveNonMember) {\n    LruCache<int, int> cache(100);\n    cache.put(1, 4);\n    cache.put(2, 5);\n    cache.put(3, 6);\n\n    cache.remove(7);\n\n    LruCache<int, int>::Iterator it(cache);\n    std::unordered_set<int> returnedValues;\n    while (it.next()) {\n        returnedValues.insert(it.value());\n    }\n    EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);\n}\n\nTEST_F(LruCacheTest, DontCopyKeyInGet) {\n    LruCache<KeyFailsOnCopy, KeyFailsOnCopy> cache(1);\n    // Check that get doesn't copy the key\n    cache.get(KeyFailsOnCopy(0));\n}\n\n}\n"
  },
  {
    "path": "libutils/MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "libutils/Mutex_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/Mutex.h>\n\n#include <gtest/gtest.h>\n\nstatic android::Mutex mLock;\nstatic int i GUARDED_BY(mLock);\n\nvoid modifyLockedVariable() REQUIRES(mLock) {\n    i = 1;\n}\n\nTEST(Mutex, compile) {\n    android::Mutex::Autolock _l(mLock);\n    i = 0;\n    modifyLockedVariable();\n}\n\nTEST(Mutex, tryLock) {\n    if (mLock.tryLock() != 0) {\n        return;\n    }\n    mLock.unlock();\n}\n\n#if defined(__ANDROID__)\nTEST(Mutex, timedLock) {\n    if (mLock.timedLock(1) != 0) {\n        return;\n    }\n    mLock.unlock();\n}\n#endif\n"
  },
  {
    "path": "libutils/NOTICE",
    "content": "\n   Copyright (c) 2005-2008, The Android Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "libutils/NativeHandle.cpp",
    "content": "/*\n * Copyright 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/NativeHandle.h>\n#include <cutils/native_handle.h>\n\nnamespace android {\n\nsp<NativeHandle> NativeHandle::create(native_handle_t* handle, bool ownsHandle) {\n    return handle ? sp<NativeHandle>::make(handle, ownsHandle) : nullptr;\n}\n\nNativeHandle::NativeHandle(native_handle_t* handle, bool ownsHandle)\n        : mHandle(handle), mOwnsHandle(ownsHandle) {\n\n}\n\nNativeHandle::~NativeHandle() {\n    if (mOwnsHandle) {\n        native_handle_close(mHandle);\n        native_handle_delete(mHandle);\n    }\n}\n\n} // namespace android\n"
  },
  {
    "path": "libutils/OWNERS",
    "content": "shayba@google.com\nsmoreland@google.com\n"
  },
  {
    "path": "libutils/Printer.cpp",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Printer\"\n// #define LOG_NDEBUG 0\n\n#include <utils/Printer.h>\n#include <utils/String8.h>\n#include <log/log.h>\n\n#include <stdlib.h>\n\nnamespace android {\n\n/*\n * Implementation of Printer\n */\nPrinter::Printer() {\n    // Intentionally left empty\n}\n\nPrinter::~Printer() {\n    // Intentionally left empty\n}\n\nvoid Printer::printFormatLine(const char* format, ...) {\n    va_list arglist;\n    va_start(arglist, format);\n\n    char* formattedString;\n\n#ifndef _WIN32\n    if (vasprintf(&formattedString, format, arglist) < 0) { // returns -1 on error\n        ALOGE(\"%s: Failed to format string\", __FUNCTION__);\n        va_end(arglist);\n        return;\n    }\n#else\n    va_end(arglist);\n    return;\n#endif\n\n    va_end(arglist);\n\n    printLine(formattedString);\n    free(formattedString);\n}\n\n/*\n * Implementation of LogPrinter\n */\nLogPrinter::LogPrinter(const char* logtag,\n                       android_LogPriority priority,\n                       const char* prefix,\n                       bool ignoreBlankLines) :\n        mLogTag(logtag),\n        mPriority(priority),\n        mPrefix(prefix ?: \"\"),\n        mIgnoreBlankLines(ignoreBlankLines) {\n}\n\nvoid LogPrinter::printLine(const char* string) {\n    if (string == nullptr) {\n        ALOGW(\"%s: NULL string passed in\", __FUNCTION__);\n        return;\n    }\n\n    if (mIgnoreBlankLines || (*string)) {\n        // Simple case: Line is not blank, or we don't care about printing blank lines\n        printRaw(string);\n    } else {\n        // Force logcat to print empty lines by adding prefixing with a space\n        printRaw(\" \");\n    }\n}\n\nvoid LogPrinter::printRaw(const char* string) {\n    __android_log_print(mPriority, mLogTag, \"%s%s\", mPrefix, string);\n}\n\n\n/*\n * Implementation of FdPrinter\n */\nFdPrinter::FdPrinter(int fd, unsigned int indent, const char* prefix) :\n        mFd(fd), mIndent(indent), mPrefix(prefix ?: \"\") {\n\n    if (fd < 0) {\n        ALOGW(\"%s: File descriptor out of range (%d)\", __FUNCTION__, fd);\n    }\n\n    // <indent><prefix><line> -- e.g. '%-4s%s\\n' for indent=4\n    snprintf(mFormatString, sizeof(mFormatString), \"%%-%us%%s\\n\", mIndent);\n}\n\nvoid FdPrinter::printLine(const char* string) {\n    if (string == nullptr) {\n        ALOGW(\"%s: NULL string passed in\", __FUNCTION__);\n        return;\n    } else if (mFd < 0) {\n        ALOGW(\"%s: File descriptor out of range (%d)\", __FUNCTION__, mFd);\n        return;\n    }\n\n#ifndef _WIN32\n    dprintf(mFd, mFormatString, mPrefix, string);\n#endif\n}\n\n/*\n * Implementation of String8Printer\n */\nString8Printer::String8Printer(String8* target, const char* prefix) :\n        mTarget(target),\n        mPrefix(prefix ?: \"\") {\n\n    if (target == nullptr) {\n        ALOGW(\"%s: Target string was NULL\", __FUNCTION__);\n    }\n}\n\nvoid String8Printer::printLine(const char* string) {\n    if (string == nullptr) {\n        ALOGW(\"%s: NULL string passed in\", __FUNCTION__);\n        return;\n    } else if (mTarget == nullptr) {\n        ALOGW(\"%s: Target string was NULL\", __FUNCTION__);\n        return;\n    }\n\n    mTarget->append(mPrefix);\n    mTarget->append(string);\n    mTarget->append(\"\\n\");\n}\n\n/*\n * Implementation of PrefixPrinter\n */\nPrefixPrinter::PrefixPrinter(Printer& printer, const char* prefix) :\n        mPrinter(printer), mPrefix(prefix ?: \"\") {\n}\n\nvoid PrefixPrinter::printLine(const char* string) {\n    mPrinter.printFormatLine(\"%s%s\", mPrefix, string);\n}\n\n}; //namespace android\n"
  },
  {
    "path": "libutils/Printer_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"android-base/file.h\"\n#include \"android/log.h\"\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/Printer.h\"\n#include \"utils/String8.h\"\nstatic constexpr int MAX_STR_SIZE = 1000;\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    android::String8 outStr = android::String8();\n    // Line indent/formatting\n    uint indent = dataProvider.ConsumeIntegral<uint>();\n    std::string prefix = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);\n    std::string line = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);\n\n    // Misc properties\n    std::string logTag = dataProvider.ConsumeRandomLengthString(MAX_STR_SIZE);\n    android_LogPriority priority =\n            static_cast<android_LogPriority>(dataProvider.ConsumeIntegral<int>());\n    bool ignoreBlankLines = dataProvider.ConsumeBool();\n\n    TemporaryFile tf;\n    android::FdPrinter filePrinter = android::FdPrinter(tf.fd, indent, prefix.c_str());\n    android::String8Printer stringPrinter = android::String8Printer(&outStr);\n    android::PrefixPrinter printer = android::PrefixPrinter(stringPrinter, prefix.c_str());\n    android::LogPrinter logPrinter =\n            android::LogPrinter(logTag.c_str(), priority, prefix.c_str(), ignoreBlankLines);\n\n    printer.printLine(line.c_str());\n    printer.printFormatLine(\"%s\", line.c_str());\n    logPrinter.printLine(line.c_str());\n    logPrinter.printFormatLine(\"%s\", line.c_str());\n    filePrinter.printLine(line.c_str());\n    filePrinter.printFormatLine(\"%s\", line.c_str());\n    return 0;\n}\n"
  },
  {
    "path": "libutils/ProcessCallStack.cpp",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"ProcessCallStack\"\n// #define LOG_NDEBUG 0\n\n#include <utils/ProcessCallStack.h>\n\n#include <dirent.h>\n#include <unistd.h>\n\n#include <memory>\n\n#include <utils/Printer.h>\n\nnamespace android {\n\nenum {\n    // Max sizes for various dynamically generated strings\n    MAX_TIME_STRING = 64,\n    MAX_PROC_PATH = 1024,\n\n    // Dump related prettiness constants\n    IGNORE_DEPTH_CURRENT_THREAD = 2,\n};\n\nstatic const char* CALL_STACK_PREFIX = \"  \";\nstatic const char* PATH_THREAD_NAME = \"/proc/self/task/%d/comm\";\nstatic const char* PATH_SELF_TASK = \"/proc/self/task\";\n\nstatic void dumpProcessHeader(Printer& printer, pid_t pid, const char* timeStr) {\n    if (timeStr == nullptr) {\n        ALOGW(\"%s: timeStr was NULL\", __FUNCTION__);\n        return;\n    }\n\n    char path[PATH_MAX];\n    char procNameBuf[MAX_PROC_PATH];\n    char* procName = nullptr;\n    FILE* fp;\n\n    snprintf(path, sizeof(path), \"/proc/%d/cmdline\", pid);\n    if ((fp = fopen(path, \"r\"))) {\n        procName = fgets(procNameBuf, sizeof(procNameBuf), fp);\n        fclose(fp);\n    }\n\n    if (!procName) {\n        procName = const_cast<char*>(\"<unknown>\");\n    }\n\n    printer.printLine();\n    printer.printLine();\n    printer.printFormatLine(\"----- pid %d at %s -----\", pid, timeStr);\n    printer.printFormatLine(\"Cmd line: %s\", procName);\n}\n\nstatic void dumpProcessFooter(Printer& printer, pid_t pid) {\n    printer.printLine();\n    printer.printFormatLine(\"----- end %d -----\", pid);\n    printer.printLine();\n}\n\nstatic String8 getThreadName(pid_t tid) {\n    char path[PATH_MAX];\n    char* procName = nullptr;\n    char procNameBuf[MAX_PROC_PATH];\n    FILE* fp;\n\n    snprintf(path, sizeof(path), PATH_THREAD_NAME, tid);\n    if ((fp = fopen(path, \"r\"))) {\n        procName = fgets(procNameBuf, sizeof(procNameBuf), fp);\n        fclose(fp);\n    } else {\n        ALOGE(\"%s: Failed to open %s\", __FUNCTION__, path);\n    }\n\n    if (procName == nullptr) {\n        // Reading /proc/self/task/%d/comm failed due to a race\n        return String8::format(\"[err-unknown-tid-%d]\", tid);\n    }\n\n    // Strip ending newline\n    strtok(procName, \"\\n\");\n\n    return String8(procName);\n}\n\nstatic String8 getTimeString(struct tm tm) {\n    char timestr[MAX_TIME_STRING];\n    // i.e. '2013-10-22 14:42:05'\n    strftime(timestr, sizeof(timestr), \"%F %T\", &tm);\n\n    return String8(timestr);\n}\n\n/*\n * Implementation of ProcessCallStack\n */\nProcessCallStack::ProcessCallStack() {\n}\n\nProcessCallStack::ProcessCallStack(const ProcessCallStack& rhs) :\n        mThreadMap(rhs.mThreadMap),\n        mTimeUpdated(rhs.mTimeUpdated) {\n}\n\nProcessCallStack::~ProcessCallStack() {\n}\n\nvoid ProcessCallStack::clear() {\n    mThreadMap.clear();\n    mTimeUpdated = tm();\n}\n\nvoid ProcessCallStack::update() {\n    std::unique_ptr<DIR, decltype(&closedir)> dp(opendir(PATH_SELF_TASK), closedir);\n    if (dp == nullptr) {\n        ALOGE(\"%s: Failed to update the process's call stacks: %s\",\n              __FUNCTION__, strerror(errno));\n        return;\n    }\n\n    pid_t selfPid = getpid();\n\n    clear();\n\n    // Get current time.\n    {\n        time_t t = time(nullptr);\n        struct tm tm;\n        localtime_r(&t, &tm);\n\n        mTimeUpdated = tm;\n    }\n\n    /*\n     * Each tid is a directory inside of /proc/self/task\n     * - Read every file in directory => get every tid\n     */\n    dirent* ep;\n    while ((ep = readdir(dp.get())) != nullptr) {\n        pid_t tid = -1;\n        sscanf(ep->d_name, \"%d\", &tid);\n\n        if (tid < 0) {\n            // Ignore '.' and '..'\n            ALOGV(\"%s: Failed to read tid from %s/%s\",\n                  __FUNCTION__, PATH_SELF_TASK, ep->d_name);\n            continue;\n        }\n\n        ssize_t idx = mThreadMap.add(tid, ThreadInfo());\n        if (idx < 0) { // returns negative error value on error\n            ALOGE(\"%s: Failed to add new ThreadInfo: %s\",\n                  __FUNCTION__, strerror(-idx));\n            continue;\n        }\n\n        ThreadInfo& threadInfo = mThreadMap.editValueAt(static_cast<size_t>(idx));\n\n        /*\n         * Ignore CallStack::update and ProcessCallStack::update for current thread\n         * - Every other thread doesn't need this since we call update off-thread\n         */\n        int ignoreDepth = (selfPid == tid) ? IGNORE_DEPTH_CURRENT_THREAD : 0;\n\n        // Update thread's call stacks\n        threadInfo.callStack.update(ignoreDepth, tid);\n\n        // Read/save thread name\n        threadInfo.threadName = getThreadName(tid);\n\n        ALOGV(\"%s: Got call stack for tid %d (size %zu)\",\n              __FUNCTION__, tid, threadInfo.callStack.size());\n    }\n}\n\nvoid ProcessCallStack::log(const char* logtag, android_LogPriority priority,\n                           const char* prefix) const {\n    LogPrinter printer(logtag, priority, prefix, /*ignoreBlankLines*/false);\n    print(printer);\n}\n\nvoid ProcessCallStack::print(Printer& printer) const {\n    /*\n     * Print the header/footer with the regular printer.\n     * Print the callstack with an additional two spaces as the prefix for legibility.\n     */\n    PrefixPrinter csPrinter(printer, CALL_STACK_PREFIX);\n    printInternal(printer, csPrinter);\n}\n\nvoid ProcessCallStack::printInternal(Printer& printer, Printer& csPrinter) const {\n    dumpProcessHeader(printer, getpid(), getTimeString(mTimeUpdated).c_str());\n\n    for (size_t i = 0; i < mThreadMap.size(); ++i) {\n        pid_t tid = mThreadMap.keyAt(i);\n        const ThreadInfo& threadInfo = mThreadMap.valueAt(i);\n        const String8& threadName = threadInfo.threadName;\n\n        printer.printLine(\"\");\n        printer.printFormatLine(\"\\\"%s\\\" sysTid=%d\", threadName.c_str(), tid);\n\n        threadInfo.callStack.print(csPrinter);\n    }\n\n    dumpProcessFooter(printer, getpid());\n}\n\nvoid ProcessCallStack::dump(int fd, int indent, const char* prefix) const {\n\n    if (indent < 0) {\n        ALOGW(\"%s: Bad indent (%d)\", __FUNCTION__, indent);\n        return;\n    }\n\n    FdPrinter printer(fd, static_cast<unsigned int>(indent), prefix);\n    print(printer);\n}\n\nString8 ProcessCallStack::toString(const char* prefix) const {\n\n    String8 dest;\n    String8Printer printer(&dest, prefix);\n    print(printer);\n\n    return dest;\n}\n\nsize_t ProcessCallStack::size() const {\n    return mThreadMap.size();\n}\n\n}; //namespace android\n"
  },
  {
    "path": "libutils/ProcessCallStack_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <atomic>\n#include <thread>\n\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/ProcessCallStack.h\"\nusing android::ProcessCallStack;\n\nstatic constexpr int MAX_NAME_SIZE = 1000;\nstatic constexpr int MAX_LOG_META_SIZE = 1000;\nstatic constexpr uint8_t MAX_THREADS = 10;\n\nstd::atomic_bool ranCallStackUpdate(false);\nvoid loop() {\n    while (!ranCallStackUpdate.load()) {\n        std::this_thread::sleep_for(std::chrono::milliseconds(50));\n    }\n}\n\nvoid spawnThreads(FuzzedDataProvider* dataProvider) {\n    std::vector<std::thread> threads = std::vector<std::thread>();\n\n    // Get the number of threads to generate\n    uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, MAX_THREADS);\n\n    // Generate threads\n    for (uint8_t i = 0; i < count; i++) {\n        std::string threadName =\n                dataProvider->ConsumeRandomLengthString(MAX_NAME_SIZE).append(std::to_string(i));\n        std::thread th = std::thread(loop);\n        pthread_setname_np(th.native_handle(), threadName.c_str());\n        threads.push_back(std::move(th));\n    }\n\n    // Collect thread information\n    ProcessCallStack callStack = ProcessCallStack();\n    callStack.update();\n\n    // Tell our patiently waiting threads they can be done now.\n    ranCallStackUpdate.store(true);\n\n    std::string logTag = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);\n    std::string prefix = dataProvider->ConsumeRandomLengthString(MAX_LOG_META_SIZE);\n    // Both of these, along with dump, all call print() under the hood,\n    // Which is covered by the Printer fuzzer.\n    callStack.log(logTag.c_str());\n    callStack.toString(prefix.c_str());\n\n    // Check size\n    callStack.size();\n\n    // wait for any remaining threads\n    for (auto& thread : threads) {\n        thread.join();\n    }\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    spawnThreads(&dataProvider);\n    return 0;\n}\n"
  },
  {
    "path": "libutils/Singleton_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Singleton_test\"\n\n#include <dlfcn.h>\n\n#include <android-base/file.h>\n#include <android-base/stringprintf.h>\n#include <utils/Singleton.h>\n\n#include <gtest/gtest.h>\n\n#include \"Singleton_test.h\"\n\nnamespace android {\n\nTEST(SingletonTest, bug35674422) {\n    std::string path = android::base::GetExecutableDirectory();\n    // libutils_test_singleton1.so contains the ANDROID_SINGLETON_STATIC_INSTANCE\n    // definition of SingletonTestData, load it first.\n    std::string lib = android::base::StringPrintf(\"%s/libutils_test_singleton1.so\", path.c_str());\n    void* handle1 = dlopen(lib.c_str(), RTLD_NOW);\n    ASSERT_TRUE(handle1 != nullptr) << dlerror();\n\n    // libutils_test_singleton2.so references SingletonTestData but should not\n    // have a definition\n    lib = android::base::StringPrintf(\"%s/libutils_test_singleton2.so\", path.c_str());\n    void* handle2 = dlopen(lib.c_str(), RTLD_NOW);\n    ASSERT_TRUE(handle2 != nullptr) << dlerror();\n\n    using has_fn_t = decltype(&singletonHasInstance);\n    using get_fn_t = decltype(&singletonGetInstanceContents);\n    using set_fn_t = decltype(&singletonSetInstanceContents);\n\n    has_fn_t has1 = reinterpret_cast<has_fn_t>(dlsym(handle1, \"singletonHasInstance\"));\n    ASSERT_TRUE(has1 != nullptr) << dlerror();\n    has_fn_t has2 = reinterpret_cast<has_fn_t>(dlsym(handle2, \"singletonHasInstance\"));\n    ASSERT_TRUE(has2 != nullptr) << dlerror();\n    get_fn_t get1 = reinterpret_cast<get_fn_t>(dlsym(handle1, \"singletonGetInstanceContents\"));\n    ASSERT_TRUE(get1 != nullptr) << dlerror();\n    get_fn_t get2 = reinterpret_cast<get_fn_t>(dlsym(handle2, \"singletonGetInstanceContents\"));\n    ASSERT_TRUE(get2 != nullptr) << dlerror();\n    set_fn_t set1 = reinterpret_cast<set_fn_t>(dlsym(handle2, \"singletonSetInstanceContents\"));\n    ASSERT_TRUE(set1 != nullptr) << dlerror();\n\n    EXPECT_FALSE(has1());\n    EXPECT_FALSE(has2());\n    set1(12345678U);\n    EXPECT_TRUE(has1());\n    EXPECT_TRUE(has2());\n    EXPECT_EQ(12345678U, get1());\n    EXPECT_EQ(12345678U, get2());\n}\n\n}\n"
  },
  {
    "path": "libutils/Singleton_test.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_UTILS_SINGLETON_TEST_H\n#define ANDROID_UTILS_SINGLETON_TEST_H\n\n#include <sys/cdefs.h>\n\n#include \"Singleton_test.h\"\n\nnamespace android {\n\nstruct SingletonTestData : Singleton<SingletonTestData> {\n    unsigned int contents;\n};\n\n__BEGIN_DECLS\n\nunsigned int singletonGetInstanceContents();\nvoid singletonSetInstanceContents(unsigned int);\nbool singletonHasInstance();\n\n__END_DECLS\n\n}\n\n#endif // ANDROID_UTILS_SINGLETON_TEST_H\n\n"
  },
  {
    "path": "libutils/Singleton_test1.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/Singleton.h>\n\n#include \"Singleton_test.h\"\n\nnamespace android {\n\n// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and\n// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.\nANDROID_SINGLETON_STATIC_INSTANCE(SingletonTestData);\n\nvoid singletonSetInstanceContents(unsigned int contents) {\n    SingletonTestData::getInstance().contents = contents;\n}\n\nunsigned int singletonGetInstanceContents() {\n    return SingletonTestData::getInstance().contents;\n}\n\nbool singletonHasInstance() {\n    return SingletonTestData::hasInstance();\n}\n\n}\n"
  },
  {
    "path": "libutils/Singleton_test2.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/Singleton.h>\n\n#include \"Singleton_test.h\"\n\nnamespace android {\n\n// Singleton<SingletonTestStruct> is referenced in Singleton_test1.cpp and\n// Singleton_test2.cpp, but only defined in Singleton_test1.cpp.\n\nvoid singletonSetInstanceContents(unsigned int contents) {\n    SingletonTestData::getInstance().contents = contents;\n}\n\nunsigned int singletonGetInstanceContents() {\n    return SingletonTestData::getInstance().contents;\n}\n\nbool singletonHasInstance() {\n    return SingletonTestData::hasInstance();\n}\n\n}\n"
  },
  {
    "path": "libutils/StopWatch.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"StopWatch\"\n\n#include <utils/StopWatch.h>\n\n#include <inttypes.h>\n\n#include <log/log.h>\n\nnamespace android {\n\nStopWatch::StopWatch(const char* name, int clock) : mName(name), mClock(clock) {\n    reset();\n}\n\nStopWatch::~StopWatch() {\n    ALOGD(\"StopWatch %s (us): %\" PRId64 \" \", name(), ns2us(elapsedTime()));\n}\n\nconst char* StopWatch::name() const {\n    return mName;\n}\n\nnsecs_t StopWatch::elapsedTime() const {\n    return systemTime(mClock) - mStartTime;\n}\n\nvoid StopWatch::reset() {\n    mStartTime = systemTime(mClock);\n}\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/SystemClock.cpp",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n/*\n * System clock functions.\n */\n\n#define LOG_TAG \"SystemClock\"\n\n#include <utils/SystemClock.h>\n\n#include <string.h>\n#include <errno.h>\n#include <time.h>\n\n#include <cutils/compiler.h>\n\n#include <utils/Timers.h>\n#include <log/log.h>\n\nnamespace android {\n\n/*\n * native public static long uptimeMillis();\n */\nint64_t uptimeMillis()\n{\n    return nanoseconds_to_milliseconds(uptimeNanos());\n}\n\n/*\n * public static native long uptimeNanos();\n */\nint64_t uptimeNanos()\n{\n    return systemTime(SYSTEM_TIME_MONOTONIC);\n}\n\n/*\n * native public static long elapsedRealtime();\n */\nint64_t elapsedRealtime()\n{\n\treturn nanoseconds_to_milliseconds(elapsedRealtimeNano());\n}\n\n/*\n * native public static long elapsedRealtimeNano();\n */\nint64_t elapsedRealtimeNano()\n{\n#if defined(__linux__)\n    struct timespec ts;\n    int err = clock_gettime(CLOCK_BOOTTIME, &ts);\n    if (CC_UNLIKELY(err)) {\n        // This should never happen, but just in case ...\n        ALOGE(\"clock_gettime(CLOCK_BOOTTIME) failed: %s\", strerror(errno));\n        return 0;\n    }\n\n    return seconds_to_nanoseconds(ts.tv_sec) + ts.tv_nsec;\n#else\n    return systemTime(SYSTEM_TIME_MONOTONIC);\n#endif\n}\n\n}; // namespace android\n"
  },
  {
    "path": "libutils/SystemClock_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <unistd.h>\n#include <utils/SystemClock.h>\n\n#include <gtest/gtest.h>\n\nstatic const auto MS_IN_NS = 1000000;\n\nstatic const int64_t SLEEP_MS = 500;\nstatic const int64_t SLEEP_NS = SLEEP_MS * MS_IN_NS;\n// Conservatively assume that we might be descheduled for up to 50 ms\nstatic const int64_t SLACK_MS = 50;\nstatic const int64_t SLACK_NS = SLACK_MS * MS_IN_NS;\n\nTEST(SystemClock, SystemClock) {\n    auto startUptimeMs = android::uptimeMillis();\n    auto startUptimeNs = android::uptimeNanos();\n    auto startRealtimeMs = android::elapsedRealtime();\n    auto startRealtimeNs = android::elapsedRealtimeNano();\n\n    ASSERT_GT(startUptimeMs, 0)\n            << \"uptimeMillis() reported an impossible uptime\";\n    ASSERT_GT(startUptimeNs, 0)\n            << \"uptimeNanos() reported an impossible uptime\";\n    ASSERT_GE(startRealtimeMs, startUptimeMs)\n            << \"elapsedRealtime() thinks we've suspended for negative time\";\n    ASSERT_GE(startRealtimeNs, startUptimeNs)\n            << \"elapsedRealtimeNano() thinks we've suspended for negative time\";\n\n    ASSERT_GE(startUptimeNs, startUptimeMs * MS_IN_NS)\n            << \"uptimeMillis() and uptimeNanos() are inconsistent\";\n    ASSERT_LT(startUptimeNs, (startUptimeMs + SLACK_MS) * MS_IN_NS)\n            << \"uptimeMillis() and uptimeNanos() are inconsistent\";\n\n    ASSERT_GE(startRealtimeNs, startRealtimeMs * MS_IN_NS)\n            << \"elapsedRealtime() and elapsedRealtimeNano() are inconsistent\";\n    ASSERT_LT(startRealtimeNs, (startRealtimeMs + SLACK_MS) * MS_IN_NS)\n            << \"elapsedRealtime() and elapsedRealtimeNano() are inconsistent\";\n\n    timespec ts;\n    ts.tv_sec = 0;\n    ts.tv_nsec = SLEEP_MS * MS_IN_NS;\n    auto nanosleepErr = TEMP_FAILURE_RETRY(nanosleep(&ts, nullptr));\n    ASSERT_EQ(nanosleepErr, 0) << \"nanosleep() failed: \" << strerror(errno);\n\n    auto endUptimeMs = android::uptimeMillis();\n    auto endUptimeNs = android::uptimeNanos();\n    auto endRealtimeMs = android::elapsedRealtime();\n    auto endRealtimeNs = android::elapsedRealtimeNano();\n\n    EXPECT_GE(endUptimeMs - startUptimeMs, SLEEP_MS)\n            << \"uptimeMillis() advanced too little after nanosleep()\";\n    EXPECT_LT(endUptimeMs - startUptimeMs, SLEEP_MS + SLACK_MS)\n            << \"uptimeMillis() advanced too much after nanosleep()\";\n    EXPECT_GE(endUptimeNs - startUptimeNs, SLEEP_NS)\n            << \"uptimeNanos() advanced too little after nanosleep()\";\n    EXPECT_LT(endUptimeNs - startUptimeNs, SLEEP_NS + SLACK_NS)\n            << \"uptimeNanos() advanced too much after nanosleep()\";\n    EXPECT_GE(endRealtimeMs - startRealtimeMs, SLEEP_MS)\n            << \"elapsedRealtime() advanced too little after nanosleep()\";\n    EXPECT_LT(endRealtimeMs - startRealtimeMs, SLEEP_MS + SLACK_MS)\n            << \"elapsedRealtime() advanced too much after nanosleep()\";\n    EXPECT_GE(endRealtimeNs - startRealtimeNs, SLEEP_NS)\n            << \"elapsedRealtimeNano() advanced too little after nanosleep()\";\n    EXPECT_LT(endRealtimeNs - startRealtimeNs, SLEEP_NS + SLACK_NS)\n            << \"elapsedRealtimeNano() advanced too much after nanosleep()\";\n\n    EXPECT_GE(endUptimeNs, endUptimeMs * MS_IN_NS)\n            << \"uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()\";\n    EXPECT_LT(endUptimeNs, (endUptimeMs + SLACK_MS) * MS_IN_NS)\n            << \"uptimeMillis() and uptimeNanos() are inconsistent after nanosleep()\";\n\n    EXPECT_GE(endRealtimeNs, endRealtimeMs * MS_IN_NS)\n            << \"elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()\";\n    EXPECT_LT(endRealtimeNs, (endRealtimeMs + SLACK_MS) * MS_IN_NS)\n            << \"elapsedRealtime() and elapsedRealtimeNano() are inconsistent after nanosleep()\";\n}\n"
  },
  {
    "path": "libutils/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"libutils_test\"\n    },\n    {\n      \"name\": \"libutils_binder_test\"\n    }\n  ]\n}\n"
  },
  {
    "path": "libutils/Threads.cpp",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// #define LOG_NDEBUG 0\n#define LOG_TAG \"libutils.threads\"\n\n#include <assert.h>\n#include <utils/AndroidThreads.h>\n#include <utils/Thread.h>\n\n#if !defined(_WIN32)\n# include <sys/resource.h>\n#else\n# include <windows.h>\n# include <stdint.h>\n# include <process.h>\n# define HAVE_CREATETHREAD  // Cygwin, vs. HAVE__BEGINTHREADEX for MinGW\n#endif\n\n#if defined(__linux__)\n#include <sys/prctl.h>\n#endif\n\n#include <log/log.h>\n\n#if defined(__ANDROID__)\n# define __android_unused\n#else\n# define __android_unused __attribute__((__unused__))\n#endif\n\n/*\n * ===========================================================================\n *      Thread wrappers\n * ===========================================================================\n */\n\nusing namespace android;\n\n// ----------------------------------------------------------------------------\n#if !defined(_WIN32)\n// ----------------------------------------------------------------------------\n\n/*\n * Create and run a new thread.\n *\n * We create it \"detached\", so it cleans up after itself.\n */\n\ntypedef int (*android_pthread_entry)(void*);\n\n#if defined(__ANDROID__)\nstruct thread_data_t {\n    thread_func_t   entryFunction;\n    void*           userData;\n    int             priority;\n    char *          threadName;\n\n    // we use this trampoline when we need to set the priority with\n    // nice/setpriority, and name with prctl.\n    static int trampoline(const thread_data_t* t) {\n        thread_func_t f = t->entryFunction;\n        void* u = t->userData;\n        int prio = t->priority;\n        char * name = t->threadName;\n        delete t;\n        setpriority(PRIO_PROCESS, 0, prio);\n\n        if (name) {\n            androidSetThreadName(name);\n            free(name);\n        }\n        return f(u);\n    }\n};\n#endif\n\n// Adapted from bionic's implmenetation of trampoline to make C11 thrd_create\n// work with pthread_create.\nstruct libutil_thread_data {\n  android_pthread_entry _Nonnull entry_func;\n  void* _Nullable entry_func_arg;\n};\n\nstatic void* _Nonnull libutil_thread_trampoline(void* _Nonnull arg) {\n  libutil_thread_data *data_ptr = static_cast<libutil_thread_data*>(arg);\n  int result = data_ptr->entry_func(data_ptr->entry_func_arg);\n  delete data_ptr;\n  return reinterpret_cast<void*>(static_cast<uintptr_t>(result));\n}\n\nvoid androidSetThreadName(const char* name) {\n#if defined(__linux__)\n    // Mac OS doesn't have this, and we build libutil for the host too\n    int hasAt = 0;\n    int hasDot = 0;\n    const char *s = name;\n    while (*s) {\n        if (*s == '.') hasDot = 1;\n        else if (*s == '@') hasAt = 1;\n        s++;\n    }\n    int len = s - name;\n    if (len < 15 || hasAt || !hasDot) {\n        s = name;\n    } else {\n        s = name + len - 15;\n    }\n    prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0);\n#endif\n}\n\nint androidCreateRawThreadEtc(android_thread_func_t entryFunction,\n                               void *userData,\n                               const char* threadName __android_unused,\n                               int32_t threadPriority,\n                               size_t threadStackSize,\n                               android_thread_id_t *threadId)\n{\n    pthread_attr_t attr;\n    pthread_attr_init(&attr);\n    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);\n\n#if defined(__ANDROID__)  /* valgrind is rejecting RT-priority create reqs */\n    if (threadPriority != PRIORITY_DEFAULT || threadName != NULL) {\n        // Now that the pthread_t has a method to find the associated\n        // android_thread_id_t (pid) from pthread_t, it would be possible to avoid\n        // this trampoline in some cases as the parent could set the properties\n        // for the child.  However, there would be a race condition because the\n        // child becomes ready immediately, and it doesn't work for the name.\n        // prctl(PR_SET_NAME) only works for self; prctl(PR_SET_THREAD_NAME) was\n        // proposed but not yet accepted.\n        thread_data_t* t = new thread_data_t;\n        t->priority = threadPriority;\n        t->threadName = threadName ? strdup(threadName) : NULL;\n        t->entryFunction = entryFunction;\n        t->userData = userData;\n        entryFunction = (android_thread_func_t)&thread_data_t::trampoline;\n        userData = t;\n    }\n#endif\n\n    if (threadStackSize) {\n        pthread_attr_setstacksize(&attr, threadStackSize);\n    }\n\n    errno = 0;\n    pthread_t thread;\n\n    libutil_thread_data* pthread_arg = new libutil_thread_data;\n    pthread_arg->entry_func = entryFunction;\n    pthread_arg->entry_func_arg = userData;\n\n    int result = pthread_create(&thread, &attr,\n                    libutil_thread_trampoline, pthread_arg);\n    pthread_attr_destroy(&attr);\n    if (result != 0) {\n        ALOGE(\"androidCreateRawThreadEtc failed (entry=%p, res=%d, %s)\\n\"\n             \"(android threadPriority=%d)\",\n            entryFunction, result, strerror(errno), threadPriority);\n        return 0;\n    }\n\n    // Note that *threadID is directly available to the parent only, as it is\n    // assigned after the child starts.  Use memory barrier / lock if the child\n    // or other threads also need access.\n    if (threadId != nullptr) {\n        *threadId = (android_thread_id_t)thread; // XXX: this is not portable\n    }\n    return 1;\n}\n\n#if defined(__ANDROID__)\nstatic pthread_t android_thread_id_t_to_pthread(android_thread_id_t thread)\n{\n    return (pthread_t) thread;\n}\n#endif\n\nandroid_thread_id_t androidGetThreadId()\n{\n    return (android_thread_id_t)pthread_self();\n}\n\n// ----------------------------------------------------------------------------\n#else // !defined(_WIN32)\n// ----------------------------------------------------------------------------\n\n/*\n * Trampoline to make us __stdcall-compliant.\n *\n * We're expected to delete \"vDetails\" when we're done.\n */\nstruct threadDetails {\n    int (*func)(void*);\n    void* arg;\n};\nstatic __stdcall unsigned int threadIntermediary(void* vDetails)\n{\n    struct threadDetails* pDetails = (struct threadDetails*) vDetails;\n    int result;\n\n    result = (*(pDetails->func))(pDetails->arg);\n\n    delete pDetails;\n\n    ALOG(LOG_VERBOSE, \"thread\", \"thread exiting\\n\");\n    return (unsigned int) result;\n}\n\n/*\n * Create and run a new thread.\n */\nstatic bool doCreateThread(android_thread_func_t fn, void* arg, android_thread_id_t *id)\n{\n    HANDLE hThread;\n    struct threadDetails* pDetails = new threadDetails; // must be on heap\n    unsigned int thrdaddr;\n\n    pDetails->func = fn;\n    pDetails->arg = arg;\n\n#if defined(HAVE__BEGINTHREADEX)\n    hThread = (HANDLE) _beginthreadex(NULL, 0, threadIntermediary, pDetails, 0,\n                    &thrdaddr);\n    if (hThread == 0)\n#elif defined(HAVE_CREATETHREAD)\n    hThread = CreateThread(NULL, 0,\n                    (LPTHREAD_START_ROUTINE) threadIntermediary,\n                    (void*) pDetails, 0, (DWORD*) &thrdaddr);\n    if (hThread == NULL)\n#endif\n    {\n        ALOG(LOG_WARN, \"thread\", \"WARNING: thread create failed\\n\");\n        return false;\n    }\n\n#if defined(HAVE_CREATETHREAD)\n    /* close the management handle */\n    CloseHandle(hThread);\n#endif\n\n    if (id != NULL) {\n      \t*id = (android_thread_id_t)thrdaddr;\n    }\n\n    return true;\n}\n\nint androidCreateRawThreadEtc(android_thread_func_t fn,\n                               void *userData,\n                               const char* /*threadName*/,\n                               int32_t /*threadPriority*/,\n                               size_t /*threadStackSize*/,\n                               android_thread_id_t *threadId)\n{\n    return doCreateThread(  fn, userData, threadId);\n}\n\nandroid_thread_id_t androidGetThreadId()\n{\n    return (android_thread_id_t)GetCurrentThreadId();\n}\n\n// ----------------------------------------------------------------------------\n#endif // !defined(_WIN32)\n\n// ----------------------------------------------------------------------------\n\nint androidCreateThread(android_thread_func_t fn, void* arg)\n{\n    return createThreadEtc(fn, arg);\n}\n\nint androidCreateThreadGetID(android_thread_func_t fn, void *arg, android_thread_id_t *id)\n{\n    return createThreadEtc(fn, arg, \"android:unnamed_thread\",\n                           PRIORITY_DEFAULT, 0, id);\n}\n\nstatic android_create_thread_fn gCreateThreadFn = androidCreateRawThreadEtc;\n\nint androidCreateThreadEtc(android_thread_func_t entryFunction,\n                            void *userData,\n                            const char* threadName,\n                            int32_t threadPriority,\n                            size_t threadStackSize,\n                            android_thread_id_t *threadId)\n{\n    return gCreateThreadFn(entryFunction, userData, threadName,\n        threadPriority, threadStackSize, threadId);\n}\n\nvoid androidSetCreateThreadFunc(android_create_thread_fn func)\n{\n    gCreateThreadFn = func;\n}\n\n#if defined(__ANDROID__)\nint androidSetThreadPriority(pid_t tid, int pri)\n{\n    int rc = 0;\n\n    if (setpriority(PRIO_PROCESS, tid, pri) < 0) {\n        rc = INVALID_OPERATION;\n    } else {\n        errno = 0;\n    }\n\n    return rc;\n}\n\nint androidGetThreadPriority(pid_t tid) {\n    return getpriority(PRIO_PROCESS, tid);\n}\n\n#endif\n\nnamespace android {\n\n/*\n * ===========================================================================\n *      Mutex class\n * ===========================================================================\n */\n\n#if !defined(_WIN32)\n// implemented as inlines in threads.h\n#else\n\nMutex::Mutex()\n{\n    HANDLE hMutex;\n\n    assert(sizeof(hMutex) == sizeof(mState));\n\n    hMutex = CreateMutex(NULL, FALSE, NULL);\n    mState = (void*) hMutex;\n}\n\nMutex::Mutex(const char* /*name*/)\n{\n    // XXX: name not used for now\n    HANDLE hMutex;\n\n    assert(sizeof(hMutex) == sizeof(mState));\n\n    hMutex = CreateMutex(NULL, FALSE, NULL);\n    mState = (void*) hMutex;\n}\n\nMutex::Mutex(int /*type*/, const char* /*name*/)\n{\n    // XXX: type and name not used for now\n    HANDLE hMutex;\n\n    assert(sizeof(hMutex) == sizeof(mState));\n\n    hMutex = CreateMutex(NULL, FALSE, NULL);\n    mState = (void*) hMutex;\n}\n\nMutex::~Mutex()\n{\n    CloseHandle((HANDLE) mState);\n}\n\nstatus_t Mutex::lock()\n{\n    DWORD dwWaitResult;\n    dwWaitResult = WaitForSingleObject((HANDLE) mState, INFINITE);\n    return dwWaitResult != WAIT_OBJECT_0 ? -1 : OK;\n}\n\nvoid Mutex::unlock()\n{\n    if (!ReleaseMutex((HANDLE) mState))\n        ALOG(LOG_WARN, \"thread\", \"WARNING: bad result from unlocking mutex\\n\");\n}\n\nstatus_t Mutex::tryLock()\n{\n    DWORD dwWaitResult;\n\n    dwWaitResult = WaitForSingleObject((HANDLE) mState, 0);\n    if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_TIMEOUT)\n        ALOG(LOG_WARN, \"thread\", \"WARNING: bad result from try-locking mutex\\n\");\n    return (dwWaitResult == WAIT_OBJECT_0) ? 0 : -1;\n}\n\n#endif // !defined(_WIN32)\n\n\n/*\n * ===========================================================================\n *      Condition class\n * ===========================================================================\n */\n\n#if !defined(_WIN32)\n// implemented as inlines in threads.h\n#else\n\n/*\n * Windows doesn't have a condition variable solution.  It's possible\n * to create one, but it's easy to get it wrong.  For a discussion, and\n * the origin of this implementation, see:\n *\n *  http://www.cs.wustl.edu/~schmidt/win32-cv-1.html\n *\n * The implementation shown on the page does NOT follow POSIX semantics.\n * As an optimization they require acquiring the external mutex before\n * calling signal() and broadcast(), whereas POSIX only requires grabbing\n * it before calling wait().  The implementation here has been un-optimized\n * to have the correct behavior.\n */\ntypedef struct WinCondition {\n    // Number of waiting threads.\n    int                 waitersCount;\n\n    // Serialize access to waitersCount.\n    CRITICAL_SECTION    waitersCountLock;\n\n    // Semaphore used to queue up threads waiting for the condition to\n    // become signaled.\n    HANDLE              sema;\n\n    // An auto-reset event used by the broadcast/signal thread to wait\n    // for all the waiting thread(s) to wake up and be released from\n    // the semaphore.\n    HANDLE              waitersDone;\n\n    // This mutex wouldn't be necessary if we required that the caller\n    // lock the external mutex before calling signal() and broadcast().\n    // I'm trying to mimic pthread semantics though.\n    HANDLE              internalMutex;\n\n    // Keeps track of whether we were broadcasting or signaling.  This\n    // allows us to optimize the code if we're just signaling.\n    bool                wasBroadcast;\n\n    status_t wait(WinCondition* condState, HANDLE hMutex, nsecs_t* abstime)\n    {\n        // Increment the wait count, avoiding race conditions.\n        EnterCriticalSection(&condState->waitersCountLock);\n        condState->waitersCount++;\n        //printf(\"+++ wait: incr waitersCount to %d (tid=%ld)\\n\",\n        //    condState->waitersCount, getThreadId());\n        LeaveCriticalSection(&condState->waitersCountLock);\n\n        DWORD timeout = INFINITE;\n        if (abstime) {\n            nsecs_t reltime = *abstime - systemTime();\n            if (reltime < 0)\n                reltime = 0;\n            timeout = reltime/1000000;\n        }\n\n        // Atomically release the external mutex and wait on the semaphore.\n        DWORD res =\n            SignalObjectAndWait(hMutex, condState->sema, timeout, FALSE);\n\n        //printf(\"+++ wait: awake (tid=%ld)\\n\", getThreadId());\n\n        // Reacquire lock to avoid race conditions.\n        EnterCriticalSection(&condState->waitersCountLock);\n\n        // No longer waiting.\n        condState->waitersCount--;\n\n        // Check to see if we're the last waiter after a broadcast.\n        bool lastWaiter = (condState->wasBroadcast && condState->waitersCount == 0);\n\n        //printf(\"+++ wait: lastWaiter=%d (wasBc=%d wc=%d)\\n\",\n        //    lastWaiter, condState->wasBroadcast, condState->waitersCount);\n\n        LeaveCriticalSection(&condState->waitersCountLock);\n\n        // If we're the last waiter thread during this particular broadcast\n        // then signal broadcast() that we're all awake.  It'll drop the\n        // internal mutex.\n        if (lastWaiter) {\n            // Atomically signal the \"waitersDone\" event and wait until we\n            // can acquire the internal mutex.  We want to do this in one step\n            // because it ensures that everybody is in the mutex FIFO before\n            // any thread has a chance to run.  Without it, another thread\n            // could wake up, do work, and hop back in ahead of us.\n            SignalObjectAndWait(condState->waitersDone, condState->internalMutex,\n                INFINITE, FALSE);\n        } else {\n            // Grab the internal mutex.\n            WaitForSingleObject(condState->internalMutex, INFINITE);\n        }\n\n        // Release the internal and grab the external.\n        ReleaseMutex(condState->internalMutex);\n        WaitForSingleObject(hMutex, INFINITE);\n\n        return res == WAIT_OBJECT_0 ? OK : -1;\n    }\n} WinCondition;\n\n/*\n * Constructor.  Set up the WinCondition stuff.\n */\nCondition::Condition()\n{\n    WinCondition* condState = new WinCondition;\n\n    condState->waitersCount = 0;\n    condState->wasBroadcast = false;\n    // semaphore: no security, initial value of 0\n    condState->sema = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);\n    InitializeCriticalSection(&condState->waitersCountLock);\n    // auto-reset event, not signaled initially\n    condState->waitersDone = CreateEvent(NULL, FALSE, FALSE, NULL);\n    // used so we don't have to lock external mutex on signal/broadcast\n    condState->internalMutex = CreateMutex(NULL, FALSE, NULL);\n\n    mState = condState;\n}\n\n/*\n * Destructor.  Free Windows resources as well as our allocated storage.\n */\nCondition::~Condition()\n{\n    WinCondition* condState = (WinCondition*) mState;\n    if (condState != NULL) {\n        CloseHandle(condState->sema);\n        CloseHandle(condState->waitersDone);\n        delete condState;\n    }\n}\n\n\nstatus_t Condition::wait(Mutex& mutex)\n{\n    WinCondition* condState = (WinCondition*) mState;\n    HANDLE hMutex = (HANDLE) mutex.mState;\n\n    return ((WinCondition*)mState)->wait(condState, hMutex, NULL);\n}\n\nstatus_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime)\n{\n    WinCondition* condState = (WinCondition*) mState;\n    HANDLE hMutex = (HANDLE) mutex.mState;\n    nsecs_t absTime = systemTime()+reltime;\n\n    return ((WinCondition*)mState)->wait(condState, hMutex, &absTime);\n}\n\n/*\n * Signal the condition variable, allowing one thread to continue.\n */\nvoid Condition::signal()\n{\n    WinCondition* condState = (WinCondition*) mState;\n\n    // Lock the internal mutex.  This ensures that we don't clash with\n    // broadcast().\n    WaitForSingleObject(condState->internalMutex, INFINITE);\n\n    EnterCriticalSection(&condState->waitersCountLock);\n    bool haveWaiters = (condState->waitersCount > 0);\n    LeaveCriticalSection(&condState->waitersCountLock);\n\n    // If no waiters, then this is a no-op.  Otherwise, knock the semaphore\n    // down a notch.\n    if (haveWaiters)\n        ReleaseSemaphore(condState->sema, 1, 0);\n\n    // Release internal mutex.\n    ReleaseMutex(condState->internalMutex);\n}\n\n/*\n * Signal the condition variable, allowing all threads to continue.\n *\n * First we have to wake up all threads waiting on the semaphore, then\n * we wait until all of the threads have actually been woken before\n * releasing the internal mutex.  This ensures that all threads are woken.\n */\nvoid Condition::broadcast()\n{\n    WinCondition* condState = (WinCondition*) mState;\n\n    // Lock the internal mutex.  This keeps the guys we're waking up\n    // from getting too far.\n    WaitForSingleObject(condState->internalMutex, INFINITE);\n\n    EnterCriticalSection(&condState->waitersCountLock);\n    bool haveWaiters = false;\n\n    if (condState->waitersCount > 0) {\n        haveWaiters = true;\n        condState->wasBroadcast = true;\n    }\n\n    if (haveWaiters) {\n        // Wake up all the waiters.\n        ReleaseSemaphore(condState->sema, condState->waitersCount, 0);\n\n        LeaveCriticalSection(&condState->waitersCountLock);\n\n        // Wait for all awakened threads to acquire the counting semaphore.\n        // The last guy who was waiting sets this.\n        WaitForSingleObject(condState->waitersDone, INFINITE);\n\n        // Reset wasBroadcast.  (No crit section needed because nobody\n        // else can wake up to poke at it.)\n        condState->wasBroadcast = 0;\n    } else {\n        // nothing to do\n        LeaveCriticalSection(&condState->waitersCountLock);\n    }\n\n    // Release internal mutex.\n    ReleaseMutex(condState->internalMutex);\n}\n\n#endif // !defined(_WIN32)\n\n// ----------------------------------------------------------------------------\n\n/*\n * This is our thread object!\n */\n\nThread::Thread(bool canCallJava)\n    : mCanCallJava(canCallJava),\n      mThread(thread_id_t(-1)),\n      mLock(\"Thread::mLock\"),\n      mStatus(OK),\n      mExitPending(false),\n      mRunning(false)\n#if defined(__ANDROID__)\n      ,\n      mTid(-1)\n#endif\n{\n}\n\nThread::~Thread()\n{\n}\n\nstatus_t Thread::readyToRun()\n{\n    return OK;\n}\n\nstatus_t Thread::run(const char* name, int32_t priority, size_t stack)\n{\n    LOG_ALWAYS_FATAL_IF(name == nullptr, \"thread name not provided to Thread::run\");\n\n    Mutex::Autolock _l(mLock);\n\n    if (mRunning) {\n        // thread already started\n        return INVALID_OPERATION;\n    }\n\n    // reset status and exitPending to their default value, so we can\n    // try again after an error happened (either below, or in readyToRun())\n    mStatus = OK;\n    mExitPending = false;\n    mThread = thread_id_t(-1);\n\n    // hold a strong reference on ourself\n    mHoldSelf = sp<Thread>::fromExisting(this);\n\n    mRunning = true;\n\n    bool res;\n    if (mCanCallJava) {\n        res = createThreadEtc(_threadLoop,\n                this, name, priority, stack, &mThread);\n    } else {\n        res = androidCreateRawThreadEtc(_threadLoop,\n                this, name, priority, stack, &mThread);\n    }\n\n    if (res == false) {\n        mStatus = UNKNOWN_ERROR;   // something happened!\n        mRunning = false;\n        mThread = thread_id_t(-1);\n        mHoldSelf.clear();  // \"this\" may have gone away after this.\n\n        return UNKNOWN_ERROR;\n    }\n\n    // Do not refer to mStatus here: The thread is already running (may, in fact\n    // already have exited with a valid mStatus result). The OK indication\n    // here merely indicates successfully starting the thread and does not\n    // imply successful termination/execution.\n    return OK;\n\n    // Exiting scope of mLock is a memory barrier and allows new thread to run\n}\n\nint Thread::_threadLoop(void* user)\n{\n    Thread* const self = static_cast<Thread*>(user);\n\n    sp<Thread> strong(self->mHoldSelf);\n    wp<Thread> weak(strong);\n    self->mHoldSelf.clear();\n\n#if defined(__ANDROID__)\n    // this is very useful for debugging with gdb\n    self->mTid = gettid();\n#endif\n\n    bool first = true;\n\n    do {\n        bool result;\n        if (first) {\n            first = false;\n            self->mStatus = self->readyToRun();\n            result = (self->mStatus == OK);\n\n            if (result && !self->exitPending()) {\n                // Binder threads (and maybe others) rely on threadLoop\n                // running at least once after a successful ::readyToRun()\n                // (unless, of course, the thread has already been asked to exit\n                // at that point).\n                // This is because threads are essentially used like this:\n                //   (new ThreadSubclass())->run();\n                // The caller therefore does not retain a strong reference to\n                // the thread and the thread would simply disappear after the\n                // successful ::readyToRun() call instead of entering the\n                // threadLoop at least once.\n                result = self->threadLoop();\n            }\n        } else {\n            result = self->threadLoop();\n        }\n\n        // establish a scope for mLock\n        {\n        Mutex::Autolock _l(self->mLock);\n        if (result == false || self->mExitPending) {\n            self->mExitPending = true;\n            self->mRunning = false;\n            // clear thread ID so that requestExitAndWait() does not exit if\n            // called by a new thread using the same thread ID as this one.\n            self->mThread = thread_id_t(-1);\n            // note that interested observers blocked in requestExitAndWait are\n            // awoken by broadcast, but blocked on mLock until break exits scope\n            self->mThreadExitedCondition.broadcast();\n            break;\n        }\n        }\n\n        // Release our strong reference, to let a chance to the thread\n        // to die a peaceful death.\n        strong.clear();\n        // And immediately, re-acquire a strong reference for the next loop\n        strong = weak.promote();\n    } while(strong != nullptr);\n\n    return 0;\n}\n\nvoid Thread::requestExit()\n{\n    Mutex::Autolock _l(mLock);\n    mExitPending = true;\n}\n\nstatus_t Thread::requestExitAndWait()\n{\n    Mutex::Autolock _l(mLock);\n    if (mThread == getThreadId()) {\n        ALOGW(\n        \"Thread (this=%p): don't call waitForExit() from this \"\n        \"Thread object's thread. It's a guaranteed deadlock!\",\n        this);\n\n        return WOULD_BLOCK;\n    }\n\n    mExitPending = true;\n\n    while (mRunning == true) {\n        mThreadExitedCondition.wait(mLock);\n    }\n    // This next line is probably not needed any more, but is being left for\n    // historical reference. Note that each interested party will clear flag.\n    mExitPending = false;\n\n    return mStatus;\n}\n\nstatus_t Thread::join()\n{\n    Mutex::Autolock _l(mLock);\n    if (mThread == getThreadId()) {\n        ALOGW(\n        \"Thread (this=%p): don't call join() from this \"\n        \"Thread object's thread. It's a guaranteed deadlock!\",\n        this);\n\n        return WOULD_BLOCK;\n    }\n\n    while (mRunning == true) {\n        mThreadExitedCondition.wait(mLock);\n    }\n\n    return mStatus;\n}\n\nbool Thread::isRunning() const {\n    Mutex::Autolock _l(mLock);\n    return mRunning;\n}\n\n#if defined(__ANDROID__)\npid_t Thread::getTid() const\n{\n    // mTid is not defined until the child initializes it, and the caller may need it earlier\n    Mutex::Autolock _l(mLock);\n    pid_t tid;\n    if (mRunning) {\n        pthread_t pthread = android_thread_id_t_to_pthread(mThread);\n        tid = pthread_gettid_np(pthread);\n    } else {\n        ALOGW(\"Thread (this=%p): getTid() is undefined before run()\", this);\n        tid = -1;\n    }\n    return tid;\n}\n#endif\n\nbool Thread::exitPending() const\n{\n    Mutex::Autolock _l(mLock);\n    return mExitPending;\n}\n\n\n\n};  // namespace android\n"
  },
  {
    "path": "libutils/Timers.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/Timers.h>\n\n#include <limits.h>\n#include <stdlib.h>\n#include <time.h>\n\n#include <android-base/macros.h>\n#include <log/log.h>\n\nstatic constexpr size_t clock_id_max = 5;\n\nstatic void checkClockId(int clock) {\n    LOG_ALWAYS_FATAL_IF(clock < 0 || clock >= clock_id_max, \"invalid clock id\");\n}\n\n#if defined(__linux__)\nnsecs_t systemTime(int clock) {\n    checkClockId(clock);\n    static constexpr clockid_t clocks[] = {CLOCK_REALTIME, CLOCK_MONOTONIC,\n                                           CLOCK_PROCESS_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID,\n                                           CLOCK_BOOTTIME};\n    static_assert(clock_id_max == arraysize(clocks));\n    timespec t = {};\n    clock_gettime(clocks[clock], &t);\n    return nsecs_t(t.tv_sec)*1000000000LL + t.tv_nsec;\n}\n#else\nnsecs_t systemTime(int clock) {\n    // TODO: is this ever called with anything but REALTIME on mac/windows?\n    checkClockId(clock);\n\n    // Clock support varies widely across hosts. Mac OS doesn't support\n    // CLOCK_BOOTTIME (and doesn't even have clock_gettime until 10.12).\n    // Windows is windows.\n    timeval t = {};\n    gettimeofday(&t, nullptr);\n    return nsecs_t(t.tv_sec)*1000000000LL + nsecs_t(t.tv_usec)*1000LL;\n}\n#endif\n\nint toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime) {\n    if (timeoutTime <= referenceTime) return 0;\n\n    uint64_t timeoutDelay = uint64_t(timeoutTime - referenceTime);\n    if (timeoutDelay > uint64_t((INT_MAX - 1) * 1000000LL)) return -1;\n    return (timeoutDelay + 999999LL) / 1000000LL;\n}\n"
  },
  {
    "path": "libutils/Timers_test.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/Timers.h>\n\n#include <gtest/gtest.h>\n\nTEST(Timers, systemTime_invalid) {\n    EXPECT_EXIT(systemTime(-1), testing::KilledBySignal(SIGABRT), \"\");\n    systemTime(SYSTEM_TIME_REALTIME);\n    systemTime(SYSTEM_TIME_MONOTONIC);\n    systemTime(SYSTEM_TIME_PROCESS);\n    systemTime(SYSTEM_TIME_THREAD);\n    systemTime(SYSTEM_TIME_BOOTTIME);\n    EXPECT_EXIT(systemTime(SYSTEM_TIME_BOOTTIME + 1), testing::KilledBySignal(SIGABRT), \"\");\n}\n\nTEST(Timers, toMillisecondTimeoutDelay) {\n    EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 100));\n    EXPECT_EQ(0, toMillisecondTimeoutDelay(100, 10));\n\n    EXPECT_EQ(-1, toMillisecondTimeoutDelay(0, INT_MAX * 1000000LL));\n\n    EXPECT_EQ(123, toMillisecondTimeoutDelay(0, 123000000));\n}\n"
  },
  {
    "path": "libutils/Tokenizer.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Tokenizer\"\n\n#include <utils/Tokenizer.h>\n#include <fcntl.h>\n#include <sys/stat.h>\n#include <log/log.h>\n\n#ifndef DEBUG_TOKENIZER\n// Enables debug output for the tokenizer.\n#define DEBUG_TOKENIZER 0\n#endif\n\nnamespace android {\n\nstatic inline bool isDelimiter(char ch, const char* delimiters) {\n    return strchr(delimiters, ch) != nullptr;\n}\n\nTokenizer::Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,\n        bool ownBuffer, size_t length) :\n        mFilename(filename), mFileMap(fileMap),\n        mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length),\n        mCurrent(buffer), mLineNumber(1) {\n}\n\nTokenizer::~Tokenizer() {\n    delete mFileMap;\n    if (mOwnBuffer) {\n        delete[] mBuffer;\n    }\n}\n\nstatus_t Tokenizer::open(const String8& filename, Tokenizer** outTokenizer) {\n    *outTokenizer = nullptr;\n\n    int result = OK;\n    int fd = ::open(filename.c_str(), O_RDONLY);\n    if (fd < 0) {\n        result = -errno;\n        ALOGE(\"Error opening file '%s': %s\", filename.c_str(), strerror(errno));\n    } else {\n        struct stat stat;\n        if (fstat(fd, &stat)) {\n            result = -errno;\n            ALOGE(\"Error getting size of file '%s': %s\", filename.c_str(), strerror(errno));\n        } else {\n            size_t length = size_t(stat.st_size);\n\n            FileMap* fileMap = new FileMap();\n            bool ownBuffer = false;\n            char* buffer;\n            if (fileMap->create(nullptr, fd, 0, length, true)) {\n                fileMap->advise(FileMap::SEQUENTIAL);\n                buffer = static_cast<char*>(fileMap->getDataPtr());\n            } else {\n                delete fileMap;\n                fileMap = nullptr;\n\n                // Fall back to reading into a buffer since we can't mmap files in sysfs.\n                // The length we obtained from stat is wrong too (it will always be 4096)\n                // so we must trust that read will read the entire file.\n                buffer = new char[length];\n                ownBuffer = true;\n                ssize_t nrd = read(fd, buffer, length);\n                if (nrd < 0) {\n                    result = -errno;\n                    ALOGE(\"Error reading file '%s': %s\", filename.c_str(), strerror(errno));\n                    delete[] buffer;\n                    buffer = nullptr;\n                } else {\n                    length = size_t(nrd);\n                }\n            }\n\n            if (!result) {\n                *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length);\n            }\n        }\n        close(fd);\n    }\n    return result;\n}\n\nstatus_t Tokenizer::fromContents(const String8& filename,\n        const char* contents, Tokenizer** outTokenizer) {\n    *outTokenizer = new Tokenizer(filename, nullptr,\n            const_cast<char*>(contents), false, strlen(contents));\n    return OK;\n}\n\nString8 Tokenizer::getLocation() const {\n    String8 result;\n    result.appendFormat(\"%s:%d\", mFilename.c_str(), mLineNumber);\n    return result;\n}\n\nString8 Tokenizer::peekRemainderOfLine() const {\n    const char* end = getEnd();\n    const char* eol = mCurrent;\n    while (eol != end) {\n        char ch = *eol;\n        if (ch == '\\n') {\n            break;\n        }\n        eol += 1;\n    }\n    return String8(mCurrent, eol - mCurrent);\n}\n\nString8 Tokenizer::nextToken(const char* delimiters) {\n#if DEBUG_TOKENIZER\n    ALOGD(\"nextToken\");\n#endif\n    const char* end = getEnd();\n    const char* tokenStart = mCurrent;\n    while (mCurrent != end) {\n        char ch = *mCurrent;\n        if (ch == '\\n' || isDelimiter(ch, delimiters)) {\n            break;\n        }\n        mCurrent += 1;\n    }\n    return String8(tokenStart, mCurrent - tokenStart);\n}\n\nvoid Tokenizer::nextLine() {\n#if DEBUG_TOKENIZER\n    ALOGD(\"nextLine\");\n#endif\n    const char* end = getEnd();\n    while (mCurrent != end) {\n        char ch = *(mCurrent++);\n        if (ch == '\\n') {\n            mLineNumber += 1;\n            break;\n        }\n    }\n}\n\nvoid Tokenizer::skipDelimiters(const char* delimiters) {\n#if DEBUG_TOKENIZER\n    ALOGD(\"skipDelimiters\");\n#endif\n    const char* end = getEnd();\n    while (mCurrent != end) {\n        char ch = *mCurrent;\n        if (ch == '\\n' || !isDelimiter(ch, delimiters)) {\n            break;\n        }\n        mCurrent += 1;\n    }\n}\n\n} // namespace android\n"
  },
  {
    "path": "libutils/Trace.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/Trace.h>\n#include <utils/misc.h>\n\nstatic void traceInit() __attribute__((constructor));\n\nstatic void traceInit() {\n    ::android::add_sysprop_change_callback(atrace_update_tags, 0);\n}\n"
  },
  {
    "path": "libutils/abi-dumps/arm64/source-based/libutils.so.lsdump",
    "content": "{\n \"array_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIA0_i\",\n   \"name\" : \"int[0]\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include_outside_system/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"linker_set_key\" : \"_ZTIA1_Ds\",\n   \"name\" : \"char16_t[1]\",\n   \"referenced_type\" : \"_ZTIDs\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIA20_c\",\n   \"name\" : \"char[20]\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIA5121_h\",\n   \"name\" : \"unsigned char[5121]\",\n   \"referenced_type\" : \"_ZTIh\",\n   \"size\" : 5121,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIA8_j\",\n   \"name\" : \"unsigned int[8]\",\n   \"referenced_type\" : \"_ZTIj\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"is_of_unknown_bound\" : true,\n   \"linker_set_key\" : \"_ZTIA_f\",\n   \"name\" : \"float[]\",\n   \"referenced_type\" : \"_ZTIf\",\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  }\n ],\n \"builtin_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIDi\",\n   \"name\" : \"char32_t\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIDn\",\n   \"name\" : \"std::nullptr_t\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIDs\",\n   \"name\" : \"char16_t\",\n   \"size\" : 2\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIa\",\n   \"name\" : \"signed char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIb\",\n   \"name\" : \"bool\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIc\",\n   \"name\" : \"char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTId\",\n   \"name\" : \"double\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIf\",\n   \"name\" : \"float\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIh\",\n   \"name\" : \"unsigned char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIi\",\n   \"name\" : \"int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIj\",\n   \"name\" : \"unsigned int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIl\",\n   \"name\" : \"long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIm\",\n   \"name\" : \"unsigned long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIs\",\n   \"name\" : \"short\",\n   \"size\" : 2\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIt\",\n   \"name\" : \"unsigned short\",\n   \"size\" : 2\n  },\n  {\n   \"linker_set_key\" : \"_ZTIv\",\n   \"name\" : \"void\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIx\",\n   \"name\" : \"long long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIy\",\n   \"name\" : \"unsigned long long\",\n   \"size\" : 8\n  }\n ],\n \"elf_functions\" :\n [\n  {\n   \"name\" : \"_Z24androidCreateThreadGetIDPFiPvES_PS_\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinter8printRawEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinter9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl11appendArrayEPKvm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl11setCapacityEm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl12appendVectorERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13editArrayImplEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13finish_vectorEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13insertArrayAtEPKvmm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13removeItemsAtEmm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl14insertVectorAtERKS0_m\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl15release_storageEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl16editItemLocationEm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl3addEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl3addEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl3popEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4pushEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4pushEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_E\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl5_growEmm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl5clearEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl6resizeEm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl7_shrinkEmm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl8insertAtEPKvmm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl8insertAtEmm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl9replaceAtEPKvm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl9replaceAtEm\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplC2ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplC2Emj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplaSERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android11uptimeNanosEv\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandle6createEP13native_handleb\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleC1EP13native_handleb\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleC2EP13native_handleb\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android12SharedBuffer5allocEm\"\n  },\n  {\n   \"name\" : \"_ZN7android12SharedBuffer7deallocEPKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android12uptimeMillisEv\"\n  },\n  {\n   \"name\" : \"_ZN7android13PrefixPrinter9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14LooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14LooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14LooperCallbackD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14MessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14MessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14MessageHandlerD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14String8Printer9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14String8PrinterC1EPNS_7String8EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14String8PrinterC2EPNS_7String8EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14sp_report_raceEv\"\n  },\n  {\n   \"name\" : \"_ZN7android14statusToStringEi\"\n  },\n  {\n   \"name\" : \"_ZN7android15elapsedRealtimeEv\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl3addEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl5mergeERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl6removeEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplC2Emj\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplaSERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android17JenkinsHashWhitenEj\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android19JenkinsHashMixBytesEjPKhm\"\n  },\n  {\n   \"name\" : \"_ZN7android19elapsedRealtimeNanoEv\"\n  },\n  {\n   \"name\" : \"_ZN7android20JenkinsHashMixShortsEjPKtm\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallback11handleEventEiiPv\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackC1EPFiiiPvE\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackC2EPFiiiPvE\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android21report_sysprop_changeEv\"\n  },\n  {\n   \"name\" : \"_ZN7android23sp_report_stack_pointerEv\"\n  },\n  {\n   \"name\" : \"_ZN7android27add_sysprop_change_callbackEPFvvEi\"\n  },\n  {\n   \"name\" : \"_ZN7android30get_report_sysprop_change_funcEv\"\n  },\n  {\n   \"name\" : \"_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper12getForThreadEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper12setForThreadERKNS_2spIS0_EE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper15getFdStateDebugEiPiS1_PNS_2spINS_14LooperCallbackEEEPPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper17sendMessageAtTimeElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper18rebuildEpollLockedEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper18sendMessageDelayedElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper26removeSequenceNumberLockedEm\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper26scheduleEpollRebuildLockedEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper4wakeEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper5addFdEiiiPFiiiPvES1_\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper6awokenEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper6repollEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper7pollAllEiPiS1_PPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper7prepareEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper8pollOnceEiPiS1_PPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper8removeFdEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper9pollInnerEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperC1Eb\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperC2Eb\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread10readyToRunEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread11_threadLoopEPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread11requestExitEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread18requestExitAndWaitEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread3runEPKcim\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread4joinEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadC2Eb\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMap6adviseENS0_9MapAdviceE\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMap6createEPKcilmb\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC1EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC2EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapaSEOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterC2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase10renameRefsEmRKNS_16ReferenceRenamerE\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase11renameRefIdEPS0_PKvS3_\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type7decWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type7incWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type7trackMeEbb\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase20extendObjectLifetimeEi\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseC1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseC2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String810lockBufferEm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String811real_appendEPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String812appendFormatEPKcz\"\n  },\n  {\n   \"name\" : \"_ZN7android7String812unlockBufferEm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String812unlockBufferEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7String813appendFormatVEPKcSt9__va_list\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85clearEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKDim\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86appendEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86appendEPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86appendERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86formatEPKcz\"\n  },\n  {\n   \"name\" : \"_ZN7android7String87formatVEPKcSt9__va_list\"\n  },\n  {\n   \"name\" : \"_ZN7android7String87toLowerEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7String89removeAllEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDi\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDim\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1ERKNS_8String16E\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDi\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDim\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2ERKNS_8String16E\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8D1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8D2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1610editResizeEm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1610replaceAllEDsDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1613allocFromUTF8EPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1614allocFromUTF16EPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String164editEv\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165allocEm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToEPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToEPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToERKS0_mm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166appendEPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166appendERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166insertEmPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166insertEmPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String167acquireEv\"\n  },\n  {\n   \"name\" : \"_ZN7android8String167releaseEv\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1ERKNS_7String8E\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1ERKS0_mm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKDsm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKcm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2ERKNS_7String8E\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2ERKS0_mm\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16D1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16D2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16aSEOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android9FdPrinter9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9FdPrinterC1EijPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9FdPrinterC2EijPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatch5resetEv\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchC1EPKci\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchC2EPKci\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer14skipDelimitersEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer4openERKNS_7String8EPPS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer8nextLineEv\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer9nextTokenEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbm\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbm\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerD2Ev\"\n  },\n  {\n   \"name\" : \"_ZNK7android10VectorImpl12itemLocationEm\"\n  },\n  {\n   \"name\" : \"_ZNK7android10VectorImpl8capacityEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android10VectorImpl8itemSizeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer10editResizeEm\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer11attemptEditEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer4editEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer5resetEm\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer7acquireEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer7releaseEj\"\n  },\n  {\n   \"name\" : \"_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPm\"\n  },\n  {\n   \"name\" : \"_ZNK7android16SortedVectorImpl7indexOfEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android16SortedVectorImpl7orderOfEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Looper20getAllowNonCallbacksEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Looper7Request14getEpollEventsEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Looper9isPollingEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Thread11exitPendingEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Thread6getTidEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Thread9isRunningEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase10createWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase11getWeakRefsEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase12weakref_type12getWeakCountEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase12weakref_type7refBaseEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase12weakref_type9printRefsEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase14forceIncStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase14getStrongCountEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase22incStrongRequireStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase9decStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase9incStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String810getPathDirEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String816getPathExtensionEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String84findEPKcm\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String86lengthEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1610startsWithEPKDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1610startsWithERKS0_\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1614isStaticStringEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1616staticStringSizeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String164sizeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String168containsEPKDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String168findLastEDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String169findFirstEDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android9StopWatch11elapsedTimeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android9StopWatch4nameEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android9Tokenizer11getLocationEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android9Tokenizer19peekRemainderOfLineEv\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android14LooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android14LooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android14MessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android14MessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android18WeakMessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android18WeakMessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android20SimpleLooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android20SimpleLooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android6ThreadD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n24_N7android6ThreadD1Ev\"\n  },\n  {\n   \"name\" : \"androidCreateRawThreadEtc\"\n  },\n  {\n   \"name\" : \"androidCreateThread\"\n  },\n  {\n   \"name\" : \"androidCreateThreadEtc\"\n  },\n  {\n   \"name\" : \"androidGetThreadId\"\n  },\n  {\n   \"name\" : \"androidGetThreadPriority\"\n  },\n  {\n   \"name\" : \"androidSetCreateThreadFunc\"\n  },\n  {\n   \"name\" : \"androidSetThreadName\"\n  },\n  {\n   \"name\" : \"androidSetThreadPriority\"\n  },\n  {\n   \"name\" : \"do_report_sysprop_change\"\n  },\n  {\n   \"name\" : \"strcmp16\"\n  },\n  {\n   \"name\" : \"strlen16\"\n  },\n  {\n   \"name\" : \"strncmp16\"\n  },\n  {\n   \"name\" : \"strnlen16\"\n  },\n  {\n   \"name\" : \"strstr16\"\n  },\n  {\n   \"name\" : \"strzcmp16\"\n  },\n  {\n   \"name\" : \"systemTime\"\n  },\n  {\n   \"name\" : \"toMillisecondTimeoutDelay\"\n  },\n  {\n   \"name\" : \"utf16_to_utf8\"\n  },\n  {\n   \"name\" : \"utf16_to_utf8_length\"\n  },\n  {\n   \"name\" : \"utf32_from_utf8_at\"\n  },\n  {\n   \"name\" : \"utf32_to_utf8\"\n  },\n  {\n   \"name\" : \"utf32_to_utf8_length\"\n  },\n  {\n   \"name\" : \"utf8_to_utf16\"\n  },\n  {\n   \"name\" : \"utf8_to_utf16_length\"\n  },\n  {\n   \"name\" : \"utf8_to_utf16_no_null_terminator\"\n  }\n ],\n \"elf_objects\" :\n [\n  {\n   \"name\" : \"_ZN7android7FileMap9mPageSizeE\"\n  },\n  {\n   \"name\" : \"_ZTCN7android18WeakMessageHandlerE0_NS_14MessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTCN7android20SimpleLooperCallbackE0_NS_14LooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android14LooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android14MessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android18WeakMessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android20SimpleLooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android6ThreadE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android10LogPrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android10VectorImplE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android13PrefixPrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android14LooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android14MessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android14String8PrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android16SortedVectorImplE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android18WeakMessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android20SimpleLooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android6LooperE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android6ThreadE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZTVN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZTVN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZTVN7android6VectorINS_6Looper8ResponseEEE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android7PrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android7RefBaseE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android9FdPrinterE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"__llvm_fs_discriminator__\"\n  }\n ],\n \"enum_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_HDR_DOLBY_VISION\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_HDR_HDR10\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_HDR_HLG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13android_hdr_t\",\n   \"name\" : \"android_hdr_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_HDR_HDR10_PLUS\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI18android_hdr_v1_2_t\",\n   \"name\" : \"android_hdr_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"ANDROID_LOG_UNKNOWN\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"ANDROID_LOG_DEFAULT\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"ANDROID_LOG_VERBOSE\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"ANDROID_LOG_DEBUG\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"ANDROID_LOG_INFO\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"ANDROID_LOG_WARN\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"ANDROID_LOG_ERROR\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"ANDROID_LOG_FATAL\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"ANDROID_LOG_SILENT\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_LogPriority\",\n   \"name\" : \"android_LogPriority\",\n   \"size\" : 4,\n   \"source_file\" : \"system/logging/liblog/include_vndk/android/log.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_UNKNOWN\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_DATASPACE_ARBITRARY\"\n    },\n    {\n     \"enum_field_value\" : 16,\n     \"name\" : \"HAL_DATASPACE_STANDARD_SHIFT\"\n    },\n    {\n     \"enum_field_value\" : 4128768,\n     \"name\" : \"HAL_DATASPACE_STANDARD_MASK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_STANDARD_UNSPECIFIED\"\n    },\n    {\n     \"enum_field_value\" : 65536,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT709\"\n    },\n    {\n     \"enum_field_value\" : 131072,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 196608,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 262144,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 327680,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 393216,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 458752,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE\"\n    },\n    {\n     \"enum_field_value\" : 524288,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT470M\"\n    },\n    {\n     \"enum_field_value\" : 589824,\n     \"name\" : \"HAL_DATASPACE_STANDARD_FILM\"\n    },\n    {\n     \"enum_field_value\" : 655360,\n     \"name\" : \"HAL_DATASPACE_STANDARD_DCI_P3\"\n    },\n    {\n     \"enum_field_value\" : 720896,\n     \"name\" : \"HAL_DATASPACE_STANDARD_ADOBE_RGB\"\n    },\n    {\n     \"enum_field_value\" : 22,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_SHIFT\"\n    },\n    {\n     \"enum_field_value\" : 130023424,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_MASK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_UNSPECIFIED\"\n    },\n    {\n     \"enum_field_value\" : 4194304,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 8388608,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 12582912,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_SMPTE_170M\"\n    },\n    {\n     \"enum_field_value\" : 16777216,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_GAMMA2_2\"\n    },\n    {\n     \"enum_field_value\" : 20971520,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_GAMMA2_6\"\n    },\n    {\n     \"enum_field_value\" : 25165824,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_GAMMA2_8\"\n    },\n    {\n     \"enum_field_value\" : 29360128,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_ST2084\"\n    },\n    {\n     \"enum_field_value\" : 33554432,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_HLG\"\n    },\n    {\n     \"enum_field_value\" : 27,\n     \"name\" : \"HAL_DATASPACE_RANGE_SHIFT\"\n    },\n    {\n     \"enum_field_value\" : 939524096,\n     \"name\" : \"HAL_DATASPACE_RANGE_MASK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_RANGE_UNSPECIFIED\"\n    },\n    {\n     \"enum_field_value\" : 134217728,\n     \"name\" : \"HAL_DATASPACE_RANGE_FULL\"\n    },\n    {\n     \"enum_field_value\" : 268435456,\n     \"name\" : \"HAL_DATASPACE_RANGE_LIMITED\"\n    },\n    {\n     \"enum_field_value\" : 402653184,\n     \"name\" : \"HAL_DATASPACE_RANGE_EXTENDED\"\n    },\n    {\n     \"enum_field_value\" : 512,\n     \"name\" : \"HAL_DATASPACE_SRGB_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 138477568,\n     \"name\" : \"HAL_DATASPACE_V0_SRGB_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 406913024,\n     \"name\" : \"HAL_DATASPACE_V0_SCRGB_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 513,\n     \"name\" : \"HAL_DATASPACE_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 142671872,\n     \"name\" : \"HAL_DATASPACE_V0_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 411107328,\n     \"name\" : \"HAL_DATASPACE_V0_SCRGB\"\n    },\n    {\n     \"enum_field_value\" : 257,\n     \"name\" : \"HAL_DATASPACE_JFIF\"\n    },\n    {\n     \"enum_field_value\" : 146931712,\n     \"name\" : \"HAL_DATASPACE_V0_JFIF\"\n    },\n    {\n     \"enum_field_value\" : 258,\n     \"name\" : \"HAL_DATASPACE_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 281149440,\n     \"name\" : \"HAL_DATASPACE_V0_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 259,\n     \"name\" : \"HAL_DATASPACE_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 281280512,\n     \"name\" : \"HAL_DATASPACE_V0_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 260,\n     \"name\" : \"HAL_DATASPACE_BT709\"\n    },\n    {\n     \"enum_field_value\" : 281083904,\n     \"name\" : \"HAL_DATASPACE_V0_BT709\"\n    },\n    {\n     \"enum_field_value\" : 139067392,\n     \"name\" : \"HAL_DATASPACE_DCI_P3_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 155844608,\n     \"name\" : \"HAL_DATASPACE_DCI_P3\"\n    },\n    {\n     \"enum_field_value\" : 139067392,\n     \"name\" : \"HAL_DATASPACE_DISPLAY_P3_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 143261696,\n     \"name\" : \"HAL_DATASPACE_DISPLAY_P3\"\n    },\n    {\n     \"enum_field_value\" : 151715840,\n     \"name\" : \"HAL_DATASPACE_ADOBE_RGB\"\n    },\n    {\n     \"enum_field_value\" : 138805248,\n     \"name\" : \"HAL_DATASPACE_BT2020_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 147193856,\n     \"name\" : \"HAL_DATASPACE_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 163971072,\n     \"name\" : \"HAL_DATASPACE_BT2020_PQ\"\n    },\n    {\n     \"enum_field_value\" : 4096,\n     \"name\" : \"HAL_DATASPACE_DEPTH\"\n    },\n    {\n     \"enum_field_value\" : 4097,\n     \"name\" : \"HAL_DATASPACE_SENSOR\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_dataspace_t\",\n   \"name\" : \"android_dataspace_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"FLEX_FORMAT_INVALID\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"FLEX_FORMAT_Y\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"FLEX_FORMAT_YCbCr\"\n    },\n    {\n     \"enum_field_value\" : 1073741831,\n     \"name\" : \"FLEX_FORMAT_YCbCrA\"\n    },\n    {\n     \"enum_field_value\" : 7168,\n     \"name\" : \"FLEX_FORMAT_RGB\"\n    },\n    {\n     \"enum_field_value\" : 1073748992,\n     \"name\" : \"FLEX_FORMAT_RGBA\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_flex_format\",\n   \"name\" : \"android_flex_format\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_TRANSFORM_FLIP_H\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_TRANSFORM_FLIP_V\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_TRANSFORM_ROT_90\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_TRANSFORM_ROT_180\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"HAL_TRANSFORM_ROT_270\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_transform_t\",\n   \"name\" : \"android_transform_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_COLOR_MODE_NATIVE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT709\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"HAL_COLOR_MODE_DCI_P3\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"HAL_COLOR_MODE_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"HAL_COLOR_MODE_ADOBE_RGB\"\n    },\n    {\n     \"enum_field_value\" : 9,\n     \"name\" : \"HAL_COLOR_MODE_DISPLAY_P3\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI20android_color_mode_t\",\n   \"name\" : \"android_color_mode_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"SYSTEM_TIME_REALTIME\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"SYSTEM_TIME_MONOTONIC\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"SYSTEM_TIME_PROCESS\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"SYSTEM_TIME_THREAD\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"SYSTEM_TIME_BOOTTIME\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI21$SYSTEM_TIME_BOOTTIME\",\n   \"name\" : \"(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Timers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"FLEX_COMPONENT_Y\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"FLEX_COMPONENT_Cb\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"FLEX_COMPONENT_Cr\"\n    },\n    {\n     \"enum_field_value\" : 1024,\n     \"name\" : \"FLEX_COMPONENT_R\"\n    },\n    {\n     \"enum_field_value\" : 2048,\n     \"name\" : \"FLEX_COMPONENT_G\"\n    },\n    {\n     \"enum_field_value\" : 4096,\n     \"name\" : \"FLEX_COMPONENT_B\"\n    },\n    {\n     \"enum_field_value\" : 1073741824,\n     \"name\" : \"FLEX_COMPONENT_A\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI22android_flex_component\",\n   \"name\" : \"android_flex_component\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBA_8888\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBX_8888\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGB_888\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGB_565\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"HAL_PIXEL_FORMAT_BGRA_8888\"\n    },\n    {\n     \"enum_field_value\" : 16,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_422_SP\"\n    },\n    {\n     \"enum_field_value\" : 17,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCRCB_420_SP\"\n    },\n    {\n     \"enum_field_value\" : 20,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_422_I\"\n    },\n    {\n     \"enum_field_value\" : 22,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBA_FP16\"\n    },\n    {\n     \"enum_field_value\" : 32,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW16\"\n    },\n    {\n     \"enum_field_value\" : 33,\n     \"name\" : \"HAL_PIXEL_FORMAT_BLOB\"\n    },\n    {\n     \"enum_field_value\" : 34,\n     \"name\" : \"HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED\"\n    },\n    {\n     \"enum_field_value\" : 35,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_420_888\"\n    },\n    {\n     \"enum_field_value\" : 36,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW_OPAQUE\"\n    },\n    {\n     \"enum_field_value\" : 37,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW10\"\n    },\n    {\n     \"enum_field_value\" : 38,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW12\"\n    },\n    {\n     \"enum_field_value\" : 43,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBA_1010102\"\n    },\n    {\n     \"enum_field_value\" : 538982489,\n     \"name\" : \"HAL_PIXEL_FORMAT_Y8\"\n    },\n    {\n     \"enum_field_value\" : 540422489,\n     \"name\" : \"HAL_PIXEL_FORMAT_Y16\"\n    },\n    {\n     \"enum_field_value\" : 842094169,\n     \"name\" : \"HAL_PIXEL_FORMAT_YV12\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI22android_pixel_format_t\",\n   \"name\" : \"android_pixel_format_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 19,\n     \"name\" : \"ANDROID_PRIORITY_LOWEST\"\n    },\n    {\n     \"enum_field_value\" : 10,\n     \"name\" : \"ANDROID_PRIORITY_BACKGROUND\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"ANDROID_PRIORITY_NORMAL\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"ANDROID_PRIORITY_FOREGROUND\"\n    },\n    {\n     \"enum_field_value\" : -4,\n     \"name\" : \"ANDROID_PRIORITY_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -8,\n     \"name\" : \"ANDROID_PRIORITY_URGENT_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -10,\n     \"name\" : \"ANDROID_PRIORITY_VIDEO\"\n    },\n    {\n     \"enum_field_value\" : -16,\n     \"name\" : \"ANDROID_PRIORITY_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"ANDROID_PRIORITY_URGENT_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -20,\n     \"name\" : \"ANDROID_PRIORITY_HIGHEST\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"ANDROID_PRIORITY_DEFAULT\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"ANDROID_PRIORITY_MORE_FAVORABLE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"ANDROID_PRIORITY_LESS_FAVORABLE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI23$ANDROID_PRIORITY_AUDIO\",\n   \"name\" : \"(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/thread_defs.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 281411584,\n     \"name\" : \"HAL_DATASPACE_BT2020_ITU\"\n    },\n    {\n     \"enum_field_value\" : 298188800,\n     \"name\" : \"HAL_DATASPACE_BT2020_ITU_PQ\"\n    },\n    {\n     \"enum_field_value\" : 302383104,\n     \"name\" : \"HAL_DATASPACE_BT2020_ITU_HLG\"\n    },\n    {\n     \"enum_field_value\" : 168165376,\n     \"name\" : \"HAL_DATASPACE_BT2020_HLG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI24android_dataspace_v1_1_t\",\n   \"name\" : \"android_dataspace_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 142999552,\n     \"name\" : \"HAL_DATASPACE_DISPLAY_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 4098,\n     \"name\" : \"HAL_DATASPACE_DYNAMIC_DEPTH\"\n    },\n    {\n     \"enum_field_value\" : 4099,\n     \"name\" : \"HAL_DATASPACE_JPEG_APP_SEGMENTS\"\n    },\n    {\n     \"enum_field_value\" : 4100,\n     \"name\" : \"HAL_DATASPACE_HEIF\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI24android_dataspace_v1_2_t\",\n   \"name\" : \"android_dataspace_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 10,\n     \"name\" : \"HAL_COLOR_MODE_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 11,\n     \"name\" : \"HAL_COLOR_MODE_BT2100_PQ\"\n    },\n    {\n     \"enum_field_value\" : 12,\n     \"name\" : \"HAL_COLOR_MODE_BT2100_HLG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_color_mode_v1_1_t\",\n   \"name\" : \"android_color_mode_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 13,\n     \"name\" : \"HAL_COLOR_MODE_DISPLAY_BT2020\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_color_mode_v1_2_t\",\n   \"name\" : \"android_color_mode_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_COLOR_TRANSFORM_IDENTITY\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_COLOR_TRANSFORM_VALUE_INVERSE\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_COLOR_TRANSFORM_GRAYSCALE\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_color_transform_t\",\n   \"name\" : \"android_color_transform_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 39,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_422_888\"\n    },\n    {\n     \"enum_field_value\" : 40,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_444_888\"\n    },\n    {\n     \"enum_field_value\" : 41,\n     \"name\" : \"HAL_PIXEL_FORMAT_FLEX_RGB_888\"\n    },\n    {\n     \"enum_field_value\" : 42,\n     \"name\" : \"HAL_PIXEL_FORMAT_FLEX_RGBA_8888\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_pixel_format_sw_t\",\n   \"name\" : \"android_pixel_format_sw_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-sw.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 48,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_16\"\n    },\n    {\n     \"enum_field_value\" : 49,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_24\"\n    },\n    {\n     \"enum_field_value\" : 50,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8\"\n    },\n    {\n     \"enum_field_value\" : 51,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_32F\"\n    },\n    {\n     \"enum_field_value\" : 52,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8\"\n    },\n    {\n     \"enum_field_value\" : 53,\n     \"name\" : \"HAL_PIXEL_FORMAT_STENCIL_8\"\n    },\n    {\n     \"enum_field_value\" : 54,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_P010\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI27android_pixel_format_v1_1_t\",\n   \"name\" : \"android_pixel_format_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 55,\n     \"name\" : \"HAL_PIXEL_FORMAT_HSV_888\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI27android_pixel_format_v1_2_t\",\n   \"name\" : \"android_pixel_format_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_RENDER_INTENT_COLORIMETRIC\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_RENDER_INTENT_ENHANCE\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_RENDER_INTENT_TONE_MAP_ENHANCE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI28android_render_intent_v1_1_t\",\n   \"name\" : \"android_render_intent_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"LOG_ID_MIN\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"LOG_ID_MAIN\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"LOG_ID_RADIO\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"LOG_ID_EVENTS\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"LOG_ID_SYSTEM\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"LOG_ID_CRASH\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"LOG_ID_STATS\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"LOG_ID_SECURITY\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"LOG_ID_KERNEL\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"LOG_ID_MAX\"\n    },\n    {\n     \"enum_field_value\" : 2147483647,\n     \"name\" : \"LOG_ID_DEFAULT\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI6log_id\",\n   \"name\" : \"log_id\",\n   \"size\" : 4,\n   \"source_file\" : \"system/logging/liblog/include_vndk/android/log.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_CTOR\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_DTOR\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_COPY\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE\",\n   \"name\" : \"android::VectorImpl::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_CTOR\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_DTOR\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_COPY\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE\",\n   \"name\" : \"android::VectorImpl::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_pointer<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_pointer<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_pointer<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_pointer<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_pointer<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_pointer<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::OK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::NO_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -2147483648,\n     \"name\" : \"android::UNKNOWN_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -12,\n     \"name\" : \"android::NO_MEMORY\"\n    },\n    {\n     \"enum_field_value\" : -38,\n     \"name\" : \"android::INVALID_OPERATION\"\n    },\n    {\n     \"enum_field_value\" : -22,\n     \"name\" : \"android::BAD_VALUE\"\n    },\n    {\n     \"enum_field_value\" : -2147483647,\n     \"name\" : \"android::BAD_TYPE\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::NAME_NOT_FOUND\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::PERMISSION_DENIED\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"android::NO_INIT\"\n    },\n    {\n     \"enum_field_value\" : -17,\n     \"name\" : \"android::ALREADY_EXISTS\"\n    },\n    {\n     \"enum_field_value\" : -32,\n     \"name\" : \"android::DEAD_OBJECT\"\n    },\n    {\n     \"enum_field_value\" : -2147483646,\n     \"name\" : \"android::FAILED_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -75,\n     \"name\" : \"android::BAD_INDEX\"\n    },\n    {\n     \"enum_field_value\" : -61,\n     \"name\" : \"android::NOT_ENOUGH_DATA\"\n    },\n    {\n     \"enum_field_value\" : -11,\n     \"name\" : \"android::WOULD_BLOCK\"\n    },\n    {\n     \"enum_field_value\" : -110,\n     \"name\" : \"android::TIMED_OUT\"\n    },\n    {\n     \"enum_field_value\" : -74,\n     \"name\" : \"android::UNKNOWN_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -2147483641,\n     \"name\" : \"android::FDS_NOT_ALLOWED\"\n    },\n    {\n     \"enum_field_value\" : -2147483640,\n     \"name\" : \"android::UNEXPECTED_NULL\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android15$ALREADY_EXISTSE\",\n   \"name\" : \"android::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Errors.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::OK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::NO_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -2147483648,\n     \"name\" : \"android::UNKNOWN_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -12,\n     \"name\" : \"android::NO_MEMORY\"\n    },\n    {\n     \"enum_field_value\" : -38,\n     \"name\" : \"android::INVALID_OPERATION\"\n    },\n    {\n     \"enum_field_value\" : -22,\n     \"name\" : \"android::BAD_VALUE\"\n    },\n    {\n     \"enum_field_value\" : -2147483647,\n     \"name\" : \"android::BAD_TYPE\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::NAME_NOT_FOUND\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::PERMISSION_DENIED\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"android::NO_INIT\"\n    },\n    {\n     \"enum_field_value\" : -17,\n     \"name\" : \"android::ALREADY_EXISTS\"\n    },\n    {\n     \"enum_field_value\" : -32,\n     \"name\" : \"android::DEAD_OBJECT\"\n    },\n    {\n     \"enum_field_value\" : -2147483646,\n     \"name\" : \"android::FAILED_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -75,\n     \"name\" : \"android::BAD_INDEX\"\n    },\n    {\n     \"enum_field_value\" : -61,\n     \"name\" : \"android::NOT_ENOUGH_DATA\"\n    },\n    {\n     \"enum_field_value\" : -11,\n     \"name\" : \"android::WOULD_BLOCK\"\n    },\n    {\n     \"enum_field_value\" : -110,\n     \"name\" : \"android::TIMED_OUT\"\n    },\n    {\n     \"enum_field_value\" : -74,\n     \"name\" : \"android::UNKNOWN_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -2147483641,\n     \"name\" : \"android::FDS_NOT_ALLOWED\"\n    },\n    {\n     \"enum_field_value\" : -2147483640,\n     \"name\" : \"android::UNEXPECTED_NULL\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android15$ALREADY_EXISTSE\",\n   \"name\" : \"android::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android15$ALREADY_EXISTSE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/Errors.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Errors.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 19,\n     \"name\" : \"android::PRIORITY_LOWEST\"\n    },\n    {\n     \"enum_field_value\" : 10,\n     \"name\" : \"android::PRIORITY_BACKGROUND\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::PRIORITY_NORMAL\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::PRIORITY_FOREGROUND\"\n    },\n    {\n     \"enum_field_value\" : -4,\n     \"name\" : \"android::PRIORITY_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -8,\n     \"name\" : \"android::PRIORITY_URGENT_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -16,\n     \"name\" : \"android::PRIORITY_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"android::PRIORITY_URGENT_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -20,\n     \"name\" : \"android::PRIORITY_HIGHEST\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::PRIORITY_DEFAULT\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::PRIORITY_MORE_FAVORABLE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::PRIORITY_LESS_FAVORABLE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android15$PRIORITY_AUDIOE\",\n   \"name\" : \"android::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/ThreadDefs.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_copy<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_copy<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_copy<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_ctor<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_ctor<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_ctor<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_dtor<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_dtor<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_dtor<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_move<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_move<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_move<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<android::String8>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<android::String8>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<android::String16>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::String16>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::Mutex::PRIVATE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Mutex::SHARED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android5Mutex8$PRIVATEE\",\n   \"name\" : \"android::Mutex::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Looper::EVENT_INPUT\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::Looper::EVENT_OUTPUT\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::Looper::EVENT_ERROR\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"android::Looper::EVENT_HANGUP\"\n    },\n    {\n     \"enum_field_value\" : 16,\n     \"name\" : \"android::Looper::EVENT_INVALID\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper12$EVENT_ERRORE\",\n   \"name\" : \"android::Looper::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::Looper::POLL_WAKE\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::Looper::POLL_CALLBACK\"\n    },\n    {\n     \"enum_field_value\" : -3,\n     \"name\" : \"android::Looper::POLL_TIMEOUT\"\n    },\n    {\n     \"enum_field_value\" : -4,\n     \"name\" : \"android::Looper::POLL_ERROR\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper14$POLL_CALLBACKE\",\n   \"name\" : \"android::Looper::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Looper::PREPARE_ALLOW_NON_CALLBACKS\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE\",\n   \"name\" : \"android::Looper::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::RWLock::PRIVATE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RWLock::SHARED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLock8$PRIVATEE\",\n   \"name\" : \"android::RWLock::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::is_pointer\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_ctor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_dtor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_copy\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_move\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE\",\n   \"name\" : \"android::traits<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::is_pointer\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_ctor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_dtor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_copy\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_move\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE\",\n   \"name\" : \"android::traits<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::is_pointer\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_ctor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_dtor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_copy\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_move\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE\",\n   \"name\" : \"android::traits<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::FileMap::NORMAL\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::FileMap::RANDOM\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::FileMap::SEQUENTIAL\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"android::FileMap::WILLNEED\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::FileMap::DONTNEED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7FileMap9MapAdviceE\",\n   \"name\" : \"android::FileMap::MapAdvice\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::FIRST_INC_STRONG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase17$FIRST_INC_STRONGE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::FIRST_INC_STRONG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase17$FIRST_INC_STRONGE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android7RefBase17$FIRST_INC_STRONGE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_STRONG\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_WEAK\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_MASK\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_STRONG\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_WEAK\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_MASK\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::Condition::WAKE_UP_ONE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Condition::WAKE_UP_ALL\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9Condition10WakeUpTypeE\",\n   \"name\" : \"android::Condition::WakeUpType\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::Condition::PRIVATE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Condition::SHARED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9Condition8$PRIVATEE\",\n   \"name\" : \"android::Condition::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 20,\n     \"name\" : \"android::FdPrinter::MAX_FORMAT_STRING\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE\",\n   \"name\" : \"android::FdPrinter::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  }\n ],\n \"function_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPFiPvES_PKcimPS_E\",\n   \"name\" : \"int (int (*)(void *), void *, const char *, int, unsigned long, void **)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPKvS0_E\",\n   \"name\" : \"int (const void *, const void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPKvS0_PvE\",\n   \"name\" : \"int (const void *, const void *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPvE\",\n   \"name\" : \"int (void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiiiPvE\",\n   \"name\" : \"int (int, int, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFvvE\",\n   \"name\" : \"void ()\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  }\n ],\n \"functions\" :\n [\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::LogPrinter::printRaw\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinter8printRawEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::LogPrinter::printLine\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinter9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::LogPrinter::LogPrinter\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTI19android_LogPriority\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::LogPrinter::LogPrinter\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTI19android_LogPriority\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::appendArray\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl11appendArrayEPKvm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::setCapacity\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl11setCapacityEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::appendVector\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl12appendVectorERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::editArrayImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13editArrayImplEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::finish_vector\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13finish_vectorEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertArrayAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13insertArrayAtEPKvmm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::removeItemsAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13removeItemsAtEmm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertVectorAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl14insertVectorAtERKS0_m\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::VectorImpl::release_storage\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl15release_storageEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::editItemLocation\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl16editItemLocationEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::add\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl3addEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::add\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl3addEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::pop\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl3popEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::push\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4pushEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::push\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4pushEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::sort\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiPKvS0_E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::sort\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiPKvS0_PvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::VectorImpl::_grow\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl5_growEmm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::clear\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl5clearEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::resize\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl6resizeEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::VectorImpl::_shrink\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl7_shrinkEmm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl8insertAtEPKvmm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl8insertAtEmm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::replaceAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl9replaceAtEPKvm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::replaceAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl9replaceAtEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplC2ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplC2Emj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::~VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::~VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::~VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::operator=\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplaSERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android10VectorImplE\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::uptimeNanos\",\n   \"linker_set_key\" : \"_ZN7android11uptimeNanosEv\",\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::NativeHandle::create\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandle6createEP13native_handleb\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleC1EP13native_handleb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleC2EP13native_handleb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::~NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::~NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"function_name\" : \"android::uptimeMillis\",\n   \"linker_set_key\" : \"_ZN7android12uptimeMillisEv\",\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::PrefixPrinter::printLine\",\n   \"linker_set_key\" : \"_ZN7android13PrefixPrinter9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android13PrefixPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::PrefixPrinter::PrefixPrinter\",\n   \"linker_set_key\" : \"_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android13PrefixPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRN7android7PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::PrefixPrinter::PrefixPrinter\",\n   \"linker_set_key\" : \"_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android13PrefixPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRN7android7PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::LooperCallback::~LooperCallback\",\n   \"linker_set_key\" : \"_ZN7android14LooperCallbackD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::LooperCallback::~LooperCallback\",\n   \"linker_set_key\" : \"_ZN7android14LooperCallbackD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::LooperCallback::~LooperCallback\",\n   \"linker_set_key\" : \"_ZN7android14LooperCallbackD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::MessageHandler::~MessageHandler\",\n   \"linker_set_key\" : \"_ZN7android14MessageHandlerD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::MessageHandler::~MessageHandler\",\n   \"linker_set_key\" : \"_ZN7android14MessageHandlerD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::MessageHandler::~MessageHandler\",\n   \"linker_set_key\" : \"_ZN7android14MessageHandlerD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::String8Printer::printLine\",\n   \"linker_set_key\" : \"_ZN7android14String8Printer9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14String8PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::String8Printer::String8Printer\",\n   \"linker_set_key\" : \"_ZN7android14String8PrinterC1EPNS_7String8EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14String8PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7String8E\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::String8Printer::String8Printer\",\n   \"linker_set_key\" : \"_ZN7android14String8PrinterC2EPNS_7String8EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14String8PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7String8E\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::statusToString\",\n   \"linker_set_key\" : \"_ZN7android14statusToStringEi\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTINSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Errors.h\"\n  },\n  {\n   \"function_name\" : \"android::elapsedRealtime\",\n   \"linker_set_key\" : \"_ZN7android15elapsedRealtimeEv\",\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::add\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl3addEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::merge\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::merge\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl5mergeERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::remove\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl6removeEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplC2Emj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::~SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::~SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::~SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::operator=\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplaSERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android16SortedVectorImplE\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::JenkinsHashWhiten\",\n   \"linker_set_key\" : \"_ZN7android17JenkinsHashWhitenEj\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"function_name\" : \"android::WeakMessageHandler::handleMessage\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::WeakMessageHandler::WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2wpINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::WeakMessageHandler::WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2wpINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::WeakMessageHandler::~WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::WeakMessageHandler::~WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::WeakMessageHandler::~WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::JenkinsHashMixBytes\",\n   \"linker_set_key\" : \"_ZN7android19JenkinsHashMixBytesEjPKhm\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"function_name\" : \"android::elapsedRealtimeNano\",\n   \"linker_set_key\" : \"_ZN7android19elapsedRealtimeNanoEv\",\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::JenkinsHashMixShorts\",\n   \"linker_set_key\" : \"_ZN7android20JenkinsHashMixShortsEjPKtm\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKt\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"function_name\" : \"android::SimpleLooperCallback::handleEvent\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallback11handleEventEiiPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::SimpleLooperCallback::SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackC1EPFiiiPvE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::SimpleLooperCallback::SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackC2EPFiiiPvE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::SimpleLooperCallback::~SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::SimpleLooperCallback::~SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::SimpleLooperCallback::~SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::report_sysprop_change\",\n   \"linker_set_key\" : \"_ZN7android21report_sysprop_changeEv\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  },\n  {\n   \"function_name\" : \"android::add_sysprop_change_callback\",\n   \"linker_set_key\" : \"_ZN7android27add_sysprop_change_callbackEPFvvEi\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFvvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  },\n  {\n   \"function_name\" : \"android::LightRefBase_reportIncStrongRequireStrongFailed\",\n   \"linker_set_key\" : \"_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::sendMessage\",\n   \"linker_set_key\" : \"_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::getForThread\",\n   \"linker_set_key\" : \"_ZN7android6Looper12getForThreadEv\",\n   \"return_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::setForThread\",\n   \"linker_set_key\" : \"_ZN7android6Looper12setForThreadERKNS_2spIS0_EE\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_6LooperEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::removeMessages\",\n   \"linker_set_key\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::removeMessages\",\n   \"linker_set_key\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::getFdStateDebug\",\n   \"linker_set_key\" : \"_ZN7android6Looper15getFdStateDebugEiPiS1_PNS_2spINS_14LooperCallbackEEEPPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android2spINS_14LooperCallbackEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::sendMessageAtTime\",\n   \"linker_set_key\" : \"_ZN7android6Looper17sendMessageAtTimeElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIl\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::rebuildEpollLocked\",\n   \"linker_set_key\" : \"_ZN7android6Looper18rebuildEpollLockedEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::sendMessageDelayed\",\n   \"linker_set_key\" : \"_ZN7android6Looper18sendMessageDelayedElRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIl\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::removeSequenceNumberLocked\",\n   \"linker_set_key\" : \"_ZN7android6Looper26removeSequenceNumberLockedEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::scheduleEpollRebuildLocked\",\n   \"linker_set_key\" : \"_ZN7android6Looper26scheduleEpollRebuildLockedEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::wake\",\n   \"linker_set_key\" : \"_ZN7android6Looper4wakeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::addFd\",\n   \"linker_set_key\" : \"_ZN7android6Looper5addFdEiiiPFiiiPvES1_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::addFd\",\n   \"linker_set_key\" : \"_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14LooperCallbackEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::awoken\",\n   \"linker_set_key\" : \"_ZN7android6Looper6awokenEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::repoll\",\n   \"linker_set_key\" : \"_ZN7android6Looper6repollEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::pollAll\",\n   \"linker_set_key\" : \"_ZN7android6Looper7pollAllEiPiS1_PPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::prepare\",\n   \"linker_set_key\" : \"_ZN7android6Looper7prepareEi\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::pollOnce\",\n   \"linker_set_key\" : \"_ZN7android6Looper8pollOnceEiPiS1_PPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::removeFd\",\n   \"linker_set_key\" : \"_ZN7android6Looper8removeFdEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::pollInner\",\n   \"linker_set_key\" : \"_ZN7android6Looper9pollInnerEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperC1Eb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperC2Eb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Looper::~Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Looper::~Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Looper::~Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::readyToRun\",\n   \"linker_set_key\" : \"_ZN7android6Thread10readyToRunEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Thread::_threadLoop\",\n   \"linker_set_key\" : \"_ZN7android6Thread11_threadLoopEPv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::requestExit\",\n   \"linker_set_key\" : \"_ZN7android6Thread11requestExitEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::requestExitAndWait\",\n   \"linker_set_key\" : \"_ZN7android6Thread18requestExitAndWaitEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::run\",\n   \"linker_set_key\" : \"_ZN7android6Thread3runEPKcim\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::join\",\n   \"linker_set_key\" : \"_ZN7android6Thread4joinEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadC2Eb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::~Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::~Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::~Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::advise\",\n   \"linker_set_key\" : \"_ZN7android7FileMap6adviseENS0_9MapAdviceE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIN7android7FileMap9MapAdviceE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::create\",\n   \"linker_set_key\" : \"_ZN7android7FileMap6createEPKcilmb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIl\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC1EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC2EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::~FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::~FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::operator=\",\n   \"linker_set_key\" : \"_ZN7android7FileMapaSEOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android7FileMapE\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::Printer::printFormatLine\",\n   \"linker_set_key\" : \"_ZN7android7Printer15printFormatLineEPKcz\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterC2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::~Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::~Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::~Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onFirstRef\",\n   \"linker_set_key\" : \"_ZN7android7RefBase10onFirstRefEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::RefBase::renameRefs\",\n   \"linker_set_key\" : \"_ZN7android7RefBase10renameRefsEmRKNS_16ReferenceRenamerE\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android16ReferenceRenamerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::RefBase::renameRefId\",\n   \"linker_set_key\" : \"_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::RefBase::renameRefId\",\n   \"linker_set_key\" : \"_ZN7android7RefBase11renameRefIdEPS0_PKvS3_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::attemptIncWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::attemptIncStrong\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::incWeakRequireWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::decWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type7decWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::incWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type7incWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::trackMe\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type7trackMeEbb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onLastWeakRef\",\n   \"linker_set_key\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onLastStrongRef\",\n   \"linker_set_key\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::extendObjectLifetime\",\n   \"linker_set_key\" : \"_ZN7android7RefBase20extendObjectLifetimeEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onIncStrongAttempted\",\n   \"linker_set_key\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseC1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseC2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::~RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::~RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::~RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::lockBuffer\",\n   \"linker_set_key\" : \"_ZN7android7String810lockBufferEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPc\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String8::real_append\",\n   \"linker_set_key\" : \"_ZN7android7String811real_appendEPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::appendFormat\",\n   \"linker_set_key\" : \"_ZN7android7String812appendFormatEPKcz\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::unlockBuffer\",\n   \"linker_set_key\" : \"_ZN7android7String812unlockBufferEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::unlockBuffer\",\n   \"linker_set_key\" : \"_ZN7android7String812unlockBufferEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::appendFormatV\",\n   \"linker_set_key\" : \"_ZN7android7String813appendFormatVEPKcSt9__va_list\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTISt9__va_list\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::clear\",\n   \"linker_set_key\" : \"_ZN7android7String85clearEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKDim\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::append\",\n   \"linker_set_key\" : \"_ZN7android7String86appendEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::append\",\n   \"linker_set_key\" : \"_ZN7android7String86appendEPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::append\",\n   \"linker_set_key\" : \"_ZN7android7String86appendERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::format\",\n   \"linker_set_key\" : \"_ZN7android7String86formatEPKcz\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::formatV\",\n   \"linker_set_key\" : \"_ZN7android7String87formatVEPKcSt9__va_list\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTISt9__va_list\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::toLower\",\n   \"linker_set_key\" : \"_ZN7android7String87toLowerEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::removeAll\",\n   \"linker_set_key\" : \"_ZN7android7String89removeAllEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDim\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1ERKNS_8String16E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDim\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2ERKNS_8String16E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::~String8\",\n   \"linker_set_key\" : \"_ZN7android7String8D1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::~String8\",\n   \"linker_set_key\" : \"_ZN7android7String8D2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::editResize\",\n   \"linker_set_key\" : \"_ZN7android8String1610editResizeEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::replaceAll\",\n   \"linker_set_key\" : \"_ZN7android8String1610replaceAllEDsDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::allocFromUTF8\",\n   \"linker_set_key\" : \"_ZN7android8String1613allocFromUTF8EPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::allocFromUTF16\",\n   \"linker_set_key\" : \"_ZN7android8String1614allocFromUTF16EPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::edit\",\n   \"linker_set_key\" : \"_ZN7android8String164editEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::alloc\",\n   \"linker_set_key\" : \"_ZN7android8String165allocEm\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToEPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToEPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToERKS0_mm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::append\",\n   \"linker_set_key\" : \"_ZN7android8String166appendEPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::append\",\n   \"linker_set_key\" : \"_ZN7android8String166appendERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::insert\",\n   \"linker_set_key\" : \"_ZN7android8String166insertEmPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::insert\",\n   \"linker_set_key\" : \"_ZN7android8String166insertEmPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::acquire\",\n   \"linker_set_key\" : \"_ZN7android8String167acquireEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::release\",\n   \"linker_set_key\" : \"_ZN7android8String167releaseEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1ERKNS_7String8E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1ERKS0_mm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKDsm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2ERKNS_7String8E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2ERKS0_mm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::~String16\",\n   \"linker_set_key\" : \"_ZN7android8String16D1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::~String16\",\n   \"linker_set_key\" : \"_ZN7android8String16D2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::operator=\",\n   \"linker_set_key\" : \"_ZN7android8String16aSEOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android8String16E\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::FdPrinter::printLine\",\n   \"linker_set_key\" : \"_ZN7android9FdPrinter9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9FdPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::FdPrinter::FdPrinter\",\n   \"linker_set_key\" : \"_ZN7android9FdPrinterC1EijPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9FdPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::FdPrinter::FdPrinter\",\n   \"linker_set_key\" : \"_ZN7android9FdPrinterC2EijPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9FdPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::reset\",\n   \"linker_set_key\" : \"_ZN7android9StopWatch5resetEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchC1EPKci\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchC2EPKci\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::~StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::~StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::fromContents\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::skipDelimiters\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer14skipDelimitersEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::open\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer4openERKNS_7String8EPPS0_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::nextLine\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer8nextLineEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::nextToken\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer9nextTokenEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Tokenizer::Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Tokenizer::Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::~Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::~Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::itemLocation\",\n   \"linker_set_key\" : \"_ZNK7android10VectorImpl12itemLocationEm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPKv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::capacity\",\n   \"linker_set_key\" : \"_ZNK7android10VectorImpl8capacityEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::VectorImpl::itemSize\",\n   \"linker_set_key\" : \"_ZNK7android10VectorImpl8itemSizeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::SortedVectorImpl::_indexOrderOf\",\n   \"linker_set_key\" : \"_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::indexOf\",\n   \"linker_set_key\" : \"_ZNK7android16SortedVectorImpl7indexOfEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::orderOf\",\n   \"linker_set_key\" : \"_ZNK7android16SortedVectorImpl7orderOfEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::getAllowNonCallbacks\",\n   \"linker_set_key\" : \"_ZNK7android6Looper20getAllowNonCallbacksEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::Request::getEpollEvents\",\n   \"linker_set_key\" : \"_ZNK7android6Looper7Request14getEpollEventsEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6Looper7RequestE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::isPolling\",\n   \"linker_set_key\" : \"_ZNK7android6Looper9isPollingEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Thread::exitPending\",\n   \"linker_set_key\" : \"_ZNK7android6Thread11exitPendingEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::getTid\",\n   \"linker_set_key\" : \"_ZNK7android6Thread6getTidEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::isRunning\",\n   \"linker_set_key\" : \"_ZNK7android6Thread9isRunningEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::createWeak\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase10createWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::getWeakRefs\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase11getWeakRefsEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::getWeakCount\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase12weakref_type12getWeakCountEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::refBase\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase12weakref_type7refBaseEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::printRefs\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase12weakref_type9printRefsEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::forceIncStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase14forceIncStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::getStrongCount\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase14getStrongCountEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::incStrongRequireStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase22incStrongRequireStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::decStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase9decStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::incStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase9incStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String8::getPathDir\",\n   \"linker_set_key\" : \"_ZNK7android7String810getPathDirEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String8::getPathExtension\",\n   \"linker_set_key\" : \"_ZNK7android7String816getPathExtensionEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::find\",\n   \"linker_set_key\" : \"_ZNK7android7String84findEPKcm\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::length\",\n   \"linker_set_key\" : \"_ZNK7android7String86lengthEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::startsWith\",\n   \"linker_set_key\" : \"_ZNK7android8String1610startsWithEPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::startsWith\",\n   \"linker_set_key\" : \"_ZNK7android8String1610startsWithERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::isStaticString\",\n   \"linker_set_key\" : \"_ZNK7android8String1614isStaticStringEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::staticStringSize\",\n   \"linker_set_key\" : \"_ZNK7android8String1616staticStringSizeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::size\",\n   \"linker_set_key\" : \"_ZNK7android8String164sizeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::contains\",\n   \"linker_set_key\" : \"_ZNK7android8String168containsEPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::findLast\",\n   \"linker_set_key\" : \"_ZNK7android8String168findLastEDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::findFirst\",\n   \"linker_set_key\" : \"_ZNK7android8String169findFirstEDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::elapsedTime\",\n   \"linker_set_key\" : \"_ZNK7android9StopWatch11elapsedTimeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::name\",\n   \"linker_set_key\" : \"_ZNK7android9StopWatch4nameEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPKc\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::getLocation\",\n   \"linker_set_key\" : \"_ZNK7android9Tokenizer11getLocationEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::peekRemainderOfLine\",\n   \"linker_set_key\" : \"_ZNK7android9Tokenizer19peekRemainderOfLineEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"androidCreateRawThreadEtc\",\n   \"linker_set_key\" : \"androidCreateRawThreadEtc\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidCreateThread\",\n   \"linker_set_key\" : \"androidCreateThread\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidCreateThreadEtc\",\n   \"linker_set_key\" : \"androidCreateThreadEtc\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidGetThreadId\",\n   \"linker_set_key\" : \"androidGetThreadId\",\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidGetThreadPriority\",\n   \"linker_set_key\" : \"androidGetThreadPriority\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidSetCreateThreadFunc\",\n   \"linker_set_key\" : \"androidSetCreateThreadFunc\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPFiPvES_PKcimPS_E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidSetThreadName\",\n   \"linker_set_key\" : \"androidSetThreadName\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidSetThreadPriority\",\n   \"linker_set_key\" : \"androidSetThreadPriority\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"strcmp16\",\n   \"linker_set_key\" : \"strcmp16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strlen16\",\n   \"linker_set_key\" : \"strlen16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strncmp16\",\n   \"linker_set_key\" : \"strncmp16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strnlen16\",\n   \"linker_set_key\" : \"strnlen16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIm\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strstr16\",\n   \"linker_set_key\" : \"strstr16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strzcmp16\",\n   \"linker_set_key\" : \"strzcmp16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"systemTime\",\n   \"linker_set_key\" : \"systemTime\",\n   \"parameters\" :\n   [\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/Timers.h\"\n  },\n  {\n   \"function_name\" : \"toMillisecondTimeoutDelay\",\n   \"linker_set_key\" : \"toMillisecondTimeoutDelay\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIl\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIl\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Timers.h\"\n  },\n  {\n   \"function_name\" : \"utf16_to_utf8\",\n   \"linker_set_key\" : \"utf16_to_utf8\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf16_to_utf8_length\",\n   \"linker_set_key\" : \"utf16_to_utf8_length\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf32_from_utf8_at\",\n   \"linker_set_key\" : \"utf32_from_utf8_at\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf32_to_utf8\",\n   \"linker_set_key\" : \"utf32_to_utf8\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf32_to_utf8_length\",\n   \"linker_set_key\" : \"utf32_to_utf8_length\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf8_to_utf16\",\n   \"linker_set_key\" : \"utf8_to_utf16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf8_to_utf16_length\",\n   \"linker_set_key\" : \"utf8_to_utf16_length\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf8_to_utf16_no_null_terminator\",\n   \"linker_set_key\" : \"utf8_to_utf16_no_null_terminator\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  }\n ],\n \"global_vars\" :\n [\n  {\n   \"access\" : \"private\",\n   \"linker_set_key\" : \"_ZN7android7FileMap9mPageSizeE\",\n   \"name\" : \"android::FileMap::mPageSize\",\n   \"referenced_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  }\n ],\n \"lvalue_reference_types\" :\n [\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRA1_KDs\",\n   \"name\" : \"const char16_t (&)[1]\",\n   \"referenced_type\" : \"_ZTIA1_KDs\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl &\",\n   \"referenced_type\" : \"_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android16ReferenceRenamerE\",\n   \"name\" : \"const android::ReferenceRenamer &\",\n   \"referenced_type\" : \"_ZTIKN7android16ReferenceRenamerE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android16SortedVectorImplE\",\n   \"name\" : \"const android::SortedVectorImpl &\",\n   \"referenced_type\" : \"_ZTIKN7android16SortedVectorImplE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"const android::sysprop_change_callback_info &\",\n   \"referenced_type\" : \"_ZTIKN7android28sysprop_change_callback_infoE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::LooperCallback> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::sp<android::MessageHandler> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::SimpleLooperCallback> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_6LooperEEE\",\n   \"name\" : \"const android::sp<android::Looper> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6LooperEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_6ThreadEEE\",\n   \"name\" : \"const android::sp<android::Thread> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::wp<android::MessageHandler> &\",\n   \"referenced_type\" : \"_ZTIKN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"const android::Looper::MessageEnvelope &\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android6Looper8ResponseE\",\n   \"name\" : \"const android::Looper::Response &\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper8ResponseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"const android::Vector<android::sysprop_change_callback_info> &\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android7MessageE\",\n   \"name\" : \"const android::Message &\",\n   \"referenced_type\" : \"_ZTIKN7android7MessageE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android7String8E\",\n   \"name\" : \"const android::String8 &\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android7String8E\",\n   \"name\" : \"const android::String8 &\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android8String1610StaticDataILm1EEE\",\n   \"name\" : \"const android::String16::StaticData<1> &\",\n   \"referenced_type\" : \"_ZTIKN7android8String1610StaticDataILm1EEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKN7android8String16E\",\n   \"name\" : \"const android::String16 &\",\n   \"referenced_type\" : \"_ZTIKN7android8String16E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKa\",\n   \"name\" : \"const signed char &\",\n   \"referenced_type\" : \"_ZTIKa\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKb\",\n   \"name\" : \"const bool &\",\n   \"referenced_type\" : \"_ZTIKb\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKd\",\n   \"name\" : \"const double &\",\n   \"referenced_type\" : \"_ZTIKd\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKf\",\n   \"name\" : \"const float &\",\n   \"referenced_type\" : \"_ZTIKf\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKh\",\n   \"name\" : \"const unsigned char &\",\n   \"referenced_type\" : \"_ZTIKh\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKi\",\n   \"name\" : \"const int &\",\n   \"referenced_type\" : \"_ZTIKi\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKj\",\n   \"name\" : \"const unsigned int &\",\n   \"referenced_type\" : \"_ZTIKj\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKl\",\n   \"name\" : \"const long &\",\n   \"referenced_type\" : \"_ZTIKl\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKm\",\n   \"name\" : \"const unsigned long &\",\n   \"referenced_type\" : \"_ZTIKm\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKs\",\n   \"name\" : \"const short &\",\n   \"referenced_type\" : \"_ZTIKs\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRKt\",\n   \"name\" : \"const unsigned short &\",\n   \"referenced_type\" : \"_ZTIKt\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl &\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl &\",\n   \"referenced_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"android::sp<android::LooperCallback> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android5MutexE\",\n   \"name\" : \"android::Mutex &\",\n   \"referenced_type\" : \"_ZTIN7android5MutexE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android6Looper8ResponseE\",\n   \"name\" : \"android::Looper::Response &\",\n   \"referenced_type\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android6RWLockE\",\n   \"name\" : \"android::RWLock &\",\n   \"referenced_type\" : \"_ZTIN7android6RWLockE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::Vector<android::sysprop_change_callback_info> &\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android7FileMapE\",\n   \"name\" : \"android::FileMap &\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android7PrinterE\",\n   \"name\" : \"android::Printer &\",\n   \"referenced_type\" : \"_ZTIN7android7PrinterE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android7String8E\",\n   \"name\" : \"android::String8 &\",\n   \"referenced_type\" : \"_ZTIN7android7String8E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android7String8E\",\n   \"name\" : \"android::String8 &\",\n   \"referenced_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIRN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRN7android8String16E\",\n   \"name\" : \"android::String16 &\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRP13native_handle\",\n   \"name\" : \"native_handle *&\",\n   \"referenced_type\" : \"_ZTIP13native_handle\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRPFiiiPvE\",\n   \"name\" : \"int (*&)(int, int, void *)\",\n   \"referenced_type\" : \"_ZTIPFiiiPvE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIRb\",\n   \"name\" : \"bool &\",\n   \"referenced_type\" : \"_ZTIb\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  }\n ],\n \"pointer_types\" :\n [\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP13native_handle\",\n   \"name\" : \"native_handle *\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP18android_flex_plane\",\n   \"name\" : \"android_flex_plane *\",\n   \"referenced_type\" : \"_ZTI18android_flex_plane\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP3DIR\",\n   \"name\" : \"DIR *\",\n   \"referenced_type\" : \"_ZTI3DIR\",\n   \"size\" : 8,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP7__sFILE\",\n   \"name\" : \"__sFILE *\",\n   \"referenced_type\" : \"_ZTI7__sFILE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIP7log_msg\",\n   \"name\" : \"log_msg *\",\n   \"referenced_type\" : \"_ZTI7log_msg\",\n   \"size\" : 8,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPDs\",\n   \"name\" : \"char16_t *\",\n   \"referenced_type\" : \"_ZTIDs\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFiPFiPvES_PKcimPS_E\",\n   \"name\" : \"int (*)(int (*)(void *), void *, const char *, int, unsigned long, void **)\",\n   \"referenced_type\" : \"_ZTIFiPFiPvES_PKcimPS_E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFiPKvS0_E\",\n   \"name\" : \"int (*)(const void *, const void *)\",\n   \"referenced_type\" : \"_ZTIFiPKvS0_E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFiPKvS0_PvE\",\n   \"name\" : \"int (*)(const void *, const void *, void *)\",\n   \"referenced_type\" : \"_ZTIFiPKvS0_PvE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFiPvE\",\n   \"name\" : \"int (*)(void *)\",\n   \"referenced_type\" : \"_ZTIFiPvE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFiiiPvE\",\n   \"name\" : \"int (*)(int, int, void *)\",\n   \"referenced_type\" : \"_ZTIFiiiPvE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPFvvE\",\n   \"name\" : \"void (*)()\",\n   \"referenced_type\" : \"_ZTIFvvE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPK13native_handle\",\n   \"name\" : \"const native_handle *\",\n   \"referenced_type\" : \"_ZTIK13native_handle\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPK7log_msg\",\n   \"name\" : \"const log_msg *\",\n   \"referenced_type\" : \"_ZTIK7log_msg\",\n   \"size\" : 8,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKDi\",\n   \"name\" : \"const char32_t *\",\n   \"referenced_type\" : \"_ZTIKDi\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKDs\",\n   \"name\" : \"const char16_t *\",\n   \"referenced_type\" : \"_ZTIKDs\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl *\",\n   \"referenced_type\" : \"_ZTIKN7android10VectorImplE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl *\",\n   \"referenced_type\" : \"_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"const android::LightRefBase<android::NativeHandle> *\",\n   \"referenced_type\" : \"_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android12NativeHandleE\",\n   \"name\" : \"const android::NativeHandle *\",\n   \"referenced_type\" : \"_ZTIKN7android12NativeHandleE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android16SortedVectorImplE\",\n   \"name\" : \"const android::SortedVectorImpl *\",\n   \"referenced_type\" : \"_ZTIKN7android16SortedVectorImplE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"const android::sysprop_change_callback_info *\",\n   \"referenced_type\" : \"_ZTIKN7android28sysprop_change_callback_infoE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::LooperCallback> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::sp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_6LooperEEE\",\n   \"name\" : \"const android::sp<android::Looper> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6LooperEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_6ThreadEEE\",\n   \"name\" : \"const android::sp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::wp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIKN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"const android::wp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIKN7android2wpINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android4base11borrowed_fdE\",\n   \"name\" : \"const android::base::borrowed_fd *\",\n   \"referenced_type\" : \"_ZTIKN7android4base11borrowed_fdE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"const android::base::unique_fd_impl<android::base::DefaultCloser> *\",\n   \"referenced_type\" : \"_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"const android::Looper::MessageEnvelope *\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6Looper7RequestE\",\n   \"name\" : \"const android::Looper::Request *\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper7RequestE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6Looper8ResponseE\",\n   \"name\" : \"const android::Looper::Response *\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper8ResponseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6LooperE\",\n   \"name\" : \"const android::Looper *\",\n   \"referenced_type\" : \"_ZTIKN7android6LooperE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6ThreadE\",\n   \"name\" : \"const android::Thread *\",\n   \"referenced_type\" : \"_ZTIKN7android6ThreadE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"const android::Vector<android::sysprop_change_callback_info> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"const android::Vector<android::Looper::MessageEnvelope> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"const android::Vector<android::Looper::Response> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_6Looper8ResponseEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_7String8EEE\",\n   \"name\" : \"const android::Vector<android::String8> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_7String8EEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android7FileMapE\",\n   \"name\" : \"const android::FileMap *\",\n   \"referenced_type\" : \"_ZTIKN7android7FileMapE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android7RefBase12weakref_typeE\",\n   \"name\" : \"const android::RefBase::weakref_type *\",\n   \"referenced_type\" : \"_ZTIKN7android7RefBase12weakref_typeE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase *\",\n   \"referenced_type\" : \"_ZTIKN7android7RefBaseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase *\",\n   \"referenced_type\" : \"_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android7String8E\",\n   \"name\" : \"const android::String8 *\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android7String8E\",\n   \"name\" : \"const android::String8 *\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android8String16E\",\n   \"name\" : \"const android::String16 *\",\n   \"referenced_type\" : \"_ZTIKN7android8String16E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android9StopWatchE\",\n   \"name\" : \"const android::StopWatch *\",\n   \"referenced_type\" : \"_ZTIKN7android9StopWatchE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKN7android9TokenizerE\",\n   \"name\" : \"const android::Tokenizer *\",\n   \"referenced_type\" : \"_ZTIKN7android9TokenizerE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKc\",\n   \"name\" : \"const char *\",\n   \"referenced_type\" : \"_ZTIKc\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKh\",\n   \"name\" : \"const unsigned char *\",\n   \"referenced_type\" : \"_ZTIKh\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKt\",\n   \"name\" : \"const unsigned short *\",\n   \"referenced_type\" : \"_ZTIKt\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPKv\",\n   \"name\" : \"const void *\",\n   \"referenced_type\" : \"_ZTIKv\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android10LogPrinterE\",\n   \"name\" : \"android::LogPrinter *\",\n   \"referenced_type\" : \"_ZTIN7android10LogPrinterE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl *\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android11ScopedTraceE\",\n   \"name\" : \"android::ScopedTrace *\",\n   \"referenced_type\" : \"_ZTIN7android11ScopedTraceE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Trace.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"android::LightRefBase<android::NativeHandle> *\",\n   \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android12NativeHandleE\",\n   \"name\" : \"android::NativeHandle *\",\n   \"referenced_type\" : \"_ZTIN7android12NativeHandleE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android13PrefixPrinterE\",\n   \"name\" : \"android::PrefixPrinter *\",\n   \"referenced_type\" : \"_ZTIN7android13PrefixPrinterE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android14LooperCallbackE\",\n   \"name\" : \"android::LooperCallback *\",\n   \"referenced_type\" : \"_ZTIN7android14LooperCallbackE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android14MessageHandlerE\",\n   \"name\" : \"android::MessageHandler *\",\n   \"referenced_type\" : \"_ZTIN7android14MessageHandlerE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android14StaticString16ILm1EEE\",\n   \"name\" : \"android::StaticString16<1> *\",\n   \"referenced_type\" : \"_ZTIN7android14StaticString16ILm1EEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android14String8PrinterE\",\n   \"name\" : \"android::String8Printer *\",\n   \"referenced_type\" : \"_ZTIN7android14String8PrinterE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer *\",\n   \"referenced_type\" : \"_ZTIN7android16ReferenceRenamerE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer *\",\n   \"referenced_type\" : \"_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl *\",\n   \"referenced_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android18WeakMessageHandlerE\",\n   \"name\" : \"android::WeakMessageHandler *\",\n   \"referenced_type\" : \"_ZTIN7android18WeakMessageHandlerE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase *\",\n   \"referenced_type\" : \"_ZTIN7android19VirtualLightRefBaseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase *\",\n   \"referenced_type\" : \"_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android20SimpleLooperCallbackE\",\n   \"name\" : \"android::SimpleLooperCallback *\",\n   \"referenced_type\" : \"_ZTIN7android20SimpleLooperCallbackE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"android::sysprop_change_callback_info *\",\n   \"referenced_type\" : \"_ZTIN7android28sysprop_change_callback_infoE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_12NativeHandleEEE\",\n   \"name\" : \"android::sp<android::NativeHandle> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"android::sp<android::LooperCallback> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"android::wp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"android::wp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android4base11borrowed_fdE\",\n   \"name\" : \"android::base::borrowed_fd *\",\n   \"referenced_type\" : \"_ZTIN7android4base11borrowed_fdE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"android::base::unique_fd_impl<android::base::DefaultCloser> *\",\n   \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android5Mutex8AutolockE\",\n   \"name\" : \"android::Mutex::Autolock *\",\n   \"referenced_type\" : \"_ZTIN7android5Mutex8AutolockE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android5MutexE\",\n   \"name\" : \"android::Mutex *\",\n   \"referenced_type\" : \"_ZTIN7android5MutexE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"android::Looper::MessageEnvelope *\",\n   \"referenced_type\" : \"_ZTIN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6Looper8ResponseE\",\n   \"name\" : \"android::Looper::Response *\",\n   \"referenced_type\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6LooperE\",\n   \"name\" : \"android::Looper *\",\n   \"referenced_type\" : \"_ZTIN7android6LooperE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6RWLock9AutoRLockE\",\n   \"name\" : \"android::RWLock::AutoRLock *\",\n   \"referenced_type\" : \"_ZTIN7android6RWLock9AutoRLockE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6RWLock9AutoWLockE\",\n   \"name\" : \"android::RWLock::AutoWLock *\",\n   \"referenced_type\" : \"_ZTIN7android6RWLock9AutoWLockE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6RWLockE\",\n   \"name\" : \"android::RWLock *\",\n   \"referenced_type\" : \"_ZTIN7android6RWLockE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6ThreadE\",\n   \"name\" : \"android::Thread *\",\n   \"referenced_type\" : \"_ZTIN7android6ThreadE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::Vector<android::sysprop_change_callback_info> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::Vector<android::Looper::MessageEnvelope> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::Vector<android::Looper::Response> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_7String8EEE\",\n   \"name\" : \"android::Vector<android::String8> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_7String8EEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7FileMapE\",\n   \"name\" : \"android::FileMap *\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7MessageE\",\n   \"name\" : \"android::Message *\",\n   \"referenced_type\" : \"_ZTIN7android7MessageE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7PrinterE\",\n   \"name\" : \"android::Printer *\",\n   \"referenced_type\" : \"_ZTIN7android7PrinterE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBase12weakref_implE\",\n   \"name\" : \"android::RefBase::weakref_impl *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_implE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_typeE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBaseE\",\n   \"name\" : \"android::RefBase *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBaseE\",\n   \"name\" : \"android::RefBase *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7String8E\",\n   \"name\" : \"android::String8 *\",\n   \"referenced_type\" : \"_ZTIN7android7String8E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android7String8E\",\n   \"name\" : \"android::String8 *\",\n   \"referenced_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android8String1610StaticDataILm1EEE\",\n   \"name\" : \"android::String16::StaticData<1> *\",\n   \"referenced_type\" : \"_ZTIN7android8String1610StaticDataILm1EEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android8String16E\",\n   \"name\" : \"android::String16 *\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android9ConditionE\",\n   \"name\" : \"android::Condition *\",\n   \"referenced_type\" : \"_ZTIN7android9ConditionE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android9FdPrinterE\",\n   \"name\" : \"android::FdPrinter *\",\n   \"referenced_type\" : \"_ZTIN7android9FdPrinterE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android9StopWatchE\",\n   \"name\" : \"android::StopWatch *\",\n   \"referenced_type\" : \"_ZTIN7android9StopWatchE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPN7android9TokenizerE\",\n   \"name\" : \"android::Tokenizer *\",\n   \"referenced_type\" : \"_ZTIN7android9TokenizerE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPPN7android9TokenizerE\",\n   \"name\" : \"android::Tokenizer **\",\n   \"referenced_type\" : \"_ZTIPN7android9TokenizerE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPPv\",\n   \"name\" : \"void **\",\n   \"referenced_type\" : \"_ZTIPv\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPc\",\n   \"name\" : \"char *\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPh\",\n   \"name\" : \"unsigned char *\",\n   \"referenced_type\" : \"_ZTIh\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPi\",\n   \"name\" : \"int *\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPm\",\n   \"name\" : \"unsigned long *\",\n   \"referenced_type\" : \"_ZTIm\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIPv\",\n   \"name\" : \"void *\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  }\n ],\n \"qualified_types\" :\n [\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIA1_KDs\",\n   \"name\" : \"const char16_t[1]\",\n   \"referenced_type\" : \"_ZTIA1_Ds\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK13native_handle\",\n   \"name\" : \"const native_handle\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK7log_msg\",\n   \"name\" : \"const log_msg\",\n   \"referenced_type\" : \"_ZTI7log_msg\",\n   \"size\" : 5124,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKDi\",\n   \"name\" : \"const char32_t\",\n   \"referenced_type\" : \"_ZTIDi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKDs\",\n   \"name\" : \"const char16_t\",\n   \"referenced_type\" : \"_ZTIDs\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"const android::LightRefBase<android::NativeHandle>\",\n   \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android12NativeHandleE\",\n   \"name\" : \"const android::NativeHandle\",\n   \"referenced_type\" : \"_ZTIN7android12NativeHandleE\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android16ReferenceRenamerE\",\n   \"name\" : \"const android::ReferenceRenamer\",\n   \"referenced_type\" : \"_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android16SortedVectorImplE\",\n   \"name\" : \"const android::SortedVectorImpl\",\n   \"referenced_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"const android::sysprop_change_callback_info\",\n   \"referenced_type\" : \"_ZTIN7android28sysprop_change_callback_infoE\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::LooperCallback>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::sp<android::MessageHandler>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::SimpleLooperCallback>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_6LooperEEE\",\n   \"name\" : \"const android::sp<android::Looper>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_6ThreadEEE\",\n   \"name\" : \"const android::sp<android::Thread>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::wp<android::MessageHandler>\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"const android::wp<android::Thread>\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_6ThreadEEE\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android4base11borrowed_fdE\",\n   \"name\" : \"const android::base::borrowed_fd\",\n   \"referenced_type\" : \"_ZTIN7android4base11borrowed_fdE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"const android::base::unique_fd_impl<android::base::DefaultCloser>\",\n   \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"const android::Looper::MessageEnvelope\",\n   \"referenced_type\" : \"_ZTIN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6Looper7RequestE\",\n   \"name\" : \"const android::Looper::Request\",\n   \"referenced_type\" : \"_ZTIN7android6Looper7RequestE\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6Looper8ResponseE\",\n   \"name\" : \"const android::Looper::Response\",\n   \"referenced_type\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"size\" : 48,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6LooperE\",\n   \"name\" : \"const android::Looper\",\n   \"referenced_type\" : \"_ZTIN7android6LooperE\",\n   \"size\" : 264,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6ThreadE\",\n   \"name\" : \"const android::Thread\",\n   \"referenced_type\" : \"_ZTIN7android6ThreadE\",\n   \"size\" : 152,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"const android::Vector<android::sysprop_change_callback_info>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"const android::Vector<android::Looper::MessageEnvelope>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"const android::Vector<android::Looper::Response>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_7String8EEE\",\n   \"name\" : \"const android::Vector<android::String8>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_7String8EEE\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7FileMapE\",\n   \"name\" : \"const android::FileMap\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 48,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7MessageE\",\n   \"name\" : \"const android::Message\",\n   \"referenced_type\" : \"_ZTIN7android7MessageE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7RefBase12weakref_typeE\",\n   \"name\" : \"const android::RefBase::weakref_type\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7String8E\",\n   \"name\" : \"const android::String8\",\n   \"referenced_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7String8E\",\n   \"name\" : \"const android::String8\",\n   \"referenced_type\" : \"_ZTIN7android7String8E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android8String1610StaticDataILm1EEE\",\n   \"name\" : \"const android::String16::StaticData<1>\",\n   \"referenced_type\" : \"_ZTIN7android8String1610StaticDataILm1EEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android8String16E\",\n   \"name\" : \"const android::String16\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android9StopWatchE\",\n   \"name\" : \"const android::StopWatch\",\n   \"referenced_type\" : \"_ZTIN7android9StopWatchE\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android9TokenizerE\",\n   \"name\" : \"const android::Tokenizer\",\n   \"referenced_type\" : \"_ZTIN7android9TokenizerE\",\n   \"size\" : 56,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKPN7android7RefBase12weakref_implE\",\n   \"name\" : \"android::RefBase::weakref_impl *const\",\n   \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_implE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKa\",\n   \"name\" : \"const signed char\",\n   \"referenced_type\" : \"_ZTIa\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKb\",\n   \"name\" : \"const bool\",\n   \"referenced_type\" : \"_ZTIb\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKc\",\n   \"name\" : \"const char\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKd\",\n   \"name\" : \"const double\",\n   \"referenced_type\" : \"_ZTId\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKf\",\n   \"name\" : \"const float\",\n   \"referenced_type\" : \"_ZTIf\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKh\",\n   \"name\" : \"const unsigned char\",\n   \"referenced_type\" : \"_ZTIh\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKi\",\n   \"name\" : \"const int\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKj\",\n   \"name\" : \"const unsigned int\",\n   \"referenced_type\" : \"_ZTIj\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKl\",\n   \"name\" : \"const long\",\n   \"referenced_type\" : \"_ZTIl\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKm\",\n   \"name\" : \"const unsigned long\",\n   \"referenced_type\" : \"_ZTIm\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKs\",\n   \"name\" : \"const short\",\n   \"referenced_type\" : \"_ZTIs\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKt\",\n   \"name\" : \"const unsigned short\",\n   \"referenced_type\" : \"_ZTIt\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKv\",\n   \"name\" : \"const void\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_volatile\" : true,\n   \"linker_set_key\" : \"_ZTIVb\",\n   \"name\" : \"volatile bool\",\n   \"referenced_type\" : \"_ZTIb\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  }\n ],\n \"record_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"len\",\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"field_name\" : \"hdr_size\",\n     \"field_offset\" : 16,\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"field_name\" : \"pid\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"tid\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"sec\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"nsec\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"lid\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"uid\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI12logger_entry\",\n   \"name\" : \"logger_entry\",\n   \"size\" : 28,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"y\",\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"field_name\" : \"cb\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"field_name\" : \"cr\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"field_name\" : \"ystride\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"field_name\" : \"cstride\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"field_name\" : \"chroma_step\",\n     \"field_offset\" : 320,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"field_name\" : \"reserved\",\n     \"field_offset\" : 384,\n     \"referenced_type\" : \"_ZTIA8_j\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13android_ycbcr\",\n   \"name\" : \"android_ycbcr\",\n   \"size\" : 80,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"version\",\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numFds\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numInts\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIA0_i\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13native_handle\",\n   \"name\" : \"native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libcutils/include_outside_system/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"x\",\n     \"referenced_type\" : \"_ZTIf\"\n    },\n    {\n     \"field_name\" : \"y\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI16android_xy_color\",\n   \"name\" : \"android_xy_color\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"top_left\",\n     \"referenced_type\" : \"_ZTIPh\"\n    },\n    {\n     \"field_name\" : \"component\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTI22android_flex_component\"\n    },\n    {\n     \"field_name\" : \"bits_per_component\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"bits_used\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"h_increment\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"v_increment\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"h_subsampling\",\n     \"field_offset\" : 224,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"v_subsampling\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI18android_flex_plane\",\n   \"name\" : \"android_flex_plane\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"format\",\n     \"referenced_type\" : \"_ZTI19android_flex_format\"\n    },\n    {\n     \"field_name\" : \"num_planes\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"planes\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIP18android_flex_plane\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_flex_layout\",\n   \"name\" : \"android_flex_layout\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"num_points\",\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"reserved\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIA8_j\"\n    },\n    {\n     \"field_name\" : \"xyzc_points\",\n     \"field_offset\" : 288,\n     \"referenced_type\" : \"_ZTIA_f\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI20android_depth_points\",\n   \"name\" : \"android_depth_points\",\n   \"size\" : 36,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"struct_size\",\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"field_name\" : \"buffer_id\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"priority\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"tag\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"field_name\" : \"file\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"field_name\" : \"line\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"message\",\n     \"field_offset\" : 320,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI21__android_log_message\",\n   \"name\" : \"__android_log_message\",\n   \"size\" : 48,\n   \"source_file\" : \"system/logging/liblog/include_vndk/android/log.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"maxContentLightLevel\",\n     \"referenced_type\" : \"_ZTIf\"\n    },\n    {\n     \"field_name\" : \"maxFrameAverageLightLevel\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_cta861_3_metadata\",\n   \"name\" : \"android_cta861_3_metadata\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"displayPrimaryRed\",\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"displayPrimaryGreen\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"displayPrimaryBlue\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"whitePoint\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"maxLuminance\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIf\"\n    },\n    {\n     \"field_name\" : \"minLuminance\",\n     \"field_offset\" : 288,\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI26android_smpte2086_metadata\",\n   \"name\" : \"android_smpte2086_metadata\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7log_msgUt_E\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI7log_msg\",\n   \"name\" : \"log_msg\",\n   \"size\" : 5124,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"tv_sec\",\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"tv_nsec\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI8log_time\",\n   \"name\" : \"log_time\",\n   \"size\" : 8,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_time.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLogTag\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPriority\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTI19android_LogPriority\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mIgnoreBlankLines\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10LogPrinterE\",\n   \"name\" : \"android::LogPrinter\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android10LogPrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android10LogPrinter9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10LogPrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10LogPrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStorage\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFlags\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIKj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mItemSize\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIKm\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android10VectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStorage\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFlags\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIKj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mItemSize\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIKm\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android10VectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mTag\",\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android11ScopedTraceE\",\n   \"name\" : \"android::ScopedTrace\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Trace.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"referenced_type\" : \"_ZTINSt3__16atomicIiEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"android::LightRefBase<android::NativeHandle>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android12NativeHandleE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"referenced_type\" : \"_ZTINSt3__16atomicIiEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE\",\n   \"name\" : \"android::LightRefBase<android::VirtualLightRefBase>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android19VirtualLightRefBaseE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"referenced_type\" : \"_ZTINSt3__16atomicIiEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE\",\n   \"name\" : \"android::LightRefBase<android::VirtualLightRefBase>\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/LightRefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mHandle\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mOwnsHandle\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12NativeHandleE\",\n   \"name\" : \"android::NativeHandle\",\n   \"record_kind\" : \"class\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrinter\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIRN7android7PrinterE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13PrefixPrinterE\",\n   \"name\" : \"android::PrefixPrinter\",\n   \"record_kind\" : \"class\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android13PrefixPrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android13PrefixPrinter9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android13PrefixPrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android13PrefixPrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_pointer<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_pointer<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_pointer<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"is_virtual\" : true,\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14LooperCallbackE\",\n   \"name\" : \"android::LooperCallback\",\n   \"record_kind\" : \"class\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 8,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14LooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14LooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14LooperCallbackD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android14LooperCallback11handleEventEiiPv\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -8,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -8,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14LooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android14LooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android14LooperCallbackD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"is_virtual\" : true,\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14MessageHandlerE\",\n   \"name\" : \"android::MessageHandler\",\n   \"record_kind\" : \"class\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 8,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14MessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14MessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14MessageHandlerD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android14MessageHandler13handleMessageERKNS_7MessageE\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -8,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -8,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14MessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android14MessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android14MessageHandlerD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android14ReferenceMoverE\",\n   \"name\" : \"android::ReferenceMover\",\n   \"record_kind\" : \"class\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android14ReferenceMoverE\",\n   \"name\" : \"android::ReferenceMover\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android14ReferenceMoverE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android8String16E\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mData\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIKN7android8String1610StaticDataILm1EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14StaticString16ILm1EEE\",\n   \"name\" : \"android::StaticString16<1>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mTarget\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPN7android7String8E\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14String8PrinterE\",\n   \"name\" : \"android::String8Printer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14String8PrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android14String8Printer9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14String8PrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14String8PrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16ReferenceRenamerE\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16ReferenceRenamerclEm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16ReferenceRenamerE\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16ReferenceRenamerclEm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16SortedVectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16SortedVectorImpl10do_compareEPKvS2_\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16SortedVectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvm\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16SortedVectorImpl10do_compareEPKvS2_\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTINSt3__117integral_constantIbLb0EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::use_trivial_move<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTINSt3__117integral_constantIbLb0EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::use_trivial_move<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTINSt3__117integral_constantIbLb0EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::use_trivial_move<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android14MessageHandlerE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mHandler\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18WeakMessageHandlerE\",\n   \"name\" : \"android::WeakMessageHandler\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 24,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android18WeakMessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android18WeakMessageHandlerD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -24,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -24,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android18WeakMessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android18WeakMessageHandlerD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_copy<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbEE\",\n   \"name\" : \"android::trait_trivial_copy<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbEE\",\n   \"name\" : \"android::trait_trivial_copy<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcEE\",\n   \"name\" : \"android::trait_trivial_copy<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcEE\",\n   \"name\" : \"android::trait_trivial_copy<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdEE\",\n   \"name\" : \"android::trait_trivial_copy<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdEE\",\n   \"name\" : \"android::trait_trivial_copy<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfEE\",\n   \"name\" : \"android::trait_trivial_copy<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfEE\",\n   \"name\" : \"android::trait_trivial_copy<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiEE\",\n   \"name\" : \"android::trait_trivial_copy<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiEE\",\n   \"name\" : \"android::trait_trivial_copy<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlEE\",\n   \"name\" : \"android::trait_trivial_copy<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlEE\",\n   \"name\" : \"android::trait_trivial_copy<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsEE\",\n   \"name\" : \"android::trait_trivial_copy<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsEE\",\n   \"name\" : \"android::trait_trivial_copy<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvEE\",\n   \"name\" : \"android::trait_trivial_copy<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvEE\",\n   \"name\" : \"android::trait_trivial_copy<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxEE\",\n   \"name\" : \"android::trait_trivial_copy<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxEE\",\n   \"name\" : \"android::trait_trivial_copy<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_ctor<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbEE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbEE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcEE\",\n   \"name\" : \"android::trait_trivial_ctor<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcEE\",\n   \"name\" : \"android::trait_trivial_ctor<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdEE\",\n   \"name\" : \"android::trait_trivial_ctor<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdEE\",\n   \"name\" : \"android::trait_trivial_ctor<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfEE\",\n   \"name\" : \"android::trait_trivial_ctor<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfEE\",\n   \"name\" : \"android::trait_trivial_ctor<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiEE\",\n   \"name\" : \"android::trait_trivial_ctor<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiEE\",\n   \"name\" : \"android::trait_trivial_ctor<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlEE\",\n   \"name\" : \"android::trait_trivial_ctor<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlEE\",\n   \"name\" : \"android::trait_trivial_ctor<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsEE\",\n   \"name\" : \"android::trait_trivial_ctor<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsEE\",\n   \"name\" : \"android::trait_trivial_ctor<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvEE\",\n   \"name\" : \"android::trait_trivial_ctor<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvEE\",\n   \"name\" : \"android::trait_trivial_ctor<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxEE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxEE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_dtor<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbEE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbEE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcEE\",\n   \"name\" : \"android::trait_trivial_dtor<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcEE\",\n   \"name\" : \"android::trait_trivial_dtor<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdEE\",\n   \"name\" : \"android::trait_trivial_dtor<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdEE\",\n   \"name\" : \"android::trait_trivial_dtor<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfEE\",\n   \"name\" : \"android::trait_trivial_dtor<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfEE\",\n   \"name\" : \"android::trait_trivial_dtor<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiEE\",\n   \"name\" : \"android::trait_trivial_dtor<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiEE\",\n   \"name\" : \"android::trait_trivial_dtor<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlEE\",\n   \"name\" : \"android::trait_trivial_dtor<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlEE\",\n   \"name\" : \"android::trait_trivial_dtor<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsEE\",\n   \"name\" : \"android::trait_trivial_dtor<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsEE\",\n   \"name\" : \"android::trait_trivial_dtor<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvEE\",\n   \"name\" : \"android::trait_trivial_dtor<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvEE\",\n   \"name\" : \"android::trait_trivial_dtor<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxEE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxEE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_move<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EEE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android7String8E\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EEE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_8String16EEE\",\n   \"name\" : \"android::trait_trivial_move<android::String16>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android8String16E\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbEE\",\n   \"name\" : \"android::trait_trivial_move<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbEE\",\n   \"name\" : \"android::trait_trivial_move<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcEE\",\n   \"name\" : \"android::trait_trivial_move<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcEE\",\n   \"name\" : \"android::trait_trivial_move<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdEE\",\n   \"name\" : \"android::trait_trivial_move<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdEE\",\n   \"name\" : \"android::trait_trivial_move<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfEE\",\n   \"name\" : \"android::trait_trivial_move<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfEE\",\n   \"name\" : \"android::trait_trivial_move<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiEE\",\n   \"name\" : \"android::trait_trivial_move<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiEE\",\n   \"name\" : \"android::trait_trivial_move<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlEE\",\n   \"name\" : \"android::trait_trivial_move<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlEE\",\n   \"name\" : \"android::trait_trivial_move<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsEE\",\n   \"name\" : \"android::trait_trivial_move<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsEE\",\n   \"name\" : \"android::trait_trivial_move<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvEE\",\n   \"name\" : \"android::trait_trivial_move<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvEE\",\n   \"name\" : \"android::trait_trivial_move<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxEE\",\n   \"name\" : \"android::trait_trivial_move<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxEE\",\n   \"name\" : \"android::trait_trivial_move<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase\",\n   \"record_kind\" : \"class\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android19VirtualLightRefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/LightRefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android19VirtualLightRefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android14LooperCallbackE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCallback\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android20SimpleLooperCallbackE\",\n   \"name\" : \"android::SimpleLooperCallback\",\n   \"record_kind\" : \"class\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 16,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android20SimpleLooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android20SimpleLooperCallbackD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android20SimpleLooperCallback11handleEventEiiPv\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -16,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -16,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android20SimpleLooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android20SimpleLooperCallbackD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"name\" : \"android::sp<android::NativeHandle>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android12NativeHandleE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"android::sp<android::LooperCallback>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android14LooperCallbackE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android14MessageHandlerE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android20SimpleLooperCallbackE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6LooperE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6ThreadE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_refs\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"android::wp<android::MessageHandler>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android14MessageHandlerE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_refs\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"android::wp<android::Thread>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6ThreadE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"fd_\",\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android4base11borrowed_fdE\",\n   \"name\" : \"android::base::borrowed_fd\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android4base13DefaultCloserE\",\n   \"name\" : \"android::base::DefaultCloser\",\n   \"size\" : 1,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"fd_\",\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"android::base::unique_fd_impl<android::base::DefaultCloser>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android4base13DefaultCloserE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"referenced_type\" : \"_ZTIRN7android5MutexE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android5Mutex8AutolockE\",\n   \"name\" : \"android::Mutex::Autolock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mMutex\",\n     \"referenced_type\" : \"_ZTI15pthread_mutex_t\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android5MutexE\",\n   \"name\" : \"android::Mutex\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"uptime\",\n     \"referenced_type\" : \"_ZTIl\"\n    },\n    {\n     \"field_name\" : \"handler\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"field_name\" : \"message\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIN7android7MessageE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"android::Looper::MessageEnvelope\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"fd\",\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"ident\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"events\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"callback\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper7RequestE\",\n   \"name\" : \"android::Looper::Request\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"seq\",\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"field_name\" : \"events\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"request\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIN7android6Looper7RequestE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"name\" : \"android::Looper::Response\",\n   \"size\" : 48,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mAllowNonCallbacks\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIKb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mWakeEventFd\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIN7android5MutexE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mMessageEnvelopes\",\n     \"field_offset\" : 512,\n     \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mSendingMessage\",\n     \"field_offset\" : 832,\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPolling\",\n     \"field_offset\" : 840,\n     \"referenced_type\" : \"_ZTIVb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mEpollFd\",\n     \"field_offset\" : 864,\n     \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mEpollRebuildRequired\",\n     \"field_offset\" : 896,\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRequests\",\n     \"field_offset\" : 960,\n     \"referenced_type\" : \"_ZTINSt3__113unordered_mapImN7android6Looper7RequestENS_4hashImEENS_8equal_toImEENS_9allocatorINS_4pairIKmS3_EEEEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mSequenceNumberByFd\",\n     \"field_offset\" : 1280,\n     \"referenced_type\" : \"_ZTINSt3__113unordered_mapIimNS_4hashIiEENS_8equal_toIiEENS_9allocatorINS_4pairIKimEEEEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mNextRequestSeq\",\n     \"field_offset\" : 1600,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mResponses\",\n     \"field_offset\" : 1664,\n     \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mResponseIndex\",\n     \"field_offset\" : 1984,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mNextMessageUptime\",\n     \"field_offset\" : 2048,\n     \"referenced_type\" : \"_ZTIl\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6LooperE\",\n   \"name\" : \"android::Looper\",\n   \"record_kind\" : \"class\",\n   \"size\" : 264,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6LooperE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6LooperD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6LooperD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"referenced_type\" : \"_ZTIRN7android6RWLockE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLock9AutoRLockE\",\n   \"name\" : \"android::RWLock::AutoRLock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"referenced_type\" : \"_ZTIRN7android6RWLockE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLock9AutoWLockE\",\n   \"name\" : \"android::RWLock::AutoWLock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRWLock\",\n     \"referenced_type\" : \"_ZTI16pthread_rwlock_t\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLockE\",\n   \"name\" : \"android::RWLock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 56,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"is_virtual\" : true,\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCanCallJava\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIKb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mThread\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIN7android5MutexE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mThreadExitedCondition\",\n     \"field_offset\" : 512,\n     \"referenced_type\" : \"_ZTIN7android9ConditionE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStatus\",\n     \"field_offset\" : 896,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mExitPending\",\n     \"field_offset\" : 928,\n     \"referenced_type\" : \"_ZTIVb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRunning\",\n     \"field_offset\" : 936,\n     \"referenced_type\" : \"_ZTIVb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mHoldSelf\",\n     \"field_offset\" : 960,\n     \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mTid\",\n     \"field_offset\" : 1024,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6ThreadE\",\n   \"name\" : \"android::Thread\",\n   \"record_kind\" : \"class\",\n   \"size\" : 152,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 136,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6ThreadE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6ThreadD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6ThreadD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android6Thread3runEPKcim\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android6Thread11requestExitEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android6Thread10readyToRunEv\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android6Thread10threadLoopEv\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -136,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -136,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6ThreadE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android6ThreadD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n24_N7android6ThreadD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::Vector<android::sysprop_change_callback_info>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_28sysprop_change_callback_infoEED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_28sysprop_change_callback_infoEED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::Vector<android::Looper::MessageEnvelope>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper15MessageEnvelopeEED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper15MessageEnvelopeEED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::Vector<android::Looper::Response>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper8ResponseEED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper8ResponseEED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_7String8EEE\",\n   \"name\" : \"android::Vector<android::String8>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_7String8EEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_7String8EED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_7String8EED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE12do_constructEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE10do_destroyEPvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE7do_copyEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE8do_splatEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE15do_move_forwardEPvPKvm\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE16do_move_backwardEPvPKvm\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::traits<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::traits<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::traits<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFileName\",\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mBasePtr\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mBaseLength\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mDataOffset\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIl\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mDataPtr\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mDataLength\",\n     \"field_offset\" : 320,\n     \"referenced_type\" : \"_ZTIm\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7FileMapE\",\n   \"name\" : \"android::FileMap\",\n   \"record_kind\" : \"class\",\n   \"size\" : 48,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"what\",\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7MessageE\",\n   \"name\" : \"android::Message\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTIN7android7PrinterE\",\n   \"name\" : \"android::Printer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android7PrinterE\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android7Printer9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7PrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7PrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type\",\n   \"record_kind\" : \"class\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRefs\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIKPN7android7RefBase12weakref_implE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBaseE\",\n   \"name\" : \"android::RefBase\",\n   \"record_kind\" : \"class\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android7RefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRefs\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIKPN7android7RefBase12weakref_implE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBaseE\",\n   \"name\" : \"android::RefBase\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android7RefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mString\",\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7String8E\",\n   \"name\" : \"android::String8\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mString\",\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7String8E\",\n   \"name\" : \"android::String8\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm64_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"size\",\n     \"referenced_type\" : \"_ZTIKj\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIA1_Ds\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android8String1610StaticDataILm1EEE\",\n   \"name\" : \"android::String16::StaticData<1>\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mString\",\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android8String16E\",\n   \"name\" : \"android::String16\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCond\",\n     \"referenced_type\" : \"_ZTI14pthread_cond_t\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9ConditionE\",\n   \"name\" : \"android::Condition\",\n   \"record_kind\" : \"class\",\n   \"size\" : 48,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFd\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mIndent\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFormatString\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIA20_c\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9FdPrinterE\",\n   \"name\" : \"android::FdPrinter\",\n   \"record_kind\" : \"class\",\n   \"size\" : 48,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android9FdPrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android9FdPrinter9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android9FdPrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android9FdPrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mName\",\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mClock\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStartTime\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIl\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9StopWatchE\",\n   \"name\" : \"android::StopWatch\",\n   \"record_kind\" : \"class\",\n   \"size\" : 24,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFilename\",\n     \"referenced_type\" : \"_ZTIN7android7String8E\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFileMap\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mBuffer\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mOwnBuffer\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLength\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIm\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCurrent\",\n     \"field_offset\" : 320,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLineNumber\",\n     \"field_offset\" : 384,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9TokenizerE\",\n   \"name\" : \"android::Tokenizer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 56,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"buf\",\n     \"referenced_type\" : \"_ZTIA5121_h\"\n    },\n    {\n     \"field_name\" : \"entry\",\n     \"referenced_type\" : \"_ZTI12logger_entry\"\n    }\n   ],\n   \"is_anonymous\" : true,\n   \"linker_set_key\" : \"_ZTIN7log_msgUt_E\",\n   \"name\" : \"log_msg::(anonymous)\",\n   \"record_kind\" : \"union\",\n   \"size\" : 5124,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  }\n ],\n \"rvalue_reference_types\" :\n [\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android2spINS_12NativeHandleEEE\",\n   \"name\" : \"android::sp<android::NativeHandle> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"android::base::unique_fd_impl<android::base::DefaultCloser> &&\",\n   \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android7FileMapE\",\n   \"name\" : \"android::FileMap &&\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTION7android8String16E\",\n   \"name\" : \"android::String16 &&\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  }\n ]\n}\n"
  },
  {
    "path": "libutils/abi-dumps/arm_arm64/source-based/libutils.so.lsdump",
    "content": "{\n \"array_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIA0_i\",\n   \"name\" : \"int[0]\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libcutils/include_outside_system/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"linker_set_key\" : \"_ZTIA1_Ds\",\n   \"name\" : \"char16_t[1]\",\n   \"referenced_type\" : \"_ZTIDs\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIA20_c\",\n   \"name\" : \"char[20]\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIA5121_h\",\n   \"name\" : \"unsigned char[5121]\",\n   \"referenced_type\" : \"_ZTIh\",\n   \"size\" : 5121,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIA8_j\",\n   \"name\" : \"unsigned int[8]\",\n   \"referenced_type\" : \"_ZTIj\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"is_of_unknown_bound\" : true,\n   \"linker_set_key\" : \"_ZTIA_f\",\n   \"name\" : \"float[]\",\n   \"referenced_type\" : \"_ZTIf\",\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  }\n ],\n \"builtin_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIDi\",\n   \"name\" : \"char32_t\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIDn\",\n   \"name\" : \"std::nullptr_t\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIDs\",\n   \"name\" : \"char16_t\",\n   \"size\" : 2\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIa\",\n   \"name\" : \"signed char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIb\",\n   \"name\" : \"bool\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIc\",\n   \"name\" : \"char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 8,\n   \"linker_set_key\" : \"_ZTId\",\n   \"name\" : \"double\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIf\",\n   \"name\" : \"float\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 1,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIh\",\n   \"name\" : \"unsigned char\",\n   \"size\" : 1\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIi\",\n   \"name\" : \"int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIj\",\n   \"name\" : \"unsigned int\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIl\",\n   \"name\" : \"long\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 4,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIm\",\n   \"name\" : \"unsigned long\",\n   \"size\" : 4\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIs\",\n   \"name\" : \"short\",\n   \"size\" : 2\n  },\n  {\n   \"alignment\" : 2,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIt\",\n   \"name\" : \"unsigned short\",\n   \"size\" : 2\n  },\n  {\n   \"linker_set_key\" : \"_ZTIv\",\n   \"name\" : \"void\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"linker_set_key\" : \"_ZTIx\",\n   \"name\" : \"long long\",\n   \"size\" : 8\n  },\n  {\n   \"alignment\" : 8,\n   \"is_integral\" : true,\n   \"is_unsigned\" : true,\n   \"linker_set_key\" : \"_ZTIy\",\n   \"name\" : \"unsigned long long\",\n   \"size\" : 8\n  }\n ],\n \"elf_functions\" :\n [\n  {\n   \"name\" : \"_Z24androidCreateThreadGetIDPFiPvES_PS_\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinter8printRawEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinter9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b\"\n  },\n  {\n   \"name\" : \"_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl11appendArrayEPKvj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl11setCapacityEj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl12appendVectorERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13editArrayImplEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13finish_vectorEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13insertArrayAtEPKvjj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl13removeItemsAtEjj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl14insertVectorAtERKS0_j\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl15release_storageEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl16editItemLocationEj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl3addEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl3addEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl3popEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4pushEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4pushEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_E\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl5_growEjj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl5clearEv\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl6resizeEj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl7_shrinkEjj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl8insertAtEPKvjj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl8insertAtEjj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl9replaceAtEPKvj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImpl9replaceAtEj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplC2ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplC2Ejj\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android10VectorImplaSERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android11uptimeNanosEv\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandle6createEP13native_handleb\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleC1EP13native_handleb\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleC2EP13native_handleb\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android12NativeHandleD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android12SharedBuffer5allocEj\"\n  },\n  {\n   \"name\" : \"_ZN7android12SharedBuffer7deallocEPKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android12uptimeMillisEv\"\n  },\n  {\n   \"name\" : \"_ZN7android13PrefixPrinter9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14LooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14LooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14LooperCallbackD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14MessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14MessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14MessageHandlerD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android14String8Printer9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14String8PrinterC1EPNS_7String8EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14String8PrinterC2EPNS_7String8EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android14sp_report_raceEv\"\n  },\n  {\n   \"name\" : \"_ZN7android14statusToStringEi\"\n  },\n  {\n   \"name\" : \"_ZN7android15elapsedRealtimeEv\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl3addEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl5mergeERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImpl6removeEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplC2Ejj\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android16SortedVectorImplaSERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android17JenkinsHashWhitenEj\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android18WeakMessageHandlerD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android19JenkinsHashMixBytesEjPKhj\"\n  },\n  {\n   \"name\" : \"_ZN7android19elapsedRealtimeNanoEv\"\n  },\n  {\n   \"name\" : \"_ZN7android20JenkinsHashMixShortsEjPKtj\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallback11handleEventEiiPv\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackC1EPFiiiPvE\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackC2EPFiiiPvE\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android20SimpleLooperCallbackD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android21report_sysprop_changeEv\"\n  },\n  {\n   \"name\" : \"_ZN7android23sp_report_stack_pointerEv\"\n  },\n  {\n   \"name\" : \"_ZN7android27add_sysprop_change_callbackEPFvvEi\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android2spINS_14LooperCallbackEE5clearEv\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android2spINS_14LooperCallbackEEaSERKS2_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android2spINS_6LooperEED2Ev\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android2spINS_6LooperEEaSEOS2_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android2spINS_6LooperEEaSERKS2_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android2spINS_6ThreadEE5clearEv\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZN7android2spINS_6ThreadEEaSEOS2_\"\n  },\n  {\n   \"name\" : \"_ZN7android30get_report_sysprop_change_funcEv\"\n  },\n  {\n   \"name\" : \"_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper12getForThreadEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper12setForThreadERKNS_2spIS0_EE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper15getFdStateDebugEiPiS1_PNS_2spINS_14LooperCallbackEEEPPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper17sendMessageAtTimeExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper18rebuildEpollLockedEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper18sendMessageDelayedExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper26removeSequenceNumberLockedEy\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper26scheduleEpollRebuildLockedEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper4wakeEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper5addFdEiiiPFiiiPvES1_\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper6awokenEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper6repollEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper7pollAllEiPiS1_PPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper7prepareEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper8pollOnceEiPiS1_PPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper8removeFdEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6Looper9pollInnerEi\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperC1Eb\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperC2Eb\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6LooperD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread10readyToRunEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread11_threadLoopEPv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread11requestExitEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread18requestExitAndWaitEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread3runEPKcij\"\n  },\n  {\n   \"name\" : \"_ZN7android6Thread4joinEv\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadC2Eb\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android6ThreadD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMap6adviseENS0_9MapAdviceE\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMap6createEPKcixjb\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC1EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC2EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapC2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7FileMapaSEOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterC2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7PrinterD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase10renameRefsEjRKNS_16ReferenceRenamerE\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase11renameRefIdEPS0_PKvS3_\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type7decWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type7incWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase12weakref_type7trackMeEbb\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase20extendObjectLifetimeEi\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseC1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseC2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseD0Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7RefBaseD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String810lockBufferEj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String811real_appendEPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String812appendFormatEPKcz\"\n  },\n  {\n   \"name\" : \"_ZN7android7String812unlockBufferEj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String812unlockBufferEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7String813appendFormatVEPKcSt9__va_list\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85clearEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKDij\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToEPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String85setToERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86appendEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86appendEPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86appendERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String86formatEPKcz\"\n  },\n  {\n   \"name\" : \"_ZN7android7String87formatVEPKcSt9__va_list\"\n  },\n  {\n   \"name\" : \"_ZN7android7String87toLowerEv\"\n  },\n  {\n   \"name\" : \"_ZN7android7String89removeAllEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDi\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDij\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1EPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1ERKNS_8String16E\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDi\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDij\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2EPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2ERKNS_8String16E\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8C2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8D1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android7String8D2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1610editResizeEj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1610replaceAllEDsDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1613allocFromUTF8EPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String1614allocFromUTF16EPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String164editEv\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165allocEj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToEPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToEPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String165setToERKS0_jj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166appendEPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166appendERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166insertEjPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String166insertEjPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String167acquireEv\"\n  },\n  {\n   \"name\" : \"_ZN7android8String167releaseEv\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1EPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1ERKNS_7String8E\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1ERKS0_jj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKDs\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKDsj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2EPKcj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2ERKNS_7String8E\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2ERKS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2ERKS0_jj\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16C2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16D1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16D2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android8String16aSEOS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android9FdPrinter9printLineEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9FdPrinterC1EijPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9FdPrinterC2EijPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatch5resetEv\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchC1EPKci\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchC2EPKci\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android9StopWatchD2Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer14skipDelimitersEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer4openERKNS_7String8EPPS0_\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer8nextLineEv\"\n  },\n  {\n   \"name\" : \"_ZN7android9Tokenizer9nextTokenEPKc\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbj\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbj\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZN7android9TokenizerD2Ev\"\n  },\n  {\n   \"name\" : \"_ZNK7android10VectorImpl12itemLocationEj\"\n  },\n  {\n   \"name\" : \"_ZNK7android10VectorImpl8capacityEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android10VectorImpl8itemSizeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer10editResizeEj\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer11attemptEditEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer4editEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer5resetEj\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer7acquireEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android12SharedBuffer7releaseEj\"\n  },\n  {\n   \"name\" : \"_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPj\"\n  },\n  {\n   \"name\" : \"_ZNK7android16SortedVectorImpl7indexOfEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android16SortedVectorImpl7orderOfEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Looper20getAllowNonCallbacksEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Looper7Request14getEpollEventsEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Looper9isPollingEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Thread11exitPendingEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Thread6getTidEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android6Thread9isRunningEv\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvj\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase10createWeakEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase11getWeakRefsEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase12weakref_type12getWeakCountEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase12weakref_type7refBaseEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase12weakref_type9printRefsEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase14forceIncStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase14getStrongCountEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase22incStrongRequireStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase9decStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7RefBase9incStrongEPKv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String810getPathDirEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String816getPathExtensionEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String84findEPKcj\"\n  },\n  {\n   \"name\" : \"_ZNK7android7String86lengthEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1610startsWithEPKDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1610startsWithERKS0_\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1614isStaticStringEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String1616staticStringSizeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String164sizeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String168containsEPKDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String168findLastEDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android8String169findFirstEDs\"\n  },\n  {\n   \"name\" : \"_ZNK7android9StopWatch11elapsedTimeEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android9StopWatch4nameEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android9Tokenizer11getLocationEv\"\n  },\n  {\n   \"name\" : \"_ZNK7android9Tokenizer19peekRemainderOfLineEv\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEENS_8equal_toIiEELb1EEENS_21__unordered_map_equalIiS2_S7_S5_Lb1EEENS_9allocatorIS2_EEE11__do_rehashILb1EEEvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEENS_8equal_toIiEELb1EEENS_21__unordered_map_equalIiS2_S7_S5_Lb1EEENS_9allocatorIS2_EEE14__erase_uniqueIiEEjRKT_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEENS_8equal_toIiEELb1EEENS_21__unordered_map_equalIiS2_S7_S5_Lb1EEENS_9allocatorIS2_EEE25__emplace_unique_key_argsIiJRiRKyEEENS_4pairINS_15__hash_iteratorIPNS_11__hash_nodeIS2_PvEEEEbEERKT_DpOT0_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEENS_8equal_toIiEELb1EEENS_21__unordered_map_equalIiS2_S7_S5_Lb1EEENS_9allocatorIS2_EEE4findIiEENS_15__hash_iteratorIPNS_11__hash_nodeIS2_PvEEEERKT_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIiyEENS_22__unordered_map_hasherIiS2_NS_4hashIiEENS_8equal_toIiEELb1EEENS_21__unordered_map_equalIiS2_S7_S5_Lb1EEENS_9allocatorIS2_EEE8__rehashILb1EEEvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEENS_8equal_toIyEELb1EEENS_21__unordered_map_equalIyS5_SA_S8_Lb1EEENS_9allocatorIS5_EEE11__do_rehashILb1EEEvj\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEENS_8equal_toIyEELb1EEENS_21__unordered_map_equalIyS5_SA_S8_Lb1EEENS_9allocatorIS5_EEE14__erase_uniqueIyEEjRKT_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEENS_8equal_toIyEELb1EEENS_21__unordered_map_equalIyS5_SA_S8_Lb1EEENS_9allocatorIS5_EEE25__emplace_unique_key_argsIyJRKyRS4_EEENS_4pairINS_15__hash_iteratorIPNS_11__hash_nodeIS5_PvEEEEbEERKT_DpOT0_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEENS_8equal_toIyEELb1EEENS_21__unordered_map_equalIyS5_SA_S8_Lb1EEENS_9allocatorIS5_EEE4findIyEENS_15__hash_iteratorIPNS_11__hash_nodeIS5_PvEEEERKT_\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZNSt3__112__hash_tableINS_17__hash_value_typeIyN7android6Looper7RequestEEENS_22__unordered_map_hasherIyS5_NS_4hashIyEENS_8equal_toIyEELb1EEENS_21__unordered_map_equalIyS5_SA_S8_Lb1EEENS_9allocatorIS5_EEE8__rehashILb1EEEvj\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android14LooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android14LooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android14MessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android14MessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android18WeakMessageHandlerD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android18WeakMessageHandlerD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android20SimpleLooperCallbackD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android20SimpleLooperCallbackD1Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android6ThreadD0Ev\"\n  },\n  {\n   \"name\" : \"_ZTv0_n12_N7android6ThreadD1Ev\"\n  },\n  {\n   \"name\" : \"androidCreateRawThreadEtc\"\n  },\n  {\n   \"name\" : \"androidCreateThread\"\n  },\n  {\n   \"name\" : \"androidCreateThreadEtc\"\n  },\n  {\n   \"name\" : \"androidGetThreadId\"\n  },\n  {\n   \"name\" : \"androidGetThreadPriority\"\n  },\n  {\n   \"name\" : \"androidSetCreateThreadFunc\"\n  },\n  {\n   \"name\" : \"androidSetThreadName\"\n  },\n  {\n   \"name\" : \"androidSetThreadPriority\"\n  },\n  {\n   \"name\" : \"do_report_sysprop_change\"\n  },\n  {\n   \"name\" : \"strcmp16\"\n  },\n  {\n   \"name\" : \"strlen16\"\n  },\n  {\n   \"name\" : \"strncmp16\"\n  },\n  {\n   \"name\" : \"strnlen16\"\n  },\n  {\n   \"name\" : \"strstr16\"\n  },\n  {\n   \"name\" : \"strzcmp16\"\n  },\n  {\n   \"name\" : \"systemTime\"\n  },\n  {\n   \"name\" : \"toMillisecondTimeoutDelay\"\n  },\n  {\n   \"name\" : \"utf16_to_utf8\"\n  },\n  {\n   \"name\" : \"utf16_to_utf8_length\"\n  },\n  {\n   \"name\" : \"utf32_from_utf8_at\"\n  },\n  {\n   \"name\" : \"utf32_to_utf8\"\n  },\n  {\n   \"name\" : \"utf32_to_utf8_length\"\n  },\n  {\n   \"name\" : \"utf8_to_utf16\"\n  },\n  {\n   \"name\" : \"utf8_to_utf16_length\"\n  },\n  {\n   \"name\" : \"utf8_to_utf16_no_null_terminator\"\n  }\n ],\n \"elf_objects\" :\n [\n  {\n   \"name\" : \"_ZN7android7FileMap9mPageSizeE\"\n  },\n  {\n   \"name\" : \"_ZTCN7android18WeakMessageHandlerE0_NS_14MessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTCN7android20SimpleLooperCallbackE0_NS_14LooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android14LooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android14MessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android18WeakMessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android20SimpleLooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTTN7android6ThreadE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android10LogPrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android10VectorImplE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android13PrefixPrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android14LooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android14MessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android14String8PrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android16SortedVectorImplE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android18WeakMessageHandlerE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android20SimpleLooperCallbackE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android6LooperE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android6ThreadE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZTVN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZTVN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"_ZTVN7android6VectorINS_6Looper8ResponseEEE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android7PrinterE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android7RefBaseE\"\n  },\n  {\n   \"name\" : \"_ZTVN7android9FdPrinterE\"\n  },\n  {\n   \"binding\" : \"weak\",\n   \"name\" : \"__llvm_fs_discriminator__\"\n  }\n ],\n \"enum_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_HDR_DOLBY_VISION\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_HDR_HDR10\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_HDR_HLG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13android_hdr_t\",\n   \"name\" : \"android_hdr_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_HDR_HDR10_PLUS\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI18android_hdr_v1_2_t\",\n   \"name\" : \"android_hdr_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"ANDROID_LOG_UNKNOWN\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"ANDROID_LOG_DEFAULT\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"ANDROID_LOG_VERBOSE\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"ANDROID_LOG_DEBUG\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"ANDROID_LOG_INFO\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"ANDROID_LOG_WARN\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"ANDROID_LOG_ERROR\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"ANDROID_LOG_FATAL\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"ANDROID_LOG_SILENT\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_LogPriority\",\n   \"name\" : \"android_LogPriority\",\n   \"size\" : 4,\n   \"source_file\" : \"system/logging/liblog/include_vndk/android/log.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_UNKNOWN\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_DATASPACE_ARBITRARY\"\n    },\n    {\n     \"enum_field_value\" : 16,\n     \"name\" : \"HAL_DATASPACE_STANDARD_SHIFT\"\n    },\n    {\n     \"enum_field_value\" : 4128768,\n     \"name\" : \"HAL_DATASPACE_STANDARD_MASK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_STANDARD_UNSPECIFIED\"\n    },\n    {\n     \"enum_field_value\" : 65536,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT709\"\n    },\n    {\n     \"enum_field_value\" : 131072,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 196608,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 262144,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 327680,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 393216,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 458752,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE\"\n    },\n    {\n     \"enum_field_value\" : 524288,\n     \"name\" : \"HAL_DATASPACE_STANDARD_BT470M\"\n    },\n    {\n     \"enum_field_value\" : 589824,\n     \"name\" : \"HAL_DATASPACE_STANDARD_FILM\"\n    },\n    {\n     \"enum_field_value\" : 655360,\n     \"name\" : \"HAL_DATASPACE_STANDARD_DCI_P3\"\n    },\n    {\n     \"enum_field_value\" : 720896,\n     \"name\" : \"HAL_DATASPACE_STANDARD_ADOBE_RGB\"\n    },\n    {\n     \"enum_field_value\" : 22,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_SHIFT\"\n    },\n    {\n     \"enum_field_value\" : 130023424,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_MASK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_UNSPECIFIED\"\n    },\n    {\n     \"enum_field_value\" : 4194304,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 8388608,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 12582912,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_SMPTE_170M\"\n    },\n    {\n     \"enum_field_value\" : 16777216,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_GAMMA2_2\"\n    },\n    {\n     \"enum_field_value\" : 20971520,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_GAMMA2_6\"\n    },\n    {\n     \"enum_field_value\" : 25165824,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_GAMMA2_8\"\n    },\n    {\n     \"enum_field_value\" : 29360128,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_ST2084\"\n    },\n    {\n     \"enum_field_value\" : 33554432,\n     \"name\" : \"HAL_DATASPACE_TRANSFER_HLG\"\n    },\n    {\n     \"enum_field_value\" : 27,\n     \"name\" : \"HAL_DATASPACE_RANGE_SHIFT\"\n    },\n    {\n     \"enum_field_value\" : 939524096,\n     \"name\" : \"HAL_DATASPACE_RANGE_MASK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_DATASPACE_RANGE_UNSPECIFIED\"\n    },\n    {\n     \"enum_field_value\" : 134217728,\n     \"name\" : \"HAL_DATASPACE_RANGE_FULL\"\n    },\n    {\n     \"enum_field_value\" : 268435456,\n     \"name\" : \"HAL_DATASPACE_RANGE_LIMITED\"\n    },\n    {\n     \"enum_field_value\" : 402653184,\n     \"name\" : \"HAL_DATASPACE_RANGE_EXTENDED\"\n    },\n    {\n     \"enum_field_value\" : 512,\n     \"name\" : \"HAL_DATASPACE_SRGB_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 138477568,\n     \"name\" : \"HAL_DATASPACE_V0_SRGB_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 406913024,\n     \"name\" : \"HAL_DATASPACE_V0_SCRGB_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 513,\n     \"name\" : \"HAL_DATASPACE_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 142671872,\n     \"name\" : \"HAL_DATASPACE_V0_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 411107328,\n     \"name\" : \"HAL_DATASPACE_V0_SCRGB\"\n    },\n    {\n     \"enum_field_value\" : 257,\n     \"name\" : \"HAL_DATASPACE_JFIF\"\n    },\n    {\n     \"enum_field_value\" : 146931712,\n     \"name\" : \"HAL_DATASPACE_V0_JFIF\"\n    },\n    {\n     \"enum_field_value\" : 258,\n     \"name\" : \"HAL_DATASPACE_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 281149440,\n     \"name\" : \"HAL_DATASPACE_V0_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 259,\n     \"name\" : \"HAL_DATASPACE_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 281280512,\n     \"name\" : \"HAL_DATASPACE_V0_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 260,\n     \"name\" : \"HAL_DATASPACE_BT709\"\n    },\n    {\n     \"enum_field_value\" : 281083904,\n     \"name\" : \"HAL_DATASPACE_V0_BT709\"\n    },\n    {\n     \"enum_field_value\" : 139067392,\n     \"name\" : \"HAL_DATASPACE_DCI_P3_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 155844608,\n     \"name\" : \"HAL_DATASPACE_DCI_P3\"\n    },\n    {\n     \"enum_field_value\" : 139067392,\n     \"name\" : \"HAL_DATASPACE_DISPLAY_P3_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 143261696,\n     \"name\" : \"HAL_DATASPACE_DISPLAY_P3\"\n    },\n    {\n     \"enum_field_value\" : 151715840,\n     \"name\" : \"HAL_DATASPACE_ADOBE_RGB\"\n    },\n    {\n     \"enum_field_value\" : 138805248,\n     \"name\" : \"HAL_DATASPACE_BT2020_LINEAR\"\n    },\n    {\n     \"enum_field_value\" : 147193856,\n     \"name\" : \"HAL_DATASPACE_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 163971072,\n     \"name\" : \"HAL_DATASPACE_BT2020_PQ\"\n    },\n    {\n     \"enum_field_value\" : 4096,\n     \"name\" : \"HAL_DATASPACE_DEPTH\"\n    },\n    {\n     \"enum_field_value\" : 4097,\n     \"name\" : \"HAL_DATASPACE_SENSOR\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_dataspace_t\",\n   \"name\" : \"android_dataspace_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"FLEX_FORMAT_INVALID\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"FLEX_FORMAT_Y\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"FLEX_FORMAT_YCbCr\"\n    },\n    {\n     \"enum_field_value\" : 1073741831,\n     \"name\" : \"FLEX_FORMAT_YCbCrA\"\n    },\n    {\n     \"enum_field_value\" : 7168,\n     \"name\" : \"FLEX_FORMAT_RGB\"\n    },\n    {\n     \"enum_field_value\" : 1073748992,\n     \"name\" : \"FLEX_FORMAT_RGBA\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_flex_format\",\n   \"name\" : \"android_flex_format\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_TRANSFORM_FLIP_H\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_TRANSFORM_FLIP_V\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_TRANSFORM_ROT_90\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_TRANSFORM_ROT_180\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"HAL_TRANSFORM_ROT_270\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_transform_t\",\n   \"name\" : \"android_transform_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_COLOR_MODE_NATIVE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_625\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_625_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_525\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT601_525_UNADJUSTED\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"HAL_COLOR_MODE_STANDARD_BT709\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"HAL_COLOR_MODE_DCI_P3\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"HAL_COLOR_MODE_SRGB\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"HAL_COLOR_MODE_ADOBE_RGB\"\n    },\n    {\n     \"enum_field_value\" : 9,\n     \"name\" : \"HAL_COLOR_MODE_DISPLAY_P3\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI20android_color_mode_t\",\n   \"name\" : \"android_color_mode_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"SYSTEM_TIME_REALTIME\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"SYSTEM_TIME_MONOTONIC\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"SYSTEM_TIME_PROCESS\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"SYSTEM_TIME_THREAD\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"SYSTEM_TIME_BOOTTIME\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI21$SYSTEM_TIME_BOOTTIME\",\n   \"name\" : \"(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Timers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"FLEX_COMPONENT_Y\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"FLEX_COMPONENT_Cb\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"FLEX_COMPONENT_Cr\"\n    },\n    {\n     \"enum_field_value\" : 1024,\n     \"name\" : \"FLEX_COMPONENT_R\"\n    },\n    {\n     \"enum_field_value\" : 2048,\n     \"name\" : \"FLEX_COMPONENT_G\"\n    },\n    {\n     \"enum_field_value\" : 4096,\n     \"name\" : \"FLEX_COMPONENT_B\"\n    },\n    {\n     \"enum_field_value\" : 1073741824,\n     \"name\" : \"FLEX_COMPONENT_A\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI22android_flex_component\",\n   \"name\" : \"android_flex_component\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBA_8888\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBX_8888\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGB_888\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGB_565\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"HAL_PIXEL_FORMAT_BGRA_8888\"\n    },\n    {\n     \"enum_field_value\" : 16,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_422_SP\"\n    },\n    {\n     \"enum_field_value\" : 17,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCRCB_420_SP\"\n    },\n    {\n     \"enum_field_value\" : 20,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_422_I\"\n    },\n    {\n     \"enum_field_value\" : 22,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBA_FP16\"\n    },\n    {\n     \"enum_field_value\" : 32,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW16\"\n    },\n    {\n     \"enum_field_value\" : 33,\n     \"name\" : \"HAL_PIXEL_FORMAT_BLOB\"\n    },\n    {\n     \"enum_field_value\" : 34,\n     \"name\" : \"HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED\"\n    },\n    {\n     \"enum_field_value\" : 35,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_420_888\"\n    },\n    {\n     \"enum_field_value\" : 36,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW_OPAQUE\"\n    },\n    {\n     \"enum_field_value\" : 37,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW10\"\n    },\n    {\n     \"enum_field_value\" : 38,\n     \"name\" : \"HAL_PIXEL_FORMAT_RAW12\"\n    },\n    {\n     \"enum_field_value\" : 43,\n     \"name\" : \"HAL_PIXEL_FORMAT_RGBA_1010102\"\n    },\n    {\n     \"enum_field_value\" : 538982489,\n     \"name\" : \"HAL_PIXEL_FORMAT_Y8\"\n    },\n    {\n     \"enum_field_value\" : 540422489,\n     \"name\" : \"HAL_PIXEL_FORMAT_Y16\"\n    },\n    {\n     \"enum_field_value\" : 842094169,\n     \"name\" : \"HAL_PIXEL_FORMAT_YV12\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI22android_pixel_format_t\",\n   \"name\" : \"android_pixel_format_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 19,\n     \"name\" : \"ANDROID_PRIORITY_LOWEST\"\n    },\n    {\n     \"enum_field_value\" : 10,\n     \"name\" : \"ANDROID_PRIORITY_BACKGROUND\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"ANDROID_PRIORITY_NORMAL\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"ANDROID_PRIORITY_FOREGROUND\"\n    },\n    {\n     \"enum_field_value\" : -4,\n     \"name\" : \"ANDROID_PRIORITY_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -8,\n     \"name\" : \"ANDROID_PRIORITY_URGENT_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -10,\n     \"name\" : \"ANDROID_PRIORITY_VIDEO\"\n    },\n    {\n     \"enum_field_value\" : -16,\n     \"name\" : \"ANDROID_PRIORITY_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"ANDROID_PRIORITY_URGENT_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -20,\n     \"name\" : \"ANDROID_PRIORITY_HIGHEST\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"ANDROID_PRIORITY_DEFAULT\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"ANDROID_PRIORITY_MORE_FAVORABLE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"ANDROID_PRIORITY_LESS_FAVORABLE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI23$ANDROID_PRIORITY_AUDIO\",\n   \"name\" : \"(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/thread_defs.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 281411584,\n     \"name\" : \"HAL_DATASPACE_BT2020_ITU\"\n    },\n    {\n     \"enum_field_value\" : 298188800,\n     \"name\" : \"HAL_DATASPACE_BT2020_ITU_PQ\"\n    },\n    {\n     \"enum_field_value\" : 302383104,\n     \"name\" : \"HAL_DATASPACE_BT2020_ITU_HLG\"\n    },\n    {\n     \"enum_field_value\" : 168165376,\n     \"name\" : \"HAL_DATASPACE_BT2020_HLG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI24android_dataspace_v1_1_t\",\n   \"name\" : \"android_dataspace_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 142999552,\n     \"name\" : \"HAL_DATASPACE_DISPLAY_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 4098,\n     \"name\" : \"HAL_DATASPACE_DYNAMIC_DEPTH\"\n    },\n    {\n     \"enum_field_value\" : 4099,\n     \"name\" : \"HAL_DATASPACE_JPEG_APP_SEGMENTS\"\n    },\n    {\n     \"enum_field_value\" : 4100,\n     \"name\" : \"HAL_DATASPACE_HEIF\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI24android_dataspace_v1_2_t\",\n   \"name\" : \"android_dataspace_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 10,\n     \"name\" : \"HAL_COLOR_MODE_BT2020\"\n    },\n    {\n     \"enum_field_value\" : 11,\n     \"name\" : \"HAL_COLOR_MODE_BT2100_PQ\"\n    },\n    {\n     \"enum_field_value\" : 12,\n     \"name\" : \"HAL_COLOR_MODE_BT2100_HLG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_color_mode_v1_1_t\",\n   \"name\" : \"android_color_mode_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 13,\n     \"name\" : \"HAL_COLOR_MODE_DISPLAY_BT2020\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_color_mode_v1_2_t\",\n   \"name\" : \"android_color_mode_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_COLOR_TRANSFORM_IDENTITY\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_COLOR_TRANSFORM_ARBITRARY_MATRIX\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_COLOR_TRANSFORM_VALUE_INVERSE\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_COLOR_TRANSFORM_GRAYSCALE\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"HAL_COLOR_TRANSFORM_CORRECT_PROTANOPIA\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"HAL_COLOR_TRANSFORM_CORRECT_DEUTERANOPIA\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"HAL_COLOR_TRANSFORM_CORRECT_TRITANOPIA\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_color_transform_t\",\n   \"name\" : \"android_color_transform_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.0.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 39,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_422_888\"\n    },\n    {\n     \"enum_field_value\" : 40,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_444_888\"\n    },\n    {\n     \"enum_field_value\" : 41,\n     \"name\" : \"HAL_PIXEL_FORMAT_FLEX_RGB_888\"\n    },\n    {\n     \"enum_field_value\" : 42,\n     \"name\" : \"HAL_PIXEL_FORMAT_FLEX_RGBA_8888\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_pixel_format_sw_t\",\n   \"name\" : \"android_pixel_format_sw_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-sw.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 48,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_16\"\n    },\n    {\n     \"enum_field_value\" : 49,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_24\"\n    },\n    {\n     \"enum_field_value\" : 50,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_24_STENCIL_8\"\n    },\n    {\n     \"enum_field_value\" : 51,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_32F\"\n    },\n    {\n     \"enum_field_value\" : 52,\n     \"name\" : \"HAL_PIXEL_FORMAT_DEPTH_32F_STENCIL_8\"\n    },\n    {\n     \"enum_field_value\" : 53,\n     \"name\" : \"HAL_PIXEL_FORMAT_STENCIL_8\"\n    },\n    {\n     \"enum_field_value\" : 54,\n     \"name\" : \"HAL_PIXEL_FORMAT_YCBCR_P010\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI27android_pixel_format_v1_1_t\",\n   \"name\" : \"android_pixel_format_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 55,\n     \"name\" : \"HAL_PIXEL_FORMAT_HSV_888\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI27android_pixel_format_v1_2_t\",\n   \"name\" : \"android_pixel_format_v1_2_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.2.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"HAL_RENDER_INTENT_COLORIMETRIC\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"HAL_RENDER_INTENT_ENHANCE\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"HAL_RENDER_INTENT_TONE_MAP_COLORIMETRIC\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"HAL_RENDER_INTENT_TONE_MAP_ENHANCE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI28android_render_intent_v1_1_t\",\n   \"name\" : \"android_render_intent_v1_1_t\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics-base-v1.1.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"LOG_ID_MIN\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"LOG_ID_MAIN\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"LOG_ID_RADIO\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"LOG_ID_EVENTS\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"LOG_ID_SYSTEM\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"LOG_ID_CRASH\"\n    },\n    {\n     \"enum_field_value\" : 5,\n     \"name\" : \"LOG_ID_STATS\"\n    },\n    {\n     \"enum_field_value\" : 6,\n     \"name\" : \"LOG_ID_SECURITY\"\n    },\n    {\n     \"enum_field_value\" : 7,\n     \"name\" : \"LOG_ID_KERNEL\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"LOG_ID_MAX\"\n    },\n    {\n     \"enum_field_value\" : 2147483647,\n     \"name\" : \"LOG_ID_DEFAULT\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI6log_id\",\n   \"name\" : \"log_id\",\n   \"size\" : 4,\n   \"source_file\" : \"system/logging/liblog/include_vndk/android/log.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_CTOR\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_DTOR\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_COPY\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE\",\n   \"name\" : \"android::VectorImpl::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_CTOR\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_DTOR\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::VectorImpl::HAS_TRIVIAL_COPY\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE\",\n   \"name\" : \"android::VectorImpl::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android10VectorImpl17$HAS_TRIVIAL_COPYE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_pointer<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_pointer<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_pointer<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_pointer<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_pointer<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_pointer<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::OK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::NO_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -2147483648,\n     \"name\" : \"android::UNKNOWN_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -12,\n     \"name\" : \"android::NO_MEMORY\"\n    },\n    {\n     \"enum_field_value\" : -38,\n     \"name\" : \"android::INVALID_OPERATION\"\n    },\n    {\n     \"enum_field_value\" : -22,\n     \"name\" : \"android::BAD_VALUE\"\n    },\n    {\n     \"enum_field_value\" : -2147483647,\n     \"name\" : \"android::BAD_TYPE\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::NAME_NOT_FOUND\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::PERMISSION_DENIED\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"android::NO_INIT\"\n    },\n    {\n     \"enum_field_value\" : -17,\n     \"name\" : \"android::ALREADY_EXISTS\"\n    },\n    {\n     \"enum_field_value\" : -32,\n     \"name\" : \"android::DEAD_OBJECT\"\n    },\n    {\n     \"enum_field_value\" : -2147483646,\n     \"name\" : \"android::FAILED_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -75,\n     \"name\" : \"android::BAD_INDEX\"\n    },\n    {\n     \"enum_field_value\" : -61,\n     \"name\" : \"android::NOT_ENOUGH_DATA\"\n    },\n    {\n     \"enum_field_value\" : -11,\n     \"name\" : \"android::WOULD_BLOCK\"\n    },\n    {\n     \"enum_field_value\" : -110,\n     \"name\" : \"android::TIMED_OUT\"\n    },\n    {\n     \"enum_field_value\" : -74,\n     \"name\" : \"android::UNKNOWN_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -2147483641,\n     \"name\" : \"android::FDS_NOT_ALLOWED\"\n    },\n    {\n     \"enum_field_value\" : -2147483640,\n     \"name\" : \"android::UNEXPECTED_NULL\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android15$ALREADY_EXISTSE\",\n   \"name\" : \"android::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Errors.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::OK\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::NO_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -2147483648,\n     \"name\" : \"android::UNKNOWN_ERROR\"\n    },\n    {\n     \"enum_field_value\" : -12,\n     \"name\" : \"android::NO_MEMORY\"\n    },\n    {\n     \"enum_field_value\" : -38,\n     \"name\" : \"android::INVALID_OPERATION\"\n    },\n    {\n     \"enum_field_value\" : -22,\n     \"name\" : \"android::BAD_VALUE\"\n    },\n    {\n     \"enum_field_value\" : -2147483647,\n     \"name\" : \"android::BAD_TYPE\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::NAME_NOT_FOUND\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::PERMISSION_DENIED\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"android::NO_INIT\"\n    },\n    {\n     \"enum_field_value\" : -17,\n     \"name\" : \"android::ALREADY_EXISTS\"\n    },\n    {\n     \"enum_field_value\" : -32,\n     \"name\" : \"android::DEAD_OBJECT\"\n    },\n    {\n     \"enum_field_value\" : -2147483646,\n     \"name\" : \"android::FAILED_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -75,\n     \"name\" : \"android::BAD_INDEX\"\n    },\n    {\n     \"enum_field_value\" : -61,\n     \"name\" : \"android::NOT_ENOUGH_DATA\"\n    },\n    {\n     \"enum_field_value\" : -11,\n     \"name\" : \"android::WOULD_BLOCK\"\n    },\n    {\n     \"enum_field_value\" : -110,\n     \"name\" : \"android::TIMED_OUT\"\n    },\n    {\n     \"enum_field_value\" : -74,\n     \"name\" : \"android::UNKNOWN_TRANSACTION\"\n    },\n    {\n     \"enum_field_value\" : -2147483641,\n     \"name\" : \"android::FDS_NOT_ALLOWED\"\n    },\n    {\n     \"enum_field_value\" : -2147483640,\n     \"name\" : \"android::UNEXPECTED_NULL\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android15$ALREADY_EXISTSE\",\n   \"name\" : \"android::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android15$ALREADY_EXISTSE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/Errors.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Errors.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 19,\n     \"name\" : \"android::PRIORITY_LOWEST\"\n    },\n    {\n     \"enum_field_value\" : 10,\n     \"name\" : \"android::PRIORITY_BACKGROUND\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::PRIORITY_NORMAL\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::PRIORITY_FOREGROUND\"\n    },\n    {\n     \"enum_field_value\" : -4,\n     \"name\" : \"android::PRIORITY_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -8,\n     \"name\" : \"android::PRIORITY_URGENT_DISPLAY\"\n    },\n    {\n     \"enum_field_value\" : -16,\n     \"name\" : \"android::PRIORITY_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -19,\n     \"name\" : \"android::PRIORITY_URGENT_AUDIO\"\n    },\n    {\n     \"enum_field_value\" : -20,\n     \"name\" : \"android::PRIORITY_HIGHEST\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::PRIORITY_DEFAULT\"\n    },\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::PRIORITY_MORE_FAVORABLE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::PRIORITY_LESS_FAVORABLE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android15$PRIORITY_AUDIOE\",\n   \"name\" : \"android::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/ThreadDefs.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_copy<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_copy<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_copy<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_copy<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_ctor<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_ctor<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_ctor<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_ctor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_dtor<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_dtor<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_dtor<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_dtor<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_move<android::sysprop_change_callback_info>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_move<android::Looper::MessageEnvelope>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::trait_trivial_move<android::Looper::Response>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<android::String8>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<android::String8>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<android::String16>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_8String16EE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<android::String16>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<bool>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<bool>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<bool>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIbE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIcE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<double>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<double>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<double>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIdE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<float>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<float>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<float>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIfE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned char>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIhE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIiE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned int>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIjE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIlE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveImE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIsE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned short>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveItE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<void>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<void>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<void>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIvE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIxE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::trait_trivial_move<unsigned long long>::value\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyE6$valueE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIyE6$valueE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::Mutex::PRIVATE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Mutex::SHARED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android5Mutex8$PRIVATEE\",\n   \"name\" : \"android::Mutex::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Looper::EVENT_INPUT\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::Looper::EVENT_OUTPUT\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::Looper::EVENT_ERROR\"\n    },\n    {\n     \"enum_field_value\" : 8,\n     \"name\" : \"android::Looper::EVENT_HANGUP\"\n    },\n    {\n     \"enum_field_value\" : 16,\n     \"name\" : \"android::Looper::EVENT_INVALID\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper12$EVENT_ERRORE\",\n   \"name\" : \"android::Looper::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : -1,\n     \"name\" : \"android::Looper::POLL_WAKE\"\n    },\n    {\n     \"enum_field_value\" : -2,\n     \"name\" : \"android::Looper::POLL_CALLBACK\"\n    },\n    {\n     \"enum_field_value\" : -3,\n     \"name\" : \"android::Looper::POLL_TIMEOUT\"\n    },\n    {\n     \"enum_field_value\" : -4,\n     \"name\" : \"android::Looper::POLL_ERROR\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper14$POLL_CALLBACKE\",\n   \"name\" : \"android::Looper::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"underlying_type\" : \"_ZTIi\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Looper::PREPARE_ALLOW_NON_CALLBACKS\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper28$PREPARE_ALLOW_NON_CALLBACKSE\",\n   \"name\" : \"android::Looper::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::RWLock::PRIVATE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RWLock::SHARED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLock8$PRIVATEE\",\n   \"name\" : \"android::RWLock::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::is_pointer\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_ctor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_dtor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_copy\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::sysprop_change_callback_info>::has_trivial_move\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_28sysprop_change_callback_infoEE17$has_trivial_copyE\",\n   \"name\" : \"android::traits<android::sysprop_change_callback_info>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::is_pointer\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_ctor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_dtor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_copy\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::MessageEnvelope>::has_trivial_move\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEE17$has_trivial_copyE\",\n   \"name\" : \"android::traits<android::Looper::MessageEnvelope>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::is_pointer\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_ctor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_dtor\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_copy\"\n    },\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::traits<android::Looper::Response>::has_trivial_move\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper8ResponseEE17$has_trivial_copyE\",\n   \"name\" : \"android::traits<android::Looper::Response>::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::FileMap::NORMAL\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::FileMap::RANDOM\"\n    },\n    {\n     \"enum_field_value\" : 2,\n     \"name\" : \"android::FileMap::SEQUENTIAL\"\n    },\n    {\n     \"enum_field_value\" : 3,\n     \"name\" : \"android::FileMap::WILLNEED\"\n    },\n    {\n     \"enum_field_value\" : 4,\n     \"name\" : \"android::FileMap::DONTNEED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7FileMap9MapAdviceE\",\n   \"name\" : \"android::FileMap::MapAdvice\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::FIRST_INC_STRONG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase17$FIRST_INC_STRONGE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::FIRST_INC_STRONG\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase17$FIRST_INC_STRONGE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android7RefBase17$FIRST_INC_STRONGE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_STRONG\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_WEAK\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_MASK\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_STRONG\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_WEAK\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::RefBase::OBJECT_LIFETIME_MASK\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE\",\n   \"name\" : \"android::RefBase::(unnamed)\",\n   \"self_type\" : \"_ZTIN7android7RefBase21$OBJECT_LIFETIME_MASKE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::Condition::WAKE_UP_ONE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Condition::WAKE_UP_ALL\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9Condition10WakeUpTypeE\",\n   \"name\" : \"android::Condition::WakeUpType\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 0,\n     \"name\" : \"android::Condition::PRIVATE\"\n    },\n    {\n     \"enum_field_value\" : 1,\n     \"name\" : \"android::Condition::SHARED\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9Condition8$PRIVATEE\",\n   \"name\" : \"android::Condition::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 4,\n   \"enum_fields\" :\n   [\n    {\n     \"enum_field_value\" : 20,\n     \"name\" : \"android::FdPrinter::MAX_FORMAT_STRING\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9FdPrinter18$MAX_FORMAT_STRINGE\",\n   \"name\" : \"android::FdPrinter::(unnamed)\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"underlying_type\" : \"_ZTIj\"\n  }\n ],\n \"function_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPFiPvES_PKcijPS_E\",\n   \"name\" : \"int (int (*)(void *), void *, const char *, int, unsigned int, void **)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPKvS0_E\",\n   \"name\" : \"int (const void *, const void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPKvS0_PvE\",\n   \"name\" : \"int (const void *, const void *, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiPvE\",\n   \"name\" : \"int (void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFiiiPvE\",\n   \"name\" : \"int (int, int, void *)\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIFvvE\",\n   \"name\" : \"void ()\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  }\n ],\n \"functions\" :\n [\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::LogPrinter::printRaw\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinter8printRawEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::LogPrinter::printLine\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinter9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::LogPrinter::LogPrinter\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinterC1EPKc19android_LogPriorityS2_b\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTI19android_LogPriority\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::LogPrinter::LogPrinter\",\n   \"linker_set_key\" : \"_ZN7android10LogPrinterC2EPKc19android_LogPriorityS2_b\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10LogPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTI19android_LogPriority\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::appendArray\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl11appendArrayEPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::setCapacity\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl11setCapacityEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::appendVector\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl12appendVectorERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::editArrayImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13editArrayImplEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::finish_vector\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13finish_vectorEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertArrayAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13insertArrayAtEPKvjj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::removeItemsAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl13removeItemsAtEjj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertVectorAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl14insertVectorAtERKS0_j\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::VectorImpl::release_storage\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl15release_storageEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::editItemLocation\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl16editItemLocationEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::add\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl3addEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::add\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl3addEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::pop\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl3popEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::push\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4pushEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::push\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4pushEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::sort\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiPKvS0_E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::sort\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl4sortEPFiPKvS2_PvES3_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiPKvS0_PvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::VectorImpl::_grow\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl5_growEjj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::clear\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl5clearEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::resize\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl6resizeEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::VectorImpl::_shrink\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl7_shrinkEjj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl8insertAtEPKvjj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::insertAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl8insertAtEjj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::replaceAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl9replaceAtEPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::replaceAt\",\n   \"linker_set_key\" : \"_ZN7android10VectorImpl9replaceAtEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplC2ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplC2Ejj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::~VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::~VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::~VectorImpl\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::operator=\",\n   \"linker_set_key\" : \"_ZN7android10VectorImplaSERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android10VectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android10VectorImplE\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::uptimeNanos\",\n   \"linker_set_key\" : \"_ZN7android11uptimeNanosEv\",\n   \"return_type\" : \"_ZTIx\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::NativeHandle::create\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandle6createEP13native_handleb\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleC1EP13native_handleb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleC2EP13native_handleb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::~NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::NativeHandle::~NativeHandle\",\n   \"linker_set_key\" : \"_ZN7android12NativeHandleD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"function_name\" : \"android::uptimeMillis\",\n   \"linker_set_key\" : \"_ZN7android12uptimeMillisEv\",\n   \"return_type\" : \"_ZTIx\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::PrefixPrinter::printLine\",\n   \"linker_set_key\" : \"_ZN7android13PrefixPrinter9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android13PrefixPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::PrefixPrinter::PrefixPrinter\",\n   \"linker_set_key\" : \"_ZN7android13PrefixPrinterC1ERNS_7PrinterEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android13PrefixPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRN7android7PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::PrefixPrinter::PrefixPrinter\",\n   \"linker_set_key\" : \"_ZN7android13PrefixPrinterC2ERNS_7PrinterEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android13PrefixPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRN7android7PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::LooperCallback::~LooperCallback\",\n   \"linker_set_key\" : \"_ZN7android14LooperCallbackD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::LooperCallback::~LooperCallback\",\n   \"linker_set_key\" : \"_ZN7android14LooperCallbackD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::LooperCallback::~LooperCallback\",\n   \"linker_set_key\" : \"_ZN7android14LooperCallbackD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::MessageHandler::~MessageHandler\",\n   \"linker_set_key\" : \"_ZN7android14MessageHandlerD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::MessageHandler::~MessageHandler\",\n   \"linker_set_key\" : \"_ZN7android14MessageHandlerD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::MessageHandler::~MessageHandler\",\n   \"linker_set_key\" : \"_ZN7android14MessageHandlerD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::String8Printer::printLine\",\n   \"linker_set_key\" : \"_ZN7android14String8Printer9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14String8PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::String8Printer::String8Printer\",\n   \"linker_set_key\" : \"_ZN7android14String8PrinterC1EPNS_7String8EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14String8PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7String8E\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::String8Printer::String8Printer\",\n   \"linker_set_key\" : \"_ZN7android14String8PrinterC2EPNS_7String8EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android14String8PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7String8E\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::statusToString\",\n   \"linker_set_key\" : \"_ZN7android14statusToStringEi\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTINSt3__112basic_stringIcNS_11char_traitsIcEENS_9allocatorIcEEEE\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Errors.h\"\n  },\n  {\n   \"function_name\" : \"android::elapsedRealtime\",\n   \"linker_set_key\" : \"_ZN7android15elapsedRealtimeEv\",\n   \"return_type\" : \"_ZTIx\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::add\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl3addEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::merge\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl5mergeERKNS_10VectorImplE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::merge\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl5mergeERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::remove\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImpl6removeEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplC2ERKNS_10VectorImplE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android10VectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplC2Ejj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::~SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::~SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::~SortedVectorImpl\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::operator=\",\n   \"linker_set_key\" : \"_ZN7android16SortedVectorImplaSERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android16SortedVectorImplE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android16SortedVectorImplE\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::JenkinsHashWhiten\",\n   \"linker_set_key\" : \"_ZN7android17JenkinsHashWhitenEj\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"function_name\" : \"android::WeakMessageHandler::handleMessage\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::WeakMessageHandler::WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerC1ERKNS_2wpINS_14MessageHandlerEEE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2wpINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::WeakMessageHandler::WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerC2ERKNS_2wpINS_14MessageHandlerEEE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2wpINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::WeakMessageHandler::~WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::WeakMessageHandler::~WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::WeakMessageHandler::~WeakMessageHandler\",\n   \"linker_set_key\" : \"_ZN7android18WeakMessageHandlerD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android18WeakMessageHandlerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::JenkinsHashMixBytes\",\n   \"linker_set_key\" : \"_ZN7android19JenkinsHashMixBytesEjPKhj\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"function_name\" : \"android::elapsedRealtimeNano\",\n   \"linker_set_key\" : \"_ZN7android19elapsedRealtimeNanoEv\",\n   \"return_type\" : \"_ZTIx\",\n   \"source_file\" : \"system/core/libutils/include/utils/SystemClock.h\"\n  },\n  {\n   \"function_name\" : \"android::JenkinsHashMixShorts\",\n   \"linker_set_key\" : \"_ZN7android20JenkinsHashMixShortsEjPKtj\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKt\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"function_name\" : \"android::SimpleLooperCallback::handleEvent\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallback11handleEventEiiPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::SimpleLooperCallback::SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackC1EPFiiiPvE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::SimpleLooperCallback::SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackC2EPFiiiPvE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::SimpleLooperCallback::~SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::SimpleLooperCallback::~SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::SimpleLooperCallback::~SimpleLooperCallback\",\n   \"linker_set_key\" : \"_ZN7android20SimpleLooperCallbackD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::report_sysprop_change\",\n   \"linker_set_key\" : \"_ZN7android21report_sysprop_changeEv\",\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  },\n  {\n   \"function_name\" : \"android::add_sysprop_change_callback\",\n   \"linker_set_key\" : \"_ZN7android27add_sysprop_change_callbackEPFvvEi\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFvvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  },\n  {\n   \"function_name\" : \"android::sp<android::LooperCallback>::clear\",\n   \"linker_set_key\" : \"_ZN7android2spINS_14LooperCallbackEE5clearEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android2spINS_14LooperCallbackEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"function_name\" : \"android::sp<android::LooperCallback>::operator=\",\n   \"linker_set_key\" : \"_ZN7android2spINS_14LooperCallbackEEaSERKS2_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android2spINS_14LooperCallbackEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14LooperCallbackEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android2spINS_14LooperCallbackEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"function_name\" : \"android::sp<android::Looper>::~sp\",\n   \"linker_set_key\" : \"_ZN7android2spINS_6LooperEED2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android2spINS_6LooperEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"function_name\" : \"android::sp<android::Looper>::operator=\",\n   \"linker_set_key\" : \"_ZN7android2spINS_6LooperEEaSEOS2_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android2spINS_6LooperEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android2spINS_6LooperEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android2spINS_6LooperEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"function_name\" : \"android::sp<android::Looper>::operator=\",\n   \"linker_set_key\" : \"_ZN7android2spINS_6LooperEEaSERKS2_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android2spINS_6LooperEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_6LooperEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android2spINS_6LooperEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"function_name\" : \"android::sp<android::Thread>::clear\",\n   \"linker_set_key\" : \"_ZN7android2spINS_6ThreadEE5clearEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android2spINS_6ThreadEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"function_name\" : \"android::sp<android::Thread>::operator=\",\n   \"linker_set_key\" : \"_ZN7android2spINS_6ThreadEEaSEOS2_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android2spINS_6ThreadEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android2spINS_6ThreadEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android2spINS_6ThreadEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"function_name\" : \"android::LightRefBase_reportIncStrongRequireStrongFailed\",\n   \"linker_set_key\" : \"_ZN7android47LightRefBase_reportIncStrongRequireStrongFailedEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::sendMessage\",\n   \"linker_set_key\" : \"_ZN7android6Looper11sendMessageERKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::getForThread\",\n   \"linker_set_key\" : \"_ZN7android6Looper12getForThreadEv\",\n   \"return_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::setForThread\",\n   \"linker_set_key\" : \"_ZN7android6Looper12setForThreadERKNS_2spIS0_EE\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_6LooperEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::removeMessages\",\n   \"linker_set_key\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::removeMessages\",\n   \"linker_set_key\" : \"_ZN7android6Looper14removeMessagesERKNS_2spINS_14MessageHandlerEEEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::getFdStateDebug\",\n   \"linker_set_key\" : \"_ZN7android6Looper15getFdStateDebugEiPiS1_PNS_2spINS_14LooperCallbackEEEPPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android2spINS_14LooperCallbackEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::sendMessageAtTime\",\n   \"linker_set_key\" : \"_ZN7android6Looper17sendMessageAtTimeExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIx\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::rebuildEpollLocked\",\n   \"linker_set_key\" : \"_ZN7android6Looper18rebuildEpollLockedEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::sendMessageDelayed\",\n   \"linker_set_key\" : \"_ZN7android6Looper18sendMessageDelayedExRKNS_2spINS_14MessageHandlerEEERKNS_7MessageE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIx\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7MessageE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::removeSequenceNumberLocked\",\n   \"linker_set_key\" : \"_ZN7android6Looper26removeSequenceNumberLockedEy\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIy\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::scheduleEpollRebuildLocked\",\n   \"linker_set_key\" : \"_ZN7android6Looper26scheduleEpollRebuildLockedEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::wake\",\n   \"linker_set_key\" : \"_ZN7android6Looper4wakeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::addFd\",\n   \"linker_set_key\" : \"_ZN7android6Looper5addFdEiiiPFiiiPvES1_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::addFd\",\n   \"linker_set_key\" : \"_ZN7android6Looper5addFdEiiiRKNS_2spINS_14LooperCallbackEEEPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android2spINS_14LooperCallbackEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::awoken\",\n   \"linker_set_key\" : \"_ZN7android6Looper6awokenEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::repoll\",\n   \"linker_set_key\" : \"_ZN7android6Looper6repollEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::pollAll\",\n   \"linker_set_key\" : \"_ZN7android6Looper7pollAllEiPiS1_PPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::prepare\",\n   \"linker_set_key\" : \"_ZN7android6Looper7prepareEi\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::pollOnce\",\n   \"linker_set_key\" : \"_ZN7android6Looper8pollOnceEiPiS1_PPv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::removeFd\",\n   \"linker_set_key\" : \"_ZN7android6Looper8removeFdEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Looper::pollInner\",\n   \"linker_set_key\" : \"_ZN7android6Looper9pollInnerEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperC1Eb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperC2Eb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Looper::~Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Looper::~Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Looper::~Looper\",\n   \"linker_set_key\" : \"_ZN7android6LooperD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::readyToRun\",\n   \"linker_set_key\" : \"_ZN7android6Thread10readyToRunEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Thread::_threadLoop\",\n   \"linker_set_key\" : \"_ZN7android6Thread11_threadLoopEPv\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::requestExit\",\n   \"linker_set_key\" : \"_ZN7android6Thread11requestExitEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::requestExitAndWait\",\n   \"linker_set_key\" : \"_ZN7android6Thread18requestExitAndWaitEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::run\",\n   \"linker_set_key\" : \"_ZN7android6Thread3runEPKcij\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::join\",\n   \"linker_set_key\" : \"_ZN7android6Thread4joinEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadC2Eb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::~Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::~Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::~Thread\",\n   \"linker_set_key\" : \"_ZN7android6ThreadD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::advise\",\n   \"linker_set_key\" : \"_ZN7android7FileMap6adviseENS0_9MapAdviceE\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIN7android7FileMap9MapAdviceE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::create\",\n   \"linker_set_key\" : \"_ZN7android7FileMap6createEPKcixjb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIx\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC1EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC2EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapC2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::~FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::~FileMap\",\n   \"linker_set_key\" : \"_ZN7android7FileMapD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::FileMap::operator=\",\n   \"linker_set_key\" : \"_ZN7android7FileMapaSEOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android7FileMapE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android7FileMapE\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"function_name\" : \"android::Printer::printFormatLine\",\n   \"linker_set_key\" : \"_ZN7android7Printer15printFormatLineEPKcz\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterC2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::~Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::~Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Printer::~Printer\",\n   \"linker_set_key\" : \"_ZN7android7PrinterD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7PrinterE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onFirstRef\",\n   \"linker_set_key\" : \"_ZN7android7RefBase10onFirstRefEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::RefBase::renameRefs\",\n   \"linker_set_key\" : \"_ZN7android7RefBase10renameRefsEjRKNS_16ReferenceRenamerE\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android16ReferenceRenamerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::RefBase::renameRefId\",\n   \"linker_set_key\" : \"_ZN7android7RefBase11renameRefIdEPNS0_12weakref_typeEPKvS4_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::RefBase::renameRefId\",\n   \"linker_set_key\" : \"_ZN7android7RefBase11renameRefIdEPS0_PKvS3_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::attemptIncWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type14attemptIncWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::attemptIncStrong\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type16attemptIncStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::incWeakRequireWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type18incWeakRequireWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::decWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type7decWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::incWeak\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type7incWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::trackMe\",\n   \"linker_set_key\" : \"_ZN7android7RefBase12weakref_type7trackMeEbb\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onLastWeakRef\",\n   \"linker_set_key\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onLastStrongRef\",\n   \"linker_set_key\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::extendObjectLifetime\",\n   \"linker_set_key\" : \"_ZN7android7RefBase20extendObjectLifetimeEi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::onIncStrongAttempted\",\n   \"linker_set_key\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseC1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseC2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::~RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseD0Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::~RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::RefBase::~RefBase\",\n   \"linker_set_key\" : \"_ZN7android7RefBaseD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::lockBuffer\",\n   \"linker_set_key\" : \"_ZN7android7String810lockBufferEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPc\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String8::real_append\",\n   \"linker_set_key\" : \"_ZN7android7String811real_appendEPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::appendFormat\",\n   \"linker_set_key\" : \"_ZN7android7String812appendFormatEPKcz\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::unlockBuffer\",\n   \"linker_set_key\" : \"_ZN7android7String812unlockBufferEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::unlockBuffer\",\n   \"linker_set_key\" : \"_ZN7android7String812unlockBufferEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::appendFormatV\",\n   \"linker_set_key\" : \"_ZN7android7String813appendFormatVEPKcSt9__va_list\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTISt9__va_list\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::clear\",\n   \"linker_set_key\" : \"_ZN7android7String85clearEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKDij\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToEPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::setTo\",\n   \"linker_set_key\" : \"_ZN7android7String85setToERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::append\",\n   \"linker_set_key\" : \"_ZN7android7String86appendEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::append\",\n   \"linker_set_key\" : \"_ZN7android7String86appendEPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::append\",\n   \"linker_set_key\" : \"_ZN7android7String86appendERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::format\",\n   \"linker_set_key\" : \"_ZN7android7String86formatEPKcz\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::formatV\",\n   \"linker_set_key\" : \"_ZN7android7String87formatVEPKcSt9__va_list\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTISt9__va_list\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::toLower\",\n   \"linker_set_key\" : \"_ZN7android7String87toLowerEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::removeAll\",\n   \"linker_set_key\" : \"_ZN7android7String89removeAllEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDij\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1EPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1ERKNS_8String16E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDi\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDij\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2EPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2ERKNS_8String16E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::String8\",\n   \"linker_set_key\" : \"_ZN7android7String8C2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::~String8\",\n   \"linker_set_key\" : \"_ZN7android7String8D1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::~String8\",\n   \"linker_set_key\" : \"_ZN7android7String8D2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::editResize\",\n   \"linker_set_key\" : \"_ZN7android8String1610editResizeEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::replaceAll\",\n   \"linker_set_key\" : \"_ZN7android8String1610replaceAllEDsDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::allocFromUTF8\",\n   \"linker_set_key\" : \"_ZN7android8String1613allocFromUTF8EPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::allocFromUTF16\",\n   \"linker_set_key\" : \"_ZN7android8String1614allocFromUTF16EPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::edit\",\n   \"linker_set_key\" : \"_ZN7android8String164editEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::alloc\",\n   \"linker_set_key\" : \"_ZN7android8String165allocEj\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToEPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToEPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::setTo\",\n   \"linker_set_key\" : \"_ZN7android8String165setToERKS0_jj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::append\",\n   \"linker_set_key\" : \"_ZN7android8String166appendEPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::append\",\n   \"linker_set_key\" : \"_ZN7android8String166appendERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::insert\",\n   \"linker_set_key\" : \"_ZN7android8String166insertEjPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::insert\",\n   \"linker_set_key\" : \"_ZN7android8String166insertEjPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::acquire\",\n   \"linker_set_key\" : \"_ZN7android8String167acquireEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::release\",\n   \"linker_set_key\" : \"_ZN7android8String167releaseEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1EPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1ERKNS_7String8E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1ERKS0_jj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKDsj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2EPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2ERKNS_7String8E\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2ERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2ERKS0_jj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::String16\",\n   \"linker_set_key\" : \"_ZN7android8String16C2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::~String16\",\n   \"linker_set_key\" : \"_ZN7android8String16D1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::~String16\",\n   \"linker_set_key\" : \"_ZN7android8String16D2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::operator=\",\n   \"linker_set_key\" : \"_ZN7android8String16aSEOS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTION7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIRN7android8String16E\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::FdPrinter::printLine\",\n   \"linker_set_key\" : \"_ZN7android9FdPrinter9printLineEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9FdPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::FdPrinter::FdPrinter\",\n   \"linker_set_key\" : \"_ZN7android9FdPrinterC1EijPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9FdPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::FdPrinter::FdPrinter\",\n   \"linker_set_key\" : \"_ZN7android9FdPrinterC2EijPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9FdPrinterE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::reset\",\n   \"linker_set_key\" : \"_ZN7android9StopWatch5resetEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchC1EPKci\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchC2EPKci\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::~StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::~StopWatch\",\n   \"linker_set_key\" : \"_ZN7android9StopWatchD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::fromContents\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer12fromContentsERKNS_7String8EPKcPPS0_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::skipDelimiters\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer14skipDelimitersEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::open\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer4openERKNS_7String8EPPS0_\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::nextLine\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer8nextLineEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::nextToken\",\n   \"linker_set_key\" : \"_ZN7android9Tokenizer9nextTokenEPKc\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Tokenizer::Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerC1ERKNS_7String8EPNS_7FileMapEPcbj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::Tokenizer::Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerC2ERKNS_7String8EPNS_7FileMapEPcbj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android7String8E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::~Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerD1Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::~Tokenizer\",\n   \"linker_set_key\" : \"_ZN7android9TokenizerD2Ev\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::itemLocation\",\n   \"linker_set_key\" : \"_ZNK7android10VectorImpl12itemLocationEj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPKv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::VectorImpl::capacity\",\n   \"linker_set_key\" : \"_ZNK7android10VectorImpl8capacityEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::VectorImpl::itemSize\",\n   \"linker_set_key\" : \"_ZNK7android10VectorImpl8itemSizeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::SortedVectorImpl::_indexOrderOf\",\n   \"linker_set_key\" : \"_ZNK7android16SortedVectorImpl13_indexOrderOfEPKvPj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::indexOf\",\n   \"linker_set_key\" : \"_ZNK7android16SortedVectorImpl7indexOfEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::SortedVectorImpl::orderOf\",\n   \"linker_set_key\" : \"_ZNK7android16SortedVectorImpl7orderOfEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android16SortedVectorImplE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::getAllowNonCallbacks\",\n   \"linker_set_key\" : \"_ZNK7android6Looper20getAllowNonCallbacksEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::Request::getEpollEvents\",\n   \"linker_set_key\" : \"_ZNK7android6Looper7Request14getEpollEventsEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6Looper7RequestE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"function_name\" : \"android::Looper::isPolling\",\n   \"linker_set_key\" : \"_ZNK7android6Looper9isPollingEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6LooperE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Thread::exitPending\",\n   \"linker_set_key\" : \"_ZNK7android6Thread11exitPendingEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::getTid\",\n   \"linker_set_key\" : \"_ZNK7android6Thread6getTidEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"function_name\" : \"android::Thread::isRunning\",\n   \"linker_set_key\" : \"_ZNK7android6Thread9isRunningEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6ThreadE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::sysprop_change_callback_info>::do_destroy\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::sysprop_change_callback_info>::do_construct\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::sysprop_change_callback_info>::do_move_forward\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::sysprop_change_callback_info>::do_move_backward\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::sysprop_change_callback_info>::do_copy\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::sysprop_change_callback_info>::do_splat\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::MessageEnvelope>::do_destroy\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::MessageEnvelope>::do_construct\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::MessageEnvelope>::do_move_forward\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::MessageEnvelope>::do_move_backward\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::MessageEnvelope>::do_copy\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::MessageEnvelope>::do_splat\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::Response>::do_destroy\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::Response>::do_construct\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::Response>::do_move_forward\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::Response>::do_move_backward\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::Response>::do_copy\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"access\" : \"protected\",\n   \"function_name\" : \"android::Vector<android::Looper::Response>::do_splat\",\n   \"linker_set_key\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::createWeak\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase10createWeakEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::getWeakRefs\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase11getWeakRefsEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::getWeakCount\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase12weakref_type12getWeakCountEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::refBase\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase12weakref_type7refBaseEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::weakref_type::printRefs\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase12weakref_type9printRefsEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::forceIncStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase14forceIncStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::getStrongCount\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase14getStrongCountEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::incStrongRequireStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase22incStrongRequireStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::decStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase9decStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"function_name\" : \"android::RefBase::incStrong\",\n   \"linker_set_key\" : \"_ZNK7android7RefBase9incStrongEPKv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String8::getPathDir\",\n   \"linker_set_key\" : \"_ZNK7android7String810getPathDirEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String8::getPathExtension\",\n   \"linker_set_key\" : \"_ZNK7android7String816getPathExtensionEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::find\",\n   \"linker_set_key\" : \"_ZNK7android7String84findEPKcj\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String8::length\",\n   \"linker_set_key\" : \"_ZNK7android7String86lengthEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::startsWith\",\n   \"linker_set_key\" : \"_ZNK7android8String1610startsWithEPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::startsWith\",\n   \"linker_set_key\" : \"_ZNK7android8String1610startsWithERKS0_\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIRKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::isStaticString\",\n   \"linker_set_key\" : \"_ZNK7android8String1614isStaticStringEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"function_name\" : \"android::String16::staticStringSize\",\n   \"linker_set_key\" : \"_ZNK7android8String1616staticStringSizeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::size\",\n   \"linker_set_key\" : \"_ZNK7android8String164sizeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::contains\",\n   \"linker_set_key\" : \"_ZNK7android8String168containsEPKDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIb\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::findLast\",\n   \"linker_set_key\" : \"_ZNK7android8String168findLastEDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::String16::findFirst\",\n   \"linker_set_key\" : \"_ZNK7android8String169findFirstEDs\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android8String16E\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::elapsedTime\",\n   \"linker_set_key\" : \"_ZNK7android9StopWatch11elapsedTimeEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIx\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::StopWatch::name\",\n   \"linker_set_key\" : \"_ZNK7android9StopWatch4nameEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9StopWatchE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPKc\",\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::getLocation\",\n   \"linker_set_key\" : \"_ZNK7android9Tokenizer11getLocationEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"android::Tokenizer::peekRemainderOfLine\",\n   \"linker_set_key\" : \"_ZNK7android9Tokenizer19peekRemainderOfLineEv\",\n   \"parameters\" :\n   [\n    {\n     \"is_this_ptr\" : true,\n     \"referenced_type\" : \"_ZTIPKN7android9TokenizerE\"\n    }\n   ],\n   \"return_type\" : \"_ZTIN7android7String8E\",\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"function_name\" : \"androidCreateRawThreadEtc\",\n   \"linker_set_key\" : \"androidCreateRawThreadEtc\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidCreateThread\",\n   \"linker_set_key\" : \"androidCreateThread\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidCreateThreadEtc\",\n   \"linker_set_key\" : \"androidCreateThreadEtc\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPvE\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPPv\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidGetThreadId\",\n   \"linker_set_key\" : \"androidGetThreadId\",\n   \"return_type\" : \"_ZTIPv\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidGetThreadPriority\",\n   \"linker_set_key\" : \"androidGetThreadPriority\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidSetCreateThreadFunc\",\n   \"linker_set_key\" : \"androidSetCreateThreadFunc\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPFiPFiPvES_PKcijPS_E\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidSetThreadName\",\n   \"linker_set_key\" : \"androidSetThreadName\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"androidSetThreadPriority\",\n   \"linker_set_key\" : \"androidSetThreadPriority\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"function_name\" : \"strcmp16\",\n   \"linker_set_key\" : \"strcmp16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strlen16\",\n   \"linker_set_key\" : \"strlen16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strncmp16\",\n   \"linker_set_key\" : \"strncmp16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strnlen16\",\n   \"linker_set_key\" : \"strnlen16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIj\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strstr16\",\n   \"linker_set_key\" : \"strstr16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"strzcmp16\",\n   \"linker_set_key\" : \"strzcmp16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"systemTime\",\n   \"linker_set_key\" : \"systemTime\",\n   \"parameters\" :\n   [\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"return_type\" : \"_ZTIx\",\n   \"source_file\" : \"system/core/libutils/include/utils/Timers.h\"\n  },\n  {\n   \"function_name\" : \"toMillisecondTimeoutDelay\",\n   \"linker_set_key\" : \"toMillisecondTimeoutDelay\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIx\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIx\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/include/utils/Timers.h\"\n  },\n  {\n   \"function_name\" : \"utf16_to_utf8\",\n   \"linker_set_key\" : \"utf16_to_utf8\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf16_to_utf8_length\",\n   \"linker_set_key\" : \"utf16_to_utf8_length\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf32_from_utf8_at\",\n   \"linker_set_key\" : \"utf32_from_utf8_at\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf32_to_utf8\",\n   \"linker_set_key\" : \"utf32_to_utf8\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf32_to_utf8_length\",\n   \"linker_set_key\" : \"utf32_to_utf8_length\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKDi\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf8_to_utf16\",\n   \"linker_set_key\" : \"utf8_to_utf16\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf8_to_utf16_length\",\n   \"linker_set_key\" : \"utf8_to_utf16_length\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"default_arg\" : true,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"return_type\" : \"_ZTIi\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"function_name\" : \"utf8_to_utf16_no_null_terminator\",\n   \"linker_set_key\" : \"utf8_to_utf16_no_null_terminator\",\n   \"parameters\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIPKh\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIPDs\"\n    },\n    {\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"return_type\" : \"_ZTIPDs\",\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  }\n ],\n \"global_vars\" :\n [\n  {\n   \"access\" : \"private\",\n   \"linker_set_key\" : \"_ZN7android7FileMap9mPageSizeE\",\n   \"name\" : \"android::FileMap::mPageSize\",\n   \"referenced_type\" : \"_ZTIl\",\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  }\n ],\n \"lvalue_reference_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRA1_KDs\",\n   \"name\" : \"const char16_t (&)[1]\",\n   \"referenced_type\" : \"_ZTIA1_KDs\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl &\",\n   \"referenced_type\" : \"_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android16ReferenceRenamerE\",\n   \"name\" : \"const android::ReferenceRenamer &\",\n   \"referenced_type\" : \"_ZTIKN7android16ReferenceRenamerE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android16SortedVectorImplE\",\n   \"name\" : \"const android::SortedVectorImpl &\",\n   \"referenced_type\" : \"_ZTIKN7android16SortedVectorImplE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"const android::sysprop_change_callback_info &\",\n   \"referenced_type\" : \"_ZTIKN7android28sysprop_change_callback_infoE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::LooperCallback> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::sp<android::MessageHandler> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::SimpleLooperCallback> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_6LooperEEE\",\n   \"name\" : \"const android::sp<android::Looper> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6LooperEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android2spINS_6ThreadEEE\",\n   \"name\" : \"const android::sp<android::Thread> &\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::wp<android::MessageHandler> &\",\n   \"referenced_type\" : \"_ZTIKN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"const android::Looper::MessageEnvelope &\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android6Looper8ResponseE\",\n   \"name\" : \"const android::Looper::Response &\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper8ResponseE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"const android::Vector<android::sysprop_change_callback_info> &\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android7MessageE\",\n   \"name\" : \"const android::Message &\",\n   \"referenced_type\" : \"_ZTIKN7android7MessageE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android7String8E\",\n   \"name\" : \"const android::String8 &\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android7String8E\",\n   \"name\" : \"const android::String8 &\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIRKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android8String1610StaticDataILj1EEE\",\n   \"name\" : \"const android::String16::StaticData<1> &\",\n   \"referenced_type\" : \"_ZTIKN7android8String1610StaticDataILj1EEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKN7android8String16E\",\n   \"name\" : \"const android::String16 &\",\n   \"referenced_type\" : \"_ZTIKN7android8String16E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKa\",\n   \"name\" : \"const signed char &\",\n   \"referenced_type\" : \"_ZTIKa\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKb\",\n   \"name\" : \"const bool &\",\n   \"referenced_type\" : \"_ZTIKb\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKd\",\n   \"name\" : \"const double &\",\n   \"referenced_type\" : \"_ZTIKd\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKf\",\n   \"name\" : \"const float &\",\n   \"referenced_type\" : \"_ZTIKf\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKh\",\n   \"name\" : \"const unsigned char &\",\n   \"referenced_type\" : \"_ZTIKh\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKi\",\n   \"name\" : \"const int &\",\n   \"referenced_type\" : \"_ZTIKi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKj\",\n   \"name\" : \"const unsigned int &\",\n   \"referenced_type\" : \"_ZTIKj\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKs\",\n   \"name\" : \"const short &\",\n   \"referenced_type\" : \"_ZTIKs\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKt\",\n   \"name\" : \"const unsigned short &\",\n   \"referenced_type\" : \"_ZTIKt\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKx\",\n   \"name\" : \"const long long &\",\n   \"referenced_type\" : \"_ZTIKx\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRKy\",\n   \"name\" : \"const unsigned long long &\",\n   \"referenced_type\" : \"_ZTIKy\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl &\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl &\",\n   \"referenced_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"android::sp<android::LooperCallback> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread> &\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android5MutexE\",\n   \"name\" : \"android::Mutex &\",\n   \"referenced_type\" : \"_ZTIN7android5MutexE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android6Looper8ResponseE\",\n   \"name\" : \"android::Looper::Response &\",\n   \"referenced_type\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android6RWLockE\",\n   \"name\" : \"android::RWLock &\",\n   \"referenced_type\" : \"_ZTIN7android6RWLockE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::Vector<android::sysprop_change_callback_info> &\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android7FileMapE\",\n   \"name\" : \"android::FileMap &\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android7PrinterE\",\n   \"name\" : \"android::Printer &\",\n   \"referenced_type\" : \"_ZTIN7android7PrinterE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android7String8E\",\n   \"name\" : \"android::String8 &\",\n   \"referenced_type\" : \"_ZTIN7android7String8E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android7String8E\",\n   \"name\" : \"android::String8 &\",\n   \"referenced_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIRN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRN7android8String16E\",\n   \"name\" : \"android::String16 &\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRP13native_handle\",\n   \"name\" : \"native_handle *&\",\n   \"referenced_type\" : \"_ZTIP13native_handle\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRPFiiiPvE\",\n   \"name\" : \"int (*&)(int, int, void *)\",\n   \"referenced_type\" : \"_ZTIPFiiiPvE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIRb\",\n   \"name\" : \"bool &\",\n   \"referenced_type\" : \"_ZTIb\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  }\n ],\n \"pointer_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP13native_handle\",\n   \"name\" : \"native_handle *\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP18android_flex_plane\",\n   \"name\" : \"android_flex_plane *\",\n   \"referenced_type\" : \"_ZTI18android_flex_plane\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP3DIR\",\n   \"name\" : \"DIR *\",\n   \"referenced_type\" : \"_ZTI3DIR\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP7__sFILE\",\n   \"name\" : \"__sFILE *\",\n   \"referenced_type\" : \"_ZTI7__sFILE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIP7log_msg\",\n   \"name\" : \"log_msg *\",\n   \"referenced_type\" : \"_ZTI7log_msg\",\n   \"size\" : 4,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPDs\",\n   \"name\" : \"char16_t *\",\n   \"referenced_type\" : \"_ZTIDs\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFiPFiPvES_PKcijPS_E\",\n   \"name\" : \"int (*)(int (*)(void *), void *, const char *, int, unsigned int, void **)\",\n   \"referenced_type\" : \"_ZTIFiPFiPvES_PKcijPS_E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFiPKvS0_E\",\n   \"name\" : \"int (*)(const void *, const void *)\",\n   \"referenced_type\" : \"_ZTIFiPKvS0_E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFiPKvS0_PvE\",\n   \"name\" : \"int (*)(const void *, const void *, void *)\",\n   \"referenced_type\" : \"_ZTIFiPKvS0_PvE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFiPvE\",\n   \"name\" : \"int (*)(void *)\",\n   \"referenced_type\" : \"_ZTIFiPvE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFiiiPvE\",\n   \"name\" : \"int (*)(int, int, void *)\",\n   \"referenced_type\" : \"_ZTIFiiiPvE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPFvvE\",\n   \"name\" : \"void (*)()\",\n   \"referenced_type\" : \"_ZTIFvvE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/misc.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPK13native_handle\",\n   \"name\" : \"const native_handle *\",\n   \"referenced_type\" : \"_ZTIK13native_handle\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPK7log_msg\",\n   \"name\" : \"const log_msg *\",\n   \"referenced_type\" : \"_ZTIK7log_msg\",\n   \"size\" : 4,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKDi\",\n   \"name\" : \"const char32_t *\",\n   \"referenced_type\" : \"_ZTIKDi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKDs\",\n   \"name\" : \"const char16_t *\",\n   \"referenced_type\" : \"_ZTIKDs\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl *\",\n   \"referenced_type\" : \"_ZTIKN7android10VectorImplE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl *\",\n   \"referenced_type\" : \"_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"const android::LightRefBase<android::NativeHandle> *\",\n   \"referenced_type\" : \"_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android12NativeHandleE\",\n   \"name\" : \"const android::NativeHandle *\",\n   \"referenced_type\" : \"_ZTIKN7android12NativeHandleE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android16SortedVectorImplE\",\n   \"name\" : \"const android::SortedVectorImpl *\",\n   \"referenced_type\" : \"_ZTIKN7android16SortedVectorImplE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"const android::sysprop_change_callback_info *\",\n   \"referenced_type\" : \"_ZTIKN7android28sysprop_change_callback_infoE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::LooperCallback> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::sp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_6LooperEEE\",\n   \"name\" : \"const android::sp<android::Looper> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6LooperEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android2spINS_6ThreadEEE\",\n   \"name\" : \"const android::sp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIKN7android2spINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::wp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIKN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"const android::wp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIKN7android2wpINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android4base11borrowed_fdE\",\n   \"name\" : \"const android::base::borrowed_fd *\",\n   \"referenced_type\" : \"_ZTIKN7android4base11borrowed_fdE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"const android::base::unique_fd_impl<android::base::DefaultCloser> *\",\n   \"referenced_type\" : \"_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"const android::Looper::MessageEnvelope *\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6Looper7RequestE\",\n   \"name\" : \"const android::Looper::Request *\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper7RequestE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6Looper8ResponseE\",\n   \"name\" : \"const android::Looper::Response *\",\n   \"referenced_type\" : \"_ZTIKN7android6Looper8ResponseE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6LooperE\",\n   \"name\" : \"const android::Looper *\",\n   \"referenced_type\" : \"_ZTIKN7android6LooperE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6ThreadE\",\n   \"name\" : \"const android::Thread *\",\n   \"referenced_type\" : \"_ZTIKN7android6ThreadE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"const android::Vector<android::sysprop_change_callback_info> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"const android::Vector<android::Looper::MessageEnvelope> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"const android::Vector<android::Looper::Response> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_6Looper8ResponseEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android6VectorINS_7String8EEE\",\n   \"name\" : \"const android::Vector<android::String8> *\",\n   \"referenced_type\" : \"_ZTIKN7android6VectorINS_7String8EEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android7FileMapE\",\n   \"name\" : \"const android::FileMap *\",\n   \"referenced_type\" : \"_ZTIKN7android7FileMapE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android7RefBase12weakref_typeE\",\n   \"name\" : \"const android::RefBase::weakref_type *\",\n   \"referenced_type\" : \"_ZTIKN7android7RefBase12weakref_typeE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase *\",\n   \"referenced_type\" : \"_ZTIKN7android7RefBaseE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase *\",\n   \"referenced_type\" : \"_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android7String8E\",\n   \"name\" : \"const android::String8 *\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android7String8E\",\n   \"name\" : \"const android::String8 *\",\n   \"referenced_type\" : \"_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android8String16E\",\n   \"name\" : \"const android::String16 *\",\n   \"referenced_type\" : \"_ZTIKN7android8String16E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android9StopWatchE\",\n   \"name\" : \"const android::StopWatch *\",\n   \"referenced_type\" : \"_ZTIKN7android9StopWatchE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKN7android9TokenizerE\",\n   \"name\" : \"const android::Tokenizer *\",\n   \"referenced_type\" : \"_ZTIKN7android9TokenizerE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKc\",\n   \"name\" : \"const char *\",\n   \"referenced_type\" : \"_ZTIKc\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKh\",\n   \"name\" : \"const unsigned char *\",\n   \"referenced_type\" : \"_ZTIKh\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKt\",\n   \"name\" : \"const unsigned short *\",\n   \"referenced_type\" : \"_ZTIKt\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/JenkinsHash.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPKv\",\n   \"name\" : \"const void *\",\n   \"referenced_type\" : \"_ZTIKv\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android10LogPrinterE\",\n   \"name\" : \"android::LogPrinter *\",\n   \"referenced_type\" : \"_ZTIN7android10LogPrinterE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl *\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android11ScopedTraceE\",\n   \"name\" : \"android::ScopedTrace *\",\n   \"referenced_type\" : \"_ZTIN7android11ScopedTraceE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Trace.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"android::LightRefBase<android::NativeHandle> *\",\n   \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android12NativeHandleE\",\n   \"name\" : \"android::NativeHandle *\",\n   \"referenced_type\" : \"_ZTIN7android12NativeHandleE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android13PrefixPrinterE\",\n   \"name\" : \"android::PrefixPrinter *\",\n   \"referenced_type\" : \"_ZTIN7android13PrefixPrinterE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android14LooperCallbackE\",\n   \"name\" : \"android::LooperCallback *\",\n   \"referenced_type\" : \"_ZTIN7android14LooperCallbackE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android14MessageHandlerE\",\n   \"name\" : \"android::MessageHandler *\",\n   \"referenced_type\" : \"_ZTIN7android14MessageHandlerE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android14StaticString16ILj1EEE\",\n   \"name\" : \"android::StaticString16<1> *\",\n   \"referenced_type\" : \"_ZTIN7android14StaticString16ILj1EEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android14String8PrinterE\",\n   \"name\" : \"android::String8Printer *\",\n   \"referenced_type\" : \"_ZTIN7android14String8PrinterE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer *\",\n   \"referenced_type\" : \"_ZTIN7android16ReferenceRenamerE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer *\",\n   \"referenced_type\" : \"_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl *\",\n   \"referenced_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android18WeakMessageHandlerE\",\n   \"name\" : \"android::WeakMessageHandler *\",\n   \"referenced_type\" : \"_ZTIN7android18WeakMessageHandlerE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase *\",\n   \"referenced_type\" : \"_ZTIN7android19VirtualLightRefBaseE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase *\",\n   \"referenced_type\" : \"_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android20SimpleLooperCallbackE\",\n   \"name\" : \"android::SimpleLooperCallback *\",\n   \"referenced_type\" : \"_ZTIN7android20SimpleLooperCallbackE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"android::sysprop_change_callback_info *\",\n   \"referenced_type\" : \"_ZTIN7android28sysprop_change_callback_infoE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_12NativeHandleEEE\",\n   \"name\" : \"android::sp<android::NativeHandle> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"android::sp<android::LooperCallback> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"android::wp<android::MessageHandler> *\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"android::wp<android::Thread> *\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android4base11borrowed_fdE\",\n   \"name\" : \"android::base::borrowed_fd *\",\n   \"referenced_type\" : \"_ZTIN7android4base11borrowed_fdE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"android::base::unique_fd_impl<android::base::DefaultCloser> *\",\n   \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android5Mutex8AutolockE\",\n   \"name\" : \"android::Mutex::Autolock *\",\n   \"referenced_type\" : \"_ZTIN7android5Mutex8AutolockE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android5MutexE\",\n   \"name\" : \"android::Mutex *\",\n   \"referenced_type\" : \"_ZTIN7android5MutexE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"android::Looper::MessageEnvelope *\",\n   \"referenced_type\" : \"_ZTIN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6Looper8ResponseE\",\n   \"name\" : \"android::Looper::Response *\",\n   \"referenced_type\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6LooperE\",\n   \"name\" : \"android::Looper *\",\n   \"referenced_type\" : \"_ZTIN7android6LooperE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6RWLock9AutoRLockE\",\n   \"name\" : \"android::RWLock::AutoRLock *\",\n   \"referenced_type\" : \"_ZTIN7android6RWLock9AutoRLockE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6RWLock9AutoWLockE\",\n   \"name\" : \"android::RWLock::AutoWLock *\",\n   \"referenced_type\" : \"_ZTIN7android6RWLock9AutoWLockE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6RWLockE\",\n   \"name\" : \"android::RWLock *\",\n   \"referenced_type\" : \"_ZTIN7android6RWLockE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6ThreadE\",\n   \"name\" : \"android::Thread *\",\n   \"referenced_type\" : \"_ZTIN7android6ThreadE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::Vector<android::sysprop_change_callback_info> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::Vector<android::Looper::MessageEnvelope> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::Vector<android::Looper::Response> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android6VectorINS_7String8EEE\",\n   \"name\" : \"android::Vector<android::String8> *\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_7String8EEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7FileMapE\",\n   \"name\" : \"android::FileMap *\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7MessageE\",\n   \"name\" : \"android::Message *\",\n   \"referenced_type\" : \"_ZTIN7android7MessageE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7PrinterE\",\n   \"name\" : \"android::Printer *\",\n   \"referenced_type\" : \"_ZTIN7android7PrinterE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBase12weakref_implE\",\n   \"name\" : \"android::RefBase::weakref_impl *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_implE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_typeE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBaseE\",\n   \"name\" : \"android::RefBase *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7RefBaseE\",\n   \"name\" : \"android::RefBase *\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7String8E\",\n   \"name\" : \"android::String8 *\",\n   \"referenced_type\" : \"_ZTIN7android7String8E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android7String8E\",\n   \"name\" : \"android::String8 *\",\n   \"referenced_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIPN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android8String1610StaticDataILj1EEE\",\n   \"name\" : \"android::String16::StaticData<1> *\",\n   \"referenced_type\" : \"_ZTIN7android8String1610StaticDataILj1EEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android8String16E\",\n   \"name\" : \"android::String16 *\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android9ConditionE\",\n   \"name\" : \"android::Condition *\",\n   \"referenced_type\" : \"_ZTIN7android9ConditionE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android9FdPrinterE\",\n   \"name\" : \"android::FdPrinter *\",\n   \"referenced_type\" : \"_ZTIN7android9FdPrinterE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android9StopWatchE\",\n   \"name\" : \"android::StopWatch *\",\n   \"referenced_type\" : \"_ZTIN7android9StopWatchE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPN7android9TokenizerE\",\n   \"name\" : \"android::Tokenizer *\",\n   \"referenced_type\" : \"_ZTIN7android9TokenizerE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPPN7android9TokenizerE\",\n   \"name\" : \"android::Tokenizer **\",\n   \"referenced_type\" : \"_ZTIPN7android9TokenizerE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPPv\",\n   \"name\" : \"void **\",\n   \"referenced_type\" : \"_ZTIPv\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/AndroidThreads.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPc\",\n   \"name\" : \"char *\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPh\",\n   \"name\" : \"unsigned char *\",\n   \"referenced_type\" : \"_ZTIh\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPi\",\n   \"name\" : \"int *\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPj\",\n   \"name\" : \"unsigned int *\",\n   \"referenced_type\" : \"_ZTIj\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Unicode.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIPv\",\n   \"name\" : \"void *\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  }\n ],\n \"qualified_types\" :\n [\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIA1_KDs\",\n   \"name\" : \"const char16_t[1]\",\n   \"referenced_type\" : \"_ZTIA1_Ds\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK13native_handle\",\n   \"name\" : \"const native_handle\",\n   \"referenced_type\" : \"_ZTI13native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIK7log_msg\",\n   \"name\" : \"const log_msg\",\n   \"referenced_type\" : \"_ZTI7log_msg\",\n   \"size\" : 5124,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKDi\",\n   \"name\" : \"const char32_t\",\n   \"referenced_type\" : \"_ZTIDi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKDs\",\n   \"name\" : \"const char16_t\",\n   \"referenced_type\" : \"_ZTIDs\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIKN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android10VectorImplE\",\n   \"name\" : \"const android::VectorImpl\",\n   \"referenced_type\" : \"_ZTIN7android10VectorImplE\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"const android::LightRefBase<android::NativeHandle>\",\n   \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android12NativeHandleE\",\n   \"name\" : \"const android::NativeHandle\",\n   \"referenced_type\" : \"_ZTIN7android12NativeHandleE\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android16ReferenceRenamerE\",\n   \"name\" : \"const android::ReferenceRenamer\",\n   \"referenced_type\" : \"_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android16SortedVectorImplE\",\n   \"name\" : \"const android::SortedVectorImpl\",\n   \"referenced_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android28sysprop_change_callback_infoE\",\n   \"name\" : \"const android::sysprop_change_callback_info\",\n   \"referenced_type\" : \"_ZTIN7android28sysprop_change_callback_infoE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::LooperCallback>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::sp<android::MessageHandler>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"const android::sp<android::SimpleLooperCallback>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_6LooperEEE\",\n   \"name\" : \"const android::sp<android::Looper>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2spINS_6ThreadEEE\",\n   \"name\" : \"const android::sp<android::Thread>\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"const android::wp<android::MessageHandler>\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"const android::wp<android::Thread>\",\n   \"referenced_type\" : \"_ZTIN7android2wpINS_6ThreadEEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android4base11borrowed_fdE\",\n   \"name\" : \"const android::base::borrowed_fd\",\n   \"referenced_type\" : \"_ZTIN7android4base11borrowed_fdE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"const android::base::unique_fd_impl<android::base::DefaultCloser>\",\n   \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"const android::Looper::MessageEnvelope\",\n   \"referenced_type\" : \"_ZTIN7android6Looper15MessageEnvelopeE\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6Looper7RequestE\",\n   \"name\" : \"const android::Looper::Request\",\n   \"referenced_type\" : \"_ZTIN7android6Looper7RequestE\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6Looper8ResponseE\",\n   \"name\" : \"const android::Looper::Response\",\n   \"referenced_type\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6LooperE\",\n   \"name\" : \"const android::Looper\",\n   \"referenced_type\" : \"_ZTIN7android6LooperE\",\n   \"size\" : 136,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6ThreadE\",\n   \"name\" : \"const android::Thread\",\n   \"referenced_type\" : \"_ZTIN7android6ThreadE\",\n   \"size\" : 44,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"const android::Vector<android::sysprop_change_callback_info>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"const android::Vector<android::Looper::MessageEnvelope>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"const android::Vector<android::Looper::Response>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android6VectorINS_7String8EEE\",\n   \"name\" : \"const android::Vector<android::String8>\",\n   \"referenced_type\" : \"_ZTIN7android6VectorINS_7String8EEE\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7FileMapE\",\n   \"name\" : \"const android::FileMap\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7MessageE\",\n   \"name\" : \"const android::Message\",\n   \"referenced_type\" : \"_ZTIN7android7MessageE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7RefBase12weakref_typeE\",\n   \"name\" : \"const android::RefBase::weakref_type\",\n   \"referenced_type\" : \"_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIKN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7RefBaseE\",\n   \"name\" : \"const android::RefBase\",\n   \"referenced_type\" : \"_ZTIN7android7RefBaseE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7String8E\",\n   \"name\" : \"const android::String8\",\n   \"referenced_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"self_type\" : \"_ZTIKN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android7String8E\",\n   \"name\" : \"const android::String8\",\n   \"referenced_type\" : \"_ZTIN7android7String8E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android8String1610StaticDataILj1EEE\",\n   \"name\" : \"const android::String16::StaticData<1>\",\n   \"referenced_type\" : \"_ZTIN7android8String1610StaticDataILj1EEE\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android8String16E\",\n   \"name\" : \"const android::String16\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android9StopWatchE\",\n   \"name\" : \"const android::StopWatch\",\n   \"referenced_type\" : \"_ZTIN7android9StopWatchE\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKN7android9TokenizerE\",\n   \"name\" : \"const android::Tokenizer\",\n   \"referenced_type\" : \"_ZTIN7android9TokenizerE\",\n   \"size\" : 28,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKPN7android7RefBase12weakref_implE\",\n   \"name\" : \"android::RefBase::weakref_impl *const\",\n   \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_implE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKa\",\n   \"name\" : \"const signed char\",\n   \"referenced_type\" : \"_ZTIa\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKb\",\n   \"name\" : \"const bool\",\n   \"referenced_type\" : \"_ZTIb\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKc\",\n   \"name\" : \"const char\",\n   \"referenced_type\" : \"_ZTIc\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKd\",\n   \"name\" : \"const double\",\n   \"referenced_type\" : \"_ZTId\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKf\",\n   \"name\" : \"const float\",\n   \"referenced_type\" : \"_ZTIf\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKh\",\n   \"name\" : \"const unsigned char\",\n   \"referenced_type\" : \"_ZTIh\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKi\",\n   \"name\" : \"const int\",\n   \"referenced_type\" : \"_ZTIi\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKj\",\n   \"name\" : \"const unsigned int\",\n   \"referenced_type\" : \"_ZTIj\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKs\",\n   \"name\" : \"const short\",\n   \"referenced_type\" : \"_ZTIs\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 2,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKt\",\n   \"name\" : \"const unsigned short\",\n   \"referenced_type\" : \"_ZTIt\",\n   \"size\" : 2,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKv\",\n   \"name\" : \"const void\",\n   \"referenced_type\" : \"_ZTIv\",\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKx\",\n   \"name\" : \"const long long\",\n   \"referenced_type\" : \"_ZTIx\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"is_const\" : true,\n   \"linker_set_key\" : \"_ZTIKy\",\n   \"name\" : \"const unsigned long long\",\n   \"referenced_type\" : \"_ZTIy\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"is_volatile\" : true,\n   \"linker_set_key\" : \"_ZTIVb\",\n   \"name\" : \"volatile bool\",\n   \"referenced_type\" : \"_ZTIb\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\"\n  }\n ],\n \"record_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"len\",\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"field_name\" : \"hdr_size\",\n     \"field_offset\" : 16,\n     \"referenced_type\" : \"_ZTIt\"\n    },\n    {\n     \"field_name\" : \"pid\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"tid\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"sec\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"nsec\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"lid\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"uid\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI12logger_entry\",\n   \"name\" : \"logger_entry\",\n   \"size\" : 28,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"y\",\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"field_name\" : \"cb\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"field_name\" : \"cr\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"field_name\" : \"ystride\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"cstride\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"chroma_step\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"reserved\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIA8_j\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13android_ycbcr\",\n   \"name\" : \"android_ycbcr\",\n   \"size\" : 56,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"version\",\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numFds\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"numInts\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIA0_i\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI13native_handle\",\n   \"name\" : \"native_handle\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libcutils/include_outside_system/cutils/native_handle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"x\",\n     \"referenced_type\" : \"_ZTIf\"\n    },\n    {\n     \"field_name\" : \"y\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI16android_xy_color\",\n   \"name\" : \"android_xy_color\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"top_left\",\n     \"referenced_type\" : \"_ZTIPh\"\n    },\n    {\n     \"field_name\" : \"component\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTI22android_flex_component\"\n    },\n    {\n     \"field_name\" : \"bits_per_component\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"bits_used\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"h_increment\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"v_increment\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"h_subsampling\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"v_subsampling\",\n     \"field_offset\" : 224,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI18android_flex_plane\",\n   \"name\" : \"android_flex_plane\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"format\",\n     \"referenced_type\" : \"_ZTI19android_flex_format\"\n    },\n    {\n     \"field_name\" : \"num_planes\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"planes\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIP18android_flex_plane\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI19android_flex_layout\",\n   \"name\" : \"android_flex_layout\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"num_points\",\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"reserved\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIA8_j\"\n    },\n    {\n     \"field_name\" : \"xyzc_points\",\n     \"field_offset\" : 288,\n     \"referenced_type\" : \"_ZTIA_f\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI20android_depth_points\",\n   \"name\" : \"android_depth_points\",\n   \"size\" : 36,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"struct_size\",\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"buffer_id\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"priority\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"tag\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"field_name\" : \"file\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"field_name\" : \"line\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"message\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI21__android_log_message\",\n   \"name\" : \"__android_log_message\",\n   \"size\" : 28,\n   \"source_file\" : \"system/logging/liblog/include_vndk/android/log.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"maxContentLightLevel\",\n     \"referenced_type\" : \"_ZTIf\"\n    },\n    {\n     \"field_name\" : \"maxFrameAverageLightLevel\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI25android_cta861_3_metadata\",\n   \"name\" : \"android_cta861_3_metadata\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"displayPrimaryRed\",\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"displayPrimaryGreen\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"displayPrimaryBlue\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"whitePoint\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTI16android_xy_color\"\n    },\n    {\n     \"field_name\" : \"maxLuminance\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIf\"\n    },\n    {\n     \"field_name\" : \"minLuminance\",\n     \"field_offset\" : 288,\n     \"referenced_type\" : \"_ZTIf\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI26android_smpte2086_metadata\",\n   \"name\" : \"android_smpte2086_metadata\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libsystem/include/system/graphics.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7log_msgUt_E\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI7log_msg\",\n   \"name\" : \"log_msg\",\n   \"size\" : 5124,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"tv_sec\",\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"field_name\" : \"tv_nsec\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTI8log_time\",\n   \"name\" : \"log_time\",\n   \"size\" : 8,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_time.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLogTag\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPriority\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTI19android_LogPriority\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mIgnoreBlankLines\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10LogPrinterE\",\n   \"name\" : \"android::LogPrinter\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android10LogPrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android10LogPrinter9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10LogPrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10LogPrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStorage\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFlags\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIKj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mItemSize\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIKj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android10VectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStorage\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFlags\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIKj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mItemSize\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIKj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android10VectorImplE\",\n   \"name\" : \"android::VectorImpl\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android10VectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android10VectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mTag\",\n     \"referenced_type\" : \"_ZTIy\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android11ScopedTraceE\",\n   \"name\" : \"android::ScopedTrace\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/Trace.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"referenced_type\" : \"_ZTINSt3__16atomicIiEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\",\n   \"name\" : \"android::LightRefBase<android::NativeHandle>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android12NativeHandleE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"referenced_type\" : \"_ZTINSt3__16atomicIiEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE\",\n   \"name\" : \"android::LightRefBase<android::VirtualLightRefBase>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android19VirtualLightRefBaseE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCount\",\n     \"referenced_type\" : \"_ZTINSt3__16atomicIiEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE\",\n   \"name\" : \"android::LightRefBase<android::VirtualLightRefBase>\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/LightRefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_12NativeHandleEEE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mHandle\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIP13native_handle\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mOwnsHandle\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIb\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android12NativeHandleE\",\n   \"name\" : \"android::NativeHandle\",\n   \"record_kind\" : \"class\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/NativeHandle.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrinter\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIRN7android7PrinterE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android13PrefixPrinterE\",\n   \"name\" : \"android::PrefixPrinter\",\n   \"record_kind\" : \"class\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android13PrefixPrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android13PrefixPrinter9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android13PrefixPrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android13PrefixPrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_pointer<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_pointer<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android13trait_pointerINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_pointer<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"is_virtual\" : true,\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14LooperCallbackE\",\n   \"name\" : \"android::LooperCallback\",\n   \"record_kind\" : \"class\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 4,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14LooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14LooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14LooperCallbackD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android14LooperCallback11handleEventEiiPv\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -4,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -4,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14LooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android14LooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android14LooperCallbackD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"is_virtual\" : true,\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14MessageHandlerE\",\n   \"name\" : \"android::MessageHandler\",\n   \"record_kind\" : \"class\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 4,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14MessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14MessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14MessageHandlerD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android14MessageHandler13handleMessageERKNS_7MessageE\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -4,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -4,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14MessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android14MessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android14MessageHandlerD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android14ReferenceMoverE\",\n   \"name\" : \"android::ReferenceMover\",\n   \"record_kind\" : \"class\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android14ReferenceMoverE\",\n   \"name\" : \"android::ReferenceMover\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android14ReferenceMoverE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android8String16E\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mData\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIKN7android8String1610StaticDataILj1EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14StaticString16ILj1EEE\",\n   \"name\" : \"android::StaticString16<1>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mTarget\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPN7android7String8E\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android14String8PrinterE\",\n   \"name\" : \"android::String8Printer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 12,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android14String8PrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android14String8Printer9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14String8PrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android14String8PrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16ReferenceRenamerE\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16ReferenceRenamerclEj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIN7android16ReferenceRenamerE\",\n   \"name\" : \"android::ReferenceRenamer\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android16ReferenceRenamerE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16ReferenceRenamerE\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16ReferenceRenamerclEj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16SortedVectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16SortedVectorImpl10do_compareEPKvS2_\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16SortedVectorImplE\",\n   \"name\" : \"android::SortedVectorImpl\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android16SortedVectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/VectorImpl.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android16SortedVectorImplE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android16SortedVectorImplD0Ev\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl12do_constructEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl10do_destroyEPvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl7do_copyEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl8do_splatEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android10VectorImpl16do_move_backwardEPvPKvj\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZNK7android16SortedVectorImpl10do_compareEPKvS2_\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTINSt3__117integral_constantIbLb0EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16use_trivial_moveINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::use_trivial_move<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTINSt3__117integral_constantIbLb0EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16use_trivial_moveINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::use_trivial_move<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTINSt3__117integral_constantIbLb0EEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android16use_trivial_moveINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::use_trivial_move<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android14MessageHandlerE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mHandler\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android18WeakMessageHandlerE\",\n   \"name\" : \"android::WeakMessageHandler\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 12,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android18WeakMessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android18WeakMessageHandlerD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android18WeakMessageHandler13handleMessageERKNS_7MessageE\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -12,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -12,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android18WeakMessageHandlerE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android18WeakMessageHandlerD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android18WeakMessageHandlerD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_copy<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_copy<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbEE\",\n   \"name\" : \"android::trait_trivial_copy<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIbEE\",\n   \"name\" : \"android::trait_trivial_copy<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcEE\",\n   \"name\" : \"android::trait_trivial_copy<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIcEE\",\n   \"name\" : \"android::trait_trivial_copy<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdEE\",\n   \"name\" : \"android::trait_trivial_copy<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIdEE\",\n   \"name\" : \"android::trait_trivial_copy<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfEE\",\n   \"name\" : \"android::trait_trivial_copy<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIfEE\",\n   \"name\" : \"android::trait_trivial_copy<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIhEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiEE\",\n   \"name\" : \"android::trait_trivial_copy<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIiEE\",\n   \"name\" : \"android::trait_trivial_copy<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIjEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlEE\",\n   \"name\" : \"android::trait_trivial_copy<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIlEE\",\n   \"name\" : \"android::trait_trivial_copy<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyImEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsEE\",\n   \"name\" : \"android::trait_trivial_copy<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIsEE\",\n   \"name\" : \"android::trait_trivial_copy<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyItEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvEE\",\n   \"name\" : \"android::trait_trivial_copy<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIvEE\",\n   \"name\" : \"android::trait_trivial_copy<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxEE\",\n   \"name\" : \"android::trait_trivial_copy<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIxEE\",\n   \"name\" : \"android::trait_trivial_copy<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_copyIyEE\",\n   \"name\" : \"android::trait_trivial_copy<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_copyIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_ctor<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_ctor<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbEE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIbEE\",\n   \"name\" : \"android::trait_trivial_ctor<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcEE\",\n   \"name\" : \"android::trait_trivial_ctor<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIcEE\",\n   \"name\" : \"android::trait_trivial_ctor<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdEE\",\n   \"name\" : \"android::trait_trivial_ctor<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIdEE\",\n   \"name\" : \"android::trait_trivial_ctor<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfEE\",\n   \"name\" : \"android::trait_trivial_ctor<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIfEE\",\n   \"name\" : \"android::trait_trivial_ctor<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIhEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiEE\",\n   \"name\" : \"android::trait_trivial_ctor<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIiEE\",\n   \"name\" : \"android::trait_trivial_ctor<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIjEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlEE\",\n   \"name\" : \"android::trait_trivial_ctor<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIlEE\",\n   \"name\" : \"android::trait_trivial_ctor<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorImEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsEE\",\n   \"name\" : \"android::trait_trivial_ctor<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIsEE\",\n   \"name\" : \"android::trait_trivial_ctor<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorItEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvEE\",\n   \"name\" : \"android::trait_trivial_ctor<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIvEE\",\n   \"name\" : \"android::trait_trivial_ctor<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxEE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIxEE\",\n   \"name\" : \"android::trait_trivial_ctor<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_ctorIyEE\",\n   \"name\" : \"android::trait_trivial_ctor<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_ctorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_dtor<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_dtor<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbEE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIbEE\",\n   \"name\" : \"android::trait_trivial_dtor<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcEE\",\n   \"name\" : \"android::trait_trivial_dtor<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIcEE\",\n   \"name\" : \"android::trait_trivial_dtor<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdEE\",\n   \"name\" : \"android::trait_trivial_dtor<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIdEE\",\n   \"name\" : \"android::trait_trivial_dtor<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfEE\",\n   \"name\" : \"android::trait_trivial_dtor<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIfEE\",\n   \"name\" : \"android::trait_trivial_dtor<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIhEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiEE\",\n   \"name\" : \"android::trait_trivial_dtor<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIiEE\",\n   \"name\" : \"android::trait_trivial_dtor<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIjEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlEE\",\n   \"name\" : \"android::trait_trivial_dtor<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIlEE\",\n   \"name\" : \"android::trait_trivial_dtor<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorImEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsEE\",\n   \"name\" : \"android::trait_trivial_dtor<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIsEE\",\n   \"name\" : \"android::trait_trivial_dtor<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorItEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvEE\",\n   \"name\" : \"android::trait_trivial_dtor<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIvEE\",\n   \"name\" : \"android::trait_trivial_dtor<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxEE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIxEE\",\n   \"name\" : \"android::trait_trivial_dtor<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_dtorIyEE\",\n   \"name\" : \"android::trait_trivial_dtor<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_dtorIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::trait_trivial_move<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::trait_trivial_move<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EEE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android7String8E\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EEE\",\n   \"name\" : \"android::trait_trivial_move<android::String8>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveINS_7String8EEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveINS_8String16EEE\",\n   \"name\" : \"android::trait_trivial_move<android::String16>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android8String16E\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbEE\",\n   \"name\" : \"android::trait_trivial_move<bool>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIbEE\",\n   \"name\" : \"android::trait_trivial_move<bool>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIbEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIb\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcEE\",\n   \"name\" : \"android::trait_trivial_move<char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIcEE\",\n   \"name\" : \"android::trait_trivial_move<char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIcEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIc\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdEE\",\n   \"name\" : \"android::trait_trivial_move<double>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIdEE\",\n   \"name\" : \"android::trait_trivial_move<double>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIdEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTId\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfEE\",\n   \"name\" : \"android::trait_trivial_move<float>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIfEE\",\n   \"name\" : \"android::trait_trivial_move<float>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIfEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIf\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIhEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned char>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIhEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIh\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiEE\",\n   \"name\" : \"android::trait_trivial_move<int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIiEE\",\n   \"name\" : \"android::trait_trivial_move<int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIiEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIi\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIjEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned int>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIjEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIj\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlEE\",\n   \"name\" : \"android::trait_trivial_move<long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIlEE\",\n   \"name\" : \"android::trait_trivial_move<long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIlEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIl\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveImEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveImEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIm\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsEE\",\n   \"name\" : \"android::trait_trivial_move<short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIsEE\",\n   \"name\" : \"android::trait_trivial_move<short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIsEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIs\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveItEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned short>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveItEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIt\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvEE\",\n   \"name\" : \"android::trait_trivial_move<void>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIvEE\",\n   \"name\" : \"android::trait_trivial_move<void>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIvEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIv\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxEE\",\n   \"name\" : \"android::trait_trivial_move<long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIxEE\",\n   \"name\" : \"android::trait_trivial_move<long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIxEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIx\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android18trait_trivial_moveIyEE\",\n   \"name\" : \"android::trait_trivial_move<unsigned long long>\",\n   \"self_type\" : \"_ZTIN7android18trait_trivial_moveIyEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIy\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/LightRefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android19VirtualLightRefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android12LightRefBaseINS_19VirtualLightRefBaseEEE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android19VirtualLightRefBaseE\",\n   \"name\" : \"android::VirtualLightRefBase\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android19VirtualLightRefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/LightRefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android19VirtualLightRefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android19VirtualLightRefBaseD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android14LooperCallbackE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCallback\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPFiiiPvE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android20SimpleLooperCallbackE\",\n   \"name\" : \"android::SimpleLooperCallback\",\n   \"record_kind\" : \"class\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 8,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android20SimpleLooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android20SimpleLooperCallbackD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android20SimpleLooperCallback11handleEventEiiPv\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -8,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -8,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android20SimpleLooperCallbackE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android20SimpleLooperCallbackD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android20SimpleLooperCallbackD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android12NativeHandleE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"name\" : \"android::sp<android::NativeHandle>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android12NativeHandleE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android14LooperCallbackE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\",\n   \"name\" : \"android::sp<android::LooperCallback>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android14LooperCallbackE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android14MessageHandlerE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android20SimpleLooperCallbackE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android20SimpleLooperCallbackE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android6LooperE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6LooperE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6ThreadE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android14MessageHandlerE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_refs\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2wpINS_14MessageHandlerEEE\",\n   \"name\" : \"android::wp<android::MessageHandler>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android14MessageHandlerE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_ptr\",\n     \"referenced_type\" : \"_ZTIPN7android6ThreadE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"m_refs\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPN7android7RefBase12weakref_typeE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android2wpINS_6ThreadEEE\",\n   \"name\" : \"android::wp<android::Thread>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6ThreadE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"fd_\",\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android4base11borrowed_fdE\",\n   \"name\" : \"android::base::borrowed_fd\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android4base13DefaultCloserE\",\n   \"name\" : \"android::base::DefaultCloser\",\n   \"size\" : 1,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"fd_\",\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"android::base::unique_fd_impl<android::base::DefaultCloser>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android4base13DefaultCloserE\"\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"referenced_type\" : \"_ZTIRN7android5MutexE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android5Mutex8AutolockE\",\n   \"name\" : \"android::Mutex::Autolock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mMutex\",\n     \"referenced_type\" : \"_ZTI15pthread_mutex_t\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android5MutexE\",\n   \"name\" : \"android::Mutex\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Mutex.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"uptime\",\n     \"referenced_type\" : \"_ZTIx\"\n    },\n    {\n     \"field_name\" : \"handler\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\"\n    },\n    {\n     \"field_name\" : \"message\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIN7android7MessageE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper15MessageEnvelopeE\",\n   \"name\" : \"android::Looper::MessageEnvelope\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"fd\",\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"ident\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"events\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"callback\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIN7android2spINS_14LooperCallbackEEE\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIPv\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper7RequestE\",\n   \"name\" : \"android::Looper::Request\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"access\" : \"private\",\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"seq\",\n     \"referenced_type\" : \"_ZTIy\"\n    },\n    {\n     \"field_name\" : \"events\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"field_name\" : \"request\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIN7android6Looper7RequestE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6Looper8ResponseE\",\n   \"name\" : \"android::Looper::Response\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 8,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mAllowNonCallbacks\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIKb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mWakeEventFd\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIN7android5MutexE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mMessageEnvelopes\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mSendingMessage\",\n     \"field_offset\" : 320,\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPolling\",\n     \"field_offset\" : 328,\n     \"referenced_type\" : \"_ZTIVb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mEpollFd\",\n     \"field_offset\" : 352,\n     \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mEpollRebuildRequired\",\n     \"field_offset\" : 384,\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRequests\",\n     \"field_offset\" : 416,\n     \"referenced_type\" : \"_ZTINSt3__113unordered_mapIyN7android6Looper7RequestENS_4hashIyEENS_8equal_toIyEENS_9allocatorINS_4pairIKyS3_EEEEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mSequenceNumberByFd\",\n     \"field_offset\" : 576,\n     \"referenced_type\" : \"_ZTINSt3__113unordered_mapIiyNS_4hashIiEENS_8equal_toIiEENS_9allocatorINS_4pairIKiyEEEEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mNextRequestSeq\",\n     \"field_offset\" : 768,\n     \"referenced_type\" : \"_ZTIy\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mResponses\",\n     \"field_offset\" : 832,\n     \"referenced_type\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mResponseIndex\",\n     \"field_offset\" : 992,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mNextMessageUptime\",\n     \"field_offset\" : 1024,\n     \"referenced_type\" : \"_ZTIx\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6LooperE\",\n   \"name\" : \"android::Looper\",\n   \"record_kind\" : \"class\",\n   \"size\" : 136,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6LooperE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6LooperD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6LooperD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"referenced_type\" : \"_ZTIRN7android6RWLockE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLock9AutoRLockE\",\n   \"name\" : \"android::RWLock::AutoRLock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"referenced_type\" : \"_ZTIRN7android6RWLockE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLock9AutoWLockE\",\n   \"name\" : \"android::RWLock::AutoWLock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRWLock\",\n     \"referenced_type\" : \"_ZTI16pthread_rwlock_t\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6RWLockE\",\n   \"name\" : \"android::RWLock\",\n   \"record_kind\" : \"class\",\n   \"size\" : 40,\n   \"source_file\" : \"system/core/libutils/include/utils/RWLock.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"is_virtual\" : true,\n     \"referenced_type\" : \"_ZTIN7android7RefBaseE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCanCallJava\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIKb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mThread\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLock\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIN7android5MutexE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mThreadExitedCondition\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIN7android9ConditionE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStatus\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mExitPending\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIVb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRunning\",\n     \"field_offset\" : 200,\n     \"referenced_type\" : \"_ZTIVb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mHoldSelf\",\n     \"field_offset\" : 224,\n     \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mTid\",\n     \"field_offset\" : 256,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6ThreadE\",\n   \"name\" : \"android::Thread\",\n   \"record_kind\" : \"class\",\n   \"size\" : 44,\n   \"source_file\" : \"system/core/libutils/include/utils/Thread.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"component_value\" : 36,\n     \"kind\" : \"vbase_offset\"\n    },\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6ThreadE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6ThreadD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6ThreadD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android6Thread3runEPKcij\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android6Thread11requestExitEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android6Thread10readyToRunEv\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android6Thread10threadLoopEv\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -36,\n     \"kind\" : \"vcall_offset\"\n    },\n    {\n     \"component_value\" : -36,\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6ThreadE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android6ThreadD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZTv0_n12_N7android6ThreadD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::Vector<android::sysprop_change_callback_info>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_28sysprop_change_callback_infoEEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_28sysprop_change_callback_infoEED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_28sysprop_change_callback_infoEED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE12do_constructEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE10do_destroyEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE7do_copyEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE8do_splatEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_28sysprop_change_callback_infoEE16do_move_backwardEPvPKvj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::Vector<android::Looper::MessageEnvelope>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_6Looper15MessageEnvelopeEEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper15MessageEnvelopeEED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper15MessageEnvelopeEED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE12do_constructEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE10do_destroyEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE7do_copyEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE8do_splatEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper15MessageEnvelopeEE16do_move_backwardEPvPKvj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::Vector<android::Looper::Response>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_6Looper8ResponseEEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper8ResponseEED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_6Looper8ResponseEED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE12do_constructEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE10do_destroyEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE7do_copyEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE8do_splatEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_6Looper8ResponseEE16do_move_backwardEPvPKvj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"referenced_type\" : \"_ZTIN7android10VectorImplE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android6VectorINS_7String8EEE\",\n   \"name\" : \"android::Vector<android::String8>\",\n   \"record_kind\" : \"class\",\n   \"size\" : 20,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/Vector.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\"\n   ],\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android6VectorINS_7String8EEE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_7String8EED1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android6VectorINS_7String8EED0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE12do_constructEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE10do_destroyEPvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE7do_copyEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE8do_splatEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE15do_move_forwardEPvPKvj\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZNK7android6VectorINS_7String8EE16do_move_backwardEPvPKvj\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_28sysprop_change_callback_infoEEE\",\n   \"name\" : \"android::traits<android::sysprop_change_callback_info>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android28sysprop_change_callback_infoE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper15MessageEnvelopeEEE\",\n   \"name\" : \"android::traits<android::Looper::MessageEnvelope>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper15MessageEnvelopeE\"\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android6traitsINS_6Looper8ResponseEEE\",\n   \"name\" : \"android::traits<android::Looper::Response>\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/TypeHelpers.h\",\n   \"template_args\" :\n   [\n    \"_ZTIN7android6Looper8ResponseE\"\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFileName\",\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mBasePtr\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mBaseLength\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mDataOffset\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIx\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mDataPtr\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIPv\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mDataLength\",\n     \"field_offset\" : 224,\n     \"referenced_type\" : \"_ZTIj\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7FileMapE\",\n   \"name\" : \"android::FileMap\",\n   \"record_kind\" : \"class\",\n   \"size\" : 32,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"what\",\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7MessageE\",\n   \"name\" : \"android::Message\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Looper.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTIN7android7PrinterE\",\n   \"name\" : \"android::Printer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android7PrinterE\"\n    },\n    {\n     \"is_pure\" : true,\n     \"mangled_component_name\" : \"_ZN7android7Printer9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7PrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7PrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type\",\n   \"record_kind\" : \"class\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 1,\n   \"linker_set_key\" : \"_ZTIN7android7RefBase12weakref_typeE\",\n   \"name\" : \"android::RefBase::weakref_type\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android7RefBase12weakref_typeE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 1,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRefs\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIKPN7android7RefBase12weakref_implE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBaseE\",\n   \"name\" : \"android::RefBase\",\n   \"record_kind\" : \"class\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android7RefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mRefs\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIKPN7android7RefBase12weakref_implE\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7RefBaseE\",\n   \"name\" : \"android::RefBase\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android7RefBaseE#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/RefBase.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android7RefBaseE\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android7RefBaseD0Ev\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase10onFirstRefEv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase15onLastStrongRefEPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase20onIncStrongAttemptedEjPKv\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7RefBase13onLastWeakRefEPKv\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mString\",\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7String8E\",\n   \"name\" : \"android::String8\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mString\",\n     \"referenced_type\" : \"_ZTIPKc\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android7String8E\",\n   \"name\" : \"android::String8\",\n   \"record_kind\" : \"class\",\n   \"self_type\" : \"_ZTIN7android7String8E#ODR:out/soong/.intermediates/system/core/libutils/binder/libutils_binder/android_vendor_arm_armv8-a_cortex-a53_static_afdo-libutils/obj/system/core/libutils/binder/RefBase.sdump\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String8.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"size\",\n     \"referenced_type\" : \"_ZTIKj\"\n    },\n    {\n     \"field_name\" : \"data\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIA1_Ds\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android8String1610StaticDataILj1EEE\",\n   \"name\" : \"android::String16::StaticData<1>\",\n   \"size\" : 8,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mString\",\n     \"referenced_type\" : \"_ZTIPKDs\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android8String16E\",\n   \"name\" : \"android::String16\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCond\",\n     \"referenced_type\" : \"_ZTI14pthread_cond_t\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9ConditionE\",\n   \"name\" : \"android::Condition\",\n   \"record_kind\" : \"class\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/Condition.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"base_specifiers\" :\n   [\n    {\n     \"referenced_type\" : \"_ZTIN7android7PrinterE\"\n    }\n   ],\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFd\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mIndent\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mPrefix\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFormatString\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIA20_c\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9FdPrinterE\",\n   \"name\" : \"android::FdPrinter\",\n   \"record_kind\" : \"class\",\n   \"size\" : 36,\n   \"source_file\" : \"system/core/libutils/include/utils/Printer.h\",\n   \"vtable_components\" :\n   [\n    {\n     \"kind\" : \"offset_to_top\"\n    },\n    {\n     \"kind\" : \"rtti\",\n     \"mangled_component_name\" : \"_ZTIN7android9FdPrinterE\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android9FdPrinter9printLineEPKc\"\n    },\n    {\n     \"mangled_component_name\" : \"_ZN7android7Printer15printFormatLineEPKcz\"\n    },\n    {\n     \"kind\" : \"complete_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android9FdPrinterD1Ev\"\n    },\n    {\n     \"kind\" : \"deleting_dtor_pointer\",\n     \"mangled_component_name\" : \"_ZN7android9FdPrinterD0Ev\"\n    }\n   ]\n  },\n  {\n   \"alignment\" : 8,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mName\",\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mClock\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIi\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mStartTime\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIx\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9StopWatchE\",\n   \"name\" : \"android::StopWatch\",\n   \"record_kind\" : \"class\",\n   \"size\" : 16,\n   \"source_file\" : \"system/core/libutils/include/utils/StopWatch.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFilename\",\n     \"referenced_type\" : \"_ZTIN7android7String8E\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mFileMap\",\n     \"field_offset\" : 32,\n     \"referenced_type\" : \"_ZTIPN7android7FileMapE\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mBuffer\",\n     \"field_offset\" : 64,\n     \"referenced_type\" : \"_ZTIPc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mOwnBuffer\",\n     \"field_offset\" : 96,\n     \"referenced_type\" : \"_ZTIb\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLength\",\n     \"field_offset\" : 128,\n     \"referenced_type\" : \"_ZTIj\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mCurrent\",\n     \"field_offset\" : 160,\n     \"referenced_type\" : \"_ZTIPKc\"\n    },\n    {\n     \"access\" : \"private\",\n     \"field_name\" : \"mLineNumber\",\n     \"field_offset\" : 192,\n     \"referenced_type\" : \"_ZTIi\"\n    }\n   ],\n   \"linker_set_key\" : \"_ZTIN7android9TokenizerE\",\n   \"name\" : \"android::Tokenizer\",\n   \"record_kind\" : \"class\",\n   \"size\" : 28,\n   \"source_file\" : \"system/core/libutils/include/utils/Tokenizer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"fields\" :\n   [\n    {\n     \"field_name\" : \"buf\",\n     \"referenced_type\" : \"_ZTIA5121_h\"\n    },\n    {\n     \"field_name\" : \"entry\",\n     \"referenced_type\" : \"_ZTI12logger_entry\"\n    }\n   ],\n   \"is_anonymous\" : true,\n   \"linker_set_key\" : \"_ZTIN7log_msgUt_E\",\n   \"name\" : \"log_msg::(anonymous)\",\n   \"record_kind\" : \"union\",\n   \"size\" : 5124,\n   \"source_file\" : \"system/logging/liblog/include_vndk/log/log_read.h\"\n  }\n ],\n \"rvalue_reference_types\" :\n [\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android2spINS_12NativeHandleEEE\",\n   \"name\" : \"android::sp<android::NativeHandle> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_12NativeHandleEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android2spINS_14MessageHandlerEEE\",\n   \"name\" : \"android::sp<android::MessageHandler> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_14MessageHandlerEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android2spINS_20SimpleLooperCallbackEEE\",\n   \"name\" : \"android::sp<android::SimpleLooperCallback> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_20SimpleLooperCallbackEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android2spINS_6LooperEEE\",\n   \"name\" : \"android::sp<android::Looper> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6LooperEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android2spINS_6ThreadEEE\",\n   \"name\" : \"android::sp<android::Thread> &&\",\n   \"referenced_type\" : \"_ZTIN7android2spINS_6ThreadEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/StrongPointer.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"name\" : \"android::base::unique_fd_impl<android::base::DefaultCloser> &&\",\n   \"referenced_type\" : \"_ZTIN7android4base14unique_fd_implINS0_13DefaultCloserEEE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/libbase/include/android-base/unique_fd.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android7FileMapE\",\n   \"name\" : \"android::FileMap &&\",\n   \"referenced_type\" : \"_ZTIN7android7FileMapE\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/include/utils/FileMap.h\"\n  },\n  {\n   \"alignment\" : 4,\n   \"linker_set_key\" : \"_ZTION7android8String16E\",\n   \"name\" : \"android::String16 &&\",\n   \"referenced_type\" : \"_ZTIN7android8String16E\",\n   \"size\" : 4,\n   \"source_file\" : \"system/core/libutils/binder/include/utils/String16.h\"\n  }\n ]\n}\n"
  },
  {
    "path": "libutils/binder/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"system_core_libutils_license\"],\n}\n\ncc_defaults {\n    name: \"libutils_binder_impl_defaults_nodeps\",\n    defaults: [\n        \"libutils_defaults_nodeps\",\n        \"apex-lowest-min-sdk-version\",\n    ],\n    native_bridge_supported: true,\n\n    export_include_dirs: [\"include\"],\n    srcs: [\n        \"Errors.cpp\",\n        \"RefBase.cpp\",\n        \"SharedBuffer.cpp\",\n        \"String16.cpp\",\n        \"String8.cpp\",\n        \"StrongPointer.cpp\",\n        \"Unicode.cpp\",\n        \"VectorImpl.cpp\",\n    ],\n\n    cflags: [\n        \"-Winvalid-offsetof\",\n        \"-Wsequence-point\",\n        \"-Wzero-as-null-pointer-constant\",\n    ],\n\n    apex_available: [\n        \"//apex_available:anyapex\",\n        \"//apex_available:platform\",\n    ],\n\n    afdo: true,\n}\n\ncc_defaults {\n    name: \"libutils_binder_impl_defaults\",\n    defaults: [\n        \"libutils_defaults\",\n        \"libutils_binder_impl_defaults_nodeps\",\n    ],\n}\n\ncc_library {\n    name: \"libutils_binder\",\n    defaults: [\"libutils_binder_impl_defaults\"],\n    cmake_snapshot_supported: false,\n}\n\ncc_library_shared {\n    name: \"libutils_binder_sdk\",\n    defaults: [\"libutils_binder_impl_defaults_nodeps\"],\n    cmake_snapshot_supported: true,\n\n    header_libs: [\n        \"liblog_stub\",\n    ],\n\n    cflags: [\n        \"-DANDROID_LOG_STUB_WEAK_PRINT\",\n        \"-DANDROID_UTILS_CALLSTACK_ENABLED=0\",\n    ],\n}\n\ncc_library {\n    name: \"libutils_binder_test_compile\",\n    defaults: [\"libutils_binder_impl_defaults\"],\n\n    cflags: [\n        \"-DDEBUG_REFS=1\",\n    ],\n\n    visibility: [\":__subpackages__\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_string8\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"String8_fuzz.cpp\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_string16\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"String16_fuzz.cpp\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_vector\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"Vector_fuzz.cpp\"],\n}\n\ncc_fuzz {\n    name: \"libutils_fuzz_refbase\",\n    defaults: [\"libutils_fuzz_defaults\"],\n    srcs: [\"RefBase_fuzz.cpp\"],\n}\n\ncc_test {\n    name: \"libutils_binder_test\",\n    host_supported: true,\n\n    srcs: [\n        \"Errors_test.cpp\",\n        \"SharedBuffer_test.cpp\",\n        \"String16_test.cpp\",\n        \"String8_test.cpp\",\n        \"StrongPointer_test.cpp\",\n        \"Unicode_test.cpp\",\n        \"Vector_test.cpp\",\n    ],\n\n    target: {\n        android: {\n            shared_libs: [\n                \"libbase\",\n                \"libcutils\",\n                \"liblog\",\n                \"liblzma\",\n                \"libutils\", // which includes libutils_binder\n                \"libz\",\n            ],\n        },\n        linux: {\n            srcs: [\n                \"RefBase_test.cpp\",\n            ],\n        },\n        host: {\n            static_libs: [\n                \"libbase\",\n                \"liblog\",\n                \"liblzma\",\n                \"libutils\", // which includes libutils_binder\n            ],\n        },\n    },\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n        \"-Wthread-safety\",\n    ],\n\n    test_suites: [\"device-tests\"],\n}\n\ncc_benchmark {\n    name: \"libutils_binder_benchmark\",\n    srcs: [\"Vector_benchmark.cpp\"],\n    shared_libs: [\"libutils\"],\n}\n"
  },
  {
    "path": "libutils/binder/Errors.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <utils/Errors.h>\n\n#include <string.h>\n\nnamespace android {\n\nstd::string statusToString(status_t s) {\n#define STATUS_CASE(STATUS) \\\n    case STATUS:            \\\n        return #STATUS\n\n    switch (s) {\n        STATUS_CASE(OK);\n        STATUS_CASE(UNKNOWN_ERROR);\n        STATUS_CASE(NO_MEMORY);\n        STATUS_CASE(INVALID_OPERATION);\n        STATUS_CASE(BAD_VALUE);\n        STATUS_CASE(BAD_TYPE);\n        STATUS_CASE(NAME_NOT_FOUND);\n        STATUS_CASE(PERMISSION_DENIED);\n        STATUS_CASE(NO_INIT);\n        STATUS_CASE(ALREADY_EXISTS);\n        STATUS_CASE(DEAD_OBJECT);\n        STATUS_CASE(FAILED_TRANSACTION);\n        STATUS_CASE(BAD_INDEX);\n        STATUS_CASE(NOT_ENOUGH_DATA);\n        STATUS_CASE(WOULD_BLOCK);\n        STATUS_CASE(TIMED_OUT);\n        STATUS_CASE(UNKNOWN_TRANSACTION);\n        STATUS_CASE(FDS_NOT_ALLOWED);\n        STATUS_CASE(UNEXPECTED_NULL);\n#undef STATUS_CASE\n    }\n\n    return std::to_string(s) + \" (\" + strerror(-s) + \")\";\n}\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/binder/Errors_test.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"utils/ErrorsMacros.h\"\n\n#include <android-base/result.h>\n\n#include <gtest/gtest.h>\n\nusing namespace android;\n\nusing android::base::Error;\nusing android::base::Result;\n\nstatus_t success_or_fail(bool success) {\n    if (success)\n        return OK;\n    else\n        return PERMISSION_DENIED;\n}\n\nTEST(errors, unwrap_or_return) {\n    auto f = [](bool success, int* val) -> status_t {\n        OR_RETURN(success_or_fail(success));\n        *val = 10;\n        return OK;\n    };\n\n    int val;\n    status_t s = f(true, &val);\n    EXPECT_EQ(OK, s);\n    EXPECT_EQ(10, val);\n\n    val = 0;  // reset\n    status_t q = f(false, &val);\n    EXPECT_EQ(PERMISSION_DENIED, q);\n    EXPECT_EQ(0, val);\n}\n\nTEST(errors, unwrap_or_return_result) {\n    auto f = [](bool success) -> Result<std::string, StatusT> {\n        OR_RETURN(success_or_fail(success));\n        return \"hello\";\n    };\n\n    auto r = f(true);\n    EXPECT_TRUE(r.ok());\n    EXPECT_EQ(\"hello\", *r);\n\n    auto s = f(false);\n    EXPECT_FALSE(s.ok());\n    EXPECT_EQ(PERMISSION_DENIED, s.error().code());\n    EXPECT_EQ(\"PERMISSION_DENIED\", s.error().message());\n}\n\nTEST(errors, unwrap_or_return_result_int) {\n    auto f = [](bool success) -> Result<int, StatusT> {\n        OR_RETURN(success_or_fail(success));\n        return 10;\n    };\n\n    auto r = f(true);\n    EXPECT_TRUE(r.ok());\n    EXPECT_EQ(10, *r);\n\n    auto s = f(false);\n    EXPECT_FALSE(s.ok());\n    EXPECT_EQ(PERMISSION_DENIED, s.error().code());\n    EXPECT_EQ(\"PERMISSION_DENIED\", s.error().message());\n}\n\nTEST(errors, unwrap_or_fatal) {\n    OR_FATAL(success_or_fail(true));\n\n    EXPECT_DEATH(OR_FATAL(success_or_fail(false)), \"PERMISSION_DENIED\");\n}\n\nTEST(errors, result_in_status) {\n    auto f = [](bool success) -> Result<std::string, StatusT> {\n        if (success)\n            return \"OK\";\n        else\n            return Error<StatusT>(PERMISSION_DENIED) << \"custom error message\";\n    };\n\n    auto g = [&](bool success) -> status_t {\n        std::string val = OR_RETURN(f(success));\n        EXPECT_EQ(\"OK\", val);\n        return OK;\n    };\n\n    status_t a = g(true);\n    EXPECT_EQ(OK, a);\n\n    status_t b = g(false);\n    EXPECT_EQ(PERMISSION_DENIED, b);\n}\n\nTEST(errors, conversion_promotion) {\n    constexpr size_t successVal = 10ull;\n    auto f = [&](bool success) -> Result<size_t, StatusT> {\n        OR_RETURN(success_or_fail(success));\n        return successVal;\n    };\n    auto s = f(true);\n    ASSERT_TRUE(s.ok());\n    EXPECT_EQ(s.value(), successVal);\n    auto r = f(false);\n    EXPECT_TRUE(!r.ok());\n    EXPECT_EQ(PERMISSION_DENIED, r.error().code());\n}\n\nTEST(errors, conversion_promotion_bool) {\n    constexpr size_t successVal = true;\n    auto f = [&](bool success) -> Result<bool, StatusT> {\n        OR_RETURN(success_or_fail(success));\n        return successVal;\n    };\n    auto s = f(true);\n    ASSERT_TRUE(s.ok());\n    EXPECT_EQ(s.value(), successVal);\n    auto r = f(false);\n    EXPECT_TRUE(!r.ok());\n    EXPECT_EQ(PERMISSION_DENIED, r.error().code());\n}\n\nTEST(errors, conversion_promotion_char) {\n    constexpr char successVal = 'a';\n    auto f = [&](bool success) -> Result<unsigned char, StatusT> {\n        OR_RETURN(success_or_fail(success));\n        return successVal;\n    };\n    auto s = f(true);\n    ASSERT_TRUE(s.ok());\n    EXPECT_EQ(s.value(), successVal);\n    auto r = f(false);\n    EXPECT_TRUE(!r.ok());\n    EXPECT_EQ(PERMISSION_DENIED, r.error().code());\n}\n\nstruct IntContainer {\n  // Implicit conversion from int is desired\n  IntContainer(int val) : val_(val) {}\n  int val_;\n};\n\nTEST(errors, conversion_construct) {\n    constexpr int successVal = 10;\n    auto f = [&](bool success) -> Result<IntContainer, StatusT> {\n        OR_RETURN(success_or_fail(success));\n        return successVal;\n    };\n    auto s = f(true);\n    ASSERT_TRUE(s.ok());\n    EXPECT_EQ(s.value().val_, successVal);\n    auto r = f(false);\n    EXPECT_TRUE(!r.ok());\n    EXPECT_EQ(PERMISSION_DENIED, r.error().code());\n}\n"
  },
  {
    "path": "libutils/binder/FuzzFormatTypes.h",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n#include <string>\n\nstatic const std::string kFormatChars = std::string(\"duoxXfFeEgGaAcsp\");\nstatic constexpr int32_t kMaxFormatFlagValue = INT16_MAX;\nenum FormatChar : uint8_t {\n    SIGNED_DECIMAL = 0,\n    UNSIGNED_DECIMAL = 1,\n    UNSIGNED_OCTAL = 2,\n    UNSIGNED_HEX_LOWER = 3,\n    UNSIGNED_HEX_UPPER = 4,\n    // Uppercase/lowercase floating point impacts 'inf', 'infinity', and 'nan'\n    FLOAT_LOWER = 5,\n    FLOAT_UPPER = 6,\n    // Upper/lower impacts the \"e\" in exponents.\n    EXPONENT_LOWER = 7,\n    EXPONENT_UPPER = 8,\n    // %g will use %e or %f, whichever is shortest\n    SHORT_EXP_LOWER = 9,\n    // %G will use %E or %F, whichever is shortest\n    SHORT_EXP_UPPER = 10,\n    HEX_FLOAT_LOWER = 11,\n    HEX_FLOAT_UPPER = 12,\n    CHAR = 13,\n    STRING = 14,\n    POINTER = 15,\n    // Used by libfuzzer\n    kMaxValue = POINTER\n};\n\nbool canApplyFlag(FormatChar formatChar, char modifier) {\n    if (modifier == '#') {\n        return formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||\n               formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||\n               formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||\n               formatChar == SHORT_EXP_UPPER;\n    } else if (modifier == '.') {\n        return formatChar == SIGNED_DECIMAL || formatChar == UNSIGNED_DECIMAL ||\n               formatChar == UNSIGNED_OCTAL || formatChar == UNSIGNED_HEX_LOWER ||\n               formatChar == UNSIGNED_HEX_UPPER || formatChar == FLOAT_LOWER ||\n               formatChar == FLOAT_UPPER || formatChar == SHORT_EXP_LOWER ||\n               formatChar == SHORT_EXP_UPPER || formatChar == STRING;\n    }\n    return true;\n}\n"
  },
  {
    "path": "libutils/binder/RefBase.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"RefBase\"\n// #define LOG_NDEBUG 0\n\n#include <memory>\n#include <mutex>\n\n#include <fcntl.h>\n#include <log/log.h>\n\n#include <utils/RefBase.h>\n#include <utils/String8.h>\n\n#ifndef __unused\n#define __unused __attribute__((__unused__))\n#endif\n\n// Compile with refcounting debugging enabled.\n#ifndef DEBUG_REFS\n#define DEBUG_REFS 0\n#endif\n\n// The following three are ignored unless DEBUG_REFS is set.\n\n// whether ref-tracking is enabled by default, if not, trackMe(true, false)\n// needs to be called explicitly\n#define DEBUG_REFS_ENABLED_BY_DEFAULT 0\n\n// whether callstack are collected (significantly slows things down)\n#define DEBUG_REFS_CALLSTACK_ENABLED 1\n\n// folder where stack traces are saved when DEBUG_REFS is enabled\n// this folder needs to exist and be writable\n#ifdef __ANDROID__\n#define DEBUG_REFS_CALLSTACK_PATH \"/data/debug\"\n#else\n#define DEBUG_REFS_CALLSTACK_PATH \".\"\n#endif\n\n// log all reference counting operations\n#define PRINT_REFS 0\n\n#if !defined(ANDROID_UTILS_CALLSTACK_ENABLED)\n#if defined(__linux__)\n// CallStack is only supported on linux type platforms.\n#define ANDROID_UTILS_CALLSTACK_ENABLED 1\n#else\n#define ANDROID_UTILS_CALLSTACK_ENABLED 0\n#endif  // defined(__linux__)\n#endif  // !defined(ANDROID_UTILS_CALLSTACK_ENABLED)\n\n#if ANDROID_UTILS_CALLSTACK_ENABLED\n#include \"../../include/utils/CallStack.h\"\n#endif\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\n// Observations, invariants, etc:\n\n// By default, obects are destroyed when the last strong reference disappears\n// or, if the object never had a strong reference, when the last weak reference\n// disappears.\n//\n// OBJECT_LIFETIME_WEAK changes this behavior to retain the object\n// unconditionally until the last reference of either kind disappears.  The\n// client ensures that the extendObjectLifetime call happens before the dec\n// call that would otherwise have deallocated the object, or before an\n// attemptIncStrong call that might rely on it.  We do not worry about\n// concurrent changes to the object lifetime.\n//\n// AttemptIncStrong will succeed if the object has a strong reference, or if it\n// has a weak reference and has never had a strong reference.\n// AttemptIncWeak really does succeed only if there is already a WEAK\n// reference, and thus may fail when attemptIncStrong would succeed.\n//\n// mStrong is the strong reference count.  mWeak is the weak reference count.\n// Between calls, and ignoring memory ordering effects, mWeak includes strong\n// references, and is thus >= mStrong.\n//\n// A weakref_impl holds all the information, including both reference counts,\n// required to perform wp<> operations.  Thus these can continue to be performed\n// after the RefBase object has been destroyed.\n//\n// A weakref_impl is allocated as the value of mRefs in a RefBase object on\n// construction.\n// In the OBJECT_LIFETIME_STRONG case, it is normally deallocated in decWeak,\n// and hence lives as long as the last weak reference. (It can also be\n// deallocated in the RefBase destructor iff the strong reference count was\n// never incremented and the weak count is zero, e.g.  if the RefBase object is\n// explicitly destroyed without decrementing the strong count.  This should be\n// avoided.) In this case, the RefBase destructor should be invoked from\n// decStrong.\n// In the OBJECT_LIFETIME_WEAK case, the weakref_impl is always deallocated in\n// the RefBase destructor, which is always invoked by decWeak. DecStrong\n// explicitly avoids the deletion in this case.\n//\n// Memory ordering:\n// The client must ensure that every inc() call, together with all other\n// accesses to the object, happens before the corresponding dec() call.\n//\n// We try to keep memory ordering constraints on atomics as weak as possible,\n// since memory fences or ordered memory accesses are likely to be a major\n// performance cost for this code. All accesses to mStrong, mWeak, and mFlags\n// explicitly relax memory ordering in some way.\n//\n// The only operations that are not memory_order_relaxed are reference count\n// decrements. All reference count decrements are release operations.  In\n// addition, the final decrement leading the deallocation is followed by an\n// acquire fence, which we can view informally as also turning it into an\n// acquire operation.  (See 29.8p4 [atomics.fences] for details. We could\n// alternatively use acq_rel operations for all decrements. This is probably\n// slower on most current (2016) hardware, especially on ARMv7, but that may\n// not be true indefinitely.)\n//\n// This convention ensures that the second-to-last decrement synchronizes with\n// (in the language of 1.10 in the C++ standard) the final decrement of a\n// reference count. Since reference counts are only updated using atomic\n// read-modify-write operations, this also extends to any earlier decrements.\n// (See \"release sequence\" in 1.10.)\n//\n// Since all operations on an object happen before the corresponding reference\n// count decrement, and all reference count decrements happen before the final\n// one, we are guaranteed that all other object accesses happen before the\n// object is destroyed.\n\n\n#define INITIAL_STRONG_VALUE (1<<28)\n\n#define MAX_COUNT 0xfffff\n\n// Test whether the argument is a clearly invalid strong reference count.\n// Used only for error checking on the value before an atomic decrement.\n// Intended to be very cheap.\n// Note that we cannot just check for excess decrements by comparing to zero\n// since the object would be deallocated before that.\n#define BAD_STRONG(c) \\\n        ((c) == 0 || ((c) & (~(MAX_COUNT | INITIAL_STRONG_VALUE))) != 0)\n\n// Same for weak counts.\n#define BAD_WEAK(c) ((c) == 0 || ((c) & (~MAX_COUNT)) != 0)\n\n// name kept because prebuilts used to use it from inlining sp<> code\nvoid sp_report_stack_pointer() { LOG_ALWAYS_FATAL(\"RefBase used with stack pointer argument\"); }\n\n// Check whether address is definitely on the calling stack.  We actually check whether it is on\n// the same 4K page as the frame pointer.\n//\n// Assumptions:\n// - Pages are never smaller than 4K (MIN_PAGE_SIZE)\n// - Malloced memory never shares a page with a stack.\n//\n// It does not appear safe to broaden this check to include adjacent pages; apparently this code\n// is used in environments where there may not be a guard page below (at higher addresses than)\n// the bottom of the stack.\nstatic void check_not_on_stack(const void* ptr) {\n    static constexpr int MIN_PAGE_SIZE = 0x1000;  // 4K. Safer than including sys/user.h.\n    static constexpr uintptr_t MIN_PAGE_MASK = ~static_cast<uintptr_t>(MIN_PAGE_SIZE - 1);\n    uintptr_t my_frame_address =\n            reinterpret_cast<uintptr_t>(__builtin_frame_address(0 /* this frame */));\n    if (((reinterpret_cast<uintptr_t>(ptr) ^ my_frame_address) & MIN_PAGE_MASK) == 0) {\n        sp_report_stack_pointer();\n    }\n}\n\n// ---------------------------------------------------------------------------\n\nclass RefBase::weakref_impl : public RefBase::weakref_type\n{\npublic:\n    std::atomic<int32_t>    mStrong;\n    std::atomic<int32_t>    mWeak;\n    RefBase* const          mBase;\n    std::atomic<int32_t>    mFlags;\n\n#if !DEBUG_REFS\n\n    explicit weakref_impl(RefBase* base)\n        : mStrong(INITIAL_STRONG_VALUE)\n        , mWeak(0)\n        , mBase(base)\n        , mFlags(OBJECT_LIFETIME_STRONG)\n    {\n    }\n\n    void addStrongRef(const void* /*id*/) { }\n    void removeStrongRef(const void* /*id*/) { }\n    void renameStrongRefId(const void* /*old_id*/, const void* /*new_id*/) { }\n    void addWeakRef(const void* /*id*/) { }\n    void removeWeakRef(const void* /*id*/) { }\n    void renameWeakRefId(const void* /*old_id*/, const void* /*new_id*/) { }\n    void printRefs() const { }\n    void trackMe(bool, bool) { }\n\n#else\n\n    weakref_impl(RefBase* base)\n        : mStrong(INITIAL_STRONG_VALUE)\n        , mWeak(0)\n        , mBase(base)\n        , mFlags(OBJECT_LIFETIME_STRONG)\n        , mStrongRefs(NULL)\n        , mWeakRefs(NULL)\n        , mTrackEnabled(!!DEBUG_REFS_ENABLED_BY_DEFAULT)\n        , mRetain(false)\n    {\n    }\n\n    ~weakref_impl()\n    {\n        bool dumpStack = false;\n        if (!mRetain && mStrongRefs != NULL) {\n            dumpStack = true;\n            ALOGE(\"Strong references remain:\");\n            ref_entry* refs = mStrongRefs;\n            while (refs) {\n                char inc = refs->ref >= 0 ? '+' : '-';\n                ALOGD(\"\\t%c ID %p (ref %d):\", inc, refs->id, refs->ref);\n#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED\n                CallStack::logStack(LOG_TAG, refs->stack.get());\n#endif\n                refs = refs->next;\n            }\n        }\n\n        if (!mRetain && mWeakRefs != NULL) {\n            dumpStack = true;\n            ALOGE(\"Weak references remain!\");\n            ref_entry* refs = mWeakRefs;\n            while (refs) {\n                char inc = refs->ref >= 0 ? '+' : '-';\n                ALOGD(\"\\t%c ID %p (ref %d):\", inc, refs->id, refs->ref);\n#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED\n                CallStack::logStack(LOG_TAG, refs->stack.get());\n#endif\n                refs = refs->next;\n            }\n        }\n        if (dumpStack) {\n            ALOGE(\"above errors at:\");\n#if ANDROID_UTILS_CALLSTACK_ENABLED\n            CallStack::logStack(LOG_TAG);\n#endif\n        }\n    }\n\n    void addStrongRef(const void* id) {\n        //ALOGD_IF(mTrackEnabled,\n        //        \"addStrongRef: RefBase=%p, id=%p\", mBase, id);\n        addRef(&mStrongRefs, id, mStrong.load(std::memory_order_relaxed));\n    }\n\n    void removeStrongRef(const void* id) {\n        //ALOGD_IF(mTrackEnabled,\n        //        \"removeStrongRef: RefBase=%p, id=%p\", mBase, id);\n        if (!mRetain) {\n            removeRef(&mStrongRefs, id);\n        } else {\n            addRef(&mStrongRefs, id, -mStrong.load(std::memory_order_relaxed));\n        }\n    }\n\n    void renameStrongRefId(const void* old_id, const void* new_id) {\n        //ALOGD_IF(mTrackEnabled,\n        //        \"renameStrongRefId: RefBase=%p, oid=%p, nid=%p\",\n        //        mBase, old_id, new_id);\n        renameRefsId(mStrongRefs, old_id, new_id);\n    }\n\n    void addWeakRef(const void* id) {\n        addRef(&mWeakRefs, id, mWeak.load(std::memory_order_relaxed));\n    }\n\n    void removeWeakRef(const void* id) {\n        if (!mRetain) {\n            removeRef(&mWeakRefs, id);\n        } else {\n            addRef(&mWeakRefs, id, -mWeak.load(std::memory_order_relaxed));\n        }\n    }\n\n    void renameWeakRefId(const void* old_id, const void* new_id) {\n        renameRefsId(mWeakRefs, old_id, new_id);\n    }\n\n    void trackMe(bool track, bool retain) {\n        mTrackEnabled = track;\n        mRetain = retain;\n    }\n\n    void printRefs() const\n    {\n        String8 text;\n\n        {\n            std::lock_guard<std::mutex> _l(mMutex);\n            char buf[128];\n            snprintf(buf, sizeof(buf),\n                     \"Strong references on RefBase %p (weakref_type %p):\\n\",\n                     mBase, this);\n            text.append(buf);\n            printRefsLocked(&text, mStrongRefs);\n            snprintf(buf, sizeof(buf),\n                     \"Weak references on RefBase %p (weakref_type %p):\\n\",\n                     mBase, this);\n            text.append(buf);\n            printRefsLocked(&text, mWeakRefs);\n        }\n\n        {\n            char name[100];\n            snprintf(name, sizeof(name), DEBUG_REFS_CALLSTACK_PATH \"/%p.stack\",\n                     this);\n            int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 0644);\n            if (rc >= 0) {\n                (void)write(rc, text.c_str(), text.length());\n                close(rc);\n                ALOGI(\"STACK TRACE for %p saved in %s\", this, name);\n            }\n            else ALOGE(\"FAILED TO PRINT STACK TRACE for %p in %s: %s\", this,\n                      name, strerror(errno));\n        }\n    }\n\nprivate:\n    struct ref_entry\n    {\n        ref_entry* next;\n        const void* id;\n#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED\n        CallStack::CallStackUPtr stack;\n#endif\n        int32_t ref;\n    };\n\n    void addRef(ref_entry** refs, const void* id, int32_t mRef)\n    {\n        if (mTrackEnabled) {\n            std::lock_guard<std::mutex> _l(mMutex);\n\n            ref_entry* ref = new ref_entry;\n            // Reference count at the time of the snapshot, but before the\n            // update.  Positive value means we increment, negative--we\n            // decrement the reference count.\n            ref->ref = mRef;\n            ref->id = id;\n#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED\n            ref->stack = CallStack::getCurrent(2);\n#endif\n            ref->next = *refs;\n            *refs = ref;\n        }\n    }\n\n    void removeRef(ref_entry** refs, const void* id)\n    {\n        if (mTrackEnabled) {\n            std::lock_guard<std::mutex> _l(mMutex);\n\n            ref_entry* const head = *refs;\n            ref_entry* ref = head;\n            while (ref != NULL) {\n                if (ref->id == id) {\n                    *refs = ref->next;\n                    delete ref;\n                    return;\n                }\n                refs = &ref->next;\n                ref = *refs;\n            }\n\n            ALOGE(\"RefBase: removing id %p on RefBase %p\"\n                    \"(weakref_type %p) that doesn't exist!\",\n                    id, mBase, this);\n\n            ref = head;\n            while (ref) {\n                char inc = ref->ref >= 0 ? '+' : '-';\n                ALOGD(\"\\t%c ID %p (ref %d):\", inc, ref->id, ref->ref);\n                ref = ref->next;\n            }\n\n#if ANDROID_UTILS_CALLSTACK_ENABLED\n            CallStack::logStack(LOG_TAG);\n#endif\n        }\n    }\n\n    void renameRefsId(ref_entry* r, const void* old_id, const void* new_id)\n    {\n        if (mTrackEnabled) {\n            std::lock_guard<std::mutex> _l(mMutex);\n            ref_entry* ref = r;\n            while (ref != NULL) {\n                if (ref->id == old_id) {\n                    ref->id = new_id;\n                }\n                ref = ref->next;\n            }\n        }\n    }\n\n    void printRefsLocked(String8* out, const ref_entry* refs) const\n    {\n        char buf[128];\n        while (refs) {\n            char inc = refs->ref >= 0 ? '+' : '-';\n            snprintf(buf, sizeof(buf), \"\\t%c ID %p (ref %d):\\n\",\n                     inc, refs->id, refs->ref);\n            out->append(buf);\n#if DEBUG_REFS_CALLSTACK_ENABLED && ANDROID_UTILS_CALLSTACK_ENABLED\n            out->append(CallStack::stackToString(\"\\t\\t\", refs->stack.get()));\n#else\n            out->append(\"\\t\\t(call stacks disabled)\");\n#endif\n            refs = refs->next;\n        }\n    }\n\n    mutable std::mutex mMutex;\n    ref_entry* mStrongRefs;\n    ref_entry* mWeakRefs;\n\n    bool mTrackEnabled;\n    // Collect stack traces on addref and removeref, instead of deleting the stack references\n    // on removeref that match the address ones.\n    bool mRetain;\n\n#endif\n};\n\n// ---------------------------------------------------------------------------\n\nvoid RefBase::incStrong(const void* id) const\n{\n    weakref_impl* const refs = mRefs;\n    refs->incWeak(id);\n\n    refs->addStrongRef(id);\n    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);\n    ALOG_ASSERT(c > 0, \"incStrong() called on %p after last strong ref\", refs);\n#if PRINT_REFS\n    ALOGD(\"incStrong of %p from %p: cnt=%d\\n\", this, id, c);\n#endif\n    if (c != INITIAL_STRONG_VALUE)  {\n        return;\n    }\n\n    check_not_on_stack(this);\n\n    int32_t old __unused = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE, std::memory_order_relaxed);\n    // A decStrong() must still happen after us.\n    ALOG_ASSERT(old > INITIAL_STRONG_VALUE, \"0x%x too small\", old);\n    refs->mBase->onFirstRef();\n}\n\nvoid RefBase::incStrongRequireStrong(const void* id) const {\n    weakref_impl* const refs = mRefs;\n    refs->incWeak(id);\n\n    refs->addStrongRef(id);\n    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);\n\n    LOG_ALWAYS_FATAL_IF(c <= 0 || c == INITIAL_STRONG_VALUE,\n                        \"incStrongRequireStrong() called on %p which isn't already owned\", refs);\n#if PRINT_REFS\n    ALOGD(\"incStrong (requiring strong) of %p from %p: cnt=%d\\n\", this, id, c);\n#endif\n}\n\nvoid RefBase::decStrong(const void* id) const\n{\n    weakref_impl* const refs = mRefs;\n    refs->removeStrongRef(id);\n    const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);\n#if PRINT_REFS\n    ALOGD(\"decStrong of %p from %p: cnt=%d\\n\", this, id, c);\n#endif\n    LOG_ALWAYS_FATAL_IF(\n            BAD_STRONG(c),\n            \"decStrong() called on %p too many times, possible memory corruption. Consider \"\n            \"compiling with ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION for better errors\",\n            refs);\n    if (c == 1) {\n        std::atomic_thread_fence(std::memory_order_acquire);\n        refs->mBase->onLastStrongRef(id);\n        int32_t flags = refs->mFlags.load(std::memory_order_relaxed);\n        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {\n            delete this;\n            // The destructor does not delete refs in this case.\n        }\n    }\n    // Note that even with only strong reference operations, the thread\n    // deallocating this may not be the same as the thread deallocating refs.\n    // That's OK: all accesses to this happen before its deletion here,\n    // and all accesses to refs happen before its deletion in the final decWeak.\n    // The destructor can safely access mRefs because either it's deleting\n    // mRefs itself, or it's running entirely before the final mWeak decrement.\n    //\n    // Since we're doing atomic loads of `flags`, the static analyzer assumes\n    // they can change between `delete this;` and `refs->decWeak(id);`. This is\n    // not the case. The analyzer may become more okay with this patten when\n    // https://bugs.llvm.org/show_bug.cgi?id=34365 gets resolved. NOLINTNEXTLINE\n    refs->decWeak(id);\n}\n\nvoid RefBase::forceIncStrong(const void* id) const\n{\n    // Allows initial mStrong of 0 in addition to INITIAL_STRONG_VALUE.\n    // TODO: Better document assumptions.\n    weakref_impl* const refs = mRefs;\n    refs->incWeak(id);\n\n    refs->addStrongRef(id);\n    const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);\n    ALOG_ASSERT(c >= 0, \"forceIncStrong called on %p after ref count underflow\",\n               refs);\n#if PRINT_REFS\n    ALOGD(\"forceIncStrong of %p from %p: cnt=%d\\n\", this, id, c);\n#endif\n\n    switch (c) {\n    case INITIAL_STRONG_VALUE:\n        refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,\n                std::memory_order_relaxed);\n        [[fallthrough]];\n    case 0:\n        refs->mBase->onFirstRef();\n    }\n}\n\nint32_t RefBase::getStrongCount() const\n{\n    // Debugging only; No memory ordering guarantees.\n    return mRefs->mStrong.load(std::memory_order_relaxed);\n}\n\nRefBase* RefBase::weakref_type::refBase() const\n{\n    return static_cast<const weakref_impl*>(this)->mBase;\n}\n\nvoid RefBase::weakref_type::incWeak(const void* id)\n{\n    weakref_impl* const impl = static_cast<weakref_impl*>(this);\n    impl->addWeakRef(id);\n    const int32_t c __unused = impl->mWeak.fetch_add(1,\n            std::memory_order_relaxed);\n    ALOG_ASSERT(c >= 0, \"incWeak called on %p after last weak ref\", this);\n}\n\nvoid RefBase::weakref_type::incWeakRequireWeak(const void* id)\n{\n    weakref_impl* const impl = static_cast<weakref_impl*>(this);\n    impl->addWeakRef(id);\n    const int32_t c __unused = impl->mWeak.fetch_add(1,\n            std::memory_order_relaxed);\n    LOG_ALWAYS_FATAL_IF(c <= 0, \"incWeakRequireWeak called on %p which has no weak refs\", this);\n}\n\nvoid RefBase::weakref_type::decWeak(const void* id)\n{\n    weakref_impl* const impl = static_cast<weakref_impl*>(this);\n    impl->removeWeakRef(id);\n    const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);\n    LOG_ALWAYS_FATAL_IF(\n            BAD_WEAK(c),\n            \"decWeak called on %p too many times, possible memory corruption. Consider compiling \"\n            \"with ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION for better errors\",\n            this);\n    if (c != 1) return;\n    atomic_thread_fence(std::memory_order_acquire);\n\n    int32_t flags = impl->mFlags.load(std::memory_order_relaxed);\n    if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {\n        // This is the regular lifetime case. The object is destroyed\n        // when the last strong reference goes away. Since weakref_impl\n        // outlives the object, it is not destroyed in the dtor, and\n        // we'll have to do it here.\n        if (impl->mStrong.load(std::memory_order_relaxed)\n                == INITIAL_STRONG_VALUE) {\n            // Decrementing a weak count to zero when object never had a strong\n            // reference.  We assume it acquired a weak reference early, e.g.\n            // in the constructor, and will eventually be properly destroyed,\n            // usually via incrementing and decrementing the strong count.\n            // Thus we no longer do anything here.  We log this case, since it\n            // seems to be extremely rare, and should not normally occur. We\n            // used to deallocate mBase here, so this may now indicate a leak.\n            ALOGW(\"RefBase: Object at %p lost last weak reference \"\n                    \"before it had a strong reference\", impl->mBase);\n        } else {\n            // ALOGV(\"Freeing refs %p of old RefBase %p\\n\", this, impl->mBase);\n            delete impl;\n        }\n    } else {\n        // This is the OBJECT_LIFETIME_WEAK case. The last weak-reference\n        // is gone, we can destroy the object.\n        impl->mBase->onLastWeakRef(id);\n        delete impl->mBase;\n    }\n}\n\nbool RefBase::weakref_type::attemptIncStrong(const void* id)\n{\n    incWeak(id);\n\n    weakref_impl* const impl = static_cast<weakref_impl*>(this);\n    int32_t curCount = impl->mStrong.load(std::memory_order_relaxed);\n\n    ALOG_ASSERT(curCount >= 0,\n            \"attemptIncStrong called on %p after underflow\", this);\n\n    while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) {\n        // we're in the easy/common case of promoting a weak-reference\n        // from an existing strong reference.\n        if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,\n                std::memory_order_relaxed)) {\n            break;\n        }\n        // the strong count has changed on us, we need to re-assert our\n        // situation. curCount was updated by compare_exchange_weak.\n    }\n\n    if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) {\n        // we're now in the harder case of either:\n        // - there never was a strong reference on us\n        // - or, all strong references have been released\n        int32_t flags = impl->mFlags.load(std::memory_order_relaxed);\n        if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {\n            // this object has a \"normal\" life-time, i.e.: it gets destroyed\n            // when the last strong reference goes away\n            if (curCount <= 0) {\n                // the last strong-reference got released, the object cannot\n                // be revived.\n                decWeak(id);\n                return false;\n            }\n\n            // here, curCount == INITIAL_STRONG_VALUE, which means\n            // there never was a strong-reference, so we can try to\n            // promote this object; we need to do that atomically.\n            while (curCount > 0) {\n                if (impl->mStrong.compare_exchange_weak(curCount, curCount+1,\n                        std::memory_order_relaxed)) {\n                    break;\n                }\n                // the strong count has changed on us, we need to re-assert our\n                // situation (e.g.: another thread has inc/decStrong'ed us)\n                // curCount has been updated.\n            }\n\n            if (curCount <= 0) {\n                // promote() failed, some other thread destroyed us in the\n                // meantime (i.e.: strong count reached zero).\n                decWeak(id);\n                return false;\n            }\n        } else {\n            // this object has an \"extended\" life-time, i.e.: it can be\n            // revived from a weak-reference only.\n            // Ask the object's implementation if it agrees to be revived\n            if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) {\n                // it didn't so give-up.\n                decWeak(id);\n                return false;\n            }\n            // grab a strong-reference, which is always safe due to the\n            // extended life-time.\n            curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);\n            // If the strong reference count has already been incremented by\n            // someone else, the implementor of onIncStrongAttempted() is holding\n            // an unneeded reference.  So call onLastStrongRef() here to remove it.\n            // (No, this is not pretty.)  Note that we MUST NOT do this if we\n            // are in fact acquiring the first reference.\n            if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) {\n                impl->mBase->onLastStrongRef(id);\n            }\n        }\n    }\n\n    impl->addStrongRef(id);\n\n#if PRINT_REFS\n    ALOGD(\"attemptIncStrong of %p from %p: cnt=%d\\n\", this, id, curCount);\n#endif\n\n    // curCount is the value of mStrong before we incremented it.\n    // Now we need to fix-up the count if it was INITIAL_STRONG_VALUE.\n    // This must be done safely, i.e.: handle the case where several threads\n    // were here in attemptIncStrong().\n    // curCount > INITIAL_STRONG_VALUE is OK, and can happen if we're doing\n    // this in the middle of another incStrong.  The subtraction is handled\n    // by the thread that started with INITIAL_STRONG_VALUE.\n    if (curCount == INITIAL_STRONG_VALUE) {\n        impl->mStrong.fetch_sub(INITIAL_STRONG_VALUE,\n                std::memory_order_relaxed);\n    }\n\n    return true;\n}\n\nbool RefBase::weakref_type::attemptIncWeak(const void* id)\n{\n    weakref_impl* const impl = static_cast<weakref_impl*>(this);\n\n    int32_t curCount = impl->mWeak.load(std::memory_order_relaxed);\n    ALOG_ASSERT(curCount >= 0, \"attemptIncWeak called on %p after underflow\",\n               this);\n    while (curCount > 0) {\n        if (impl->mWeak.compare_exchange_weak(curCount, curCount+1,\n                std::memory_order_relaxed)) {\n            break;\n        }\n        // curCount has been updated.\n    }\n\n    if (curCount > 0) {\n        impl->addWeakRef(id);\n    }\n\n    return curCount > 0;\n}\n\nint32_t RefBase::weakref_type::getWeakCount() const\n{\n    // Debug only!\n    return static_cast<const weakref_impl*>(this)->mWeak\n            .load(std::memory_order_relaxed);\n}\n\nvoid RefBase::weakref_type::printRefs() const\n{\n    static_cast<const weakref_impl*>(this)->printRefs();\n}\n\nvoid RefBase::weakref_type::trackMe(bool enable, bool retain)\n{\n    static_cast<weakref_impl*>(this)->trackMe(enable, retain);\n}\n\nRefBase::weakref_type* RefBase::createWeak(const void* id) const\n{\n    mRefs->incWeak(id);\n    return mRefs;\n}\n\nRefBase::weakref_type* RefBase::getWeakRefs() const\n{\n    return mRefs;\n}\n\nRefBase::RefBase()\n    : mRefs(new weakref_impl(this))\n{\n}\n\nRefBase::~RefBase()\n{\n    int32_t flags = mRefs->mFlags.load(std::memory_order_relaxed);\n    // Life-time of this object is extended to WEAK, in\n    // which case weakref_impl doesn't out-live the object and we\n    // can free it now.\n    if ((flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK) {\n        // It's possible that the weak count is not 0 if the object\n        // re-acquired a weak reference in its destructor\n        if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {\n            delete mRefs;\n        }\n    } else {\n        int32_t strongs = mRefs->mStrong.load(std::memory_order_relaxed);\n\n        if (strongs == INITIAL_STRONG_VALUE) {\n            // We never acquired a strong reference on this object.\n\n            // It would be nice to make this fatal, but many places use RefBase on the stack.\n            // However, this is dangerous because it's also common for code to use the\n            // sp<T>(T*) constructor, assuming that if the object is around, it is already\n            // owned by an sp<>.\n            ALOGW(\"RefBase: Explicit destruction, weak count = %d (in %p). Use sp<> to manage this \"\n                  \"object. Note - if weak count is 0, this leaks mRefs (weakref_impl).\",\n                  mRefs->mWeak.load(), this);\n\n#if ANDROID_UTILS_CALLSTACK_ENABLED\n            CallStack::logStack(LOG_TAG);\n#endif\n        } else if (strongs != 0) {\n            LOG_ALWAYS_FATAL(\"RefBase: object %p with strong count %d deleted. Double owned?\", this,\n                             strongs);\n        }\n    }\n    // For debugging purposes, clear mRefs.  Ineffective against outstanding wp's.\n    const_cast<weakref_impl*&>(mRefs) = nullptr;\n}\n\nvoid RefBase::extendObjectLifetime(int32_t mode)\n{\n    check_not_on_stack(this);\n\n    // Must be happens-before ordered with respect to construction or any\n    // operation that could destroy the object.\n    mRefs->mFlags.fetch_or(mode, std::memory_order_relaxed);\n}\n\nvoid RefBase::onFirstRef()\n{\n}\n\nvoid RefBase::onLastStrongRef(const void* /*id*/)\n{\n}\n\nbool RefBase::onIncStrongAttempted(uint32_t flags, const void* /*id*/)\n{\n    return (flags&FIRST_INC_STRONG) ? true : false;\n}\n\nvoid RefBase::onLastWeakRef(const void* /*id*/)\n{\n}\n\n// ---------------------------------------------------------------------------\n\n#if DEBUG_REFS\nvoid RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) {\n    for (size_t i=0 ; i<n ; i++) {\n        renamer(i);\n    }\n}\n#else\nvoid RefBase::renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }\n#endif\n\nvoid RefBase::renameRefId(weakref_type* ref,\n        const void* old_id, const void* new_id) {\n    weakref_impl* const impl = static_cast<weakref_impl*>(ref);\n    impl->renameStrongRefId(old_id, new_id);\n    impl->renameWeakRefId(old_id, new_id);\n}\n\nvoid RefBase::renameRefId(RefBase* ref,\n        const void* old_id, const void* new_id) {\n    ref->mRefs->renameStrongRefId(old_id, new_id);\n    ref->mRefs->renameWeakRefId(old_id, new_id);\n}\n\n}; // namespace android\n"
  },
  {
    "path": "libutils/binder/RefBase_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"RefBaseFuzz\"\n\n#include <thread>\n\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"log/log.h\"\n#include \"utils/RWLock.h\"\n#include \"utils/RefBase.h\"\n#include \"utils/StrongPointer.h\"\n\nusing android::RefBase;\nusing android::RWLock;\nusing android::sp;\nusing android::wp;\n\nstatic constexpr int kMaxOperations = 100;\nstatic constexpr int kMaxThreads = 10;\nstruct RefBaseSubclass : public RefBase {\n  public:\n    RefBaseSubclass(bool* deletedCheck, RWLock& deletedMtx)\n        : mDeleted(deletedCheck), mRwLock(deletedMtx) {\n        RWLock::AutoWLock lock(mRwLock);\n        *mDeleted = false;\n        extendObjectLifetime(OBJECT_LIFETIME_WEAK);\n    }\n\n    virtual ~RefBaseSubclass() {\n        RWLock::AutoWLock lock(mRwLock);\n        *mDeleted = true;\n    }\n\n  private:\n    bool* mDeleted;\n    android::RWLock& mRwLock;\n};\n\n// A thread-specific state object for ref\nstruct RefThreadState {\n    size_t strongCount = 0;\n    size_t weakCount = 0;\n};\n\nRWLock gRefDeletedLock;\nbool gRefDeleted = false;\nbool gHasModifiedRefs = false;\nRefBaseSubclass* ref;\nRefBase::weakref_type* weakRefs;\n\n// These operations don't need locks as they explicitly check per-thread counts before running\n// they also have the potential to write to gRefDeleted, so must not be locked.\nconst std::vector<std::function<void(RefThreadState*)>> kUnlockedOperations = {\n        [](RefThreadState* refState) -> void {\n            if (refState->strongCount > 0) {\n                ref->decStrong(nullptr);\n                gHasModifiedRefs = true;\n                refState->strongCount--;\n            }\n        },\n        [](RefThreadState* refState) -> void {\n            if (refState->weakCount > 0) {\n                weakRefs->decWeak(nullptr);\n                gHasModifiedRefs = true;\n                refState->weakCount--;\n            }\n        },\n};\n\nconst std::vector<std::function<void(RefThreadState*)>> kMaybeLockedOperations = {\n        // Read-only operations\n        [](RefThreadState*) -> void { ref->getStrongCount(); },\n        [](RefThreadState*) -> void { weakRefs->getWeakCount(); },\n        [](RefThreadState*) -> void { ref->printRefs(); },\n\n        // Read/write operations\n        [](RefThreadState* refState) -> void {\n            ref->incStrong(nullptr);\n            gHasModifiedRefs = true;\n            refState->strongCount++;\n        },\n        [](RefThreadState* refState) -> void {\n            ref->forceIncStrong(nullptr);\n            gHasModifiedRefs = true;\n            refState->strongCount++;\n        },\n        [](RefThreadState* refState) -> void {\n            ref->createWeak(nullptr);\n            gHasModifiedRefs = true;\n            refState->weakCount++;\n        },\n        [](RefThreadState* refState) -> void {\n            // This will increment weak internally, then attempt to\n            // promote it to strong. If it fails, it decrements weak.\n            // If it succeeds, the weak is converted to strong.\n            // Both cases net no weak reference change.\n            if (weakRefs->attemptIncStrong(nullptr)) {\n                refState->strongCount++;\n                gHasModifiedRefs = true;\n            }\n        },\n        [](RefThreadState* refState) -> void {\n            if (weakRefs->attemptIncWeak(nullptr)) {\n                refState->weakCount++;\n                gHasModifiedRefs = true;\n            }\n        },\n        [](RefThreadState* refState) -> void {\n            weakRefs->incWeak(nullptr);\n            gHasModifiedRefs = true;\n            refState->weakCount++;\n        },\n};\n\nvoid loop(const std::vector<uint8_t>& fuzzOps) {\n    RefThreadState state;\n    uint8_t lockedOpSize = kMaybeLockedOperations.size();\n    uint8_t totalOperationTypes = lockedOpSize + kUnlockedOperations.size();\n    for (auto op : fuzzOps) {\n        auto opVal = op % totalOperationTypes;\n        if (opVal >= lockedOpSize) {\n            kUnlockedOperations[opVal % lockedOpSize](&state);\n        } else {\n            // We only need to lock if we have no strong or weak count\n            bool shouldLock = state.strongCount == 0 && state.weakCount == 0;\n            if (shouldLock) {\n                gRefDeletedLock.readLock();\n                // If ref has deleted itself, we can no longer fuzz on this thread.\n                if (gRefDeleted) {\n                    // Unlock since we're exiting the loop here.\n                    gRefDeletedLock.unlock();\n                    return;\n                }\n            }\n            // Execute the locked operation\n            kMaybeLockedOperations[opVal](&state);\n            // Unlock if we locked.\n            if (shouldLock) {\n                gRefDeletedLock.unlock();\n            }\n        }\n    }\n\n    // Instead of explicitly freeing this, we're going to remove our weak and\n    // strong references.\n    for (; state.weakCount > 0; state.weakCount--) {\n        weakRefs->decWeak(nullptr);\n    }\n\n    // Clean up any strong references\n    for (; state.strongCount > 0; state.strongCount--) {\n        ref->decStrong(nullptr);\n    }\n}\n\nvoid spawnThreads(FuzzedDataProvider* dataProvider) {\n    std::vector<std::thread> threads = std::vector<std::thread>();\n\n    // Get the number of threads to generate\n    uint8_t count = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxThreads);\n    // Generate threads\n    for (uint8_t i = 0; i < count; i++) {\n        uint8_t opCount = dataProvider->ConsumeIntegralInRange<uint8_t>(1, kMaxOperations);\n        std::vector<uint8_t> threadOperations = dataProvider->ConsumeBytes<uint8_t>(opCount);\n        std::thread tmpThread = std::thread(loop, threadOperations);\n        threads.push_back(std::move(tmpThread));\n    }\n\n    for (auto& th : threads) {\n        th.join();\n    }\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    gHasModifiedRefs = false;\n    ref = new RefBaseSubclass(&gRefDeleted, gRefDeletedLock);\n    weakRefs = ref->getWeakRefs();\n    // Since we are modifying flags, (flags & OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_WEAK\n    // is true. The destructor for RefBase should clean up weakrefs because of this.\n    FuzzedDataProvider dataProvider(data, size);\n    spawnThreads(&dataProvider);\n    LOG_ALWAYS_FATAL_IF(!gHasModifiedRefs && gRefDeleted, \"ref(%p) was prematurely deleted!\", ref);\n    // We need to explicitly delete this object\n    // if no refs have been added or deleted.\n    if (!gHasModifiedRefs && !gRefDeleted) {\n        delete ref;\n    }\n    LOG_ALWAYS_FATAL_IF(gHasModifiedRefs && !gRefDeleted,\n                        \"ref(%p) should be deleted, is it leaking?\", ref);\n    return 0;\n}\n"
  },
  {
    "path": "libutils/binder/RefBase_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include <utils/StrongPointer.h>\n#include <utils/RefBase.h>\n\n#include <thread>\n#include <atomic>\n#include <sched.h>\n#include <errno.h>\n\n// Enhanced version of StrongPointer_test, but using RefBase underneath.\n\nusing namespace android;\n\nstatic constexpr int NITERS = 500000;\n\nstatic constexpr int INITIAL_STRONG_VALUE = 1 << 28;  // Mirroring RefBase definition.\n\nclass Foo : public RefBase {\npublic:\n    Foo(bool* deleted_check) : mDeleted(deleted_check) {\n        *mDeleted = false;\n    }\n\n    ~Foo() {\n        *mDeleted = true;\n    }\nprivate:\n    bool* mDeleted;\n};\n\n// A version of Foo that ensures that all objects are allocated at the same\n// address. No more than one can be allocated at a time. Thread-hostile.\nclass FooFixedAlloc : public RefBase {\npublic:\n    static void* operator new(size_t size) {\n        if (mAllocCount != 0) {\n            abort();\n        }\n        mAllocCount = 1;\n        if (theMemory == nullptr) {\n            theMemory = malloc(size);\n        }\n        return theMemory;\n    }\n\n    static void operator delete(void *p) {\n        if (mAllocCount != 1 || p != theMemory) {\n            abort();\n        }\n        mAllocCount = 0;\n    }\n\n    FooFixedAlloc(bool* deleted_check) : mDeleted(deleted_check) {\n        *mDeleted = false;\n    }\n\n    ~FooFixedAlloc() {\n        *mDeleted = true;\n    }\nprivate:\n    bool* mDeleted;\n    static int mAllocCount;\n    static void* theMemory;\n};\n\nint FooFixedAlloc::mAllocCount(0);\nvoid* FooFixedAlloc::theMemory(nullptr);\n\nTEST(RefBase, StrongMoves) {\n    bool isDeleted;\n    Foo* foo = new Foo(&isDeleted);\n    ASSERT_EQ(INITIAL_STRONG_VALUE, foo->getStrongCount());\n    ASSERT_FALSE(isDeleted) << \"Already deleted...?\";\n    sp<Foo> sp1(foo);\n    wp<Foo> wp1(sp1);\n    ASSERT_EQ(1, foo->getStrongCount());\n    // Weak count includes both strong and weak references.\n    ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());\n    {\n        sp<Foo> sp2 = std::move(sp1);\n        ASSERT_EQ(1, foo->getStrongCount())\n                << \"std::move failed, incremented refcnt\";\n        ASSERT_EQ(nullptr, sp1.get()) << \"std::move failed, sp1 is still valid\";\n        // The strong count isn't increasing, let's double check the old object\n        // is properly reset and doesn't early delete\n        sp1 = std::move(sp2);\n    }\n    ASSERT_FALSE(isDeleted) << \"deleted too early! still has a reference!\";\n    {\n        // Now let's double check it deletes on time\n        sp<Foo> sp2 = std::move(sp1);\n    }\n    ASSERT_TRUE(isDeleted) << \"foo was leaked!\";\n    ASSERT_TRUE(wp1.promote().get() == nullptr);\n}\n\nTEST(RefBase, WeakCopies) {\n    bool isDeleted;\n    Foo* foo = new Foo(&isDeleted);\n    EXPECT_EQ(0, foo->getWeakRefs()->getWeakCount());\n    ASSERT_FALSE(isDeleted) << \"Foo (weak) already deleted...?\";\n    wp<Foo> wp1(foo);\n    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());\n    {\n        wp<Foo> wp2 = wp1;\n        ASSERT_EQ(2, foo->getWeakRefs()->getWeakCount());\n    }\n    EXPECT_EQ(1, foo->getWeakRefs()->getWeakCount());\n    ASSERT_FALSE(isDeleted) << \"deleted too early! still has a reference!\";\n    wp1 = nullptr;\n    ASSERT_FALSE(isDeleted) << \"Deletion on wp destruction should no longer occur\";\n}\n\nTEST(RefBase, Comparisons) {\n    bool isDeleted, isDeleted2, isDeleted3;\n    Foo* foo = new Foo(&isDeleted);\n    Foo* foo2 = new Foo(&isDeleted2);\n    sp<Foo> sp1(foo);\n    sp<Foo> sp2(foo2);\n    wp<Foo> wp1(sp1);\n    wp<Foo> wp2(sp1);\n    wp<Foo> wp3(sp2);\n    ASSERT_TRUE(wp1 == wp2);\n    ASSERT_TRUE(wp1 == sp1);\n    ASSERT_TRUE(wp3 == sp2);\n    ASSERT_TRUE(wp1 != sp2);\n    ASSERT_TRUE(wp1 <= wp2);\n    ASSERT_TRUE(wp1 >= wp2);\n    ASSERT_FALSE(wp1 != wp2);\n    ASSERT_FALSE(wp1 > wp2);\n    ASSERT_FALSE(wp1 < wp2);\n    ASSERT_FALSE(sp1 == sp2);\n    ASSERT_TRUE(sp1 != sp2);\n    bool sp1_smaller = sp1 < sp2;\n    wp<Foo>wp_smaller = sp1_smaller ? wp1 : wp3;\n    wp<Foo>wp_larger = sp1_smaller ? wp3 : wp1;\n    ASSERT_TRUE(wp_smaller < wp_larger);\n    ASSERT_TRUE(wp_smaller != wp_larger);\n    ASSERT_TRUE(wp_smaller <= wp_larger);\n    ASSERT_FALSE(wp_smaller == wp_larger);\n    ASSERT_FALSE(wp_smaller > wp_larger);\n    ASSERT_FALSE(wp_smaller >= wp_larger);\n    sp2 = nullptr;\n    ASSERT_TRUE(isDeleted2);\n    ASSERT_FALSE(isDeleted);\n    ASSERT_FALSE(wp3 == sp2);\n    // Comparison results on weak pointers should not be affected.\n    ASSERT_TRUE(wp_smaller < wp_larger);\n    ASSERT_TRUE(wp_smaller != wp_larger);\n    ASSERT_TRUE(wp_smaller <= wp_larger);\n    ASSERT_FALSE(wp_smaller == wp_larger);\n    ASSERT_FALSE(wp_smaller > wp_larger);\n    ASSERT_FALSE(wp_smaller >= wp_larger);\n    wp2 = nullptr;\n    ASSERT_FALSE(wp1 == wp2);\n    ASSERT_TRUE(wp1 != wp2);\n    wp1.clear();\n    ASSERT_TRUE(wp1 == wp2);\n    ASSERT_FALSE(wp1 != wp2);\n    wp3.clear();\n    ASSERT_TRUE(wp1 == wp3);\n    ASSERT_FALSE(wp1 != wp3);\n    ASSERT_FALSE(isDeleted);\n    sp1.clear();\n    ASSERT_TRUE(isDeleted);\n    ASSERT_TRUE(sp1 == sp2);\n    // Try to check that null pointers are properly initialized.\n    {\n        // Try once with non-null, to maximize chances of getting junk on the\n        // stack.\n        sp<Foo> sp3(new Foo(&isDeleted3));\n        wp<Foo> wp4(sp3);\n        wp<Foo> wp5;\n        ASSERT_FALSE(wp4 == wp5);\n        ASSERT_TRUE(wp4 != wp5);\n        ASSERT_FALSE(sp3 == wp5);\n        ASSERT_FALSE(wp5 == sp3);\n        ASSERT_TRUE(sp3 != wp5);\n        ASSERT_TRUE(wp5 != sp3);\n        ASSERT_TRUE(sp3 == wp4);\n    }\n    {\n        sp<Foo> sp3;\n        wp<Foo> wp4(sp3);\n        wp<Foo> wp5;\n        ASSERT_TRUE(wp4 == wp5);\n        ASSERT_FALSE(wp4 != wp5);\n        ASSERT_TRUE(sp3 == wp5);\n        ASSERT_TRUE(wp5 == sp3);\n        ASSERT_FALSE(sp3 != wp5);\n        ASSERT_FALSE(wp5 != sp3);\n        ASSERT_TRUE(sp3 == wp4);\n    }\n}\n\n// Check whether comparison against dead wp works, even if the object referenced\n// by the new wp happens to be at the same address.\nTEST(RefBase, ReplacedComparison) {\n    bool isDeleted, isDeleted2;\n    FooFixedAlloc* foo = new FooFixedAlloc(&isDeleted);\n    sp<FooFixedAlloc> sp1(foo);\n    wp<FooFixedAlloc> wp1(sp1);\n    ASSERT_TRUE(wp1 == sp1);\n    sp1.clear();  // Deallocates the object.\n    ASSERT_TRUE(isDeleted);\n    FooFixedAlloc* foo2 = new FooFixedAlloc(&isDeleted2);\n    ASSERT_FALSE(isDeleted2);\n    ASSERT_EQ(foo, foo2);  // Not technically a legal comparison, but ...\n    sp<FooFixedAlloc> sp2(foo2);\n    wp<FooFixedAlloc> wp2(sp2);\n    ASSERT_TRUE(sp2 == wp2);\n    ASSERT_FALSE(sp2 != wp2);\n    ASSERT_TRUE(sp2 != wp1);\n    ASSERT_FALSE(sp2 == wp1);\n    ASSERT_FALSE(sp2 == sp1);  // sp1 is null.\n    ASSERT_FALSE(wp1 == wp2);  // wp1 refers to old object.\n    ASSERT_TRUE(wp1 != wp2);\n    ASSERT_TRUE(wp1 > wp2 || wp1 < wp2);\n    ASSERT_TRUE(wp1 >= wp2 || wp1 <= wp2);\n    ASSERT_FALSE(wp1 >= wp2 && wp1 <= wp2);\n    ASSERT_FALSE(wp1 == nullptr);\n    wp1 = sp2;\n    ASSERT_TRUE(wp1 == wp2);\n    ASSERT_FALSE(wp1 != wp2);\n}\n\nTEST(RefBase, AssertWeakRefExistsSuccess) {\n    bool isDeleted;\n    sp<Foo> foo = sp<Foo>::make(&isDeleted);\n    wp<Foo> weakFoo = foo;\n\n    EXPECT_EQ(weakFoo, wp<Foo>::fromExisting(foo.get()));\n    EXPECT_EQ(weakFoo.unsafe_get(), wp<Foo>::fromExisting(foo.get()).unsafe_get());\n\n    EXPECT_FALSE(isDeleted);\n    foo = nullptr;\n    EXPECT_TRUE(isDeleted);\n}\n\nTEST(RefBase, AssertWeakRefExistsDeath) {\n    // uses some other refcounting method, or none at all\n    bool isDeleted;\n    Foo* foo = new Foo(&isDeleted);\n\n    // can only get a valid wp<> object when you construct it from an sp<>\n    EXPECT_DEATH(wp<Foo>::fromExisting(foo), \"\");\n\n    delete foo;\n}\n\nTEST(RefBase, NoStrongCountPromoteFromWeak) {\n    bool isDeleted;\n    Foo* foo = new Foo(&isDeleted);\n\n    wp<Foo> weakFoo = wp<Foo>(foo);\n\n    EXPECT_FALSE(isDeleted);\n\n    {\n        sp<Foo> strongFoo = weakFoo.promote();\n        EXPECT_EQ(strongFoo, foo);\n    }\n\n    // this shows the justification of wp<>::fromExisting.\n    // if you construct a wp<>, for instance in a constructor, and it is\n    // accidentally promoted, that promoted sp<> will exclusively own\n    // the object. If that happens during the initialization of the\n    // object or in this scope, as you can see 'Foo* foo' is unowned,\n    // then we are left with a deleted object, and we could not put it\n    // into an sp<>.\n    //\n    // Consider the other implementation, where we disallow promoting\n    // a wp<> if there are no strong counts. If we return null, then\n    // the object would be unpromotable even though it hasn't been deleted.\n    // This is also errorprone.\n    //\n    // attemptIncStrong aborting in this case is a backwards incompatible\n    // change due to frequent use of wp<T>(this) in the constructor.\n    EXPECT_TRUE(isDeleted);\n}\n\nTEST(RefBase, DoubleOwnershipDeath) {\n    bool isDeleted;\n    auto foo = sp<Foo>::make(&isDeleted);\n\n    // if something else thinks it owns foo, should die\n    EXPECT_DEATH(delete foo.get(), \"\");\n\n    EXPECT_FALSE(isDeleted);\n}\n\nTEST(RefBase, StackOwnershipDeath) {\n    bool isDeleted;\n    EXPECT_DEATH({ Foo foo(&isDeleted); foo.incStrong(nullptr); }, \"\");\n}\n\n// Set up a situation in which we race with visit2AndRremove() to delete\n// 2 strong references.  Bar destructor checks that there are no early\n// deletions and prior updates are visible to destructor.\nclass Bar : public RefBase {\npublic:\n    Bar(std::atomic<int>* delete_count) : mVisited1(false), mVisited2(false),\n            mDeleteCount(delete_count) {\n    }\n\n    ~Bar() {\n        EXPECT_TRUE(mVisited1);\n        EXPECT_TRUE(mVisited2);\n        (*mDeleteCount)++;\n    }\n    bool mVisited1;\n    bool mVisited2;\nprivate:\n    std::atomic<int>* mDeleteCount;\n};\n\n[[clang::no_destroy]] static constinit sp<Bar> buffer;\nstatic constinit std::atomic<bool> bufferFull(false);\n\n// Wait until bufferFull has value val.\nstatic inline void waitFor(bool val) {\n    while (bufferFull != val) {}\n}\n\ncpu_set_t otherCpus;\n\n// Divide the cpus we're allowed to run on into myCpus and otherCpus.\n// Set origCpus to the processors we were originally allowed to run on.\n// Return false if origCpus doesn't include at least processors 0 and 1.\nstatic bool setExclusiveCpus(cpu_set_t* origCpus /* out */,\n        cpu_set_t* myCpus /* out */, cpu_set_t* otherCpus) {\n    if (sched_getaffinity(0, sizeof(cpu_set_t), origCpus) != 0) {\n        return false;\n    }\n    if (!CPU_ISSET(0,  origCpus) || !CPU_ISSET(1, origCpus)) {\n        return false;\n    }\n    CPU_ZERO(myCpus);\n    CPU_ZERO(otherCpus);\n    CPU_OR(myCpus, myCpus, origCpus);\n    CPU_OR(otherCpus, otherCpus, origCpus);\n    for (unsigned i = 0; i < CPU_SETSIZE; ++i) {\n        // I get the even cores, the other thread gets the odd ones.\n        if (i & 1) {\n            CPU_CLR(i, myCpus);\n        } else {\n            CPU_CLR(i, otherCpus);\n        }\n    }\n    return true;\n}\n\nstatic void visit2AndRemove() {\n    if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {\n        FAIL() << \"setaffinity returned:\" << errno;\n    }\n    for (int i = 0; i < NITERS; ++i) {\n        waitFor(true);\n        buffer->mVisited2 = true;\n        buffer = nullptr;\n        bufferFull = false;\n    }\n}\n\nTEST(RefBase, RacingDestructors) {\n    cpu_set_t origCpus;\n    cpu_set_t myCpus;\n    // Restrict us and the helper thread to disjoint cpu sets.\n    // This prevents us from getting scheduled against each other,\n    // which would be atrociously slow.\n    if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {\n        std::thread t(visit2AndRemove);\n        std::atomic<int> deleteCount(0);\n        if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {\n            FAIL() << \"setaffinity returned:\" << errno;\n        }\n        for (int i = 0; i < NITERS; ++i) {\n            waitFor(false);\n            Bar* bar = new Bar(&deleteCount);\n            sp<Bar> sp3(bar);\n            buffer = sp3;\n            bufferFull = true;\n            ASSERT_TRUE(bar->getStrongCount() >= 1);\n            // Weak count includes strong count.\n            ASSERT_TRUE(bar->getWeakRefs()->getWeakCount() >= 1);\n            sp3->mVisited1 = true;\n            sp3 = nullptr;\n        }\n        t.join();\n        if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {\n            FAIL();\n        }\n        ASSERT_EQ(NITERS, deleteCount) << \"Deletions missed!\";\n    }  // Otherwise this is slow and probably pointless on a uniprocessor.\n}\n\n[[clang::no_destroy]] static constinit wp<Bar> wpBuffer;\nstatic constinit std::atomic<bool> wpBufferFull(false);\n\n// Wait until wpBufferFull has value val.\nstatic inline void wpWaitFor(bool val) {\n    while (wpBufferFull != val) {}\n}\n\nstatic void visit3AndRemove() {\n    if (sched_setaffinity(0, sizeof(cpu_set_t), &otherCpus) != 0) {\n        FAIL() << \"setaffinity returned:\" << errno;\n    }\n    for (int i = 0; i < NITERS; ++i) {\n        wpWaitFor(true);\n        {\n            sp<Bar> sp1 = wpBuffer.promote();\n            // We implicitly check that sp1 != NULL\n            sp1->mVisited2 = true;\n        }\n        wpBuffer = nullptr;\n        wpBufferFull = false;\n    }\n}\n\nTEST(RefBase, RacingPromotions) {\n    cpu_set_t origCpus;\n    cpu_set_t myCpus;\n    // Restrict us and the helper thread to disjoint cpu sets.\n    // This prevents us from getting scheduled against each other,\n    // which would be atrociously slow.\n    if (setExclusiveCpus(&origCpus, &myCpus, &otherCpus)) {\n        std::thread t(visit3AndRemove);\n        std::atomic<int> deleteCount(0);\n        if (sched_setaffinity(0, sizeof(cpu_set_t), &myCpus) != 0) {\n            FAIL() << \"setaffinity returned:\" << errno;\n        }\n        for (int i = 0; i < NITERS; ++i) {\n            Bar* bar = new Bar(&deleteCount);\n            wp<Bar> wp1(bar);\n            bar->mVisited1 = true;\n            if (i % (NITERS / 10) == 0) {\n                // Do this rarely, since it generates a log message.\n                wp1 = nullptr;  // No longer destroys the object.\n                wp1 = bar;\n            }\n            wpBuffer = wp1;\n            ASSERT_EQ(bar->getWeakRefs()->getWeakCount(), 2);\n            wpBufferFull = true;\n            // Promotion races with that in visit3AndRemove.\n            // This may or may not succeed, but it shouldn't interfere with\n            // the concurrent one.\n            sp<Bar> sp1 = wp1.promote();\n            wpWaitFor(false);  // Waits for other thread to drop strong pointer.\n            sp1 = nullptr;\n            // No strong pointers here.\n            sp1 = wp1.promote();\n            ASSERT_EQ(sp1.get(), nullptr) << \"Dead wp promotion succeeded!\";\n        }\n        t.join();\n        if (sched_setaffinity(0, sizeof(cpu_set_t), &origCpus) != 0) {\n            FAIL();\n        }\n        ASSERT_EQ(NITERS, deleteCount) << \"Deletions missed!\";\n    }  // Otherwise this is slow and probably pointless on a uniprocessor.\n}\n"
  },
  {
    "path": "libutils/binder/SharedBuffer.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"sharedbuffer\"\n\n#include \"SharedBuffer.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#include <log/log.h>\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\nSharedBuffer* SharedBuffer::alloc(size_t size)\n{\n    // Don't overflow if the combined size of the buffer / header is larger than\n    // size_max.\n    LOG_ALWAYS_FATAL_IF((size >= (SIZE_MAX - sizeof(SharedBuffer))),\n                        \"Invalid buffer size %zu\", size);\n\n    SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));\n    if (sb) {\n        // Should be std::atomic_init(&sb->mRefs, 1);\n        // But that generates a warning with some compilers.\n        // The following is OK on Android-supported platforms.\n        sb->mRefs.store(1, std::memory_order_relaxed);\n        sb->mSize = size;\n        sb->mClientMetadata = 0;\n    }\n    return sb;\n}\n\n\nvoid SharedBuffer::dealloc(const SharedBuffer* released)\n{\n    free(const_cast<SharedBuffer*>(released));\n}\n\nSharedBuffer* SharedBuffer::edit() const\n{\n    if (onlyOwner()) {\n        return const_cast<SharedBuffer*>(this);\n    }\n    SharedBuffer* sb = alloc(mSize);\n    if (sb) {\n        memcpy(sb->data(), data(), size());\n        release();\n    }\n    return sb;\n}\n\nSharedBuffer* SharedBuffer::editResize(size_t newSize) const\n{\n    if (onlyOwner()) {\n        SharedBuffer* buf = const_cast<SharedBuffer*>(this);\n        if (buf->mSize == newSize) return buf;\n        // Don't overflow if the combined size of the new buffer / header is larger than\n        // size_max.\n        LOG_ALWAYS_FATAL_IF((newSize >= (SIZE_MAX - sizeof(SharedBuffer))),\n                            \"Invalid buffer size %zu\", newSize);\n\n        buf = (SharedBuffer*)realloc(reinterpret_cast<void*>(buf), sizeof(SharedBuffer) + newSize);\n        if (buf != nullptr) {\n            buf->mSize = newSize;\n            return buf;\n        }\n    }\n    SharedBuffer* sb = alloc(newSize);\n    if (sb) {\n        const size_t mySize = mSize;\n        memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);\n        release();\n    }\n    return sb;    \n}\n\nSharedBuffer* SharedBuffer::attemptEdit() const\n{\n    if (onlyOwner()) {\n        return const_cast<SharedBuffer*>(this);\n    }\n    return nullptr;\n}\n\nSharedBuffer* SharedBuffer::reset(size_t new_size) const\n{\n    // cheap-o-reset.\n    SharedBuffer* sb = alloc(new_size);\n    if (sb) {\n        release();\n    }\n    return sb;\n}\n\nvoid SharedBuffer::acquire() const {\n    mRefs.fetch_add(1, std::memory_order_relaxed);\n}\n\nint32_t SharedBuffer::release(uint32_t flags) const\n{\n    const bool useDealloc = ((flags & eKeepStorage) == 0);\n    if (onlyOwner()) {\n        // Since we're the only owner, our reference count goes to zero.\n        mRefs.store(0, std::memory_order_relaxed);\n        if (useDealloc) {\n            dealloc(this);\n        }\n        // As the only owner, our previous reference count was 1.\n        return 1;\n    }\n    // There's multiple owners, we need to use an atomic decrement.\n    int32_t prevRefCount = mRefs.fetch_sub(1, std::memory_order_release);\n    if (prevRefCount == 1) {\n        // We're the last reference, we need the acquire fence.\n        atomic_thread_fence(std::memory_order_acquire);\n        if (useDealloc) {\n            dealloc(this);\n        }\n    }\n    return prevRefCount;\n}\n\n\n}; // namespace android\n"
  },
  {
    "path": "libutils/binder/SharedBuffer.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * DEPRECATED.  DO NOT USE FOR NEW CODE.\n */\n\n#ifndef ANDROID_SHARED_BUFFER_H\n#define ANDROID_SHARED_BUFFER_H\n\n#include <atomic>\n#include <stdint.h>\n#include <sys/types.h>\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\nclass SharedBuffer\n{\npublic:\n\n    /* flags to use with release() */\n    enum {\n        eKeepStorage = 0x00000001\n    };\n\n    /*! allocate a buffer of size 'size' and acquire() it.\n     *  call release() to free it.\n     */\n    static          SharedBuffer*           alloc(size_t size);\n    \n    /*! free the memory associated with the SharedBuffer.\n     * Fails if there are any users associated with this SharedBuffer.\n     * In other words, the buffer must have been release by all its\n     * users.\n     */\n    static          void                    dealloc(const SharedBuffer* released);\n\n    //! access the data for read\n    inline          const void*             data() const;\n    \n    //! access the data for read/write\n    inline          void*                   data();\n\n    //! get size of the buffer\n    inline          size_t                  size() const;\n \n    //! get back a SharedBuffer object from its data\n    static  inline  SharedBuffer*           bufferFromData(void* data);\n    \n    //! get back a SharedBuffer object from its data\n    static  inline  const SharedBuffer*     bufferFromData(const void* data);\n\n    //! get the size of a SharedBuffer object from its data\n    static  inline  size_t                  sizeFromData(const void* data);\n    \n    //! edit the buffer (get a writtable, or non-const, version of it)\n                    SharedBuffer*           edit() const;\n\n    //! edit the buffer, resizing if needed\n                    SharedBuffer*           editResize(size_t size) const;\n\n    //! like edit() but fails if a copy is required\n                    SharedBuffer*           attemptEdit() const;\n    \n    //! resize and edit the buffer, loose it's content.\n                    SharedBuffer*           reset(size_t size) const;\n\n    //! acquire/release a reference on this buffer\n                    void                    acquire() const;\n                    \n    /*! release a reference on this buffer, with the option of not\n     * freeing the memory associated with it if it was the last reference\n     * returns the previous reference count\n     */     \n                    int32_t                 release(uint32_t flags = 0) const;\n    \n    //! returns wether or not we're the only owner\n    inline          bool                    onlyOwner() const;\n    \n\nprivate:\n        inline SharedBuffer() { }\n        inline ~SharedBuffer() { }\n        SharedBuffer(const SharedBuffer&);\n        SharedBuffer& operator = (const SharedBuffer&);\n \n        // Must be sized to preserve correct alignment.\n        mutable std::atomic<int32_t>        mRefs;\n                size_t                      mSize;\n                uint32_t                    mReserved;\npublic:\n        // mClientMetadata is reserved for client use.  It is initialized to 0\n        // and the clients can do whatever they want with it.  Note that this is\n        // placed last so that it is adjcent to the buffer allocated.\n                uint32_t                    mClientMetadata;\n};\n\nstatic_assert(sizeof(SharedBuffer) % 8 == 0\n        && (sizeof(size_t) > 4 || sizeof(SharedBuffer) == 16),\n        \"SharedBuffer has unexpected size\");\n\n// ---------------------------------------------------------------------------\n\nconst void* SharedBuffer::data() const {\n    return this + 1;\n}\n\nvoid* SharedBuffer::data() {\n    return this + 1;\n}\n\nsize_t SharedBuffer::size() const {\n    return mSize;\n}\n\nSharedBuffer* SharedBuffer::bufferFromData(void* data) {\n    return data ? static_cast<SharedBuffer *>(data)-1 : nullptr;\n}\n    \nconst SharedBuffer* SharedBuffer::bufferFromData(const void* data) {\n    return data ? static_cast<const SharedBuffer *>(data)-1 : nullptr;\n}\n\nsize_t SharedBuffer::sizeFromData(const void* data) {\n    return data ? bufferFromData(data)->mSize : 0;\n}\n\nbool SharedBuffer::onlyOwner() const {\n    return (mRefs.load(std::memory_order_acquire) == 1);\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_VECTOR_H\n"
  },
  {
    "path": "libutils/binder/SharedBuffer_test.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include <memory>\n#include <stdint.h>\n\n#include \"SharedBuffer.h\"\n\nextern \"C\" void __hwasan_init() __attribute__((weak));\n#define SKIP_WITH_HWASAN \\\n    if (&__hwasan_init != 0) GTEST_SKIP()\n\nTEST(SharedBufferTest, alloc_death) {\n    EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX), \"\");\n    EXPECT_DEATH(android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer)), \"\");\n}\n\nTEST(SharedBufferTest, alloc_max) {\n    SKIP_WITH_HWASAN;  // hwasan has a 2GiB allocation limit.\n\n    android::SharedBuffer* buf =\n            android::SharedBuffer::alloc(SIZE_MAX - sizeof(android::SharedBuffer) - 1);\n    if (buf != nullptr) {\n        EXPECT_NE(nullptr, buf->data());\n        buf->release();\n    }\n}\n\nTEST(SharedBufferTest, alloc_big) {\n    SKIP_WITH_HWASAN;  // hwasan has a 2GiB allocation limit.\n\n    android::SharedBuffer* buf = android::SharedBuffer::alloc(SIZE_MAX / 2);\n    if (buf != nullptr) {\n        EXPECT_NE(nullptr, buf->data());\n        buf->release();\n    }\n}\n\nTEST(SharedBufferTest, alloc_zero_size) {\n    android::SharedBuffer* buf = android::SharedBuffer::alloc(0);\n    ASSERT_NE(nullptr, buf);\n    ASSERT_EQ(0U, buf->size());\n    buf->release();\n}\n\nTEST(SharedBufferTest, editResize_death) {\n    android::SharedBuffer* buf = android::SharedBuffer::alloc(10);\n    EXPECT_DEATH(buf->editResize(SIZE_MAX - sizeof(android::SharedBuffer)), \"\");\n    buf = android::SharedBuffer::alloc(10);\n    EXPECT_DEATH(buf->editResize(SIZE_MAX), \"\");\n}\n\nTEST(SharedBufferTest, editResize_null) {\n    // Big enough to fail, not big enough to abort.\n    SKIP_WITH_HWASAN;  // hwasan has a 2GiB allocation limit.\n    android::SharedBuffer* buf = android::SharedBuffer::alloc(10);\n    android::SharedBuffer* buf2 = buf->editResize(SIZE_MAX / 2);\n    if (buf2 == nullptr) {\n        buf->release();\n    } else {\n        EXPECT_NE(nullptr, buf2->data());\n        buf2->release();\n    }\n}\n\nTEST(SharedBufferTest, editResize_zero_size) {\n    android::SharedBuffer* buf = android::SharedBuffer::alloc(10);\n    buf = buf->editResize(0);\n    ASSERT_EQ(0U, buf->size());\n    buf->release();\n}\n"
  },
  {
    "path": "libutils/binder/String16.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/String16.h>\n\n#include <log/log.h>\n\n#include <ctype.h>\n\n#include \"SharedBuffer.h\"\n\n#define LIBUTILS_PRAGMA(arg) _Pragma(#arg)\n#if defined(__clang__)\n#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) LIBUTILS_PRAGMA(clang arg)\n#elif defined(__GNUC__)\n#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) LIBUTILS_PRAGMA(GCC arg)\n#else\n#define LIBUTILS_PRAGMA_FOR_COMPILER(arg)\n#endif\n#define LIBUTILS_IGNORE(warning_flag)             \\\n    LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic push) \\\n    LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic ignored warning_flag)\n#define LIBUTILS_IGNORE_END() LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic pop)\n\nnamespace android {\n\nstatic const StaticString16 emptyString(u\"\");\nstatic inline char16_t* getEmptyString() {\n    return const_cast<char16_t*>(emptyString.c_str());\n}\n\n// ---------------------------------------------------------------------------\n\nvoid* String16::alloc(size_t size)\n{\n    SharedBuffer* buf = SharedBuffer::alloc(size);\n    buf->mClientMetadata = kIsSharedBufferAllocated;\n    return buf;\n}\n\nchar16_t* String16::allocFromUTF8(const char* u8str, size_t u8len)\n{\n    if (u8len == 0) return getEmptyString();\n\n    const uint8_t* u8cur = (const uint8_t*) u8str;\n\n    const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len);\n    if (u16len < 0) {\n        return getEmptyString();\n    }\n\n    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc(sizeof(char16_t) * (u16len + 1)));\n    if (buf) {\n        u8cur = (const uint8_t*) u8str;\n        char16_t* u16str = (char16_t*)buf->data();\n\n        utf8_to_utf16(u8cur, u8len, u16str, ((size_t) u16len) + 1);\n\n        //printf(\"Created UTF-16 string from UTF-8 \\\"%s\\\":\", in);\n        //printHexData(1, str, buf->size(), 16, 1);\n        //printf(\"\\n\");\n\n        return u16str;\n    }\n\n    return getEmptyString();\n}\n\nchar16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) {\n    if (u16len >= SIZE_MAX / sizeof(char16_t)) {\n        android_errorWriteLog(0x534e4554, \"73826242\");\n        abort();\n    }\n\n    SharedBuffer* buf = static_cast<SharedBuffer*>(alloc((u16len + 1) * sizeof(char16_t)));\n    ALOG_ASSERT(buf, \"Unable to allocate shared buffer\");\n    if (buf) {\n        char16_t* str = (char16_t*)buf->data();\n        memcpy(str, u16str, u16len * sizeof(char16_t));\n        str[u16len] = 0;\n        return str;\n    }\n    return getEmptyString();\n}\n\n// ---------------------------------------------------------------------------\n\nString16::String16()\n    : mString(getEmptyString())\n{\n}\n\nString16::String16(const String16& o)\n    : mString(o.mString)\n{\n    acquire();\n}\n\nString16::String16(String16&& o) noexcept\n    : mString(o.mString)\n{\n    o.mString = getEmptyString();\n}\n\nString16::String16(const String16& o, size_t len, size_t begin)\n    : mString(getEmptyString())\n{\n    setTo(o, len, begin);\n}\n\nString16::String16(const char16_t* o) : mString(allocFromUTF16(o, strlen16(o))) {}\n\nString16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {}\n\nString16::String16(const String8& o) : mString(allocFromUTF8(o.c_str(), o.size())) {}\n\nString16::String16(const char* o)\n    : mString(allocFromUTF8(o, strlen(o)))\n{\n}\n\nString16::String16(const char* o, size_t len)\n    : mString(allocFromUTF8(o, len))\n{\n}\n\nString16::~String16()\n{\n    release();\n}\n\nString16& String16::operator=(String16&& other) noexcept {\n    release();\n    mString = other.mString;\n    other.mString = getEmptyString();\n    return *this;\n}\n\nsize_t String16::size() const\n{\n    if (isStaticString()) {\n        return staticStringSize();\n    } else {\n        return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1;\n    }\n}\n\nvoid String16::setTo(const String16& other)\n{\n    release();\n    mString = other.mString;\n    acquire();\n}\n\nstatus_t String16::setTo(const String16& other, size_t len, size_t begin)\n{\n    const size_t N = other.size();\n    if (begin >= N) {\n        release();\n        mString = getEmptyString();\n        return OK;\n    }\n    if ((begin+len) > N) len = N-begin;\n    if (begin == 0 && len == N) {\n        setTo(other);\n        return OK;\n    }\n\n    if (&other == this) {\n        LOG_ALWAYS_FATAL(\"Not implemented\");\n    }\n\n    return setTo(other.c_str() + begin, len);\n}\n\nstatus_t String16::setTo(const char16_t* other)\n{\n    return setTo(other, strlen16(other));\n}\n\nstatus_t String16::setTo(const char16_t* other, size_t len)\n{\n    if (len >= SIZE_MAX / sizeof(char16_t)) {\n        android_errorWriteLog(0x534e4554, \"73826242\");\n        abort();\n    }\n\n    SharedBuffer* buf = static_cast<SharedBuffer*>(editResize((len + 1) * sizeof(char16_t)));\n    if (buf) {\n        char16_t* str = (char16_t*)buf->data();\n        memmove(str, other, len*sizeof(char16_t));\n        str[len] = 0;\n        mString = str;\n        return OK;\n    }\n    return NO_MEMORY;\n}\n\nstatus_t String16::append(const String16& other) {\n    return append(other.c_str(), other.size());\n}\n\nstatus_t String16::append(const char16_t* chrs, size_t otherLen) {\n    const size_t myLen = size();\n\n    if (myLen == 0) return setTo(chrs, otherLen);\n\n    if (otherLen == 0) return OK;\n\n    size_t size = myLen;\n    if (__builtin_add_overflow(size, otherLen, &size) ||\n        __builtin_add_overflow(size, 1, &size) ||\n        __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY;\n\n    SharedBuffer* buf = static_cast<SharedBuffer*>(editResize(size));\n    if (!buf) return NO_MEMORY;\n\n    char16_t* str = static_cast<char16_t*>(buf->data());\n    memcpy(str + myLen, chrs, otherLen * sizeof(char16_t));\n    str[myLen + otherLen] = 0;\n    mString = str;\n    return OK;\n}\n\nstatus_t String16::insert(size_t pos, const char16_t* chrs) {\n    return insert(pos, chrs, strlen16(chrs));\n}\n\nstatus_t String16::insert(size_t pos, const char16_t* chrs, size_t otherLen) {\n    const size_t myLen = size();\n\n    if (myLen == 0) return setTo(chrs, otherLen);\n\n    if (otherLen == 0) return OK;\n\n    if (pos > myLen) pos = myLen;\n\n    size_t size = myLen;\n    if (__builtin_add_overflow(size, otherLen, &size) ||\n        __builtin_add_overflow(size, 1, &size) ||\n        __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY;\n\n    SharedBuffer* buf = static_cast<SharedBuffer*>(editResize(size));\n    if (!buf) return NO_MEMORY;\n\n    char16_t* str = static_cast<char16_t*>(buf->data());\n    if (pos < myLen) memmove(str + pos + otherLen, str + pos, (myLen - pos) * sizeof(char16_t));\n    memcpy(str + pos, chrs, otherLen * sizeof(char16_t));\n    str[myLen + otherLen] = 0;\n    mString = str;\n    return OK;\n}\n\nssize_t String16::findFirst(char16_t c) const\n{\n    const char16_t* str = string();\n    const char16_t* p = str;\n    const char16_t* e = p + size();\n    while (p < e) {\n        if (*p == c) {\n            return p-str;\n        }\n        p++;\n    }\n    return -1;\n}\n\nssize_t String16::findLast(char16_t c) const\n{\n    const char16_t* str = string();\n    const char16_t* p = str;\n    const char16_t* e = p + size();\n    while (p < e) {\n        e--;\n        if (*e == c) {\n            return e-str;\n        }\n    }\n    return -1;\n}\n\nbool String16::startsWith(const String16& prefix) const\n{\n    const size_t ps = prefix.size();\n    if (ps > size()) return false;\n    return strzcmp16(mString, ps, prefix.c_str(), ps) == 0;\n}\n\nbool String16::startsWith(const char16_t* prefix) const\n{\n    const size_t ps = strlen16(prefix);\n    if (ps > size()) return false;\n    return strncmp16(mString, prefix, ps) == 0;\n}\n\nbool String16::contains(const char16_t* chrs) const\n{\n    return strstr16(mString, chrs) != nullptr;\n}\n\nvoid* String16::edit() {\n    SharedBuffer* buf;\n    if (isStaticString()) {\n        buf = static_cast<SharedBuffer*>(alloc((size() + 1) * sizeof(char16_t)));\n        if (buf) {\n            memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t));\n        }\n    } else {\n        buf = SharedBuffer::bufferFromData(mString)->edit();\n        buf->mClientMetadata = kIsSharedBufferAllocated;\n    }\n    return buf;\n}\n\nvoid* String16::editResize(size_t newSize) {\n    SharedBuffer* buf;\n    if (isStaticString()) {\n        size_t copySize = (size() + 1) * sizeof(char16_t);\n        if (newSize < copySize) {\n            copySize = newSize;\n        }\n        buf = static_cast<SharedBuffer*>(alloc(newSize));\n        if (buf) {\n            memcpy(buf->data(), mString, copySize);\n        }\n    } else {\n        buf = SharedBuffer::bufferFromData(mString)->editResize(newSize);\n        buf->mClientMetadata = kIsSharedBufferAllocated;\n    }\n    return buf;\n}\n\nvoid String16::acquire()\n{\n    if (!isStaticString()) {\n        SharedBuffer::bufferFromData(mString)->acquire();\n    }\n}\n\nvoid String16::release()\n{\n    if (!isStaticString()) {\n        SharedBuffer::bufferFromData(mString)->release();\n    }\n}\n\nbool String16::isStaticString() const {\n    // See String16.h for notes on the memory layout of String16::StaticData and\n    // SharedBuffer.\n    LIBUTILS_IGNORE(\"-Winvalid-offsetof\")\n    static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);\n    LIBUTILS_IGNORE_END()\n    const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);\n    return (*(p - 1) & kIsSharedBufferAllocated) == 0;\n}\n\nsize_t String16::staticStringSize() const {\n    // See String16.h for notes on the memory layout of String16::StaticData and\n    // SharedBuffer.\n    LIBUTILS_IGNORE(\"-Winvalid-offsetof\")\n    static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4);\n    LIBUTILS_IGNORE_END()\n    const uint32_t* p = reinterpret_cast<const uint32_t*>(mString);\n    return static_cast<size_t>(*(p - 1));\n}\n\nstatus_t String16::replaceAll(char16_t replaceThis, char16_t withThis)\n{\n    const size_t N = size();\n    const char16_t* str = string();\n    char16_t* edited = nullptr;\n    for (size_t i=0; i<N; i++) {\n        if (str[i] == replaceThis) {\n            if (!edited) {\n                SharedBuffer* buf = static_cast<SharedBuffer*>(edit());\n                if (!buf) {\n                    return NO_MEMORY;\n                }\n                edited = (char16_t*)buf->data();\n                mString = str = edited;\n            }\n            edited[i] = withThis;\n        }\n    }\n    return OK;\n}\n\n}; // namespace android\n"
  },
  {
    "path": "libutils/binder/String16_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <functional>\n#include <iostream>\n#include <vector>\n\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/String16.h\"\nstatic constexpr int MAX_STRING_BYTES = 256;\nstatic constexpr uint8_t MAX_OPERATIONS = 50;\n\nstd::vector<std::function<void(FuzzedDataProvider&, android::String16, android::String16)>>\n        operations = {\n\n                // Bytes and size\n                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {\n                    str1.c_str();\n                }),\n                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {\n                    str1.isStaticString();\n                }),\n                ([](FuzzedDataProvider&, android::String16 str1, android::String16) -> void {\n                    str1.size();\n                }),\n\n                // Comparison\n                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {\n                    str1.startsWith(str2);\n                }),\n                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {\n                    str1.contains(str2.c_str());\n                }),\n                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {\n                    str1.compare(str2);\n                }),\n\n                // Append and format\n                ([](FuzzedDataProvider&, android::String16 str1, android::String16 str2) -> void {\n                    str1.append(str2);\n                }),\n                ([](FuzzedDataProvider& dataProvider, android::String16 str1,\n                    android::String16 str2) -> void {\n                    int pos = dataProvider.ConsumeIntegralInRange<int>(0, str1.size());\n                    str1.insert(pos, str2.c_str());\n                }),\n\n                // Find and replace operations\n                ([](FuzzedDataProvider& dataProvider, android::String16 str1,\n                    android::String16) -> void {\n                    char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();\n                    str1.findFirst(findChar);\n                }),\n                ([](FuzzedDataProvider& dataProvider, android::String16 str1,\n                    android::String16) -> void {\n                    char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();\n                    str1.findLast(findChar);\n                }),\n                ([](FuzzedDataProvider& dataProvider, android::String16 str1,\n                    android::String16) -> void {\n                    char16_t findChar = dataProvider.ConsumeIntegral<char16_t>();\n                    char16_t replaceChar = dataProvider.ConsumeIntegral<char16_t>();\n                    str1.replaceAll(findChar, replaceChar);\n                }),\n};\n\nvoid callFunc(uint8_t index, FuzzedDataProvider& dataProvider, android::String16 str1,\n              android::String16 str2) {\n    operations[index](dataProvider, str1, str2);\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    // We're generating two char vectors.\n    // First, generate lengths.\n    const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);\n    const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);\n\n    // Next, populate the vectors\n    std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);\n    std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);\n\n    // Get pointers to their data\n    char* char_one = vec.data();\n    char* char_two = vec_two.data();\n\n    // Create UTF16 representations\n    android::String16 str_one_utf16 = android::String16(char_one);\n    android::String16 str_two_utf16 = android::String16(char_two);\n\n    // Run operations against strings\n    int opsRun = 0;\n    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {\n        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);\n        callFunc(op, dataProvider, str_one_utf16, str_two_utf16);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "libutils/binder/String16_test.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/String16.h>\n#include <utils/String8.h>\n#include <compare>\n#include <utility>\n\n#include <gtest/gtest.h>\n\nusing namespace android;\n\n::testing::AssertionResult Char16_tStringEquals(const char16_t* a, const char16_t* b) {\n    if (strcmp16(a, b) != 0) {\n        return ::testing::AssertionFailure()\n               << \"\\\"\" << String8(a).c_str() << \"\\\" not equal to \\\"\" << String8(b).c_str() << \"\\\"\";\n    }\n    return ::testing::AssertionSuccess();\n}\n\n#define EXPECT_STR16EQ(a, b) EXPECT_TRUE(Char16_tStringEquals(a, b))\n\nTEST(String16Test, FromChar16_t) {\n    String16 tmp(u\"Verify me\");\n    EXPECT_STR16EQ(u\"Verify me\", tmp.c_str());\n}\n\nTEST(String16Test, FromChar16_tSized) {\n    String16 tmp(u\"Verify me\", 7);\n    EXPECT_STR16EQ(u\"Verify \", tmp.c_str());\n}\n\nTEST(String16Test, FromChar) {\n    String16 tmp(\"Verify me\");\n    EXPECT_STR16EQ(u\"Verify me\", tmp.c_str());\n}\n\nTEST(String16Test, FromCharSized) {\n    String16 tmp(\"Verify me\", 7);\n    EXPECT_STR16EQ(u\"Verify \", tmp.c_str());\n}\n\nTEST(String16Test, Copy) {\n    String16 tmp(\"Verify me\");\n    String16 another = tmp;\n    EXPECT_STR16EQ(u\"Verify me\", tmp.c_str());\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n}\n\nTEST(String16Test, CopyAssign) {\n    String16 tmp(\"Verify me\");\n    String16 another;\n    another = tmp;\n    EXPECT_STR16EQ(u\"Verify me\", tmp.c_str());\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n}\n\nTEST(String16Test, Move) {\n    String16 tmp(\"Verify me\");\n    String16 another(std::move(tmp));\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n}\n\nTEST(String16Test, MoveAssign) {\n    String16 tmp(\"Verify me\");\n    String16 another;\n    another = std::move(tmp);\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n}\n\nTEST(String16Test, Size) {\n    String16 tmp(\"Verify me\");\n    EXPECT_EQ(9U, tmp.size());\n}\n\nTEST(String16Test, setTo) {\n    String16 tmp(\"Verify me\");\n    tmp.setTo(u\"New content\");\n    EXPECT_EQ(11U, tmp.size());\n    EXPECT_STR16EQ(u\"New content\", tmp.c_str());\n}\n\nTEST(String16Test, Append) {\n    String16 tmp(\"Verify me\");\n    tmp.append(String16(\"Hello\"));\n    EXPECT_EQ(14U, tmp.size());\n    EXPECT_STR16EQ(u\"Verify meHello\", tmp.c_str());\n}\n\nTEST(String16Test, Insert) {\n    String16 tmp(\"Verify me\");\n    tmp.insert(6, u\"Insert\");\n    EXPECT_EQ(15U, tmp.size());\n    EXPECT_STR16EQ(u\"VerifyInsert me\", tmp.c_str());\n}\n\nTEST(String16Test, ReplaceAll) {\n    String16 tmp(\"Verify verify Verify\");\n    tmp.replaceAll(u'r', u'!');\n    EXPECT_STR16EQ(u\"Ve!ify ve!ify Ve!ify\", tmp.c_str());\n}\n\nTEST(String16Test, Compare) {\n    String16 tmp(\"Verify me\");\n    EXPECT_EQ(String16(u\"Verify me\"), tmp);\n}\n\nTEST(String16Test, StaticString) {\n    String16 nonStaticString(\"NonStatic\");\n    StaticString16 staticString(u\"Static\");\n\n    EXPECT_TRUE(staticString.isStaticString());\n    EXPECT_FALSE(nonStaticString.isStaticString());\n}\n\nTEST(String16Test, StaticStringCopy) {\n    StaticString16 tmp(u\"Verify me\");\n    String16 another = tmp;\n    EXPECT_STR16EQ(u\"Verify me\", tmp.c_str());\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n    EXPECT_TRUE(tmp.isStaticString());\n    EXPECT_TRUE(another.isStaticString());\n}\n\nTEST(String16Test, StaticStringMove) {\n    StaticString16 tmp(u\"Verify me\");\n    String16 another(std::move(tmp));\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n    EXPECT_TRUE(another.isStaticString());\n}\n\nTEST(String16Test, StaticStringSize) {\n    StaticString16 tmp(u\"Verify me\");\n    EXPECT_EQ(9U, tmp.size());\n}\n\nTEST(String16Test, StaticStringSetTo) {\n    StaticString16 tmp(u\"Verify me\");\n    tmp.setTo(u\"New content\");\n    EXPECT_EQ(11U, tmp.size());\n    EXPECT_STR16EQ(u\"New content\", tmp);\n    EXPECT_FALSE(tmp.isStaticString());\n}\n\nTEST(String16Test, StaticStringAppend) {\n    StaticString16 tmp(u\"Verify me\");\n    tmp.append(String16(\"Hello\"));\n    EXPECT_EQ(14U, tmp.size());\n    EXPECT_STR16EQ(u\"Verify meHello\", tmp.c_str());\n    EXPECT_FALSE(tmp.isStaticString());\n}\n\nTEST(String16Test, StaticStringInsert) {\n    StaticString16 tmp(u\"Verify me\");\n    tmp.insert(6, u\"Insert\");\n    EXPECT_EQ(15U, tmp.size());\n    EXPECT_STR16EQ(u\"VerifyInsert me\", tmp.c_str());\n    EXPECT_FALSE(tmp.isStaticString());\n}\n\nTEST(String16Test, StaticStringReplaceAll) {\n    StaticString16 tmp(u\"Verify verify Verify\");\n    tmp.replaceAll(u'r', u'!');\n    EXPECT_STR16EQ(u\"Ve!ify ve!ify Ve!ify\", tmp.c_str());\n    EXPECT_FALSE(tmp.isStaticString());\n}\n\nTEST(String16Test, StaticStringCompare) {\n    StaticString16 tmp(u\"Verify me\");\n    EXPECT_EQ(String16(u\"Verify me\"), tmp);\n}\n\nTEST(String16Test, StringSetToStaticString) {\n    StaticString16 tmp(u\"Verify me\");\n    String16 another(u\"nonstatic\");\n    another = tmp;\n    EXPECT_STR16EQ(u\"Verify me\", tmp.c_str());\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n}\n\nTEST(String16Test, StringCopyAssignFromStaticString) {\n    StaticString16 tmp(u\"Verify me\");\n    String16 another(u\"nonstatic\");\n    another = tmp;\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n    EXPECT_TRUE(another.isStaticString());\n    EXPECT_STR16EQ(u\"Verify me\", tmp.c_str());\n    EXPECT_TRUE(tmp.isStaticString());\n}\n\nTEST(String16Test, StringMoveAssignFromStaticString) {\n    StaticString16 tmp(u\"Verify me\");\n    String16 another(u\"nonstatic\");\n    another = std::move(tmp);\n    EXPECT_STR16EQ(u\"Verify me\", another.c_str());\n    EXPECT_TRUE(another.isStaticString());\n}\n\nTEST(String16Test, EmptyStringIsStatic) {\n    String16 tmp(\"\");\n    EXPECT_TRUE(tmp.isStaticString());\n}\n\nTEST(String16Test, OverreadUtf8Conversion) {\n    char tmp[] = {'a', static_cast<char>(0xe0), '\\0'};\n    String16 another(tmp);\n    EXPECT_TRUE(another.size() == 0);\n}\n\nTEST(String16Test, ValidUtf8Conversion) {\n    String16 another(\"abcdef\");\n    EXPECT_EQ(6U, another.size());\n    EXPECT_STR16EQ(another.c_str(), u\"abcdef\");\n}\n\nTEST(String16Test, append) {\n    String16 s;\n    EXPECT_EQ(OK, s.append(String16(u\"foo\")));\n    EXPECT_STR16EQ(u\"foo\", s.c_str());\n    EXPECT_EQ(OK, s.append(String16(u\"bar\")));\n    EXPECT_STR16EQ(u\"foobar\", s.c_str());\n    EXPECT_EQ(OK, s.append(u\"baz\", 0));\n    EXPECT_STR16EQ(u\"foobar\", s.c_str());\n    EXPECT_EQ(NO_MEMORY, s.append(u\"baz\", SIZE_MAX));\n    EXPECT_STR16EQ(u\"foobar\", s.c_str());\n}\n\nTEST(String16Test, insert) {\n    String16 s;\n\n    // Inserting into the empty string inserts at the start.\n    EXPECT_EQ(OK, s.insert(123, u\"foo\"));\n    EXPECT_STR16EQ(u\"foo\", s.c_str());\n\n    // Inserting zero characters at any position is okay, but won't expand the string.\n    EXPECT_EQ(OK, s.insert(123, u\"foo\", 0));\n    EXPECT_STR16EQ(u\"foo\", s.c_str());\n\n    // Inserting past the end of a non-empty string appends.\n    EXPECT_EQ(OK, s.insert(123, u\"bar\"));\n    EXPECT_STR16EQ(u\"foobar\", s.c_str());\n\n    EXPECT_EQ(OK, s.insert(3, u\"!\"));\n    EXPECT_STR16EQ(u\"foo!bar\", s.c_str());\n\n    EXPECT_EQ(NO_MEMORY, s.insert(3, u\"\", SIZE_MAX));\n    EXPECT_STR16EQ(u\"foo!bar\", s.c_str());\n}\n\nTEST(String16Test, comparisons) {\n    const char16_t* cstr1 = u\"abc\";\n    const char16_t* cstr2 = u\"def\";\n\n    // str1 and str1b will point to different blocks of memory but with equal contents.\n    String16 str1(cstr1);\n    String16 str1b(cstr1);\n    String16 str2(cstr2);\n\n    EXPECT_TRUE((str1 <=> str1b) == 0);\n    EXPECT_FALSE(str1 != str1b);\n    EXPECT_FALSE(str1 < str1b);\n    EXPECT_TRUE(str1 <= str1b);\n    EXPECT_TRUE(str1 == str1b);\n    EXPECT_TRUE(str1 >= str1b);\n    EXPECT_FALSE(str1 > str1b);\n\n    EXPECT_TRUE((str1 <=> str2) < 0);\n    EXPECT_TRUE((str2 <=> str1) > 0);\n    EXPECT_TRUE(str1 != str2);\n    EXPECT_TRUE(str1 < str2);\n    EXPECT_TRUE(str1 <= str2);\n    EXPECT_FALSE(str1 == str2);\n    EXPECT_FALSE(str1 >= str2);\n    EXPECT_FALSE(str1 > str2);\n\n    // Verify that pre-C++20 comparison operators work with a std::pair of a String8, which only\n    // provides <=> in C++20 and up. See b/339775405.\n\n    std::pair<String16, int> pair1(str1, 13);\n    std::pair<String16, int> pair1b(str1b, 13);\n    std::pair<String16, int> pair2(str2, 13);\n\n    EXPECT_TRUE(pair1 == pair1b);\n    EXPECT_FALSE(pair1 < pair1b);\n    EXPECT_FALSE(pair1 > pair1b);\n\n    EXPECT_TRUE(pair1 != pair2);\n    EXPECT_TRUE(pair1 < pair2);\n    EXPECT_FALSE(pair1 > pair2);\n}\n"
  },
  {
    "path": "libutils/binder/String8.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <utils/String8.h>\n\n#include <log/log.h>\n#include <utils/String16.h>\n\n#include <ctype.h>\n#include <stdint.h>\n\n#include <limits>\n#include <string>\n\n#include \"SharedBuffer.h\"\n\n/*\n * Functions outside android is below the namespace android, since they use\n * functions and constants in android namespace.\n */\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\nstatic inline char* getEmptyString() {\n    static SharedBuffer* gEmptyStringBuf = [] {\n        SharedBuffer* buf = SharedBuffer::alloc(1);\n        char* str = static_cast<char*>(buf->data());\n        *str = 0;\n        return buf;\n    }();\n\n    gEmptyStringBuf->acquire();\n    return static_cast<char*>(gEmptyStringBuf->data());\n}\n\n// ---------------------------------------------------------------------------\n\nstatic char* allocFromUTF8(const char* in, size_t len)\n{\n    if (len > 0) {\n        if (len == SIZE_MAX) {\n            return nullptr;\n        }\n        SharedBuffer* buf = SharedBuffer::alloc(len+1);\n        ALOG_ASSERT(buf, \"Unable to allocate shared buffer\");\n        if (buf) {\n            char* str = (char*)buf->data();\n            memcpy(str, in, len);\n            str[len] = 0;\n            return str;\n        }\n        return nullptr;\n    }\n\n    return getEmptyString();\n}\n\nstatic char* allocFromUTF16(const char16_t* in, size_t len)\n{\n    if (len == 0) return getEmptyString();\n\n     // Allow for closing '\\0'\n    const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1;\n    if (resultStrLen < 1) {\n        return getEmptyString();\n    }\n\n    SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);\n    ALOG_ASSERT(buf, \"Unable to allocate shared buffer\");\n    if (!buf) {\n        return getEmptyString();\n    }\n\n    char* resultStr = (char*)buf->data();\n    utf16_to_utf8(in, len, resultStr, resultStrLen);\n    return resultStr;\n}\n\nstatic char* allocFromUTF32(const char32_t* in, size_t len)\n{\n    if (len == 0) {\n        return getEmptyString();\n    }\n\n    const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1;\n    if (resultStrLen < 1) {\n        return getEmptyString();\n    }\n\n    SharedBuffer* buf = SharedBuffer::alloc(resultStrLen);\n    ALOG_ASSERT(buf, \"Unable to allocate shared buffer\");\n    if (!buf) {\n        return getEmptyString();\n    }\n\n    char* resultStr = (char*) buf->data();\n    utf32_to_utf8(in, len, resultStr, resultStrLen);\n\n    return resultStr;\n}\n\n// ---------------------------------------------------------------------------\n\nString8::String8()\n    : mString(getEmptyString())\n{\n}\n\nString8::String8(const String8& o)\n    : mString(o.mString)\n{\n    SharedBuffer::bufferFromData(mString)->acquire();\n}\n\nString8::String8(const char* o)\n    : mString(allocFromUTF8(o, strlen(o)))\n{\n    if (mString == nullptr) {\n        mString = getEmptyString();\n    }\n}\n\nString8::String8(const char* o, size_t len)\n    : mString(allocFromUTF8(o, len))\n{\n    if (mString == nullptr) {\n        mString = getEmptyString();\n    }\n}\n\nString8::String8(const String16& o) : mString(allocFromUTF16(o.c_str(), o.size())) {}\n\nString8::String8(const char16_t* o)\n    : mString(allocFromUTF16(o, strlen16(o)))\n{\n}\n\nString8::String8(const char16_t* o, size_t len)\n    : mString(allocFromUTF16(o, len))\n{\n}\n\nString8::String8(const char32_t* o)\n    : mString(allocFromUTF32(o, std::char_traits<char32_t>::length(o))) {}\n\nString8::String8(const char32_t* o, size_t len)\n    : mString(allocFromUTF32(o, len))\n{\n}\n\nString8::~String8()\n{\n    SharedBuffer::bufferFromData(mString)->release();\n}\n\nsize_t String8::length() const\n{\n    return SharedBuffer::sizeFromData(mString)-1;\n}\n\nString8 String8::format(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n\n    String8 result(formatV(fmt, args));\n\n    va_end(args);\n    return result;\n}\n\nString8 String8::formatV(const char* fmt, va_list args)\n{\n    String8 result;\n    result.appendFormatV(fmt, args);\n    return result;\n}\n\nvoid String8::clear() {\n    SharedBuffer::bufferFromData(mString)->release();\n    mString = getEmptyString();\n}\n\nvoid String8::setTo(const String8& other)\n{\n    SharedBuffer::bufferFromData(other.mString)->acquire();\n    SharedBuffer::bufferFromData(mString)->release();\n    mString = other.mString;\n}\n\nstatus_t String8::setTo(const char* other)\n{\n    const char *newString = allocFromUTF8(other, strlen(other));\n    SharedBuffer::bufferFromData(mString)->release();\n    mString = newString;\n    if (mString) return OK;\n\n    mString = getEmptyString();\n    return NO_MEMORY;\n}\n\nstatus_t String8::setTo(const char* other, size_t len)\n{\n    const char *newString = allocFromUTF8(other, len);\n    SharedBuffer::bufferFromData(mString)->release();\n    mString = newString;\n    if (mString) return OK;\n\n    mString = getEmptyString();\n    return NO_MEMORY;\n}\n\nstatus_t String8::setTo(const char16_t* other, size_t len)\n{\n    const char *newString = allocFromUTF16(other, len);\n    SharedBuffer::bufferFromData(mString)->release();\n    mString = newString;\n    if (mString) return OK;\n\n    mString = getEmptyString();\n    return NO_MEMORY;\n}\n\nstatus_t String8::setTo(const char32_t* other, size_t len)\n{\n    const char *newString = allocFromUTF32(other, len);\n    SharedBuffer::bufferFromData(mString)->release();\n    mString = newString;\n    if (mString) return OK;\n\n    mString = getEmptyString();\n    return NO_MEMORY;\n}\n\nstatus_t String8::append(const String8& other)\n{\n    const size_t otherLen = other.bytes();\n    if (bytes() == 0) {\n        setTo(other);\n        return OK;\n    } else if (otherLen == 0) {\n        return OK;\n    }\n\n    return real_append(other.c_str(), otherLen);\n}\n\nstatus_t String8::append(const char* other)\n{\n    return append(other, strlen(other));\n}\n\nstatus_t String8::append(const char* other, size_t otherLen)\n{\n    if (bytes() == 0) {\n        return setTo(other, otherLen);\n    } else if (otherLen == 0) {\n        return OK;\n    }\n\n    return real_append(other, otherLen);\n}\n\nstatus_t String8::appendFormat(const char* fmt, ...)\n{\n    va_list args;\n    va_start(args, fmt);\n\n    status_t result = appendFormatV(fmt, args);\n\n    va_end(args);\n    return result;\n}\n\nstatus_t String8::appendFormatV(const char* fmt, va_list args)\n{\n    int n, result = OK;\n    va_list tmp_args;\n\n    /* args is undefined after vsnprintf.\n     * So we need a copy here to avoid the\n     * second vsnprintf access undefined args.\n     */\n    va_copy(tmp_args, args);\n    n = vsnprintf(nullptr, 0, fmt, tmp_args);\n    va_end(tmp_args);\n\n    if (n < 0) return UNKNOWN_ERROR;\n\n    if (n > 0) {\n        size_t oldLength = length();\n        if (static_cast<size_t>(n) > std::numeric_limits<size_t>::max() - 1 ||\n            oldLength > std::numeric_limits<size_t>::max() - n - 1) {\n            return NO_MEMORY;\n        }\n        char* buf = lockBuffer(oldLength + n);\n        if (buf) {\n            vsnprintf(buf + oldLength, n + 1, fmt, args);\n        } else {\n            result = NO_MEMORY;\n        }\n    }\n    return result;\n}\n\nstatus_t String8::real_append(const char* other, size_t otherLen) {\n    const size_t myLen = bytes();\n\n    SharedBuffer* buf;\n    size_t newLen;\n    if (__builtin_add_overflow(myLen, otherLen, &newLen) ||\n        __builtin_add_overflow(newLen, 1, &newLen) ||\n        (buf = SharedBuffer::bufferFromData(mString)->editResize(newLen)) == nullptr) {\n        return NO_MEMORY;\n    }\n\n    char* str = (char*)buf->data();\n    mString = str;\n    str += myLen;\n    memcpy(str, other, otherLen);\n    str[otherLen] = '\\0';\n    return OK;\n}\n\nchar* String8::lockBuffer(size_t size)\n{\n    SharedBuffer* buf = SharedBuffer::bufferFromData(mString)\n        ->editResize(size+1);\n    if (buf) {\n        char* str = (char*)buf->data();\n        mString = str;\n        return str;\n    }\n    return nullptr;\n}\n\nvoid String8::unlockBuffer()\n{\n    unlockBuffer(strlen(mString));\n}\n\nstatus_t String8::unlockBuffer(size_t size)\n{\n    if (size != this->size()) {\n        SharedBuffer* buf = SharedBuffer::bufferFromData(mString)\n            ->editResize(size+1);\n        if (! buf) {\n            return NO_MEMORY;\n        }\n\n        char* str = (char*)buf->data();\n        str[size] = 0;\n        mString = str;\n    }\n\n    return OK;\n}\n\nssize_t String8::find(const char* other, size_t start) const\n{\n    size_t len = size();\n    if (start >= len) {\n        return -1;\n    }\n    const char* s = mString+start;\n    const char* p = strstr(s, other);\n    return p ? p-mString : -1;\n}\n\nbool String8::removeAll(const char* other) {\n    ALOG_ASSERT(other, \"String8::removeAll() requires a non-NULL string\");\n\n    if (*other == '\\0')\n        return true;\n\n    ssize_t index = find(other);\n    if (index < 0) return false;\n\n    char* buf = lockBuffer(size());\n    if (!buf) return false; // out of memory\n\n    size_t skip = strlen(other);\n    size_t len = size();\n    size_t tail = index;\n    while (size_t(index) < len) {\n        ssize_t next = find(other, index + skip);\n        if (next < 0) {\n            next = len;\n        }\n\n        memmove(buf + tail, buf + index + skip, next - index - skip);\n        tail += next - index - skip;\n        index = next;\n    }\n    unlockBuffer(tail);\n    return true;\n}\n\nvoid String8::toLower()\n{\n    const size_t length = size();\n    if (length == 0) return;\n\n    char* buf = lockBuffer(length);\n    for (size_t i = length; i > 0; --i) {\n        *buf = static_cast<char>(tolower(*buf));\n        buf++;\n    }\n    unlockBuffer(length);\n}\n\n// ---------------------------------------------------------------------------\n// Path functions\n\n// TODO: we should remove all the path functions from String8\n#if defined(_WIN32)\n#define OS_PATH_SEPARATOR '\\\\'\n#else\n#define OS_PATH_SEPARATOR '/'\n#endif\n\nString8 String8::getPathDir(void) const\n{\n    const char* cp;\n    const char*const str = mString;\n\n    cp = strrchr(str, OS_PATH_SEPARATOR);\n    if (cp == nullptr)\n        return String8(\"\");\n    else\n        return String8(str, cp - str);\n}\n\n/*\n * Helper function for finding the start of an extension in a pathname.\n *\n * Returns a pointer inside mString, or NULL if no extension was found.\n */\nstatic const char* find_extension(const char* str) {\n    const char* lastSlash;\n    const char* lastDot;\n\n    // only look at the filename\n    lastSlash = strrchr(str, OS_PATH_SEPARATOR);\n    if (lastSlash == nullptr)\n        lastSlash = str;\n    else\n        lastSlash++;\n\n    // find the last dot\n    lastDot = strrchr(lastSlash, '.');\n    if (lastDot == nullptr)\n        return nullptr;\n\n    // looks good, ship it\n    return lastDot;\n}\n\nString8 String8::getPathExtension(void) const\n{\n    auto ext = find_extension(mString);\n    if (ext != nullptr)\n        return String8(ext);\n    else\n        return String8(\"\");\n}\n\n}; // namespace android\n"
  },
  {
    "path": "libutils/binder/String8_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <functional>\n#include <iostream>\n#include <memory>\n\n#include \"FuzzFormatTypes.h\"\n#include \"fuzzer/FuzzedDataProvider.h\"\n#include \"utils/String8.h\"\n\nstatic constexpr int MAX_STRING_BYTES = 256;\nstatic constexpr uint8_t MAX_OPERATIONS = 50;\n// Interestingly, 2147483614 (INT32_MAX - 33) seems to be the max value that is handled for format\n// flags. Unfortunately we need to use a smaller value so we avoid consuming too much memory.\n\nvoid fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend);\nstd::vector<std::function<void(FuzzedDataProvider*, android::String8*, android::String8*)>>\n        operations = {\n                // Bytes and size\n                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {\n                    str1->bytes();\n                },\n                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {\n                    str1->empty();\n                },\n                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {\n                    str1->length();\n                },\n\n                // Casing\n                [](FuzzedDataProvider*, android::String8* str1, android::String8*) -> void {\n                    str1->toLower();\n                },\n                [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {\n                    if (str2->size() == 0) return;\n\n                    str1->removeAll(str2->c_str());\n                },\n                [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {\n                    const android::String8& constRef(*str2);\n                    str1->compare(constRef);\n                },\n\n                // Append and format\n                [](FuzzedDataProvider*, android::String8* str1, android::String8* str2) -> void {\n                    str1->append(str2->c_str());\n                },\n                [](FuzzedDataProvider* dataProvider, android::String8* str1, android::String8*)\n                        -> void { fuzzFormat(dataProvider, str1, dataProvider->ConsumeBool()); },\n\n                // Find operation\n                [](FuzzedDataProvider* dataProvider, android::String8* str1,\n                   android::String8* str2) -> void {\n                    // We need to get a value from our fuzzer here.\n                    int start_index = dataProvider->ConsumeIntegralInRange<int>(0, str1->size());\n                    str1->find(str2->c_str(), start_index);\n                },\n};\n\nvoid fuzzFormat(FuzzedDataProvider* dataProvider, android::String8* str1, bool shouldAppend) {\n    FormatChar formatType = dataProvider->ConsumeEnum<FormatChar>();\n\n    std::string formatString(\"%\");\n    // Width specifier\n    if (dataProvider->ConsumeBool()) {\n        // Left pad with zeroes\n        if (dataProvider->ConsumeBool()) {\n            formatString.push_back('0');\n        }\n        // Right justify (or left justify if negative)\n        int32_t justify = dataProvider->ConsumeIntegralInRange<int32_t>(-kMaxFormatFlagValue,\n                                                                        kMaxFormatFlagValue);\n        formatString += std::to_string(justify);\n    }\n\n    // The # specifier only works with o, x, X, a, A, e, E, f, F, g, and G\n    if (canApplyFlag(formatType, '#') && dataProvider->ConsumeBool()) {\n        formatString.push_back('#');\n    }\n\n    // Precision specifier\n    if (canApplyFlag(formatType, '.') && dataProvider->ConsumeBool()) {\n        formatString.push_back('.');\n        formatString +=\n                std::to_string(dataProvider->ConsumeIntegralInRange<int>(0, kMaxFormatFlagValue));\n    }\n\n    formatString.push_back(kFormatChars.at(static_cast<uint8_t>(formatType)));\n\n    switch (formatType) {\n        case SIGNED_DECIMAL: {\n            int val = dataProvider->ConsumeIntegral<int>();\n            if (shouldAppend) {\n                str1->appendFormat(formatString.c_str(), val);\n            } else {\n                str1->format(formatString.c_str(), dataProvider->ConsumeIntegral<int>());\n            }\n            break;\n        }\n\n        case UNSIGNED_DECIMAL:\n        case UNSIGNED_OCTAL:\n        case UNSIGNED_HEX_LOWER:\n        case UNSIGNED_HEX_UPPER: {\n            // Unsigned integers for u, o, x, and X\n            uint val = dataProvider->ConsumeIntegral<uint>();\n            if (shouldAppend) {\n                str1->appendFormat(formatString.c_str(), val);\n            } else {\n                str1->format(formatString.c_str(), val);\n            }\n            break;\n        }\n\n        case FLOAT_LOWER:\n        case FLOAT_UPPER:\n        case EXPONENT_LOWER:\n        case EXPONENT_UPPER:\n        case SHORT_EXP_LOWER:\n        case SHORT_EXP_UPPER:\n        case HEX_FLOAT_LOWER:\n        case HEX_FLOAT_UPPER: {\n            // Floating points for f, F, e, E, g, G, a, and A\n            float val = dataProvider->ConsumeFloatingPoint<float>();\n            if (shouldAppend) {\n                str1->appendFormat(formatString.c_str(), val);\n            } else {\n                str1->format(formatString.c_str(), val);\n            }\n            break;\n        }\n\n        case CHAR: {\n            char val = dataProvider->ConsumeIntegral<char>();\n            if (shouldAppend) {\n                str1->appendFormat(formatString.c_str(), val);\n            } else {\n                str1->format(formatString.c_str(), val);\n            }\n            break;\n        }\n\n        case STRING: {\n            std::string val = dataProvider->ConsumeRandomLengthString(MAX_STRING_BYTES);\n            if (shouldAppend) {\n                str1->appendFormat(formatString.c_str(), val.c_str());\n            } else {\n                str1->format(formatString.c_str(), val.c_str());\n            }\n            break;\n        }\n        case POINTER: {\n            uintptr_t val = dataProvider->ConsumeIntegral<uintptr_t>();\n            if (shouldAppend) {\n                str1->appendFormat(formatString.c_str(), val);\n            } else {\n                str1->format(formatString.c_str(), val);\n            }\n            break;\n        }\n    }\n}\n\nvoid callFunc(uint8_t index, FuzzedDataProvider* dataProvider, android::String8* str1,\n              android::String8* str2) {\n    operations[index](dataProvider, str1, str2);\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider dataProvider(data, size);\n    // Generate vector lengths\n    const size_t kVecOneLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);\n    const size_t kVecTwoLen = dataProvider.ConsumeIntegralInRange<size_t>(1, MAX_STRING_BYTES);\n    // Populate vectors\n    std::vector<char> vec = dataProvider.ConsumeBytesWithTerminator<char>(kVecOneLen);\n    std::vector<char> vec_two = dataProvider.ConsumeBytesWithTerminator<char>(kVecTwoLen);\n    // Create UTF-8 pointers\n    android::String8 str_one_utf8 = android::String8(vec.data());\n    android::String8 str_two_utf8 = android::String8(vec_two.data());\n    // Run operations against strings\n    int opsRun = 0;\n    while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) {\n        uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1);\n        operations[op](&dataProvider, &str_one_utf8, &str_two_utf8);\n    }\n    // Just to be extra sure these can be freed, we're going to explicitly clear\n    // them\n    str_one_utf8.clear();\n    str_two_utf8.clear();\n    return 0;\n}\n"
  },
  {
    "path": "libutils/binder/String8_test.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"String8_test\"\n\n#include <log/log.h>\n#include <utils/String16.h>\n#include <utils/String8.h>\n#include <compare>\n#include <utility>\n\n#include <gtest/gtest.h>\n\nusing namespace android;\n\nclass String8Test : public testing::Test {\nprotected:\n    virtual void SetUp() {\n    }\n\n    virtual void TearDown() {\n    }\n};\n\nTEST_F(String8Test, Cstr) {\n    String8 tmp(\"Hello, world!\");\n\n    EXPECT_STREQ(tmp.c_str(), \"Hello, world!\");\n}\n\nTEST_F(String8Test, OperatorPlus) {\n    String8 src1(\"Hello, \");\n\n    // Test adding String8 + const char*\n    const char* ccsrc2 = \"world!\";\n    String8 dst1 = src1 + ccsrc2;\n    EXPECT_STREQ(dst1.c_str(), \"Hello, world!\");\n    EXPECT_STREQ(src1.c_str(), \"Hello, \");\n    EXPECT_STREQ(ccsrc2, \"world!\");\n\n    // Test adding String8 + String8\n    String8 ssrc2(\"world!\");\n    String8 dst2 = src1 + ssrc2;\n    EXPECT_STREQ(dst2.c_str(), \"Hello, world!\");\n    EXPECT_STREQ(src1.c_str(), \"Hello, \");\n    EXPECT_STREQ(ssrc2.c_str(), \"world!\");\n}\n\nTEST_F(String8Test, OperatorPlusEquals) {\n    String8 src1(\"My voice\");\n\n    // Testing String8 += String8\n    String8 src2(\" is my passport.\");\n    src1 += src2;\n    EXPECT_STREQ(src1.c_str(), \"My voice is my passport.\");\n    EXPECT_STREQ(src2.c_str(), \" is my passport.\");\n\n    // Adding const char* to the previous string.\n    const char* src3 = \" Verify me.\";\n    src1 += src3;\n    EXPECT_STREQ(src1.c_str(), \"My voice is my passport. Verify me.\");\n    EXPECT_STREQ(src2.c_str(), \" is my passport.\");\n    EXPECT_STREQ(src3, \" Verify me.\");\n}\n\nTEST_F(String8Test, SetToSizeMaxReturnsNoMemory) {\n    const char *in = \"some string\";\n    EXPECT_EQ(NO_MEMORY, String8(\"\").setTo(in, SIZE_MAX));\n}\n\n// http://b/29250543\nTEST_F(String8Test, CorrectInvalidSurrogate) {\n    // d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the\n    // first character in the pair and handling the rest correctly.\n    String16 string16(u\"\\xd841\\xd841\\xdc41\\x0000\");\n    String8 string8(string16);\n\n    EXPECT_EQ(4U, string8.length());\n}\n\nTEST_F(String8Test, CheckUtf32Conversion) {\n    // Since bound checks were added, check the conversion can be done without fatal errors.\n    // The utf8 lengths of these are chars are 1 + 2 + 3 + 4 = 10.\n    const char32_t string32[] = U\"\\x0000007f\\x000007ff\\x0000911\\x0010fffe\";\n    String8 string8(string32);\n    EXPECT_EQ(10U, string8.length());\n}\n\nTEST_F(String8Test, ValidUtf16Conversion) {\n    char16_t tmp[] = u\"abcdef\";\n    String8 valid = String8(String16(tmp));\n    EXPECT_STREQ(valid.c_str(), \"abcdef\");\n}\n\nTEST_F(String8Test, append) {\n    String8 s;\n    EXPECT_EQ(OK, s.append(\"foo\"));\n    EXPECT_STREQ(\"foo\", s.c_str());\n    EXPECT_EQ(OK, s.append(\"bar\"));\n    EXPECT_STREQ(\"foobar\", s.c_str());\n    EXPECT_EQ(OK, s.append(\"baz\", 0));\n    EXPECT_STREQ(\"foobar\", s.c_str());\n    EXPECT_EQ(NO_MEMORY, s.append(\"baz\", SIZE_MAX));\n    EXPECT_STREQ(\"foobar\", s.c_str());\n}\n\nTEST_F(String8Test, removeAll) {\n    String8 s(\"Hello, world!\");\n\n    // NULL input should cause an assertion failure and error message in logcat\n    EXPECT_DEATH(s.removeAll(NULL), \"\");\n\n    // expect to return true and string content should remain unchanged\n    EXPECT_TRUE(s.removeAll(\"\"));\n    EXPECT_STREQ(\"Hello, world!\", s.c_str());\n\n    // expect to return false\n    EXPECT_FALSE(s.removeAll(\"x\"));\n    EXPECT_STREQ(\"Hello, world!\", s.c_str());\n\n    EXPECT_TRUE(s.removeAll(\"o\"));\n    EXPECT_STREQ(\"Hell, wrld!\", s.c_str());\n}\n\nTEST_F(String8Test, comparisons) {\n    const char* cstr1 = \"abc\";\n    const char* cstr2 = \"def\";\n\n    // str1 and str1b will point to different blocks of memory but with equal contents.\n    String8 str1(cstr1);\n    String8 str1b(cstr1);\n    String8 str2(cstr2);\n\n    EXPECT_TRUE((str1 <=> str1b) == 0);\n    EXPECT_FALSE(str1 != str1b);\n    EXPECT_FALSE(str1 < str1b);\n    EXPECT_TRUE(str1 <= str1b);\n    EXPECT_TRUE(str1 == str1b);\n    EXPECT_TRUE(str1 >= str1b);\n    EXPECT_FALSE(str1 > str1b);\n\n    EXPECT_TRUE((str1 <=> str2) < 0);\n    EXPECT_TRUE((str2 <=> str1) > 0);\n    EXPECT_TRUE(str1 != str2);\n    EXPECT_TRUE(str1 < str2);\n    EXPECT_TRUE(str1 <= str2);\n    EXPECT_FALSE(str1 == str2);\n    EXPECT_FALSE(str1 >= str2);\n    EXPECT_FALSE(str1 > str2);\n\n    // Verify that pre-C++20 comparison operators work with a std::pair of a String8, which only\n    // provides <=> in C++20 and up. See b/339775405.\n\n    std::pair<String8, int> pair1(str1, 13);\n    std::pair<String8, int> pair1b(str1b, 13);\n    std::pair<String8, int> pair2(str2, 13);\n\n    EXPECT_TRUE(pair1 == pair1b);\n    EXPECT_FALSE(pair1 < pair1b);\n    EXPECT_FALSE(pair1 > pair1b);\n\n    EXPECT_TRUE(pair1 != pair2);\n    EXPECT_TRUE(pair1 < pair2);\n    EXPECT_FALSE(pair1 > pair2);\n}\n\nTEST_F(String8Test, SvCtor) {\n    const char* expected = \"abc\";\n    std::string s{expected};\n    EXPECT_STREQ(String8{s}.c_str(), expected);\n    EXPECT_STREQ(String8{std::string_view{s}}.c_str(), expected);\n    EXPECT_STREQ(String8{expected}.c_str(), expected);\n}\n"
  },
  {
    "path": "libutils/binder/StrongPointer.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"sp\"\n\n#include <log/log.h>\n\nnamespace android {\n\nvoid sp_report_race() { LOG_ALWAYS_FATAL(\"sp<> assignment detected data race\"); }\n}\n"
  },
  {
    "path": "libutils/binder/StrongPointer_test.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <gtest/gtest.h>\n\n#include <utils/StrongPointer.h>\n#include <utils/RefBase.h>\n\nusing namespace android;\n\nclass SPFoo : virtual public RefBase {\n  public:\n    explicit SPFoo(bool* deleted_check) : mDeleted(deleted_check) {\n        *mDeleted = false;\n    }\n\n    ~SPFoo() {\n        *mDeleted = true;\n    }\n\n  private:\n    bool* mDeleted;\n};\n\nclass SPLightFoo : virtual public VirtualLightRefBase {\n  public:\n    explicit SPLightFoo(bool* deleted_check) : mDeleted(deleted_check) { *mDeleted = false; }\n\n    ~SPLightFoo() { *mDeleted = true; }\n\n  private:\n    bool* mDeleted;\n};\n\ntemplate <typename T>\nclass StrongPointer : public ::testing::Test {};\n\nusing RefBaseTypes = ::testing::Types<SPFoo, SPLightFoo>;\nTYPED_TEST_CASE(StrongPointer, RefBaseTypes);\n\nTYPED_TEST(StrongPointer, move) {\n    bool isDeleted;\n    sp<TypeParam> sp1 = sp<TypeParam>::make(&isDeleted);\n    TypeParam* foo = sp1.get();\n    ASSERT_EQ(1, foo->getStrongCount());\n    {\n        sp<TypeParam> sp2 = std::move(sp1);\n        ASSERT_EQ(1, foo->getStrongCount()) << \"std::move failed, incremented refcnt\";\n        ASSERT_EQ(nullptr, sp1.get()) << \"std::move failed, sp1 is still valid\";\n        // The strong count isn't increasing, let's double check the old object\n        // is properly reset and doesn't early delete\n        sp1 = std::move(sp2);\n    }\n    ASSERT_FALSE(isDeleted) << \"deleted too early! still has a reference!\";\n    {\n        // Now let's double check it deletes on time\n        sp<TypeParam> sp2 = std::move(sp1);\n    }\n    ASSERT_TRUE(isDeleted) << \"foo was leaked!\";\n}\n\nTYPED_TEST(StrongPointer, NullptrComparison) {\n    sp<TypeParam> foo;\n    ASSERT_EQ(foo, nullptr);\n    ASSERT_EQ(nullptr, foo);\n}\n\nTYPED_TEST(StrongPointer, PointerComparison) {\n    bool isDeleted;\n    sp<TypeParam> foo = sp<TypeParam>::make(&isDeleted);\n    ASSERT_EQ(foo.get(), foo);\n    ASSERT_EQ(foo, foo.get());\n    ASSERT_NE(nullptr, foo);\n    ASSERT_NE(foo, nullptr);\n}\n\nTYPED_TEST(StrongPointer, Deleted) {\n    bool isDeleted;\n    sp<TypeParam> foo = sp<TypeParam>::make(&isDeleted);\n\n    auto foo2 = sp<TypeParam>::fromExisting(foo.get());\n\n    EXPECT_FALSE(isDeleted);\n    foo = nullptr;\n    EXPECT_FALSE(isDeleted);\n    foo2 = nullptr;\n    EXPECT_TRUE(isDeleted);\n}\n\nTYPED_TEST(StrongPointer, AssertStrongRefExists) {\n    bool isDeleted;\n    TypeParam* foo = new TypeParam(&isDeleted);\n    EXPECT_DEATH(sp<TypeParam>::fromExisting(foo), \"\");\n    delete foo;\n}\n\nTYPED_TEST(StrongPointer, release) {\n    bool isDeleted = false;\n    TypeParam* foo = nullptr;\n    {\n        sp<TypeParam> sp1 = sp<TypeParam>::make(&isDeleted);\n        ASSERT_EQ(1, sp1->getStrongCount());\n        foo = sp1.release();\n    }\n    ASSERT_FALSE(isDeleted) << \"release failed, deleted anyway when sp left scope\";\n    ASSERT_EQ(1, foo->getStrongCount()) << \"release mismanaged refcount\";\n    foo->decStrong(nullptr);\n    ASSERT_TRUE(isDeleted) << \"foo was leaked!\";\n}\n"
  },
  {
    "path": "libutils/binder/Unicode.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"unicode\"\n\n#include <limits.h>\n#include <utils/Unicode.h>\n\n#include <log/log.h>\n\nextern \"C\" {\n\nstatic const char32_t kByteMask = 0x000000BF;\nstatic const char32_t kByteMark = 0x00000080;\n\n// Surrogates aren't valid for UTF-32 characters, so define some\n// constants that will let us screen them out.\nstatic const char32_t kUnicodeSurrogateHighStart  = 0x0000D800;\n// Unused, here for completeness:\n// static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF;\n// static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00;\nstatic const char32_t kUnicodeSurrogateLowEnd     = 0x0000DFFF;\nstatic const char32_t kUnicodeSurrogateStart      = kUnicodeSurrogateHighStart;\nstatic const char32_t kUnicodeSurrogateEnd        = kUnicodeSurrogateLowEnd;\nstatic const char32_t kUnicodeMaxCodepoint        = 0x0010FFFF;\n\n// Mask used to set appropriate bits in first byte of UTF-8 sequence,\n// indexed by number of bytes in the sequence.\n// 0xxxxxxx\n// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000\n// 110yyyyx 10xxxxxx\n// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0\n// 1110yyyy 10yxxxxx 10xxxxxx\n// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0\n// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx\n// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0\nstatic const char32_t kFirstByteMark[] = {\n    0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0\n};\n\n// --------------------------------------------------------------------------\n// UTF-32\n// --------------------------------------------------------------------------\n\n/**\n * Return number of UTF-8 bytes required for the character. If the character\n * is invalid, return size of 0.\n */\nstatic inline size_t utf32_codepoint_utf8_length(char32_t srcChar)\n{\n    // Figure out how many bytes the result will require.\n    if (srcChar < 0x00000080) {\n        return 1;\n    } else if (srcChar < 0x00000800) {\n        return 2;\n    } else if (srcChar < 0x00010000) {\n        if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) {\n            return 3;\n        } else {\n            // Surrogates are invalid UTF-32 characters.\n            return 0;\n        }\n    }\n    // Max code point for Unicode is 0x0010FFFF.\n    else if (srcChar <= kUnicodeMaxCodepoint) {\n        return 4;\n    } else {\n        // Invalid UTF-32 character.\n        return 0;\n    }\n}\n\n// Write out the source character to <dstP>.\n\nstatic inline void utf32_codepoint_to_utf8(uint8_t* dstP, char32_t srcChar, size_t bytes)\n{\n    dstP += bytes;\n    switch (bytes)\n    {   /* note: everything falls through. */\n        case 4: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;\n            [[fallthrough]];\n        case 3: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;\n            [[fallthrough]];\n        case 2: *--dstP = (uint8_t)((srcChar | kByteMark) & kByteMask); srcChar >>= 6;\n            [[fallthrough]];\n        case 1: *--dstP = (uint8_t)(srcChar | kFirstByteMark[bytes]);\n    }\n}\n\nstatic inline int32_t utf32_at_internal(const char* cur, size_t *num_read)\n{\n    const char first_char = *cur;\n    if ((first_char & 0x80) == 0) { // ASCII\n        *num_read = 1;\n        return *cur;\n    }\n    cur++;\n    char32_t mask, to_ignore_mask;\n    size_t num_to_read = 0;\n    char32_t utf32 = first_char;\n    for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80;\n         (first_char & mask);\n         num_to_read++, to_ignore_mask |= mask, mask >>= 1) {\n        // 0x3F == 00111111\n        utf32 = (utf32 << 6) + (*cur++ & 0x3F);\n    }\n    to_ignore_mask |= mask;\n    utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1)));\n\n    *num_read = num_to_read;\n    return static_cast<int32_t>(utf32);\n}\n\nint32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index)\n{\n    if (index >= src_len) {\n        return -1;\n    }\n    size_t unused_index;\n    if (next_index == nullptr) {\n        next_index = &unused_index;\n    }\n    size_t num_read;\n    int32_t ret = utf32_at_internal(src + index, &num_read);\n    if (ret >= 0) {\n        *next_index = index + num_read;\n    }\n\n    return ret;\n}\n\nssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len)\n{\n    if (src == nullptr || src_len == 0) {\n        return -1;\n    }\n\n    size_t ret = 0;\n    const char32_t *end = src + src_len;\n    while (src < end) {\n        size_t char_len = utf32_codepoint_utf8_length(*src++);\n        if (SSIZE_MAX - char_len < ret) {\n            // If this happens, we would overflow the ssize_t type when\n            // returning from this function, so we cannot express how\n            // long this string is in an ssize_t.\n            android_errorWriteLog(0x534e4554, \"37723026\");\n            return -1;\n        }\n        ret += char_len;\n    }\n    return ret;\n}\n\nvoid utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len)\n{\n    if (src == nullptr || src_len == 0 || dst == nullptr) {\n        return;\n    }\n\n    const char32_t *cur_utf32 = src;\n    const char32_t *end_utf32 = src + src_len;\n    char *cur = dst;\n    while (cur_utf32 < end_utf32) {\n        size_t len = utf32_codepoint_utf8_length(*cur_utf32);\n        LOG_ALWAYS_FATAL_IF(dst_len < len, \"%zu < %zu\", dst_len, len);\n        utf32_codepoint_to_utf8((uint8_t *)cur, *cur_utf32++, len);\n        cur += len;\n        dst_len -= len;\n    }\n    LOG_ALWAYS_FATAL_IF(dst_len < 1, \"dst_len < 1: %zu < 1\", dst_len);\n    *cur = '\\0';\n}\n\n// --------------------------------------------------------------------------\n// UTF-16\n// --------------------------------------------------------------------------\n\nint strcmp16(const char16_t *s1, const char16_t *s2)\n{\n  char16_t ch;\n  int d = 0;\n\n  while ( 1 ) {\n    d = (int)(ch = *s1++) - (int)*s2++;\n    if ( d || !ch )\n      break;\n  }\n\n  return d;\n}\n\nint strncmp16(const char16_t *s1, const char16_t *s2, size_t n)\n{\n  char16_t ch;\n  int d = 0;\n\n  if (n == 0) {\n    return 0;\n  }\n\n  do {\n    d = (int)(ch = *s1++) - (int)*s2++;\n    if ( d || !ch ) {\n      break;\n    }\n  } while (--n);\n\n  return d;\n}\n\nsize_t strlen16(const char16_t *s)\n{\n  const char16_t *ss = s;\n  while ( *ss )\n    ss++;\n  return ss-s;\n}\n\nsize_t strnlen16(const char16_t *s, size_t maxlen)\n{\n  const char16_t *ss = s;\n\n  /* Important: the maxlen test must precede the reference through ss;\n     since the byte beyond the maximum may segfault */\n  while ((maxlen > 0) && *ss) {\n    ss++;\n    maxlen--;\n  }\n  return ss-s;\n}\n\nchar16_t* strstr16(const char16_t* src, const char16_t* target)\n{\n    const char16_t needle = *target;\n    if (needle == '\\0') return (char16_t*)src;\n\n    const size_t target_len = strlen16(++target);\n    do {\n        do {\n            if (*src == '\\0') {\n                return nullptr;\n            }\n        } while (*src++ != needle);\n    } while (strncmp16(src, target, target_len) != 0);\n    src--;\n\n    return (char16_t*)src;\n}\n\nint strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2)\n{\n    const char16_t* e1 = s1+n1;\n    const char16_t* e2 = s2+n2;\n\n    while (s1 < e1 && s2 < e2) {\n        const int d = (int)*s1++ - (int)*s2++;\n        if (d) {\n            return d;\n        }\n    }\n\n    return n1 < n2\n        ? (0 - (int)*s2)\n        : (n1 > n2\n           ? ((int)*s1 - 0)\n           : 0);\n}\n\n// is_any_surrogate() returns true if w is either a high or low surrogate\nstatic constexpr bool is_any_surrogate(char16_t w) {\n    return (w & 0xf800) == 0xd800;\n}\n\n// is_surrogate_pair() returns true if w1 and w2 form a valid surrogate pair\nstatic constexpr bool is_surrogate_pair(char16_t w1, char16_t w2) {\n    return ((w1 & 0xfc00) == 0xd800) && ((w2 & 0xfc00) == 0xdc00);\n}\n\n// TODO: currently utf16_to_utf8_length() returns -1 if src_len == 0,\n// which is inconsistent with utf8_to_utf16_length(), here we keep the\n// current behavior as intended not to break compatibility\nssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)\n{\n    if (src == nullptr || src_len == 0)\n        return -1;\n\n    const char16_t* const end = src + src_len;\n    const char16_t* in = src;\n    size_t utf8_len = 0;\n\n    while (in < end) {\n        char16_t w = *in++;\n        if (w < 0x0080) [[likely]] {\n            utf8_len += 1;\n            continue;\n        }\n        if (w < 0x0800) [[likely]] {\n            utf8_len += 2;\n            continue;\n        }\n        if (!is_any_surrogate(w)) [[likely]] {\n            utf8_len += 3;\n            continue;\n        }\n        if (in < end && is_surrogate_pair(w, *in)) {\n            utf8_len += 4;\n            in++;\n            continue;\n        }\n        /* skip if at the end of the string or invalid surrogate pair */\n    }\n    return (in == end && utf8_len < SSIZE_MAX) ? utf8_len : -1;\n}\n\nvoid utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len)\n{\n    if (src == nullptr || src_len == 0 || dst == nullptr) {\n        return;\n    }\n\n    const char16_t* in = src;\n    const char16_t* const in_end = src + src_len;\n    char* out = dst;\n    const char* const out_end = dst + dst_len;\n    char16_t w2;\n\n    auto err_out = [&out, &out_end, &dst_len]() {\n        LOG_ALWAYS_FATAL_IF(out >= out_end,\n                \"target utf8 string size %zu too short\", dst_len);\n    };\n\n    while (in < in_end) {\n        char16_t w = *in++;\n        if (w < 0x0080) [[likely]] {\n            if (out + 1 > out_end)\n                return err_out();\n            *out++ = (char)(w & 0xff);\n            continue;\n        }\n        if (w < 0x0800) [[likely]] {\n            if (out + 2 > out_end)\n                return err_out();\n            *out++ = (char)(0xc0 | ((w >> 6) & 0x1f));\n            *out++ = (char)(0x80 | ((w >> 0) & 0x3f));\n            continue;\n        }\n        if (!is_any_surrogate(w)) [[likely]] {\n            if (out + 3 > out_end)\n                return err_out();\n            *out++ = (char)(0xe0 | ((w >> 12) & 0xf));\n            *out++ = (char)(0x80 | ((w >> 6) & 0x3f));\n            *out++ = (char)(0x80 | ((w >> 0) & 0x3f));\n            continue;\n        }\n        /* surrogate pair */\n        if (in < in_end && (w2 = *in, is_surrogate_pair(w, w2))) {\n            if (out + 4 > out_end)\n                return err_out();\n            char32_t dw = (char32_t)(0x10000 + ((w - 0xd800) << 10) + (w2 - 0xdc00));\n            *out++ = (char)(0xf0 | ((dw >> 18) & 0x07));\n            *out++ = (char)(0x80 | ((dw >> 12) & 0x3f));\n            *out++ = (char)(0x80 | ((dw >> 6)  & 0x3f));\n            *out++ = (char)(0x80 | ((dw >> 0)  & 0x3f));\n            in++;\n        }\n        /* We reach here in two cases:\n         *  1) (in == in_end), which means end of the input string\n         *  2) (w2 & 0xfc00) != 0xdc00, which means invalid surrogate pair\n         * In either case, we intentionally do nothing and skip\n         */\n    }\n    *out = '\\0';\n    return;\n}\n\n// --------------------------------------------------------------------------\n// UTF-8\n// --------------------------------------------------------------------------\n\nstatic char32_t utf8_4b_to_utf32(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) {\n    return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f);\n}\n\n// TODO: current behavior of converting UTF8 to UTF-16 has a few issues below\n//\n// 1. invalid trailing bytes (i.e. not b'10xxxxxx) are treated as valid trailing\n//    bytes and follows normal conversion rules\n// 2. invalid leading byte (b'10xxxxxx) is treated as a valid single UTF-8 byte\n// 3. invalid leading byte (b'11111xxx) is treated as a valid leading byte\n//    (same as b'11110xxx) for a 4-byte UTF-8 sequence\n// 4. an invalid 4-byte UTF-8 sequence that translates to a codepoint < U+10000\n//    will be converted as a valid UTF-16 character\n//\n// We keep the current behavior as is but with warnings logged, so as not to\n// break compatibility.  However, this needs to be addressed later.\n\nssize_t utf8_to_utf16_length(const uint8_t* u8str, size_t u8len, bool overreadIsFatal)\n{\n    if (u8str == nullptr)\n        return -1;\n\n    const uint8_t* const in_end = u8str + u8len;\n    const uint8_t* in = u8str;\n    size_t utf16_len = 0;\n\n    while (in < in_end) {\n        uint8_t c = *in;\n        utf16_len++;\n        if ((c & 0x80) == 0) [[likely]] {\n            in++;\n            continue;\n        }\n        if (c < 0xc0) [[unlikely]] {\n            ALOGW(\"Invalid UTF-8 leading byte: 0x%02x\", c);\n            in++;\n            continue;\n        }\n        if (c < 0xe0) [[likely]] {\n            in += 2;\n            continue;\n        }\n        if (c < 0xf0) [[likely]] {\n            in += 3;\n            continue;\n        } else {\n            uint8_t c2, c3, c4;\n            if (c >= 0xf8) [[unlikely]] {\n                ALOGW(\"Invalid UTF-8 leading byte: 0x%02x\", c);\n            }\n            c2 = in[1]; c3 = in[2]; c4 = in[3];\n            if (utf8_4b_to_utf32(c, c2, c3, c4) >= 0x10000) {\n                utf16_len++;\n            }\n            in += 4;\n            continue;\n        }\n    }\n    if (in == in_end) {\n        return utf16_len < SSIZE_MAX ? utf16_len : -1;\n    }\n    if (overreadIsFatal)\n        LOG_ALWAYS_FATAL(\"Attempt to overread computing length of utf8 string\");\n    return -1;\n}\n\nchar16_t* utf8_to_utf16(const uint8_t* u8str, size_t u8len, char16_t* u16str, size_t u16len) {\n    // A value > SSIZE_MAX is probably a negative value returned as an error and casted.\n    LOG_ALWAYS_FATAL_IF(u16len == 0 || u16len > SSIZE_MAX, \"u16len is %zu\", u16len);\n    char16_t* end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1);\n    *end = 0;\n    return end;\n}\n\nchar16_t* utf8_to_utf16_no_null_terminator(\n        const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen) {\n    if (src == nullptr || srcLen == 0 || dstLen == 0) {\n        return dst;\n    }\n    // A value > SSIZE_MAX is probably a negative value returned as an error and casted.\n    LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, \"dstLen is %zu\", dstLen);\n\n    const uint8_t* const in_end = src + srcLen;\n    const uint8_t* in = src;\n    const char16_t* const out_end = dst + dstLen;\n    char16_t* out = dst;\n    uint8_t c, c2, c3, c4;\n    char32_t w;\n\n    auto err_in = [&c, &out]() {\n        ALOGW(\"Unended UTF-8 byte: 0x%02x\", c);\n        return out;\n    };\n\n    while (in < in_end && out < out_end) {\n        c = *in++;\n        if ((c & 0x80) == 0) [[likely]] {\n            *out++ = (char16_t)(c);\n            continue;\n        }\n        if (c < 0xc0) [[unlikely]] {\n            ALOGW(\"Invalid UTF-8 leading byte: 0x%02x\", c);\n            *out++ = (char16_t)(c);\n            continue;\n        }\n        if (c < 0xe0) [[likely]] {\n            if (in + 1 > in_end) [[unlikely]] {\n                return err_in();\n            }\n            c2 = *in++;\n            *out++ = (char16_t)(((c & 0x1f) << 6) | (c2 & 0x3f));\n            continue;\n        }\n        if (c < 0xf0) [[likely]] {\n            if (in + 2 > in_end) [[unlikely]] {\n                return err_in();\n            }\n            c2 = *in++; c3 = *in++;\n            *out++ = (char16_t)(((c & 0x0f) << 12) |\n                                ((c2 & 0x3f) << 6) | (c3 & 0x3f));\n            continue;\n        } else {\n            if (in + 3 > in_end) [[unlikely]] {\n                return err_in();\n            }\n            if (c >= 0xf8) [[unlikely]] {\n                ALOGW(\"Invalid UTF-8 leading byte: 0x%02x\", c);\n            }\n            // Multiple UTF16 characters with surrogates\n            c2 = *in++; c3 = *in++; c4 = *in++;\n            w = utf8_4b_to_utf32(c, c2, c3, c4);\n            if (w < 0x10000) [[unlikely]] {\n                *out++ = (char16_t)(w);\n            } else {\n                if (out + 2 > out_end) [[unlikely]] {\n                    // Ooops.... not enough room for this surrogate pair.\n                    return out;\n                }\n                *out++ = (char16_t)(((w - 0x10000) >> 10) + 0xd800);\n                *out++ = (char16_t)(((w - 0x10000) & 0x3ff) + 0xdc00);\n            }\n            continue;\n        }\n    }\n    return out;\n}\n\n}\n"
  },
  {
    "path": "libutils/binder/Unicode_test.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Unicode_test\"\n\n#include <sys/mman.h>\n#include <unistd.h>\n\n#include <log/log.h>\n#include <utils/Unicode.h>\n\n#include <gtest/gtest.h>\n\nnamespace android {\n\nclass UnicodeTest : public testing::Test {\nprotected:\n    virtual void SetUp() {\n    }\n\n    virtual void TearDown() {\n    }\n\n    char16_t const * const kSearchString = u\"I am a leaf on the wind.\";\n\n    constexpr static size_t BUFSIZE = 64;       // large enough for all tests\n\n    void TestUTF8toUTF16(std::initializer_list<uint8_t> input,\n                         std::initializer_list<char16_t> expect,\n                         const char* err_msg_length = \"\",\n                         ssize_t expected_length = 0) {\n        uint8_t empty_str[] = {};\n        char16_t output[BUFSIZE];\n\n        const size_t inlen = input.size(), outlen = expect.size();\n        ASSERT_LT(outlen, BUFSIZE);\n\n        const uint8_t *input_data = inlen ? std::data(input) : empty_str;\n        ssize_t measured = utf8_to_utf16_length(input_data, inlen);\n        EXPECT_EQ(expected_length ? : (ssize_t)outlen, measured) << err_msg_length;\n\n        utf8_to_utf16(input_data, inlen, output, outlen + 1);\n        for (size_t i = 0; i < outlen; i++) {\n            EXPECT_EQ(std::data(expect)[i], output[i]);\n        }\n        EXPECT_EQ(0, output[outlen]) << \"should be null terminated\";\n    }\n\n    void TestUTF16toUTF8(std::initializer_list<char16_t> input,\n                         std::initializer_list<char> expect,\n                         const char* err_msg_length = \"\",\n                         ssize_t expected_length = 0) {\n        char16_t empty_str[] = {};\n        char output[BUFSIZE];\n\n        const size_t inlen = input.size(), outlen = expect.size();\n        ASSERT_LT(outlen, BUFSIZE);\n\n        const char16_t *input_data = inlen ? std::data(input) : empty_str;\n        ssize_t measured = utf16_to_utf8_length(input_data, inlen);\n        EXPECT_EQ(expected_length ? : (ssize_t)outlen, measured) << err_msg_length;\n\n        utf16_to_utf8(input_data, inlen, output, outlen + 1);\n        for (size_t i = 0; i < outlen; i++) {\n            EXPECT_EQ(std::data(expect)[i], output[i]);\n        }\n        EXPECT_EQ(0, output[outlen]) << \"should be null terminated\";\n    }\n};\n\nTEST_F(UnicodeTest, UTF8toUTF16ZeroLength) {\n    TestUTF8toUTF16({}, {},\n        \"Zero length input should return zero length output.\");\n}\n\nTEST_F(UnicodeTest, UTF8toUTF16ASCII) {\n    TestUTF8toUTF16(\n        { 0x30 },               // U+0030 or ASCII '0'\n        { 0x0030 },\n        \"ASCII codepoints should have a length of 1 char16_t\");\n}\n\nTEST_F(UnicodeTest, UTF8toUTF16Plane1) {\n    TestUTF8toUTF16(\n        { 0xE2, 0x8C, 0xA3 },   // U+2323 SMILE\n        { 0x2323 },\n        \"Plane 1 codepoints should have a length of 1 char16_t\");\n}\n\nTEST_F(UnicodeTest, UTF8toUTF16Surrogate) {\n    TestUTF8toUTF16(\n        { 0xF0, 0x90, 0x80, 0x80 },   // U+10000\n        { 0xD800, 0xDC00 },\n        \"Surrogate pairs should have a length of 2 char16_t\");\n}\n\nTEST_F(UnicodeTest, UTF8toUTF16TruncatedUTF8) {\n    TestUTF8toUTF16(\n        { 0xE2, 0x8C },       // Truncated U+2323 SMILE\n        { },                  // Conversion should still work but produce nothing\n        \"Truncated UTF-8 should return -1 to indicate invalid\",\n        -1);\n}\n\nTEST_F(UnicodeTest, UTF8toUTF16Normal) {\n    TestUTF8toUTF16({\n        0x30,                   // U+0030, 1 UTF-16 character\n        0xC4, 0x80,             // U+0100, 1 UTF-16 character\n        0xE2, 0x8C, 0xA3,       // U+2323, 1 UTF-16 character\n        0xF0, 0x90, 0x80, 0x80, // U+10000, 2 UTF-16 character\n    }, {\n        0x0030,\n        0x0100,\n        0x2323,\n        0xD800, 0xDC00\n    });\n}\n\nTEST_F(UnicodeTest, UTF8toUTF16Invalid) {\n    // TODO: The current behavior of utf8_to_utf16 is to treat invalid\n    // leading byte (>= 0xf8) as a 4-byte UTF8 sequence, and to treat\n    // invalid trailing byte(s) (i.e. bytes not having MSB set) as if\n    // they are valid and do the normal conversion. However, a better\n    // handling would be to treat invalid sequences as errors, such\n    // cases need to be reported and invalid characters (e.g. U+FFFD)\n    // could be produced at the place of error.  Until a fix is ready\n    // and compatibility is not an issue, we will keep testing the\n    // current behavior\n    TestUTF8toUTF16({\n        0xf8,                   // invalid leading byte\n        0xc4, 0x00,             // U+0100 with invalid trailing byte\n        0xe2, 0x0c, 0xa3,       // U+2323 with invalid trailing bytes\n        0xf0, 0x10, 0x00, 0x00, // U+10000 with invalid trailing bytes\n    }, {\n        0x4022,                 // invalid leading byte (>=0xfc) is treated\n                                // as valid for 4-byte UTF8 sequence\n\t0x000C,\n\t0x00A3,                 // invalid leadnig byte (b'10xxxxxx) is\n                                // treated as valid single UTF-8 byte\n        0xD800,                 // invalid trailing bytes are treated\n        0xDC00,                 // as valid bytes and follow normal\n    });\n}\n\nTEST_F(UnicodeTest, UTF16toUTF8ZeroLength) {\n    // TODO: The current behavior of utf16_to_utf8_length() is that\n    // it returns -1 if the input is a zero length UTF16 string.\n    // This is inconsistent with utf8_to_utf16_length() where a zero\n    // length string returns 0.  However, to fix the current behavior,\n    // we could have compatibility issue.  Until then, we will keep\n    // testing the current behavior\n    TestUTF16toUTF8({}, {},\n        \"Zero length UTF16 input should return length of -1.\", -1);\n}\n\nTEST_F(UnicodeTest, UTF16toUTF8ASCII) {\n    TestUTF16toUTF8(\n        { 0x0030 },  // U+0030 or ASCII '0'\n        { '\\x30' },\n        \"ASCII codepoints in UTF16 should give a length of 1 in UTF8\");\n}\n\nTEST_F(UnicodeTest, UTF16toUTF8Plane1) {\n    TestUTF16toUTF8(\n        { 0x2323 },  // U+2323 SMILE\n        { '\\xE2', '\\x8C', '\\xA3' },\n        \"Plane 1 codepoints should have a length of 3 char in UTF-8\");\n}\n\nTEST_F(UnicodeTest, UTF16toUTF8Surrogate) {\n    TestUTF16toUTF8(\n        { 0xD800, 0xDC00 },  // U+10000\n        { '\\xF0', '\\x90', '\\x80', '\\x80' },\n        \"Surrogate pairs should have a length of 4 chars\");\n}\n\nTEST_F(UnicodeTest, UTF16toUTF8UnpairedSurrogate) {\n    TestUTF16toUTF8(\n        { 0xD800 },     // U+10000 with high surrogate pair only\n        { },            // Unpaired surrogate should be ignored\n        \"A single unpaired high surrogate should have a length of 0 chars\");\n\n    TestUTF16toUTF8(\n        { 0xDC00 },     // U+10000 with low surrogate pair only\n        { },            // Unpaired surrogate should be ignored\n        \"A single unpaired low surrogate should have a length of 0 chars\");\n\n    TestUTF16toUTF8(\n        // U+0030, U+0100, U+10000 with high surrogate pair only, U+2323\n        { 0x0030, 0x0100, 0xDC00, 0x2323 },\n        { '\\x30', '\\xC4', '\\x80', '\\xE2', '\\x8C', '\\xA3' },\n        \"Unpaired high surrogate should be skipped in the middle\");\n\n    TestUTF16toUTF8(\n        // U+0030, U+0100, U+10000 with high surrogate pair only, U+2323\n        { 0x0030, 0x0100, 0xDC00, 0x2323 },\n        { '\\x30', '\\xC4', '\\x80', '\\xE2', '\\x8C', '\\xA3' },\n        \"Unpaired low surrogate should be skipped in the middle\");\n}\n\nTEST_F(UnicodeTest, UTF16toUTF8CorrectInvalidSurrogate) {\n    // http://b/29250543\n    // d841d8 is an invalid start for a surrogate pair. Make sure this is handled by ignoring the\n    // first character in the pair and handling the rest correctly.\n    TestUTF16toUTF8(\n        { 0xD841, 0xD841, 0xDC41 },     // U+20441\n        { '\\xF0', '\\xA0', '\\x91', '\\x81' },\n        \"Invalid start for a surrogate pair should be ignored\");\n}\n\nTEST_F(UnicodeTest, UTF16toUTF8Normal) {\n    TestUTF16toUTF8({\n        0x0024, // U+0024 ($) --> 0x24,           1 UTF-8 byte\n        0x00A3, // U+00A3 (£) --> 0xC2 0xA3,      2 UTF-8 bytes\n        0x0939, // U+0939 (ह) --> 0xE0 0xA4 0xB9, 3 UTF-8 bytes\n        0x20AC, // U+20AC (€) --> 0xE2 0x82 0xAC, 3 UTF-8 bytes\n        0xD55C, // U+D55C (한)--> 0xED 0x95 0x9C, 3 UTF-8 bytes\n        0xD801, 0xDC37, // U+10437 (𐐷) --> 0xF0 0x90 0x90 0xB7, 4 UTF-8 bytes\n    }, {\n        '\\x24',\n        '\\xC2', '\\xA3',\n        '\\xE0', '\\xA4', '\\xB9',\n        '\\xE2', '\\x82', '\\xAC',\n        '\\xED', '\\x95', '\\x9C',\n        '\\xF0', '\\x90', '\\x90', '\\xB7',\n    });\n}\n\nTEST_F(UnicodeTest, strstr16EmptyTarget) {\n    EXPECT_EQ(strstr16(kSearchString, u\"\"), kSearchString)\n            << \"should return the original pointer\";\n}\n\nTEST_F(UnicodeTest, strstr16EmptyTarget_bug) {\n    // In the original code when target is an empty string strlen16() would\n    // start reading the memory until a \"terminating null\" (that is, zero)\n    // character is found.   This happens because \"*target++\" in the original\n    // code would increment the pointer beyond the actual string.\n    void* memptr;\n    const size_t alignment = sysconf(_SC_PAGESIZE);\n    const size_t size = 2 * alignment;\n    ASSERT_EQ(posix_memalign(&memptr, alignment, size), 0);\n    // Fill allocated memory.\n    memset(memptr, 'A', size);\n    // Create a pointer to an \"empty\" string on the first page.\n    char16_t* const emptyString = (char16_t* const)((char*)memptr + alignment - 4);\n    *emptyString = (char16_t)0;\n    // Protect the second page to show that strstr16() violates that.\n    ASSERT_EQ(mprotect((char*)memptr + alignment, alignment, PROT_NONE), 0);\n    // Test strstr16(): when bug is present a segmentation fault is raised.\n    ASSERT_EQ(strstr16((char16_t*)memptr, emptyString), (char16_t*)memptr)\n        << \"should not read beyond the first char16_t.\";\n    // Reset protection of the second page\n    ASSERT_EQ(mprotect((char*)memptr + alignment, alignment, PROT_READ | PROT_WRITE), 0);\n    // Free allocated memory.\n    free(memptr);\n}\n\nTEST_F(UnicodeTest, strstr16SameString) {\n    const char16_t* result = strstr16(kSearchString, kSearchString);\n    EXPECT_EQ(kSearchString, result)\n            << \"should return the original pointer\";\n}\n\nTEST_F(UnicodeTest, strstr16TargetStartOfString) {\n    const char16_t* result = strstr16(kSearchString, u\"I am\");\n    EXPECT_EQ(kSearchString, result)\n            << \"should return the original pointer\";\n}\n\n\nTEST_F(UnicodeTest, strstr16TargetEndOfString) {\n    const char16_t* result = strstr16(kSearchString, u\"wind.\");\n    EXPECT_EQ(kSearchString+19, result);\n}\n\nTEST_F(UnicodeTest, strstr16TargetWithinString) {\n    const char16_t* result = strstr16(kSearchString, u\"leaf\");\n    EXPECT_EQ(kSearchString+7, result);\n}\n\nTEST_F(UnicodeTest, strstr16TargetNotPresent) {\n    const char16_t* result = strstr16(kSearchString, u\"soar\");\n    EXPECT_EQ(nullptr, result);\n}\n\n// http://b/29267949\n// Test that overreading in utf8_to_utf16_length is detected\nTEST_F(UnicodeTest, InvalidUtf8OverreadDetected) {\n    // An utf8 char starting with \\xc4 is two bytes long.\n    // Add extra zeros so no extra memory is read in case the code doesn't\n    // work as expected.\n    static char utf8[] = \"\\xc4\\x00\\x00\\x00\";\n    ASSERT_DEATH(utf8_to_utf16_length((uint8_t *) utf8, strlen(utf8),\n            true /* overreadIsFatal */), \"\" /* regex for ASSERT_DEATH */);\n}\n\n}\n"
  },
  {
    "path": "libutils/binder/VectorImpl.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Vector\"\n\n#include <utils/VectorImpl.h>\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include <log/log.h>\n\n#include \"SharedBuffer.h\"\n\n/*****************************************************************************/\n\n\nnamespace android {\n\n// ----------------------------------------------------------------------------\n\nconst size_t kMinVectorCapacity = 4;\n\nstatic inline size_t max(size_t a, size_t b) {\n    return a>b ? a : b;\n}\n\n// ----------------------------------------------------------------------------\n\nVectorImpl::VectorImpl(size_t itemSize, uint32_t flags)\n    : mStorage(nullptr), mCount(0), mFlags(flags), mItemSize(itemSize)\n{\n}\n\nVectorImpl::VectorImpl(const VectorImpl& rhs)\n    :   mStorage(rhs.mStorage), mCount(rhs.mCount),\n        mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)\n{\n    if (mStorage) {\n        SharedBuffer::bufferFromData(mStorage)->acquire();\n    }\n}\n\nVectorImpl::~VectorImpl()\n{\n    ALOGW_IF(mCount,\n        \"[%p] subclasses of VectorImpl must call finish_vector()\"\n        \" in their destructor. Leaking %d bytes.\",\n        this, (int)(mCount*mItemSize));\n    // We can't call _do_destroy() here because the vtable is already gone.\n}\n\nVectorImpl& VectorImpl::operator = (const VectorImpl& rhs)\n{\n    LOG_ALWAYS_FATAL_IF(mItemSize != rhs.mItemSize,\n        \"Vector<> have different types (this=%p, rhs=%p)\", this, &rhs);\n    if (this != &rhs) {\n        release_storage();\n        if (rhs.mCount) {\n            mStorage = rhs.mStorage;\n            mCount = rhs.mCount;\n            SharedBuffer::bufferFromData(mStorage)->acquire();\n        } else {\n            mStorage = nullptr;\n            mCount = 0;\n        }\n    }\n    return *this;\n}\n\nvoid* VectorImpl::editArrayImpl()\n{\n    if (mStorage) {\n        const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);\n        SharedBuffer* editable = sb->attemptEdit();\n        if (editable == nullptr) {\n            // If we're here, we're not the only owner of the buffer.\n            // We must make a copy of it.\n            editable = SharedBuffer::alloc(sb->size());\n            // Fail instead of returning a pointer to storage that's not\n            // editable. Otherwise we'd be editing the contents of a buffer\n            // for which we're not the only owner, which is undefined behaviour.\n            LOG_ALWAYS_FATAL_IF(editable == nullptr);\n            _do_copy(editable->data(), mStorage, mCount);\n            release_storage();\n            mStorage = editable->data();\n        }\n    }\n    return mStorage;\n}\n\nsize_t VectorImpl::capacity() const\n{\n    if (mStorage) {\n        return SharedBuffer::bufferFromData(mStorage)->size() / mItemSize;\n    }\n    return 0;\n}\n\nssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)\n{\n    return insertArrayAt(vector.arrayImpl(), index, vector.size());\n}\n\nssize_t VectorImpl::appendVector(const VectorImpl& vector)\n{\n    return insertVectorAt(vector, size());\n}\n\nssize_t VectorImpl::insertArrayAt(const void* array, size_t index, size_t length)\n{\n    if (index > size())\n        return BAD_INDEX;\n    void* where = _grow(index, length);\n    if (where) {\n        _do_copy(where, array, length);\n    }\n    return where ? index : (ssize_t)NO_MEMORY;\n}\n\nssize_t VectorImpl::appendArray(const void* array, size_t length)\n{\n    return insertArrayAt(array, size(), length);\n}\n\nssize_t VectorImpl::insertAt(size_t index, size_t numItems)\n{\n    return insertAt(nullptr, index, numItems);\n}\n\nssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)\n{\n    if (index > size())\n        return BAD_INDEX;\n    void* where = _grow(index, numItems);\n    if (where) {\n        if (item) {\n            _do_splat(where, item, numItems);\n        } else {\n            _do_construct(where, numItems);\n        }\n    }\n    return where ? index : (ssize_t)NO_MEMORY;\n}\n\nstatic int sortProxy(const void* lhs, const void* rhs, void* func)\n{\n    return (*(VectorImpl::compar_t)func)(lhs, rhs);\n}\n\nstatus_t VectorImpl::sort(VectorImpl::compar_t cmp)\n{\n    return sort(sortProxy, (void*)cmp);\n}\n\nstatus_t VectorImpl::sort(VectorImpl::compar_r_t cmp, void* state)\n{\n    // the sort must be stable. we're using insertion sort which\n    // is well suited for small and already sorted arrays\n    // for big arrays, it could be better to use mergesort\n    const ssize_t count = size();\n    if (count > 1) {\n        void* array = const_cast<void*>(arrayImpl());\n        void* temp = nullptr;\n        ssize_t i = 1;\n        while (i < count) {\n            void* item = reinterpret_cast<char*>(array) + mItemSize*(i);\n            void* curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);\n            if (cmp(curr, item, state) > 0) {\n\n                if (!temp) {\n                    // we're going to have to modify the array...\n                    array = editArrayImpl();\n                    if (!array) return NO_MEMORY;\n                    temp = malloc(mItemSize);\n                    if (!temp) return NO_MEMORY;\n                    item = reinterpret_cast<char*>(array) + mItemSize*(i);\n                    curr = reinterpret_cast<char*>(array) + mItemSize*(i-1);\n                } else {\n                    _do_destroy(temp, 1);\n                }\n\n                _do_copy(temp, item, 1);\n\n                ssize_t j = i-1;\n                void* next = reinterpret_cast<char*>(array) + mItemSize*(i);\n                do {\n                    _do_destroy(next, 1);\n                    _do_copy(next, curr, 1);\n                    next = curr;\n                    --j;\n                    curr = nullptr;\n                    if (j >= 0) {\n                        curr = reinterpret_cast<char*>(array) + mItemSize*(j);\n                    }\n                } while (j>=0 && (cmp(curr, temp, state) > 0));\n\n                _do_destroy(next, 1);\n                _do_copy(next, temp, 1);\n            }\n            i++;\n        }\n\n        if (temp) {\n            _do_destroy(temp, 1);\n            free(temp);\n        }\n    }\n    return OK;\n}\n\nvoid VectorImpl::pop()\n{\n    if (size())\n        removeItemsAt(size()-1, 1);\n}\n\nvoid VectorImpl::push()\n{\n    push(nullptr);\n}\n\nvoid VectorImpl::push(const void* item)\n{\n    insertAt(item, size());\n}\n\nssize_t VectorImpl::add()\n{\n    return add(nullptr);\n}\n\nssize_t VectorImpl::add(const void* item)\n{\n    return insertAt(item, size());\n}\n\nssize_t VectorImpl::replaceAt(size_t index)\n{\n    return replaceAt(nullptr, index);\n}\n\nssize_t VectorImpl::replaceAt(const void* prototype, size_t index)\n{\n    ALOG_ASSERT(index<size(),\n        \"[%p] replace: index=%d, size=%d\", this, (int)index, (int)size());\n\n    if (index >= size()) {\n        return BAD_INDEX;\n    }\n\n    void* item = editItemLocation(index);\n    if (item != prototype) {\n        if (item == nullptr)\n            return NO_MEMORY;\n        _do_destroy(item, 1);\n        if (prototype == nullptr) {\n            _do_construct(item, 1);\n        } else {\n            _do_copy(item, prototype, 1);\n        }\n    }\n    return ssize_t(index);\n}\n\nssize_t VectorImpl::removeItemsAt(size_t index, size_t count)\n{\n    size_t end;\n    LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(index, count, &end), \"overflow: index=%zu count=%zu\",\n                        index, count);\n    if (end > size()) return BAD_VALUE;\n    _shrink(index, count);\n    return index;\n}\n\nvoid VectorImpl::finish_vector()\n{\n    release_storage();\n    mStorage = nullptr;\n    mCount = 0;\n}\n\nvoid VectorImpl::clear()\n{\n    _shrink(0, mCount);\n}\n\nvoid* VectorImpl::editItemLocation(size_t index)\n{\n    ALOG_ASSERT(index<capacity(),\n        \"[%p] editItemLocation: index=%d, capacity=%d, count=%d\",\n        this, (int)index, (int)capacity(), (int)mCount);\n\n    if (index < capacity()) {\n        void* buffer = editArrayImpl();\n        if (buffer) {\n            return reinterpret_cast<char*>(buffer) + index*mItemSize;\n        }\n    }\n    return nullptr;\n}\n\nconst void* VectorImpl::itemLocation(size_t index) const\n{\n    ALOG_ASSERT(index<capacity(),\n        \"[%p] itemLocation: index=%d, capacity=%d, count=%d\",\n        this, (int)index, (int)capacity(), (int)mCount);\n\n    if (index < capacity()) {\n        const  void* buffer = arrayImpl();\n        if (buffer) {\n            return reinterpret_cast<const char*>(buffer) + index*mItemSize;\n        }\n    }\n    return nullptr;\n}\n\nssize_t VectorImpl::setCapacity(size_t new_capacity)\n{\n    // The capacity must always be greater than or equal to the size\n    // of this vector.\n    if (new_capacity <= size()) {\n        return capacity();\n    }\n\n    size_t new_allocation_size = 0;\n    LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_allocation_size));\n    SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size);\n    if (sb) {\n        void* array = sb->data();\n        _do_copy(array, mStorage, size());\n        release_storage();\n        mStorage = const_cast<void*>(array);\n    } else {\n        return NO_MEMORY;\n    }\n    return new_capacity;\n}\n\nssize_t VectorImpl::resize(size_t size) {\n    ssize_t result = OK;\n    if (size > mCount) {\n        result = insertAt(mCount, size - mCount);\n    } else if (size < mCount) {\n        result = removeItemsAt(size, mCount - size);\n    }\n    return result < 0 ? result : size;\n}\n\nvoid VectorImpl::release_storage()\n{\n    if (mStorage) {\n        const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);\n        if (sb->release(SharedBuffer::eKeepStorage) == 1) {\n            _do_destroy(mStorage, mCount);\n            SharedBuffer::dealloc(sb);\n        }\n    }\n}\n\nvoid* VectorImpl::_grow(size_t where, size_t amount)\n{\n//    ALOGV(\"_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d\",\n//        this, (int)where, (int)amount, (int)mCount, (int)capacity());\n\n    ALOG_ASSERT(where <= mCount,\n            \"[%p] _grow: where=%d, amount=%d, count=%d\",\n            this, (int)where, (int)amount, (int)mCount); // caller already checked\n\n    size_t new_size;\n    LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(mCount, amount, &new_size), \"new_size overflow\");\n\n    if (capacity() < new_size) {\n        // NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)\n        // (sigh..). Also note, the \" + 1\" was necessary to handle the special case\n        // where x == 1, where the resized_capacity will be equal to the old\n        // capacity without the +1. The old calculation wouldn't work properly\n        // if x was zero.\n        //\n        // This approximates the old calculation, using (x + (x/2) + 1) instead.\n        size_t new_capacity = 0;\n        LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(new_size, (new_size / 2), &new_capacity),\n                            \"new_capacity overflow\");\n        LOG_ALWAYS_FATAL_IF(\n                __builtin_add_overflow(new_capacity, static_cast<size_t>(1u), &new_capacity),\n                \"new_capacity overflow\");\n        new_capacity = max(kMinVectorCapacity, new_capacity);\n\n        size_t new_alloc_size = 0;\n        LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_alloc_size),\n                            \"new_alloc_size overflow\");\n\n        // ALOGV(\"grow vector %p, new_capacity=%d\", this, (int)new_capacity);\n        if ((mStorage) &&\n            (mCount==where) &&\n            (mFlags & HAS_TRIVIAL_COPY) &&\n            (mFlags & HAS_TRIVIAL_DTOR))\n        {\n            const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);\n            SharedBuffer* sb = cur_sb->editResize(new_alloc_size);\n            if (sb) {\n                mStorage = sb->data();\n            } else {\n                return nullptr;\n            }\n        } else {\n            SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);\n            if (sb) {\n                void* array = sb->data();\n                if (where != 0) {\n                    _do_copy(array, mStorage, where);\n                }\n                if (where != mCount) {\n                    const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;\n                    void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;\n                    _do_copy(dest, from, mCount-where);\n                }\n                release_storage();\n                mStorage = const_cast<void*>(array);\n            } else {\n                return nullptr;\n            }\n        }\n    } else {\n        void* array = editArrayImpl();\n        if (where != mCount) {\n            const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;\n            void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;\n            _do_move_forward(to, from, mCount - where);\n        }\n    }\n    mCount = new_size;\n    void* free_space = const_cast<void*>(itemLocation(where));\n    return free_space;\n}\n\nvoid VectorImpl::_shrink(size_t where, size_t amount)\n{\n    if (!mStorage)\n        return;\n\n//    ALOGV(\"_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d\",\n//        this, (int)where, (int)amount, (int)mCount, (int)capacity());\n\n    ALOG_ASSERT(where + amount <= mCount,\n            \"[%p] _shrink: where=%d, amount=%d, count=%d\",\n            this, (int)where, (int)amount, (int)mCount); // caller already checked\n\n    size_t new_size;\n    LOG_ALWAYS_FATAL_IF(__builtin_sub_overflow(mCount, amount, &new_size));\n\n    const size_t prev_capacity = capacity();\n    if (new_size < (prev_capacity / 2) && prev_capacity > kMinVectorCapacity) {\n        // NOTE: (new_size * 2) is safe because capacity didn't overflow and\n        // new_size < (capacity / 2)).\n        const size_t new_capacity = max(kMinVectorCapacity, new_size * 2);\n\n        // NOTE: (new_capacity * mItemSize), (where * mItemSize) and\n        // ((where + amount) * mItemSize) beyond this point are safe because\n        // we are always reducing the capacity of the underlying SharedBuffer.\n        // In other words, (old_capacity * mItemSize) did not overflow, and\n        // where < (where + amount) < new_capacity < old_capacity.\n        if ((where == new_size) &&\n            (mFlags & HAS_TRIVIAL_COPY) &&\n            (mFlags & HAS_TRIVIAL_DTOR))\n        {\n            const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);\n            SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);\n            if (sb) {\n                mStorage = sb->data();\n            } else {\n                return;\n            }\n        } else {\n            SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);\n            if (sb) {\n                void* array = sb->data();\n                if (where != 0) {\n                    _do_copy(array, mStorage, where);\n                }\n                if (where != new_size) {\n                    const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;\n                    void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;\n                    _do_copy(dest, from, new_size - where);\n                }\n                release_storage();\n                mStorage = const_cast<void*>(array);\n            } else{\n                return;\n            }\n        }\n    } else {\n        void* array = editArrayImpl();\n        void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;\n        _do_destroy(to, amount);\n        if (where != new_size) {\n            const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;\n            _do_move_backward(to, from, new_size - where);\n        }\n    }\n    mCount = new_size;\n}\n\nsize_t VectorImpl::itemSize() const {\n    return mItemSize;\n}\n\nvoid VectorImpl::_do_construct(void* storage, size_t num) const\n{\n    if (!(mFlags & HAS_TRIVIAL_CTOR)) {\n        do_construct(storage, num);\n    }\n}\n\nvoid VectorImpl::_do_destroy(void* storage, size_t num) const\n{\n    if (!(mFlags & HAS_TRIVIAL_DTOR)) {\n        do_destroy(storage, num);\n    }\n}\n\nvoid VectorImpl::_do_copy(void* dest, const void* from, size_t num) const\n{\n    if (!(mFlags & HAS_TRIVIAL_COPY)) {\n        do_copy(dest, from, num);\n    } else {\n        memcpy(dest, from, num*itemSize());\n    }\n}\n\nvoid VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {\n    do_splat(dest, item, num);\n}\n\nvoid VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {\n    do_move_forward(dest, from, num);\n}\n\nvoid VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {\n    do_move_backward(dest, from, num);\n}\n\n/*****************************************************************************/\n\nSortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)\n    : VectorImpl(itemSize, flags)\n{\n}\n\nSortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)\n: VectorImpl(rhs)\n{\n}\n\nSortedVectorImpl::~SortedVectorImpl()\n{\n}\n\nSortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)\n{\n    return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );\n}\n\nssize_t SortedVectorImpl::indexOf(const void* item) const\n{\n    return _indexOrderOf(item);\n}\n\nsize_t SortedVectorImpl::orderOf(const void* item) const\n{\n    size_t o;\n    _indexOrderOf(item, &o);\n    return o;\n}\n\nssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const\n{\n    if (order) *order = 0;\n    if (isEmpty()) {\n        return NAME_NOT_FOUND;\n    }\n    // binary search\n    ssize_t err = NAME_NOT_FOUND;\n    ssize_t l = 0;\n    ssize_t h = size()-1;\n    ssize_t mid;\n    const void* a = arrayImpl();\n    const size_t s = itemSize();\n    while (l <= h) {\n        mid = l + (h - l)/2;\n        const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);\n        const int c = do_compare(curr, item);\n        if (c == 0) {\n            err = l = mid;\n            break;\n        } else if (c < 0) {\n            l = mid + 1;\n        } else {\n            h = mid - 1;\n        }\n    }\n    if (order) *order = l;\n    return err;\n}\n\nssize_t SortedVectorImpl::add(const void* item)\n{\n    size_t order;\n    ssize_t index = _indexOrderOf(item, &order);\n    if (index < 0) {\n        index = VectorImpl::insertAt(item, order, 1);\n    } else {\n        index = VectorImpl::replaceAt(item, index);\n    }\n    return index;\n}\n\nssize_t SortedVectorImpl::merge(const VectorImpl& vector)\n{\n    // naive merge...\n    if (!vector.isEmpty()) {\n        const void* buffer = vector.arrayImpl();\n        const size_t is = itemSize();\n        size_t s = vector.size();\n        for (size_t i=0 ; i<s ; i++) {\n            ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );\n            if (err<0) {\n                return err;\n            }\n        }\n    }\n    return OK;\n}\n\nssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)\n{\n    // we've merging a sorted vector... nice!\n    ssize_t err = OK;\n    if (!vector.isEmpty()) {\n        // first take care of the case where the vectors are sorted together\n        if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {\n            err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);\n        } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {\n            err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));\n        } else {\n            // this could be made a little better\n            err = merge(static_cast<const VectorImpl&>(vector));\n        }\n    }\n    return err;\n}\n\nssize_t SortedVectorImpl::remove(const void* item)\n{\n    ssize_t i = indexOf(item);\n    if (i>=0) {\n        VectorImpl::removeItemsAt(i, 1);\n    }\n    return i;\n}\n\n/*****************************************************************************/\n\n}; // namespace android\n"
  },
  {
    "path": "libutils/binder/Vector_benchmark.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <benchmark/benchmark.h>\n#include <utils/Vector.h>\n#include <vector>\n\nvoid BM_fill_android_vector(benchmark::State& state) {\n    android::Vector<char> v;\n    while (state.KeepRunning()) {\n        v.push('A');\n    }\n}\nBENCHMARK(BM_fill_android_vector);\n\nvoid BM_fill_std_vector(benchmark::State& state) {\n    std::vector<char> v;\n    while (state.KeepRunning()) {\n        v.push_back('A');\n    }\n}\nBENCHMARK(BM_fill_std_vector);\n\nvoid BM_prepend_android_vector(benchmark::State& state) {\n    android::Vector<char> v;\n    while (state.KeepRunning()) {\n        v.insertAt('A', 0);\n    }\n}\nBENCHMARK(BM_prepend_android_vector);\n\nvoid BM_prepend_std_vector(benchmark::State& state) {\n    std::vector<char> v;\n    while (state.KeepRunning()) {\n        v.insert(v.begin(), 'A');\n    }\n}\nBENCHMARK(BM_prepend_std_vector);\n\nBENCHMARK_MAIN();\n"
  },
  {
    "path": "libutils/binder/Vector_fuzz.cpp",
    "content": "/*\n * Copyright 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <fuzzer/FuzzedDataProvider.h>\n#include <log/log.h>\n#include <utils/Vector.h>\n\n#include <functional>\n\nusing android::Vector;\n\nstatic constexpr uint16_t MAX_VEC_SIZE = 100;\nstatic constexpr bool kLog = false;\n\nstruct NonTrivialDestructor {\n    NonTrivialDestructor() : mInit(1) {}\n    ~NonTrivialDestructor() {\n        LOG_ALWAYS_FATAL_IF(mInit != 1, \"mInit should be 1, but it's: %d\", mInit);\n        mInit--;\n        LOG_ALWAYS_FATAL_IF(mInit != 0, \"mInit should be 0, but it's: %d\", mInit);\n    }\n\n  private:\n    uint8_t mInit;\n};\n\ntemplate <typename T>\nstruct VectorFuzzerData {\n    Vector<T> vector;\n    const std::vector<std::function<void(FuzzedDataProvider&, Vector<T>&)>> funcs = {\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                // operator= Vector<TYPE>, still needs for SortedVector\n                if (kLog) ALOGI(\"operator=\");\n                vector = testVector(provider);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"clear\");\n                vector.clear();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"size\");\n                vector.size();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"isEmpty\");\n                vector.isEmpty();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"capacity\");\n                vector.capacity();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);\n                if (kLog) ALOGI(\"setCapacity\");\n                vector.setCapacity(vectorSize);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);\n                if (kLog) ALOGI(\"resize\");\n                vector.resize(vectorSize);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"array\");\n                vector.array();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"editArray\");\n                vector.editArray();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                if (vector.size() == 0) return;\n                size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);\n                if (kLog) ALOGI(\"operator[]\");\n                vector[idx];  // returns a const value for Vector\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                if (vector.size() == 0) return;\n                size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);\n                if (kLog) ALOGI(\"itemAt\");\n                vector.itemAt(idx);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (vector.size() == 0) return;\n                if (kLog) ALOGI(\"top\");\n                vector.top();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                if (vector.size() == 0) return;\n                size_t idx = provider.ConsumeIntegralInRange<size_t>(0, vector.size() - 1);\n                if (kLog) ALOGI(\"editItemAt\");\n                vector.editItemAt(idx);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (vector.size() == 0) return;\n                if (kLog) ALOGI(\"editTop\");\n                vector.editTop() = T{};\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());\n                Vector vec2 = testVector(provider);\n                if (vec2.size() == 0) return;  // TODO: maybe we should support this?\n                if (kLog) ALOGI(\"insertVectorAt %d of size %zu\", idx, vec2.size());\n                vector.insertVectorAt(vec2, idx);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                if (kLog) ALOGI(\"appendVector\");\n                vector.appendVector(testVector(provider));\n            },\n            // TODO: insertArrayAt\n            // TODO: appendArray\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());\n                uint8_t numItems = provider.ConsumeIntegralInRange<uint8_t>(1, 100);\n                if (kLog) ALOGI(\"insertAt\");\n                vector.insertAt(idx, numItems);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size());\n                uint8_t numItems = provider.ConsumeIntegralInRange<uint8_t>(1, 100);\n                if (kLog) ALOGI(\"insertAt\");\n                vector.insertAt(T{}, idx, numItems);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (vector.size() == 0) return;\n                if (kLog) ALOGI(\"pop\");\n                vector.pop();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"push\");\n                vector.push();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"add\");\n                vector.add();\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"add\");\n                vector.add(T{});\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);\n                if (kLog) ALOGI(\"replaceAt\");\n                vector.replaceAt(idx);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);\n                if (kLog) ALOGI(\"replaceAt\");\n                vector.replaceAt(T{}, idx);\n            },\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                if (vector.size() == 0) return;\n                uint8_t idx = provider.ConsumeIntegralInRange<uint8_t>(0, vector.size() - 1);\n                if (kLog) ALOGI(\"remoteItemsAt\");\n                vector.removeItemsAt(idx);  // TODO: different count\n            },\n            // removeAt is alias for removeItemsAt\n            // TODO: sort\n            [&](FuzzedDataProvider& provider, Vector<T>& vector) {\n                (void)provider;\n                if (kLog) ALOGI(\"getItemSize\");\n                vector.getItemSize();\n            },\n            // TODO: iterators\n    };\n\n    Vector<T> testVector(FuzzedDataProvider& provider) {\n        Vector<T> vec;\n        size_t vectorSize = provider.ConsumeIntegralInRange<size_t>(0, MAX_VEC_SIZE);\n        return vec;\n    }\n\n    void fuzz(FuzzedDataProvider&& provider) {\n        while (provider.remaining_bytes()) {\n            size_t funcIdx = provider.ConsumeIntegralInRange<size_t>(0, funcs.size() - 1);\n            funcs[funcIdx](provider, vector);\n        }\n    }\n};\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    FuzzedDataProvider provider(data, size);\n\n    provider.PickValueInArray<std::function<void()>>({\n            [&]() { VectorFuzzerData<uint8_t>().fuzz(std::move(provider)); },\n            [&]() { VectorFuzzerData<int32_t>().fuzz(std::move(provider)); },\n            [&]() { VectorFuzzerData<NonTrivialDestructor>().fuzz(std::move(provider)); },\n    })();\n\n    return 0;\n}\n"
  },
  {
    "path": "libutils/binder/Vector_test.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"Vector_test\"\n\n#include <stdint.h>\n#include <unistd.h>\n\n#include <android/log.h>\n#include <gtest/gtest.h>\n#include <utils/Vector.h>\n\nnamespace android {\n\nclass VectorTest : public testing::Test {\nprotected:\n    virtual void SetUp() {\n    }\n\n    virtual void TearDown() {\n    }\n\npublic:\n};\n\n\nTEST_F(VectorTest, CopyOnWrite_CopyAndAddElements) {\n\n    Vector<int> vector;\n    Vector<int> other;\n    vector.setCapacity(8);\n\n    vector.add(1);\n    vector.add(2);\n    vector.add(3);\n\n    EXPECT_EQ(3U, vector.size());\n\n    // copy the vector\n    other = vector;\n\n    EXPECT_EQ(3U, other.size());\n\n    // add an element to the first vector\n    vector.add(4);\n\n    // make sure the sizes are correct\n    EXPECT_EQ(4U, vector.size());\n    EXPECT_EQ(3U, other.size());\n\n    // add an element to the copy\n    other.add(5);\n\n    // make sure the sizes are correct\n    EXPECT_EQ(4U, vector.size());\n    EXPECT_EQ(4U, other.size());\n\n    // make sure the content of both vectors are correct\n    EXPECT_EQ(vector[3], 4);\n    EXPECT_EQ(other[3], 5);\n}\n\nTEST_F(VectorTest, SetCapacity_Overflow) {\n  Vector<int> vector;\n  EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), \"Assertion failed\");\n}\n\nTEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {\n  Vector<int> vector;\n  vector.add(1);\n  vector.add(2);\n  vector.add(3);\n  vector.add(4);\n\n  vector.setCapacity(8);\n  ASSERT_EQ(8U, vector.capacity());\n  vector.setCapacity(2);\n  ASSERT_EQ(8U, vector.capacity());\n}\n\nTEST_F(VectorTest, _grow_OverflowSize) {\n  Vector<int> vector;\n  vector.add(1);\n\n  // Checks that the size calculation (not the capacity calculation) doesn't\n  // overflow : the size here will be (1 + SIZE_MAX).\n  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, SIZE_MAX), \"new_size overflow\");\n}\n\nTEST_F(VectorTest, _grow_OverflowCapacityDoubling) {\n  Vector<int> vector;\n\n  // This should fail because the calculated capacity will overflow even though\n  // the size of the vector doesn't.\n  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX - 1)), \"new_capacity overflow\");\n}\n\nTEST_F(VectorTest, _grow_OverflowBufferAlloc) {\n  Vector<int> vector;\n  // This should fail because the capacity * sizeof(int) overflows, even\n  // though the capacity itself doesn't.\n  EXPECT_DEATH(vector.insertArrayAt(nullptr, 0, (SIZE_MAX / 2)), \"new_alloc_size overflow\");\n}\n\nTEST_F(VectorTest, editArray_Shared) {\n  Vector<int> vector1;\n  vector1.add(1);\n  vector1.add(2);\n  vector1.add(3);\n  vector1.add(4);\n\n  Vector<int> vector2 = vector1;\n  ASSERT_EQ(vector1.array(), vector2.array());\n  // We must make a copy here, since we're not the exclusive owners\n  // of this array.\n  ASSERT_NE(vector1.editArray(), vector2.editArray());\n\n  // Vector doesn't implement operator ==.\n  ASSERT_EQ(vector1.size(), vector2.size());\n  for (size_t i = 0; i < vector1.size(); ++i) {\n    EXPECT_EQ(vector1[i], vector2[i]);\n  }\n}\n\nTEST_F(VectorTest, removeItemsAt_overflow) {\n    android::Vector<int> v;\n    for (int i = 0; i < 666; i++) v.add(i);\n\n    ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, 666), \"overflow\");\n    ASSERT_DEATH(v.removeItemsAt(666, SIZE_MAX), \"overflow\");\n    ASSERT_DEATH(v.removeItemsAt(SIZE_MAX, SIZE_MAX), \"overflow\");\n}\n\n} // namespace android\n"
  },
  {
    "path": "libutils/binder/include/utils/Errors.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <errno.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <string>\n\nnamespace android {\n\n/**\n * The type used to return success/failure from frameworks APIs.\n * See the anonymous enum below for valid values.\n */\ntypedef int32_t status_t;\n\n/*\n * Error codes. \n * All error codes are negative values.\n */\n\nenum {\n    OK                = 0,    // Preferred constant for checking success.\n#ifndef NO_ERROR\n    // Win32 #defines NO_ERROR as well.  It has the same value, so there's no\n    // real conflict, though it's a bit awkward.\n    NO_ERROR          = OK,   // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows.\n#endif\n\n    UNKNOWN_ERROR       = (-2147483647-1), // INT32_MIN value\n\n    NO_MEMORY           = -ENOMEM,\n    INVALID_OPERATION   = -ENOSYS,\n    BAD_VALUE           = -EINVAL,\n    BAD_TYPE            = (UNKNOWN_ERROR + 1),\n    NAME_NOT_FOUND      = -ENOENT,\n    PERMISSION_DENIED   = -EPERM,\n    NO_INIT             = -ENODEV,\n    ALREADY_EXISTS      = -EEXIST,\n    DEAD_OBJECT         = -EPIPE,\n    FAILED_TRANSACTION  = (UNKNOWN_ERROR + 2),\n#if !defined(_WIN32)\n    BAD_INDEX           = -EOVERFLOW,\n    NOT_ENOUGH_DATA     = -ENODATA,\n    WOULD_BLOCK         = -EWOULDBLOCK, \n    TIMED_OUT           = -ETIMEDOUT,\n    UNKNOWN_TRANSACTION = -EBADMSG,\n#else    \n    BAD_INDEX           = -E2BIG,\n    NOT_ENOUGH_DATA     = (UNKNOWN_ERROR + 3),\n    WOULD_BLOCK         = (UNKNOWN_ERROR + 4),\n    TIMED_OUT           = (UNKNOWN_ERROR + 5),\n    UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),\n#endif    \n    FDS_NOT_ALLOWED     = (UNKNOWN_ERROR + 7),\n    UNEXPECTED_NULL     = (UNKNOWN_ERROR + 8),\n};\n\n// Human readable name of error\nstd::string statusToString(status_t status);\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/binder/include/utils/LightRefBase.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n/*\n * See documentation in RefBase.h\n */\n\n#include <atomic>\n\n#include <sys/types.h>\n\nnamespace android {\n\nclass ReferenceRenamer;\n\nvoid LightRefBase_reportIncStrongRequireStrongFailed(const void* thiz);\n\ntemplate <class T>\nclass LightRefBase\n{\npublic:\n    inline LightRefBase() : mCount(0) { }\n    inline void incStrong(__attribute__((unused)) const void* id) const {\n        mCount.fetch_add(1, std::memory_order_relaxed);\n    }\n    inline void incStrongRequireStrong(__attribute__((unused)) const void* id) const {\n        if (0 == mCount.fetch_add(1, std::memory_order_relaxed)) {\n            LightRefBase_reportIncStrongRequireStrongFailed(this);\n        }\n    }\n    inline void decStrong(__attribute__((unused)) const void* id) const {\n        if (mCount.fetch_sub(1, std::memory_order_release) == 1) {\n            std::atomic_thread_fence(std::memory_order_acquire);\n            delete static_cast<const T*>(this);\n        }\n    }\n    //! DEBUGGING ONLY: Get current strong ref count.\n    inline int32_t getStrongCount() const {\n        return mCount.load(std::memory_order_relaxed);\n    }\n\nprotected:\n    inline ~LightRefBase() { }\n\nprivate:\n    friend class ReferenceMover;\n    inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }\n    inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }\n\nprivate:\n    mutable std::atomic<int32_t> mCount;\n};\n\n// This is a wrapper around LightRefBase that simply enforces a virtual\n// destructor to eliminate the template requirement of LightRefBase\nclass VirtualLightRefBase : public LightRefBase<VirtualLightRefBase> {\npublic:\n    virtual ~VirtualLightRefBase() = default;\n};\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/binder/include/utils/RefBase.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n// SOME COMMENTS ABOUT USAGE:\n\n// This provides primarily wp<> weak pointer types and RefBase, which work\n// together with sp<> from <StrongPointer.h>.\n\n// sp<> (and wp<>) are a type of smart pointer that use a well defined protocol\n// to operate. As long as the object they are templated with implements that\n// protocol, these smart pointers work. In several places the platform\n// instantiates sp<> with non-RefBase objects; the two are not tied to each\n// other.\n\n// RefBase is such an implementation and it supports strong pointers, weak\n// pointers and some magic features for the binder.\n\n// So, when using RefBase objects, you have the ability to use strong and weak\n// pointers through sp<> and wp<>.\n\n// Normally, when the last strong pointer goes away, the object is destroyed,\n// i.e. it's destructor is called. HOWEVER, parts of its associated memory is not\n// freed until the last weak pointer is released.\n\n// Weak pointers are essentially \"safe\" pointers. They are always safe to\n// access through promote(). They may return nullptr if the object was\n// destroyed because it ran out of strong pointers. This makes them good candidates\n// for keys in a cache for instance.\n\n// Weak pointers remain valid for comparison purposes even after the underlying\n// object has been destroyed. Even if object A is destroyed and its memory reused\n// for B, A remaining weak pointer to A will not compare equal to one to B.\n// This again makes them attractive for use as keys.\n\n// How is this supposed / intended to be used?\n\n// Our recommendation is to use strong references (sp<>) when there is an\n// ownership relation. e.g. when an object \"owns\" another one, use a strong\n// ref. And of course use strong refs as arguments of functions (it's extremely\n// rare that a function will take a wp<>).\n\n// Typically a newly allocated object will immediately be used to initialize\n// a strong pointer, which may then be used to construct or assign to other\n// strong and weak pointers.\n\n// Use weak references when there are no ownership relation. e.g. the keys in a\n// cache (you cannot use plain pointers because there is no safe way to acquire\n// a strong reference from a vanilla pointer).\n\n// This implies that two objects should never (or very rarely) have sp<> on\n// each other, because they can't both own each other.\n\n\n// Caveats with reference counting\n\n// Obviously, circular strong references are a big problem; this creates leaks\n// and it's hard to debug -- except it's in fact really easy because RefBase has\n// tons of debugging code for that. It can basically tell you exactly where the\n// leak is.\n\n// Another problem has to do with destructors with side effects. You must\n// assume that the destructor of reference counted objects can be called AT ANY\n// TIME. For instance code as simple as this:\n\n// void setStuff(const sp<Stuff>& stuff) {\n//   std::lock_guard<std::mutex> lock(mMutex);\n//   mStuff = stuff;\n// }\n\n// is very dangerous. This code WILL deadlock one day or another.\n\n// What isn't obvious is that ~Stuff() can be called as a result of the\n// assignment. And it gets called with the lock held. First of all, the lock is\n// protecting mStuff, not ~Stuff(). Secondly, if ~Stuff() uses its own internal\n// mutex, now you have mutex ordering issues.  Even worse, if ~Stuff() is\n// virtual, now you're calling into \"user\" code (potentially), by that, I mean,\n// code you didn't even write.\n\n// A correct way to write this code is something like:\n\n// void setStuff(const sp<Stuff>& stuff) {\n//   std::unique_lock<std::mutex> lock(mMutex);\n//   sp<Stuff> hold = mStuff;\n//   mStuff = stuff;\n//   lock.unlock();\n// }\n\n// More importantly, reference counted objects should do as little work as\n// possible in their destructor, or at least be mindful that their destructor\n// could be called from very weird and unintended places.\n\n// Other more specific restrictions for wp<> and sp<>:\n\n// Do not construct a strong pointer to \"this\" in an object's constructor.\n// The onFirstRef() callback would be made on an incompletely constructed\n// object.\n// Construction of a weak pointer to \"this\" in an object's constructor is also\n// discouraged. But the implementation was recently changed so that, in the\n// absence of extendObjectLifetime() calls, weak pointers no longer impact\n// object lifetime, and hence this no longer risks premature deallocation,\n// and hence usually works correctly.\n\n// Such strong or weak pointers can be safely created in the RefBase onFirstRef()\n// callback.\n\n// Use of wp::unsafe_get() for any purpose other than debugging is almost\n// always wrong.  Unless you somehow know that there is a longer-lived sp<> to\n// the same object, it may well return a pointer to a deallocated object that\n// has since been reallocated for a different purpose. (And if you know there\n// is a longer-lived sp<>, why not use an sp<> directly?) A wp<> should only be\n// dereferenced by using promote().\n\n// Any object inheriting from RefBase should always be destroyed as the result\n// of a reference count decrement, not via any other means.  Such objects\n// should never be stack allocated, or appear directly as data members in other\n// objects. Objects inheriting from RefBase should have their strong reference\n// count incremented as soon as possible after construction. Usually this\n// will be done via construction of an sp<> to the object, but may instead\n// involve other means of calling RefBase::incStrong().\n// Explicitly deleting or otherwise destroying a RefBase object with outstanding\n// wp<> or sp<> pointers to it will result in an abort or heap corruption.\n\n// It is particularly important not to mix sp<> and direct storage management\n// since the sp from raw pointer constructor is implicit. Thus if a RefBase-\n// -derived object of type T is managed without ever incrementing its strong\n// count, and accidentally passed to f(sp<T>), a strong pointer to the object\n// will be temporarily constructed and destroyed, prematurely deallocating the\n// object, and resulting in heap corruption. None of this would be easily\n// visible in the source. See below on\n// ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION for a compile time\n// option which helps avoid this case.\n\n// Extra Features:\n\n// RefBase::extendObjectLifetime() can be used to prevent destruction of the\n// object while there are still weak references. This is really special purpose\n// functionality to support Binder.\n\n// Wp::promote(), implemented via the attemptIncStrong() member function, is\n// used to try to convert a weak pointer back to a strong pointer.  It's the\n// normal way to try to access the fields of an object referenced only through\n// a wp<>.  Binder code also sometimes uses attemptIncStrong() directly.\n\n// RefBase provides a number of additional callbacks for certain reference count\n// events, as well as some debugging facilities.\n\n// Debugging support can be enabled by turning on DEBUG_REFS in RefBase.cpp.\n// Otherwise little checking is provided.\n\n// Thread safety:\n\n// Like std::shared_ptr, sp<> and wp<> allow concurrent accesses to DIFFERENT\n// sp<> and wp<> instances that happen to refer to the same underlying object.\n// They do NOT support concurrent access (where at least one access is a write)\n// to THE SAME sp<> or wp<>.  In effect, their thread-safety properties are\n// exactly like those of T*, NOT atomic<T*>.\n\n// Safety option: ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION\n//\n// This flag makes the semantics for using a RefBase object with wp<> and sp<>\n// much stricter by disabling implicit conversion from raw pointers to these\n// objects. In order to use this, apply this flag in Android.bp like so:\n//\n//    cflags: [\n//        \"-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION\",\n//    ],\n//\n// REGARDLESS of whether this flag is on, best usage of sp<> is shown below. If\n// this flag is on, no other usage is possible (directly calling RefBase methods\n// is possible, but seeing code using 'incStrong' instead of 'sp<>', for\n// instance, should already set off big alarm bells. With carefully constructed\n// data structures, it should NEVER be necessary to directly use RefBase\n// methods). Proper RefBase usage:\n//\n//    class Foo : virtual public RefBase { ... };\n//\n//    // always construct an sp object with sp::make\n//    sp<Foo> myFoo = sp<Foo>::make(/*args*/);\n//\n//    // if you need a weak pointer, it must be constructed from a strong\n//    // pointer\n//    wp<Foo> weakFoo = myFoo; // NOT myFoo.get()\n//\n//    // If you are inside of a method of Foo and need access to a strong\n//    // explicitly call this function. This documents your intention to code\n//    // readers, and it will give a runtime error for what otherwise would\n//    // be potential double ownership\n//    .... Foo::someMethod(...) {\n//        // asserts if there is a memory issue\n//        sp<Foo> thiz = sp<Foo>::fromExisting(this);\n//    }\n//\n\n#ifndef ANDROID_REF_BASE_H\n#define ANDROID_REF_BASE_H\n\n#include <atomic>\n#include <functional>\n#include <memory>\n#include <type_traits>  // for common_type.\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <stdlib.h>\n#include <string.h>\n\n// LightRefBase used to be declared in this header, so we have to include it\n#include <utils/LightRefBase.h>\n\n#include <utils/StrongPointer.h>\n#include <utils/TypeHelpers.h>\n\n// ---------------------------------------------------------------------------\nnamespace android {\n\n// ---------------------------------------------------------------------------\n\n#define COMPARE_WEAK(_op_)                                      \\\ntemplate<typename U>                                            \\\ninline bool operator _op_ (const U* o) const {                  \\\n    return m_ptr _op_ o;                                        \\\n}                                                               \\\n/* Needed to handle type inference for nullptr: */              \\\ninline bool operator _op_ (const T* o) const {                  \\\n    return m_ptr _op_ o;                                        \\\n}\n\ntemplate<template<typename C> class comparator, typename T, typename U>\nstatic inline bool _wp_compare_(T* a, U* b) {\n    return comparator<typename std::common_type<T*, U*>::type>()(a, b);\n}\n\n// Use std::less and friends to avoid undefined behavior when ordering pointers\n// to different objects.\n#define COMPARE_WEAK_FUNCTIONAL(_op_, _compare_)                 \\\ntemplate<typename U>                                             \\\ninline bool operator _op_ (const U* o) const {                   \\\n    return _wp_compare_<_compare_>(m_ptr, o);                    \\\n}\n\n// ---------------------------------------------------------------------------\n\n// RefererenceRenamer is pure abstract, there is no virtual method\n// implementation to put in a translation unit in order to silence the\n// weak vtables warning.\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wweak-vtables\"\n#endif\n\nclass ReferenceRenamer {\nprotected:\n    // destructor is purposely not virtual so we avoid code overhead from\n    // subclasses; we have to make it protected to guarantee that it\n    // cannot be called from this base class (and to make strict compilers\n    // happy).\n    ~ReferenceRenamer() { }\npublic:\n    virtual void operator()(size_t i) const = 0;\n};\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n// ---------------------------------------------------------------------------\n\nclass RefBase\n{\npublic:\n            void            incStrong(const void* id) const;\n            void            incStrongRequireStrong(const void* id) const;\n            void            decStrong(const void* id) const;\n    \n            void            forceIncStrong(const void* id) const;\n\n            //! DEBUGGING ONLY: Get current strong ref count.\n            int32_t         getStrongCount() const;\n\n    class weakref_type\n    {\n    public:\n        RefBase*            refBase() const;\n\n        void                incWeak(const void* id);\n        void                incWeakRequireWeak(const void* id);\n        void                decWeak(const void* id);\n\n        // acquires a strong reference if there is already one.\n        bool                attemptIncStrong(const void* id);\n\n        // acquires a weak reference if there is already one.\n        // This is not always safe. see ProcessState.cpp and BpBinder.cpp\n        // for proper use.\n        bool                attemptIncWeak(const void* id);\n\n        //! DEBUGGING ONLY: Get current weak ref count.\n        int32_t             getWeakCount() const;\n\n        //! DEBUGGING ONLY: Print references held on object.\n        void                printRefs() const;\n\n        //! DEBUGGING ONLY: Enable tracking for this object.\n        // enable -- enable/disable tracking\n        // retain -- when tracking is enable, if true, then we save a stack trace\n        //           for each reference and dereference; when retain == false, we\n        //           match up references and dereferences and keep only the\n        //           outstanding ones.\n\n        void                trackMe(bool enable, bool retain);\n    };\n\n            weakref_type*   createWeak(const void* id) const;\n            \n            weakref_type*   getWeakRefs() const;\n\n            //! DEBUGGING ONLY: Print references held on object.\n    inline  void            printRefs() const { getWeakRefs()->printRefs(); }\n\n            //! DEBUGGING ONLY: Enable tracking of object.\n    inline  void            trackMe(bool enable, bool retain)\n    { \n        getWeakRefs()->trackMe(enable, retain); \n    }\n\nprotected:\n    // When constructing these objects, prefer using sp::make<>. Using a RefBase\n    // object on the stack or with other refcount mechanisms (e.g.\n    // std::shared_ptr) is inherently wrong. RefBase types have an implicit\n    // ownership model and cannot be safely used with other ownership models.\n\n                            RefBase();\n    virtual                 ~RefBase();\n    \n    //! Flags for extendObjectLifetime()\n    enum {\n        OBJECT_LIFETIME_STRONG  = 0x0000,\n        OBJECT_LIFETIME_WEAK    = 0x0001,\n        OBJECT_LIFETIME_MASK    = 0x0001\n    };\n    \n            void            extendObjectLifetime(int32_t mode);\n            \n    //! Flags for onIncStrongAttempted()\n    enum {\n        FIRST_INC_STRONG = 0x0001\n    };\n    \n    // Invoked after creation of initial strong pointer/reference.\n    virtual void            onFirstRef();\n    // Invoked when either the last strong reference goes away, or we need to undo\n    // the effect of an unnecessary onIncStrongAttempted.\n    virtual void            onLastStrongRef(const void* id);\n    // Only called in OBJECT_LIFETIME_WEAK case.  Returns true if OK to promote to\n    // strong reference. May have side effects if it returns true.\n    // The first flags argument is always FIRST_INC_STRONG.\n    // TODO: Remove initial flag argument.\n    virtual bool            onIncStrongAttempted(uint32_t flags, const void* id);\n    // Invoked in the OBJECT_LIFETIME_WEAK case when the last reference of either\n    // kind goes away.  Unused.\n    // TODO: Remove.\n    virtual void            onLastWeakRef(const void* id);\n\nprivate:\n    friend class weakref_type;\n    class weakref_impl;\n    \n                            RefBase(const RefBase& o);\n            RefBase&        operator=(const RefBase& o);\n\nprivate:\n    friend class ReferenceMover;\n\n    static void renameRefs(size_t n, const ReferenceRenamer& renamer);\n\n    static void renameRefId(weakref_type* ref,\n            const void* old_id, const void* new_id);\n\n    static void renameRefId(RefBase* ref,\n            const void* old_id, const void* new_id);\n\n        weakref_impl* const mRefs;\n};\n\n// ---------------------------------------------------------------------------\n\ntemplate <typename T>\nclass wp\n{\npublic:\n    typedef typename RefBase::weakref_type weakref_type;\n\n    inline constexpr wp() : m_ptr(nullptr), m_refs(nullptr) { }\n\n    // if nullptr, returns nullptr\n    //\n    // if a weak pointer is already available, this will retrieve it,\n    // otherwise, this will abort\n    static inline wp<T> fromExisting(T* other);\n\n    // for more information about this flag, see above\n#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)\n    wp(std::nullptr_t) : wp() {}\n#else\n    wp(T* other);  // NOLINT(implicit)\n    template <typename U>\n    wp(U* other);  // NOLINT(implicit)\n    wp& operator=(T* other);\n    template <typename U>\n    wp& operator=(U* other);\n#endif\n\n    wp(const wp<T>& other);\n    explicit wp(const sp<T>& other);\n\n    template<typename U> wp(const sp<U>& other);  // NOLINT(implicit)\n    template<typename U> wp(const wp<U>& other);  // NOLINT(implicit)\n\n    ~wp();\n\n    // Assignment\n\n    wp& operator = (const wp<T>& other);\n    wp& operator = (const sp<T>& other);\n\n    template<typename U> wp& operator = (const wp<U>& other);\n    template<typename U> wp& operator = (const sp<U>& other);\n\n    void set_object_and_refs(T* other, weakref_type* refs);\n\n    // promotion to sp\n\n    sp<T> promote() const;\n\n    // Reset\n\n    void clear();\n\n    // Accessors\n\n    inline  weakref_type* get_refs() const { return m_refs; }\n\n    inline  T* unsafe_get() const { return m_ptr; }\n\n    // Operators\n\n    COMPARE_WEAK(==)\n    COMPARE_WEAK(!=)\n    COMPARE_WEAK_FUNCTIONAL(>, std::greater)\n    COMPARE_WEAK_FUNCTIONAL(<, std::less)\n    COMPARE_WEAK_FUNCTIONAL(<=, std::less_equal)\n    COMPARE_WEAK_FUNCTIONAL(>=, std::greater_equal)\n\n    template<typename U>\n    inline bool operator == (const wp<U>& o) const {\n        return m_refs == o.m_refs;  // Implies m_ptr == o.mptr; see invariants below.\n    }\n\n    template<typename U>\n    inline bool operator == (const sp<U>& o) const {\n        // Just comparing m_ptr fields is often dangerous, since wp<> may refer to an older\n        // object at the same address.\n        if (o == nullptr) {\n          return m_ptr == nullptr;\n        } else {\n          return m_refs == o->getWeakRefs();  // Implies m_ptr == o.mptr.\n        }\n    }\n\n    template<typename U>\n    inline bool operator != (const sp<U>& o) const {\n        return !(*this == o);\n    }\n\n    template<typename U>\n    inline bool operator > (const wp<U>& o) const {\n        if (m_ptr == o.m_ptr) {\n            return _wp_compare_<std::greater>(m_refs, o.m_refs);\n        } else {\n            return _wp_compare_<std::greater>(m_ptr, o.m_ptr);\n        }\n    }\n\n    template<typename U>\n    inline bool operator < (const wp<U>& o) const {\n        if (m_ptr == o.m_ptr) {\n            return _wp_compare_<std::less>(m_refs, o.m_refs);\n        } else {\n            return _wp_compare_<std::less>(m_ptr, o.m_ptr);\n        }\n    }\n    template<typename U> inline bool operator != (const wp<U>& o) const { return !operator == (o); }\n    template<typename U> inline bool operator <= (const wp<U>& o) const { return !operator > (o); }\n    template<typename U> inline bool operator >= (const wp<U>& o) const { return !operator < (o); }\n\nprivate:\n    template<typename Y> friend class sp;\n    template<typename Y> friend class wp;\n\n    T*              m_ptr;\n    weakref_type*   m_refs;\n};\n\n#undef COMPARE_WEAK\n#undef COMPARE_WEAK_FUNCTIONAL\n\n// ---------------------------------------------------------------------------\n// No user serviceable parts below here.\n\n// Implementation invariants:\n// Either\n// 1) m_ptr and m_refs are both null, or\n// 2) m_refs == m_ptr->mRefs, or\n// 3) *m_ptr is no longer live, and m_refs points to the weakref_type object that corresponded\n//    to m_ptr while it was live. *m_refs remains live while a wp<> refers to it.\n//\n// The m_refs field in a RefBase object is allocated on construction, unique to that RefBase\n// object, and never changes. Thus if two wp's have identical m_refs fields, they are either both\n// null or point to the same object. If two wp's have identical m_ptr fields, they either both\n// point to the same live object and thus have the same m_ref fields, or at least one of the\n// objects is no longer live.\n//\n// Note that the above comparison operations go out of their way to provide an ordering consistent\n// with ordinary pointer comparison; otherwise they could ignore m_ptr, and just compare m_refs.\n\ntemplate <typename T>\nwp<T> wp<T>::fromExisting(T* other) {\n    if (!other) return nullptr;\n\n    auto refs = other->getWeakRefs();\n    refs->incWeakRequireWeak(other);\n\n    wp<T> ret;\n    ret.m_ptr = other;\n    ret.m_refs = refs;\n    return ret;\n}\n\n#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)\ntemplate<typename T>\nwp<T>::wp(T* other)\n    : m_ptr(other)\n{\n    m_refs = other ? other->createWeak(this) : nullptr;\n}\n\ntemplate <typename T>\ntemplate <typename U>\nwp<T>::wp(U* other) : m_ptr(other) {\n    m_refs = other ? other->createWeak(this) : nullptr;\n}\n\ntemplate <typename T>\nwp<T>& wp<T>::operator=(T* other) {\n    weakref_type* newRefs = other ? other->createWeak(this) : nullptr;\n    if (m_ptr) m_refs->decWeak(this);\n    m_ptr = other;\n    m_refs = newRefs;\n    return *this;\n}\n\ntemplate <typename T>\ntemplate <typename U>\nwp<T>& wp<T>::operator=(U* other) {\n    weakref_type* newRefs = other ? other->createWeak(this) : 0;\n    if (m_ptr) m_refs->decWeak(this);\n    m_ptr = other;\n    m_refs = newRefs;\n    return *this;\n}\n#endif\n\ntemplate<typename T>\nwp<T>::wp(const wp<T>& other)\n    : m_ptr(other.m_ptr), m_refs(other.m_refs)\n{\n    if (m_ptr) m_refs->incWeak(this);\n}\n\ntemplate<typename T>\nwp<T>::wp(const sp<T>& other)\n    : m_ptr(other.m_ptr)\n{\n    m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;\n}\n\ntemplate<typename T> template<typename U>\nwp<T>::wp(const wp<U>& other)\n    : m_ptr(other.m_ptr)\n{\n    if (m_ptr) {\n        m_refs = other.m_refs;\n        m_refs->incWeak(this);\n    } else {\n        m_refs = nullptr;\n    }\n}\n\ntemplate<typename T> template<typename U>\nwp<T>::wp(const sp<U>& other)\n    : m_ptr(other.m_ptr)\n{\n    m_refs = m_ptr ? m_ptr->createWeak(this) : nullptr;\n}\n\ntemplate<typename T>\nwp<T>::~wp()\n{\n    if (m_ptr) m_refs->decWeak(this);\n}\n\ntemplate<typename T>\nwp<T>& wp<T>::operator = (const wp<T>& other)\n{\n    weakref_type* otherRefs(other.m_refs);\n    T* otherPtr(other.m_ptr);\n    if (otherPtr) otherRefs->incWeak(this);\n    if (m_ptr) m_refs->decWeak(this);\n    m_ptr = otherPtr;\n    m_refs = otherRefs;\n    return *this;\n}\n\ntemplate<typename T>\nwp<T>& wp<T>::operator = (const sp<T>& other)\n{\n    weakref_type* newRefs =\n        other != nullptr ? other->createWeak(this) : nullptr;\n    T* otherPtr(other.m_ptr);\n    if (m_ptr) m_refs->decWeak(this);\n    m_ptr = otherPtr;\n    m_refs = newRefs;\n    return *this;\n}\n\ntemplate<typename T> template<typename U>\nwp<T>& wp<T>::operator = (const wp<U>& other)\n{\n    weakref_type* otherRefs(other.m_refs);\n    U* otherPtr(other.m_ptr);\n    if (otherPtr) otherRefs->incWeak(this);\n    if (m_ptr) m_refs->decWeak(this);\n    m_ptr = otherPtr;\n    m_refs = otherRefs;\n    return *this;\n}\n\ntemplate<typename T> template<typename U>\nwp<T>& wp<T>::operator = (const sp<U>& other)\n{\n    weakref_type* newRefs = other != nullptr ? other->createWeak(this) : nullptr;\n    U* otherPtr(other.m_ptr);\n    if (m_ptr) m_refs->decWeak(this);\n    m_ptr = otherPtr;\n    m_refs = newRefs;\n    return *this;\n}\n\ntemplate<typename T>\nvoid wp<T>::set_object_and_refs(T* other, weakref_type* refs)\n{\n    if (other) refs->incWeak(this);\n    if (m_ptr) m_refs->decWeak(this);\n    m_ptr = other;\n    m_refs = refs;\n}\n\ntemplate<typename T>\nsp<T> wp<T>::promote() const\n{\n    sp<T> result;\n    if (m_ptr && m_refs->attemptIncStrong(&result)) {\n        result.set_pointer(m_ptr);\n    }\n    return result;\n}\n\ntemplate<typename T>\nvoid wp<T>::clear()\n{\n    if (m_ptr) {\n        m_refs->decWeak(this);\n        m_refs = nullptr;\n        m_ptr = nullptr;\n    }\n}\n\n// ---------------------------------------------------------------------------\n\n// this class just serves as a namespace so TYPE::moveReferences can stay\n// private.\nclass ReferenceMover {\npublic:\n    // it would be nice if we could make sure no extra code is generated\n    // for sp<TYPE> or wp<TYPE> when TYPE is a descendant of RefBase:\n    // Using a sp<RefBase> override doesn't work; it's a bit like we wanted\n    // a template<typename TYPE inherits RefBase> template...\n\n    template<typename TYPE> static inline\n    void move_references(sp<TYPE>* dest, sp<TYPE> const* src, size_t n) {\n\n        class Renamer : public ReferenceRenamer {\n            sp<TYPE>* d_;\n            sp<TYPE> const* s_;\n            virtual void operator()(size_t i) const {\n                // The id are known to be the sp<>'s this pointer\n                TYPE::renameRefId(d_[i].get(), &s_[i], &d_[i]);\n            }\n        public:\n            Renamer(sp<TYPE>* d, sp<TYPE> const* s) : d_(d), s_(s) { }\n            virtual ~Renamer() { }\n        };\n\n        memmove(dest, src, n*sizeof(sp<TYPE>));\n        TYPE::renameRefs(n, Renamer(dest, src));\n    }\n\n\n    template<typename TYPE> static inline\n    void move_references(wp<TYPE>* dest, wp<TYPE> const* src, size_t n) {\n\n        class Renamer : public ReferenceRenamer {\n            wp<TYPE>* d_;\n            wp<TYPE> const* s_;\n            virtual void operator()(size_t i) const {\n                // The id are known to be the wp<>'s this pointer\n                TYPE::renameRefId(d_[i].get_refs(), &s_[i], &d_[i]);\n            }\n        public:\n            Renamer(wp<TYPE>* rd, wp<TYPE> const* rs) : d_(rd), s_(rs) { }\n            virtual ~Renamer() { }\n        };\n\n        memmove(dest, src, n*sizeof(wp<TYPE>));\n        TYPE::renameRefs(n, Renamer(dest, src));\n    }\n};\n\n// specialization for moving sp<> and wp<> types.\n// these are used by the [Sorted|Keyed]Vector<> implementations\n// sp<> and wp<> need to be handled specially, because they do not\n// have trivial copy operation in the general case (see RefBase.cpp\n// when DEBUG ops are enabled), but can be implemented very\n// efficiently in most cases.\n\ntemplate<typename TYPE> inline\nvoid move_forward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {\n    ReferenceMover::move_references(d, s, n);\n}\n\ntemplate<typename TYPE> inline\nvoid move_backward_type(sp<TYPE>* d, sp<TYPE> const* s, size_t n) {\n    ReferenceMover::move_references(d, s, n);\n}\n\ntemplate<typename TYPE> inline\nvoid move_forward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {\n    ReferenceMover::move_references(d, s, n);\n}\n\ntemplate<typename TYPE> inline\nvoid move_backward_type(wp<TYPE>* d, wp<TYPE> const* s, size_t n) {\n    ReferenceMover::move_references(d, s, n);\n}\n\n}  // namespace android\n\nnamespace libutilsinternal {\ntemplate <typename T, typename = void>\nstruct is_complete_type : std::false_type {};\n\ntemplate <typename T>\nstruct is_complete_type<T, decltype(void(sizeof(T)))> : std::true_type {};\n}  // namespace libutilsinternal\n\nnamespace std {\n\n// Define `RefBase` specific versions of `std::make_shared` and\n// `std::make_unique` to block people from using them. Using them to allocate\n// `RefBase` objects results in double ownership. Use\n// `sp<T>::make(...)` instead.\n//\n// Note: We exclude incomplete types because `std::is_base_of` is undefined in\n// that case.\n\ntemplate <typename T, typename... Args,\n          typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,\n          typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>\nshared_ptr<T> make_shared(Args...) {  // SEE COMMENT ABOVE.\n    static_assert(!std::is_base_of<android::RefBase, T>::value, \"Must use RefBase with sp<>\");\n}\n\ntemplate <typename T, typename... Args,\n          typename std::enable_if<libutilsinternal::is_complete_type<T>::value, bool>::value = true,\n          typename std::enable_if<std::is_base_of<android::RefBase, T>::value, bool>::value = true>\nunique_ptr<T> make_unique(Args...) {  // SEE COMMENT ABOVE.\n    static_assert(!std::is_base_of<android::RefBase, T>::value, \"Must use RefBase with sp<>\");\n}\n\n}  // namespace std\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_REF_BASE_H\n"
  },
  {
    "path": "libutils/binder/include/utils/String16.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_STRING16_H\n#define ANDROID_STRING16_H\n\n#include <iostream>\n#include <string>\n#include <string_view>\n\n#include <utils/Errors.h>\n#include <utils/String8.h>\n#include <utils/TypeHelpers.h>\n\n#if __cplusplus >= 202002L\n#include <compare>\n#endif\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\n// ---------------------------------------------------------------------------\n\ntemplate <size_t N>\nclass StaticString16;\n\n// DO NOT USE: please use std::u16string\n\n//! This is a string holding UTF-16 characters.\nclass String16\n{\npublic:\n                                String16();\n                                String16(const String16& o);\n                                String16(String16&& o) noexcept;\n                                String16(const String16& o,\n                                         size_t len,\n                                         size_t begin=0);\n    explicit                    String16(const char16_t* o);\n    explicit                    String16(const char16_t* o, size_t len);\n    explicit                    String16(const String8& o);\n    explicit                    String16(const char* o);\n    explicit                    String16(const char* o, size_t len);\n\n                                ~String16();\n\n    inline  const char16_t*     c_str() const;\n\n            size_t              size() const;\n    inline  bool                empty() const;\n\n    inline  size_t              length() const;\n\n            void                setTo(const String16& other);\n            status_t            setTo(const char16_t* other);\n            status_t            setTo(const char16_t* other, size_t len);\n            status_t            setTo(const String16& other,\n                                      size_t len,\n                                      size_t begin=0);\n\n            status_t            append(const String16& other);\n            status_t            append(const char16_t* other, size_t len);\n\n    inline  String16&           operator=(const String16& other);\n            String16&           operator=(String16&& other) noexcept;\n\n    inline  String16&           operator+=(const String16& other);\n    inline  String16            operator+(const String16& other) const;\n\n            status_t            insert(size_t pos, const char16_t* chrs);\n            status_t            insert(size_t pos,\n                                       const char16_t* chrs, size_t len);\n\n            ssize_t             findFirst(char16_t c) const;\n            ssize_t             findLast(char16_t c) const;\n\n            bool                startsWith(const String16& prefix) const;\n            bool                startsWith(const char16_t* prefix) const;\n\n            bool                contains(const char16_t* chrs) const;\n    inline  bool                contains(const String16& other) const;\n\n            status_t            replaceAll(char16_t replaceThis,\n                                           char16_t withThis);\n\n    inline  int                 compare(const String16& other) const;\n\n    inline  bool                operator<(const String16& other) const;\n    inline  bool                operator<=(const String16& other) const;\n    inline  bool                operator==(const String16& other) const;\n    inline  bool                operator!=(const String16& other) const;\n    inline  bool                operator>=(const String16& other) const;\n    inline  bool                operator>(const String16& other) const;\n#if __cplusplus >= 202002L\n    inline std::strong_ordering operator<=>(const String16& other) const;\n#endif\n\n    inline  bool                operator<(const char16_t* other) const;\n    inline  bool                operator<=(const char16_t* other) const;\n    inline  bool                operator==(const char16_t* other) const;\n    inline  bool                operator!=(const char16_t* other) const;\n    inline  bool                operator>=(const char16_t* other) const;\n    inline  bool                operator>(const char16_t* other) const;\n#if __cplusplus >= 202002L\n    inline std::strong_ordering operator<=>(const char16_t* other) const;\n#endif\n\n    inline                      operator const char16_t*() const;\n\n    // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much\n    // lighter and if one needs, they can still create u16string from u16string_view.\n    inline                      operator std::u16string_view() const;\n\n    // Static and non-static String16 behave the same for the users, so\n    // this method isn't of much use for the users. It is public for testing.\n            bool                isStaticString() const;\n\n  private:\n    /*\n     * A flag indicating the type of underlying buffer.\n     */\n    static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000;\n\n    /*\n     * alloc() returns void* so that SharedBuffer class is not exposed.\n     */\n    static void* alloc(size_t size);\n    static char16_t* allocFromUTF8(const char* u8str, size_t u8len);\n    static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len);\n\n    /*\n     * edit() and editResize() return void* so that SharedBuffer class\n     * is not exposed.\n     */\n    void* edit();\n    void* editResize(size_t new_size);\n\n    void acquire();\n    void release();\n\n    size_t staticStringSize() const;\n\n    const char16_t* mString;\n\nprotected:\n    /*\n     * Data structure used to allocate static storage for static String16.\n     *\n     * Note that this data structure and SharedBuffer are used interchangably\n     * as the underlying data structure for a String16.  Therefore, the layout\n     * of this data structure must match the part in SharedBuffer that is\n     * visible to String16.\n     */\n    template <size_t N>\n    struct StaticData {\n        // The high bit of 'size' is used as a flag.\n        static_assert(N - 1 < kIsSharedBufferAllocated, \"StaticString16 too long!\");\n        constexpr StaticData() : size(N - 1), data{0} {}\n        const uint32_t size;\n        char16_t data[N];\n\n        constexpr StaticData(const StaticData<N>&) = default;\n    };\n\n    /*\n     * Helper function for constructing a StaticData object.\n     */\n    template <size_t N>\n    static constexpr const StaticData<N> makeStaticData(const char16_t (&s)[N]) {\n        StaticData<N> r;\n        // The 'size' field is at the same location where mClientMetadata would\n        // be for a SharedBuffer.  We do NOT set kIsSharedBufferAllocated flag\n        // here.\n        for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i];\n        return r;\n    }\n\n    template <size_t N>\n    explicit constexpr String16(const StaticData<N>& s) : mString(s.data) {}\n\n// These symbols are for potential backward compatibility with prebuilts. To be removed.\n#ifdef ENABLE_STRING16_OBSOLETE_METHODS\npublic:\n#else\nprivate:\n#endif\n    inline  const char16_t*     string() const;\n};\n\n// String16 can be trivially moved using memcpy() because moving does not\n// require any change to the underlying SharedBuffer contents or reference count.\nANDROID_TRIVIAL_MOVE_TRAIT(String16)\n\nstatic inline std::ostream& operator<<(std::ostream& os, const String16& str) {\n    os << String8(str);\n    return os;\n}\n\n// ---------------------------------------------------------------------------\n\n/*\n * A StaticString16 object is a specialized String16 object.  Instead of holding\n * the string data in a ref counted SharedBuffer object, it holds data in a\n * buffer within StaticString16 itself.  Note that this buffer is NOT ref\n * counted and is assumed to be available for as long as there is at least a\n * String16 object using it.  Therefore, one must be extra careful to NEVER\n * assign a StaticString16 to a String16 that outlives the StaticString16\n * object.\n *\n * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES.\n *\n * A StaticString16 SHOULD NEVER APPEAR IN APIs.  USE String16 INSTEAD.\n */\ntemplate <size_t N>\nclass StaticString16 : public String16 {\npublic:\n    constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {}\n\n    constexpr StaticString16(const StaticString16<N>& other)\n        : String16(mData), mData(other.mData) {}\n\n    constexpr StaticString16(const StaticString16<N>&&) = delete;\n\n    // There is no reason why one would want to 'new' a StaticString16.  Delete\n    // it to discourage misuse.\n    static void* operator new(std::size_t) = delete;\n\nprivate:\n    const StaticData<N> mData;\n};\n\ntemplate <typename F>\nStaticString16(const F&)->StaticString16<sizeof(F) / sizeof(char16_t)>;\n\n// ---------------------------------------------------------------------------\n// No user servicable parts below.\n\ninline int compare_type(const String16& lhs, const String16& rhs)\n{\n    return lhs.compare(rhs);\n}\n\ninline int strictly_order_type(const String16& lhs, const String16& rhs)\n{\n    return compare_type(lhs, rhs) < 0;\n}\n\ninline const char16_t* String16::c_str() const\n{\n    return mString;\n}\n\ninline const char16_t* String16::string() const\n{\n    return mString;\n}\n\ninline bool String16::empty() const\n{\n    return length() == 0;\n}\n\ninline size_t String16::length() const\n{\n    return size();\n}\n\ninline bool String16::contains(const String16& other) const\n{\n    return contains(other.c_str());\n}\n\ninline String16& String16::operator=(const String16& other)\n{\n    setTo(other);\n    return *this;\n}\n\ninline String16& String16::operator+=(const String16& other)\n{\n    append(other);\n    return *this;\n}\n\ninline String16 String16::operator+(const String16& other) const\n{\n    String16 tmp(*this);\n    tmp += other;\n    return tmp;\n}\n\ninline int String16::compare(const String16& other) const\n{\n    return strzcmp16(mString, size(), other.mString, other.size());\n}\n\ninline bool String16::operator<(const String16& other) const\n{\n    return strzcmp16(mString, size(), other.mString, other.size()) < 0;\n}\n\ninline bool String16::operator<=(const String16& other) const\n{\n    return strzcmp16(mString, size(), other.mString, other.size()) <= 0;\n}\n\ninline bool String16::operator==(const String16& other) const\n{\n    return strzcmp16(mString, size(), other.mString, other.size()) == 0;\n}\n\ninline bool String16::operator!=(const String16& other) const\n{\n    return strzcmp16(mString, size(), other.mString, other.size()) != 0;\n}\n\ninline bool String16::operator>=(const String16& other) const\n{\n    return strzcmp16(mString, size(), other.mString, other.size()) >= 0;\n}\n\ninline bool String16::operator>(const String16& other) const\n{\n    return strzcmp16(mString, size(), other.mString, other.size()) > 0;\n}\n\n#if __cplusplus >= 202002L\ninline std::strong_ordering String16::operator<=>(const String16& other) const {\n    int result = strzcmp16(mString, size(), other.mString, other.size());\n    if (result == 0) {\n        return std::strong_ordering::equal;\n    } else if (result < 0) {\n        return std::strong_ordering::less;\n    } else {\n        return std::strong_ordering::greater;\n    }\n}\n#endif\n\ninline bool String16::operator<(const char16_t* other) const\n{\n    return strcmp16(mString, other) < 0;\n}\n\ninline bool String16::operator<=(const char16_t* other) const\n{\n    return strcmp16(mString, other) <= 0;\n}\n\ninline bool String16::operator==(const char16_t* other) const\n{\n    return strcmp16(mString, other) == 0;\n}\n\ninline bool String16::operator!=(const char16_t* other) const\n{\n    return strcmp16(mString, other) != 0;\n}\n\ninline bool String16::operator>=(const char16_t* other) const\n{\n    return strcmp16(mString, other) >= 0;\n}\n\ninline bool String16::operator>(const char16_t* other) const\n{\n    return strcmp16(mString, other) > 0;\n}\n\n#if __cplusplus >= 202002L\ninline std::strong_ordering String16::operator<=>(const char16_t* other) const {\n    int result = strcmp16(mString, other);\n    if (result == 0) {\n        return std::strong_ordering::equal;\n    } else if (result < 0) {\n        return std::strong_ordering::less;\n    } else {\n        return std::strong_ordering::greater;\n    }\n}\n#endif\n\ninline String16::operator const char16_t*() const\n{\n    return mString;\n}\n\ninline String16::operator std::u16string_view() const\n{\n    return {mString, length()};\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_STRING16_H\n"
  },
  {
    "path": "libutils/binder/include/utils/String8.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_STRING8_H\n#define ANDROID_STRING8_H\n\n#include <iostream>\n#include <string>\n#include <string_view>\n\n#include <utils/Errors.h>\n#include <utils/Unicode.h>\n#include <utils/TypeHelpers.h>\n\n#include <string.h> // for strcmp\n#include <stdarg.h>\n\n#if __cplusplus >= 202002L\n#include <compare>\n#endif\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\nclass String16;\n\n// DO NOT USE: please use std::string\n\n//! This is a string holding UTF-8 characters. Does not allow the value more\n// than 0x10FFFF, which is not valid unicode codepoint.\nclass String8\n{\npublic:\n                                String8();\n                                String8(const String8& o);\n    explicit                    String8(const char* o);\n    explicit                    String8(const char* o, size_t numChars);\n    explicit                    String8(std::string_view o);\n\n    explicit                    String8(const String16& o);\n    explicit                    String8(const char16_t* o);\n    explicit                    String8(const char16_t* o, size_t numChars);\n    explicit                    String8(const char32_t* o);\n    explicit                    String8(const char32_t* o, size_t numChars);\n                                ~String8();\n\n    static String8              format(const char* fmt, ...) __attribute__((format (printf, 1, 2)));\n    static String8              formatV(const char* fmt, va_list args);\n\n    inline  const char*         c_str() const;\n\n    inline  size_t              size() const;\n    inline  size_t              bytes() const;\n    inline  bool                empty() const;\n\n            size_t              length() const;\n\n            void                clear();\n\n            void                setTo(const String8& other);\n            status_t            setTo(const char* other);\n            status_t            setTo(const char* other, size_t numChars);\n            status_t            setTo(const char16_t* other, size_t numChars);\n            status_t            setTo(const char32_t* other,\n                                      size_t length);\n\n            status_t            append(const String8& other);\n            status_t            append(const char* other);\n            status_t            append(const char* other, size_t numChars);\n\n            status_t            appendFormat(const char* fmt, ...)\n                    __attribute__((format (printf, 2, 3)));\n            status_t            appendFormatV(const char* fmt, va_list args);\n\n    inline  String8&            operator=(const String8& other);\n    inline  String8&            operator=(const char* other);\n\n    inline  String8&            operator+=(const String8& other);\n    inline  String8             operator+(const String8& other) const;\n\n    inline  String8&            operator+=(const char* other);\n    inline  String8             operator+(const char* other) const;\n\n    inline  int                 compare(const String8& other) const;\n\n    inline  bool                operator<(const String8& other) const;\n    inline  bool                operator<=(const String8& other) const;\n    inline  bool                operator==(const String8& other) const;\n    inline  bool                operator!=(const String8& other) const;\n    inline  bool                operator>=(const String8& other) const;\n    inline  bool                operator>(const String8& other) const;\n#if __cplusplus >= 202002L\n    inline std::strong_ordering operator<=>(const String8& other) const;\n#endif\n\n    inline  bool                operator<(const char* other) const;\n    inline  bool                operator<=(const char* other) const;\n    inline  bool                operator==(const char* other) const;\n    inline  bool                operator!=(const char* other) const;\n    inline  bool                operator>=(const char* other) const;\n    inline  bool                operator>(const char* other) const;\n#if __cplusplus >= 202002L\n    inline std::strong_ordering operator<=>(const char* other) const;\n#endif\n\n    inline                      operator const char*() const;\n\n    inline explicit             operator std::string_view() const;\n\n            char*               lockBuffer(size_t size);\n            void                unlockBuffer();\n            status_t            unlockBuffer(size_t size);\n\n            // return the index of the first byte of other in this at or after\n            // start, or -1 if not found\n            ssize_t             find(const char* other, size_t start = 0) const;\n    inline  ssize_t             find(const String8& other, size_t start = 0) const;\n\n            // return true if this string contains the specified substring\n    inline  bool                contains(const char* other) const;\n    inline  bool                contains(const String8& other) const;\n\n            // removes all occurrence of the specified substring\n            // returns true if any were found and removed\n            bool                removeAll(const char* other);\n    inline  bool                removeAll(const String8& other);\n\n            void                toLower();\n\nprivate:\n            String8 getPathDir(void) const;\n            String8 getPathExtension(void) const;\n\n            status_t            real_append(const char* other, size_t numChars);\n\n            const char* mString;\n\n// These symbols are for potential backward compatibility with prebuilts. To be removed.\n#ifdef ENABLE_STRING8_OBSOLETE_METHODS\npublic:\n#else\nprivate:\n#endif\n    inline  const char*         string() const;\n    inline  bool                isEmpty() const;\n};\n\n// String8 can be trivially moved using memcpy() because moving does not\n// require any change to the underlying SharedBuffer contents or reference count.\nANDROID_TRIVIAL_MOVE_TRAIT(String8)\n\nstatic inline std::ostream& operator<<(std::ostream& os, const String8& str) {\n    os << str.c_str();\n    return os;\n}\n\n// ---------------------------------------------------------------------------\n// No user servicable parts below.\n\ninline int compare_type(const String8& lhs, const String8& rhs)\n{\n    return lhs.compare(rhs);\n}\n\ninline int strictly_order_type(const String8& lhs, const String8& rhs)\n{\n    return compare_type(lhs, rhs) < 0;\n}\n\ninline const char* String8::c_str() const\n{\n    return mString;\n}\ninline const char* String8::string() const\n{\n    return mString;\n}\n\ninline size_t String8::size() const\n{\n    return length();\n}\n\ninline bool String8::empty() const\n{\n    return length() == 0;\n}\n\ninline bool String8::isEmpty() const\n{\n    return length() == 0;\n}\n\ninline size_t String8::bytes() const\n{\n    return length();\n}\n\ninline ssize_t String8::find(const String8& other, size_t start) const\n{\n    return find(other.c_str(), start);\n}\n\ninline bool String8::contains(const char* other) const\n{\n    return find(other) >= 0;\n}\n\ninline bool String8::contains(const String8& other) const\n{\n    return contains(other.c_str());\n}\n\ninline bool String8::removeAll(const String8& other)\n{\n    return removeAll(other.c_str());\n}\n\ninline String8& String8::operator=(const String8& other)\n{\n    setTo(other);\n    return *this;\n}\n\ninline String8& String8::operator=(const char* other)\n{\n    setTo(other);\n    return *this;\n}\n\ninline String8& String8::operator+=(const String8& other)\n{\n    append(other);\n    return *this;\n}\n\ninline String8 String8::operator+(const String8& other) const\n{\n    String8 tmp(*this);\n    tmp += other;\n    return tmp;\n}\n\ninline String8& String8::operator+=(const char* other)\n{\n    append(other);\n    return *this;\n}\n\ninline String8 String8::operator+(const char* other) const\n{\n    String8 tmp(*this);\n    tmp += other;\n    return tmp;\n}\n\ninline int String8::compare(const String8& other) const\n{\n    return strcmp(mString, other.mString);\n}\n\ninline bool String8::operator<(const String8& other) const\n{\n    return strcmp(mString, other.mString) < 0;\n}\n\ninline bool String8::operator<=(const String8& other) const\n{\n    return strcmp(mString, other.mString) <= 0;\n}\n\ninline bool String8::operator==(const String8& other) const\n{\n    return strcmp(mString, other.mString) == 0;\n}\n\ninline bool String8::operator!=(const String8& other) const\n{\n    return strcmp(mString, other.mString) != 0;\n}\n\ninline bool String8::operator>=(const String8& other) const\n{\n    return strcmp(mString, other.mString) >= 0;\n}\n\ninline bool String8::operator>(const String8& other) const\n{\n    return strcmp(mString, other.mString) > 0;\n}\n\n#if __cplusplus >= 202002L\ninline std::strong_ordering String8::operator<=>(const String8& other) const {\n    int result = strcmp(mString, other.mString);\n    if (result == 0) {\n        return std::strong_ordering::equal;\n    } else if (result < 0) {\n        return std::strong_ordering::less;\n    } else {\n        return std::strong_ordering::greater;\n    }\n}\n#endif\n\ninline bool String8::operator<(const char* other) const\n{\n    return strcmp(mString, other) < 0;\n}\n\ninline bool String8::operator<=(const char* other) const\n{\n    return strcmp(mString, other) <= 0;\n}\n\ninline bool String8::operator==(const char* other) const\n{\n    return strcmp(mString, other) == 0;\n}\n\ninline bool String8::operator!=(const char* other) const\n{\n    return strcmp(mString, other) != 0;\n}\n\ninline bool String8::operator>=(const char* other) const\n{\n    return strcmp(mString, other) >= 0;\n}\n\ninline bool String8::operator>(const char* other) const\n{\n    return strcmp(mString, other) > 0;\n}\n\n#if __cplusplus >= 202002L\ninline std::strong_ordering String8::operator<=>(const char* other) const {\n    int result = strcmp(mString, other);\n    if (result == 0) {\n        return std::strong_ordering::equal;\n    } else if (result < 0) {\n        return std::strong_ordering::less;\n    } else {\n        return std::strong_ordering::greater;\n    }\n}\n#endif\n\ninline String8::operator const char*() const\n{\n    return mString;\n}\n\ninline String8::String8(std::string_view o) : String8(o.data(), o.length()) { }\n\ninline String8::operator std::string_view() const\n{\n    return {mString, length()};\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_STRING8_H\n"
  },
  {
    "path": "libutils/binder/include/utils/StrongPointer.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_STRONG_POINTER_H\n#define ANDROID_STRONG_POINTER_H\n\n#include <functional>\n#include <type_traits>  // for common_type.\n\n// ---------------------------------------------------------------------------\nnamespace android {\n\ntemplate<typename T> class wp;\n\n// ---------------------------------------------------------------------------\n\ntemplate<typename T>\nclass sp {\npublic:\n    inline constexpr sp() : m_ptr(nullptr) { }\n\n    // The old way of using sp<> was like this. This is bad because it relies\n    // on implicit conversion to sp<>, which we would like to remove (if an\n    // object is being managed some other way, this is double-ownership). We\n    // want to move away from this:\n    //\n    //     sp<Foo> foo = new Foo(...); // DO NOT DO THIS\n    //\n    // Instead, prefer to do this:\n    //\n    //     sp<Foo> foo = sp<Foo>::make(...); // DO THIS\n    //\n    // Sometimes, in order to use this, when a constructor is marked as private,\n    // you may need to add this to your class:\n    //\n    //     friend class sp<Foo>;\n    template <typename... Args>\n    static inline sp<T> make(Args&&... args);\n\n    // if nullptr, returns nullptr\n    //\n    // if a strong pointer is already available, this will retrieve it,\n    // otherwise, this will abort\n    static inline sp<T> fromExisting(T* other);\n\n    // for more information about this macro and correct RefBase usage, see\n    // the comment at the top of utils/RefBase.h\n#if defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)\n    sp(std::nullptr_t) : sp() {}\n#else\n    sp(T* other);  // NOLINT(implicit)\n    template <typename U>\n    sp(U* other);  // NOLINT(implicit)\n    sp& operator=(T* other);\n    template <typename U>\n    sp& operator=(U* other);\n#endif\n\n    sp(const sp<T>& other);\n    sp(sp<T>&& other) noexcept;\n\n    template<typename U> sp(const sp<U>& other);  // NOLINT(implicit)\n    template<typename U> sp(sp<U>&& other);  // NOLINT(implicit)\n\n    // Cast a strong pointer directly from one type to another. Constructors\n    // allow changing types, but only if they are pointer-compatible. This does\n    // a static_cast internally.\n    template <typename U>\n    static inline sp<T> cast(const sp<U>& other);\n\n    ~sp();\n\n    // Assignment\n\n    sp& operator = (const sp<T>& other);\n    sp& operator=(sp<T>&& other) noexcept;\n\n    template<typename U> sp& operator = (const sp<U>& other);\n    template<typename U> sp& operator = (sp<U>&& other);\n\n    //! Special optimization for use by ProcessState (and nobody else).\n    void force_set(T* other);\n\n    // Reset\n\n    void clear();\n\n    // Releases the ownership of the object managed by this instance of sp, if any.\n    // The caller is now responsible for managing it. That is, the caller must ensure\n    // decStrong() is called when the pointer is no longer used.\n    [[nodiscard]] inline T* release() noexcept {\n        auto ret = m_ptr;\n        m_ptr = nullptr;\n        return ret;\n    }\n\n    // Accessors\n\n    inline T&       operator* () const     { return *m_ptr; }\n    inline T*       operator-> () const    { return m_ptr;  }\n    inline T*       get() const            { return m_ptr; }\n    inline explicit operator bool () const { return m_ptr != nullptr; }\n\n    // Punt these to the wp<> implementation.\n    template<typename U>\n    inline bool operator == (const wp<U>& o) const {\n        return o == *this;\n    }\n\n    template<typename U>\n    inline bool operator != (const wp<U>& o) const {\n        return o != *this;\n    }\n\nprivate:\n    template<typename Y> friend class sp;\n    template<typename Y> friend class wp;\n    void set_pointer(T* ptr);\n    T* m_ptr;\n};\n\n#define COMPARE_STRONG(_op_)                                           \\\n    template <typename T, typename U>                                  \\\n    static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \\\n        return t.get() _op_ u.get();                                   \\\n    }                                                                  \\\n    template <typename T, typename U>                                  \\\n    static inline bool operator _op_(const T* t, const sp<U>& u) {     \\\n        return t _op_ u.get();                                         \\\n    }                                                                  \\\n    template <typename T, typename U>                                  \\\n    static inline bool operator _op_(const sp<T>& t, const U* u) {     \\\n        return t.get() _op_ u;                                         \\\n    }                                                                  \\\n    template <typename T>                                              \\\n    static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \\\n        return t.get() _op_ nullptr;                                   \\\n    }                                                                  \\\n    template <typename T>                                              \\\n    static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \\\n        return nullptr _op_ t.get();                                   \\\n    }\n\ntemplate <template <typename C> class comparator, typename T, typename U>\nstatic inline bool _sp_compare_(T* a, U* b) {\n    return comparator<typename std::common_type<T*, U*>::type>()(a, b);\n}\n\n#define COMPARE_STRONG_FUNCTIONAL(_op_, _compare_)                     \\\n    template <typename T, typename U>                                  \\\n    static inline bool operator _op_(const sp<T>& t, const sp<U>& u) { \\\n        return _sp_compare_<_compare_>(t.get(), u.get());              \\\n    }                                                                  \\\n    template <typename T, typename U>                                  \\\n    static inline bool operator _op_(const T* t, const sp<U>& u) {     \\\n        return _sp_compare_<_compare_>(t, u.get());                    \\\n    }                                                                  \\\n    template <typename T, typename U>                                  \\\n    static inline bool operator _op_(const sp<T>& t, const U* u) {     \\\n        return _sp_compare_<_compare_>(t.get(), u);                    \\\n    }                                                                  \\\n    template <typename T>                                              \\\n    static inline bool operator _op_(const sp<T>& t, std::nullptr_t) { \\\n        return _sp_compare_<_compare_>(t.get(), nullptr);              \\\n    }                                                                  \\\n    template <typename T>                                              \\\n    static inline bool operator _op_(std::nullptr_t, const sp<T>& t) { \\\n        return _sp_compare_<_compare_>(nullptr, t.get());              \\\n    }\n\nCOMPARE_STRONG(==)\nCOMPARE_STRONG(!=)\nCOMPARE_STRONG_FUNCTIONAL(>, std::greater)\nCOMPARE_STRONG_FUNCTIONAL(<, std::less)\nCOMPARE_STRONG_FUNCTIONAL(<=, std::less_equal)\nCOMPARE_STRONG_FUNCTIONAL(>=, std::greater_equal)\n\n#undef COMPARE_STRONG\n#undef COMPARE_STRONG_FUNCTIONAL\n\n// For code size reasons, we do not want these inlined or templated.\nvoid sp_report_race();\n\n// ---------------------------------------------------------------------------\n// No user serviceable parts below here.\n\n// TODO: Ideally we should find a way to increment the reference count before running the\n// constructor, so that generating an sp<> to this in the constructor is no longer dangerous.\ntemplate <typename T>\ntemplate <typename... Args>\nsp<T> sp<T>::make(Args&&... args) {\n    T* t = new T(std::forward<Args>(args)...);\n    sp<T> result;\n    result.m_ptr = t;\n    t->incStrong(t);\n    return result;\n}\n\ntemplate <typename T>\nsp<T> sp<T>::fromExisting(T* other) {\n    if (other) {\n        other->incStrongRequireStrong(other);\n        sp<T> result;\n        result.m_ptr = other;\n        return result;\n    }\n    return nullptr;\n}\n\n#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)\ntemplate<typename T>\nsp<T>::sp(T* other)\n        : m_ptr(other) {\n    if (other) {\n        other->incStrong(this);\n    }\n}\n\ntemplate <typename T>\ntemplate <typename U>\nsp<T>::sp(U* other) : m_ptr(other) {\n    if (other) {\n        (static_cast<T*>(other))->incStrong(this);\n    }\n}\n\ntemplate <typename T>\nsp<T>& sp<T>::operator=(T* other) {\n    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));\n    if (other) {\n        other->incStrong(this);\n    }\n    if (oldPtr) oldPtr->decStrong(this);\n    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();\n    m_ptr = other;\n    return *this;\n}\n#endif\n\ntemplate<typename T>\nsp<T>::sp(const sp<T>& other)\n        : m_ptr(other.m_ptr) {\n    if (m_ptr)\n        m_ptr->incStrong(this);\n}\n\ntemplate <typename T>\nsp<T>::sp(sp<T>&& other) noexcept : m_ptr(other.m_ptr) {\n    other.m_ptr = nullptr;\n}\n\ntemplate<typename T> template<typename U>\nsp<T>::sp(const sp<U>& other)\n        : m_ptr(other.m_ptr) {\n    if (m_ptr)\n        m_ptr->incStrong(this);\n}\n\ntemplate<typename T> template<typename U>\nsp<T>::sp(sp<U>&& other)\n        : m_ptr(other.m_ptr) {\n    other.m_ptr = nullptr;\n}\n\ntemplate <typename T>\ntemplate <typename U>\nsp<T> sp<T>::cast(const sp<U>& other) {\n    return sp<T>::fromExisting(static_cast<T*>(other.get()));\n}\n\ntemplate<typename T>\nsp<T>::~sp() {\n    if (m_ptr)\n        m_ptr->decStrong(this);\n}\n\ntemplate<typename T>\nsp<T>& sp<T>::operator =(const sp<T>& other) {\n    // Force m_ptr to be read twice, to heuristically check for data races.\n    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));\n    T* otherPtr(other.m_ptr);\n    if (otherPtr) otherPtr->incStrong(this);\n    if (oldPtr) oldPtr->decStrong(this);\n    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();\n    m_ptr = otherPtr;\n    return *this;\n}\n\ntemplate <typename T>\nsp<T>& sp<T>::operator=(sp<T>&& other) noexcept {\n    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));\n    if (oldPtr) oldPtr->decStrong(this);\n    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();\n    m_ptr = other.m_ptr;\n    other.m_ptr = nullptr;\n    return *this;\n}\n\ntemplate<typename T> template<typename U>\nsp<T>& sp<T>::operator =(const sp<U>& other) {\n    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));\n    T* otherPtr(other.m_ptr);\n    if (otherPtr) otherPtr->incStrong(this);\n    if (oldPtr) oldPtr->decStrong(this);\n    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();\n    m_ptr = otherPtr;\n    return *this;\n}\n\ntemplate<typename T> template<typename U>\nsp<T>& sp<T>::operator =(sp<U>&& other) {\n    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));\n    if (m_ptr) m_ptr->decStrong(this);\n    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();\n    m_ptr = other.m_ptr;\n    other.m_ptr = nullptr;\n    return *this;\n}\n\n#if !defined(ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION)\ntemplate<typename T> template<typename U>\nsp<T>& sp<T>::operator =(U* other) {\n    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));\n    if (other) (static_cast<T*>(other))->incStrong(this);\n    if (oldPtr) oldPtr->decStrong(this);\n    if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();\n    m_ptr = other;\n    return *this;\n}\n#endif\n\ntemplate<typename T>\nvoid sp<T>::force_set(T* other) {\n    other->forceIncStrong(this);\n    m_ptr = other;\n}\n\ntemplate<typename T>\nvoid sp<T>::clear() {\n    T* oldPtr(*const_cast<T* volatile*>(&m_ptr));\n    if (oldPtr) {\n        oldPtr->decStrong(this);\n        if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();\n        m_ptr = nullptr;\n    }\n}\n\ntemplate<typename T>\nvoid sp<T>::set_pointer(T* ptr) {\n    m_ptr = ptr;\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_STRONG_POINTER_H\n"
  },
  {
    "path": "libutils/binder/include/utils/TypeHelpers.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_TYPE_HELPERS_H\n#define ANDROID_TYPE_HELPERS_H\n\n#include <new>\n#include <type_traits>\n\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\n/*\n * Types traits\n */\n\ntemplate <typename T> struct trait_trivial_ctor { enum { value = false }; };\ntemplate <typename T> struct trait_trivial_dtor { enum { value = false }; };\ntemplate <typename T> struct trait_trivial_copy { enum { value = false }; };\ntemplate <typename T> struct trait_trivial_move { enum { value = false }; };\ntemplate <typename T> struct trait_pointer      { enum { value = false }; };\ntemplate <typename T> struct trait_pointer<T*>  { enum { value = true }; };\n\ntemplate <typename TYPE>\nstruct traits {\n    enum {\n        // whether this type is a pointer\n        is_pointer          = trait_pointer<TYPE>::value,\n        // whether this type's constructor is a no-op\n        has_trivial_ctor    = is_pointer || trait_trivial_ctor<TYPE>::value,\n        // whether this type's destructor is a no-op\n        has_trivial_dtor    = is_pointer || trait_trivial_dtor<TYPE>::value,\n        // whether this type type can be copy-constructed with memcpy\n        has_trivial_copy    = is_pointer || trait_trivial_copy<TYPE>::value,\n        // whether this type can be moved with memmove\n        has_trivial_move    = is_pointer || trait_trivial_move<TYPE>::value\n    };\n};\n\ntemplate <typename T, typename U>\nstruct aggregate_traits {\n    enum {\n        is_pointer          = false,\n        has_trivial_ctor    =\n            traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,\n        has_trivial_dtor    =\n            traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,\n        has_trivial_copy    =\n            traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,\n        has_trivial_move    =\n            traits<T>::has_trivial_move && traits<U>::has_trivial_move\n    };\n};\n\n#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \\\n    template<> struct trait_trivial_ctor< T >   { enum { value = true }; };\n\n#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \\\n    template<> struct trait_trivial_dtor< T >   { enum { value = true }; };\n\n#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \\\n    template<> struct trait_trivial_copy< T >   { enum { value = true }; };\n\n#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \\\n    template<> struct trait_trivial_move< T >   { enum { value = true }; };\n\n#define ANDROID_BASIC_TYPES_TRAITS( T ) \\\n    ANDROID_TRIVIAL_CTOR_TRAIT( T ) \\\n    ANDROID_TRIVIAL_DTOR_TRAIT( T ) \\\n    ANDROID_TRIVIAL_COPY_TRAIT( T ) \\\n    ANDROID_TRIVIAL_MOVE_TRAIT( T )\n\n// ---------------------------------------------------------------------------\n\n/*\n * basic types traits\n */\n\nANDROID_BASIC_TYPES_TRAITS( void )\nANDROID_BASIC_TYPES_TRAITS( bool )\nANDROID_BASIC_TYPES_TRAITS( char )\nANDROID_BASIC_TYPES_TRAITS( unsigned char )\nANDROID_BASIC_TYPES_TRAITS( short )\nANDROID_BASIC_TYPES_TRAITS( unsigned short )\nANDROID_BASIC_TYPES_TRAITS( int )\nANDROID_BASIC_TYPES_TRAITS( unsigned int )\nANDROID_BASIC_TYPES_TRAITS( long )\nANDROID_BASIC_TYPES_TRAITS( unsigned long )\nANDROID_BASIC_TYPES_TRAITS( long long )\nANDROID_BASIC_TYPES_TRAITS( unsigned long long )\nANDROID_BASIC_TYPES_TRAITS( float )\nANDROID_BASIC_TYPES_TRAITS( double )\n\ntemplate<typename T> struct trait_trivial_ctor<T*>   { enum { value = true }; };\ntemplate<typename T> struct trait_trivial_dtor<T*>   { enum { value = true }; };\ntemplate<typename T> struct trait_trivial_copy<T*>   { enum { value = true }; };\ntemplate<typename T> struct trait_trivial_move<T*>   { enum { value = true }; };\n\n// ---------------------------------------------------------------------------\n\n\n/*\n * compare and order types\n */\n\ntemplate<typename TYPE> inline\nint strictly_order_type(const TYPE& lhs, const TYPE& rhs) {\n    return (lhs < rhs) ? 1 : 0;\n}\n\ntemplate<typename TYPE> inline\nint compare_type(const TYPE& lhs, const TYPE& rhs) {\n    return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);\n}\n\n/*\n * create, destroy, copy and move types...\n */\n\ntemplate<typename TYPE> inline\nvoid construct_type(TYPE* p, size_t n) {\n    if (!traits<TYPE>::has_trivial_ctor) {\n        while (n > 0) {\n            n--;\n            new(p++) TYPE;\n        }\n    }\n}\n\ntemplate<typename TYPE> inline\nvoid destroy_type(TYPE* p, size_t n) {\n    if (!traits<TYPE>::has_trivial_dtor) {\n        while (n > 0) {\n            n--;\n            p->~TYPE();\n            p++;\n        }\n    }\n}\n\ntemplate<typename TYPE>\ntypename std::enable_if<traits<TYPE>::has_trivial_copy>::type\ninline\ncopy_type(TYPE* d, const TYPE* s, size_t n) {\n    memcpy(d,s,n*sizeof(TYPE));\n}\n\ntemplate<typename TYPE>\ntypename std::enable_if<!traits<TYPE>::has_trivial_copy>::type\ninline\ncopy_type(TYPE* d, const TYPE* s, size_t n) {\n    while (n > 0) {\n        n--;\n        new(d) TYPE(*s);\n        d++, s++;\n    }\n}\n\ntemplate<typename TYPE> inline\nvoid splat_type(TYPE* where, const TYPE* what, size_t n) {\n    if (!traits<TYPE>::has_trivial_copy) {\n        while (n > 0) {\n            n--;\n            new(where) TYPE(*what);\n            where++;\n        }\n    } else {\n        while (n > 0) {\n            n--;\n            *where++ = *what;\n        }\n    }\n}\n\ntemplate<typename TYPE>\nstruct use_trivial_move : public std::integral_constant<bool,\n    (traits<TYPE>::has_trivial_dtor && traits<TYPE>::has_trivial_copy)\n    || traits<TYPE>::has_trivial_move\n> {};\n\ntemplate<typename TYPE>\ntypename std::enable_if<use_trivial_move<TYPE>::value>::type\ninline\nmove_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {\n    memmove(reinterpret_cast<void*>(d), s, n * sizeof(TYPE));\n}\n\ntemplate<typename TYPE>\ntypename std::enable_if<!use_trivial_move<TYPE>::value>::type\ninline\nmove_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {\n    d += n;\n    s += n;\n    while (n > 0) {\n        n--;\n        --d, --s;\n        if (!traits<TYPE>::has_trivial_copy) {\n            new(d) TYPE(*s);\n        } else {\n            *d = *s;\n        }\n        if (!traits<TYPE>::has_trivial_dtor) {\n            s->~TYPE();\n        }\n    }\n}\n\ntemplate<typename TYPE>\ntypename std::enable_if<use_trivial_move<TYPE>::value>::type\ninline\nmove_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {\n    memmove(reinterpret_cast<void*>(d), s, n * sizeof(TYPE));\n}\n\ntemplate<typename TYPE>\ntypename std::enable_if<!use_trivial_move<TYPE>::value>::type\ninline\nmove_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {\n    while (n > 0) {\n        n--;\n        if (!traits<TYPE>::has_trivial_copy) {\n            new(d) TYPE(*s);\n        } else {\n            *d = *s;\n        }\n        if (!traits<TYPE>::has_trivial_dtor) {\n            s->~TYPE();\n        }\n        d++, s++;\n    }\n}\n\n// ---------------------------------------------------------------------------\n\n/*\n * a key/value pair\n */\n\ntemplate <typename KEY, typename VALUE>\nstruct key_value_pair_t {\n    typedef KEY key_t;\n    typedef VALUE value_t;\n\n    KEY     key;\n    VALUE   value;\n    key_value_pair_t() { }\n    key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }\n    key_value_pair_t& operator=(const key_value_pair_t& o) {\n        key = o.key;\n        value = o.value;\n        return *this;\n    }\n    key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v)  { }\n    explicit key_value_pair_t(const KEY& k) : key(k) { }\n    inline bool operator < (const key_value_pair_t& o) const {\n        return strictly_order_type(key, o.key);\n    }\n    inline const KEY& getKey() const {\n        return key;\n    }\n    inline const VALUE& getValue() const {\n        return value;\n    }\n};\n\ntemplate <typename K, typename V>\nstruct trait_trivial_ctor< key_value_pair_t<K, V> >\n{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };\ntemplate <typename K, typename V>\nstruct trait_trivial_dtor< key_value_pair_t<K, V> >\n{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };\ntemplate <typename K, typename V>\nstruct trait_trivial_copy< key_value_pair_t<K, V> >\n{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };\ntemplate <typename K, typename V>\nstruct trait_trivial_move< key_value_pair_t<K, V> >\n{ enum { value = aggregate_traits<K,V>::has_trivial_move }; };\n\n// ---------------------------------------------------------------------------\n\n/*\n * Hash codes.\n */\ntypedef uint32_t hash_t;\n\ntemplate <typename TKey>\nhash_t hash_type(const TKey& key);\n\n/* Built-in hash code specializations */\n#define ANDROID_INT32_HASH(T) \\\n        template <> inline hash_t hash_type(const T& value) { return hash_t(value); }\n#define ANDROID_INT64_HASH(T) \\\n        template <> inline hash_t hash_type(const T& value) { \\\n                return hash_t((value >> 32) ^ value); }\n#define ANDROID_REINTERPRET_HASH(T, R) \\\n        template <> inline hash_t hash_type(const T& value) { \\\n            R newValue; \\\n            static_assert(sizeof(newValue) == sizeof(value), \"size mismatch\"); \\\n            memcpy(&newValue, &value, sizeof(newValue)); \\\n            return hash_type(newValue); \\\n        }\n\nANDROID_INT32_HASH(bool)\nANDROID_INT32_HASH(int8_t)\nANDROID_INT32_HASH(uint8_t)\nANDROID_INT32_HASH(int16_t)\nANDROID_INT32_HASH(uint16_t)\nANDROID_INT32_HASH(int32_t)\nANDROID_INT32_HASH(uint32_t)\nANDROID_INT64_HASH(int64_t)\nANDROID_INT64_HASH(uint64_t)\nANDROID_REINTERPRET_HASH(float, uint32_t)\nANDROID_REINTERPRET_HASH(double, uint64_t)\n\ntemplate <typename T> inline hash_t hash_type(T* const & value) {\n    return hash_type(uintptr_t(value));\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_TYPE_HELPERS_H\n"
  },
  {
    "path": "libutils/binder/include/utils/Unicode.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_UNICODE_H\n#define ANDROID_UNICODE_H\n\n#include <sys/types.h>\n#include <stdint.h>\n\nextern \"C\" {\n\n// Standard string functions on char16_t strings.\nint strcmp16(const char16_t *, const char16_t *);\nint strncmp16(const char16_t *s1, const char16_t *s2, size_t n);\nsize_t strlen16(const char16_t *);\nsize_t strnlen16(const char16_t *, size_t);\nchar16_t *strstr16(const char16_t*, const char16_t*);\n\n// Version of comparison that supports embedded NULs.\n// This is different than strncmp() because we don't stop\n// at a nul character and consider the strings to be different\n// if the lengths are different (thus we need to supply the\n// lengths of both strings).  This can also be used when\n// your string is not nul-terminated as it will have the\n// equivalent result as strcmp16 (unlike strncmp16).\nint strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2);\n\n/**\n * Measure the length of a UTF-32 string in UTF-8. If the string is invalid\n * such as containing a surrogate character, -1 will be returned.\n */\nssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len);\n\n/**\n * Stores a UTF-8 string converted from \"src\" in \"dst\", if \"dst_length\" is not\n * large enough to store the string, the part of the \"src\" string is stored\n * into \"dst\" as much as possible. See the examples for more detail.\n * Returns the size actually used for storing the string.\n * dst\" is not nul-terminated when dst_len is fully used (like strncpy).\n *\n * \\code\n * Example 1\n * \"src\" == \\u3042\\u3044 (\\xE3\\x81\\x82\\xE3\\x81\\x84)\n * \"src_len\" == 2\n * \"dst_len\" >= 7\n * ->\n * Returned value == 6\n * \"dst\" becomes \\xE3\\x81\\x82\\xE3\\x81\\x84\\0\n * (note that \"dst\" is nul-terminated)\n *\n * Example 2\n * \"src\" == \\u3042\\u3044 (\\xE3\\x81\\x82\\xE3\\x81\\x84)\n * \"src_len\" == 2\n * \"dst_len\" == 5\n * ->\n * Returned value == 3\n * \"dst\" becomes \\xE3\\x81\\x82\\0\n * (note that \"dst\" is nul-terminated, but \\u3044 is not stored in \"dst\"\n * since \"dst\" does not have enough size to store the character)\n *\n * Example 3\n * \"src\" == \\u3042\\u3044 (\\xE3\\x81\\x82\\xE3\\x81\\x84)\n * \"src_len\" == 2\n * \"dst_len\" == 6\n * ->\n * Returned value == 6\n * \"dst\" becomes \\xE3\\x81\\x82\\xE3\\x81\\x84\n * (note that \"dst\" is NOT nul-terminated, like strncpy)\n * \\endcode\n */\nvoid utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len);\n\n/**\n * Returns the unicode value at \"index\".\n * Returns -1 when the index is invalid (equals to or more than \"src_len\").\n * If returned value is positive, it is able to be converted to char32_t, which\n * is unsigned. Then, if \"next_index\" is not NULL, the next index to be used is\n * stored in \"next_index\". \"next_index\" can be NULL.\n */\nint32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index);\n\n\n/**\n * Returns the UTF-8 length of UTF-16 string \"src\".\n */\nssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len);\n\n/**\n * Converts a UTF-16 string to UTF-8. The destination buffer must be large\n * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added\n * NUL terminator.\n */\nvoid utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len);\n\n/**\n * Returns the UTF-16 length of UTF-8 string \"src\". Returns -1 in case\n * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you\n * can ask to log a message and fail in case the invalid utf8 could have caused an override if no\n * bound checks were used (otherwise -1 is returned).\n */\nssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen, bool overreadIsFatal = false);\n\n/**\n * Convert UTF-8 to UTF-16 including surrogate pairs.\n * Returns a pointer to the end of the string (where a NUL terminator might go\n * if you wanted to add one). At most dstLen characters are written; it won't emit half a surrogate\n * pair. If dstLen == 0 nothing is written and dst is returned. If dstLen > SSIZE_MAX it aborts\n * (this being probably a negative number returned as an error and casted to unsigned).\n */\nchar16_t* utf8_to_utf16_no_null_terminator(\n        const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);\n\n/**\n * Convert UTF-8 to UTF-16 including surrogate pairs. At most dstLen - 1\n * characters are written; it won't emit half a surrogate pair; and a NUL terminator is appended\n * after. dstLen - 1 can be measured beforehand using utf8_to_utf16_length. Aborts if dstLen == 0\n * (at least one character is needed for the NUL terminator) or dstLen > SSIZE_MAX (the latter\n * case being likely a negative number returned as an error and casted to unsigned) . Returns a\n * pointer to the NUL terminator.\n */\nchar16_t *utf8_to_utf16(\n        const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen);\n\n}\n\n#endif\n"
  },
  {
    "path": "libutils/binder/include/utils/Vector.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_VECTOR_H\n#define ANDROID_VECTOR_H\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <log/log.h>\n#include <utils/TypeHelpers.h>\n#include <utils/VectorImpl.h>\n#ifndef __has_attribute\n#define __has_attribute(x) 0\n#endif\n\n/*\n * Used to exclude some functions from CFI.\n */\n#if __has_attribute(no_sanitize)\n#define UTILS_VECTOR_NO_CFI __attribute__((no_sanitize(\"cfi\")))\n#else\n#define UTILS_VECTOR_NO_CFI\n#endif\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\ntemplate <typename TYPE>\nclass SortedVector;\n\n/*!\n * The main templated vector class ensuring type safety\n * while making use of VectorImpl.\n * This is the class users want to use.\n *\n * DO NOT USE: please use std::vector\n */\n\ntemplate <class TYPE>\nclass Vector : private VectorImpl\n{\npublic:\n            typedef TYPE    value_type;\n\n    /*!\n     * Constructors and destructors\n     */\n\n                            Vector();\n                            Vector(const Vector<TYPE>& rhs);\n    explicit                Vector(const SortedVector<TYPE>& rhs);\n    virtual                 ~Vector();\n\n    /*! copy operator */\n    Vector<TYPE>& operator=(const Vector<TYPE>& rhs);        // NOLINT(cert-oop54-cpp)\n    Vector<TYPE>& operator=(const SortedVector<TYPE>& rhs);  // NOLINT(cert-oop54-cpp)\n\n    /*\n     * empty the vector\n     */\n\n    inline  void            clear()             { VectorImpl::clear(); }\n\n    /*!\n     * vector stats\n     */\n\n    //! returns number of items in the vector\n    inline  size_t          size() const                { return VectorImpl::size(); }\n    //! returns whether or not the vector is empty\n    inline  bool            isEmpty() const             { return VectorImpl::isEmpty(); }\n    //! returns how many items can be stored without reallocating the backing store\n    inline  size_t          capacity() const            { return VectorImpl::capacity(); }\n    //! sets the capacity. capacity can never be reduced less than size()\n    inline  ssize_t         setCapacity(size_t size)    { return VectorImpl::setCapacity(size); }\n\n    /*!\n     * set the size of the vector. items are appended with the default\n     * constructor, or removed from the end as needed.\n     */\n    inline  ssize_t         resize(size_t size)         { return VectorImpl::resize(size); }\n\n    /*!\n     * C-style array access\n     */\n\n    //! read-only C-style access\n    inline  const TYPE*     array() const;\n    //! read-write C-style access\n            TYPE*           editArray();\n\n    /*!\n     * accessors\n     */\n\n    //! read-only access to an item at a given index\n    inline  const TYPE&     operator [] (size_t index) const;\n    //! alternate name for operator []\n    inline  const TYPE&     itemAt(size_t index) const;\n    //! stack-usage of the vector. returns the top of the stack (last element)\n            const TYPE&     top() const;\n\n    /*!\n     * modifying the array\n     */\n\n    //! copy-on write support, grants write access to an item\n            TYPE&           editItemAt(size_t index);\n    //! grants right access to the top of the stack (last element)\n            TYPE&           editTop();\n\n            /*!\n             * append/insert another vector\n             */\n\n    //! insert another vector at a given index\n            ssize_t         insertVectorAt(const Vector<TYPE>& vector, size_t index);\n\n    //! append another vector at the end of this one\n            ssize_t         appendVector(const Vector<TYPE>& vector);\n\n\n    //! insert an array at a given index\n            ssize_t         insertArrayAt(const TYPE* array, size_t index, size_t length);\n\n    //! append an array at the end of this vector\n            ssize_t         appendArray(const TYPE* array, size_t length);\n\n            /*!\n             * add/insert/replace items\n             */\n\n    //! insert one or several items initialized with their default constructor\n    inline  ssize_t         insertAt(size_t index, size_t numItems = 1);\n    //! insert one or several items initialized from a prototype item\n            ssize_t         insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);\n    //! pop the top of the stack (removes the last element). No-op if the stack's empty\n    inline  void            pop();\n    //! pushes an item initialized with its default constructor\n    inline  void            push();\n    //! pushes an item on the top of the stack\n            void            push(const TYPE& item);\n    //! same as push() but returns the index the item was added at (or an error)\n    inline  ssize_t         add();\n    //! same as push() but returns the index the item was added at (or an error)\n            ssize_t         add(const TYPE& item);\n    //! replace an item with a new one initialized with its default constructor\n    inline  ssize_t         replaceAt(size_t index);\n    //! replace an item with a new one\n            ssize_t         replaceAt(const TYPE& item, size_t index);\n\n    /*!\n     * remove items\n     */\n\n    //! remove several items\n    inline  ssize_t         removeItemsAt(size_t index, size_t count = 1);\n    //! remove one item\n    inline  ssize_t         removeAt(size_t index)  { return removeItemsAt(index); }\n\n    /*!\n     * sort (stable) the array\n     */\n\n     typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);\n     typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);\n\n     inline status_t        sort(compar_t cmp);\n     inline status_t        sort(compar_r_t cmp, void* state);\n\n     // for debugging only\n     inline size_t getItemSize() const { return itemSize(); }\n\n\n     /*\n      * these inlines add some level of compatibility with STL. eventually\n      * we should probably turn things around.\n      */\n     typedef TYPE* iterator;\n     typedef TYPE const* const_iterator;\n\n     inline iterator begin() { return editArray(); }\n     inline iterator end()   { return editArray() + size(); }\n     inline const_iterator begin() const { return array(); }\n     inline const_iterator end() const   { return array() + size(); }\n     inline void reserve(size_t n) { setCapacity(n); }\n     inline bool empty() const{ return isEmpty(); }\n     inline void push_back(const TYPE& item)  { insertAt(item, size(), 1); }\n     inline void push_front(const TYPE& item) { insertAt(item, 0, 1); }\n     inline iterator erase(iterator pos) {\n         ssize_t index = removeItemsAt(static_cast<size_t>(pos-array()));\n         return begin() + index;\n     }\n\nprotected:\n    virtual void    do_construct(void* storage, size_t num) const;\n    virtual void    do_destroy(void* storage, size_t num) const;\n    virtual void    do_copy(void* dest, const void* from, size_t num) const;\n    virtual void    do_splat(void* dest, const void* item, size_t num) const;\n    virtual void    do_move_forward(void* dest, const void* from, size_t num) const;\n    virtual void    do_move_backward(void* dest, const void* from, size_t num) const;\n};\n\n// ---------------------------------------------------------------------------\n// No user serviceable parts from here...\n// ---------------------------------------------------------------------------\n\ntemplate<class TYPE> inline\nVector<TYPE>::Vector()\n    : VectorImpl(sizeof(TYPE),\n                ((traits<TYPE>::has_trivial_ctor   ? HAS_TRIVIAL_CTOR   : 0)\n                |(traits<TYPE>::has_trivial_dtor   ? HAS_TRIVIAL_DTOR   : 0)\n                |(traits<TYPE>::has_trivial_copy   ? HAS_TRIVIAL_COPY   : 0))\n                )\n{\n}\n\ntemplate<class TYPE> inline\nVector<TYPE>::Vector(const Vector<TYPE>& rhs)\n    : VectorImpl(rhs) {\n}\n\ntemplate<class TYPE> inline\nVector<TYPE>::Vector(const SortedVector<TYPE>& rhs)\n    : VectorImpl(static_cast<const VectorImpl&>(rhs)) {\n}\n\ntemplate<class TYPE> inline\nVector<TYPE>::~Vector() {\n    finish_vector();\n}\n\ntemplate <class TYPE>\ninline Vector<TYPE>& Vector<TYPE>::operator=(const Vector<TYPE>& rhs)  // NOLINT(cert-oop54-cpp)\n{\n    VectorImpl::operator=(rhs);\n    return *this;\n}\n\ntemplate <class TYPE>\ninline Vector<TYPE>& Vector<TYPE>::operator=(\n        const SortedVector<TYPE>& rhs)  // NOLINT(cert-oop54-cpp)\n{\n    VectorImpl::operator=(static_cast<const VectorImpl&>(rhs));\n    return *this;\n}\n\ntemplate<class TYPE> inline\nconst TYPE* Vector<TYPE>::array() const {\n    return static_cast<const TYPE *>(arrayImpl());\n}\n\ntemplate<class TYPE> inline\nTYPE* Vector<TYPE>::editArray() {\n    return static_cast<TYPE *>(editArrayImpl());\n}\n\n\ntemplate<class TYPE> inline\nconst TYPE& Vector<TYPE>::operator[](size_t index) const {\n    LOG_FATAL_IF(index>=size(),\n            \"%s: index=%u out of range (%u)\", __PRETTY_FUNCTION__,\n            int(index), int(size()));\n    return *(array() + index);\n}\n\ntemplate<class TYPE> inline\nconst TYPE& Vector<TYPE>::itemAt(size_t index) const {\n    return operator[](index);\n}\n\ntemplate<class TYPE> inline\nconst TYPE& Vector<TYPE>::top() const {\n    return *(array() + size() - 1);\n}\n\ntemplate<class TYPE> inline\nTYPE& Vector<TYPE>::editItemAt(size_t index) {\n    return *( static_cast<TYPE *>(editItemLocation(index)) );\n}\n\ntemplate<class TYPE> inline\nTYPE& Vector<TYPE>::editTop() {\n    return *( static_cast<TYPE *>(editItemLocation(size()-1)) );\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {\n    return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {\n    return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::insertArrayAt(const TYPE* array, size_t index, size_t length) {\n    return VectorImpl::insertArrayAt(array, index, length);\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::appendArray(const TYPE* array, size_t length) {\n    return VectorImpl::appendArray(array, length);\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {\n    return VectorImpl::insertAt(&item, index, numItems);\n}\n\ntemplate<class TYPE> inline\nvoid Vector<TYPE>::push(const TYPE& item) {\n    return VectorImpl::push(&item);\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::add(const TYPE& item) {\n    return VectorImpl::add(&item);\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {\n    return VectorImpl::replaceAt(&item, index);\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {\n    return VectorImpl::insertAt(index, numItems);\n}\n\ntemplate<class TYPE> inline\nvoid Vector<TYPE>::pop() {\n    VectorImpl::pop();\n}\n\ntemplate<class TYPE> inline\nvoid Vector<TYPE>::push() {\n    VectorImpl::push();\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::add() {\n    return VectorImpl::add();\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::replaceAt(size_t index) {\n    return VectorImpl::replaceAt(index);\n}\n\ntemplate<class TYPE> inline\nssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {\n    return VectorImpl::removeItemsAt(index, count);\n}\n\ntemplate<class TYPE> inline\nstatus_t Vector<TYPE>::sort(Vector<TYPE>::compar_t cmp) {\n    return VectorImpl::sort(reinterpret_cast<VectorImpl::compar_t>(cmp));\n}\n\ntemplate<class TYPE> inline\nstatus_t Vector<TYPE>::sort(Vector<TYPE>::compar_r_t cmp, void* state) {\n    return VectorImpl::sort(reinterpret_cast<VectorImpl::compar_r_t>(cmp), state);\n}\n\n// ---------------------------------------------------------------------------\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void Vector<TYPE>::do_construct(void* storage, size_t num) const {\n    construct_type( reinterpret_cast<TYPE*>(storage), num );\n}\n\ntemplate<class TYPE>\nvoid Vector<TYPE>::do_destroy(void* storage, size_t num) const {\n    destroy_type( reinterpret_cast<TYPE*>(storage), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {\n    copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {\n    splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {\n    move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {\n    move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_VECTOR_H\n"
  },
  {
    "path": "libutils/binder/include/utils/VectorImpl.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_VECTOR_IMPL_H\n#define ANDROID_VECTOR_IMPL_H\n\n#include <assert.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <utils/Errors.h>\n\n// ---------------------------------------------------------------------------\n// No user serviceable parts in here...\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\n/*!\n * Implementation of the guts of the vector<> class\n * this ensures backward binary compatibility and\n * reduces code size.\n * For performance reasons, we expose mStorage and mCount\n * so these fields are set in stone.\n *\n */\n\nclass VectorImpl\n{\npublic:\n    enum { // flags passed to the ctor\n        HAS_TRIVIAL_CTOR    = 0x00000001,\n        HAS_TRIVIAL_DTOR    = 0x00000002,\n        HAS_TRIVIAL_COPY    = 0x00000004,\n    };\n\n                            VectorImpl(size_t itemSize, uint32_t flags);\n                            VectorImpl(const VectorImpl& rhs);\n    virtual                 ~VectorImpl();\n\n    /*! must be called from subclasses destructor */\n            void            finish_vector();\n\n            VectorImpl&     operator = (const VectorImpl& rhs);    \n            \n    /*! C-style array access */\n    inline  const void*     arrayImpl() const       { return mStorage; }\n            void*           editArrayImpl();\n            \n    /*! vector stats */\n    inline  size_t          size() const        { return mCount; }\n    inline  bool            isEmpty() const     { return mCount == 0; }\n            size_t          capacity() const;\n            ssize_t         setCapacity(size_t size);\n            ssize_t         resize(size_t size);\n\n            /*! append/insert another vector or array */\n            ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);\n            ssize_t         appendVector(const VectorImpl& vector);\n            ssize_t         insertArrayAt(const void* array, size_t index, size_t length);\n            ssize_t         appendArray(const void* array, size_t length);\n            \n            /*! add/insert/replace items */\n            ssize_t         insertAt(size_t where, size_t numItems = 1);\n            ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);\n            void            pop();\n            void            push();\n            void            push(const void* item);\n            ssize_t         add();\n            ssize_t         add(const void* item);\n            ssize_t         replaceAt(size_t index);\n            ssize_t         replaceAt(const void* item, size_t index);\n\n            /*! remove items */\n            ssize_t         removeItemsAt(size_t index, size_t count = 1);\n            void            clear();\n\n            const void*     itemLocation(size_t index) const;\n            void*           editItemLocation(size_t index);\n\n            typedef int (*compar_t)(const void* lhs, const void* rhs);\n            typedef int (*compar_r_t)(const void* lhs, const void* rhs, void* state);\n            status_t        sort(compar_t cmp);\n            status_t        sort(compar_r_t cmp, void* state);\n\nprotected:\n            size_t          itemSize() const;\n            void            release_storage();\n\n    virtual void            do_construct(void* storage, size_t num) const = 0;\n    virtual void            do_destroy(void* storage, size_t num) const = 0;\n    virtual void            do_copy(void* dest, const void* from, size_t num) const = 0;\n    virtual void            do_splat(void* dest, const void* item, size_t num) const = 0;\n    virtual void            do_move_forward(void* dest, const void* from, size_t num) const = 0;\n    virtual void            do_move_backward(void* dest, const void* from, size_t num) const = 0;\n    \nprivate:\n        void* _grow(size_t where, size_t amount);\n        void  _shrink(size_t where, size_t amount);\n\n        inline void _do_construct(void* storage, size_t num) const;\n        inline void _do_destroy(void* storage, size_t num) const;\n        inline void _do_copy(void* dest, const void* from, size_t num) const;\n        inline void _do_splat(void* dest, const void* item, size_t num) const;\n        inline void _do_move_forward(void* dest, const void* from, size_t num) const;\n        inline void _do_move_backward(void* dest, const void* from, size_t num) const;\n\n            // These 2 fields are exposed in the inlines below,\n            // so they're set in stone.\n            void *      mStorage;   // base address of the vector\n            size_t      mCount;     // number of items\n\n    const   uint32_t    mFlags;\n    const   size_t      mItemSize;\n};\n\n\n\nclass SortedVectorImpl : public VectorImpl\n{\npublic:\n                            SortedVectorImpl(size_t itemSize, uint32_t flags);\n    explicit                SortedVectorImpl(const VectorImpl& rhs);\n    virtual                 ~SortedVectorImpl();\n    \n    SortedVectorImpl&     operator = (const SortedVectorImpl& rhs);    \n\n    //! finds the index of an item\n            ssize_t         indexOf(const void* item) const;\n\n    //! finds where this item should be inserted\n            size_t          orderOf(const void* item) const;\n\n    //! add an item in the right place (or replaces it if there is one)\n            ssize_t         add(const void* item);\n\n    //! merges a vector into this one\n            ssize_t         merge(const VectorImpl& vector);\n            ssize_t         merge(const SortedVectorImpl& vector);\n             \n    //! removes an item\n            ssize_t         remove(const void* item);\n        \nprotected:\n    virtual int             do_compare(const void* lhs, const void* rhs) const = 0;\n\nprivate:\n            ssize_t         _indexOrderOf(const void* item, size_t* order = nullptr) const;\n\n            // these are made private, because they can't be used on a SortedVector\n            // (they don't have an implementation either)\n            ssize_t         add();\n            void            pop();\n            void            push();\n            void            push(const void* item);\n            ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);\n            ssize_t         appendVector(const VectorImpl& vector);\n            ssize_t         insertArrayAt(const void* array, size_t index, size_t length);\n            ssize_t         appendArray(const void* array, size_t length);\n            ssize_t         insertAt(size_t where, size_t numItems = 1);\n            ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);\n            ssize_t         replaceAt(size_t index);\n            ssize_t         replaceAt(const void* item, size_t index);\n};\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_VECTOR_IMPL_H\n"
  },
  {
    "path": "libutils/include/utils/AndroidThreads.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_UTILS_ANDROID_THREADS_H\n#define _LIBS_UTILS_ANDROID_THREADS_H\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#if !defined(_WIN32)\n# include <pthread.h>\n#endif\n\n#include <utils/ThreadDefs.h>\n\n// ---------------------------------------------------------------------------\n// C API\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n// Create and run a new thread.\nextern int androidCreateThread(android_thread_func_t, void *);\n\n// Create thread with lots of parameters\nextern int androidCreateThreadEtc(android_thread_func_t entryFunction,\n                                  void *userData,\n                                  const char* threadName,\n                                  int32_t threadPriority,\n                                  size_t threadStackSize,\n                                  android_thread_id_t *threadId);\n\n// Get some sort of unique identifier for the current thread.\nextern android_thread_id_t androidGetThreadId();\n\n// Low-level thread creation -- never creates threads that can\n// interact with the Java VM.\nextern int androidCreateRawThreadEtc(android_thread_func_t entryFunction,\n                                     void *userData,\n                                     const char* threadName,\n                                     int32_t threadPriority,\n                                     size_t threadStackSize,\n                                     android_thread_id_t *threadId);\n\n// set the same of the running thread\nextern void androidSetThreadName(const char* name);\n\n// Used by the Java Runtime to control how threads are created, so that\n// they can be proper and lovely Java threads.\ntypedef int (*android_create_thread_fn)(android_thread_func_t entryFunction,\n                                        void *userData,\n                                        const char* threadName,\n                                        int32_t threadPriority,\n                                        size_t threadStackSize,\n                                        android_thread_id_t *threadId);\n\nextern void androidSetCreateThreadFunc(android_create_thread_fn func);\n\n// ------------------------------------------------------------------\n// Extra functions working with raw pids.\n\n#if defined(__ANDROID__)\n// Change the priority AND scheduling group of a particular thread.  The priority\n// should be one of the ANDROID_PRIORITY constants.  Returns INVALID_OPERATION\n// if the priority set failed, else another value if just the group set failed;\n// in either case errno is set.  Thread ID zero means current thread.\nextern int androidSetThreadPriority(pid_t tid, int prio);\n\n// Get the current priority of a particular thread. Returns one of the\n// ANDROID_PRIORITY constants or a negative result in case of error.\nextern int androidGetThreadPriority(pid_t tid);\n#endif\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n// ----------------------------------------------------------------------------\n// C++ API\n#ifdef __cplusplus\nnamespace android {\n// ----------------------------------------------------------------------------\n\n// Create and run a new thread.\ninline bool createThread(thread_func_t f, void *a) {\n    return androidCreateThread(f, a) ? true : false;\n}\n\n// Create thread with lots of parameters\ninline bool createThreadEtc(thread_func_t entryFunction,\n                            void *userData,\n                            const char* threadName = \"android:unnamed_thread\",\n                            int32_t threadPriority = PRIORITY_DEFAULT,\n                            size_t threadStackSize = 0,\n                            thread_id_t *threadId = nullptr)\n{\n    return androidCreateThreadEtc(entryFunction, userData, threadName,\n        threadPriority, threadStackSize, threadId) ? true : false;\n}\n\n// Get some sort of unique identifier for the current thread.\ninline thread_id_t getThreadId() {\n    return androidGetThreadId();\n}\n\n// ----------------------------------------------------------------------------\n}  // namespace android\n#endif  // __cplusplus\n// ----------------------------------------------------------------------------\n\n#endif // _LIBS_UTILS_ANDROID_THREADS_H\n"
  },
  {
    "path": "libutils/include/utils/Atomic.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_UTILS_ATOMIC_H\n#define ANDROID_UTILS_ATOMIC_H\n\n// DO NOT USE: Please instead use std::atomic\n\n#include <cutils/atomic.h>\n\n#endif // ANDROID_UTILS_ATOMIC_H\n"
  },
  {
    "path": "libutils/include/utils/BitSet.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef UTILS_BITSET_H\n#define UTILS_BITSET_H\n\n#include <stdint.h>\n#include <utils/TypeHelpers.h>\n\n/*\n * A class to provide efficient manipulation of bitsets.\n *\n * Consider using std::bitset<32> or std::bitset<64> if all you want is a class to do basic bit\n * manipulation (i.e. AND / OR / XOR / flip / etc). These classes are only needed if you want to\n * efficiently perform operations like finding the first set bit in a bitset and you want to\n * avoid using the built-in functions (e.g. __builtin_clz) on std::bitset::to_ulong.\n */\n\nnamespace android {\n\n// A simple set of 32 bits that can be individually marked or cleared.\nstruct BitSet32 {\n    uint32_t value;\n\n    inline BitSet32() : value(0UL) { }\n    explicit inline BitSet32(uint32_t value) : value(value) { }\n\n    // Gets the value associated with a particular bit index.\n    static inline uint32_t valueForBit(uint32_t n) { return 0x80000000UL >> n; }\n\n    // Clears the bit set.\n    inline void clear() { clear(value); }\n\n    static inline void clear(uint32_t& value) { value = 0UL; }\n\n    // Returns the number of marked bits in the set.\n    inline uint32_t count() const { return count(value); }\n\n    static inline uint32_t count(uint32_t value) {\n        return static_cast<uint32_t>(__builtin_popcountl(value));\n    }\n\n    // Returns true if the bit set does not contain any marked bits.\n    inline bool isEmpty() const { return isEmpty(value); }\n\n    static inline bool isEmpty(uint32_t value) { return ! value; }\n\n    // Returns true if the bit set does not contain any unmarked bits.\n    inline bool isFull() const { return isFull(value); }\n\n    static inline bool isFull(uint32_t value) { return value == 0xffffffffUL; }\n\n    // Returns true if the specified bit is marked.\n    inline bool hasBit(uint32_t n) const { return hasBit(value, n); }\n\n    static inline bool hasBit(uint32_t value, uint32_t n) { return value & valueForBit(n); }\n\n    // Marks the specified bit.\n    inline void markBit(uint32_t n) { markBit(value, n); }\n\n    static inline void markBit (uint32_t& value, uint32_t n) { value |= valueForBit(n); }\n\n    // Clears the specified bit.\n    inline void clearBit(uint32_t n) { clearBit(value, n); }\n\n    static inline void clearBit(uint32_t& value, uint32_t n) { value &= ~ valueForBit(n); }\n\n    // Finds the first marked bit in the set.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }\n\n    static uint32_t firstMarkedBit(uint32_t value) { return clz_checked(value); }\n\n    // Finds the first unmarked bit in the set.\n    // Result is undefined if all bits are marked.\n    inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }\n\n    static inline uint32_t firstUnmarkedBit(uint32_t value) { return clz_checked(~ value); }\n\n    // Finds the last marked bit in the set.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }\n\n    static inline uint32_t lastMarkedBit(uint32_t value) { return 31 - ctz_checked(value); }\n\n    // Finds the first marked bit in the set and clears it.  Returns the bit index.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }\n\n    static inline uint32_t clearFirstMarkedBit(uint32_t& value) {\n        uint32_t n = firstMarkedBit(value);\n        clearBit(value, n);\n        return n;\n    }\n\n    // Finds the first unmarked bit in the set and marks it.  Returns the bit index.\n    // Result is undefined if all bits are marked.\n    inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }\n\n    static inline uint32_t markFirstUnmarkedBit(uint32_t& value) {\n        uint32_t n = firstUnmarkedBit(value);\n        markBit(value, n);\n        return n;\n    }\n\n    // Finds the last marked bit in the set and clears it.  Returns the bit index.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }\n\n    static inline uint32_t clearLastMarkedBit(uint32_t& value) {\n        uint32_t n = lastMarkedBit(value);\n        clearBit(value, n);\n        return n;\n    }\n\n    // Gets the index of the specified bit in the set, which is the number of\n    // marked bits that appear before the specified bit.\n    inline uint32_t getIndexOfBit(uint32_t n) const {\n        return getIndexOfBit(value, n);\n    }\n\n    static inline uint32_t getIndexOfBit(uint32_t value, uint32_t n) {\n        return static_cast<uint32_t>(__builtin_popcountl(value & ~(0xffffffffUL >> n)));\n    }\n\n    inline bool operator== (const BitSet32& other) const { return value == other.value; }\n    inline bool operator!= (const BitSet32& other) const { return value != other.value; }\n    inline BitSet32 operator& (const BitSet32& other) const {\n        return BitSet32(value & other.value);\n    }\n    inline BitSet32& operator&= (const BitSet32& other) {\n        value &= other.value;\n        return *this;\n    }\n    inline BitSet32 operator| (const BitSet32& other) const {\n        return BitSet32(value | other.value);\n    }\n    inline BitSet32& operator|= (const BitSet32& other) {\n        value |= other.value;\n        return *this;\n    }\n\nprivate:\n    // We use these helpers as the signature of __builtin_c{l,t}z has \"unsigned int\" for the\n    // input, which is only guaranteed to be 16b, not 32. The compiler should optimize this away.\n    static inline uint32_t clz_checked(uint32_t value) {\n        if (sizeof(unsigned int) == sizeof(uint32_t)) {\n            return static_cast<uint32_t>(__builtin_clz(value));\n        } else {\n            return static_cast<uint32_t>(__builtin_clzl(value));\n        }\n    }\n\n    static inline uint32_t ctz_checked(uint32_t value) {\n        if (sizeof(unsigned int) == sizeof(uint32_t)) {\n            return static_cast<uint32_t>(__builtin_ctz(value));\n        } else {\n            return static_cast<uint32_t>(__builtin_ctzl(value));\n        }\n    }\n};\n\nANDROID_BASIC_TYPES_TRAITS(BitSet32)\n\n// A simple set of 64 bits that can be individually marked or cleared.\nstruct BitSet64 {\n    uint64_t value;\n\n    inline BitSet64() : value(0ULL) { }\n    explicit inline BitSet64(uint64_t value) : value(value) { }\n\n    // Gets the value associated with a particular bit index.\n    static inline uint64_t valueForBit(uint32_t n) { return 0x8000000000000000ULL >> n; }\n\n    // Clears the bit set.\n    inline void clear() { clear(value); }\n\n    static inline void clear(uint64_t& value) { value = 0ULL; }\n\n    // Returns the number of marked bits in the set.\n    inline uint32_t count() const { return count(value); }\n\n    static inline uint32_t count(uint64_t value) {\n        return static_cast<uint32_t>(__builtin_popcountll(value));\n    }\n\n    // Returns true if the bit set does not contain any marked bits.\n    inline bool isEmpty() const { return isEmpty(value); }\n\n    static inline bool isEmpty(uint64_t value) { return ! value; }\n\n    // Returns true if the bit set does not contain any unmarked bits.\n    inline bool isFull() const { return isFull(value); }\n\n    static inline bool isFull(uint64_t value) { return value == 0xffffffffffffffffULL; }\n\n    // Returns true if the specified bit is marked.\n    inline bool hasBit(uint32_t n) const { return hasBit(value, n); }\n\n    static inline bool hasBit(uint64_t value, uint32_t n) { return value & valueForBit(n); }\n\n    // Marks the specified bit.\n    inline void markBit(uint32_t n) { markBit(value, n); }\n\n    static inline void markBit(uint64_t& value, uint32_t n) { value |= valueForBit(n); }\n\n    // Clears the specified bit.\n    inline void clearBit(uint32_t n) { clearBit(value, n); }\n\n    static inline void clearBit(uint64_t& value, uint32_t n) { value &= ~ valueForBit(n); }\n\n    // Finds the first marked bit in the set.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t firstMarkedBit() const { return firstMarkedBit(value); }\n\n    static inline uint32_t firstMarkedBit(uint64_t value) {\n        return static_cast<uint32_t>(__builtin_clzll(value));\n    }\n\n    // Finds the first unmarked bit in the set.\n    // Result is undefined if all bits are marked.\n    inline uint32_t firstUnmarkedBit() const { return firstUnmarkedBit(value); }\n\n    static inline uint32_t firstUnmarkedBit(uint64_t value) {\n        return static_cast<uint32_t>(__builtin_clzll(~value));\n    }\n\n    // Finds the last marked bit in the set.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t lastMarkedBit() const { return lastMarkedBit(value); }\n\n    static inline uint32_t lastMarkedBit(uint64_t value) {\n        return static_cast<uint32_t>(63 - __builtin_ctzll(value));\n    }\n\n    // Finds the first marked bit in the set and clears it.  Returns the bit index.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t clearFirstMarkedBit() { return clearFirstMarkedBit(value); }\n\n    static inline uint32_t clearFirstMarkedBit(uint64_t& value) {\n        uint32_t n = firstMarkedBit(value);\n        clearBit(value, n);\n        return n;\n    }\n\n    // Finds the first unmarked bit in the set and marks it.  Returns the bit index.\n    // Result is undefined if all bits are marked.\n    inline uint32_t markFirstUnmarkedBit() { return markFirstUnmarkedBit(value); }\n\n    static inline uint32_t markFirstUnmarkedBit(uint64_t& value) {\n        uint32_t n = firstUnmarkedBit(value);\n        markBit(value, n);\n        return n;\n    }\n\n    // Finds the last marked bit in the set and clears it.  Returns the bit index.\n    // Result is undefined if all bits are unmarked.\n    inline uint32_t clearLastMarkedBit() { return clearLastMarkedBit(value); }\n\n    static inline uint32_t clearLastMarkedBit(uint64_t& value) {\n        uint32_t n = lastMarkedBit(value);\n        clearBit(value, n);\n        return n;\n    }\n\n    // Gets the index of the specified bit in the set, which is the number of\n    // marked bits that appear before the specified bit.\n    inline uint32_t getIndexOfBit(uint32_t n) const { return getIndexOfBit(value, n); }\n\n    static inline uint32_t getIndexOfBit(uint64_t value, uint32_t n) {\n        return static_cast<uint32_t>(__builtin_popcountll(value & ~(0xffffffffffffffffULL >> n)));\n    }\n\n    inline bool operator== (const BitSet64& other) const { return value == other.value; }\n    inline bool operator!= (const BitSet64& other) const { return value != other.value; }\n    inline BitSet64 operator& (const BitSet64& other) const {\n        return BitSet64(value & other.value);\n    }\n    inline BitSet64& operator&= (const BitSet64& other) {\n        value &= other.value;\n        return *this;\n    }\n    inline BitSet64 operator| (const BitSet64& other) const {\n        return BitSet64(value | other.value);\n    }\n    inline BitSet64& operator|= (const BitSet64& other) {\n        value |= other.value;\n        return *this;\n    }\n};\n\nANDROID_BASIC_TYPES_TRAITS(BitSet64)\n\n} // namespace android\n\n#endif // UTILS_BITSET_H\n"
  },
  {
    "path": "libutils/include/utils/ByteOrder.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n/*\n * If you're looking for a portable <endian.h> that's available on Android,\n * Linux, macOS, and Windows, see <android-base/endian.h> instead.\n *\n * Nothing in this file is useful because all supported Android ABIs are\n * little-endian and all our code that runs on the host assumes that the host is\n * also little-endian. What pretense at big-endian support exists is completely\n * untested and unlikely to actually work.\n */\n\n#include <stdint.h>\n#include <sys/types.h>\n#if defined(_WIN32)\n#include <winsock2.h>\n#else\n#include <netinet/in.h>\n#endif\n\n/* TODO: move this cruft to frameworks/. */\n\n#define dtohl(x) (x)\n#define dtohs(x) (x)\n#define htodl(x) (x)\n#define htods(x) (x)\n\n#define fromlel(x) (x)\n#define tolel(x) (x)\n"
  },
  {
    "path": "libutils/include/utils/CallStack.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <memory>\n\n#include <android/log.h>\n#include <utils/String8.h>\n#include <utils/Vector.h>\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#if !defined(__APPLE__) && !defined(_WIN32)\n# define CALLSTACK_WEAKS_AVAILABLE 1\n#endif\n#ifndef CALLSTACK_WEAK\n# ifdef CALLSTACK_WEAKS_AVAILABLE\n#   define CALLSTACK_WEAK __attribute__((weak))\n# else // !CALLSTACK_WEAKS_AVAILABLE\n#   define CALLSTACK_WEAK\n# endif // !CALLSTACK_WEAKS_AVAILABLE\n#endif // CALLSTACK_WEAK predefined\n\n#ifndef CALLSTACK_ALWAYS_INLINE\n#define CALLSTACK_ALWAYS_INLINE __attribute__((always_inline))\n#endif  // CALLSTACK_ALWAYS_INLINE predefined\n\nnamespace android {\n\nclass Printer;\n\n// Collect/print the call stack (function, file, line) traces for a single thread.\nclass CallStack {\npublic:\n    // Create an empty call stack. No-op.\n    CallStack();\n    // Create a callstack with the current thread's stack trace.\n    // Immediately dump it to logcat using the given logtag.\n    CallStack(const char* logtag, int32_t ignoreDepth = 1);\n    ~CallStack();\n\n    // Reset the stack frames (same as creating an empty call stack).\n    void clear() { mFrameLines.clear(); }\n\n    // Immediately collect the stack traces for the specified thread.\n    // The default is to dump the stack of the current call.\n    void update(int32_t ignoreDepth = 1, pid_t tid = -1);\n\n    // Dump a stack trace to the log using the supplied logtag.\n    void log(const char* logtag,\n             android_LogPriority priority = ANDROID_LOG_DEBUG,\n             const char* prefix = nullptr) const;\n\n    // Dump a stack trace to the specified file descriptor.\n    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;\n\n    // Return a string (possibly very long) containing the complete stack trace.\n    String8 toString(const char* prefix = nullptr) const;\n\n    // Dump a serialized representation of the stack trace to the specified printer.\n    void print(Printer& printer) const;\n\n    // Get the count of stack frames that are in this call stack.\n    size_t size() const { return mFrameLines.size(); }\n\n    // DO NOT USE ANYTHING BELOW HERE. The following public members are expected\n    // to disappear again shortly, once a better replacement facility exists.\n    // The replacement facility will be incompatible!\n\n    // Debugging accesses to some basic functionality. These use weak symbols to\n    // avoid introducing a dependency on libutilscallstack. Such a dependency from\n    // libutils results in a cyclic build dependency. These routines can be called\n    // from within libutils. But if the actual library is unavailable, they do\n    // nothing.\n    //\n    // DO NOT USE THESE. They will disappear.\n    struct StackDeleter {\n#ifdef CALLSTACK_WEAKS_AVAILABLE\n        void operator()(CallStack* stack) {\n            deleteStack(stack);\n        }\n#else\n        void operator()(CallStack*) {}\n#endif\n    };\n\n    typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;\n\n    // Return current call stack if possible, nullptr otherwise.\n#ifdef CALLSTACK_WEAKS_AVAILABLE\n    static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {\n        if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {\n            ALOGW(\"CallStack::getCurrentInternal not linked, returning null\");\n            return CallStackUPtr(nullptr);\n        } else {\n            return getCurrentInternal(ignoreDepth);\n        }\n    }\n#else // !CALLSTACK_WEAKS_AVAILABLE\n    static CallStackUPtr CALLSTACK_ALWAYS_INLINE getCurrent(int32_t = 1) {\n        return CallStackUPtr(nullptr);\n    }\n#endif // !CALLSTACK_WEAKS_AVAILABLE\n\n#ifdef CALLSTACK_WEAKS_AVAILABLE\n    static void CALLSTACK_ALWAYS_INLINE logStack(const char* logtag,\n                                                 CallStack* stack = getCurrent().get(),\n                                                 android_LogPriority priority = ANDROID_LOG_DEBUG) {\n        if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {\n            logStackInternal(logtag, stack, priority);\n        } else {\n            ALOG(LOG_WARN, logtag, \"CallStack::logStackInternal not linked\");\n        }\n    }\n\n#else\n    static void CALLSTACK_ALWAYS_INLINE logStack(const char* logtag,\n                                                 CallStack* = getCurrent().get(),\n                                                 android_LogPriority = ANDROID_LOG_DEBUG) {\n        ALOG(LOG_WARN, logtag, \"CallStack::logStackInternal not linked\");\n    }\n#endif // !CALLSTACK_WEAKS_AVAILABLE\n\n#ifdef CALLSTACK_WEAKS_AVAILABLE\n    static String8 CALLSTACK_ALWAYS_INLINE\n    stackToString(const char* prefix = nullptr, const CallStack* stack = getCurrent().get()) {\n        if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {\n            return stackToStringInternal(prefix, stack);\n        } else {\n            return String8::format(\"%s<CallStack package not linked>\", (prefix ? prefix : \"\"));\n        }\n    }\n#else // !CALLSTACK_WEAKS_AVAILABLE\n    static String8 CALLSTACK_ALWAYS_INLINE stackToString(const char* prefix = nullptr,\n                                                         const CallStack* = getCurrent().get()) {\n        return String8::format(\"%s<CallStack package not linked>\", (prefix ? prefix : \"\"));\n    }\n#endif // !CALLSTACK_WEAKS_AVAILABLE\n\n  private:\n#ifdef CALLSTACK_WEAKS_AVAILABLE\n    static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);\n    static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,\n                                                android_LogPriority priority);\n    static String8 CALLSTACK_WEAK stackToStringInternal(const char* prefix, const CallStack* stack);\n    // The deleter is only invoked on non-null pointers. Hence it will never be\n    // invoked if CallStack is not linked.\n    static void CALLSTACK_WEAK deleteStack(CallStack* stack);\n#endif // CALLSTACK_WEAKS_AVAILABLE\n\n    Vector<String8> mFrameLines;\n};\n\n}  // namespace android\n\n#undef CALLSTACK_WEAKS_AVAILABLE\n#undef CALLSTACK_WEAK\n#undef CALLSTACK_ALWAYS_INLINE\n"
  },
  {
    "path": "libutils/include/utils/Compat.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __LIB_UTILS_COMPAT_H\n#define __LIB_UTILS_COMPAT_H\n\n#include <unistd.h>\n\n#if !defined(__MINGW32__)\n#include <sys/mman.h>\n#endif\n\n#if defined(__APPLE__)\n\n/* Mac OS has always had a 64-bit off_t, so it doesn't have off64_t. */\nstatic_assert(sizeof(off_t) >= 8, \"This code requires that Mac OS have at least a 64-bit off_t.\");\ntypedef off_t off64_t;\n\nstatic inline void* mmap64(void* addr, size_t length, int prot, int flags, int fd, off64_t offset) {\n    return mmap(addr, length, prot, flags, fd, offset);\n}\n\nstatic inline off64_t lseek64(int fd, off64_t offset, int whence) {\n    return lseek(fd, offset, whence);\n}\n\nstatic inline ssize_t pread64(int fd, void* buf, size_t nbytes, off64_t offset) {\n    return pread(fd, buf, nbytes, offset);\n}\n\nstatic inline ssize_t pwrite64(int fd, const void* buf, size_t nbytes, off64_t offset) {\n    return pwrite(fd, buf, nbytes, offset);\n}\n\nstatic inline int ftruncate64(int fd, off64_t length) {\n    return ftruncate(fd, length);\n}\n\n#endif /* __APPLE__ */\n\n#if defined(_WIN32)\n#define O_CLOEXEC O_NOINHERIT\n#define O_NOFOLLOW 0\n#define DEFFILEMODE 0666\n#endif /* _WIN32 */\n\n#define ZD \"%zd\"\n#define ZD_TYPE ssize_t\n\n/*\n * Needed for cases where something should be constexpr if possible, but not\n * being constexpr is fine if in pre-C++11 code (such as a const static float\n * member variable).\n */\n#if __cplusplus >= 201103L\n#define CONSTEXPR constexpr\n#else\n#define CONSTEXPR\n#endif\n\n/* TEMP_FAILURE_RETRY is not available on macOS, but still useful there. */\n#ifndef TEMP_FAILURE_RETRY\n/* Used to retry syscalls that can return EINTR. */\n#define TEMP_FAILURE_RETRY(exp)                \\\n    ({                                         \\\n        __typeof__(exp) _rc;                   \\\n        do {                                   \\\n            _rc = (exp);                       \\\n        } while (_rc == -1 && errno == EINTR); \\\n        _rc;                                   \\\n    })\n#endif\n\n#if defined(_WIN32)\n#define OS_PATH_SEPARATOR '\\\\'\n#else\n#define OS_PATH_SEPARATOR '/'\n#endif\n\n#endif /* __LIB_UTILS_COMPAT_H */\n"
  },
  {
    "path": "libutils/include/utils/Condition.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_UTILS_CONDITION_H\n#define _LIBS_UTILS_CONDITION_H\n\n#include <limits.h>\n#include <stdint.h>\n#include <sys/types.h>\n#include <time.h>\n\n#if !defined(_WIN32)\n# include <pthread.h>\n#endif\n\n#include <utils/Errors.h>\n#include <utils/Mutex.h>\n#include <utils/Timers.h>\n\n// ---------------------------------------------------------------------------\nnamespace android {\n// ---------------------------------------------------------------------------\n\n// DO NOT USE: please use std::condition_variable instead.\n\n/*\n * Condition variable class.  The implementation is system-dependent.\n *\n * Condition variables are paired up with mutexes.  Lock the mutex,\n * call wait(), then either re-wait() if things aren't quite what you want,\n * or unlock the mutex and continue.  All threads calling wait() must\n * use the same mutex for a given Condition.\n *\n * On Android and Apple platforms, these are implemented as a simple wrapper\n * around pthread condition variables.  Care must be taken to abide by\n * the pthreads semantics, in particular, a boolean predicate must\n * be re-evaluated after a wake-up, as spurious wake-ups may happen.\n */\nclass Condition {\npublic:\n    enum {\n        PRIVATE = 0,\n        SHARED = 1\n    };\n\n    enum WakeUpType {\n        WAKE_UP_ONE = 0,\n        WAKE_UP_ALL = 1\n    };\n\n    Condition();\n    explicit Condition(int type);\n    ~Condition();\n    // Wait on the condition variable.  Lock the mutex before calling.\n    // Note that spurious wake-ups may happen.\n    status_t wait(Mutex& mutex);\n    // same with relative timeout\n    status_t waitRelative(Mutex& mutex, nsecs_t reltime);\n    // Signal the condition variable, allowing one thread to continue.\n    void signal();\n    // Signal the condition variable, allowing one or all threads to continue.\n    void signal(WakeUpType type) {\n        if (type == WAKE_UP_ONE) {\n            signal();\n        } else {\n            broadcast();\n        }\n    }\n    // Signal the condition variable, allowing all threads to continue.\n    void broadcast();\n\nprivate:\n#if !defined(_WIN32)\n    pthread_cond_t mCond;\n#else\n    void*   mState;\n#endif\n};\n\n// ---------------------------------------------------------------------------\n\n#if !defined(_WIN32)\n\ninline Condition::Condition() : Condition(PRIVATE) {\n}\ninline Condition::Condition(int type) {\n    pthread_condattr_t attr;\n    pthread_condattr_init(&attr);\n#if defined(__linux__)\n    pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);\n#endif\n\n    if (type == SHARED) {\n        pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);\n    }\n\n    pthread_cond_init(&mCond, &attr);\n    pthread_condattr_destroy(&attr);\n\n}\ninline Condition::~Condition() {\n    pthread_cond_destroy(&mCond);\n}\ninline status_t Condition::wait(Mutex& mutex) {\n    return -pthread_cond_wait(&mCond, &mutex.mMutex);\n}\ninline status_t Condition::waitRelative(Mutex& mutex, nsecs_t reltime) {\n    struct timespec ts;\n#if defined(__linux__)\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n#else // __APPLE__\n    // Apple doesn't support POSIX clocks.\n    struct timeval t;\n    gettimeofday(&t, nullptr);\n    ts.tv_sec = t.tv_sec;\n    ts.tv_nsec = t.tv_usec*1000;\n#endif\n\n    // On 32-bit devices, tv_sec is 32-bit, but `reltime` is 64-bit.\n    int64_t reltime_sec = reltime/1000000000;\n\n    ts.tv_nsec += static_cast<long>(reltime%1000000000);\n    if (reltime_sec < INT64_MAX && ts.tv_nsec >= 1000000000) {\n        ts.tv_nsec -= 1000000000;\n        ++reltime_sec;\n    }\n\n    int64_t time_sec = ts.tv_sec;\n    if (time_sec > INT64_MAX - reltime_sec) {\n        time_sec = INT64_MAX;\n    } else {\n        time_sec += reltime_sec;\n    }\n\n    ts.tv_sec = (time_sec > LONG_MAX) ? LONG_MAX : static_cast<long>(time_sec);\n\n    return -pthread_cond_timedwait(&mCond, &mutex.mMutex, &ts);\n}\ninline void Condition::signal() {\n    pthread_cond_signal(&mCond);\n}\ninline void Condition::broadcast() {\n    pthread_cond_broadcast(&mCond);\n}\n\n#endif // !defined(_WIN32)\n\n// ---------------------------------------------------------------------------\n}  // namespace android\n// ---------------------------------------------------------------------------\n\n#endif // _LIBS_UTILS_CONDITON_H\n"
  },
  {
    "path": "libutils/include/utils/Debug.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// Note: new code should use static_assert directly.\n\n#define COMPILE_TIME_ASSERT static_assert\n#define COMPILE_TIME_ASSERT_FUNCTION_SCOPE static_assert\n"
  },
  {
    "path": "libutils/include/utils/Endian.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Android endian-ness defines.\n//\n#ifndef _LIBS_UTILS_ENDIAN_H\n#define _LIBS_UTILS_ENDIAN_H\n\n#if defined(__APPLE__) || defined(_WIN32)\n\n#define __BIG_ENDIAN 0x1000\n#define __LITTLE_ENDIAN 0x0001\n#define __BYTE_ORDER __LITTLE_ENDIAN\n\n#else\n\n#include <endian.h>\n\n#endif\n\n#endif /*_LIBS_UTILS_ENDIAN_H*/\n"
  },
  {
    "path": "libutils/include/utils/ErrorsMacros.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include \"Errors.h\"\n\n// It would have been better if this file (ErrorsMacros.h) is entirely in utils/Errors.h. However\n// that is infeasible as some (actually many) are using utils/Errors.h via the implicit include path\n// `system/core/include` [1].  Since such users are not guaranteed to specify the dependency to\n// libbase_headers, the following headers from libbase_headers can't be found.\n// [1] build/soong/cc/config/global.go#commonGlobalIncludes\n#include <android-base/errors.h>\n#include <android-base/result.h>\n#include <log/log_main.h>\n\n#include <assert.h>\n\nnamespace android {\n\n// StatusT is a wrapper class for status_t. Use this type instead of status_t when instantiating\n// Result<T, E> and Error<E> template classes. This is required to distinguish status_t from\n// other integer-based error code types like errno, and also to provide utility functions like\n// print().\nstruct StatusT {\n    StatusT() : val_(OK) {}\n    StatusT(status_t s) : val_(s) {}\n    const status_t& value() const { return val_; }\n    operator status_t() const { return val_; }\n    std::string print() const { return statusToString(val_); }\n\n    status_t val_;\n};\n\n\nnamespace base {\n// TODO(b/221235365) StatusT fulfill ResultError contract and cleanup.\n\n// Unlike typical ResultError types, the underlying code should be a status_t\n// instead of a StatusT. We also special-case message generation.\ntemplate<>\nstruct ResultError<StatusT, false> {\n    ResultError(status_t s) : val_(s) {\n        LOG_FATAL_IF(s == OK, \"Result error should not hold success\");\n    }\n\n    template <typename T>\n    operator expected<T, ResultError<StatusT, false>>() const {\n        return unexpected(*this);\n    }\n\n    std::string message() const { return statusToString(val_); }\n    status_t code() const { return val_; }\n\n private:\n    const status_t val_;\n};\n\ntemplate<>\nstruct ResultError<StatusT, true> {\n    template <typename T>\n    ResultError(T&& message, status_t s) : val_(s), message_(std::forward<T>(message)) {\n        LOG_FATAL_IF(s == OK, \"Result error should not hold success\");\n    }\n\n    ResultError(status_t s) : val_(s) {}\n\n    template <typename T>\n    operator expected<T, ResultError<StatusT, true>>() const {\n        return unexpected(*this);\n    }\n\n    status_t code() const { return val_; }\n\n    std::string message() const { return statusToString(val_) + message_; }\n private:\n    const status_t val_;\n    std::string message_;\n};\n\n// Specialization of android::base::OkOrFail<V> for V = status_t. This is used to use the OR_RETURN\n// and OR_FATAL macros with statements that yields a value of status_t. See android-base/errors.h\n// for the detailed contract.\ntemplate <>\nstruct OkOrFail<status_t> {\n    static_assert(std::is_same_v<status_t, int>);\n    // Tests if status_t is a success value of not.\n    static bool IsOk(const status_t& s) { return s == OK; }\n\n    // Unwrapping status_t in the success case is just asserting that it is actually a success.\n    // We don't return OK because it would be redundant.\n    static void Unwrap([[maybe_unused]] status_t&& s) { assert(IsOk(s)); }\n\n    // Consumes status_t when it's a fail value\n    static OkOrFail<status_t> Fail(status_t&& s) {\n        assert(!IsOk(s));\n        return OkOrFail<status_t>{s};\n    }\n    status_t val_;\n\n    // And converts back into status_t. This is used when OR_RETURN is used in a function whose\n    // return type is status_t.\n    operator status_t() && { return val_; }\n\n    // Or converts into Result<T, StatusT>. This is used when OR_RETURN is used in a function whose\n    // return type is Result<T, StatusT>.\n\n    template <typename T>\n    operator Result<T, StatusT>() && {\n        return ResultError<StatusT>(std::move(val_));\n    }\n\n    template<typename T>\n    operator Result<T, StatusT, false>() && {\n        return ResultError<StatusT, false>(std::move(val_));\n    }\n\n    // Since user defined conversion can be followed by numeric conversion,\n    // we have to specialize all conversions to results holding numeric types to\n    // avoid conversion ambiguities with the constructor of expected.\n#pragma push_macro(\"SPECIALIZED_CONVERSION\")\n#define SPECIALIZED_CONVERSION(type)\\\n  operator Result<type, StatusT>() && { return ResultError<StatusT>(std::move(val_)); }\\\n  operator Result<type, StatusT, false>() && { return ResultError<StatusT, false>(std::move(val_));}\n\n    SPECIALIZED_CONVERSION(int)\n    SPECIALIZED_CONVERSION(short int)\n    SPECIALIZED_CONVERSION(unsigned short int)\n    SPECIALIZED_CONVERSION(unsigned int)\n    SPECIALIZED_CONVERSION(long int)\n    SPECIALIZED_CONVERSION(unsigned long int)\n    SPECIALIZED_CONVERSION(long long int)\n    SPECIALIZED_CONVERSION(unsigned long long int)\n    SPECIALIZED_CONVERSION(bool)\n    SPECIALIZED_CONVERSION(char)\n    SPECIALIZED_CONVERSION(unsigned char)\n    SPECIALIZED_CONVERSION(signed char)\n    SPECIALIZED_CONVERSION(wchar_t)\n    SPECIALIZED_CONVERSION(char16_t)\n    SPECIALIZED_CONVERSION(char32_t)\n    SPECIALIZED_CONVERSION(float)\n    SPECIALIZED_CONVERSION(double)\n    SPECIALIZED_CONVERSION(long double)\n#undef SPECIALIZED_CONVERSION\n#pragma pop_macro(\"SPECIALIZED_CONVERSION\")\n    // String representation of the error value.\n    static std::string ErrorMessage(const status_t& s) { return statusToString(s); }\n};\n}  // namespace base\n\n\n// These conversions make StatusT directly comparable to status_t in order to\n// avoid calling code whenever comparisons are desired.\n\ntemplate <bool include_message>\nbool operator==(const base::ResultError<StatusT, include_message>& l, const status_t& r) {\n    return (l.code() == r);\n}\ntemplate <bool include_message>\nbool operator==(const status_t& l, const base::ResultError<StatusT, include_message>& r) {\n    return (l == r.code());\n}\n\ntemplate <bool include_message>\nbool operator!=(const base::ResultError<StatusT, include_message>& l, const status_t& r) {\n    return (l.code() != r);\n}\ntemplate <bool include_message>\nbool operator!=(const status_t& l, const base::ResultError<StatusT, include_message>& r) {\n    return (l != r.code());\n}\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/include/utils/FastStrcmp.h",
    "content": "/*\n * Copyright (C) 2014-2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _ANDROID_UTILS_FASTSTRCMP_H__\n#define _ANDROID_UTILS_FASTSTRCMP_H__\n\n#include <ctype.h>\n#include <string.h>\n\n#ifndef __predict_true\n#define __predict_true(exp) __builtin_expect((exp) != 0, 1)\n#endif\n\n#ifdef __cplusplus\n\n// Optimized for instruction cache locality\n//\n// Template class fastcmp used to create more time-efficient str*cmp\n// functions by pre-checking the first character before resorting\n// to calling the underlying string function.  Profiled with a\n// measurable speedup when used in hot code.  Usage is of the form:\n//\n//  fastcmp<strncmp>(str1, str2, len)\n//\n// NB: use fasticmp for the case insensitive str*cmp functions.\n// NB: Returns boolean, do not use if expecting to check negative value.\n//     Thus not semantically identical to the expected function behavior.\n\ntemplate <int (*cmp)(const char* l, const char* r, const size_t s)>\nstatic inline int fastcmp(const char* l, const char* r, const size_t s) {\n    const ssize_t n = s;  // To help reject negative sizes, treat like zero\n    return __predict_true(n > 0) &&\n           ((*l != *r) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));\n}\n\ntemplate <int (*cmp)(const char* l, const char* r, const size_t s)>\nstatic inline int fasticmp(const char* l, const char* r, const size_t s) {\n    const ssize_t n = s;  // To help reject negative sizes, treat like zero\n    return __predict_true(n > 0) &&\n           ((tolower(*l) != tolower(*r)) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));\n}\n\ntemplate <int (*cmp)(const void* l, const void* r, const size_t s)>\nstatic inline int fastcmp(const void* lv, const void* rv, const size_t s) {\n    const char* l = static_cast<const char*>(lv);\n    const char* r = static_cast<const char*>(rv);\n    const ssize_t n = s;  // To help reject negative sizes, treat like zero\n    return __predict_true(n > 0) &&\n           ((*l != *r) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));\n}\n\ntemplate <int (*cmp)(const char* l, const char* r)>\nstatic inline int fastcmp(const char* l, const char* r) {\n    return (*l != *r) || (__predict_true(*l) && cmp(l + 1, r + 1));\n}\n\ntemplate <int (*cmp)(const char* l, const char* r)>\nstatic inline int fasticmp(const char* l, const char* r) {\n    return (tolower(*l) != tolower(*r)) || (__predict_true(*l) && cmp(l + 1, r + 1));\n}\n\n#endif\n\n#endif // _ANDROID_UTILS_FASTSTRCMP_H__\n"
  },
  {
    "path": "libutils/include/utils/FileMap.h",
    "content": "/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Encapsulate a shared file mapping.\n//\n#ifndef __LIBS_FILE_MAP_H\n#define __LIBS_FILE_MAP_H\n\n#include <sys/types.h>\n\n#include <utils/Compat.h>\n\n#if defined(__MINGW32__)\n// Ensure that we always pull in winsock2.h before windows.h\n#if defined(_WIN32)\n#include <winsock2.h>\n#endif\n#include <windows.h>\n#endif\n\nnamespace android {\n\n/*\n * This represents a memory-mapped file.  It might be the entire file or\n * only part of it.  This requires a little bookkeeping because the mapping\n * needs to be aligned on page boundaries, and in some cases we'd like to\n * have multiple references to the mapped area without creating additional\n * maps.\n *\n * This always uses MAP_SHARED.\n *\n * TODO: we should be able to create a new FileMap that is a subset of\n * an existing FileMap and shares the underlying mapped pages.  Requires\n * completing the refcounting stuff and possibly introducing the notion\n * of a FileMap hierarchy.\n */\nclass FileMap {\npublic:\n    FileMap(void);\n\n    FileMap(FileMap&& f) noexcept;\n    FileMap& operator=(FileMap&& f) noexcept;\n\n    /*\n     * Create a new mapping on an open file.\n     *\n     * Closing the file descriptor does not unmap the pages, so we don't\n     * claim ownership of the fd.\n     *\n     * Returns \"false\" on failure.\n     */\n    bool create(const char* origFileName, int fd,\n                off64_t offset, size_t length, bool readOnly);\n\n    ~FileMap(void);\n\n    /*\n     * Return the name of the file this map came from, if known.\n     */\n    const char* getFileName(void) const { return mFileName; }\n    \n    /*\n     * Get a pointer to the piece of the file we requested.\n     */\n    void* getDataPtr(void) const { return mDataPtr; }\n\n    /*\n     * Get the length we requested.\n     */\n    size_t getDataLength(void) const { return mDataLength; }\n\n    /*\n     * Get the data offset used to create this map.\n     */\n    off64_t getDataOffset(void) const { return mDataOffset; }\n\n    /*\n     * This maps directly to madvise() values, but allows us to avoid\n     * including <sys/mman.h> everywhere.\n     */\n    enum MapAdvice {\n        NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED\n    };\n\n    /*\n     * Apply an madvise() call to the entire file.\n     *\n     * Returns 0 on success, -1 on failure.\n     */\n    int advise(MapAdvice advice);\n\nprotected:\n\nprivate:\n    // these are not implemented\n    FileMap(const FileMap& src);\n    const FileMap& operator=(const FileMap& src);\n\n    char*       mFileName;      // original file name, if known\n    void*       mBasePtr;       // base of mmap area; page aligned\n    size_t      mBaseLength;    // length, measured from \"mBasePtr\"\n    off64_t     mDataOffset;    // offset used when map was created\n    void*       mDataPtr;       // start of requested data, offset from base\n    size_t      mDataLength;    // length, measured from \"mDataPtr\"\n#if defined(__MINGW32__)\n    HANDLE      mFileHandle;    // Win32 file handle\n    HANDLE      mFileMapping;   // Win32 file mapping handle\n#endif\n\n    static long mPageSize;\n};\n\n}  // namespace android\n\n#endif // __LIBS_FILE_MAP_H\n"
  },
  {
    "path": "libutils/include/utils/Flattenable.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// DO NOT USE: please use parcelable instead\n// This code is deprecated and will not be supported via AIDL code gen. For data\n// to be sent over binder, please use parcelables.\n\n#include <stdint.h>\n#include <string.h>\n#include <sys/types.h>\n#include <utils/Errors.h>\n\n#include <type_traits>\n\nnamespace android {\n\n// DO NOT USE: please use parcelable instead\n// This code is deprecated and will not be supported via AIDL code gen. For data\n// to be sent over binder, please use parcelables.\nclass FlattenableUtils {\npublic:\n    template<size_t N>\n    static size_t align(size_t size) {\n        static_assert(!(N & (N - 1)), \"Can only align to a power of 2.\");\n        return (size + (N-1)) & ~(N-1);\n    }\n\n    template<size_t N>\n    static size_t align(void const*& buffer) {\n        static_assert(!(N & (N - 1)), \"Can only align to a power of 2.\");\n        uintptr_t b = uintptr_t(buffer);\n        buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));\n        return size_t(uintptr_t(buffer) - b);\n    }\n\n    template<size_t N>\n    static size_t align(void*& buffer) {\n        static_assert(!(N & (N - 1)), \"Can only align to a power of 2.\");\n        void* b = buffer;\n        buffer = reinterpret_cast<void*>((uintptr_t(buffer) + (N-1)) & ~(N-1));\n        size_t delta = size_t(uintptr_t(buffer) - uintptr_t(b));\n        memset(b, 0, delta);\n        return delta;\n    }\n\n    static void advance(void*& buffer, size_t& size, size_t offset) {\n        buffer = reinterpret_cast<void*>( uintptr_t(buffer) + offset );\n        size -= offset;\n    }\n\n    static void advance(void const*& buffer, size_t& size, size_t offset) {\n        buffer = reinterpret_cast<void const*>( uintptr_t(buffer) + offset );\n        size -= offset;\n    }\n\n    // write a POD structure\n    template<typename T>\n    static void write(void*& buffer, size_t& size, const T& value) {\n        static_assert(std::is_trivially_copyable<T>::value,\n                      \"Cannot flatten a non-trivially-copyable type\");\n        memcpy(buffer, &value, sizeof(T));\n        advance(buffer, size, sizeof(T));\n    }\n\n    // read a POD structure\n    template<typename T>\n    static void read(void const*& buffer, size_t& size, T& value) {\n        static_assert(std::is_trivially_copyable<T>::value,\n                      \"Cannot unflatten a non-trivially-copyable type\");\n        memcpy(&value, buffer, sizeof(T));\n        advance(buffer, size, sizeof(T));\n    }\n};\n\n// DO NOT USE: please use parcelable instead\n// This code is deprecated and will not be supported via AIDL code gen. For data\n// to be sent over binder, please use parcelables.\n/*\n * The Flattenable protocol allows an object to serialize itself out\n * to a byte-buffer and an array of file descriptors.\n * Flattenable objects must implement this protocol.\n */\n\ntemplate <typename T>\nclass Flattenable {\npublic:\n    // size in bytes of the flattened object\n    inline size_t getFlattenedSize() const;\n\n    // number of file descriptors to flatten\n    inline size_t getFdCount() const;\n\n    // flattens the object into buffer.\n    // size should be at least of getFlattenedSize()\n    // file descriptors are written in the fds[] array but ownership is\n    // not transfered (ie: they must be dupped by the caller of\n    // flatten() if needed).\n    inline status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;\n\n    // unflattens the object from buffer.\n    // size should be equal to the value of getFlattenedSize() when the\n    // object was flattened.\n    // unflattened file descriptors are found in the fds[] array and\n    // don't need to be dupped(). ie: the caller of unflatten doesn't\n    // keep ownership. If a fd is not retained by unflatten() it must be\n    // explicitly closed.\n    inline status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);\n};\n\ntemplate<typename T>\ninline size_t Flattenable<T>::getFlattenedSize() const {\n    return static_cast<T const*>(this)->T::getFlattenedSize();\n}\ntemplate<typename T>\ninline size_t Flattenable<T>::getFdCount() const {\n    return static_cast<T const*>(this)->T::getFdCount();\n}\ntemplate<typename T>\ninline status_t Flattenable<T>::flatten(\n        void*& buffer, size_t& size, int*& fds, size_t& count) const {\n    return static_cast<T const*>(this)->T::flatten(buffer, size, fds, count);\n}\ntemplate<typename T>\ninline status_t Flattenable<T>::unflatten(\n        void const*& buffer, size_t& size, int const*& fds, size_t& count) {\n    return static_cast<T*>(this)->T::unflatten(buffer, size, fds, count);\n}\n\n// DO NOT USE: please use parcelable instead\n// This code is deprecated and will not be supported via AIDL code gen. For data\n// to be sent over binder, please use parcelables.\n/*\n * LightFlattenable is a protocol allowing object to serialize themselves out\n * to a byte-buffer. Because it doesn't handle file-descriptors,\n * LightFlattenable is usually more size efficient than Flattenable.\n * LightFlattenable objects must implement this protocol.\n */\ntemplate <typename T>\nclass LightFlattenable {\npublic:\n    // returns whether this object always flatten into the same size.\n    // for efficiency, this should always be inline.\n    inline bool isFixedSize() const;\n\n    // returns size in bytes of the flattened object. must be a constant.\n    inline size_t getFlattenedSize() const;\n\n    // flattens the object into buffer.\n    inline status_t flatten(void* buffer, size_t size) const;\n\n    // unflattens the object from buffer of given size.\n    inline status_t unflatten(void const* buffer, size_t size);\n};\n\ntemplate <typename T>\ninline bool LightFlattenable<T>::isFixedSize() const {\n    return static_cast<T const*>(this)->T::isFixedSize();\n}\ntemplate <typename T>\ninline size_t LightFlattenable<T>::getFlattenedSize() const {\n    return static_cast<T const*>(this)->T::getFlattenedSize();\n}\ntemplate <typename T>\ninline status_t LightFlattenable<T>::flatten(void* buffer, size_t size) const {\n    return static_cast<T const*>(this)->T::flatten(buffer, size);\n}\ntemplate <typename T>\ninline status_t LightFlattenable<T>::unflatten(void const* buffer, size_t size) {\n    return static_cast<T*>(this)->T::unflatten(buffer, size);\n}\n\n// DO NOT USE: please use parcelable instead\n// This code is deprecated and will not be supported via AIDL code gen. For data\n// to be sent over binder, please use parcelables.\n/*\n * LightFlattenablePod is an implementation of the LightFlattenable protocol\n * for POD (plain-old-data) objects.\n * Simply derive from LightFlattenablePod<Foo> to make Foo flattenable; no\n * need to implement any methods; obviously Foo must be a POD structure.\n */\ntemplate <typename T>\nclass LightFlattenablePod : public LightFlattenable<T> {\npublic:\n    inline bool isFixedSize() const {\n        return true;\n    }\n\n    inline size_t getFlattenedSize() const {\n        return sizeof(T);\n    }\n    inline status_t flatten(void* buffer, size_t size) const {\n        if (size < sizeof(T)) return NO_MEMORY;\n        memcpy(buffer, static_cast<T const*>(this), sizeof(T));\n        return OK;\n    }\n    inline status_t unflatten(void const* buffer, size_t) {\n        memcpy(static_cast<T*>(this), buffer, sizeof(T));\n        return OK;\n    }\n};\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/include/utils/Functor.h",
    "content": "/*\n * Copyright (C) 2011 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_FUNCTOR_H\n#define ANDROID_FUNCTOR_H\n\n#include <utils/Errors.h>\n\nnamespace  android {\n\n// DO NOT USE: please use\n// - C++ lambda\n// - class with well-defined and specific functionality and semantics\n\nclass Functor {\npublic:\n    Functor() {}\n    virtual ~Functor() {}\n    virtual status_t operator()(int /*what*/, void* /*data*/) { return OK; }\n};\n\n}  // namespace android\n\n#endif // ANDROID_FUNCTOR_H\n"
  },
  {
    "path": "libutils/include/utils/JenkinsHash.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* Implementation of Jenkins one-at-a-time hash function. These choices are\n * optimized for code size and portability, rather than raw speed. But speed\n * should still be quite good.\n **/\n\n#ifndef ANDROID_JENKINS_HASH_H\n#define ANDROID_JENKINS_HASH_H\n\n#include <utils/TypeHelpers.h>\n\nnamespace android {\n\n/* The Jenkins hash of a sequence of 32 bit words A, B, C is:\n * Whiten(Mix(Mix(Mix(0, A), B), C)) */\n\n#ifdef __clang__\n__attribute__((no_sanitize(\"integer\")))\n#endif\ninline uint32_t JenkinsHashMix(uint32_t hash, uint32_t data) {\n    hash += data;\n    hash += (hash << 10);\n    hash ^= (hash >> 6);\n    return hash;\n}\n\nhash_t JenkinsHashWhiten(uint32_t hash);\n\n/* Helpful utility functions for hashing data in 32 bit chunks */\nuint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size);\n\nuint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size);\n\n}\n\n#endif // ANDROID_JENKINS_HASH_H\n"
  },
  {
    "path": "libutils/include/utils/KeyedVector.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_KEYED_VECTOR_H\n#define ANDROID_KEYED_VECTOR_H\n\n#include <assert.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <log/log.h>\n#include <utils/Errors.h>\n#include <utils/SortedVector.h>\n#include <utils/TypeHelpers.h>\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\n// DO NOT USE: please use std::map\n\ntemplate <typename KEY, typename VALUE>\nclass KeyedVector\n{\npublic:\n    typedef KEY    key_type;\n    typedef VALUE  value_type;\n\n    inline                  KeyedVector();\n\n    /*\n     * empty the vector\n     */\n\n    inline  void            clear()                     { mVector.clear(); }\n\n    /*!\n     * vector stats\n     */\n\n    //! returns number of items in the vector\n    inline  size_t          size() const                { return mVector.size(); }\n    //! returns whether or not the vector is empty\n    inline  bool            isEmpty() const             { return mVector.isEmpty(); }\n    //! returns how many items can be stored without reallocating the backing store\n    inline  size_t          capacity() const            { return mVector.capacity(); }\n    //! sets the capacity. capacity can never be reduced less than size()\n    inline ssize_t          setCapacity(size_t size)    { return mVector.setCapacity(size); }\n\n    // returns true if the arguments is known to be identical to this vector\n    inline bool isIdenticalTo(const KeyedVector& rhs) const;\n\n    /*!\n     * accessors\n     */\n    const VALUE& valueFor(const KEY& key) const;\n    const VALUE& valueAt(size_t index) const;\n    const KEY& keyAt(size_t index) const;\n    ssize_t indexOfKey(const KEY& key) const;\n    const VALUE& operator[](size_t index) const;\n\n    /*!\n     * modifying the array\n     */\n\n            VALUE&          editValueFor(const KEY& key);\n            VALUE&          editValueAt(size_t index);\n\n            /*!\n             * add/insert/replace items\n             */\n\n            ssize_t         add(const KEY& key, const VALUE& item);\n            ssize_t         replaceValueFor(const KEY& key, const VALUE& item);\n            ssize_t         replaceValueAt(size_t index, const VALUE& item);\n\n    /*!\n     * remove items\n     */\n\n            ssize_t         removeItem(const KEY& key);\n            ssize_t         removeItemsAt(size_t index, size_t count = 1);\n\nprivate:\n            SortedVector< key_value_pair_t<KEY, VALUE> >    mVector;\n};\n\n// ---------------------------------------------------------------------------\n\n/**\n * Variation of KeyedVector that holds a default value to return when\n * valueFor() is called with a key that doesn't exist.\n */\ntemplate <typename KEY, typename VALUE>\nclass DefaultKeyedVector : public KeyedVector<KEY, VALUE>\n{\npublic:\n    inline                  DefaultKeyedVector(const VALUE& defValue = VALUE());\n            const VALUE&    valueFor(const KEY& key) const;\n\nprivate:\n            VALUE                                           mDefault;\n};\n\n// ---------------------------------------------------------------------------\n\ntemplate<typename KEY, typename VALUE> inline\nKeyedVector<KEY,VALUE>::KeyedVector()\n{\n}\n\ntemplate<typename KEY, typename VALUE> inline\nbool KeyedVector<KEY,VALUE>::isIdenticalTo(const KeyedVector<KEY,VALUE>& rhs) const {\n    return mVector.array() == rhs.mVector.array();\n}\n\ntemplate<typename KEY, typename VALUE> inline\nssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {\n    return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );\n}\n\ntemplate<typename KEY, typename VALUE> inline\nconst VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {\n    ssize_t i = this->indexOfKey(key);\n    LOG_ALWAYS_FATAL_IF(i<0, \"%s: key not found\", __PRETTY_FUNCTION__);\n    return mVector.itemAt(i).value;\n}\n\ntemplate<typename KEY, typename VALUE> inline\nconst VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {\n    return mVector.itemAt(index).value;\n}\n\ntemplate<typename KEY, typename VALUE> inline\nconst VALUE& KeyedVector<KEY,VALUE>::operator[] (size_t index) const {\n    return valueAt(index);\n}\n\ntemplate<typename KEY, typename VALUE> inline\nconst KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {\n    return mVector.itemAt(index).key;\n}\n\ntemplate<typename KEY, typename VALUE> inline\nVALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {\n    ssize_t i = this->indexOfKey(key);\n    LOG_ALWAYS_FATAL_IF(i<0, \"%s: key not found\", __PRETTY_FUNCTION__);\n    return mVector.editItemAt(static_cast<size_t>(i)).value;\n}\n\ntemplate<typename KEY, typename VALUE> inline\nVALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {\n    return mVector.editItemAt(index).value;\n}\n\ntemplate<typename KEY, typename VALUE> inline\nssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {\n    return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );\n}\n\ntemplate<typename KEY, typename VALUE> inline\nssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {\n    key_value_pair_t<KEY,VALUE> pair(key, value);\n    mVector.remove(pair);\n    return mVector.add(pair);\n}\n\ntemplate<typename KEY, typename VALUE> inline\nssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {\n    if (index<size()) {\n        mVector.editItemAt(index).value = item;\n        return static_cast<ssize_t>(index);\n    }\n    return BAD_INDEX;\n}\n\ntemplate<typename KEY, typename VALUE> inline\nssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {\n    return mVector.remove(key_value_pair_t<KEY,VALUE>(key));\n}\n\ntemplate<typename KEY, typename VALUE> inline\nssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {\n    return mVector.removeItemsAt(index, count);\n}\n\n// ---------------------------------------------------------------------------\n\ntemplate<typename KEY, typename VALUE> inline\nDefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)\n    : mDefault(defValue)\n{\n}\n\ntemplate<typename KEY, typename VALUE> inline\nconst VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {\n    ssize_t i = this->indexOfKey(key);\n    return i >= 0 ? KeyedVector<KEY, VALUE>::valueAt(static_cast<size_t>(i)) : mDefault;\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_KEYED_VECTOR_H\n"
  },
  {
    "path": "libutils/include/utils/List.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Templated list class.  Normally we'd use STL, but we don't have that.\n// This class mimics STL's interfaces.\n//\n// Objects are copied into the list with the '=' operator or with copy-\n// construction, so if the compiler's auto-generated versions won't work for\n// you, define your own.\n//\n// The only class you want to use from here is \"List\".\n//\n#ifndef _LIBS_UTILS_LIST_H\n#define _LIBS_UTILS_LIST_H\n\n#include <stddef.h>\n#include <stdint.h>\n\nnamespace android {\n\n/*\n * Doubly-linked list.  Instantiate with \"List<MyClass> myList\".\n *\n * Objects added to the list are copied using the assignment operator,\n * so this must be defined.\n *\n * DO NOT USE: please use std::list<T>\n */\ntemplate<typename T> \nclass List \n{\nprotected:\n    /*\n     * One element in the list.\n     */\n    class _Node {\n    public:\n        explicit _Node(const T& val) : mVal(val) {}\n        ~_Node() {}\n        inline T& getRef() { return mVal; }\n        inline const T& getRef() const { return mVal; }\n        inline _Node* getPrev() const { return mpPrev; }\n        inline _Node* getNext() const { return mpNext; }\n        inline void setVal(const T& val) { mVal = val; }\n        inline void setPrev(_Node* ptr) { mpPrev = ptr; }\n        inline void setNext(_Node* ptr) { mpNext = ptr; }\n    private:\n        friend class List;\n        friend class _ListIterator;\n        T           mVal;\n        _Node*      mpPrev;\n        _Node*      mpNext;\n    };\n\n    /*\n     * Iterator for walking through the list.\n     */\n    \n    template <typename TYPE>\n    struct CONST_ITERATOR {\n        typedef _Node const * NodePtr;\n        typedef const TYPE Type;\n    };\n    \n    template <typename TYPE>\n    struct NON_CONST_ITERATOR {\n        typedef _Node* NodePtr;\n        typedef TYPE Type;\n    };\n    \n    template<\n        typename U,\n        template <class> class Constness\n    > \n    class _ListIterator {\n        typedef _ListIterator<U, Constness>     _Iter;\n        typedef typename Constness<U>::NodePtr  _NodePtr;\n        typedef typename Constness<U>::Type     _Type;\n\n        explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}\n\n    public:\n        _ListIterator() {}\n        _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}\n        ~_ListIterator() {}\n        \n        // this will handle conversions from iterator to const_iterator\n        // (and also all convertible iterators)\n        // Here, in this implementation, the iterators can be converted\n        // if the nodes can be converted\n        template<typename V> explicit \n        _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}\n        \n\n        /*\n         * Dereference operator.  Used to get at the juicy insides.\n         */\n        _Type& operator*() const { return mpNode->getRef(); }\n        _Type* operator->() const { return &(mpNode->getRef()); }\n\n        /*\n         * Iterator comparison.\n         */\n        inline bool operator==(const _Iter& right) const { \n            return mpNode == right.mpNode; }\n        \n        inline bool operator!=(const _Iter& right) const { \n            return mpNode != right.mpNode; }\n\n        /*\n         * handle comparisons between iterator and const_iterator\n         */\n        template<typename OTHER>\n        inline bool operator==(const OTHER& right) const { \n            return mpNode == right.mpNode; }\n        \n        template<typename OTHER>\n        inline bool operator!=(const OTHER& right) const { \n            return mpNode != right.mpNode; }\n\n        /*\n         * Incr/decr, used to move through the list.\n         */\n        inline _Iter& operator++() {     // pre-increment\n            mpNode = mpNode->getNext();\n            return *this;\n        }\n        const _Iter operator++(int) {    // post-increment\n            _Iter tmp(*this);\n            mpNode = mpNode->getNext();\n            return tmp;\n        }\n        inline _Iter& operator--() {     // pre-increment\n            mpNode = mpNode->getPrev();\n            return *this;\n        }\n        const _Iter operator--(int) {   // post-increment\n            _Iter tmp(*this);\n            mpNode = mpNode->getPrev();\n            return tmp;\n        }\n\n        inline _NodePtr getNode() const { return mpNode; }\n\n        _NodePtr mpNode;    /* should be private, but older gcc fails */\n    private:\n        friend class List;\n    };\n\npublic:\n    List() {\n        prep();\n    }\n    List(const List<T>& src) {      // copy-constructor\n        prep();\n        insert(begin(), src.begin(), src.end());\n    }\n    virtual ~List() {\n        clear();\n        delete[] (unsigned char*) mpMiddle;\n    }\n\n    typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;\n    typedef _ListIterator<T, CONST_ITERATOR> const_iterator;\n\n    List<T>& operator=(const List<T>& right);\n\n    /* returns true if the list is empty */\n    inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }\n\n    /* return #of elements in list */\n    size_t size() const {\n        return size_t(distance(begin(), end()));\n    }\n\n    /*\n     * Return the first element or one past the last element.  The\n     * _Node* we're returning is converted to an \"iterator\" by a\n     * constructor in _ListIterator.\n     */\n    inline iterator begin() { \n        return iterator(mpMiddle->getNext()); \n    }\n    inline const_iterator begin() const { \n        return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); \n    }\n    inline iterator end() { \n        return iterator(mpMiddle); \n    }\n    inline const_iterator end() const { \n        return const_iterator(const_cast<_Node const*>(mpMiddle)); \n    }\n\n    /* add the object to the head or tail of the list */\n    void push_front(const T& val) { insert(begin(), val); }\n    void push_back(const T& val) { insert(end(), val); }\n\n    /* insert before the current node; returns iterator at new node */\n    iterator insert(iterator posn, const T& val) \n    {\n        _Node* newNode = new _Node(val);        // alloc & copy-construct\n        newNode->setNext(posn.getNode());\n        newNode->setPrev(posn.getNode()->getPrev());\n        posn.getNode()->getPrev()->setNext(newNode);\n        posn.getNode()->setPrev(newNode);\n        return iterator(newNode);\n    }\n\n    /* insert a range of elements before the current node */\n    void insert(iterator posn, const_iterator first, const_iterator last) {\n        for ( ; first != last; ++first)\n            insert(posn, *first);\n    }\n\n    /* remove one entry; returns iterator at next node */\n    iterator erase(iterator posn) {\n        _Node* pNext = posn.getNode()->getNext();\n        _Node* pPrev = posn.getNode()->getPrev();\n        pPrev->setNext(pNext);\n        pNext->setPrev(pPrev);\n        delete posn.getNode();\n        return iterator(pNext);\n    }\n\n    /* remove a range of elements */\n    iterator erase(iterator first, iterator last) {\n        while (first != last)\n            erase(first++);     // don't erase than incr later!\n        return iterator(last);\n    }\n\n    /* remove all contents of the list */\n    void clear() {\n        _Node* pCurrent = mpMiddle->getNext();\n        _Node* pNext;\n\n        while (pCurrent != mpMiddle) {\n            pNext = pCurrent->getNext();\n            delete pCurrent;\n            pCurrent = pNext;\n        }\n        mpMiddle->setPrev(mpMiddle);\n        mpMiddle->setNext(mpMiddle);\n    }\n\n    /*\n     * Measure the distance between two iterators.  On exist, \"first\"\n     * will be equal to \"last\".  The iterators must refer to the same\n     * list.\n     *\n     * FIXME: This is actually a generic iterator function. It should be a \n     * template function at the top-level with specializations for things like\n     * vector<>, which can just do pointer math). Here we limit it to\n     * _ListIterator of the same type but different constness.\n     */\n    template<\n        typename U,\n        template <class> class CL,\n        template <class> class CR\n    > \n    ptrdiff_t distance(\n            _ListIterator<U, CL> first, _ListIterator<U, CR> last) const \n    {\n        ptrdiff_t count = 0;\n        while (first != last) {\n            ++first;\n            ++count;\n        }\n        return count;\n    }\n\nprivate:\n    /*\n     * I want a _Node but don't need it to hold valid data.  More\n     * to the point, I don't want T's constructor to fire, since it\n     * might have side-effects or require arguments.  So, we do this\n     * slightly uncouth storage alloc.\n     */\n    void prep() {\n        mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];\n        mpMiddle->setPrev(mpMiddle);\n        mpMiddle->setNext(mpMiddle);\n    }\n\n    /*\n     * This node plays the role of \"pointer to head\" and \"pointer to tail\".\n     * It sits in the middle of a circular list of nodes.  The iterator\n     * runs around the circle until it encounters this one.\n     */\n    _Node*      mpMiddle;\n};\n\n/*\n * Assignment operator.\n *\n * The simplest way to do this would be to clear out the target list and\n * fill it with the source.  However, we can speed things along by\n * re-using existing elements.\n */\ntemplate<class T>\nList<T>& List<T>::operator=(const List<T>& right)\n{\n    if (this == &right)\n        return *this;       // self-assignment\n    iterator firstDst = begin();\n    iterator lastDst = end();\n    const_iterator firstSrc = right.begin();\n    const_iterator lastSrc = right.end();\n    while (firstSrc != lastSrc && firstDst != lastDst)\n        *firstDst++ = *firstSrc++;\n    if (firstSrc == lastSrc)        // ran out of elements in source?\n        erase(firstDst, lastDst);   // yes, erase any extras\n    else\n        insert(lastDst, firstSrc, lastSrc);     // copy remaining over\n    return *this;\n}\n\n}  // namespace android\n\n#endif // _LIBS_UTILS_LIST_H\n"
  },
  {
    "path": "libutils/include/utils/Log.h",
    "content": "// DO NOT INCLUDE ANYTHING NEW IN THIS FILE.\n\n// <log/log.h> has replaced this file and all changes should go there instead.\n// This path remains strictly to include that header as there are thousands of\n// references to <utils/Log.h> in the tree.\n\n#include <log/log.h>\n"
  },
  {
    "path": "libutils/include/utils/Looper.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef UTILS_LOOPER_H\n#define UTILS_LOOPER_H\n\n#include <utils/RefBase.h>\n#include <utils/Timers.h>\n#include <utils/Vector.h>\n#include <utils/threads.h>\n\n#include <sys/epoll.h>\n\n#include <android-base/unique_fd.h>\n\n#include <unordered_map>\n#include <utility>\n\nnamespace android {\n\n/*\n * NOTE: Since Looper is used to implement the NDK ALooper, the Looper\n * enums and the signature of Looper_callbackFunc need to align with\n * that implementation.\n */\n\n/**\n * For callback-based event loops, this is the prototype of the function\n * that is called when a file descriptor event occurs.\n * It is given the file descriptor it is associated with,\n * a bitmask of the poll events that were triggered (typically EVENT_INPUT),\n * and the data pointer that was originally supplied.\n *\n * Implementations should return 1 to continue receiving callbacks, or 0\n * to have this file descriptor and callback unregistered from the looper.\n */\ntypedef int (*Looper_callbackFunc)(int fd, int events, void* data);\n\n/**\n * A message that can be posted to a Looper.\n */\nstruct Message {\n    Message() : what(0) { }\n    Message(int w) : what(w) { }\n\n    /* The message type. (interpretation is left up to the handler) */\n    int what;\n};\n\n\n/**\n * Interface for a Looper message handler.\n *\n * The Looper holds a strong reference to the message handler whenever it has\n * a message to deliver to it.  Make sure to call Looper::removeMessages\n * to remove any pending messages destined for the handler so that the handler\n * can be destroyed.\n */\nclass MessageHandler : public virtual RefBase {\nprotected:\n    virtual ~MessageHandler();\n\npublic:\n    /**\n     * Handles a message.\n     */\n    virtual void handleMessage(const Message& message) = 0;\n};\n\n\n/**\n * A simple proxy that holds a weak reference to a message handler.\n */\nclass WeakMessageHandler : public MessageHandler {\nprotected:\n    virtual ~WeakMessageHandler();\n\npublic:\n    WeakMessageHandler(const wp<MessageHandler>& handler);\n    virtual void handleMessage(const Message& message);\n\nprivate:\n    wp<MessageHandler> mHandler;\n};\n\n\n/**\n * A looper callback.\n */\nclass LooperCallback : public virtual RefBase {\nprotected:\n    virtual ~LooperCallback();\n\npublic:\n    /**\n     * Handles a poll event for the given file descriptor.\n     * It is given the file descriptor it is associated with,\n     * a bitmask of the poll events that were triggered (typically EVENT_INPUT),\n     * and the data pointer that was originally supplied.\n     *\n     * Implementations should return 1 to continue receiving callbacks, or 0\n     * to have this file descriptor and callback unregistered from the looper.\n     */\n    virtual int handleEvent(int fd, int events, void* data) = 0;\n};\n\n/**\n * Wraps a Looper_callbackFunc function pointer.\n */\nclass SimpleLooperCallback : public LooperCallback {\nprotected:\n    virtual ~SimpleLooperCallback();\n\npublic:\n    SimpleLooperCallback(Looper_callbackFunc callback);\n    virtual int handleEvent(int fd, int events, void* data);\n\nprivate:\n    Looper_callbackFunc mCallback;\n};\n\n/**\n * A polling loop that supports monitoring file descriptor events, optionally\n * using callbacks.  The implementation uses epoll() internally.\n *\n * A looper can be associated with a thread although there is no requirement that it must be.\n */\nclass Looper : public RefBase {\nprotected:\n    virtual ~Looper();\n\npublic:\n    enum {\n        /**\n         * Result from Looper_pollOnce() and Looper_pollAll():\n         * The poll was awoken using wake() before the timeout expired\n         * and no callbacks were executed and no other file descriptors were ready.\n         */\n        POLL_WAKE = -1,\n\n        /**\n         * Result from Looper_pollOnce() and Looper_pollAll():\n         * One or more callbacks were executed.\n         */\n        POLL_CALLBACK = -2,\n\n        /**\n         * Result from Looper_pollOnce() and Looper_pollAll():\n         * The timeout expired.\n         */\n        POLL_TIMEOUT = -3,\n\n        /**\n         * Result from Looper_pollOnce() and Looper_pollAll():\n         * An error occurred.\n         */\n        POLL_ERROR = -4,\n    };\n\n    /**\n     * Flags for file descriptor events that a looper can monitor.\n     *\n     * These flag bits can be combined to monitor multiple events at once.\n     */\n    enum {\n        /**\n         * The file descriptor is available for read operations.\n         */\n        EVENT_INPUT = 1 << 0,\n\n        /**\n         * The file descriptor is available for write operations.\n         */\n        EVENT_OUTPUT = 1 << 1,\n\n        /**\n         * The file descriptor has encountered an error condition.\n         *\n         * The looper always sends notifications about errors; it is not necessary\n         * to specify this event flag in the requested event set.\n         */\n        EVENT_ERROR = 1 << 2,\n\n        /**\n         * The file descriptor was hung up.\n         * For example, indicates that the remote end of a pipe or socket was closed.\n         *\n         * The looper always sends notifications about hangups; it is not necessary\n         * to specify this event flag in the requested event set.\n         */\n        EVENT_HANGUP = 1 << 3,\n\n        /**\n         * The file descriptor is invalid.\n         * For example, the file descriptor was closed prematurely.\n         *\n         * The looper always sends notifications about invalid file descriptors; it is not necessary\n         * to specify this event flag in the requested event set.\n         */\n        EVENT_INVALID = 1 << 4,\n    };\n\n    enum {\n        /**\n         * Option for Looper_prepare: this looper will accept calls to\n         * Looper_addFd() that do not have a callback (that is provide NULL\n         * for the callback).  In this case the caller of Looper_pollOnce()\n         * or Looper_pollAll() MUST check the return from these functions to\n         * discover when data is available on such fds and process it.\n         */\n        PREPARE_ALLOW_NON_CALLBACKS = 1<<0\n    };\n\n    /**\n     * Creates a looper.\n     *\n     * If allowNonCallbaks is true, the looper will allow file descriptors to be\n     * registered without associated callbacks.  This assumes that the caller of\n     * pollOnce() is prepared to handle callback-less events itself.\n     */\n    Looper(bool allowNonCallbacks);\n\n    /**\n     * Returns whether this looper instance allows the registration of file descriptors\n     * using identifiers instead of callbacks.\n     */\n    bool getAllowNonCallbacks() const;\n\n    /**\n     * Waits for events to be available, with optional timeout in milliseconds.\n     * Invokes callbacks for all file descriptors on which an event occurred.\n     *\n     * If the timeout is zero, returns immediately without blocking.\n     * If the timeout is negative, waits indefinitely until an event appears.\n     *\n     * Returns POLL_WAKE if the poll was awoken using wake() before\n     * the timeout expired and no callbacks were invoked and no other file\n     * descriptors were ready.\n     *\n     * Returns POLL_CALLBACK if one or more callbacks were invoked.\n     *\n     * Returns POLL_TIMEOUT if there was no data before the given\n     * timeout expired.\n     *\n     * Returns POLL_ERROR if an error occurred.\n     *\n     * Returns a value >= 0 containing an identifier if its file descriptor has data\n     * and it has no callback function (requiring the caller here to handle it).\n     * In this (and only this) case outFd, outEvents and outData will contain the poll\n     * events and data associated with the fd, otherwise they will be set to NULL.\n     *\n     * This method does not return until it has finished invoking the appropriate callbacks\n     * for all file descriptors that were signalled.\n     */\n    int pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData);\n    inline int pollOnce(int timeoutMillis) {\n        return pollOnce(timeoutMillis, nullptr, nullptr, nullptr);\n    }\n\n    /**\n     * Like pollOnce(), but performs all pending callbacks until all\n     * data has been consumed or a file descriptor is available with no callback.\n     * This function will never return POLL_CALLBACK.\n     */\n    int pollAll(int timeoutMillis, int* outFd, int* outEvents, void** outData);\n    inline int pollAll(int timeoutMillis) {\n        return pollAll(timeoutMillis, nullptr, nullptr, nullptr);\n    }\n\n    /**\n     * Wakes the poll asynchronously.\n     *\n     * This method can be called on any thread.\n     * This method returns immediately.\n     */\n    void wake();\n\n    /**\n     * Adds a new file descriptor to be polled by the looper.\n     * If the same file descriptor was previously added, it is replaced.\n     *\n     * \"fd\" is the file descriptor to be added.\n     * \"ident\" is an identifier for this event, which is returned from pollOnce().\n     * The identifier must be >= 0, or POLL_CALLBACK if providing a non-NULL callback.\n     * \"events\" are the poll events to wake up on.  Typically this is EVENT_INPUT.\n     * \"callback\" is the function to call when there is an event on the file descriptor.\n     * \"data\" is a private data pointer to supply to the callback.\n     *\n     * There are two main uses of this function:\n     *\n     * (1) If \"callback\" is non-NULL, then this function will be called when there is\n     * data on the file descriptor.  It should execute any events it has pending,\n     * appropriately reading from the file descriptor.  The 'ident' is ignored in this case.\n     *\n     * (2) If \"callback\" is NULL, the 'ident' will be returned by Looper_pollOnce\n     * when its file descriptor has data available, requiring the caller to take\n     * care of processing it.\n     *\n     * Returns 1 if the file descriptor was added, 0 if the arguments were invalid.\n     *\n     * This method can be called on any thread.\n     * This method may block briefly if it needs to wake the poll.\n     *\n     * The callback may either be specified as a bare function pointer or as a smart\n     * pointer callback object.  The smart pointer should be preferred because it is\n     * easier to avoid races when the callback is removed from a different thread.\n     * See removeFd() for details.\n     */\n    int addFd(int fd, int ident, int events, Looper_callbackFunc callback, void* data);\n    int addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data);\n\n    /**\n     * May be useful for testing, instead of executing a looper on another thread for code expecting\n     * a looper, you can call callbacks directly.\n     */\n    bool getFdStateDebug(int fd, int* ident, int* events, sp<LooperCallback>* cb, void** data);\n\n    /**\n     * Removes a previously added file descriptor from the looper.\n     *\n     * When this method returns, it is safe to close the file descriptor since the looper\n     * will no longer have a reference to it.  However, it is possible for the callback to\n     * already be running or for it to run one last time if the file descriptor was already\n     * signalled.  Calling code is responsible for ensuring that this case is safely handled.\n     * For example, if the callback takes care of removing itself during its own execution either\n     * by returning 0 or by calling this method, then it can be guaranteed to not be invoked\n     * again at any later time unless registered anew.\n     *\n     * A simple way to avoid this problem is to use the version of addFd() that takes\n     * a sp<LooperCallback> instead of a bare function pointer.  The LooperCallback will\n     * be released at the appropriate time by the Looper.\n     *\n     * Returns 1 if the file descriptor was removed, 0 if none was previously registered.\n     *\n     * This method can be called on any thread.\n     * This method may block briefly if it needs to wake the poll.\n     */\n    int removeFd(int fd);\n\n    /**\n     * Tell the kernel to check for the same events we're already checking for\n     * with this FD. This is to be used when there is a kernel driver bug where\n     * the kernel does not properly mark itself as having new data available, in\n     * order to force \"fd_op->poll()\" to be called. You probably don't want to\n     * use this in general, and you shouldn't use it unless there is a plan to\n     * fix the kernel. See also b/296817256.\n     *\n     * Returns 1 if successfully repolled, 0 if not.\n     */\n    int repoll(int fd);\n\n    /**\n     * Enqueues a message to be processed by the specified handler.\n     *\n     * The handler must not be null.\n     * This method can be called on any thread.\n     */\n    void sendMessage(const sp<MessageHandler>& handler, const Message& message);\n\n    /**\n     * Enqueues a message to be processed by the specified handler after all pending messages\n     * after the specified delay.\n     *\n     * The time delay is specified in uptime nanoseconds.\n     * The handler must not be null.\n     * This method can be called on any thread.\n     */\n    void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,\n            const Message& message);\n\n    /**\n     * Enqueues a message to be processed by the specified handler after all pending messages\n     * at the specified time.\n     *\n     * The time is specified in uptime nanoseconds.\n     * The handler must not be null.\n     * This method can be called on any thread.\n     */\n    void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,\n            const Message& message);\n\n    /**\n     * Removes all messages for the specified handler from the queue.\n     *\n     * The handler must not be null.\n     * This method can be called on any thread.\n     */\n    void removeMessages(const sp<MessageHandler>& handler);\n\n    /**\n     * Removes all messages of a particular type for the specified handler from the queue.\n     *\n     * The handler must not be null.\n     * This method can be called on any thread.\n     */\n    void removeMessages(const sp<MessageHandler>& handler, int what);\n\n    /**\n     * Returns whether this looper's thread is currently polling for more work to do.\n     * This is a good signal that the loop is still alive rather than being stuck\n     * handling a callback.  Note that this method is intrinsically racy, since the\n     * state of the loop can change before you get the result back.\n     */\n    bool isPolling() const;\n\n    /**\n     * Prepares a looper associated with the calling thread, and returns it.\n     * If the thread already has a looper, it is returned.  Otherwise, a new\n     * one is created, associated with the thread, and returned.\n     *\n     * The opts may be PREPARE_ALLOW_NON_CALLBACKS or 0.\n     */\n    static sp<Looper> prepare(int opts);\n\n    /**\n     * Sets the given looper to be associated with the calling thread.\n     * If another looper is already associated with the thread, it is replaced.\n     *\n     * If \"looper\" is NULL, removes the currently associated looper.\n     */\n    static void setForThread(const sp<Looper>& looper);\n\n    /**\n     * Returns the looper associated with the calling thread, or NULL if\n     * there is not one.\n     */\n    static sp<Looper> getForThread();\n\nprivate:\n  using SequenceNumber = uint64_t;\n\n  struct Request {\n      int fd;\n      int ident;\n      int events;\n      sp<LooperCallback> callback;\n      void* data;\n\n      uint32_t getEpollEvents() const;\n  };\n\n    struct Response {\n        SequenceNumber seq;\n        int events;\n        Request request;\n    };\n\n    struct MessageEnvelope {\n        MessageEnvelope() : uptime(0) { }\n\n        MessageEnvelope(nsecs_t u, sp<MessageHandler> h, const Message& m)\n            : uptime(u), handler(std::move(h)), message(m) {}\n\n        nsecs_t uptime;\n        sp<MessageHandler> handler;\n        Message message;\n    };\n\n    const bool mAllowNonCallbacks; // immutable\n\n    android::base::unique_fd mWakeEventFd;  // immutable\n    Mutex mLock;\n\n    Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock\n    bool mSendingMessage; // guarded by mLock\n\n    // Whether we are currently waiting for work.  Not protected by a lock,\n    // any use of it is racy anyway.\n    volatile bool mPolling;\n\n    android::base::unique_fd mEpollFd;  // guarded by mLock but only modified on the looper thread\n    bool mEpollRebuildRequired; // guarded by mLock\n\n    // Locked maps of fds and sequence numbers monitoring requests.\n    // Both maps must be kept in sync at all times.\n    std::unordered_map<SequenceNumber, Request> mRequests;               // guarded by mLock\n    std::unordered_map<int /*fd*/, SequenceNumber> mSequenceNumberByFd;  // guarded by mLock\n\n    // The sequence number to use for the next fd that is added to the looper.\n    // The sequence number 0 is reserved for the WakeEventFd.\n    SequenceNumber mNextRequestSeq;  // guarded by mLock\n\n    // This state is only used privately by pollOnce and does not require a lock since\n    // it runs on a single thread.\n    Vector<Response> mResponses;\n    size_t mResponseIndex;\n    nsecs_t mNextMessageUptime; // set to LLONG_MAX when none\n\n    int pollInner(int timeoutMillis);\n    int removeSequenceNumberLocked(SequenceNumber seq);  // requires mLock\n    void awoken();\n    void rebuildEpollLocked();\n    void scheduleEpollRebuildLocked();\n\n    static void initEpollEvent(struct epoll_event* eventItem);\n};\n\n} // namespace android\n\n#endif // UTILS_LOOPER_H\n"
  },
  {
    "path": "libutils/include/utils/LruCache.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_UTILS_LRU_CACHE_H\n#define ANDROID_UTILS_LRU_CACHE_H\n\n#include <memory>\n#include <unordered_set>\n\n#include \"utils/TypeHelpers.h\"  // hash_t\n\nnamespace android {\n\n/**\n * GenerationCache callback used when an item is removed\n */\ntemplate<typename EntryKey, typename EntryValue>\nclass OnEntryRemoved {\npublic:\n    virtual ~OnEntryRemoved() { };\n    virtual void operator()(EntryKey& key, EntryValue& value) = 0;\n}; // class OnEntryRemoved\n\ntemplate <typename TKey, typename TValue>\nclass LruCache {\npublic:\n    explicit LruCache(uint32_t maxCapacity);\n    virtual ~LruCache();\n\n    enum Capacity {\n        kUnlimitedCapacity,\n    };\n\n    void setOnEntryRemovedListener(OnEntryRemoved<TKey, TValue>* listener);\n    size_t size() const;\n    const TValue& get(const TKey& key);\n    bool put(const TKey& key, const TValue& value);\n    bool remove(const TKey& key);\n    bool removeOldest();\n    void clear();\n    const TValue& peekOldestValue();\n\nprivate:\n    LruCache(const LruCache& that);  // disallow copy constructor\n\n    // Super class so that we can have entries having only a key reference, for searches.\n    class KeyedEntry {\n    public:\n        virtual const TKey& getKey() const = 0;\n        // Make sure the right destructor is executed so that keys and values are deleted.\n        virtual ~KeyedEntry() {}\n    };\n\n    class Entry final : public KeyedEntry {\n    public:\n        TKey key;\n        TValue value;\n        Entry* parent;\n        Entry* child;\n\n        Entry(TKey _key, TValue _value) : key(_key), value(_value), parent(nullptr), child(nullptr) {\n        }\n        const TKey& getKey() const final { return key; }\n    };\n\n    class EntryForSearch : public KeyedEntry {\n    public:\n        const TKey& key;\n        EntryForSearch(const TKey& key_) : key(key_) {\n        }\n        const TKey& getKey() const final { return key; }\n    };\n\n    struct HashForEntry {\n        size_t operator() (const KeyedEntry* entry) const {\n            return hash_type(entry->getKey());\n        };\n    };\n\n    struct EqualityForHashedEntries {\n        bool operator() (const KeyedEntry* lhs, const KeyedEntry* rhs) const {\n            return lhs->getKey() == rhs->getKey();\n        };\n    };\n\n    // All entries in the set will be Entry*. Using the weaker KeyedEntry as to allow entries\n    // that have only a key reference, for searching.\n    typedef std::unordered_set<KeyedEntry*, HashForEntry, EqualityForHashedEntries> LruCacheSet;\n\n    void attachToCache(Entry& entry);\n    void detachFromCache(Entry& entry);\n\n    typename LruCacheSet::iterator findByKey(const TKey& key) {\n        EntryForSearch entryForSearch(key);\n        typename LruCacheSet::iterator result = mSet->find(&entryForSearch);\n        return result;\n    }\n\n    std::unique_ptr<LruCacheSet> mSet;\n    OnEntryRemoved<TKey, TValue>* mListener;\n    Entry* mOldest;\n    Entry* mYoungest;\n    uint32_t mMaxCapacity;\n    TValue mNullValue;\n\npublic:\n    // To be used like:\n    // while (it.next()) {\n    //   it.value(); it.key();\n    // }\n    class Iterator {\n    public:\n        Iterator(const LruCache<TKey, TValue>& cache):\n                mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {\n        }\n\n        bool next() {\n            if (mIterator == mCache.mSet->end()) {\n                return false;\n            }\n            if (!mBeginReturned) {\n                // mIterator has been initialized to the beginning and\n                // hasn't been returned. Do not advance:\n                mBeginReturned = true;\n            } else {\n                std::advance(mIterator, 1);\n            }\n            bool ret = (mIterator != mCache.mSet->end());\n            return ret;\n        }\n\n        const TValue& value() const {\n            // All the elements in the set are of type Entry. See comment in the definition\n            // of LruCacheSet above.\n            return reinterpret_cast<Entry *>(*mIterator)->value;\n        }\n\n        const TKey& key() const {\n            return (*mIterator)->getKey();\n        }\n    private:\n        const LruCache<TKey, TValue>& mCache;\n        typename LruCacheSet::iterator mIterator;\n        bool mBeginReturned;\n    };\n};\n\n// Implementation is here, because it's fully templated\ntemplate <typename TKey, typename TValue>\nLruCache<TKey, TValue>::LruCache(uint32_t maxCapacity)\n    : mSet(new LruCacheSet()),\n      mListener(nullptr),\n      mOldest(nullptr),\n      mYoungest(nullptr),\n      mMaxCapacity(maxCapacity),\n      mNullValue{} {\n    mSet->max_load_factor(1.0);\n};\n\ntemplate <typename TKey, typename TValue>\nLruCache<TKey, TValue>::~LruCache() {\n    // Need to delete created entries.\n    clear();\n};\n\ntemplate<typename K, typename V>\nvoid LruCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {\n    mListener = listener;\n}\n\ntemplate <typename TKey, typename TValue>\nsize_t LruCache<TKey, TValue>::size() const {\n    return mSet->size();\n}\n\ntemplate <typename TKey, typename TValue>\nconst TValue& LruCache<TKey, TValue>::get(const TKey& key) {\n    typename LruCacheSet::const_iterator find_result = findByKey(key);\n    if (find_result == mSet->end()) {\n        return mNullValue;\n    }\n    // All the elements in the set are of type Entry. See comment in the definition\n    // of LruCacheSet above.\n    Entry *entry = reinterpret_cast<Entry*>(*find_result);\n    detachFromCache(*entry);\n    attachToCache(*entry);\n    return entry->value;\n}\n\ntemplate <typename TKey, typename TValue>\nbool LruCache<TKey, TValue>::put(const TKey& key, const TValue& value) {\n    if (mMaxCapacity != kUnlimitedCapacity && size() >= mMaxCapacity) {\n        removeOldest();\n    }\n\n    if (findByKey(key) != mSet->end()) {\n        return false;\n    }\n\n    Entry* newEntry = new Entry(key, value);\n    mSet->insert(newEntry);\n    attachToCache(*newEntry);\n    return true;\n}\n\ntemplate <typename TKey, typename TValue>\nbool LruCache<TKey, TValue>::remove(const TKey& key) {\n    typename LruCacheSet::const_iterator find_result = findByKey(key);\n    if (find_result == mSet->end()) {\n        return false;\n    }\n    // All the elements in the set are of type Entry. See comment in the definition\n    // of LruCacheSet above.\n    Entry* entry = reinterpret_cast<Entry*>(*find_result);\n    mSet->erase(entry);\n    if (mListener) {\n        (*mListener)(entry->key, entry->value);\n    }\n    detachFromCache(*entry);\n    delete entry;\n    return true;\n}\n\ntemplate <typename TKey, typename TValue>\nbool LruCache<TKey, TValue>::removeOldest() {\n    if (mOldest != nullptr) {\n        return remove(mOldest->key);\n        // TODO: should probably abort if false\n    }\n    return false;\n}\n\ntemplate <typename TKey, typename TValue>\nconst TValue& LruCache<TKey, TValue>::peekOldestValue() {\n    if (mOldest) {\n        return mOldest->value;\n    }\n    return mNullValue;\n}\n\ntemplate <typename TKey, typename TValue>\nvoid LruCache<TKey, TValue>::clear() {\n    if (mListener) {\n        for (Entry* p = mOldest; p != nullptr; p = p->child) {\n            (*mListener)(p->key, p->value);\n        }\n    }\n    mYoungest = nullptr;\n    mOldest = nullptr;\n    for (auto entry : *mSet.get()) {\n        delete entry;\n    }\n    mSet->clear();\n}\n\ntemplate <typename TKey, typename TValue>\nvoid LruCache<TKey, TValue>::attachToCache(Entry& entry) {\n    if (mYoungest == nullptr) {\n        mYoungest = mOldest = &entry;\n    } else {\n        entry.parent = mYoungest;\n        mYoungest->child = &entry;\n        mYoungest = &entry;\n    }\n}\n\ntemplate <typename TKey, typename TValue>\nvoid LruCache<TKey, TValue>::detachFromCache(Entry& entry) {\n    if (entry.parent != nullptr) {\n        entry.parent->child = entry.child;\n    } else {\n        mOldest = entry.child;\n    }\n    if (entry.child != nullptr) {\n        entry.child->parent = entry.parent;\n    } else {\n        mYoungest = entry.parent;\n    }\n\n    entry.parent = nullptr;\n    entry.child = nullptr;\n}\n\n}\n#endif // ANDROID_UTILS_LRU_CACHE_H\n"
  },
  {
    "path": "libutils/include/utils/Mutex.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_UTILS_MUTEX_H\n#define _LIBS_UTILS_MUTEX_H\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <time.h>\n\n#if !defined(_WIN32)\n# include <pthread.h>\n#endif\n\n#include <utils/Errors.h>\n#include <utils/Timers.h>\n\n// Enable thread safety attributes only with clang.\n// The attributes can be safely erased when compiling with other compilers.\n#if defined(__clang__) && (!defined(SWIG))\n#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))\n#else\n#define THREAD_ANNOTATION_ATTRIBUTE__(x)  // no-op\n#endif\n\n#define CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(capability(x))\n\n#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)\n\n#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))\n\n#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))\n\n#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))\n\n#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))\n\n#define REQUIRES(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__))\n\n#define REQUIRES_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__))\n\n#define ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__))\n\n#define ACQUIRE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__))\n\n#define RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))\n\n#define RELEASE_SHARED(...) THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))\n\n#define TRY_ACQUIRE(...) THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__))\n\n#define TRY_ACQUIRE_SHARED(...) \\\n    THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__))\n\n#define EXCLUDES(...) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))\n\n#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))\n\n#define ASSERT_SHARED_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))\n\n#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))\n\n#define NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)\n\n// ---------------------------------------------------------------------------\nnamespace android {\n// ---------------------------------------------------------------------------\n\nclass Condition;\n\n/*\n * NOTE: This class is for code that builds on Win32.  Its usage is\n * deprecated for code which doesn't build for Win32.  New code which\n * doesn't build for Win32 should use std::mutex and std::lock_guard instead.\n *\n * Simple mutex class.  The implementation is system-dependent.\n *\n * The mutex must be unlocked by the thread that locked it.  They are not\n * recursive, i.e. the same thread can't lock it multiple times.\n */\nclass CAPABILITY(\"mutex\") Mutex {\n  public:\n    enum {\n        PRIVATE = 0,\n        SHARED = 1\n    };\n\n    Mutex();\n    explicit Mutex(const char* name);\n    explicit Mutex(int type, const char* name = nullptr);\n    ~Mutex();\n\n    // lock or unlock the mutex\n    status_t lock() ACQUIRE();\n    void unlock() RELEASE();\n\n    // lock if possible; returns 0 on success, error otherwise\n    status_t tryLock() TRY_ACQUIRE(0);\n\n#if defined(__ANDROID__)\n    // Lock the mutex, but don't wait longer than timeoutNs (relative time).\n    // Returns 0 on success, TIMED_OUT for failure due to timeout expiration.\n    //\n    // OSX doesn't have pthread_mutex_timedlock() or equivalent. To keep\n    // capabilities consistent across host OSes, this method is only available\n    // when building Android binaries.\n    //\n    // FIXME?: pthread_mutex_timedlock is based on CLOCK_REALTIME,\n    // which is subject to NTP adjustments, and includes time during suspend,\n    // so a timeout may occur even though no processes could run.\n    // Not holding a partial wakelock may lead to a system suspend.\n    status_t timedLock(nsecs_t timeoutNs) TRY_ACQUIRE(0);\n#endif\n\n    // Manages the mutex automatically. It'll be locked when Autolock is\n    // constructed and released when Autolock goes out of scope.\n    class SCOPED_CAPABILITY Autolock {\n      public:\n        inline explicit Autolock(Mutex& mutex) ACQUIRE(mutex) : mLock(mutex) { mLock.lock(); }\n        inline explicit Autolock(Mutex* mutex) ACQUIRE(mutex) : mLock(*mutex) { mLock.lock(); }\n        inline ~Autolock() RELEASE() { mLock.unlock(); }\n\n      private:\n        Mutex& mLock;\n        // Cannot be copied or moved - declarations only\n        Autolock(const Autolock&);\n        Autolock& operator=(const Autolock&);\n    };\n\n  private:\n    friend class Condition;\n\n    // A mutex cannot be copied\n    Mutex(const Mutex&);\n    Mutex& operator=(const Mutex&);\n\n#if !defined(_WIN32)\n    pthread_mutex_t mMutex;\n#else\n    void _init();\n    void* mState;\n#endif\n};\n\n// ---------------------------------------------------------------------------\n\n#if !defined(_WIN32)\n\ninline Mutex::Mutex() {\n    pthread_mutex_init(&mMutex, nullptr);\n}\ninline Mutex::Mutex(__attribute__((unused)) const char* name) {\n    pthread_mutex_init(&mMutex, nullptr);\n}\ninline Mutex::Mutex(int type, __attribute__((unused)) const char* name) {\n    if (type == SHARED) {\n        pthread_mutexattr_t attr;\n        pthread_mutexattr_init(&attr);\n        pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);\n        pthread_mutex_init(&mMutex, &attr);\n        pthread_mutexattr_destroy(&attr);\n    } else {\n        pthread_mutex_init(&mMutex, nullptr);\n    }\n}\ninline Mutex::~Mutex() {\n    pthread_mutex_destroy(&mMutex);\n}\ninline status_t Mutex::lock() {\n    return -pthread_mutex_lock(&mMutex);\n}\ninline void Mutex::unlock() {\n    pthread_mutex_unlock(&mMutex);\n}\ninline status_t Mutex::tryLock() {\n    return -pthread_mutex_trylock(&mMutex);\n}\n#if defined(__ANDROID__)\ninline status_t Mutex::timedLock(nsecs_t timeoutNs) {\n    timeoutNs += systemTime(SYSTEM_TIME_REALTIME);\n    const struct timespec ts = {\n        /* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),\n        /* .tv_nsec = */ static_cast<long>(timeoutNs % 1000000000),\n    };\n    return -pthread_mutex_timedlock(&mMutex, &ts);\n}\n#endif\n\n#endif // !defined(_WIN32)\n\n// ---------------------------------------------------------------------------\n\n/*\n * Automatic mutex.  Declare one of these at the top of a function.\n * When the function returns, it will go out of scope, and release the\n * mutex.\n */\n\ntypedef Mutex::Autolock AutoMutex;\n\n// ---------------------------------------------------------------------------\n}  // namespace android\n// ---------------------------------------------------------------------------\n\n#endif // _LIBS_UTILS_MUTEX_H\n"
  },
  {
    "path": "libutils/include/utils/NativeHandle.h",
    "content": "/*\n * Copyright 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_NATIVE_HANDLE_H\n#define ANDROID_NATIVE_HANDLE_H\n\n#include <utils/RefBase.h>\n#include <utils/StrongPointer.h>\n\ntypedef struct native_handle native_handle_t;\n\nnamespace android {\n\nclass NativeHandle : public LightRefBase<NativeHandle> {\npublic:\n    // Create a refcounted wrapper around a native_handle_t, and declare\n    // whether the wrapper owns the handle (so that it should clean up the\n    // handle upon destruction) or not.\n    // If handle is NULL, no NativeHandle will be created.\n    static sp<NativeHandle> create(native_handle_t* handle, bool ownsHandle);\n\n    const native_handle_t* handle() const {\n        return mHandle;\n    }\n\nprivate:\n    // for access to the destructor\n    friend class LightRefBase<NativeHandle>;\n    // for access to the constructor\n    friend class sp<NativeHandle>;\n\n    NativeHandle(native_handle_t* handle, bool ownsHandle);\n    ~NativeHandle();\n\n    native_handle_t* mHandle;\n    bool mOwnsHandle;\n\n    // non-copyable\n    NativeHandle(const NativeHandle&);\n    NativeHandle& operator=(const NativeHandle&);\n};\n\n} // namespace android\n\n#endif // ANDROID_NATIVE_HANDLE_H\n"
  },
  {
    "path": "libutils/include/utils/Printer.h",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_PRINTER_H\n#define ANDROID_PRINTER_H\n\n#include <android/log.h>\n\nnamespace android {\n\n// Interface for printing to an arbitrary data stream\nclass Printer {\npublic:\n    // Print a new line specified by 'string'. \\n is appended automatically.\n    // -- Assumes that the string has no new line in it.\n    virtual void printLine(const char* string = \"\") = 0;\n\n    // Print a new line specified by the format string. \\n is appended automatically.\n    // -- Assumes that the resulting string has no new line in it.\n    virtual void printFormatLine(const char* format, ...) __attribute__((format (printf, 2, 3)));\n\nprotected:\n    Printer();\n    virtual ~Printer();\n}; // class Printer\n\n// Print to logcat\nclass LogPrinter : public Printer {\npublic:\n    // Create a printer using the specified logcat and log priority\n    // - Unless ignoreBlankLines is false, print blank lines to logcat\n    // (Note that the default ALOG behavior is to ignore blank lines)\n    LogPrinter(const char* logtag,\n               android_LogPriority priority = ANDROID_LOG_DEBUG,\n               const char* prefix = nullptr,\n               bool ignoreBlankLines = false);\n\n    // Print the specified line to logcat. No \\n at the end is necessary.\n    virtual void printLine(const char* string);\n\nprivate:\n    void printRaw(const char* string);\n\n    const char* mLogTag;\n    android_LogPriority mPriority;\n    const char* mPrefix;\n    bool mIgnoreBlankLines;\n}; // class LogPrinter\n\n// Print to a file descriptor\nclass FdPrinter : public Printer {\npublic:\n    // Create a printer using the specified file descriptor.\n    // - Each line will be prefixed with 'indent' number of blank spaces.\n    // - In addition, each line will be prefixed with the 'prefix' string.\n    FdPrinter(int fd, unsigned int indent = 0, const char* prefix = nullptr);\n\n    // Print the specified line to the file descriptor. \\n is appended automatically.\n    virtual void printLine(const char* string);\n\nprivate:\n    enum {\n        MAX_FORMAT_STRING = 20,\n    };\n\n    int mFd;\n    unsigned int mIndent;\n    const char* mPrefix;\n    char mFormatString[MAX_FORMAT_STRING];\n}; // class FdPrinter\n\nclass String8;\n\n// Print to a String8\nclass String8Printer : public Printer {\npublic:\n    // Create a printer using the specified String8 as the target.\n    // - In addition, each line will be prefixed with the 'prefix' string.\n    // - target's memory lifetime must be a superset of this String8Printer.\n    String8Printer(String8* target, const char* prefix = nullptr);\n\n    // Append the specified line to the String8. \\n is appended automatically.\n    virtual void printLine(const char* string);\n\nprivate:\n    String8* mTarget;\n    const char* mPrefix;\n}; // class String8Printer\n\n// Print to an existing Printer by adding a prefix to each line\nclass PrefixPrinter : public Printer {\npublic:\n    // Create a printer using the specified printer as the target.\n    PrefixPrinter(Printer& printer, const char* prefix);\n\n    // Print the line (prefixed with prefix) using the printer.\n    virtual void printLine(const char* string);\n\nprivate:\n    Printer& mPrinter;\n    const char* mPrefix;\n};\n\n}  // namespace android\n\n#endif // ANDROID_PRINTER_H\n"
  },
  {
    "path": "libutils/include/utils/ProcessCallStack.h",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_PROCESS_CALLSTACK_H\n#define ANDROID_PROCESS_CALLSTACK_H\n\n#include <utils/CallStack.h>\n#include <android/log.h>\n#include <utils/KeyedVector.h>\n#include <utils/String8.h>\n\n#include <time.h>\n#include <sys/types.h>\n\nnamespace android {\n\nclass Printer;\n\n// Collect/print the call stack (function, file, line) traces for all threads in a process.\nclass ProcessCallStack {\npublic:\n    // Create an empty call stack. No-op.\n    ProcessCallStack();\n    // Copy the existing process callstack (no other side effects).\n    ProcessCallStack(const ProcessCallStack& rhs);\n    ~ProcessCallStack();\n\n    // Immediately collect the stack traces for all threads.\n    void update();\n\n    // Print all stack traces to the log using the supplied logtag.\n    void log(const char* logtag, android_LogPriority priority = ANDROID_LOG_DEBUG,\n             const char* prefix = nullptr) const;\n\n    // Dump all stack traces to the specified file descriptor.\n    void dump(int fd, int indent = 0, const char* prefix = nullptr) const;\n\n    // Return a string (possibly very long) containing all the stack traces.\n    String8 toString(const char* prefix = nullptr) const;\n\n    // Dump a serialized representation of all the stack traces to the specified printer.\n    void print(Printer& printer) const;\n\n    // Get the number of threads whose stack traces were collected.\n    size_t size() const;\n\nprivate:\n    void printInternal(Printer& printer, Printer& csPrinter) const;\n\n    // Reset the process's stack frames and metadata.\n    void clear();\n\n    struct ThreadInfo {\n        CallStack callStack;\n        String8 threadName;\n    };\n\n    // tid -> ThreadInfo\n    KeyedVector<pid_t, ThreadInfo> mThreadMap;\n    // Time that update() was last called\n    struct tm mTimeUpdated;\n};\n\n}  // namespace android\n\n#endif // ANDROID_PROCESS_CALLSTACK_H\n"
  },
  {
    "path": "libutils/include/utils/RWLock.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_UTILS_RWLOCK_H\n#define _LIBS_UTILS_RWLOCK_H\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#if !defined(_WIN32)\n# include <pthread.h>\n#endif\n\n#include <utils/Errors.h>\n#include <utils/ThreadDefs.h>\n\n// ---------------------------------------------------------------------------\nnamespace android {\n// ---------------------------------------------------------------------------\n\n#if !defined(_WIN32)\n\n/*\n * Simple mutex class.  The implementation is system-dependent.\n *\n * The mutex must be unlocked by the thread that locked it.  They are not\n * recursive, i.e. the same thread can't lock it multiple times.\n */\nclass RWLock {\npublic:\n    enum {\n        PRIVATE = 0,\n        SHARED = 1\n    };\n\n                RWLock();\n    explicit    RWLock(const char* name);\n    explicit    RWLock(int type, const char* name = nullptr);\n                ~RWLock();\n\n    status_t    readLock();\n    status_t    tryReadLock();\n    status_t    writeLock();\n    status_t    tryWriteLock();\n    void        unlock();\n\n    class AutoRLock {\n    public:\n        inline explicit AutoRLock(RWLock& rwlock) : mLock(rwlock)  { mLock.readLock(); }\n        inline ~AutoRLock() { mLock.unlock(); }\n    private:\n        RWLock& mLock;\n    };\n\n    class AutoWLock {\n    public:\n        inline explicit AutoWLock(RWLock& rwlock) : mLock(rwlock)  { mLock.writeLock(); }\n        inline ~AutoWLock() { mLock.unlock(); }\n    private:\n        RWLock& mLock;\n    };\n\nprivate:\n    // A RWLock cannot be copied\n                RWLock(const RWLock&);\n   RWLock&      operator = (const RWLock&);\n\n   pthread_rwlock_t mRWLock;\n};\n\ninline RWLock::RWLock() {\n    pthread_rwlock_init(&mRWLock, nullptr);\n}\ninline RWLock::RWLock(__attribute__((unused)) const char* name) {\n    pthread_rwlock_init(&mRWLock, nullptr);\n}\ninline RWLock::RWLock(int type, __attribute__((unused)) const char* name) {\n    if (type == SHARED) {\n        pthread_rwlockattr_t attr;\n        pthread_rwlockattr_init(&attr);\n        pthread_rwlockattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);\n        pthread_rwlock_init(&mRWLock, &attr);\n        pthread_rwlockattr_destroy(&attr);\n    } else {\n        pthread_rwlock_init(&mRWLock, nullptr);\n    }\n}\ninline RWLock::~RWLock() {\n    pthread_rwlock_destroy(&mRWLock);\n}\ninline status_t RWLock::readLock() {\n    return -pthread_rwlock_rdlock(&mRWLock);\n}\ninline status_t RWLock::tryReadLock() {\n    return -pthread_rwlock_tryrdlock(&mRWLock);\n}\ninline status_t RWLock::writeLock() {\n    return -pthread_rwlock_wrlock(&mRWLock);\n}\ninline status_t RWLock::tryWriteLock() {\n    return -pthread_rwlock_trywrlock(&mRWLock);\n}\ninline void RWLock::unlock() {\n    pthread_rwlock_unlock(&mRWLock);\n}\n\n#endif // !defined(_WIN32)\n\n// ---------------------------------------------------------------------------\n}  // namespace android\n// ---------------------------------------------------------------------------\n\n#endif // _LIBS_UTILS_RWLOCK_H\n"
  },
  {
    "path": "libutils/include/utils/Singleton.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_UTILS_SINGLETON_H\n#define ANDROID_UTILS_SINGLETON_H\n\n#include <stdint.h>\n\n// some vendor code assumes they have atoi() after including this file.\n#include <stdlib.h>\n\n#include <sys/types.h>\n#include <utils/Mutex.h>\n#include <cutils/compiler.h>\n\nnamespace android {\n// ---------------------------------------------------------------------------\n\n// Singleton<TYPE> may be used in multiple libraries, only one of which should\n// define the static member variables using ANDROID_SINGLETON_STATIC_INSTANCE.\n// Turn off -Wundefined-var-template so other users don't get:\n// instantiation of variable 'android::Singleton<TYPE>::sLock' required here,\n// but no definition is available\n#if defined(__clang__)\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wundefined-var-template\"\n#endif\n\n// DO NOT USE: Please use scoped static initialization. For instance:\n//     MyClass& getInstance() {\n//         static MyClass gInstance(...);\n//         return gInstance;\n//     }\ntemplate <typename TYPE>\nclass ANDROID_API Singleton\n{\npublic:\n    static TYPE& getInstance() {\n        Mutex::Autolock _l(sLock);\n        TYPE* instance = sInstance;\n        if (instance == nullptr) {\n            instance = new TYPE();\n            sInstance = instance;\n        }\n        return *instance;\n    }\n\n    static bool hasInstance() {\n        Mutex::Autolock _l(sLock);\n        return sInstance != nullptr;\n    }\n    \nprotected:\n    ~Singleton() { }\n    Singleton() { }\n\nprivate:\n    Singleton(const Singleton&);\n    Singleton& operator = (const Singleton&);\n    static Mutex sLock;\n    static TYPE* sInstance;\n};\n\n#if defined(__clang__)\n#pragma clang diagnostic pop\n#endif\n\n/*\n * use ANDROID_SINGLETON_STATIC_INSTANCE(TYPE) in your implementation file\n * (eg: <TYPE>.cpp) to create the static instance of Singleton<>'s attributes,\n * and avoid to have a copy of them in each compilation units Singleton<TYPE>\n * is used.\n * NOTE: we use a version of Mutex ctor that takes a parameter, because\n * for some unknown reason using the default ctor doesn't emit the variable!\n */\n\n#define ANDROID_SINGLETON_STATIC_INSTANCE(TYPE)                 \\\n    template<> ::android::Mutex  \\\n        (::android::Singleton< TYPE >::sLock)(::android::Mutex::PRIVATE);  \\\n    template<> TYPE* ::android::Singleton< TYPE >::sInstance(nullptr);  /* NOLINT */ \\\n    template class ::android::Singleton< TYPE >;\n\n\n// ---------------------------------------------------------------------------\n}  // namespace android\n\n#endif // ANDROID_UTILS_SINGLETON_H\n\n"
  },
  {
    "path": "libutils/include/utils/SortedVector.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_SORTED_VECTOR_H\n#define ANDROID_SORTED_VECTOR_H\n\n#include <assert.h>\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <log/log.h>\n#include <utils/TypeHelpers.h>\n#include <utils/Vector.h>\n#include <utils/VectorImpl.h>\n\n// ---------------------------------------------------------------------------\n\nnamespace android {\n\n// DO NOT USE: please use std::set\n\ntemplate <class TYPE>\nclass SortedVector : private SortedVectorImpl\n{\n    friend class Vector<TYPE>;\n\npublic:\n            typedef TYPE    value_type;\n\n    /*!\n     * Constructors and destructors\n     */\n\n                            SortedVector();\n                            SortedVector(const SortedVector<TYPE>& rhs);\n    virtual                 ~SortedVector();\n\n    /*! copy operator */\n    const SortedVector<TYPE>&   operator = (const SortedVector<TYPE>& rhs) const;\n    SortedVector<TYPE>&         operator = (const SortedVector<TYPE>& rhs);\n\n    /*\n     * empty the vector\n     */\n\n    inline  void            clear()             { VectorImpl::clear(); }\n\n    /*!\n     * vector stats\n     */\n\n    //! returns number of items in the vector\n    inline  size_t          size() const                { return VectorImpl::size(); }\n    //! returns whether or not the vector is empty\n    inline  bool            isEmpty() const             { return VectorImpl::isEmpty(); }\n    //! returns how many items can be stored without reallocating the backing store\n    inline  size_t          capacity() const            { return VectorImpl::capacity(); }\n    //! sets the capacity. capacity can never be reduced less than size()\n    inline  ssize_t         setCapacity(size_t size)    { return VectorImpl::setCapacity(size); }\n\n    /*!\n     * C-style array access\n     */\n\n    //! read-only C-style access\n    inline  const TYPE*     array() const;\n\n    //! read-write C-style access. BE VERY CAREFUL when modifying the array\n    //! you must keep it sorted! You usually don't use this function.\n            TYPE*           editArray();\n\n            //! finds the index of an item\n            ssize_t         indexOf(const TYPE& item) const;\n\n            //! finds where this item should be inserted\n            size_t          orderOf(const TYPE& item) const;\n\n\n    /*!\n     * accessors\n     */\n\n    //! read-only access to an item at a given index\n    inline  const TYPE&     operator [] (size_t index) const;\n    //! alternate name for operator []\n    inline  const TYPE&     itemAt(size_t index) const;\n    //! stack-usage of the vector. returns the top of the stack (last element)\n            const TYPE&     top() const;\n\n    /*!\n     * modifying the array\n     */\n\n            //! add an item in the right place (and replace the one that is there)\n            ssize_t         add(const TYPE& item);\n\n            //! editItemAt() MUST NOT change the order of this item\n            TYPE&           editItemAt(size_t index) {\n                return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );\n            }\n\n            //! merges a vector into this one\n            ssize_t         merge(const Vector<TYPE>& vector);\n            ssize_t         merge(const SortedVector<TYPE>& vector);\n\n            //! removes an item\n            ssize_t         remove(const TYPE&);\n\n    //! remove several items\n    inline  ssize_t         removeItemsAt(size_t index, size_t count = 1);\n    //! remove one item\n    inline  ssize_t         removeAt(size_t index)  { return removeItemsAt(index); }\n\n    /*\n     * these inlines add some level of compatibility with STL.\n     */\n    typedef TYPE* iterator;\n    typedef TYPE const* const_iterator;\n\n    inline iterator begin() { return editArray(); }\n    inline iterator end()   { return editArray() + size(); }\n    inline const_iterator begin() const { return array(); }\n    inline const_iterator end() const   { return array() + size(); }\n    inline void reserve(size_t n) { setCapacity(n); }\n    inline bool empty() const{ return isEmpty(); }\n    inline iterator erase(iterator pos) {\n        ssize_t index = removeItemsAt(pos-array());\n        return begin() + index;\n    }\n\nprotected:\n    virtual void    do_construct(void* storage, size_t num) const;\n    virtual void    do_destroy(void* storage, size_t num) const;\n    virtual void    do_copy(void* dest, const void* from, size_t num) const;\n    virtual void    do_splat(void* dest, const void* item, size_t num) const;\n    virtual void    do_move_forward(void* dest, const void* from, size_t num) const;\n    virtual void    do_move_backward(void* dest, const void* from, size_t num) const;\n    virtual int     do_compare(const void* lhs, const void* rhs) const;\n};\n\n// ---------------------------------------------------------------------------\n// No user serviceable parts from here...\n// ---------------------------------------------------------------------------\n\ntemplate<class TYPE> inline\nSortedVector<TYPE>::SortedVector()\n    : SortedVectorImpl(sizeof(TYPE),\n                ((traits<TYPE>::has_trivial_ctor   ? HAS_TRIVIAL_CTOR   : 0)\n                |(traits<TYPE>::has_trivial_dtor   ? HAS_TRIVIAL_DTOR   : 0)\n                |(traits<TYPE>::has_trivial_copy   ? HAS_TRIVIAL_COPY   : 0))\n                )\n{\n}\n\ntemplate<class TYPE> inline\nSortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)\n    : SortedVectorImpl(rhs) {\n}\n\ntemplate<class TYPE> inline\nSortedVector<TYPE>::~SortedVector() {\n    finish_vector();\n}\n\ntemplate<class TYPE> inline\nSortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {\n    SortedVectorImpl::operator = (rhs);\n    return *this;\n}\n\ntemplate<class TYPE> inline\nconst SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {\n    SortedVectorImpl::operator = (rhs);\n    return *this;\n}\n\ntemplate<class TYPE> inline\nconst TYPE* SortedVector<TYPE>::array() const {\n    return static_cast<const TYPE *>(arrayImpl());\n}\n\ntemplate<class TYPE> inline\nTYPE* SortedVector<TYPE>::editArray() {\n    return static_cast<TYPE *>(editArrayImpl());\n}\n\n\ntemplate<class TYPE> inline\nconst TYPE& SortedVector<TYPE>::operator[](size_t index) const {\n    LOG_FATAL_IF(index>=size(),\n            \"%s: index=%u out of range (%u)\", __PRETTY_FUNCTION__,\n            int(index), int(size()));\n    return *(array() + index);\n}\n\ntemplate<class TYPE> inline\nconst TYPE& SortedVector<TYPE>::itemAt(size_t index) const {\n    return operator[](index);\n}\n\ntemplate<class TYPE> inline\nconst TYPE& SortedVector<TYPE>::top() const {\n    return *(array() + size() - 1);\n}\n\ntemplate<class TYPE> inline\nssize_t SortedVector<TYPE>::add(const TYPE& item) {\n    return SortedVectorImpl::add(&item);\n}\n\ntemplate<class TYPE> inline\nssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {\n    return SortedVectorImpl::indexOf(&item);\n}\n\ntemplate<class TYPE> inline\nsize_t SortedVector<TYPE>::orderOf(const TYPE& item) const {\n    return SortedVectorImpl::orderOf(&item);\n}\n\ntemplate<class TYPE> inline\nssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {\n    return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));\n}\n\ntemplate<class TYPE> inline\nssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {\n    return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));\n}\n\ntemplate<class TYPE> inline\nssize_t SortedVector<TYPE>::remove(const TYPE& item) {\n    return SortedVectorImpl::remove(&item);\n}\n\ntemplate<class TYPE> inline\nssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {\n    return VectorImpl::removeItemsAt(index, count);\n}\n\n// ---------------------------------------------------------------------------\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {\n    construct_type( reinterpret_cast<TYPE*>(storage), num );\n}\n\ntemplate<class TYPE>\nvoid SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {\n    destroy_type( reinterpret_cast<TYPE*>(storage), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {\n    copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {\n    splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {\n    move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );\n}\n\ntemplate<class TYPE>\nUTILS_VECTOR_NO_CFI void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {\n    move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );\n}\n\ntemplate<class TYPE>\nint SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {\n    return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );\n}\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n\n#endif // ANDROID_SORTED_VECTOR_H\n"
  },
  {
    "path": "libutils/include/utils/StopWatch.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <sys/types.h>\n\n#include <utils/Timers.h>\n\nnamespace android {\n\nclass StopWatch {\n  public:\n    StopWatch(const char* name, int clock = SYSTEM_TIME_MONOTONIC);\n    ~StopWatch();\n\n    const char* name() const;\n    nsecs_t elapsedTime() const;\n\n    void reset();\n\n  private:\n    const char* mName;\n    int mClock;\n\n    nsecs_t mStartTime;\n};\n\n}  // namespace android\n"
  },
  {
    "path": "libutils/include/utils/SystemClock.h",
    "content": "/*\n * Copyright (C) 2008 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_UTILS_SYSTEMCLOCK_H\n#define ANDROID_UTILS_SYSTEMCLOCK_H\n\n#include <stdint.h>\n#include <sys/types.h>\n\n// See https://developer.android.com/reference/android/os/SystemClock\n// to learn more about Android's timekeeping facilities.\n\nnamespace android {\n\n// Returns milliseconds since boot, not counting time spent in deep sleep.\nint64_t uptimeMillis();\n// Returns nanoseconds since boot, not counting time spent in deep sleep.\nint64_t uptimeNanos();\n// Returns milliseconds since boot, including time spent in sleep.\nint64_t elapsedRealtime();\n// Returns nanoseconds since boot, including time spent in sleep.\nint64_t elapsedRealtimeNano();\n\n}  // namespace android\n\n#endif // ANDROID_UTILS_SYSTEMCLOCK_H\n\n"
  },
  {
    "path": "libutils/include/utils/Thread.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_UTILS_THREAD_H\n#define _LIBS_UTILS_THREAD_H\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <time.h>\n\n#if !defined(_WIN32)\n# include <pthread.h>\n#endif\n\n#include <utils/Condition.h>\n#include <utils/Errors.h>\n#include <utils/Mutex.h>\n#include <utils/RefBase.h>\n#include <utils/Timers.h>\n#include <utils/ThreadDefs.h>\n\n// ---------------------------------------------------------------------------\nnamespace android {\n// ---------------------------------------------------------------------------\n\n// DO NOT USE: please use std::thread\n\nclass Thread : virtual public RefBase\n{\npublic:\n    // Create a Thread object, but doesn't create or start the associated\n    // thread. See the run() method. This object must be used with RefBase/sp,\n    // like any other RefBase object, because they are conventionally promoted\n    // from bare pointers (Thread::run is particularly problematic here).\n    explicit            Thread(bool canCallJava = true);\n    virtual             ~Thread();\n\n    // Start the thread in threadLoop() which needs to be implemented.\n    // NOLINTNEXTLINE(google-default-arguments)\n    virtual status_t    run(    const char* name,\n                                int32_t priority = PRIORITY_DEFAULT,\n                                size_t stack = 0);\n    \n    // Ask this object's thread to exit. This function is asynchronous, when the\n    // function returns the thread might still be running. Of course, this\n    // function can be called from a different thread.\n    virtual void        requestExit();\n\n    // Good place to do one-time initializations\n    virtual status_t    readyToRun();\n    \n    // Call requestExit() and wait until this object's thread exits.\n    // BE VERY CAREFUL of deadlocks. In particular, it would be silly to call\n    // this function from this object's thread. Will return WOULD_BLOCK in\n    // that case.\n            status_t    requestExitAndWait();\n\n    // Wait until this object's thread exits. Returns immediately if not yet running.\n    // Do not call from this object's thread; will return WOULD_BLOCK in that case.\n            status_t    join();\n\n    // Indicates whether this thread is running or not.\n            bool        isRunning() const;\n\n#if defined(__ANDROID__)\n    // Return the thread's kernel ID, same as the thread itself calling gettid(),\n    // or -1 if the thread is not running.\n            pid_t       getTid() const;\n#endif\n\nprotected:\n    // exitPending() returns true if requestExit() has been called.\n            bool        exitPending() const;\n    \nprivate:\n    // Derived class must implement threadLoop(). The thread starts its life\n    // here. There are two ways of using the Thread object:\n    // 1) loop: if threadLoop() returns true, it will be called again if\n    //          requestExit() wasn't called.\n    // 2) once: if threadLoop() returns false, the thread will exit upon return.\n    virtual bool        threadLoop() = 0;\n\nprivate:\n    Thread& operator=(const Thread&);\n    static  int             _threadLoop(void* user);\n    const   bool            mCanCallJava;\n    // always hold mLock when reading or writing\n            thread_id_t     mThread;\n    mutable Mutex           mLock;\n            Condition       mThreadExitedCondition;\n            status_t        mStatus;\n    // note that all accesses of mExitPending and mRunning need to hold mLock\n    volatile bool           mExitPending;\n    volatile bool           mRunning;\n            sp<Thread>      mHoldSelf;\n#if defined(__ANDROID__)\n    // legacy for debugging, not used by getTid() as it is set by the child thread\n    // and so is not initialized until the child reaches that point\n            pid_t           mTid;\n#endif\n};\n\n}  // namespace android\n\n// ---------------------------------------------------------------------------\n#endif // _LIBS_UTILS_THREAD_H\n// ---------------------------------------------------------------------------\n"
  },
  {
    "path": "libutils/include/utils/ThreadDefs.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_UTILS_THREAD_DEFS_H\n#define _LIBS_UTILS_THREAD_DEFS_H\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <system/graphics.h>\n#include <system/thread_defs.h>\n\n// ---------------------------------------------------------------------------\n// C API\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef _WIN32\ntypedef uint32_t android_thread_id_t;\n#else\ntypedef void* android_thread_id_t;\n#endif\n\ntypedef int (*android_thread_func_t)(void*);\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n\n// ---------------------------------------------------------------------------\n// C++ API\n#ifdef __cplusplus\nnamespace android {\n// ---------------------------------------------------------------------------\n\ntypedef android_thread_id_t thread_id_t;\ntypedef android_thread_func_t thread_func_t;\n\nenum {\n    PRIORITY_LOWEST         = ANDROID_PRIORITY_LOWEST,\n    PRIORITY_BACKGROUND     = ANDROID_PRIORITY_BACKGROUND,\n    PRIORITY_NORMAL         = ANDROID_PRIORITY_NORMAL,\n    PRIORITY_FOREGROUND     = ANDROID_PRIORITY_FOREGROUND,\n    PRIORITY_DISPLAY        = ANDROID_PRIORITY_DISPLAY,\n    PRIORITY_URGENT_DISPLAY = ANDROID_PRIORITY_URGENT_DISPLAY,\n    PRIORITY_AUDIO          = ANDROID_PRIORITY_AUDIO,\n    PRIORITY_URGENT_AUDIO   = ANDROID_PRIORITY_URGENT_AUDIO,\n    PRIORITY_HIGHEST        = ANDROID_PRIORITY_HIGHEST,\n    PRIORITY_DEFAULT        = ANDROID_PRIORITY_DEFAULT,\n    PRIORITY_MORE_FAVORABLE = ANDROID_PRIORITY_MORE_FAVORABLE,\n    PRIORITY_LESS_FAVORABLE = ANDROID_PRIORITY_LESS_FAVORABLE,\n};\n\n// ---------------------------------------------------------------------------\n}  // namespace android\n#endif  // __cplusplus\n// ---------------------------------------------------------------------------\n\n#endif // _LIBS_UTILS_THREAD_DEFS_H\n"
  },
  {
    "path": "libutils/include/utils/Timers.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <sys/types.h>\n#include <sys/time.h>\n\n#include <utils/Compat.h>\n\n// ------------------------------------------------------------------\n// C API\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\ntypedef int64_t nsecs_t;       // nano-seconds\n\nstatic CONSTEXPR inline nsecs_t seconds_to_nanoseconds(nsecs_t secs)\n{\n    return secs*1000000000;\n}\n\nstatic CONSTEXPR inline nsecs_t milliseconds_to_nanoseconds(nsecs_t secs)\n{\n    return secs*1000000;\n}\n\nstatic CONSTEXPR inline nsecs_t microseconds_to_nanoseconds(nsecs_t secs)\n{\n    return secs*1000;\n}\n\nstatic CONSTEXPR inline nsecs_t nanoseconds_to_seconds(nsecs_t secs)\n{\n    return secs/1000000000;\n}\n\nstatic CONSTEXPR inline nsecs_t nanoseconds_to_milliseconds(nsecs_t secs)\n{\n    return secs/1000000;\n}\n\nstatic CONSTEXPR inline nsecs_t nanoseconds_to_microseconds(nsecs_t secs)\n{\n    return secs/1000;\n}\n\nstatic CONSTEXPR inline nsecs_t s2ns(nsecs_t v)  {return seconds_to_nanoseconds(v);}\nstatic CONSTEXPR inline nsecs_t ms2ns(nsecs_t v) {return milliseconds_to_nanoseconds(v);}\nstatic CONSTEXPR inline nsecs_t us2ns(nsecs_t v) {return microseconds_to_nanoseconds(v);}\nstatic CONSTEXPR inline nsecs_t ns2s(nsecs_t v)  {return nanoseconds_to_seconds(v);}\nstatic CONSTEXPR inline nsecs_t ns2ms(nsecs_t v) {return nanoseconds_to_milliseconds(v);}\nstatic CONSTEXPR inline nsecs_t ns2us(nsecs_t v) {return nanoseconds_to_microseconds(v);}\n\nstatic CONSTEXPR inline nsecs_t seconds(nsecs_t v)      { return s2ns(v); }\nstatic CONSTEXPR inline nsecs_t milliseconds(nsecs_t v) { return ms2ns(v); }\nstatic CONSTEXPR inline nsecs_t microseconds(nsecs_t v) { return us2ns(v); }\n\nenum {\n    SYSTEM_TIME_REALTIME = 0,   // system-wide realtime clock\n    SYSTEM_TIME_MONOTONIC = 1,  // monotonic time since unspecified starting point\n    SYSTEM_TIME_PROCESS = 2,    // high-resolution per-process clock\n    SYSTEM_TIME_THREAD = 3,     // high-resolution per-thread clock\n    SYSTEM_TIME_BOOTTIME = 4,   // same as SYSTEM_TIME_MONOTONIC, but including CPU suspend time\n};\n\n// return the system-time according to the specified clock\n#ifdef __cplusplus\nnsecs_t systemTime(int clock = SYSTEM_TIME_MONOTONIC);\n#else\nnsecs_t systemTime(int clock);\n#endif // def __cplusplus\n\n/**\n * Returns the number of milliseconds to wait between the reference time and the timeout time.\n * If the timeout is in the past relative to the reference time, returns 0.\n * If the timeout is more than INT_MAX milliseconds in the future relative to the reference time,\n * such as when timeoutTime == LLONG_MAX, returns -1 to indicate an infinite timeout delay.\n * Otherwise, returns the difference between the reference time and timeout time\n * rounded up to the next millisecond.\n */\nint toMillisecondTimeoutDelay(nsecs_t referenceTime, nsecs_t timeoutTime);\n\n#ifdef __cplusplus\n} // extern \"C\"\n#endif\n"
  },
  {
    "path": "libutils/include/utils/Tokenizer.h",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _UTILS_TOKENIZER_H\n#define _UTILS_TOKENIZER_H\n\n#include <assert.h>\n#include <utils/Errors.h>\n#include <utils/FileMap.h>\n#include <utils/String8.h>\n\nnamespace android {\n\n/**\n * A simple tokenizer for loading and parsing ASCII text files line by line.\n */\nclass Tokenizer {\n    Tokenizer(const String8& filename, FileMap* fileMap, char* buffer,\n            bool ownBuffer, size_t length);\n\npublic:\n    ~Tokenizer();\n\n    /**\n     * Opens a file and maps it into memory.\n     *\n     * Returns OK and a tokenizer for the file, if successful.\n     * Otherwise returns an error and sets outTokenizer to NULL.\n     */\n    static status_t open(const String8& filename, Tokenizer** outTokenizer);\n\n    /**\n     * Prepares to tokenize the contents of a string.\n     *\n     * Returns OK and a tokenizer for the string, if successful.\n     * Otherwise returns an error and sets outTokenizer to NULL.\n     */\n    static status_t fromContents(const String8& filename,\n            const char* contents, Tokenizer** outTokenizer);\n\n    /**\n     * Returns true if at the end of the file.\n     */\n    inline bool isEof() const { return mCurrent == getEnd(); }\n\n    /**\n     * Returns true if at the end of the line or end of the file.\n     */\n    inline bool isEol() const { return isEof() || *mCurrent == '\\n'; }\n\n    /**\n     * Gets the name of the file.\n     */\n    inline String8 getFilename() const { return mFilename; }\n\n    /**\n     * Gets a 1-based line number index for the current position.\n     */\n    inline int32_t getLineNumber() const { return mLineNumber; }\n\n    /**\n     * Formats a location string consisting of the filename and current line number.\n     * Returns a string like \"MyFile.txt:33\".\n     */\n    String8 getLocation() const;\n\n    /**\n     * Gets the character at the current position.\n     * Returns null at end of file.\n     */\n    inline char peekChar() const { return isEof() ? '\\0' : *mCurrent; }\n\n    /**\n     * Gets the remainder of the current line as a string, excluding the newline character.\n     */\n    String8 peekRemainderOfLine() const;\n\n    /**\n     * Gets the character at the current position and advances past it.\n     * Returns null at end of file.\n     */\n    inline char nextChar() { return isEof() ? '\\0' : *(mCurrent++); }\n\n    /**\n     * Gets the next token on this line stopping at the specified delimiters\n     * or the end of the line whichever comes first and advances past it.\n     * Also stops at embedded nulls.\n     * Returns the token or an empty string if the current character is a delimiter\n     * or is at the end of the line.\n     */\n    String8 nextToken(const char* delimiters);\n\n    /**\n     * Advances to the next line.\n     * Does nothing if already at the end of the file.\n     */\n    void nextLine();\n\n    /**\n     * Skips over the specified delimiters in the line.\n     * Also skips embedded nulls.\n     */\n    void skipDelimiters(const char* delimiters);\n\nprivate:\n    Tokenizer(const Tokenizer& other); // not copyable\n\n    String8 mFilename;\n    FileMap* mFileMap;\n    char* mBuffer;\n    bool mOwnBuffer;\n    size_t mLength;\n\n    const char* mCurrent;\n    int32_t mLineNumber;\n\n    inline const char* getEnd() const { return mBuffer + mLength; }\n\n};\n\n} // namespace android\n\n#endif // _UTILS_TOKENIZER_H\n"
  },
  {
    "path": "libutils/include/utils/Trace.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_TRACE_H\n#define ANDROID_TRACE_H\n\n#include <stdint.h>\n\n#include <cutils/trace.h>\n\n// See <cutils/trace.h> for more ATRACE_* macros.\n\n// ATRACE_NAME traces from its location until the end of its enclosing scope.\n#define _PASTE(x, y) x ## y\n#define PASTE(x, y) _PASTE(x,y)\n#define ATRACE_NAME(name) ::android::ScopedTrace PASTE(___tracer, __LINE__)(ATRACE_TAG, name)\n\n// ATRACE_CALL is an ATRACE_NAME that uses the current function name.\n#define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)\n\nnamespace android {\n\nclass ScopedTrace {\npublic:\n    inline ScopedTrace(uint64_t tag, const char* name) : mTag(tag) {\n        atrace_begin(mTag, name);\n    }\n\n    inline ~ScopedTrace() {\n        atrace_end(mTag);\n    }\n\nprivate:\n    uint64_t mTag;\n};\n\n}  // namespace android\n\n#endif // ANDROID_TRACE_H\n"
  },
  {
    "path": "libutils/include/utils/misc.h",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n//\n// Handy utility functions and portability code.\n//\n#ifndef _LIBS_UTILS_MISC_H\n#define _LIBS_UTILS_MISC_H\n\n#include <utils/Endian.h>\n\n/* get #of elements in a static array\n * DO NOT USE: please use std::vector/std::array instead\n */\n#ifndef NELEM\n# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))\n#endif\n\nnamespace android {\n\ntypedef void (*sysprop_change_callback)(void);\nvoid add_sysprop_change_callback(sysprop_change_callback cb, int priority);\nvoid report_sysprop_change();\n\n}  // namespace android\n\n#endif // _LIBS_UTILS_MISC_H\n"
  },
  {
    "path": "libutils/include/utils/threads.h",
    "content": "/*\n * Copyright (C) 2007 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIBS_UTILS_THREADS_H\n#define _LIBS_UTILS_THREADS_H\n\n/*\n * Please, DO NOT USE!\n *\n * This file is here only for legacy reasons. Instead, include directly\n * the headers you need below.\n *\n */\n\n#include <utils/AndroidThreads.h>\n\n#ifdef __cplusplus\n#include <utils/Condition.h>\n#include <utils/Errors.h>\n#include <utils/Mutex.h>\n#include <utils/RWLock.h>\n#include <utils/Thread.h>\n#endif\n\n#endif // _LIBS_UTILS_THREADS_H\n"
  },
  {
    "path": "libutils/misc.cpp",
    "content": "/*\n * Copyright (C) 2005 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"misc\"\n\n#include <utils/misc.h>\n\n#include <pthread.h>\n\n#include <log/log.h>\n#include <utils/Vector.h>\n\n#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)\n#include <dlfcn.h>\n#include <vndksupport/linker.h>\n#endif\n\nextern \"C\" void do_report_sysprop_change();\n\nusing namespace android;\n\nnamespace android {\n\nstruct sysprop_change_callback_info {\n    sysprop_change_callback callback;\n    int priority;\n};\n\n#if !defined(_WIN32)\nstatic pthread_mutex_t gSyspropMutex = PTHREAD_MUTEX_INITIALIZER;\nstatic Vector<sysprop_change_callback_info>* gSyspropList = nullptr;\n#endif\n\n#if !defined(_WIN32)\nvoid add_sysprop_change_callback(sysprop_change_callback cb, int priority) {\n    pthread_mutex_lock(&gSyspropMutex);\n    if (gSyspropList == nullptr) {\n        gSyspropList = new Vector<sysprop_change_callback_info>();\n    }\n    sysprop_change_callback_info info;\n    info.callback = cb;\n    info.priority = priority;\n    bool added = false;\n    for (size_t i=0; i<gSyspropList->size(); i++) {\n        if (priority >= gSyspropList->itemAt(i).priority) {\n            gSyspropList->insertAt(info, i);\n            added = true;\n            break;\n        }\n    }\n    if (!added) {\n        gSyspropList->add(info);\n    }\n    pthread_mutex_unlock(&gSyspropMutex);\n}\n#else\nvoid add_sysprop_change_callback(sysprop_change_callback, int) {}\n#endif\n\n#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)\nvoid (*get_report_sysprop_change_func())() {\n    void (*func)() = nullptr;\n    void* handle = android_load_sphal_library(\"libutils.so\", RTLD_NOW);\n    if (handle != nullptr) {\n        func = reinterpret_cast<decltype(func)>(dlsym(handle, \"do_report_sysprop_change\"));\n    }\n\n    return func;\n}\n#endif\n\nvoid report_sysprop_change() {\n    do_report_sysprop_change();\n\n#if defined(__ANDROID__) && !defined(__ANDROID_RECOVERY__)\n    // libutils.so is double loaded; from the default namespace and from the\n    // 'sphal' namespace. Redirect the sysprop change event to the other instance\n    // of libutils.so loaded in the 'sphal' namespace so that listeners attached\n    // to that instance is also notified with this event.\n    static auto func = get_report_sysprop_change_func();\n    if (func != nullptr) {\n        (*func)();\n    }\n#endif\n}\n\n};  // namespace android\n\nvoid do_report_sysprop_change() {\n#if !defined(_WIN32)\n    pthread_mutex_lock(&gSyspropMutex);\n    Vector<sysprop_change_callback_info> listeners;\n    if (gSyspropList != nullptr) {\n        listeners = *gSyspropList;\n    }\n    pthread_mutex_unlock(&gSyspropMutex);\n\n    //ALOGI(\"Reporting sysprop change to %d listeners\", listeners.size());\n    for (size_t i=0; i<listeners.size(); i++) {\n        listeners[i].callback();\n    }\n#endif\n}\n"
  },
  {
    "path": "libvendorsupport/Android.bp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libvendorsupport\",\n    native_bridge_supported: true,\n    recovery_available: true,\n    llndk: {\n        symbol_file: \"libvendorsupport.map.txt\",\n    },\n    srcs: [\"version_props.cpp\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    local_include_dirs: [\"include/vendorsupport\"],\n    export_include_dirs: [\"include\"],\n    shared_libs: [\n        \"liblog\",\n        \"libbase\",\n    ],\n}\n"
  },
  {
    "path": "libvendorsupport/OWNERS",
    "content": "jiyong@google.com\njustinyun@google.com\n"
  },
  {
    "path": "libvendorsupport/TEST_MAPPING",
    "content": "{\n  \"postsubmit\": [\n    {\n      \"name\": \"libvendorsupport-tests\"\n    }\n  ]\n}\n"
  },
  {
    "path": "libvendorsupport/include/vendorsupport/api_level.h",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#pragma once\n\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\n#define __ANDROID_VENDOR_API_MAX__ 1000000\n#define __INVALID_API_LEVEL -1\n\n/**\n * @brief Find corresponding vendor API level from an SDK API version.\n *\n * @details\n * SDK API versions and vendor API levels are not compatible and not\n * exchangeable. However, this function can be used to compare the two versions\n * to know which one is newer than the other.\n *\n * @param sdkApiLevel The SDK version int. This must be less than 10000.\n * @return The corresponding vendor API level of the SDK version. -1 if the SDK\n * version is invalid or 10000.\n */\nint AVendorSupport_getVendorApiLevelOf(int sdkApiLevel);\n\n/**\n * @brief Find corresponding SDK API version from a vendor API level.\n *\n * @param vendorApiLevel The vendor API level int.\n * @return The corresponding SDK API version of the vendor API level. -1 if the\n * vendor API level is invalid.\n */\nint AVendorSupport_getSdkApiLevelOf(int vendorApiLevel);\n\n#if !defined(__ANDROID_VENDOR__)\n/**\n * @brief Provide vendor API level to system modules.\n *\n * @details\n * Before deprecating VNDK, system modules read ro.vndk.version to find the\n * API level that vendor image had implemented. With the VNDK deprecation, this\n * must be replaced with ro.board.api_level. However, there still are devices\n * keeping old vendor partitions with the new system upgraded. In this case, the\n * VNDK version can be used as before.\n * This API is for platform only.\n *\n * @return ro.vndk.version if exist. Otherwise fallback to ro.board.api_level.\n * 0 if none of these properties are found. This is unexpected, though.\n */\nint AVendorSupport_getVendorApiLevel();\n#endif  // __ANDROID_VENDOR__\n\n__END_DECLS\n"
  },
  {
    "path": "libvendorsupport/libvendorsupport.map.txt",
    "content": "LIBVENDORSUPPORT {\n  global:\n    AVendorSupport_getVendorApiLevelOf; # llndk systemapi\n    AVendorSupport_getSdkApiLevelOf; # llndk systemapi\n  local:\n    *;\n};\n"
  },
  {
    "path": "libvendorsupport/tests/Android.bp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test {\n    name: \"libvendorsupport-tests\",\n    srcs: [\n        \"version_props_test.cpp\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libvendorsupport\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n\n"
  },
  {
    "path": "libvendorsupport/tests/version_props_test.cpp",
    "content": "/*\n * Copyright (C) 2024 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <gtest/gtest.h>\n\n#include <vendorsupport/api_level.h>\n\nusing namespace std;\n\nnamespace {\n\nTEST(VendorSupport, GetCorrespondingVendorApiLevel) {\n    ASSERT_EQ(__ANDROID_API_U__, AVendorSupport_getVendorApiLevelOf(__ANDROID_API_U__));\n    ASSERT_EQ(202404, AVendorSupport_getVendorApiLevelOf(__ANDROID_API_V__));\n    ASSERT_EQ(__INVALID_API_LEVEL, AVendorSupport_getVendorApiLevelOf(__ANDROID_API_FUTURE__));\n}\n\nTEST(VendorSupport, GetCorrespondingSdkApiLevel) {\n    ASSERT_EQ(__ANDROID_API_U__, AVendorSupport_getSdkApiLevelOf(__ANDROID_API_U__));\n    ASSERT_EQ(__ANDROID_API_V__, AVendorSupport_getSdkApiLevelOf(202404));\n    ASSERT_EQ(__INVALID_API_LEVEL, AVendorSupport_getSdkApiLevelOf(__ANDROID_VENDOR_API_MAX__));\n    ASSERT_EQ(__INVALID_API_LEVEL, AVendorSupport_getSdkApiLevelOf(35));\n}\n\n}  // namespace"
  },
  {
    "path": "libvendorsupport/version_props.cpp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#include \"api_level.h\"\n\n#include <log/log.h>\n\n#if !defined(__ANDROID_VENDOR__)\n#include <android-base/properties.h>\n#endif\n\nint AVendorSupport_getVendorApiLevelOf(int sdkApiLevel) {\n    if (sdkApiLevel < __ANDROID_API_V__) {\n        return sdkApiLevel;\n    }\n    // In Android V, vendor API level started with version 202404.\n    // The calculation assumes that the SDK api level bumps once a year.\n    if (sdkApiLevel < __ANDROID_API_FUTURE__) {\n        return 202404 + ((sdkApiLevel - __ANDROID_API_V__) * 100);\n    }\n    ALOGE(\"The SDK version must be less than 10000: %d\", sdkApiLevel);\n    return __INVALID_API_LEVEL;\n}\n\nint AVendorSupport_getSdkApiLevelOf(int vendorApiLevel) {\n    if (vendorApiLevel < __ANDROID_API_V__) {\n        return vendorApiLevel;\n    }\n    if (vendorApiLevel >= 202404 && vendorApiLevel < __ANDROID_VENDOR_API_MAX__) {\n        return (vendorApiLevel - 202404) / 100 + __ANDROID_API_V__;\n    }\n    ALOGE(\"Unexpected vendor api level: %d\", vendorApiLevel);\n    return __INVALID_API_LEVEL;\n}\n\n#if !defined(__ANDROID_VENDOR__)\nint AVendorSupport_getVendorApiLevel() {\n    int vendorApiLevel = android::base::GetIntProperty(\"ro.vndk.version\", 0);\n    if (vendorApiLevel) {\n        return vendorApiLevel;\n    }\n    return android::base::GetIntProperty(\"ro.board.api_level\", 0);\n}\n#endif  // __ANDROID_VENDOR__\n"
  },
  {
    "path": "libvndksupport/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libvndksupport\",\n    native_bridge_supported: true,\n    llndk: {\n        symbol_file: \"libvndksupport.map.txt\",\n    },\n    srcs: [\"linker.cpp\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    local_include_dirs: [\"include/vndksupport\"],\n    export_include_dirs: [\"include\"],\n    shared_libs: [\n        \"libdl_android\",\n        \"liblog\",\n    ],\n    version_script: \"libvndksupport.map.txt\",\n    stubs: {\n        symbol_file: \"libvndksupport.map.txt\",\n        versions: [\"29\"],\n    },\n}\n"
  },
  {
    "path": "libvndksupport/OWNERS",
    "content": "jiyong@google.com\nsmoreland@google.com\n"
  },
  {
    "path": "libvndksupport/TEST_MAPPING",
    "content": "{\n  \"postsubmit\": [\n    {\n      \"name\": \"libvndksupport-tests\"\n    }\n  ]\n}\n"
  },
  {
    "path": "libvndksupport/include/vndksupport/linker.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef VNDKSUPPORT_LINKER_H_\n#define VNDKSUPPORT_LINKER_H_\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint android_is_in_vendor_process() __attribute__((\n        deprecated(\"This function would not give exact result if VNDK is deprecated.\")));\n\nvoid* android_load_sphal_library(const char* name, int flag);\n\nint android_unload_sphal_library(void* handle);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif  // VNDKSUPPORT_LINKER_H_\n"
  },
  {
    "path": "libvndksupport/libvndksupport.map.txt",
    "content": "LIBVNDKSUPPORT {\n  global:\n    android_is_in_vendor_process; # llndk-deprecated=35 systemapi\n    android_load_sphal_library; # llndk systemapi\n    android_unload_sphal_library; # llndk systemapi\n  local:\n    *;\n};\n"
  },
  {
    "path": "libvndksupport/linker.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"vndksupport\"\n\n#include \"linker.h\"\n\n#include <android/dlext.h>\n#include <dlfcn.h>\n#include <log/log.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <initializer_list>\n\nextern \"C\" android_namespace_t* android_get_exported_namespace(const char*);\n\nnamespace {\n\nstruct VendorNamespace {\n    android_namespace_t* ptr = nullptr;\n    const char* name = nullptr;\n};\n\n}  // anonymous namespace\n\nstatic VendorNamespace get_vendor_namespace() {\n    static VendorNamespace result = ([] {\n        for (const char* name : {\"sphal\", \"vendor\", \"default\"}) {\n            if (android_namespace_t* ns = android_get_exported_namespace(name)) {\n                return VendorNamespace{ns, name};\n            }\n        }\n        return VendorNamespace{};\n    })();\n    return result;\n}\n\nint android_is_in_vendor_process() {\n    // Special case init, since when init runs, ld.config.<ver>.txt hasn't been\n    // loaded (sysprop service isn't up for init to know <ver>).\n    if (getpid() == 1) {\n        return 0;\n    }\n\n    // In vendor process, 'vndk' namespace is not visible, whereas in system\n    // process, it is.\n    return android_get_exported_namespace(\"vndk\") == nullptr;\n}\n\nvoid* android_load_sphal_library(const char* name, int flag) {\n    VendorNamespace vendor_namespace = get_vendor_namespace();\n    if (vendor_namespace.ptr != nullptr) {\n        const android_dlextinfo dlextinfo = {\n                .flags = ANDROID_DLEXT_USE_NAMESPACE,\n                .library_namespace = vendor_namespace.ptr,\n        };\n        void* handle = android_dlopen_ext(name, flag, &dlextinfo);\n        if (!handle) {\n            ALOGE(\"Could not load %s from %s namespace: %s.\", name, vendor_namespace.name,\n                  dlerror());\n        }\n        return handle;\n    } else {\n        ALOGW(\"Loading %s from current namespace instead of sphal namespace.\", name);\n        return dlopen(name, flag);\n    }\n}\n\nint android_unload_sphal_library(void* handle) {\n    return dlclose(handle);\n}\n"
  },
  {
    "path": "libvndksupport/tests/Android.bp",
    "content": "// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test {\n    name: \"libvndksupport-tests\",\n    srcs: [\n        \"linker_test.cpp\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    host_supported: false,\n    shared_libs: [\n        \"libvndksupport\",\n        \"libbase\",\n    ],\n\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "libvndksupport/tests/linker_test.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <gtest/gtest.h>\n\n#include <android-base/strings.h>\n#include <dirent.h>\n#include <dlfcn.h>\n#include <vndksupport/linker.h>\n#include <string>\n\n// Let's use libEGL_<chipset>.so as a SP-HAL in test\nstatic std::string find_sphal_lib() {\n    const char* path =\n#if defined(__LP64__)\n        \"/vendor/lib64/egl\";\n#else\n        \"/vendor/lib/egl\";\n#endif\n    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(path), closedir);\n\n    dirent* dp;\n    while ((dp = readdir(dir.get())) != nullptr) {\n        std::string name = dp->d_name;\n        if (android::base::StartsWith(name, \"libEGL_\")) {\n            return std::string(path) + \"/\" + name;\n        }\n    }\n    return \"\";\n}\n\nTEST(linker, load_existing_lib) {\n    std::string name = find_sphal_lib();\n    ASSERT_NE(\"\", name);\n    void* handle = android_load_sphal_library(name.c_str(), RTLD_NOW | RTLD_LOCAL);\n    ASSERT_NE(nullptr, handle);\n    android_unload_sphal_library(handle);\n}\n\nTEST(linker, load_nonexisting_lib) {\n    void* handle = android_load_sphal_library(\"libNeverUseThisName.so\", RTLD_NOW | RTLD_LOCAL);\n    ASSERT_EQ(nullptr, handle);\n}\n"
  },
  {
    "path": "llkd/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_headers {\n    name: \"llkd_headers\",\n\n    export_include_dirs: [\"include\"],\n}\n\ncc_library_static {\n    name: \"libllkd\",\n\n    srcs: [\n        \"libllkd.cpp\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"liblog\",\n    ],\n\n    export_include_dirs: [\"include\"],\n\n    cflags: [\"-Werror\"],\n\n    product_variables: {\n        debuggable: {\n            cppflags: [\"-D__PTRACE_ENABLED__\"],\n        },\n    },\n}\n\ncc_binary {\n    name: \"llkd\",\n\n    srcs: [\n        \"llkd.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libllkd\",\n    ],\n    cflags: [\"-Werror\"],\n\n    init_rc: [\"llkd.rc\"],\n    product_variables: {\n        debuggable: {\n            init_rc: [\"llkd-debuggable.rc\"],\n\t},\n    },\n}\n"
  },
  {
    "path": "llkd/OWNERS",
    "content": "surenb@google.com\n"
  },
  {
    "path": "llkd/README.md",
    "content": "<!--\nProject: /_project.yaml\nBook: /_book.yaml\n\n{% include \"_versions.html\" %}\n-->\n\n<!--\n  Copyright 2020 The Android Open Source Project\n\n  Licensed under the Apache License, Version 2.0 (the \"License\");\n  you may not use this file except in compliance with the License.\n  You may obtain a copy of the License at\n\n      http://www.apache.org/licenses/LICENSE-2.0\n\n  Unless required by applicable law or agreed to in writing, software\n  distributed under the License is distributed on an \"AS IS\" BASIS,\n  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  See the License for the specific language governing permissions and\n  limitations under the License.\n-->\n\n# Android Live-LocK Daemon (llkd)\n\nAndroid 10 <!-- {{ androidQVersionNumber }} --> includes the Android Live-LocK Daemon\n(`llkd`), which is designed to catch and mitigate kernel deadlocks. The `llkd`\ncomponent provides a default standalone implementation, but you can\nalternatively integrate the `llkd` code into another service, either as part of\nthe main loop or as a separate thread.\n\n## Detection scenarios <!-- {:#detection-scenarios} -->\n\nThe `llkd` has two detection scenarios: Persistent D or Z state, and persistent\nstack signature.\n\n### Persistent D or Z state <!-- {:#persistent-d-or-z-state} -->\n\nIf a thread is in D (uninterruptible sleep) or Z (zombie) state with no forward\nprogress for longer than `ro.llk.timeout_ms or ro.llk.[D|Z].timeout_ms`, the\n`llkd` kills the process (or parent process). If a subsequent scan shows the\nsame process continues to exist, the `llkd` confirms a live-lock condition and\npanics the kernel in a manner that provides the most detailed bug report for the\ncondition.\n\nThe `llkd` includes a self watchdog that alarms if `llkd` locks up; watchdog is\ndouble the expected time to flow through the mainloop and sampling is every\n`ro.llk_sample_ms`.\n\n### Persistent stack signature <!-- {:#persistent-stack-signature} -->\n\nFor userdebug releases, the `llkd` can detect kernel live-locks using persistent\nstack signature checking. If a thread in any state except Z has a persistent\nlisted `ro.llk.stack` kernel symbol that is reported for longer than\n`ro.llk.timeout_ms` or `ro.llk.stack.timeout_ms`, the `llkd` kills the process\n(even if there is forward scheduling progress). If a subsequent scan shows the\nsame process continues to exist, the `llkd` confirms a live-lock condition and\npanics the kernel in a manner that provides the most detailed bug report for the\ncondition.\n\nNote: Because forward scheduling progress is allowed, the `llkd` does not\nperform [ABA detection](https://en.wikipedia.org/wiki/ABA_problem){:.external}.\n\nThe `lldk` check persists continuously when the live lock condition exists and\nlooks for the composed strings `\" symbol+0x\"` or `\" symbol.cfi+0x\"` in the\n`/proc/pid/stack` file on Linux. The list of symbols is in `ro.llk.stack` and\ndefaults to the comma-separated list of\n\"`cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable`\".\n\nSymbols should be rare and short-lived enough that on a typical system the\nfunction is seen only once in a sample over the timeout period of\n`ro.llk.stack.timeout_ms` (samples occur every `ro.llk.check_ms`). Due to lack\nof ABA protection, this is the only way to prevent a false trigger. The symbol\nfunction must appear below the function calling the lock that could contend. If\nthe lock is below or in the symbol function, the symbol appears in all affected\nprocesses, not just the one that caused the lockup.\n\n## Coverage <!-- {:#coverage} -->\n\nThe default implementation of `llkd` does not monitor `init`, `[kthreadd]`, or\n`[kthreadd]` spawns. For the `llkd` to cover `[kthreadd]`-spawned threads:\n\n* Drivers must not remain in a persistent D state,\n\nOR\n\n* Drivers must have mechanisms to recover the thread should it be killed\n  externally. For example, use `wait_event_interruptible()` instead of\n  `wait_event()`.\n\nIf one of the above conditions is met, the `llkd` ignorelist can be adjusted to\ncover kernel components.  Stack symbol checking involves an additional process\nignore list to prevent sepolicy violations on services that block `ptrace`\noperations.\n\n## Android properties <!-- {:#android-properties} -->\n\nThe `llkd` responds to several Android properties (listed below).\n\n* Properties named `prop_ms` are in milliseconds.\n* Properties that use comma (,) separator for lists use a leading separator to\n  preserve the default entry, then add or subtract entries with optional plus\n  (+) and minus (-) prefixes respectively. For these lists, the string \"false\"\n  is synonymous with an empty list, and blank or missing entries resort to the\n  specified default value.\n\n### ro.config.low_ram <!-- {:#ro-config-low-ram} -->\n\nDevice is configured with limited memory.\n\n### ro.debuggable <!-- {:#ro-debuggable} -->\n\nDevice is configured for userdebug or eng build.\n\n### ro.llk.sysrq_t <!-- {:#ro-llk-sysrq-t} -->\n\nIf property is \"eng\", the default is not `ro.config.low_ram` or `ro.debuggable`.\nIf true, dump all threads (`sysrq t`).\n\n### ro.llk.enable <!-- {:#ro-llk-enable} -->\n\nAllow live-lock daemon to be enabled. Default is false.\n\n### llk.enable <!-- {:#llk-enable} -->\n\nEvaluated for eng builds. Default is `ro.llk.enable`.\n\n### ro.khungtask.enable <!-- {:#ro-khungtask-enable} -->\n\nAllow `[khungtask]` daemon to be enabled. Default is false.\n\n### khungtask.enable <!-- {:#khungtask-enable} -->\n\nEvaluated for eng builds. Default is `ro.khungtask.enable`.\n\n### ro.llk.mlockall <!-- {:#ro-llk-mlockall} -->\n\nEnable call to `mlockall()`. Default is false.\n\n### ro.khungtask.timeout <!-- {:#ro-khungtask-timeout} -->\n\n`[khungtask]` maximum time limit. Default is 12 minutes.\n\n### ro.llk.timeout_ms <!-- {:#ro-llk-timeout-ms} -->\n\nD or Z maximum time limit. Default is 10 minutes. Double this value to set the\nalarm watchdog for `llkd`.\n\n### ro.llk.D.timeout_ms <!-- {:#ro-llk-D-timeout-ms} -->\n\nD maximum time limit. Default is `ro.llk.timeout_ms`.\n\n### ro.llk.Z.timeout_ms <!-- {:#ro-llk-Z-timeout-ms} -->\n\nZ maximum time limit. Default is `ro.llk.timeout_ms`.\n\n### ro.llk.stack.timeout_ms <!-- {:#ro-llk-stack-timeout-ms} -->\n\nChecks for persistent stack symbols maximum time limit. Default is\n`ro.llk.timeout_ms`. **Active only on userdebug or eng builds**.\n\n### ro.llk.check_ms <!-- {:#ro-llk-check-ms} -->\n\nSamples of threads for D or Z. Default is two minutes.\n\n### ro.llk.stack <!-- {:#ro-llk-stack} -->\n\nChecks for kernel stack symbols that if persistently present can indicate a\nsubsystem is locked up. Default is\n`cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable`\ncomma-separated list of kernel symbols. The check doesn't do forward scheduling\nABA except by polling every `ro.llk_check_ms` over the period\n`ro.llk.stack.timeout_ms`, so stack symbols should be exceptionally rare and\nfleeting (it is highly unlikely for a symbol to show up persistently in all\nsamples of the stack). Checks for a match for `\" symbol+0x\"` or\n`\" symbol.cfi+0x\"` in stack expansion. **Available only on userdebug or eng\nbuilds**; security concerns on user builds result in limited privileges that\nprevent this check.\n\n### ro.llk.ignorelist.process <!-- {:#ro-llk-ignorelist-process} -->\n\nThe `llkd` does not watch the specified processes. Default is `0,1,2` (`kernel`,\n`init`, and `[kthreadd]`) plus process names\n`init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd, [watchdogd],[watchdogd/0],...,[watchdogd/get_nprocs-1]`.\nA process can be a `comm`, `cmdline`, or `pid` reference. An automated default\ncan be larger than the current maximum property size of 92.\n\nNote: `false` is an extremely unlikely process to want to ignore.\n\n### ro.llk.ignorelist.parent <!-- {:#ro-llk-ignorelist-parent} -->\n\nThe `llkd` does not watch processes that have the specified parent(s). Default\nis `0,2,adbd&[setsid]` (`kernel`, `[kthreadd]`, and `adbd` only for zombie\n`setsid`). An ampersand (&) separator specifies that the parent is ignored only\nin combination with the target child process. Ampersand was selected because it\nis never part of a process name; however, a `setprop` in the shell requires the\nampersand to be escaped or quoted, although the `init rc` file where this is\nnormally specified does not have this issue. A parent or target process can be a\n`comm`, `cmdline`, or `pid` reference.\n\n### ro.llk.ignorelist.uid <!-- {:#ro-llk-ignorelist-uid} -->\n\nThe `llkd` does not watch processes that match the specified uid(s).\nComma-separated list of uid numbers or names. Default is empty or false.\n\n### ro.llk.ignorelist.process.stack <!-- {:#ro-llk-ignorelist-process-stack} -->\n\nThe `llkd` does not monitor the specified subset of processes for live lock stack\nsignatures. Default is process names\n`init,lmkd.llkd,llkd,keystore,keystore2,ueventd,apexd,logd`. Prevents the sepolicy\nviolation associated with processes that block `ptrace` (as these can't be\nchecked). **Active only on userdebug and eng builds**. For details on build\ntypes, refer to [Building Android](/setup/build/building#choose-a-target).\n\n## Architectural concerns <!-- {:#architectural-concerns} -->\n\n* Properties are limited to 92 characters.  However, this is not limited for\n  defaults defined in the `include/llkd.h` file in the sources.\n* The built-in `[khungtask]` daemon is too generic and trips on driver code that\n  sits around in D state too much. Switching drivers to sleep, or S state,\n  would make task(s) killable, and need to be resurrectable by drivers on an\n  as-need basis.\n\n## Library interface (optional) <!-- {:#library-interface-optional} -->\n\nYou can optionally incorporate the `llkd` into another privileged daemon using\nthe following C interface from the `libllkd` component:\n\n```\n#include \"llkd.h\"\nbool llkInit(const char* threadname) /* return true if enabled */\nunsigned llkCheckMillseconds(void)   /* ms to sleep for next check */\n```\n\nIf a threadname is provided, a thread automatically spawns, otherwise the caller\nmust call `llkCheckMilliseconds` in its main loop. The function returns the\nperiod of time before the next expected call to this handler.\n"
  },
  {
    "path": "llkd/include/llkd.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LLKD_H_\n#define _LLKD_H_\n\n#ifndef LOG_TAG\n#define LOG_TAG \"livelock\"\n#endif\n\n#include <stdbool.h>\n#include <sys/cdefs.h>\n\n__BEGIN_DECLS\n\nbool llkInit(const char* threadname); /* threadname NULL, not spawned */\nunsigned llkCheckMilliseconds(void);\n\n/* clang-format off */\n#define LLK_ENABLE_WRITEABLE_PROPERTY   \"llk.enable\"\n#define LLK_ENABLE_PROPERTY             \"ro.\" LLK_ENABLE_WRITEABLE_PROPERTY\n#define LLK_ENABLE_DEFAULT              false /* \"eng\" and userdebug true */\n#define KHT_ENABLE_WRITEABLE_PROPERTY   \"khungtask.enable\"\n#define KHT_ENABLE_PROPERTY             \"ro.\" KHT_ENABLE_WRITEABLE_PROPERTY\n#define LLK_ENABLE_SYSRQ_T_PROPERTY     \"ro.llk.sysrq_t\"\n#define LLK_ENABLE_SYSRQ_T_DEFAULT      true\n#define LLK_MLOCKALL_PROPERTY           \"ro.llk.mlockall\"\n#define LLK_MLOCKALL_DEFAULT            true\n#define LLK_KILLTEST_PROPERTY           \"ro.llk.killtest\"\n#define LLK_KILLTEST_DEFAULT            true\n#define LLK_TIMEOUT_MS_PROPERTY         \"ro.llk.timeout_ms\"\n#define KHT_TIMEOUT_PROPERTY            \"ro.khungtask.timeout\"\n#define LLK_D_TIMEOUT_MS_PROPERTY       \"ro.llk.D.timeout_ms\"\n#define LLK_Z_TIMEOUT_MS_PROPERTY       \"ro.llk.Z.timeout_ms\"\n#define LLK_STACK_TIMEOUT_MS_PROPERTY   \"ro.llk.stack.timeout_ms\"\n#define LLK_CHECK_MS_PROPERTY           \"ro.llk.check_ms\"\n/* LLK_CHECK_MS_DEFAULT = actual timeout_ms / LLK_CHECKS_PER_TIMEOUT_DEFAULT */\n#define LLK_CHECKS_PER_TIMEOUT_DEFAULT  5\n#define LLK_CHECK_STACK_PROPERTY        \"ro.llk.stack\"\n#define LLK_CHECK_STACK_DEFAULT         \\\n    \"cma_alloc,__get_user_pages,bit_wait_io,wait_on_page_bit_killable\"\n#define LLK_IGNORELIST_PROCESS_PROPERTY \"ro.llk.ignorelist.process\"\n#define LLK_IGNORELIST_PROCESS_DEFAULT  \\\n    \"0,1,2,init,[kthreadd],[khungtaskd],lmkd,llkd,watchdogd,[watchdogd],[watchdogd/0]\"\n#define LLK_IGNORELIST_PARENT_PROPERTY  \"ro.llk.ignorelist.parent\"\n#define LLK_IGNORELIST_PARENT_DEFAULT   \"0,2,[kthreadd],adbd&[setsid]\"\n#define LLK_IGNORELIST_UID_PROPERTY     \"ro.llk.ignorelist.uid\"\n#define LLK_IGNORELIST_UID_DEFAULT      \"\"\n#define LLK_IGNORELIST_STACK_PROPERTY   \"ro.llk.ignorelist.process.stack\"\n#define LLK_IGNORELIST_STACK_DEFAULT    \"init,lmkd.llkd,llkd,keystore,keystore2,ueventd,apexd\"\n/* clang-format on */\n\n__END_DECLS\n\n#ifdef __cplusplus\nextern \"C++\" { /* In case this included wrapped with __BEGIN_DECLS */\n\n#include <chrono>\n\n__BEGIN_DECLS\n/* C++ code allowed to not specify threadname argument for this C linkage */\nbool llkInit(const char* threadname = nullptr);\n__END_DECLS\nstd::chrono::milliseconds llkCheck(bool checkRunning = false);\n\n/* clang-format off */\n#define LLK_TIMEOUT_MS_DEFAULT  std::chrono::duration_cast<milliseconds>(std::chrono::minutes(10))\n#define LLK_TIMEOUT_MS_MINIMUM  std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(10))\n#define LLK_CHECK_MS_MINIMUM    std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(1))\n/* clang-format on */\n\n} /* extern \"C++\" */\n#endif /* __cplusplus */\n\n#endif /* _LLKD_H_ */\n"
  },
  {
    "path": "llkd/libllkd.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"llkd.h\"\n\n#include <ctype.h>\n#include <dirent.h>  // opendir() and readdir()\n#include <errno.h>\n#include <fcntl.h>\n#include <pthread.h>\n#include <pwd.h>  // getpwuid()\n#include <signal.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/cdefs.h>  // ___STRING, __predict_true() and _predict_false()\n#include <sys/mman.h>   // mlockall()\n#include <sys/prctl.h>\n#include <sys/stat.h>     // lstat()\n#include <sys/syscall.h>  // __NR_getdents64\n#include <sys/sysinfo.h>  // get_nprocs_conf()\n#include <sys/types.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <ios>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <cutils/android_get_control_file.h>\n#include <log/log_main.h>\n\n#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))\n\n#define TASK_COMM_LEN 16  // internal kernel, not uapi, from .../linux/include/linux/sched.h\n\nusing namespace std::chrono_literals;\nusing namespace std::chrono;\nusing namespace std::literals;\n\nnamespace {\n\nconstexpr pid_t kernelPid = 0;\nconstexpr pid_t initPid = 1;\nconstexpr pid_t kthreaddPid = 2;\n\nconstexpr char procdir[] = \"/proc/\";\n\n// Configuration\nmilliseconds llkUpdate;                              // last check ms signature\nmilliseconds llkCycle;                               // ms to next thread check\nbool llkEnable = LLK_ENABLE_DEFAULT;                 // llk daemon enabled\nbool llkRunning = false;                             // thread is running\nbool llkMlockall = LLK_MLOCKALL_DEFAULT;             // run mlocked\nbool llkTestWithKill = LLK_KILLTEST_DEFAULT;         // issue test kills\nmilliseconds llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;  // default timeout\nenum {                                               // enum of state indexes\n    llkStateD,                                       // Persistent 'D' state\n    llkStateZ,                                       // Persistent 'Z' state\n#ifdef __PTRACE_ENABLED__                            // Extra privileged states\n    llkStateStack,                                   // stack signature\n#endif                                               // End of extra privilege\n    llkNumStates,                                    // Maxumum number of states\n};                                                   // state indexes\nmilliseconds llkStateTimeoutMs[llkNumStates];        // timeout override for each detection state\nmilliseconds llkCheckMs;                             // checking interval to inspect any\n                                                     // persistent live-locked states\nbool llkLowRam;                                      // ro.config.low_ram\nbool llkEnableSysrqT = LLK_ENABLE_SYSRQ_T_DEFAULT;   // sysrq stack trace dump\nbool khtEnable = LLK_ENABLE_DEFAULT;                 // [khungtaskd] panic\n// [khungtaskd] should have a timeout beyond the granularity of llkTimeoutMs.\n// Provides a wide angle of margin b/c khtTimeout is also its granularity.\nseconds khtTimeout = duration_cast<seconds>(llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) /\n                                            LLK_CHECKS_PER_TIMEOUT_DEFAULT);\n#ifdef __PTRACE_ENABLED__\n// list of stack symbols to search for persistence.\nstd::unordered_set<std::string> llkCheckStackSymbols;\n#endif\n\n// Ignorelist variables, initialized with comma separated lists of high false\n// positive and/or dangerous references, e.g. without self restart, for pid,\n// ppid, name and uid:\n\n// list of pids, or tids or names to skip. kernel pid (0), init pid (1),\n// [kthreadd] pid (2), ourselves, \"init\", \"[kthreadd]\", \"lmkd\", \"llkd\" or\n// combinations of watchdogd in kernel and user space.\nstd::unordered_set<std::string> llkIgnorelistProcess;\n// list of parent pids, comm or cmdline names to skip. default:\n// kernel pid (0), [kthreadd] (2), or ourselves, enforced and implied\nstd::unordered_set<std::string> llkIgnorelistParent;\n// list of parent and target processes to skip. default:\n// adbd *and* [setsid]\nstd::unordered_map<std::string, std::unordered_set<std::string>> llkIgnorelistParentAndChild;\n// list of uids, and uid names, to skip, default nothing\nstd::unordered_set<std::string> llkIgnorelistUid;\n#ifdef __PTRACE_ENABLED__\n// list of names to skip stack checking. \"init\", \"lmkd\", \"llkd\", \"keystore\",\n// \"keystore2\", or \"logd\" (if not userdebug).\nstd::unordered_set<std::string> llkIgnorelistStack;\n#endif\n\nclass dir {\n  public:\n    enum level { proc, task, numLevels };\n\n  private:\n    int fd;\n    size_t available_bytes;\n    dirent* next;\n    // each directory level picked to be just north of 4K in size\n    static constexpr size_t buffEntries = 15;\n    static dirent buff[numLevels][buffEntries];\n\n    bool fill(enum level index) {\n        if (index >= numLevels) return false;\n        if (available_bytes != 0) return true;\n        if (__predict_false(fd < 0)) return false;\n        // getdents64 has no libc wrapper\n        auto rc = TEMP_FAILURE_RETRY(syscall(__NR_getdents64, fd, buff[index], sizeof(buff[0]), 0));\n        if (rc <= 0) return false;\n        available_bytes = rc;\n        next = buff[index];\n        return true;\n    }\n\n  public:\n    dir() : fd(-1), available_bytes(0), next(nullptr) {}\n\n    explicit dir(const char* directory)\n        : fd(__predict_true(directory != nullptr)\n                 ? ::open(directory, O_CLOEXEC | O_DIRECTORY | O_RDONLY)\n                 : -1),\n          available_bytes(0),\n          next(nullptr) {}\n\n    explicit dir(const std::string&& directory)\n        : fd(::open(directory.c_str(), O_CLOEXEC | O_DIRECTORY | O_RDONLY)),\n          available_bytes(0),\n          next(nullptr) {}\n\n    explicit dir(const std::string& directory)\n        : fd(::open(directory.c_str(), O_CLOEXEC | O_DIRECTORY | O_RDONLY)),\n          available_bytes(0),\n          next(nullptr) {}\n\n    // Don't need any copy or move constructors.\n    explicit dir(const dir& c) = delete;\n    explicit dir(dir& c) = delete;\n    explicit dir(dir&& c) = delete;\n\n    ~dir() {\n        if (fd >= 0) {\n            ::close(fd);\n        }\n    }\n\n    operator bool() const { return fd >= 0; }\n\n    void reset(void) {\n        if (fd >= 0) {\n            ::close(fd);\n            fd = -1;\n            available_bytes = 0;\n            next = nullptr;\n        }\n    }\n\n    dir& reset(const char* directory) {\n        reset();\n        // available_bytes will _always_ be zero here as its value is\n        // intimately tied to fd < 0 or not.\n        fd = ::open(directory, O_CLOEXEC | O_DIRECTORY | O_RDONLY);\n        return *this;\n    }\n\n    void rewind(void) {\n        if (fd >= 0) {\n            ::lseek(fd, off_t(0), SEEK_SET);\n            available_bytes = 0;\n            next = nullptr;\n        }\n    }\n\n    dirent* read(enum level index = proc, dirent* def = nullptr) {\n        if (!fill(index)) return def;\n        auto ret = next;\n        available_bytes -= next->d_reclen;\n        next = reinterpret_cast<dirent*>(reinterpret_cast<char*>(next) + next->d_reclen);\n        return ret;\n    }\n} llkTopDirectory;\n\ndirent dir::buff[dir::numLevels][dir::buffEntries];\n\n// helper functions\n\nbool llkIsMissingExeLink(pid_t tid) {\n    char c;\n    // CAP_SYS_PTRACE is required to prevent ret == -1, but ENOENT is signal\n    auto ret = ::readlink((procdir + std::to_string(tid) + \"/exe\").c_str(), &c, sizeof(c));\n    return (ret == -1) && (errno == ENOENT);\n}\n\n// Common routine where caller accepts empty content as error/passthrough.\n// Reduces the churn of reporting read errors in the callers.\nstd::string ReadFile(std::string&& path) {\n    std::string content;\n    if (!android::base::ReadFileToString(path, &content)) {\n        PLOG(DEBUG) << \"Read \" << path << \" failed\";\n        content = \"\";\n    }\n    return content;\n}\n\nstd::string llkProcGetName(pid_t tid, const char* node = \"/cmdline\") {\n    std::string content = ReadFile(procdir + std::to_string(tid) + node);\n    static constexpr char needles[] = \" \\t\\r\\n\";  // including trailing nul\n    auto pos = content.find_first_of(needles, 0, sizeof(needles));\n    if (pos != std::string::npos) {\n        content.erase(pos);\n    }\n    return content;\n}\n\nuid_t llkProcGetUid(pid_t tid) {\n    // Get the process' uid.  The following read from /status is admittedly\n    // racy, prone to corruption due to shape-changes.  The consequences are\n    // not catastrophic as we sample a few times before taking action.\n    //\n    // If /loginuid worked on reliably, or on Android (all tasks report -1)...\n    // Android lmkd causes /cgroup to contain memory:/<dom>/uid_<uid>/pid_<pid>\n    // which is tighter, but also not reliable.\n    std::string content = ReadFile(procdir + std::to_string(tid) + \"/status\");\n    static constexpr char Uid[] = \"\\nUid:\";\n    auto pos = content.find(Uid);\n    if (pos == std::string::npos) {\n        return -1;\n    }\n    pos += ::strlen(Uid);\n    while ((pos < content.size()) && ::isblank(content[pos])) {\n        ++pos;\n    }\n    content.erase(0, pos);\n    for (pos = 0; (pos < content.size()) && ::isdigit(content[pos]); ++pos) {\n        ;\n    }\n    // Content of form 'Uid:\t0\t0\t0\t0', newline is error\n    if ((pos >= content.size()) || !::isblank(content[pos])) {\n        return -1;\n    }\n    content.erase(pos);\n    uid_t ret;\n    if (!android::base::ParseUint(content, &ret, uid_t(0))) {\n        return -1;\n    }\n    return ret;\n}\n\nstruct proc {\n    pid_t tid;                     // monitored thread id (in Z or D state).\n    nanoseconds schedUpdate;       // /proc/<tid>/sched \"se.avg.lastUpdateTime\",\n    uint64_t nrSwitches;           // /proc/<tid>/sched \"nr_switches\" for\n                                   // refined ABA problem detection, determine\n                                   // forward scheduling progress.\n    milliseconds update;           // llkUpdate millisecond signature of last.\n    milliseconds count;            // duration in state.\n#ifdef __PTRACE_ENABLED__          // Privileged state checking\n    milliseconds count_stack;      // duration where stack is stagnant.\n#endif                             // End privilege\n    pid_t pid;                     // /proc/<pid> before iterating through\n                                   // /proc/<pid>/task/<tid> for threads.\n    pid_t ppid;                    // /proc/<tid>/stat field 4 parent pid.\n    uid_t uid;                     // /proc/<tid>/status Uid: field.\n    unsigned time;                 // sum of /proc/<tid>/stat field 14 utime &\n                                   // 15 stime for coarse ABA problem detection.\n    std::string cmdline;           // cached /cmdline content\n    char state;                    // /proc/<tid>/stat field 3: Z or D\n                                   // (others we do not monitor: S, R, T or ?)\n#ifdef __PTRACE_ENABLED__          // Privileged state checking\n    char stack;                    // index in llkCheckStackSymbols for matches\n#endif                             // and with maximum index PROP_VALUE_MAX/2.\n    char comm[TASK_COMM_LEN + 3];  // space for adding '[' and ']'\n    bool exeMissingValid;          // exeMissing has been cached\n    bool cmdlineValid;             // cmdline has been cached\n    bool updated;                  // cleared before monitoring pass.\n    bool killed;                   // sent a kill to this thread, next panic...\n    bool frozen;                   // process is in frozen cgroup.\n\n    void setComm(const char* _comm) { strncpy(comm + 1, _comm, sizeof(comm) - 2); }\n\n    void setFrozen(bool _frozen) { frozen = _frozen; }\n\n    proc(pid_t tid, pid_t pid, pid_t ppid, const char* _comm, int time, char state, bool frozen)\n        : tid(tid),\n          schedUpdate(0),\n          nrSwitches(0),\n          update(llkUpdate),\n          count(0ms),\n#ifdef __PTRACE_ENABLED__\n          count_stack(0ms),\n#endif\n          pid(pid),\n          ppid(ppid),\n          uid(-1),\n          time(time),\n          state(state),\n#ifdef __PTRACE_ENABLED__\n          stack(-1),\n#endif\n          exeMissingValid(false),\n          cmdlineValid(false),\n          updated(true),\n          killed(!llkTestWithKill),\n          frozen(frozen) {\n        memset(comm, '\\0', sizeof(comm));\n        setComm(_comm);\n    }\n\n    const char* getComm(void) {\n        if (comm[1] == '\\0') {  // comm Valid?\n            strncpy(comm + 1, llkProcGetName(tid, \"/comm\").c_str(), sizeof(comm) - 2);\n        }\n        if (!exeMissingValid) {\n            if (llkIsMissingExeLink(tid)) {\n                comm[0] = '[';\n            }\n            exeMissingValid = true;\n        }\n        size_t len = strlen(comm + 1);\n        if (__predict_true(len < (sizeof(comm) - 1))) {\n            if (comm[0] == '[') {\n                if ((comm[len] != ']') && __predict_true(len < (sizeof(comm) - 2))) {\n                    comm[++len] = ']';\n                    comm[++len] = '\\0';\n                }\n            } else {\n                if (comm[len] == ']') {\n                    comm[len] = '\\0';\n                }\n            }\n        }\n        return &comm[comm[0] != '['];\n    }\n\n    const char* getCmdline(void) {\n        if (!cmdlineValid) {\n            cmdline = llkProcGetName(tid);\n            cmdlineValid = true;\n        }\n        return cmdline.c_str();\n    }\n\n    uid_t getUid(void) {\n        if (uid <= 0) {  // Churn on root user, because most likely to setuid()\n            uid = llkProcGetUid(tid);\n        }\n        return uid;\n    }\n\n    bool isFrozen() { return frozen; }\n\n    void reset(void) {  // reset cache, if we detected pid rollover\n        uid = -1;\n        state = '?';\n#ifdef __PTRACE_ENABLED__\n        count_stack = 0ms;\n        stack = -1;\n#endif\n        cmdline = \"\";\n        comm[0] = '\\0';\n        exeMissingValid = false;\n        cmdlineValid = false;\n    }\n};\n\nstd::unordered_map<pid_t, proc> tids;\n\n// Check range and setup defaults, in order of propagation:\n//     llkTimeoutMs\n//     llkCheckMs\n//     ...\n// KISS to keep it all self-contained, and called multiple times as parameters\n// are interpreted so that defaults, llkCheckMs and llkCycle make sense.\nvoid llkValidate() {\n    if (llkTimeoutMs == 0ms) {\n        llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;\n    }\n    llkTimeoutMs = std::max(llkTimeoutMs, LLK_TIMEOUT_MS_MINIMUM);\n    if (llkCheckMs == 0ms) {\n        llkCheckMs = llkTimeoutMs / LLK_CHECKS_PER_TIMEOUT_DEFAULT;\n    }\n    llkCheckMs = std::min(llkCheckMs, llkTimeoutMs);\n\n    for (size_t state = 0; state < ARRAY_SIZE(llkStateTimeoutMs); ++state) {\n        if (llkStateTimeoutMs[state] == 0ms) {\n            llkStateTimeoutMs[state] = llkTimeoutMs;\n        }\n        llkStateTimeoutMs[state] =\n            std::min(std::max(llkStateTimeoutMs[state], LLK_TIMEOUT_MS_MINIMUM), llkTimeoutMs);\n        llkCheckMs = std::min(llkCheckMs, llkStateTimeoutMs[state]);\n    }\n\n    llkCheckMs = std::max(llkCheckMs, LLK_CHECK_MS_MINIMUM);\n    if (llkCycle == 0ms) {\n        llkCycle = llkCheckMs;\n    }\n    llkCycle = std::min(llkCycle, llkCheckMs);\n}\n\nmilliseconds llkGetTimespecDiffMs(timespec* from, timespec* to) {\n    return duration_cast<milliseconds>(seconds(to->tv_sec - from->tv_sec)) +\n           duration_cast<milliseconds>(nanoseconds(to->tv_nsec - from->tv_nsec));\n}\n\nstd::string llkProcGetName(pid_t tid, const char* comm, const char* cmdline) {\n    if ((cmdline != nullptr) && (*cmdline != '\\0')) {\n        return cmdline;\n    }\n    if ((comm != nullptr) && (*comm != '\\0')) {\n        return comm;\n    }\n\n    // UNLIKELY! Here because killed before we kill it?\n    // Assume change is afoot, do not call llkTidAlloc\n\n    // cmdline ?\n    std::string content = llkProcGetName(tid);\n    if (content.size() != 0) {\n        return content;\n    }\n    // Comm instead?\n    content = llkProcGetName(tid, \"/comm\");\n    if (llkIsMissingExeLink(tid) && (content.size() != 0)) {\n        return '[' + content + ']';\n    }\n    return content;\n}\n\nint llkKillOneProcess(pid_t pid, char state, pid_t tid, const char* tcomm = nullptr,\n                      const char* tcmdline = nullptr, const char* pcomm = nullptr,\n                      const char* pcmdline = nullptr) {\n    std::string forTid;\n    if (tid != pid) {\n        forTid = \" for '\" + llkProcGetName(tid, tcomm, tcmdline) + \"' (\" + std::to_string(tid) + \")\";\n    }\n    LOG(INFO) << \"Killing '\" << llkProcGetName(pid, pcomm, pcmdline) << \"' (\" << pid\n              << \") to check forward scheduling progress in \" << state << \" state\" << forTid;\n    // CAP_KILL required\n    errno = 0;\n    auto r = ::kill(pid, SIGKILL);\n    if (r) {\n        PLOG(ERROR) << \"kill(\" << pid << \")=\" << r << ' ';\n    }\n\n    return r;\n}\n\n// Kill one process\nint llkKillOneProcess(pid_t pid, proc* tprocp) {\n    return llkKillOneProcess(pid, tprocp->state, tprocp->tid, tprocp->getComm(),\n                             tprocp->getCmdline());\n}\n\n// Kill one process specified by kprocp\nint llkKillOneProcess(proc* kprocp, proc* tprocp) {\n    if (kprocp == nullptr) {\n        return -2;\n    }\n\n    return llkKillOneProcess(kprocp->tid, tprocp->state, tprocp->tid, tprocp->getComm(),\n                             tprocp->getCmdline(), kprocp->getComm(), kprocp->getCmdline());\n}\n\n// Acquire file descriptor from environment, or open and cache it.\n// NB: cache is unnecessary in our current context, pedantically\n//     required to prevent leakage of file descriptors in the future.\nint llkFileToWriteFd(const std::string& file) {\n    static std::unordered_map<std::string, int> cache;\n    auto search = cache.find(file);\n    if (search != cache.end()) return search->second;\n    auto fd = android_get_control_file(file.c_str());\n    if (fd >= 0) return fd;\n    fd = TEMP_FAILURE_RETRY(::open(file.c_str(), O_WRONLY | O_CLOEXEC));\n    if (fd >= 0) cache.emplace(std::make_pair(file, fd));\n    return fd;\n}\n\n// Wrap android::base::WriteStringToFile to use android_get_control_file.\nbool llkWriteStringToFile(const std::string& string, const std::string& file) {\n    auto fd = llkFileToWriteFd(file);\n    if (fd < 0) return false;\n    return android::base::WriteStringToFd(string, fd);\n}\n\nbool llkWriteStringToFileConfirm(const std::string& string, const std::string& file) {\n    auto fd = llkFileToWriteFd(file);\n    auto ret = (fd < 0) ? false : android::base::WriteStringToFd(string, fd);\n    std::string content;\n    if (!android::base::ReadFileToString(file, &content)) return ret;\n    return android::base::Trim(content) == string;\n}\n\nvoid llkPanicKernel(bool dump, pid_t tid, const char* state, const std::string& message = \"\") {\n    if (!message.empty()) LOG(ERROR) << message;\n    auto sysrqTriggerFd = llkFileToWriteFd(\"/proc/sysrq-trigger\");\n    if (sysrqTriggerFd < 0) {\n        // DYB\n        llkKillOneProcess(initPid, 'R', tid);\n        // The answer to life, the universe and everything\n        ::exit(42);\n        // NOTREACHED\n        return;\n    }\n    // Wish could ::sync() here, if storage is locked up, we will not continue.\n    if (dump) {\n        // Show all locks that are held\n        android::base::WriteStringToFd(\"d\", sysrqTriggerFd);\n        // Show all waiting tasks\n        android::base::WriteStringToFd(\"w\", sysrqTriggerFd);\n        // This can trigger hardware watchdog, that is somewhat _ok_.\n        // But useless if pstore configured for <256KB, low ram devices ...\n        if (llkEnableSysrqT) {\n            android::base::WriteStringToFd(\"t\", sysrqTriggerFd);\n            // Show all locks that are held (in case 't' overflows ramoops)\n            android::base::WriteStringToFd(\"d\", sysrqTriggerFd);\n            // Show all waiting tasks (in case 't' overflows ramoops)\n            android::base::WriteStringToFd(\"w\", sysrqTriggerFd);\n        }\n        ::usleep(200000);  // let everything settle\n    }\n    // SysRq message matches kernel format, and propagates through bootstat\n    // ultimately to the boot reason into panic,livelock,<state>.\n    llkWriteStringToFile(message + (message.empty() ? \"\" : \"\\n\") +\n                                 \"SysRq : Trigger a crash : 'livelock,\"s + state + \"'\\n\",\n                         \"/dev/kmsg\");\n    // Because panic is such a serious thing to do, let us\n    // make sure that the tid being inspected still exists!\n    auto piddir = procdir + std::to_string(tid) + \"/stat\";\n    if (access(piddir.c_str(), F_OK) != 0) {\n        PLOG(WARNING) << piddir;\n        return;\n    }\n    android::base::WriteStringToFd(\"c\", sysrqTriggerFd);\n    // NOTREACHED\n    // DYB\n    llkKillOneProcess(initPid, 'R', tid);\n    // I sat at my desk, stared into the garden and thought '42 will do'.\n    // I typed it out. End of story\n    ::exit(42);\n    // NOTREACHED\n}\n\nvoid llkAlarmHandler(int) {\n    LOG(FATAL) << \"alarm\";\n    // NOTREACHED\n    llkPanicKernel(true, ::getpid(), \"alarm\");\n}\n\nmilliseconds GetUintProperty(const std::string& key, milliseconds def) {\n    return milliseconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),\n                                                       static_cast<uint64_t>(def.max().count())));\n}\n\nseconds GetUintProperty(const std::string& key, seconds def) {\n    return seconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),\n                                                  static_cast<uint64_t>(def.max().count())));\n}\n\nproc* llkTidLookup(pid_t tid) {\n    auto search = tids.find(tid);\n    if (search == tids.end()) {\n        return nullptr;\n    }\n    return &search->second;\n}\n\nvoid llkTidRemove(pid_t tid) {\n    tids.erase(tid);\n}\n\nproc* llkTidAlloc(pid_t tid, pid_t pid, pid_t ppid, const char* comm, int time, char state,\n                  bool frozen) {\n    auto it = tids.emplace(std::make_pair(tid, proc(tid, pid, ppid, comm, time, state, frozen)));\n    return &it.first->second;\n}\n\nstd::string llkFormat(milliseconds ms) {\n    auto sec = duration_cast<seconds>(ms);\n    std::ostringstream s;\n    s << sec.count() << '.';\n    auto f = s.fill('0');\n    auto w = s.width(3);\n    s << std::right << (ms - sec).count();\n    s.width(w);\n    s.fill(f);\n    s << 's';\n    return s.str();\n}\n\nstd::string llkFormat(seconds s) {\n    return std::to_string(s.count()) + 's';\n}\n\nstd::string llkFormat(bool flag) {\n    return flag ? \"true\" : \"false\";\n}\n\nstd::string llkFormat(const std::unordered_set<std::string>& ignorelist) {\n    std::string ret;\n    for (const auto& entry : ignorelist) {\n        if (!ret.empty()) ret += \",\";\n        ret += entry;\n    }\n    return ret;\n}\n\nstd::string llkFormat(\n        const std::unordered_map<std::string, std::unordered_set<std::string>>& ignorelist,\n        bool leading_comma = false) {\n    std::string ret;\n    for (const auto& entry : ignorelist) {\n        for (const auto& target : entry.second) {\n            if (leading_comma || !ret.empty()) ret += \",\";\n            ret += entry.first + \"&\" + target;\n        }\n    }\n    return ret;\n}\n\n// This function parses the properties as a list, incorporating the supplied\n// default.  A leading comma separator means preserve the defaults and add\n// entries (with an optional leading + sign), or removes entries with a leading\n// - sign.\n//\n// We only officially support comma separators, but wetware being what they\n// are will take some liberty and I do not believe they should be punished.\nstd::unordered_set<std::string> llkSplit(const std::string& prop, const std::string& def) {\n    auto s = android::base::GetProperty(prop, def);\n    constexpr char separators[] = \", \\t:;\";\n    if (!s.empty() && (s != def) && strchr(separators, s[0])) s = def + s;\n\n    std::unordered_set<std::string> result;\n\n    // Special case, allow boolean false to empty the list, otherwise expected\n    // source of input from android::base::GetProperty will supply the default\n    // value on empty content in the property.\n    if (s == \"false\") return result;\n\n    size_t base = 0;\n    while (s.size() > base) {\n        auto found = s.find_first_of(separators, base);\n        // Only emplace unique content, empty entries are not an option\n        if (found != base) {\n            switch (s[base]) {\n                case '-':\n                    ++base;\n                    if (base >= s.size()) break;\n                    if (base != found) {\n                        auto have = result.find(s.substr(base, found - base));\n                        if (have != result.end()) result.erase(have);\n                    }\n                    break;\n                case '+':\n                    ++base;\n                    if (base >= s.size()) break;\n                    if (base == found) break;\n                    // FALLTHRU (for gcc, lint, pcc, etc; following for clang)\n                    FALLTHROUGH_INTENDED;\n                default:\n                    result.emplace(s.substr(base, found - base));\n                    break;\n            }\n        }\n        if (found == s.npos) break;\n        base = found + 1;\n    }\n    return result;\n}\n\nbool llkSkipName(const std::string& name,\n                 const std::unordered_set<std::string>& ignorelist = llkIgnorelistProcess) {\n    if (name.empty() || ignorelist.empty()) return false;\n\n    return ignorelist.find(name) != ignorelist.end();\n}\n\nbool llkSkipProc(proc* procp,\n                 const std::unordered_set<std::string>& ignorelist = llkIgnorelistProcess) {\n    if (!procp) return false;\n    if (llkSkipName(std::to_string(procp->pid), ignorelist)) return true;\n    if (llkSkipName(procp->getComm(), ignorelist)) return true;\n    if (llkSkipName(procp->getCmdline(), ignorelist)) return true;\n    if (llkSkipName(android::base::Basename(procp->getCmdline()), ignorelist)) return true;\n    return false;\n}\n\nconst std::unordered_set<std::string>& llkSkipName(\n        const std::string& name,\n        const std::unordered_map<std::string, std::unordered_set<std::string>>& ignorelist) {\n    static const std::unordered_set<std::string> empty;\n    if (name.empty() || ignorelist.empty()) return empty;\n    auto found = ignorelist.find(name);\n    if (found == ignorelist.end()) return empty;\n    return found->second;\n}\n\nbool llkSkipPproc(proc* pprocp, proc* procp,\n                  const std::unordered_map<std::string, std::unordered_set<std::string>>&\n                          ignorelist = llkIgnorelistParentAndChild) {\n    if (!pprocp || !procp || ignorelist.empty()) return false;\n    if (llkSkipProc(procp, llkSkipName(std::to_string(pprocp->pid), ignorelist))) return true;\n    if (llkSkipProc(procp, llkSkipName(pprocp->getComm(), ignorelist))) return true;\n    if (llkSkipProc(procp, llkSkipName(pprocp->getCmdline(), ignorelist))) return true;\n    return llkSkipProc(procp,\n                       llkSkipName(android::base::Basename(pprocp->getCmdline()), ignorelist));\n}\n\nbool llkSkipPid(pid_t pid) {\n    return llkSkipName(std::to_string(pid), llkIgnorelistProcess);\n}\n\nbool llkSkipPpid(pid_t ppid) {\n    return llkSkipName(std::to_string(ppid), llkIgnorelistParent);\n}\n\nbool llkSkipUid(uid_t uid) {\n    // Match by number?\n    if (llkSkipName(std::to_string(uid), llkIgnorelistUid)) {\n        return true;\n    }\n\n    // Match by name?\n    auto pwd = ::getpwuid(uid);\n    return (pwd != nullptr) && __predict_true(pwd->pw_name != nullptr) &&\n           __predict_true(pwd->pw_name[0] != '\\0') && llkSkipName(pwd->pw_name, llkIgnorelistUid);\n}\n\nbool getValidTidDir(dirent* dp, std::string* piddir) {\n    if (!::isdigit(dp->d_name[0])) {\n        return false;\n    }\n\n    // Corner case can not happen in reality b/c of above ::isdigit check\n    if (__predict_false(dp->d_type != DT_DIR)) {\n        if (__predict_false(dp->d_type == DT_UNKNOWN)) {  // can't b/c procfs\n            struct stat st;\n            *piddir = procdir;\n            *piddir += dp->d_name;\n            return (lstat(piddir->c_str(), &st) == 0) && (st.st_mode & S_IFDIR);\n        }\n        return false;\n    }\n\n    *piddir = procdir;\n    *piddir += dp->d_name;\n    return true;\n}\n\nbool llkIsMonitorState(char state) {\n    return (state == 'Z') || (state == 'D');\n}\n\n// returns -1 if not found\nlong long getSchedValue(const std::string& schedString, const char* key) {\n    auto pos = schedString.find(key);\n    if (pos == std::string::npos) {\n        return -1;\n    }\n    pos = schedString.find(':', pos);\n    if (__predict_false(pos == std::string::npos)) {\n        return -1;\n    }\n    while ((++pos < schedString.size()) && ::isblank(schedString[pos])) {\n        ;\n    }\n    long long ret;\n    if (!android::base::ParseInt(schedString.substr(pos), &ret, static_cast<long long>(0))) {\n        return -1;\n    }\n    return ret;\n}\n\n#ifdef __PTRACE_ENABLED__\nbool llkCheckStack(proc* procp, const std::string& piddir) {\n    if (llkCheckStackSymbols.empty()) return false;\n    if (procp->state == 'Z') {  // No brains for Zombies\n        procp->stack = -1;\n        procp->count_stack = 0ms;\n        return false;\n    }\n\n    // Don't check process that are known to block ptrace, save sepolicy noise.\n    if (llkSkipProc(procp, llkIgnorelistStack)) return false;\n    auto kernel_stack = ReadFile(piddir + \"/stack\");\n    if (kernel_stack.empty()) {\n        LOG(VERBOSE) << piddir << \"/stack empty comm=\" << procp->getComm()\n                     << \" cmdline=\" << procp->getCmdline();\n        return false;\n    }\n    // A scheduling incident that should not reset count_stack\n    if (kernel_stack.find(\" cpu_worker_pools+0x\") != std::string::npos) return false;\n    char idx = -1;\n    char match = -1;\n    std::string matched_stack_symbol = \"<unknown>\";\n    for (const auto& stack : llkCheckStackSymbols) {\n        if (++idx < 0) break;\n        if ((kernel_stack.find(\" \"s + stack + \"+0x\") != std::string::npos) ||\n            (kernel_stack.find(\" \"s + stack + \".cfi+0x\") != std::string::npos)) {\n            match = idx;\n            matched_stack_symbol = stack;\n            break;\n        }\n    }\n    if (procp->stack != match) {\n        procp->stack = match;\n        procp->count_stack = 0ms;\n        return false;\n    }\n    if (match == char(-1)) return false;\n    procp->count_stack += llkCycle;\n    if (procp->count_stack < llkStateTimeoutMs[llkStateStack]) return false;\n    LOG(WARNING) << \"Found \" << matched_stack_symbol << \" in stack for pid \" << procp->pid;\n    return true;\n}\n#endif\n\n// Primary ABA mitigation watching last time schedule activity happened\nvoid llkCheckSchedUpdate(proc* procp, const std::string& piddir) {\n    // Audit finds /proc/<tid>/sched is just over 1K, and\n    // is rarely larger than 2K, even less on Android.\n    // For example, the \"se.avg.lastUpdateTime\" field we are\n    // interested in typically within the primary set in\n    // the first 1K.\n    //\n    // Proc entries can not be read >1K atomically via libbase,\n    // but if there are problems we assume at least a few\n    // samples of reads occur before we take any real action.\n    std::string schedString = ReadFile(piddir + \"/sched\");\n    if (schedString.empty()) {\n        // /schedstat is not as standardized, but in 3.1+\n        // Android devices, the third field is nr_switches\n        // from /sched:\n        schedString = ReadFile(piddir + \"/schedstat\");\n        if (schedString.empty()) {\n            return;\n        }\n        auto val = static_cast<unsigned long long>(-1);\n        if (((::sscanf(schedString.c_str(), \"%*d %*d %llu\", &val)) == 1) &&\n            (val != static_cast<unsigned long long>(-1)) && (val != 0) &&\n            (val != procp->nrSwitches)) {\n            procp->nrSwitches = val;\n            procp->count = 0ms;\n            procp->killed = !llkTestWithKill;\n        }\n        return;\n    }\n\n    auto val = getSchedValue(schedString, \"\\nse.avg.lastUpdateTime\");\n    if (val == -1) {\n        val = getSchedValue(schedString, \"\\nse.svg.last_update_time\");\n    }\n    if (val != -1) {\n        auto schedUpdate = nanoseconds(val);\n        if (schedUpdate != procp->schedUpdate) {\n            procp->schedUpdate = schedUpdate;\n            procp->count = 0ms;\n            procp->killed = !llkTestWithKill;\n        }\n    }\n\n    val = getSchedValue(schedString, \"\\nnr_switches\");\n    if (val != -1) {\n        if (static_cast<uint64_t>(val) != procp->nrSwitches) {\n            procp->nrSwitches = val;\n            procp->count = 0ms;\n            procp->killed = !llkTestWithKill;\n        }\n    }\n}\n\nvoid llkLogConfig(void) {\n    LOG(INFO) << \"ro.config.low_ram=\" << llkFormat(llkLowRam) << \"\\n\"\n              << LLK_ENABLE_SYSRQ_T_PROPERTY \"=\" << llkFormat(llkEnableSysrqT) << \"\\n\"\n              << LLK_ENABLE_PROPERTY \"=\" << llkFormat(llkEnable) << \"\\n\"\n              << KHT_ENABLE_PROPERTY \"=\" << llkFormat(khtEnable) << \"\\n\"\n              << LLK_MLOCKALL_PROPERTY \"=\" << llkFormat(llkMlockall) << \"\\n\"\n              << LLK_KILLTEST_PROPERTY \"=\" << llkFormat(llkTestWithKill) << \"\\n\"\n              << KHT_TIMEOUT_PROPERTY \"=\" << llkFormat(khtTimeout) << \"\\n\"\n              << LLK_TIMEOUT_MS_PROPERTY \"=\" << llkFormat(llkTimeoutMs) << \"\\n\"\n              << LLK_D_TIMEOUT_MS_PROPERTY \"=\" << llkFormat(llkStateTimeoutMs[llkStateD]) << \"\\n\"\n              << LLK_Z_TIMEOUT_MS_PROPERTY \"=\" << llkFormat(llkStateTimeoutMs[llkStateZ]) << \"\\n\"\n#ifdef __PTRACE_ENABLED__\n              << LLK_STACK_TIMEOUT_MS_PROPERTY \"=\" << llkFormat(llkStateTimeoutMs[llkStateStack])\n              << \"\\n\"\n#endif\n              << LLK_CHECK_MS_PROPERTY \"=\" << llkFormat(llkCheckMs) << \"\\n\"\n#ifdef __PTRACE_ENABLED__\n              << LLK_CHECK_STACK_PROPERTY \"=\" << llkFormat(llkCheckStackSymbols) << \"\\n\"\n              << LLK_IGNORELIST_STACK_PROPERTY \"=\" << llkFormat(llkIgnorelistStack) << \"\\n\"\n#endif\n              << LLK_IGNORELIST_PROCESS_PROPERTY \"=\" << llkFormat(llkIgnorelistProcess) << \"\\n\"\n              << LLK_IGNORELIST_PARENT_PROPERTY \"=\" << llkFormat(llkIgnorelistParent)\n              << llkFormat(llkIgnorelistParentAndChild, true) << \"\\n\"\n              << LLK_IGNORELIST_UID_PROPERTY \"=\" << llkFormat(llkIgnorelistUid);\n}\n\nvoid* llkThread(void* obj) {\n    prctl(PR_SET_DUMPABLE, 0);\n\n    LOG(INFO) << \"started\";\n\n    std::string name = std::to_string(::gettid());\n    if (!llkSkipName(name)) {\n        llkIgnorelistProcess.emplace(name);\n    }\n    name = static_cast<const char*>(obj);\n    prctl(PR_SET_NAME, name.c_str());\n    if (__predict_false(!llkSkipName(name))) {\n        llkIgnorelistProcess.insert(name);\n    }\n    // No longer modifying llkIgnorelistProcess.\n    llkRunning = true;\n    llkLogConfig();\n    while (llkRunning) {\n        ::usleep(duration_cast<microseconds>(llkCheck(true)).count());\n    }\n    // NOTREACHED\n    LOG(INFO) << \"exiting\";\n    return nullptr;\n}\n\n}  // namespace\n\nmilliseconds llkCheck(bool checkRunning) {\n    if (!llkEnable || (checkRunning != llkRunning)) {\n        return milliseconds::max();\n    }\n\n    // Reset internal watchdog, which is a healthy engineering margin of\n    // double the maximum wait or cycle time for the mainloop that calls us.\n    //\n    // This alarm is effectively the live lock detection of llkd, as\n    // we understandably can not monitor ourselves otherwise.\n    ::alarm(duration_cast<seconds>(llkTimeoutMs * 2 * android::base::HwTimeoutMultiplier())\n                    .count());\n\n    // kernel jiffy precision fastest acquisition\n    static timespec last;\n    timespec now;\n    ::clock_gettime(CLOCK_MONOTONIC_COARSE, &now);\n    auto ms = llkGetTimespecDiffMs(&last, &now);\n    if (ms < llkCycle) {\n        return llkCycle - ms;\n    }\n    last = now;\n\n    LOG(VERBOSE) << \"opendir(\\\"\" << procdir << \"\\\")\";\n    if (__predict_false(!llkTopDirectory)) {\n        // gid containing AID_READPROC required\n        llkTopDirectory.reset(procdir);\n        if (__predict_false(!llkTopDirectory)) {\n            // Most likely reason we could be here is a resource limit.\n            // Keep our processing down to a minimum, but not so low that\n            // we do not recover in a timely manner should the issue be\n            // transitory.\n            LOG(DEBUG) << \"opendir(\\\"\" << procdir << \"\\\") failed\";\n            return llkTimeoutMs;\n        }\n    }\n\n    for (auto& it : tids) {\n        it.second.updated = false;\n    }\n\n    auto prevUpdate = llkUpdate;\n    llkUpdate += ms;\n    ms -= llkCycle;\n    auto myPid = ::getpid();\n    auto myTid = ::gettid();\n    auto dump = true;\n    for (auto dp = llkTopDirectory.read(); dp != nullptr; dp = llkTopDirectory.read()) {\n        std::string piddir;\n\n        if (!getValidTidDir(dp, &piddir)) {\n            continue;\n        }\n\n        // Get the process tasks\n        std::string taskdir = piddir + \"/task/\";\n        int pid = -1;\n        LOG(VERBOSE) << \"+opendir(\\\"\" << taskdir << \"\\\")\";\n        dir taskDirectory(taskdir);\n        if (__predict_false(!taskDirectory)) {\n            LOG(DEBUG) << \"+opendir(\\\"\" << taskdir << \"\\\") failed\";\n        }\n        for (auto tp = taskDirectory.read(dir::task, dp); tp != nullptr;\n             tp = taskDirectory.read(dir::task)) {\n            if (!getValidTidDir(tp, &piddir)) {\n                continue;\n            }\n\n            // Get the process stat\n            std::string stat = ReadFile(piddir + \"/stat\");\n            if (stat.empty()) {\n                continue;\n            }\n            unsigned tid = -1;\n            char pdir[TASK_COMM_LEN + 1];\n            char state = '?';\n            unsigned ppid = -1;\n            unsigned utime = -1;\n            unsigned stime = -1;\n            int dummy;\n            pdir[0] = '\\0';\n            // tid should not change value\n            auto match = ::sscanf(\n                stat.c_str(),\n                \"%u (%\" ___STRING(\n                    TASK_COMM_LEN) \"[^)]) %c %u %*d %*d %*d %*d %*d %*d %*d %*d %*d %u %u %d\",\n                &tid, pdir, &state, &ppid, &utime, &stime, &dummy);\n            if (pid == -1) {\n                pid = tid;\n            }\n            LOG(VERBOSE) << \"match \" << match << ' ' << tid << \" (\" << pdir << \") \" << state << ' '\n                         << ppid << \" ... \" << utime << ' ' << stime << ' ' << dummy;\n            if (match != 7) {\n                continue;\n            }\n\n            // Get the process cgroup\n            auto cgroup = ReadFile(piddir + \"/cgroup\");\n            auto frozen = cgroup.find(\":freezer:/frozen\") != std::string::npos;\n\n            auto procp = llkTidLookup(tid);\n            if (procp == nullptr) {\n                procp = llkTidAlloc(tid, pid, ppid, pdir, utime + stime, state, frozen);\n            } else {\n                // comm can change ...\n                procp->setComm(pdir);\n                // frozen can change, too...\n                procp->setFrozen(frozen);\n                procp->updated = true;\n                // pid/ppid/tid wrap?\n                if (((procp->update != prevUpdate) && (procp->update != llkUpdate)) ||\n                    (procp->ppid != ppid) || (procp->pid != pid)) {\n                    procp->reset();\n                } else if (procp->time != (utime + stime)) {  // secondary ABA.\n                    // watching utime+stime granularity jiffy\n                    procp->state = '?';\n                }\n                procp->update = llkUpdate;\n                procp->pid = pid;\n                procp->ppid = ppid;\n                procp->time = utime + stime;\n                if (procp->state != state) {\n                    procp->count = 0ms;\n                    procp->killed = !llkTestWithKill;\n                    procp->state = state;\n                } else {\n                    procp->count += llkCycle;\n                }\n            }\n\n            // Filter checks in intuitive order of CPU cost to evaluate\n            // If tid unique continue, if ppid or pid unique break\n\n            if (pid == myPid) {\n                break;\n            }\n#ifdef __PTRACE_ENABLED__\n            // if no stack monitoring, we can quickly exit here\n            if (!llkIsMonitorState(state) && llkCheckStackSymbols.empty()) {\n                continue;\n            }\n#else\n            if (!llkIsMonitorState(state)) continue;\n#endif\n            if ((tid == myTid) || llkSkipPid(tid)) {\n                continue;\n            }\n            if (procp->isFrozen()) {\n                break;\n            }\n            if (llkSkipPpid(ppid)) {\n                break;\n            }\n\n            auto process_comm = procp->getComm();\n            if (llkSkipName(process_comm)) {\n                continue;\n            }\n            if (llkSkipName(procp->getCmdline())) {\n                break;\n            }\n            if (llkSkipName(android::base::Basename(procp->getCmdline()))) {\n                break;\n            }\n\n            auto pprocp = llkTidLookup(ppid);\n            if (pprocp == nullptr) {\n                pprocp = llkTidAlloc(ppid, ppid, 0, \"\", 0, '?', false);\n            }\n            if (pprocp) {\n                if (llkSkipPproc(pprocp, procp)) break;\n                if (llkSkipProc(pprocp, llkIgnorelistParent)) break;\n            } else {\n                if (llkSkipName(std::to_string(ppid), llkIgnorelistParent)) break;\n            }\n\n            if ((llkIgnorelistUid.size() != 0) && llkSkipUid(procp->getUid())) {\n                continue;\n            }\n\n            // ABA mitigation watching last time schedule activity happened\n            llkCheckSchedUpdate(procp, piddir);\n\n#ifdef __PTRACE_ENABLED__\n            auto stuck = llkCheckStack(procp, piddir);\n            if (llkIsMonitorState(state)) {\n                if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {\n                    stuck = true;\n                } else if (procp->count != 0ms) {\n                    LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << \"->\"\n                                 << pid << \"->\" << tid << ' ' << process_comm;\n                }\n            }\n            if (!stuck) continue;\n#else\n            if (procp->count >= llkStateTimeoutMs[(state == 'Z') ? llkStateZ : llkStateD]) {\n                if (procp->count != 0ms) {\n                    LOG(VERBOSE) << state << ' ' << llkFormat(procp->count) << ' ' << ppid << \"->\"\n                                 << pid << \"->\" << tid << ' ' << process_comm;\n                }\n                continue;\n            }\n#endif\n\n            // We have to kill it to determine difference between live lock\n            // and persistent state blocked on a resource.  Is there something\n            // wrong with a process that has no forward scheduling progress in\n            // Z or D?  Yes, generally means improper accounting in the\n            // process, but not always ...\n            //\n            // Whomever we hit with a test kill must accept the Android\n            // Aphorism that everything can be burned to the ground and\n            // must survive.\n            if (procp->killed == false) {\n                procp->killed = true;\n                // confirm: re-read uid before committing to a panic.\n                procp->uid = -1;\n                switch (state) {\n                    case 'Z':  // kill ppid to free up a Zombie\n                        // Killing init will kernel panic without diagnostics\n                        // so skip right to controlled kernel panic with\n                        // diagnostics.\n                        if (ppid == initPid) {\n                            break;\n                        }\n                        LOG(WARNING) << \"Z \" << llkFormat(procp->count) << ' ' << ppid << \"->\"\n                                     << pid << \"->\" << tid << ' ' << process_comm << \" [kill]\";\n                        if ((llkKillOneProcess(pprocp, procp) >= 0) ||\n                            (llkKillOneProcess(ppid, procp) >= 0)) {\n                            continue;\n                        }\n                        break;\n\n                    case 'D':  // kill tid to free up an uninterruptible D\n                        // If ABA is doing its job, we would not need or\n                        // want the following.  Test kill is a Hail Mary\n                        // to make absolutely sure there is no forward\n                        // scheduling progress.  The cost when ABA is\n                        // not working is we kill a process that likes to\n                        // stay in 'D' state, instead of panicing the\n                        // kernel (worse).\n                    default:\n                        LOG(WARNING) << state << ' ' << llkFormat(procp->count) << ' ' << pid\n                                     << \"->\" << tid << ' ' << process_comm << \" [kill]\";\n                        if ((llkKillOneProcess(llkTidLookup(pid), procp) >= 0) ||\n                            (llkKillOneProcess(pid, state, tid) >= 0) ||\n                            (llkKillOneProcess(procp, procp) >= 0) ||\n                            (llkKillOneProcess(tid, state, tid) >= 0)) {\n                            continue;\n                        }\n                        break;\n                }\n            }\n            // We are here because we have confirmed kernel live-lock\n            std::vector<std::string> threads;\n            auto taskdir = procdir + std::to_string(tid) + \"/task/\";\n            dir taskDirectory(taskdir);\n            for (auto tp = taskDirectory.read(); tp != nullptr; tp = taskDirectory.read()) {\n                std::string piddir;\n                if (getValidTidDir(tp, &piddir))\n                    threads.push_back(android::base::Basename(piddir));\n            }\n            const auto message = state + \" \"s + llkFormat(procp->count) + \" \" +\n                                 std::to_string(ppid) + \"->\" + std::to_string(pid) + \"->\" +\n                                 std::to_string(tid) + \" \" + process_comm + \" [panic]\\n\" +\n                                 \"  thread group: {\" + android::base::Join(threads, \",\") +\n                                 \"}\";\n            llkPanicKernel(dump, tid,\n                           (state == 'Z') ? \"zombie\" : (state == 'D') ? \"driver\" : \"sleeping\",\n                           message);\n            dump = false;\n        }\n        LOG(VERBOSE) << \"+closedir()\";\n    }\n    llkTopDirectory.rewind();\n    LOG(VERBOSE) << \"closedir()\";\n\n    // garbage collection of old process references\n    for (auto p = tids.begin(); p != tids.end();) {\n        if (!p->second.updated) {\n            IF_ALOG(LOG_VERBOSE, LOG_TAG) {\n                std::string ppidCmdline = llkProcGetName(p->second.ppid, nullptr, nullptr);\n                if (!ppidCmdline.empty()) ppidCmdline = \"(\" + ppidCmdline + \")\";\n                std::string pidCmdline;\n                if (p->second.pid != p->second.tid) {\n                    pidCmdline = llkProcGetName(p->second.pid, nullptr, p->second.getCmdline());\n                    if (!pidCmdline.empty()) pidCmdline = \"(\" + pidCmdline + \")\";\n                }\n                std::string tidCmdline =\n                    llkProcGetName(p->second.tid, p->second.getComm(), p->second.getCmdline());\n                if (!tidCmdline.empty()) tidCmdline = \"(\" + tidCmdline + \")\";\n                LOG(VERBOSE) << \"thread \" << p->second.ppid << ppidCmdline << \"->\" << p->second.pid\n                             << pidCmdline << \"->\" << p->second.tid << tidCmdline << \" removed\";\n            }\n            p = tids.erase(p);\n        } else {\n            ++p;\n        }\n    }\n    if (__predict_false(tids.empty())) {\n        llkTopDirectory.reset();\n    }\n\n    llkCycle = llkCheckMs;\n\n    timespec end;\n    ::clock_gettime(CLOCK_MONOTONIC_COARSE, &end);\n    auto milli = llkGetTimespecDiffMs(&now, &end);\n    LOG((milli > 10s) ? ERROR : (milli > 1s) ? WARNING : VERBOSE) << \"sample \" << llkFormat(milli);\n\n    // cap to minimum sleep for 1 second since last cycle\n    if (llkCycle < (ms + 1s)) {\n        return 1s;\n    }\n    return llkCycle - ms;\n}\n\nunsigned llkCheckMilliseconds() {\n    return duration_cast<milliseconds>(llkCheck()).count();\n}\n\nbool llkCheckEng(const std::string& property) {\n    return android::base::GetProperty(property, \"eng\") == \"eng\";\n}\n\nbool llkInit(const char* threadname) {\n    auto debuggable = android::base::GetBoolProperty(\"ro.debuggable\", false);\n    llkLowRam = android::base::GetBoolProperty(\"ro.config.low_ram\", false);\n    llkEnableSysrqT &= !llkLowRam;\n    if (debuggable) {\n        llkEnableSysrqT |= llkCheckEng(LLK_ENABLE_SYSRQ_T_PROPERTY);\n        if (!LLK_ENABLE_DEFAULT) {\n            khtEnable |= llkCheckEng(KHT_ENABLE_PROPERTY);\n        }\n    }\n    llkEnableSysrqT = android::base::GetBoolProperty(LLK_ENABLE_SYSRQ_T_PROPERTY, llkEnableSysrqT);\n    llkEnable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, llkEnable);\n    if (llkEnable && !llkTopDirectory.reset(procdir)) {\n        // Most likely reason we could be here is llkd was started\n        // incorrectly without the readproc permissions.  Keep our\n        // processing down to a minimum.\n        llkEnable = false;\n    }\n    khtEnable = android::base::GetBoolProperty(KHT_ENABLE_PROPERTY, khtEnable);\n    llkMlockall = android::base::GetBoolProperty(LLK_MLOCKALL_PROPERTY, llkMlockall);\n    llkTestWithKill = android::base::GetBoolProperty(LLK_KILLTEST_PROPERTY, llkTestWithKill);\n    // if LLK_TIMOUT_MS_PROPERTY was not set, we will use a set\n    // KHT_TIMEOUT_PROPERTY as co-operative guidance for the default value.\n    khtTimeout = GetUintProperty(KHT_TIMEOUT_PROPERTY, khtTimeout);\n    if (khtTimeout == 0s) {\n        khtTimeout = duration_cast<seconds>(llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) /\n                                            LLK_CHECKS_PER_TIMEOUT_DEFAULT);\n    }\n    llkTimeoutMs =\n        khtTimeout * LLK_CHECKS_PER_TIMEOUT_DEFAULT / (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT);\n    llkTimeoutMs = GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);\n    llkValidate();  // validate llkTimeoutMs, llkCheckMs and llkCycle\n    llkStateTimeoutMs[llkStateD] = GetUintProperty(LLK_D_TIMEOUT_MS_PROPERTY, llkTimeoutMs);\n    llkStateTimeoutMs[llkStateZ] = GetUintProperty(LLK_Z_TIMEOUT_MS_PROPERTY, llkTimeoutMs);\n#ifdef __PTRACE_ENABLED__\n    llkStateTimeoutMs[llkStateStack] = GetUintProperty(LLK_STACK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);\n#endif\n    llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);\n    llkValidate();  // validate all (effectively minus llkTimeoutMs)\n#ifdef __PTRACE_ENABLED__\n    if (debuggable) {\n        llkCheckStackSymbols = llkSplit(LLK_CHECK_STACK_PROPERTY, LLK_CHECK_STACK_DEFAULT);\n    }\n    std::string defaultIgnorelistStack(LLK_IGNORELIST_STACK_DEFAULT);\n    if (!debuggable) defaultIgnorelistStack += \",logd,/system/bin/logd\";\n    llkIgnorelistStack = llkSplit(LLK_IGNORELIST_STACK_PROPERTY, defaultIgnorelistStack);\n#endif\n    std::string defaultIgnorelistProcess(\n            std::to_string(kernelPid) + \",\" + std::to_string(initPid) + \",\" +\n            std::to_string(kthreaddPid) + \",\" + std::to_string(::getpid()) + \",\" +\n            std::to_string(::gettid()) + \",\" LLK_IGNORELIST_PROCESS_DEFAULT);\n    if (threadname) {\n        defaultIgnorelistProcess += \",\"s + threadname;\n    }\n    for (int cpu = 1; cpu < get_nprocs_conf(); ++cpu) {\n        defaultIgnorelistProcess += \",[watchdog/\" + std::to_string(cpu) + \"]\";\n    }\n    llkIgnorelistProcess = llkSplit(LLK_IGNORELIST_PROCESS_PROPERTY, defaultIgnorelistProcess);\n    if (!llkSkipName(\"[khungtaskd]\")) {  // ALWAYS ignore as special\n        llkIgnorelistProcess.emplace(\"[khungtaskd]\");\n    }\n    llkIgnorelistParent = llkSplit(LLK_IGNORELIST_PARENT_PROPERTY,\n                                   std::to_string(kernelPid) + \",\" + std::to_string(kthreaddPid) +\n                                           \",\" LLK_IGNORELIST_PARENT_DEFAULT);\n    // derive llkIgnorelistParentAndChild by moving entries with '&' from above\n    for (auto it = llkIgnorelistParent.begin(); it != llkIgnorelistParent.end();) {\n        auto pos = it->find('&');\n        if (pos == std::string::npos) {\n            ++it;\n            continue;\n        }\n        auto parent = it->substr(0, pos);\n        auto child = it->substr(pos + 1);\n        it = llkIgnorelistParent.erase(it);\n\n        auto found = llkIgnorelistParentAndChild.find(parent);\n        if (found == llkIgnorelistParentAndChild.end()) {\n            llkIgnorelistParentAndChild.emplace(std::make_pair(\n                    std::move(parent), std::unordered_set<std::string>({std::move(child)})));\n        } else {\n            found->second.emplace(std::move(child));\n        }\n    }\n\n    llkIgnorelistUid = llkSplit(LLK_IGNORELIST_UID_PROPERTY, LLK_IGNORELIST_UID_DEFAULT);\n\n    // internal watchdog\n    ::signal(SIGALRM, llkAlarmHandler);\n\n    // kernel hung task configuration? Otherwise leave it as-is\n    if (khtEnable) {\n        // EUID must be AID_ROOT to write to /proc/sys/kernel/ nodes, there\n        // are no capability overrides.  For security reasons we do not want\n        // to run as AID_ROOT.  We may not be able to write them successfully,\n        // we will try, but the least we can do is read the values back to\n        // confirm expectations and report whether configured or not.\n        auto configured = llkWriteStringToFileConfirm(std::to_string(khtTimeout.count()),\n                                                      \"/proc/sys/kernel/hung_task_timeout_secs\");\n        if (configured) {\n            llkWriteStringToFile(\"65535\", \"/proc/sys/kernel/hung_task_warnings\");\n            llkWriteStringToFile(\"65535\", \"/proc/sys/kernel/hung_task_check_count\");\n            configured = llkWriteStringToFileConfirm(\"1\", \"/proc/sys/kernel/hung_task_panic\");\n        }\n        if (configured) {\n            LOG(INFO) << \"[khungtaskd] configured\";\n        } else {\n            LOG(WARNING) << \"[khungtaskd] not configurable\";\n        }\n    }\n\n    bool logConfig = true;\n    if (llkEnable) {\n        if (llkMlockall &&\n            // MCL_ONFAULT pins pages as they fault instead of loading\n            // everything immediately all at once. (Which would be bad,\n            // because as of this writing, we have a lot of mapped pages we\n            // never use.) Old kernels will see MCL_ONFAULT and fail with\n            // EINVAL; we ignore this failure.\n            //\n            // N.B. read the man page for mlockall. MCL_CURRENT | MCL_ONFAULT\n            // pins ⊆ MCL_CURRENT, converging to just MCL_CURRENT as we fault\n            // in pages.\n\n            // CAP_IPC_LOCK required\n            mlockall(MCL_CURRENT | MCL_FUTURE | MCL_ONFAULT) && (errno != EINVAL)) {\n            PLOG(WARNING) << \"mlockall failed \";\n        }\n\n        if (threadname) {\n            pthread_attr_t attr;\n\n            if (!pthread_attr_init(&attr)) {\n                sched_param param;\n\n                memset(&param, 0, sizeof(param));\n                pthread_attr_setschedparam(&attr, &param);\n                pthread_attr_setschedpolicy(&attr, SCHED_BATCH);\n                if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {\n                    pthread_t thread;\n                    if (!pthread_create(&thread, &attr, llkThread, const_cast<char*>(threadname))) {\n                        // wait a second for thread to start\n                        for (auto retry = 50; retry && !llkRunning; --retry) {\n                            ::usleep(20000);\n                        }\n                        logConfig = !llkRunning;  // printed in llkd context?\n                    } else {\n                        LOG(ERROR) << \"failed to spawn llkd thread\";\n                    }\n                } else {\n                    LOG(ERROR) << \"failed to detach llkd thread\";\n                }\n                pthread_attr_destroy(&attr);\n            } else {\n                LOG(ERROR) << \"failed to allocate attibutes for llkd thread\";\n            }\n        }\n    } else {\n        LOG(DEBUG) << \"[khungtaskd] left unconfigured\";\n    }\n    if (logConfig) {\n        llkLogConfig();\n    }\n\n    return llkEnable;\n}\n"
  },
  {
    "path": "llkd/llkd-debuggable.rc",
    "content": "on property:ro.debuggable=1\n    setprop llk.enable ${ro.llk.enable:-0}\n    setprop khungtask.enable ${ro.khungtask.enable:-1}\n\non property:ro.llk.enable=eng\n    setprop llk.enable ${ro.debuggable:-0}\n\non property:ro.khungtask.enable=eng\n    setprop khungtask.enable ${ro.debuggable:-0}\n\nservice llkd-1 /system/bin/llkd\n    class late_start\n    disabled\n    user llkd\n    group llkd readproc\n    capabilities KILL IPC_LOCK SYS_PTRACE DAC_OVERRIDE SYS_ADMIN\n    file /dev/kmsg w\n    file /proc/sysrq-trigger w\n    task_profiles ServiceCapacityLow\n"
  },
  {
    "path": "llkd/llkd.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"llkd.h\"\n\n#include <sched.h>\n#include <sys/prctl.h>\n#include <unistd.h>\n\n#include <chrono>\n\n#include <android-base/logging.h>\n\nusing namespace std::chrono;\n\nint main(int, char**) {\n    prctl(PR_SET_DUMPABLE, 0);\n\n    LOG(INFO) << \"started\";\n\n    bool enabled = llkInit();\n\n    // Would like this policy to be automatic as part of libllkd,\n    // but that would be presumptuous and bad side-effect.\n    struct sched_param param;\n    memset(&param, 0, sizeof(param));\n    sched_setscheduler(0, SCHED_BATCH, &param);\n\n    while (true) {\n        if (enabled) {\n            ::usleep(duration_cast<microseconds>(llkCheck()).count());\n        } else {\n            ::pause();\n        }\n    }\n    // NOTREACHED\n\n    LOG(INFO) << \"exiting\";\n    return 0;\n}\n"
  },
  {
    "path": "llkd/llkd.rc",
    "content": "# eng default for ro.llk.enable and ro.khungtask.enable\non property:ro.debuggable=*\n    setprop llk.enable ${ro.llk.enable:-0}\n    setprop khungtask.enable ${ro.khungtask.enable:-0}\n\non property:ro.llk.enable=true\n    setprop llk.enable true\n\non property:llk.enable=1\n    setprop llk.enable true\n\non property:llk.enable=0\n    setprop llk.enable false\n\non property:ro.khungtask.enable=true\n    setprop khungtask.enable true\n\non property:khungtask.enable=1\n    setprop khungtask.enable true\n\non property:khungtask.enable=0\n    setprop khungtask.enable false\n\n# Configure [khungtaskd]\non property:khungtask.enable=true\n    write /proc/sys/kernel/hung_task_timeout_secs ${ro.khungtask.timeout:-720}\n    write /proc/sys/kernel/hung_task_warnings 65535\n    write /proc/sys/kernel/hung_task_check_count 65535\n    write /proc/sys/kernel/hung_task_panic 1\n\non property:khungtask.enable=false\n    write /proc/sys/kernel/hung_task_panic 0\n\non property:llk.enable=true\n    start llkd-${ro.debuggable:-0}\n\nservice llkd-0 /system/bin/llkd\n    class late_start\n    disabled\n    user llkd\n    group llkd readproc\n    capabilities KILL IPC_LOCK\n    file /dev/kmsg w\n    file /proc/sysrq-trigger w\n    task_profiles ServiceCapacityLow\n"
  },
  {
    "path": "llkd/tests/Android.bp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test {\n    name: \"llkd_unit_test\",\n\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    header_libs: [\n        \"llkd_headers\",\n    ],\n\n    target: {\n        android: {\n            srcs: [\n                \"llkd_test.cpp\",\n            ],\n        },\n    },\n\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n\n    compile_multilib: \"first\",\n}\n"
  },
  {
    "path": "llkd/tests/llkd_test.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fcntl.h>\n#include <signal.h>\n#include <stdint.h>\n#include <sys/prctl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <chrono>\n#include <iostream>\n#include <string>\n\n#include <android-base/properties.h>\n#include <gtest/gtest.h>\n#include <log/log_time.h>  // for MS_PER_SEC and US_PER_SEC\n\n#include \"llkd.h\"\n\nusing namespace std::chrono;\nusing namespace std::chrono_literals;\n\nnamespace {\n\nmilliseconds GetUintProperty(const std::string& key, milliseconds def) {\n    return milliseconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),\n                                                       static_cast<uint64_t>(def.max().count())));\n}\n\nseconds GetUintProperty(const std::string& key, seconds def) {\n    return seconds(android::base::GetUintProperty(key, static_cast<uint64_t>(def.count()),\n                                                  static_cast<uint64_t>(def.max().count())));\n}\n\n// GTEST_LOG_(WARNING) output is fugly, this has much less noise\n// ToDo: look into fixing googletest to produce output that matches style of\n//       all the other status messages, and can switch off __line__ and\n//       __function__ noise\n#define GTEST_LOG_WARNING std::cerr << \"[ WARNING  ] \"\n#define GTEST_LOG_INFO std::cerr << \"[   INFO   ] \"\n\n// Properties is _not_ a high performance ABI!\nvoid rest() {\n    usleep(200000);\n}\n\nvoid execute(const char* command) {\n    if (getuid() || system(command)) {\n        system((std::string(\"su root \") + command).c_str());\n    }\n}\n\nseconds llkdSleepPeriod(char state) {\n    auto default_eng = android::base::GetProperty(LLK_ENABLE_PROPERTY, \"eng\") == \"eng\";\n    auto default_enable = LLK_ENABLE_DEFAULT;\n    default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);\n    if (default_eng) {\n        GTEST_LOG_INFO << LLK_ENABLE_PROPERTY \" defaults to \"\n                       << (default_enable ? \"true\" : \"false\") << \"\\n\";\n    }\n    // Hail Mary hope is unconfigured.\n    if ((GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, LLK_TIMEOUT_MS_DEFAULT) !=\n         duration_cast<milliseconds>(120s)) ||\n        (GetUintProperty(LLK_CHECK_MS_PROPERTY,\n                         LLK_TIMEOUT_MS_DEFAULT / LLK_CHECKS_PER_TIMEOUT_DEFAULT) !=\n         duration_cast<milliseconds>(10s))) {\n        execute(\"stop llkd-0\");\n        execute(\"stop llkd-1\");\n        rest();\n        std::string setprop(\"setprop \");\n        // Manually check that SyS_openat is _added_ to the list when restarted\n        // 4.19+ kernels report __arm64_sys_openat b/147486902\n        execute((setprop + LLK_CHECK_STACK_PROPERTY + \" ,SyS_openat,__arm64_sys_openat\").c_str());\n        rest();\n        execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + \" false\").c_str());\n        rest();\n        execute((setprop + LLK_TIMEOUT_MS_PROPERTY + \" 120000\").c_str());\n        rest();\n        execute((setprop + KHT_TIMEOUT_PROPERTY + \" 130\").c_str());\n        rest();\n        execute((setprop + LLK_CHECK_MS_PROPERTY + \" 10000\").c_str());\n        rest();\n        if (!default_enable) {\n            execute((setprop + LLK_ENABLE_PROPERTY + \" true\").c_str());\n            rest();\n        }\n        execute((setprop + LLK_ENABLE_WRITEABLE_PROPERTY + \" true\").c_str());\n        rest();\n    }\n    default_enable = LLK_ENABLE_DEFAULT;\n    default_enable = android::base::GetBoolProperty(LLK_ENABLE_PROPERTY, default_enable);\n    if (default_enable) {\n        execute(\"start llkd-1\");\n        rest();\n        GTEST_LOG_INFO << \"llkd enabled\\n\";\n    } else {\n        GTEST_LOG_WARNING << \"llkd disabled\\n\";\n    }\n\n    /* KISS follows llk_init() */\n    milliseconds llkTimeoutMs = LLK_TIMEOUT_MS_DEFAULT;\n    seconds khtTimeout = duration_cast<seconds>(\n        llkTimeoutMs * (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT) / LLK_CHECKS_PER_TIMEOUT_DEFAULT);\n    khtTimeout = GetUintProperty(KHT_TIMEOUT_PROPERTY, khtTimeout);\n    llkTimeoutMs =\n        khtTimeout * LLK_CHECKS_PER_TIMEOUT_DEFAULT / (1 + LLK_CHECKS_PER_TIMEOUT_DEFAULT);\n    llkTimeoutMs = GetUintProperty(LLK_TIMEOUT_MS_PROPERTY, llkTimeoutMs);\n    if (llkTimeoutMs < LLK_TIMEOUT_MS_MINIMUM) {\n        llkTimeoutMs = LLK_TIMEOUT_MS_MINIMUM;\n    }\n    milliseconds llkCheckMs = llkTimeoutMs / LLK_CHECKS_PER_TIMEOUT_DEFAULT;\n    auto timeout = GetUintProperty((state == 'Z') ? LLK_Z_TIMEOUT_MS_PROPERTY\n                                                  : (state == 'S') ? LLK_STACK_TIMEOUT_MS_PROPERTY\n                                                                   : LLK_D_TIMEOUT_MS_PROPERTY,\n                                   llkTimeoutMs);\n    if (timeout < LLK_TIMEOUT_MS_MINIMUM) {\n        timeout = LLK_TIMEOUT_MS_MINIMUM;\n    }\n\n    if (llkCheckMs > timeout) {\n        llkCheckMs = timeout;\n    }\n    llkCheckMs = GetUintProperty(LLK_CHECK_MS_PROPERTY, llkCheckMs);\n    timeout += llkCheckMs;\n    auto sec = duration_cast<seconds>(timeout);\n    if (sec == 0s) {\n        ++sec;\n    } else if (sec > 59s) {\n        GTEST_LOG_WARNING << \"llkd is configured for about \" << duration_cast<minutes>(sec).count()\n                          << \" minutes to react\\n\";\n    }\n\n    // 33% margin for the test to naturally timeout waiting for llkd to respond\n    return (sec * 4 + 2s) / 3;\n}\n\ninline void waitForPid(pid_t child_pid) {\n    int wstatus;\n    ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));\n    EXPECT_FALSE(WIFEXITED(wstatus)) << \"[   INFO   ] exit=\" << WEXITSTATUS(wstatus);\n    ASSERT_TRUE(WIFSIGNALED(wstatus));\n    ASSERT_EQ(WTERMSIG(wstatus), SIGKILL);\n}\n\nbool checkKill(const char* reason) {\n    if (android::base::GetBoolProperty(LLK_KILLTEST_PROPERTY, LLK_KILLTEST_DEFAULT)) {\n        return false;\n    }\n    auto bootreason = android::base::GetProperty(\"sys.boot.reason\", \"nothing\");\n    if (bootreason == reason) {\n        GTEST_LOG_INFO << \"Expected test result confirmed \" << reason << \"\\n\";\n        return true;\n    }\n    GTEST_LOG_WARNING << \"Expected test result is \" << reason << \"\\n\";\n\n    // apct adjustment if needed (set LLK_KILLTEST_PROPERTY to \"off\" to allow test)\n    //\n    // if (android::base::GetProperty(LLK_KILLTEST_PROPERTY, \"\") == \"false\") {\n    //     GTEST_LOG_WARNING << \"Bypassing test\\n\";\n    //     return true;\n    // }\n\n    return false;\n}\n\n}  // namespace\n\n// The tests that use this helper are to simulate processes stuck in 'D'\n// state that are experiencing forward scheduled progress. As such the\n// expectation is that llkd will _not_ perform any mitigations. The sleepfor\n// argument helps us set the amount of forward scheduler progress.\nstatic void llkd_driver_ABA(const microseconds sleepfor) {\n    const auto period = llkdSleepPeriod('D');\n    if (period <= sleepfor) {\n        GTEST_LOG_WARNING << \"llkd configuration too short for \"\n                          << duration_cast<milliseconds>(sleepfor).count() << \"ms work cycle\\n\";\n        return;\n    }\n\n    auto child_pid = fork();\n    ASSERT_LE(0, child_pid);\n    int wstatus;\n    if (!child_pid) {\n        auto ratio = period / sleepfor;\n        ASSERT_LT(0, ratio);\n        // vfork() parent is uninterruptable D state waiting for child to exec()\n        while (--ratio > 0) {\n            auto driver_pid = vfork();\n            ASSERT_LE(0, driver_pid);\n            if (driver_pid) {  // parent\n                waitpid(driver_pid, &wstatus, 0);\n                if (!WIFEXITED(wstatus)) {\n                    exit(42);\n                }\n                if (WEXITSTATUS(wstatus) != 42) {\n                    exit(42);\n                }\n            } else {\n                usleep(sleepfor.count());\n                exit(42);\n            }\n        }\n        exit(0);\n    }\n    ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));\n    EXPECT_TRUE(WIFEXITED(wstatus));\n    if (WIFEXITED(wstatus)) {\n        EXPECT_EQ(0, WEXITSTATUS(wstatus));\n    }\n    ASSERT_FALSE(WIFSIGNALED(wstatus)) << \"[   INFO   ] signo=\" << WTERMSIG(wstatus);\n}\n\nTEST(llkd, driver_ABA_fast) {\n    llkd_driver_ABA(5ms);\n}\n\nTEST(llkd, driver_ABA_slow) {\n    llkd_driver_ABA(1s);\n}\n\nTEST(llkd, driver_ABA_glacial) {\n    llkd_driver_ABA(1min);\n}\n\n// Following tests must be last in this file to capture possible errant\n// kernel_panic mitigation failure.\n\n// The following tests simulate processes stick in 'Z' or 'D' state with\n// no forward scheduling progress, but interruptible. As such the expectation\n// is that llkd will perform kill mitigation and not progress to kernel_panic.\n\nTEST(llkd, zombie) {\n    if (checkKill(\"kernel_panic,sysrq,livelock,zombie\")) {\n        return;\n    }\n\n    const auto period = llkdSleepPeriod('Z');\n\n    /* Create a Persistent Zombie Process */\n    pid_t child_pid = fork();\n    ASSERT_LE(0, child_pid);\n    if (!child_pid) {\n        auto zombie_pid = fork();\n        ASSERT_LE(0, zombie_pid);\n        if (!zombie_pid) {\n            sleep(1);\n            exit(0);\n        }\n        sleep(period.count());\n        exit(42);\n    }\n\n    waitForPid(child_pid);\n}\n\nTEST(llkd, driver) {\n    if (checkKill(\"kernel_panic,sysrq,livelock,driver\")) {\n        return;\n    }\n\n    const auto period = llkdSleepPeriod('D');\n\n    /* Create a Persistent Device Process */\n    auto child_pid = fork();\n    ASSERT_LE(0, child_pid);\n    if (!child_pid) {\n        // vfork() parent is uninterruptable D state waiting for child to exec()\n        auto driver_pid = vfork();\n        ASSERT_LE(0, driver_pid);\n        sleep(period.count());\n        exit(driver_pid ? 42 : 0);\n    }\n\n    waitForPid(child_pid);\n}\n\nTEST(llkd, sleep) {\n    if (checkKill(\"kernel_panic,sysrq,livelock,sleeping\")) {\n        return;\n    }\n    if (!android::base::GetBoolProperty(\"ro.debuggable\", false)) {\n        GTEST_LOG_WARNING << \"Features not available on user builds\\n\";\n    }\n\n    const auto period = llkdSleepPeriod('S');\n\n    /* Create a Persistent SyS_openat for single-ended pipe */\n    static constexpr char stack_pipe_file[] = \"/dev/stack_pipe_file\";\n    unlink(stack_pipe_file);\n    auto pipe_ret = mknod(stack_pipe_file, S_IFIFO | 0666, 0);\n    ASSERT_LE(0, pipe_ret);\n\n    auto child_pid = fork();\n    ASSERT_LE(0, child_pid);\n    if (!child_pid) {\n        child_pid = fork();\n        ASSERT_LE(0, child_pid);\n        if (!child_pid) {\n            sleep(period.count());\n            auto fd = open(stack_pipe_file, O_RDONLY | O_CLOEXEC);\n            close(fd);\n            exit(0);\n        } else {\n            auto fd = open(stack_pipe_file, O_WRONLY | O_CLOEXEC);\n            close(fd);\n            exit(42);\n        }\n    }\n\n    waitForPid(child_pid);\n\n    unlink(stack_pipe_file);\n}\n\n// b/120983740\nTEST(llkd, adbd_and_setsid) {\n    if (checkKill(\"kernel_panic,sysrq,livelock,zombie\")) {\n        return;\n    }\n    const auto period = llkdSleepPeriod('S');\n\n    // expect llkd.zombie to trigger, but not for adbd&[setsid]\n    // Create a Persistent Zombie setsid Process\n    pid_t child_pid = fork();\n    ASSERT_LE(0, child_pid);\n    if (!child_pid) {\n        prctl(PR_SET_NAME, \"adbd\");\n        auto zombie_pid = fork();\n        ASSERT_LE(0, zombie_pid);\n        if (!zombie_pid) {\n            prctl(PR_SET_NAME, \"setsid\");\n            sleep(1);\n            exit(0);\n        }\n        sleep(period.count());\n        exit(42);\n    }\n\n    // Reverse of waitForPid, do _not_ expect kill\n    int wstatus;\n    ASSERT_LE(0, waitpid(child_pid, &wstatus, 0));\n    EXPECT_TRUE(WIFEXITED(wstatus));\n    if (WIFEXITED(wstatus)) {\n        EXPECT_EQ(42, WEXITSTATUS(wstatus));\n    }\n    ASSERT_FALSE(WIFSIGNALED(wstatus)) << \"[   INFO   ] signo=\" << WTERMSIG(wstatus);\n}\n"
  },
  {
    "path": "mini_keyctl/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libmini_keyctl_static\",\n    srcs: [\n        \"mini_keyctl_utils.cpp\"\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libkeyutils\",\n    ],\n    cflags: [\"-Werror\", \"-Wall\", \"-Wextra\"],\n    export_include_dirs: [\".\"],\n    recovery_available: true,\n}\n\ncc_binary {\n    name: \"mini-keyctl\",\n    srcs: [\n        \"mini_keyctl.cpp\",\n    ],\n    static_libs: [\n        \"libmini_keyctl_static\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libkeyutils\",\n    ],\n    cflags: [\"-Werror\", \"-Wall\", \"-Wextra\"],\n}\n"
  },
  {
    "path": "mini_keyctl/OWNERS",
    "content": "ebiggers@google.com\njeffv@google.com\njiyong@google.com\nvictorhsieh@google.com\n"
  },
  {
    "path": "mini_keyctl/mini_keyctl.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * A tool loads keys to keyring.\n */\n\n#include <dirent.h>\n#include <errno.h>\n#include <error.h>\n#include <stdio.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <iostream>\n#include <iterator>\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/parseint.h>\n#include <keyutils.h>\n#include <mini_keyctl_utils.h>\n\nconstexpr int kMaxCertSize = 4096;\n\nstatic void Usage(int exit_code) {\n  fprintf(stderr, \"usage: mini-keyctl <action> [args,]\\n\");\n  fprintf(stderr, \"       mini-keyctl add <type> <desc> <data> <keyring>\\n\");\n  fprintf(stderr, \"       mini-keyctl padd <type> <desc> <keyring>\\n\");\n  fprintf(stderr, \"       mini-keyctl unlink <key> <keyring>\\n\");\n  fprintf(stderr, \"       mini-keyctl restrict_keyring <keyring>\\n\");\n  fprintf(stderr, \"       mini-keyctl security <key>\\n\");\n  _exit(exit_code);\n}\n\nstatic key_serial_t parseKeyOrDie(const char* str) {\n  key_serial_t key;\n  if (!android::base::ParseInt(str, &key)) {\n    error(1 /* exit code */, 0 /* errno */, \"Unparsable key: '%s'\\n\", str);\n  }\n  return key;\n}\n\nint Unlink(key_serial_t key, const std::string& keyring) {\n  key_serial_t keyring_id = android::GetKeyringId(keyring);\n  if (keyctl_unlink(key, keyring_id) < 0) {\n    error(1, errno, \"Failed to unlink key %x from keyring %s\", key, keyring.c_str());\n    return 1;\n  }\n  return 0;\n}\n\nint Add(const std::string& type, const std::string& desc, const std::string& data,\n        const std::string& keyring) {\n  if (data.size() > kMaxCertSize) {\n    error(1, 0, \"Certificate too large\");\n    return 1;\n  }\n\n  key_serial_t keyring_id = android::GetKeyringId(keyring);\n  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);\n\n  if (key < 0) {\n    error(1, errno, \"Failed to add key\");\n    return 1;\n  }\n\n  std::cout << key << std::endl;\n  return 0;\n}\n\nint Padd(const std::string& type, const std::string& desc, const std::string& keyring) {\n  key_serial_t keyring_id = android::GetKeyringId(keyring);\n\n  // read from stdin to get the certificates\n  std::istreambuf_iterator<char> begin(std::cin), end;\n  std::string data(begin, end);\n\n  if (data.size() > kMaxCertSize) {\n    error(1, 0, \"Certificate too large\");\n    return 1;\n  }\n\n  key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);\n\n  if (key < 0) {\n    error(1, errno, \"Failed to add key\");\n    return 1;\n  }\n\n  std::cout << key << std::endl;\n  return 0;\n}\n\nint RestrictKeyring(const std::string& keyring) {\n  key_serial_t keyring_id = android::GetKeyringId(keyring);\n  if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {\n    error(1, errno, \"Cannot restrict keyring '%s'\", keyring.c_str());\n    return 1;\n  }\n  return 0;\n}\n\nstd::string RetrieveSecurityContext(key_serial_t key) {\n  // Simply assume this size is enough in practice.\n  const int kMaxSupportedSize = 256;\n  std::string context;\n  context.resize(kMaxSupportedSize);\n  long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);\n  if (retval < 0) {\n    error(1, errno, \"Cannot get security context of key %x\", key);\n    return std::string();\n  }\n  if (retval > kMaxSupportedSize) {\n    error(1, 0, \"The key has unexpectedly long security context than %d\", kMaxSupportedSize);\n    return std::string();\n  }\n  context.resize(retval);\n  return context;\n}\n\nint main(int argc, const char** argv) {\n  if (argc < 2) Usage(1);\n  const std::string action = argv[1];\n\n  if (action == \"add\") {\n    if (argc != 6) Usage(1);\n    std::string type = argv[2];\n    std::string desc = argv[3];\n    std::string data = argv[4];\n    std::string keyring = argv[5];\n    return Add(type, desc, data, keyring);\n  } else if (action == \"padd\") {\n    if (argc != 5) Usage(1);\n    std::string type = argv[2];\n    std::string desc = argv[3];\n    std::string keyring = argv[4];\n    return Padd(type, desc, keyring);\n  } else if (action == \"restrict_keyring\") {\n    if (argc != 3) Usage(1);\n    std::string keyring = argv[2];\n    return RestrictKeyring(keyring);\n  } else if (action == \"unlink\") {\n    if (argc != 4) Usage(1);\n    key_serial_t key = parseKeyOrDie(argv[2]);\n    const std::string keyring = argv[3];\n    return Unlink(key, keyring);\n  } else if (action == \"security\") {\n    if (argc != 3) Usage(1);\n    const char* key_str = argv[2];\n    key_serial_t key = parseKeyOrDie(key_str);\n    std::string context = RetrieveSecurityContext(key);\n    if (context.empty()) {\n      perror(key_str);\n      return 1;\n    }\n    fprintf(stderr, \"%s\\n\", context.c_str());\n    return 0;\n  } else {\n    fprintf(stderr, \"Unrecognized action: %s\\n\", action.c_str());\n    Usage(1);\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "mini_keyctl/mini_keyctl_utils.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <mini_keyctl_utils.h>\n\n#include <fstream>\n#include <iterator>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include <android-base/logging.h>\n#include <android-base/parseint.h>\n\nnamespace android {\n\nnamespace {\n\nstd::vector<std::string> SplitBySpace(const std::string& s) {\n  std::istringstream iss(s);\n  return std::vector<std::string>{std::istream_iterator<std::string>{iss},\n                                  std::istream_iterator<std::string>{}};\n}\n\n}  // namespace\n\n// Find the keyring id. request_key(2) only finds keys in the process, session or thread keyring\n// hierarchy, but not internal keyring of a kernel subsystem (e.g. .fs-verity). To support all\n// cases, this function looks up a keyring's ID by parsing /proc/keys. The keyring description may\n// contain other information in the descritption section depending on the key type, only the first\n// word in the keyring description is used for searching.\nkey_serial_t GetKeyringId(const std::string& keyring_desc) {\n  // If the keyring id is already a hex number, directly convert it to keyring id\n  key_serial_t keyring_id;\n  if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {\n    return keyring_id;\n  }\n\n  // Only keys allowed by SELinux rules will be shown here.\n  std::ifstream proc_keys_file(\"/proc/keys\");\n  if (!proc_keys_file.is_open()) {\n    PLOG(ERROR) << \"Failed to open /proc/keys\";\n    return -1;\n  }\n\n  std::string line;\n  while (getline(proc_keys_file, line)) {\n    std::vector<std::string> tokens = SplitBySpace(line);\n    if (tokens.size() < 9) {\n      continue;\n    }\n    std::string key_id = \"0x\" + tokens[0];\n    std::string key_type = tokens[7];\n    // The key description may contain space.\n    std::string key_desc_prefix = tokens[8];\n    // The prefix has a \":\" at the end\n    std::string key_desc_pattern = keyring_desc + \":\";\n    if (key_type != \"keyring\" || key_desc_prefix != key_desc_pattern) {\n      continue;\n    }\n    if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {\n      LOG(ERROR) << \"Unexpected key format in /proc/keys: \" << key_id;\n      return -1;\n    }\n    return keyring_id;\n  }\n  return -1;\n}\n\n}  // namespace android\n"
  },
  {
    "path": "mini_keyctl/mini_keyctl_utils.h",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_\n#define _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_\n\n#include <string>\n\n#include <keyutils.h>\n\nnamespace android {\nkey_serial_t GetKeyringId(const std::string& keyring_desc);\n}  // namespace android\n\n#endif  // _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_\n"
  },
  {
    "path": "mkbootfs/Android.bp",
    "content": "// Copyright 2005 The Android Open Source Project\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary_host {\n    name: \"mkbootfs\",\n    srcs: [\"mkbootfs.cpp\"],\n    cflags: [\"-Werror\"],\n    static_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"liblog\",\n    ],\n    dist: {\n        targets: [\"dist_files\"],\n    },\n}\n"
  },
  {
    "path": "mkbootfs/mkbootfs.cpp",
    "content": "\n#include <ctype.h>\n#include <dirent.h>\n#include <err.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/sysmacros.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <linux/kdev_t.h>\n\n#include <private/android_filesystem_config.h>\n#include <private/fs_config.h>\n\n#include <android-base/file.h>\n#include <string>\n\n/* NOTES\n**\n** - see https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt\n**   for an explanation of this file format\n** - dotfiles are ignored\n** - directories named 'root' are ignored\n*/\n\nstruct fs_config_entry {\n    char* name;\n    int uid, gid, mode;\n};\n\nstatic struct fs_config_entry* canned_config = NULL;\nstatic const char* target_out_path = NULL;\n\n#define TRAILER \"TRAILER!!!\"\n\nstatic int total_size = 0;\n\nstatic void fix_stat(const char *path, struct stat *s)\n{\n    uint64_t capabilities;\n    if (canned_config) {\n        // Use the list of file uid/gid/modes loaded from the file\n        // given with -f.\n\n        struct fs_config_entry* empty_path_config = NULL;\n        struct fs_config_entry* p;\n        for (p = canned_config; p->name; ++p) {\n            if (!p->name[0]) {\n                empty_path_config = p;\n            }\n            if (strcmp(p->name, path) == 0) {\n                s->st_uid = p->uid;\n                s->st_gid = p->gid;\n                s->st_mode = p->mode | (s->st_mode & ~07777);\n                return;\n            }\n        }\n        s->st_uid = empty_path_config->uid;\n        s->st_gid = empty_path_config->gid;\n        s->st_mode = empty_path_config->mode | (s->st_mode & ~07777);\n    } else {\n        // Use the compiled-in fs_config() function.\n        unsigned st_mode = s->st_mode;\n        int is_dir = S_ISDIR(s->st_mode) || strcmp(path, TRAILER) == 0;\n        fs_config(path, is_dir, target_out_path, &s->st_uid, &s->st_gid, &st_mode, &capabilities);\n        s->st_mode = (typeof(s->st_mode)) st_mode;\n    }\n\n    if (S_ISREG(s->st_mode) || S_ISDIR(s->st_mode) || S_ISLNK(s->st_mode)) {\n        s->st_rdev = 0;\n    }\n}\n\nstatic void _eject(struct stat *s, const char *out, int olen, char *data, unsigned datasize)\n{\n    // Nothing is special about this value, just picked something in the\n    // approximate range that was being used already, and avoiding small\n    // values which may be special.\n    static unsigned next_inode = 300000;\n\n    while(total_size & 3) {\n        total_size++;\n        putchar(0);\n    }\n\n    fix_stat(out, s);\n//    fprintf(stderr, \"_eject %s: mode=0%o\\n\", out, s->st_mode);\n\n    printf(\"%06x%08x%08x%08x%08x%08x%08x\"\n           \"%08x%08x%08x%08x%08x%08x%08x%s%c\",\n           0x070701,\n           next_inode++,  //  s.st_ino,\n           s->st_mode,\n           0, // s.st_uid,\n           0, // s.st_gid,\n           1, // s.st_nlink,\n           0, // s.st_mtime,\n           datasize,\n           0, // volmajor\n           0, // volminor\n           major(s->st_rdev),\n           minor(s->st_rdev),\n           olen + 1,\n           0,\n           out,\n           0\n           );\n\n    total_size += 6 + 8*13 + olen + 1;\n\n    if(strlen(out) != (unsigned int)olen) errx(1, \"ACK!\");\n\n    while(total_size & 3) {\n        total_size++;\n        putchar(0);\n    }\n\n    if(datasize) {\n        fwrite(data, datasize, 1, stdout);\n        total_size += datasize;\n    }\n}\n\nstatic void _eject_trailer()\n{\n    struct stat s;\n    memset(&s, 0, sizeof(s));\n    _eject(&s, TRAILER, 10, 0, 0);\n\n    while(total_size & 0xff) {\n        total_size++;\n        putchar(0);\n    }\n}\n\nstatic void _archive(char *in, char *out, int ilen, int olen);\n\nstatic int compare(const void* a, const void* b) {\n  return strcmp(*(const char**)a, *(const char**)b);\n}\n\nstatic void _archive_dir(char *in, char *out, int ilen, int olen)\n{\n    int i, t;\n    struct dirent *de;\n\n    DIR* d = opendir(in);\n    if (d == NULL) err(1, \"cannot open directory '%s'\", in);\n\n    // TODO: switch to std::vector\n    int size = 32;\n    int entries = 0;\n    char** names = (char**) malloc(size * sizeof(char*));\n    if (names == NULL) {\n      errx(1, \"failed to allocate dir names array (size %d)\", size);\n    }\n\n    while((de = readdir(d)) != 0){\n            /* xxx: feature? maybe some dotfiles are okay */\n        if(de->d_name[0] == '.') continue;\n\n            /* xxx: hack. use a real exclude list */\n        if(!strcmp(de->d_name, \"root\")) continue;\n\n        if (entries >= size) {\n          size *= 2;\n          names = (char**) realloc(names, size * sizeof(char*));\n          if (names == NULL) {\n            errx(1, \"failed to reallocate dir names array (size %d)\", size);\n          }\n        }\n        names[entries] = strdup(de->d_name);\n        if (names[entries] == NULL) {\n          errx(1, \"failed to strdup name \\\"%s\\\"\", de->d_name);\n        }\n        ++entries;\n    }\n\n    qsort(names, entries, sizeof(char*), compare);\n\n    for (i = 0; i < entries; ++i) {\n        t = strlen(names[i]);\n        in[ilen] = '/';\n        memcpy(in + ilen + 1, names[i], t + 1);\n\n        if(olen > 0) {\n            out[olen] = '/';\n            memcpy(out + olen + 1, names[i], t + 1);\n            _archive(in, out, ilen + t + 1, olen + t + 1);\n        } else {\n            memcpy(out, names[i], t + 1);\n            _archive(in, out, ilen + t + 1, t);\n        }\n\n        in[ilen] = 0;\n        out[olen] = 0;\n\n        free(names[i]);\n    }\n    free(names);\n\n    closedir(d);\n}\n\nstatic void _archive(char *in, char *out, int ilen, int olen)\n{\n    struct stat s;\n    if(lstat(in, &s)) err(1, \"could not stat '%s'\", in);\n\n    if(S_ISREG(s.st_mode)){\n        std::string content;\n        if (!android::base::ReadFileToString(in, &content)) {\n            err(1, \"cannot read '%s'\", in);\n        }\n\n        _eject(&s, out, olen, content.data(), content.size());\n    } else if(S_ISDIR(s.st_mode)) {\n        _eject(&s, out, olen, 0, 0);\n        _archive_dir(in, out, ilen, olen);\n    } else if(S_ISLNK(s.st_mode)) {\n        char buf[1024];\n        int size;\n        size = readlink(in, buf, 1024);\n        if(size < 0) err(1, \"cannot read symlink '%s'\", in);\n        _eject(&s, out, olen, buf, size);\n    } else if(S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode) ||\n              S_ISFIFO(s.st_mode) || S_ISSOCK(s.st_mode)) {\n        _eject(&s, out, olen, NULL, 0);\n    } else {\n        errx(1, \"Unknown '%s' (mode %d)?\", in, s.st_mode);\n    }\n}\n\nstatic void archive(const char* start, const char* prefix) {\n    char in[8192];\n    char out[8192];\n\n    strcpy(in, start);\n    strcpy(out, prefix);\n\n    _archive_dir(in, out, strlen(in), strlen(out));\n}\n\nstatic void read_canned_config(char* filename)\n{\n    int allocated = 8;\n    int used = 0;\n\n    canned_config =\n        (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));\n\n    FILE* fp = fopen(filename, \"r\");\n    if (fp == NULL) err(1, \"failed to open canned file '%s'\", filename);\n\n    char* line = NULL;\n    size_t allocated_len;\n    while (getline(&line, &allocated_len, fp) != -1) {\n        if (!line[0]) break;\n        if (used >= allocated) {\n            allocated *= 2;\n            canned_config = (struct fs_config_entry*)realloc(\n                canned_config, allocated * sizeof(struct fs_config_entry));\n            if (canned_config == NULL) errx(1, \"failed to reallocate memory\");\n        }\n\n        struct fs_config_entry* cc = canned_config + used;\n\n        if (isspace(line[0])) {\n            cc->name = strdup(\"\");\n            cc->uid = atoi(strtok(line, \" \\n\"));\n        } else {\n            cc->name = strdup(strtok(line, \" \\n\"));\n            cc->uid = atoi(strtok(NULL, \" \\n\"));\n        }\n        cc->gid = atoi(strtok(NULL, \" \\n\"));\n        cc->mode = strtol(strtok(NULL, \" \\n\"), NULL, 8);\n        ++used;\n    }\n    if (used >= allocated) {\n        ++allocated;\n        canned_config = (struct fs_config_entry*)realloc(\n            canned_config, allocated * sizeof(struct fs_config_entry));\n        if (canned_config == NULL) errx(1, \"failed to reallocate memory\");\n    }\n    canned_config[used].name = NULL;\n\n    free(line);\n    fclose(fp);\n}\n\nstatic void devnodes_desc_error(const char* filename, unsigned long line_num,\n                              const char* msg)\n{\n    errx(1, \"failed to read nodes desc file '%s' line %lu: %s\", filename, line_num, msg);\n}\n\nstatic int append_devnodes_desc_dir(char* path, char* args)\n{\n    struct stat s;\n\n    if (sscanf(args, \"%o %d %d\", &s.st_mode, &s.st_uid, &s.st_gid) != 3) return -1;\n\n    s.st_mode |= S_IFDIR;\n\n    _eject(&s, path, strlen(path), NULL, 0);\n\n    return 0;\n}\n\nstatic int append_devnodes_desc_nod(char* path, char* args)\n{\n    int minor, major;\n    struct stat s;\n    char dev;\n\n    if (sscanf(args, \"%o %d %d %c %d %d\", &s.st_mode, &s.st_uid, &s.st_gid,\n               &dev, &major, &minor) != 6) return -1;\n\n    s.st_rdev = MKDEV(major, minor);\n    switch (dev) {\n    case 'b':\n        s.st_mode |= S_IFBLK;\n        break;\n    case 'c':\n        s.st_mode |= S_IFCHR;\n        break;\n    default:\n        return -1;\n    }\n\n    _eject(&s, path, strlen(path), NULL, 0);\n\n    return 0;\n}\n\nstatic void append_devnodes_desc(const char* filename)\n{\n    FILE* fp = fopen(filename, \"re\");\n    if (!fp) err(1, \"failed to open nodes description file '%s'\", filename);\n\n    unsigned long line_num = 0;\n\n    char* line = NULL;\n    size_t allocated_len;\n    while (getline(&line, &allocated_len, fp) != -1) {\n        char *type, *path, *args;\n\n        line_num++;\n\n        if (*line == '#') continue;\n\n        if (!(type = strtok(line, \" \\t\"))) {\n            devnodes_desc_error(filename, line_num, \"a type is missing\");\n        }\n\n        if (*type == '\\n') continue;\n\n        if (!(path = strtok(NULL, \" \\t\"))) {\n            devnodes_desc_error(filename, line_num, \"a path is missing\");\n        }\n\n        if (!(args = strtok(NULL, \"\\n\"))) {\n            devnodes_desc_error(filename, line_num, \"args are missing\");\n        }\n\n        if (!strcmp(type, \"dir\")) {\n            if (append_devnodes_desc_dir(path, args)) {\n                devnodes_desc_error(filename, line_num, \"bad arguments for dir\");\n            }\n        } else if (!strcmp(type, \"nod\")) {\n            if (append_devnodes_desc_nod(path, args)) {\n                devnodes_desc_error(filename, line_num, \"bad arguments for nod\");\n            }\n        } else {\n            devnodes_desc_error(filename, line_num, \"type unknown\");\n        }\n    }\n\n    free(line);\n    fclose(fp);\n}\n\nstatic const struct option long_options[] = {\n    { \"dirname\",    required_argument,  NULL,   'd' },\n    { \"file\",       required_argument,  NULL,   'f' },\n    { \"help\",       no_argument,        NULL,   'h' },\n    { \"nodes\",      required_argument,  NULL,   'n' },\n    { NULL,         0,                  NULL,   0   },\n};\n\nstatic void usage(void)\n{\n    fprintf(stderr,\n            \"Usage: mkbootfs [-n FILE] [-d DIR|-f FILE] DIR...\\n\"\n            \"\\n\"\n            \"\\t-d, --dirname=DIR: fs-config directory\\n\"\n            \"\\t-f, --file=FILE: Canned configuration file\\n\"\n            \"\\t-h, --help: Print this help\\n\"\n            \"\\t-n, --nodes=FILE: Dev nodes description file\\n\"\n            \"\\n\"\n            \"Dev nodes description:\\n\"\n            \"\\t[dir|nod] [perms] [uid] [gid] [c|b] [major] [minor]\\n\"\n            \"\\tExample:\\n\"\n            \"\\t\\t# My device nodes\\n\"\n            \"\\t\\tdir dev 0755 0 0\\n\"\n            \"\\t\\tnod dev/null 0600 0 0 c 1 3\\n\"\n    );\n}\n\nint main(int argc, char *argv[])\n{\n    int opt, unused;\n\n    while ((opt = getopt_long(argc, argv, \"hd:f:n:\", long_options, &unused)) != -1) {\n        switch (opt) {\n        case 'd':\n            target_out_path = argv[optind - 1];\n            break;\n        case 'f':\n            read_canned_config(argv[optind - 1]);\n            break;\n        case 'h':\n            usage();\n            return 0;\n        case 'n':\n            append_devnodes_desc(argv[optind - 1]);\n            break;\n        default:\n            usage();\n            errx(1, \"Unknown option %s\", argv[optind - 1]);\n        }\n    }\n\n    int num_dirs = argc - optind;\n    argv += optind;\n\n    while (num_dirs-- > 0){\n        char *x = strchr(*argv, '=');\n        if (x != nullptr) {\n            *x++ = '\\0';\n        }\n        archive(*argv, x ?: \"\");\n\n        argv++;\n    }\n\n    _eject_trailer();\n\n    return 0;\n}\n"
  },
  {
    "path": "overlay_remounter/Android.bp",
    "content": "//\n// Copyright (C) 2025 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\ncc_binary {\n    name: \"overlay_remounter\",\n    srcs: [\n        \"overlay_remounter.cpp\",\n    ],\n    cflags: [\n        \"-D_FILE_OFFSET_BITS=64\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    system_shared_libs: [],\n    static_executable: true,\n    install_in_xbin: true,\n}\n"
  },
  {
    "path": "overlay_remounter/overlay_remounter.cpp",
    "content": "/*\n * Copyright (C) 2025 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <sys/mount.h>\n#include <unistd.h>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n\nint main(int /*argc*/, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n    LOG(INFO) << \"Overlay remounter will remount all overlay mount points in the overlay_remounter \"\n                 \"domain\";\n\n    // Remount ouerlayfs\n    std::string contents;\n    auto result = android::base::ReadFileToString(\"/proc/mounts\", &contents, true);\n\n    auto lines = android::base::Split(contents, \"\\n\");\n    for (auto const& line : lines) {\n        if (!android::base::StartsWith(line, \"overlay\")) {\n            continue;\n        }\n        auto bits = android::base::Split(line, \" \");\n        if (int result = umount(bits[1].c_str()); result == -1) {\n            PLOG(FATAL) << \"umount FAILED: \" << bits[1];\n        }\n        std::string options;\n        for (auto const& option : android::base::Split(bits[3], \",\")) {\n            if (option == \"ro\" || option == \"seclabel\" || option == \"noatime\") continue;\n            if (!options.empty()) options += ',';\n            options += option;\n        }\n        result = mount(\"overlay\", bits[1].c_str(), \"overlay\", MS_RDONLY | MS_NOATIME,\n                       options.c_str());\n        if (result == 0) {\n            LOG(INFO) << \"mount succeeded: \" << bits[1] << \" \" << options;\n        } else {\n            PLOG(FATAL) << \"mount FAILED: \" << bits[1] << \" \" << bits[3];\n        }\n    }\n\n    const char* path = \"/system/bin/init\";\n    const char* args[] = {path, \"second_stage\", nullptr};\n    execv(path, const_cast<char**>(args));\n\n    // execv() only returns if an error happened, in which case we\n    // panic and never return from this function.\n    PLOG(FATAL) << \"execv(\\\"\" << path << \"\\\") failed\";\n\n    return 1;\n}\n"
  },
  {
    "path": "property_service/OWNERS",
    "content": "include platform/system/core:/janitors/OWNERS\n"
  },
  {
    "path": "property_service/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"propertyinfoserializer_tests\"\n    }\n  ],\n  \"hwasan-presubmit\": [\n    {\n      \"name\": \"propertyinfoserializer_tests\"\n    }\n  ]\n}\n"
  },
  {
    "path": "property_service/libpropertyinfoparser/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libpropertyinfoparser\",\n    host_supported: true,\n    vendor_available: true,\n    ramdisk_available: true,\n    vendor_ramdisk_available: true,\n    recovery_available: true,\n    native_bridge_supported: true,\n    srcs: [\"property_info_parser.cpp\"],\n\n    cppflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n    stl: \"none\",\n    target: {\n        bionic: {\n            system_shared_libs: [],\n            header_libs: [\"libc_headers\"],\n        },\n    },\n    export_include_dirs: [\"include\"],\n    apex_available: [\n        \"//apex_available:platform\",\n        \"com.android.runtime\",\n    ],\n}\n"
  },
  {
    "path": "property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#ifndef PROPERTY_INFO_PARSER_H\n#define PROPERTY_INFO_PARSER_H\n\n#include <stdint.h>\n#include <stdlib.h>\n\nstatic constexpr char PROP_TREE_FILE[] = \"/dev/__properties__/property_info\";\n\nnamespace android {\nnamespace properties {\n\n// The below structs intentionally do not end with char name[0] or other tricks to allocate\n// with a dynamic size, such that they can be added onto in the future without breaking\n// backwards compatibility.\nstruct PropertyEntry {\n  uint32_t name_offset;\n  uint32_t namelen;\n\n  // This is the context match for this node_; ~0u if it doesn't correspond to any.\n  uint32_t context_index;\n  // This is the type for this node_; ~0u if it doesn't correspond to any.\n  uint32_t type_index;\n};\n\nstruct TrieNodeInternal {\n  // This points to a property entry struct, which includes the name for this node\n  uint32_t property_entry;\n\n  // Children are a sorted list of child nodes_; binary search them.\n  uint32_t num_child_nodes;\n  uint32_t child_nodes;\n\n  // Prefixes are terminating prefix matches at this node, sorted longest to smallest\n  // Take the first match sequentially found with StartsWith().\n  uint32_t num_prefixes;\n  uint32_t prefix_entries;\n\n  // Exact matches are a sorted list of exact matches at this node_; binary search them.\n  uint32_t num_exact_matches;\n  uint32_t exact_match_entries;\n};\n\nstruct PropertyInfoAreaHeader {\n  // The current version of this data as created by property service.\n  uint32_t current_version;\n  // The lowest version of libc that can properly parse this data.\n  uint32_t minimum_supported_version;\n  uint32_t size;\n  uint32_t contexts_offset;\n  uint32_t types_offset;\n  uint32_t root_offset;\n};\n\nclass SerializedData {\n public:\n  uint32_t size() const {\n    return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base_)->size;\n  }\n\n  const char* c_string(uint32_t offset) const {\n    if (offset != 0 && offset > size()) return nullptr;\n    return static_cast<const char*>(data_base_ + offset);\n  }\n\n  const uint32_t* uint32_array(uint32_t offset) const {\n    if (offset != 0 && offset > size()) return nullptr;\n    return reinterpret_cast<const uint32_t*>(data_base_ + offset);\n  }\n\n  uint32_t uint32(uint32_t offset) const {\n    if (offset != 0 && offset > size()) return ~0u;\n    return *reinterpret_cast<const uint32_t*>(data_base_ + offset);\n  }\n\n  const char* data_base() const { return data_base_; }\n\n private:\n  const char data_base_[0];\n};\n\nclass TrieNode {\n public:\n  TrieNode() : serialized_data_(nullptr), trie_node_base_(nullptr) {}\n  TrieNode(const SerializedData* data_base, const TrieNodeInternal* trie_node_base)\n      : serialized_data_(data_base), trie_node_base_(trie_node_base) {}\n\n  const char* name() const {\n    return serialized_data_->c_string(node_property_entry()->name_offset);\n  }\n\n  uint32_t context_index() const { return node_property_entry()->context_index; }\n  uint32_t type_index() const { return node_property_entry()->type_index; }\n\n  uint32_t num_child_nodes() const { return trie_node_base_->num_child_nodes; }\n  TrieNode child_node(int n) const {\n    uint32_t child_node_offset = serialized_data_->uint32_array(trie_node_base_->child_nodes)[n];\n    const TrieNodeInternal* trie_node_base =\n        reinterpret_cast<const TrieNodeInternal*>(serialized_data_->data_base() + child_node_offset);\n    return TrieNode(serialized_data_, trie_node_base);\n  }\n\n  bool FindChildForString(const char* input, uint32_t namelen, TrieNode* child) const;\n\n  uint32_t num_prefixes() const { return trie_node_base_->num_prefixes; }\n  const PropertyEntry* prefix(int n) const {\n    uint32_t prefix_entry_offset =\n        serialized_data_->uint32_array(trie_node_base_->prefix_entries)[n];\n    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +\n                                                  prefix_entry_offset);\n  }\n\n  uint32_t num_exact_matches() const { return trie_node_base_->num_exact_matches; }\n  const PropertyEntry* exact_match(int n) const {\n    uint32_t exact_match_entry_offset =\n        serialized_data_->uint32_array(trie_node_base_->exact_match_entries)[n];\n    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +\n                                                  exact_match_entry_offset);\n  }\n\n private:\n  const PropertyEntry* node_property_entry() const {\n    return reinterpret_cast<const PropertyEntry*>(serialized_data_->data_base() +\n                                                  trie_node_base_->property_entry);\n  }\n\n  const SerializedData* serialized_data_;\n  const TrieNodeInternal* trie_node_base_;\n};\n\nclass PropertyInfoArea : private SerializedData {\n public:\n  void GetPropertyInfoIndexes(const char* name, uint32_t* context_index, uint32_t* type_index) const;\n  void GetPropertyInfo(const char* property, const char** context, const char** type) const;\n\n  int FindContextIndex(const char* context) const;\n  int FindTypeIndex(const char* type) const;\n\n  const char* context(uint32_t index) const {\n    uint32_t context_array_size_offset = contexts_offset();\n    const uint32_t* context_array = uint32_array(context_array_size_offset + sizeof(uint32_t));\n    return data_base() + context_array[index];\n  }\n\n  const char* type(uint32_t index) const {\n    uint32_t type_array_size_offset = types_offset();\n    const uint32_t* type_array = uint32_array(type_array_size_offset + sizeof(uint32_t));\n    return data_base() + type_array[index];\n  }\n\n  uint32_t current_version() const { return header()->current_version; }\n  uint32_t minimum_supported_version() const { return header()->minimum_supported_version; }\n\n  uint32_t size() const { return SerializedData::size(); }\n\n  uint32_t num_contexts() const { return uint32_array(contexts_offset())[0]; }\n  uint32_t num_types() const { return uint32_array(types_offset())[0]; }\n\n  TrieNode root_node() const { return trie(header()->root_offset); }\n\n private:\n  void CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,\n                        uint32_t* context_index, uint32_t* type_index) const;\n\n  const PropertyInfoAreaHeader* header() const {\n    return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base());\n  }\n  uint32_t contexts_offset() const { return header()->contexts_offset; }\n  uint32_t contexts_array_offset() const { return contexts_offset() + sizeof(uint32_t); }\n  uint32_t types_offset() const { return header()->types_offset; }\n  uint32_t types_array_offset() const { return types_offset() + sizeof(uint32_t); }\n\n  TrieNode trie(uint32_t offset) const {\n    if (offset != 0 && offset > size()) return TrieNode();\n    const TrieNodeInternal* trie_node_base =\n        reinterpret_cast<const TrieNodeInternal*>(data_base() + offset);\n    return TrieNode(this, trie_node_base);\n  }\n};\n\n// This is essentially a smart pointer for read only mmap region for property contexts.\nclass PropertyInfoAreaFile {\n public:\n  PropertyInfoAreaFile() : mmap_base_(nullptr), mmap_size_(0) {}\n  ~PropertyInfoAreaFile() { Reset(); }\n\n  PropertyInfoAreaFile(const PropertyInfoAreaFile&) = delete;\n  void operator=(const PropertyInfoAreaFile&) = delete;\n  PropertyInfoAreaFile(PropertyInfoAreaFile&&) = default;\n  PropertyInfoAreaFile& operator=(PropertyInfoAreaFile&&) = default;\n\n  bool LoadDefaultPath();\n  bool LoadPath(const char* filename);\n\n  const PropertyInfoArea* operator->() const {\n    return reinterpret_cast<const PropertyInfoArea*>(mmap_base_);\n  }\n\n  explicit operator bool() const { return mmap_base_ != nullptr; }\n\n  void Reset();\n\n private:\n  void* mmap_base_;\n  size_t mmap_size_;\n};\n\n}  // namespace properties\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "property_service/libpropertyinfoparser/property_info_parser.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"property_info_parser/property_info_parser.h\"\n\n#include <fcntl.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\nnamespace android {\nnamespace properties {\n\nnamespace {\n\n// Binary search to find index of element in an array compared via f(search).\ntemplate <typename F>\nint Find(uint32_t array_length, F&& f) {\n  int bottom = 0;\n  int top = array_length - 1;\n  while (top >= bottom) {\n    int search = (top + bottom) / 2;\n\n    auto cmp = f(search);\n\n    if (cmp == 0) return search;\n    if (cmp < 0) bottom = search + 1;\n    if (cmp > 0) top = search - 1;\n  }\n  return -1;\n}\n\n}  // namespace\n\n// Binary search the list of contexts to find the index of a given context string.\n// Only should be used for TrieSerializer to construct the Trie.\nint PropertyInfoArea::FindContextIndex(const char* context) const {\n  return Find(num_contexts(), [this, context](auto array_offset) {\n    auto string_offset = uint32_array(contexts_array_offset())[array_offset];\n    return strcmp(c_string(string_offset), context);\n  });\n}\n\n// Binary search the list of types to find the index of a given type string.\n// Only should be used for TrieSerializer to construct the Trie.\nint PropertyInfoArea::FindTypeIndex(const char* type) const {\n  return Find(num_types(), [this, type](auto array_offset) {\n    auto string_offset = uint32_array(types_array_offset())[array_offset];\n    return strcmp(c_string(string_offset), type);\n  });\n}\n\n// Binary search the list of children nodes to find a TrieNode for a given property piece.\n// Used to traverse the Trie in GetPropertyInfoIndexes().\nbool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const {\n  auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) {\n    const char* child_name = child_node(array_offset).name();\n    int cmp = strncmp(child_name, name, namelen);\n    if (cmp == 0 && child_name[namelen] != '\\0') {\n      // We use strncmp() since name isn't null terminated, but we don't want to match only a\n      // prefix of a child node's name, so we check here if we did only match a prefix and\n      // return 1, to indicate to the binary search to search earlier in the array for the real\n      // match.\n      return 1;\n    }\n    return cmp;\n  });\n\n  if (node_index == -1) {\n    return false;\n  }\n  *child = child_node(node_index);\n  return true;\n}\n\nvoid PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,\n                                        uint32_t* context_index, uint32_t* type_index) const {\n  const uint32_t remaining_name_size = strlen(remaining_name);\n  for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {\n    auto prefix_len = trie_node.prefix(i)->namelen;\n    if (prefix_len > remaining_name_size) continue;\n\n    if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) {\n      if (trie_node.prefix(i)->context_index != ~0u) {\n        *context_index = trie_node.prefix(i)->context_index;\n      }\n      if (trie_node.prefix(i)->type_index != ~0u) {\n        *type_index = trie_node.prefix(i)->type_index;\n      }\n      return;\n    }\n  }\n}\n\nvoid PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,\n                                              uint32_t* type_index) const {\n  uint32_t return_context_index = ~0u;\n  uint32_t return_type_index = ~0u;\n  const char* remaining_name = name;\n  auto trie_node = root_node();\n  while (true) {\n    const char* sep = strchr(remaining_name, '.');\n\n    // Apply prefix match for prefix deliminated with '.'\n    if (trie_node.context_index() != ~0u) {\n      return_context_index = trie_node.context_index();\n    }\n    if (trie_node.type_index() != ~0u) {\n      return_type_index = trie_node.type_index();\n    }\n\n    // Check prefixes at this node.  This comes after the node check since these prefixes are by\n    // definition longer than the node itself.\n    CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);\n\n    if (sep == nullptr) {\n      break;\n    }\n\n    const uint32_t substr_size = sep - remaining_name;\n    TrieNode child_node;\n    if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) {\n      break;\n    }\n\n    trie_node = child_node;\n    remaining_name = sep + 1;\n  }\n\n  // We've made it to a leaf node, so check contents and return appropriately.\n  // Check exact matches\n  for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) {\n    if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) {\n      if (context_index != nullptr) {\n        if (trie_node.exact_match(i)->context_index != ~0u) {\n          *context_index = trie_node.exact_match(i)->context_index;\n        } else {\n          *context_index = return_context_index;\n        }\n      }\n      if (type_index != nullptr) {\n        if (trie_node.exact_match(i)->type_index != ~0u) {\n          *type_index = trie_node.exact_match(i)->type_index;\n        } else {\n          *type_index = return_type_index;\n        }\n      }\n      return;\n    }\n  }\n  // Check prefix matches for prefixes not deliminated with '.'\n  CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);\n  // Return previously found prefix match.\n  if (context_index != nullptr) *context_index = return_context_index;\n  if (type_index != nullptr) *type_index = return_type_index;\n  return;\n}\n\nvoid PropertyInfoArea::GetPropertyInfo(const char* property, const char** context,\n                                       const char** type) const {\n  uint32_t context_index;\n  uint32_t type_index;\n  GetPropertyInfoIndexes(property, &context_index, &type_index);\n  if (context != nullptr) {\n    if (context_index == ~0u) {\n      *context = nullptr;\n    } else {\n      *context = this->context(context_index);\n    }\n  }\n  if (type != nullptr) {\n    if (type_index == ~0u) {\n      *type = nullptr;\n    } else {\n      *type = this->type(type_index);\n    }\n  }\n}\n\nbool PropertyInfoAreaFile::LoadDefaultPath() {\n  return LoadPath(\"/dev/__properties__/property_info\");\n}\n\nbool PropertyInfoAreaFile::LoadPath(const char* filename) {\n  int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);\n\n  struct stat fd_stat;\n  if (fstat(fd, &fd_stat) < 0) {\n    close(fd);\n    return false;\n  }\n\n  if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||\n      ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||\n      (fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {\n    close(fd);\n    return false;\n  }\n\n  auto mmap_size = fd_stat.st_size;\n\n  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);\n  if (map_result == MAP_FAILED) {\n    close(fd);\n    return false;\n  }\n\n  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);\n  if (property_info_area->minimum_supported_version() > 1 ||\n      property_info_area->size() != mmap_size) {\n    munmap(map_result, mmap_size);\n    close(fd);\n    return false;\n  }\n\n  close(fd);\n  mmap_base_ = map_result;\n  mmap_size_ = mmap_size;\n  return true;\n}\n\nvoid PropertyInfoAreaFile::Reset() {\n  if (mmap_size_ > 0) {\n    munmap(mmap_base_, mmap_size_);\n  }\n  mmap_base_ = nullptr;\n  mmap_size_ = 0;\n}\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"propertyinfoserializer_defaults\",\n    host_supported: true,\n    cppflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"libpropertyinfoparser\",\n    ],\n}\n\ncc_library_static {\n    name: \"libpropertyinfoserializer\",\n    defaults: [\"propertyinfoserializer_defaults\"],\n    recovery_available: true,\n    srcs: [\n        \"property_info_file.cpp\",\n        \"property_info_serializer.cpp\",\n        \"trie_builder.cpp\",\n        \"trie_serializer.cpp\",\n    ],\n\n    export_include_dirs: [\"include\"],\n}\n\ncc_test {\n    name: \"propertyinfoserializer_tests\",\n    defaults: [\"propertyinfoserializer_defaults\"],\n    srcs: [\n        \"trie_builder_test.cpp\",\n        \"property_info_serializer_test.cpp\",\n    ],\n    static_libs: [\"libpropertyinfoserializer\"],\n    test_suites: [\"device-tests\"],\n}\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#pragma once\n\n#include <string>\n#include <vector>\n\nnamespace android {\nnamespace properties {\n\nstruct PropertyInfoEntry {\n  PropertyInfoEntry() {}\n  template <typename T, typename U, typename V>\n  PropertyInfoEntry(T&& name, U&& context, V&& type, bool exact_match)\n      : name(std::forward<T>(name)),\n        context(std::forward<U>(context)),\n        type(std::forward<V>(type)),\n        exact_match(exact_match) {}\n  std::string name;\n  std::string context;\n  std::string type;\n  bool exact_match;\n};\n\nbool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,\n               const std::string& default_context, const std::string& default_type,\n               std::string* serialized_trie, std::string* error);\n\nvoid ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,\n                           std::vector<PropertyInfoEntry>* property_infos,\n                           std::vector<std::string>* errors);\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/property_info_file.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include <property_info_serializer/property_info_serializer.h>\n\n#include <android-base/strings.h>\n\n#include \"space_tokenizer.h\"\n\nusing android::base::Join;\nusing android::base::Split;\nusing android::base::StartsWith;\nusing android::base::Trim;\n\nnamespace android {\nnamespace properties {\n\nnamespace {\n\nbool IsTypeValid(const std::vector<std::string>& type_strings) {\n  if (type_strings.empty()) {\n    return false;\n  }\n\n  // There must be at least one string following 'enum'\n  if (type_strings[0] == \"enum\") {\n    return type_strings.size() > 1;\n  }\n\n  // There should not be any string following any other types.\n  if (type_strings.size() != 1) {\n    return false;\n  }\n\n  // Check that the type matches one of remaining valid types.\n  static const char* const no_parameter_types[] = {\"string\", \"bool\",   \"int\",\n                                                   \"uint\",   \"double\", \"size\"};\n  for (const auto& type : no_parameter_types) {\n    if (type_strings[0] == type) {\n      return true;\n    }\n  }\n  return false;\n}\n\nbool ParsePropertyInfoLine(const std::string& line, bool require_prefix_or_exact,\n                           PropertyInfoEntry* out, std::string* error) {\n  auto tokenizer = SpaceTokenizer(line);\n\n  auto property = tokenizer.GetNext();\n  if (property.empty()) {\n    *error = \"Did not find a property entry in '\" + line + \"'\";\n    return false;\n  }\n\n  auto context = tokenizer.GetNext();\n  if (context.empty()) {\n    *error = \"Did not find a context entry in '\" + line + \"'\";\n    return false;\n  }\n\n  // It is not an error to not find exact_match or a type, as older files will not contain them.\n  auto match_operation = tokenizer.GetNext();\n  // We reformat type to be space deliminated regardless of the input whitespace for easier storage\n  // and subsequent parsing.\n  auto type_strings = std::vector<std::string>{};\n  auto type = tokenizer.GetNext();\n  while (!type.empty()) {\n    type_strings.emplace_back(type);\n    type = tokenizer.GetNext();\n  }\n\n  bool exact_match = false;\n  if (match_operation == \"exact\") {\n    exact_match = true;\n  } else if (match_operation != \"prefix\" && match_operation != \"\" && require_prefix_or_exact) {\n    *error = \"Match operation '\" + match_operation +\n             \"' is not valid: must be either 'prefix' or 'exact'\";\n    return false;\n  }\n\n  if (!type_strings.empty() && !IsTypeValid(type_strings)) {\n    *error = \"Type '\" + Join(type_strings, \" \") + \"' is not valid\";\n    return false;\n  }\n\n  *out = {property, context, Join(type_strings, \" \"), exact_match};\n  return true;\n}\n\n}  // namespace\n\nvoid ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,\n                           std::vector<PropertyInfoEntry>* property_infos,\n                           std::vector<std::string>* errors) {\n  // Do not clear property_infos to allow this function to be called on multiple files, with\n  // their results concatenated.\n  errors->clear();\n\n  for (const auto& line : Split(file_contents, \"\\n\")) {\n    auto trimmed_line = Trim(line);\n    if (trimmed_line.empty() || StartsWith(trimmed_line, \"#\")) {\n      continue;\n    }\n\n    auto property_info_entry = PropertyInfoEntry{};\n    auto parse_error = std::string{};\n    if (!ParsePropertyInfoLine(trimmed_line, require_prefix_or_exact, &property_info_entry,\n                               &parse_error)) {\n      errors->emplace_back(parse_error);\n      continue;\n    }\n\n    property_infos->emplace_back(property_info_entry);\n  }\n}\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/property_info_serializer.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"property_info_serializer/property_info_serializer.h\"\n\n#include \"property_info_parser/property_info_parser.h\"\n\n#include <set>\n\n#include \"trie_builder.h\"\n#include \"trie_serializer.h\"\n\nnamespace android {\nnamespace properties {\n\nbool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,\n               const std::string& default_context, const std::string& default_type,\n               std::string* serialized_trie, std::string* error) {\n  // Check that names are legal first\n  auto trie_builder = TrieBuilder(default_context, default_type);\n\n  for (const auto& [name, context, type, is_exact] : property_info) {\n    if (!trie_builder.AddToTrie(name, context, type, is_exact, error)) {\n      return false;\n    }\n  }\n\n  auto trie_serializer = TrieSerializer();\n  *serialized_trie = trie_serializer.SerializeTrie(trie_builder);\n  return true;\n}\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/property_info_serializer_test.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"property_info_serializer/property_info_serializer.h\"\n\n#include \"property_info_parser/property_info_parser.h\"\n\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace properties {\n\nTEST(propertyinfoserializer, TrieNodeCheck) {\n  auto property_info = std::vector<PropertyInfoEntry>{\n      {\"test.\", \"1st\", \"1st\", false},     {\"test.test\", \"2nd\", \"2nd\", false},\n\n      {\"test.test1\", \"3rd\", \"3rd\", true}, {\"test.test2\", \"3rd\", \"3rd\", true},\n      {\"test.test3\", \"3rd\", \"3rd\", true}, {\"this.is.a.long.string\", \"4th\", \"4th\", true},\n  };\n\n  auto serialized_trie = std::string();\n  auto build_trie_error = std::string();\n  ASSERT_TRUE(BuildTrie(property_info, \"default\", \"default\", &serialized_trie, &build_trie_error))\n      << build_trie_error;\n\n  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());\n\n  // Initial checks for property area.\n  EXPECT_EQ(1U, property_info_area->current_version());\n  EXPECT_EQ(1U, property_info_area->minimum_supported_version());\n\n  // Check the root node\n  auto root_node = property_info_area->root_node();\n  EXPECT_STREQ(\"root\", root_node.name());\n  EXPECT_STREQ(\"default\", property_info_area->context(root_node.context_index()));\n  EXPECT_STREQ(\"default\", property_info_area->type(root_node.type_index()));\n\n  EXPECT_EQ(0U, root_node.num_prefixes());\n  EXPECT_EQ(0U, root_node.num_exact_matches());\n\n  ASSERT_EQ(2U, root_node.num_child_nodes());\n\n  // Check the 'test'. node\n  TrieNode test_node;\n  ASSERT_TRUE(root_node.FindChildForString(\"test\", 4, &test_node));\n\n  EXPECT_STREQ(\"test\", test_node.name());\n  EXPECT_STREQ(\"1st\", property_info_area->context(test_node.context_index()));\n  EXPECT_STREQ(\"1st\", property_info_area->type(test_node.type_index()));\n\n  EXPECT_EQ(0U, test_node.num_child_nodes());\n\n  EXPECT_EQ(1U, test_node.num_prefixes());\n  {\n    auto prefix = test_node.prefix(0);\n    EXPECT_STREQ(\"test\", serialized_trie.data() + prefix->name_offset);\n    EXPECT_EQ(4U, prefix->namelen);\n    EXPECT_STREQ(\"2nd\", property_info_area->context(prefix->context_index));\n    EXPECT_STREQ(\"2nd\", property_info_area->type(prefix->type_index));\n  }\n\n  EXPECT_EQ(3U, test_node.num_exact_matches());\n  {\n    auto match1 = test_node.exact_match(0);\n    auto match2 = test_node.exact_match(1);\n    auto match3 = test_node.exact_match(2);\n    EXPECT_STREQ(\"test1\", serialized_trie.data() + match1->name_offset);\n    EXPECT_STREQ(\"test2\", serialized_trie.data() + match2->name_offset);\n    EXPECT_STREQ(\"test3\", serialized_trie.data() + match3->name_offset);\n\n    EXPECT_STREQ(\"3rd\", property_info_area->context(match1->context_index));\n    EXPECT_STREQ(\"3rd\", property_info_area->context(match2->context_index));\n    EXPECT_STREQ(\"3rd\", property_info_area->context(match3->context_index));\n\n    EXPECT_STREQ(\"3rd\", property_info_area->type(match1->type_index));\n    EXPECT_STREQ(\"3rd\", property_info_area->type(match2->type_index));\n    EXPECT_STREQ(\"3rd\", property_info_area->type(match3->type_index));\n  }\n\n  // Check the long string node\n  auto expect_empty_one_child = [](auto& node) {\n    EXPECT_EQ(-1U, node.context_index());\n    EXPECT_EQ(0U, node.num_prefixes());\n    EXPECT_EQ(0U, node.num_exact_matches());\n    EXPECT_EQ(1U, node.num_child_nodes());\n  };\n\n  // Start with 'this'\n  TrieNode long_string_node;\n  ASSERT_TRUE(root_node.FindChildForString(\"this\", 4, &long_string_node));\n  expect_empty_one_child(long_string_node);\n\n  // Move to 'is'\n  ASSERT_TRUE(long_string_node.FindChildForString(\"is\", 2, &long_string_node));\n  expect_empty_one_child(long_string_node);\n\n  // Move to 'a'\n  ASSERT_TRUE(long_string_node.FindChildForString(\"a\", 1, &long_string_node));\n  expect_empty_one_child(long_string_node);\n\n  // Move to 'long'\n  ASSERT_TRUE(long_string_node.FindChildForString(\"long\", 4, &long_string_node));\n  EXPECT_EQ(0U, long_string_node.num_prefixes());\n  EXPECT_EQ(1U, long_string_node.num_exact_matches());\n  EXPECT_EQ(0U, long_string_node.num_child_nodes());\n\n  auto final_match = long_string_node.exact_match(0);\n  EXPECT_STREQ(\"string\", serialized_trie.data() + final_match->name_offset);\n  EXPECT_STREQ(\"4th\", property_info_area->context(final_match->context_index));\n  EXPECT_STREQ(\"4th\", property_info_area->type(final_match->type_index));\n}\n\nTEST(propertyinfoserializer, GetPropertyInfo) {\n  auto property_info = std::vector<PropertyInfoEntry>{\n      {\"test.\", \"1st\", \"1st\", false},       {\"test.test\", \"2nd\", \"2nd\", false},\n      {\"test.test2.\", \"6th\", \"6th\", false}, {\"test.test\", \"5th\", \"5th\", true},\n      {\"test.test1\", \"3rd\", \"3rd\", true},   {\"test.test2\", \"7th\", \"7th\", true},\n      {\"test.test3\", \"3rd\", \"3rd\", true},   {\"this.is.a.long.string\", \"4th\", \"4th\", true},\n      {\"testoneword\", \"8th\", \"8th\", true},  {\"testwordprefix\", \"9th\", \"9th\", false},\n  };\n\n  auto serialized_trie = std::string();\n  auto build_trie_error = std::string();\n  ASSERT_TRUE(BuildTrie(property_info, \"default\", \"default\", &serialized_trie, &build_trie_error))\n      << build_trie_error;\n\n  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());\n\n  // Smoke test\n  auto root_node = property_info_area->root_node();\n  EXPECT_STREQ(\"root\", root_node.name());\n  EXPECT_STREQ(\"default\", property_info_area->context(root_node.context_index()));\n  EXPECT_STREQ(\"default\", property_info_area->type(root_node.type_index()));\n\n  const char* context;\n  const char* type;\n  property_info_area->GetPropertyInfo(\"abc\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"abc.abc\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"123.abc\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n\n  property_info_area->GetPropertyInfo(\"test.a\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"test.b\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"test.c\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n\n  property_info_area->GetPropertyInfo(\"test.test\", &context, &type);\n  EXPECT_STREQ(\"5th\", context);\n  EXPECT_STREQ(\"5th\", type);\n  property_info_area->GetPropertyInfo(\"test.testa\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"test.testb\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"test.testc\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n\n  property_info_area->GetPropertyInfo(\"test.test.a\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"test.test.b\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"test.test.c\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n\n  property_info_area->GetPropertyInfo(\"test.test1\", &context, &type);\n  EXPECT_STREQ(\"3rd\", context);\n  EXPECT_STREQ(\"3rd\", type);\n  property_info_area->GetPropertyInfo(\"test.test2\", &context, &type);\n  EXPECT_STREQ(\"7th\", context);\n  EXPECT_STREQ(\"7th\", type);\n  property_info_area->GetPropertyInfo(\"test.test3\", &context, &type);\n  EXPECT_STREQ(\"3rd\", context);\n  EXPECT_STREQ(\"3rd\", type);\n\n  property_info_area->GetPropertyInfo(\"test.test11\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"test.test22\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"test.test33\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n\n  property_info_area->GetPropertyInfo(\"this.is.a.long.string\", &context, &type);\n  EXPECT_STREQ(\"4th\", context);\n  EXPECT_STREQ(\"4th\", type);\n\n  property_info_area->GetPropertyInfo(\"this.is.a.long\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"this.is.a\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"this.is\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"this\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n\n  property_info_area->GetPropertyInfo(\"test.test2.a\", &context, &type);\n  EXPECT_STREQ(\"6th\", context);\n  EXPECT_STREQ(\"6th\", type);\n\n  property_info_area->GetPropertyInfo(\"testoneword\", &context, &type);\n  EXPECT_STREQ(\"8th\", context);\n  EXPECT_STREQ(\"8th\", type);\n\n  property_info_area->GetPropertyInfo(\"testwordprefix\", &context, &type);\n  EXPECT_STREQ(\"9th\", context);\n  EXPECT_STREQ(\"9th\", type);\n\n  property_info_area->GetPropertyInfo(\"testwordprefixblah\", &context, &type);\n  EXPECT_STREQ(\"9th\", context);\n  EXPECT_STREQ(\"9th\", type);\n\n  property_info_area->GetPropertyInfo(\"testwordprefix.blah\", &context, &type);\n  EXPECT_STREQ(\"9th\", context);\n  EXPECT_STREQ(\"9th\", type);\n}\n\nTEST(propertyinfoserializer, RealProperties) {\n  auto property_info = std::vector<PropertyInfoEntry>{\n      // Contexts from system/sepolicy/private/property_contexts\n      {\"net.rmnet\", \"u:object_r:net_radio_prop:s0\", \"string\", false},\n      {\"net.gprs\", \"u:object_r:net_radio_prop:s0\", \"string\", false},\n      {\"net.ppp\", \"u:object_r:net_radio_prop:s0\", \"string\", false},\n      {\"net.qmi\", \"u:object_r:net_radio_prop:s0\", \"string\", false},\n      {\"net.lte\", \"u:object_r:net_radio_prop:s0\", \"string\", false},\n      {\"net.cdma\", \"u:object_r:net_radio_prop:s0\", \"string\", false},\n      {\"net.dns\", \"u:object_r:net_dns_prop:s0\", \"string\", false},\n      {\"sys.usb.config\", \"u:object_r:system_radio_prop:s0\", \"string\", false},\n      {\"ril.\", \"u:object_r:radio_prop:s0\", \"string\", false},\n      {\"ro.ril.\", \"u:object_r:radio_prop:s0\", \"string\", false},\n      {\"gsm.\", \"u:object_r:radio_prop:s0\", \"string\", false},\n      {\"persist.radio\", \"u:object_r:radio_prop:s0\", \"string\", false},\n\n      {\"net.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"dev.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"ro.runtime.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"ro.runtime.firstboot\", \"u:object_r:firstboot_prop:s0\", \"string\", false},\n      {\"hw.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"ro.hw.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"sys.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"sys.cppreopt\", \"u:object_r:cppreopt_prop:s0\", \"string\", false},\n      {\"sys.powerctl\", \"u:object_r:powerctl_prop:s0\", \"string\", false},\n      {\"sys.usb.ffs.\", \"u:object_r:ffs_prop:s0\", \"string\", false},\n      {\"service.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"dhcp.\", \"u:object_r:dhcp_prop:s0\", \"string\", false},\n      {\"dhcp.bt-pan.result\", \"u:object_r:pan_result_prop:s0\", \"string\", false},\n      {\"bluetooth.\", \"u:object_r:bluetooth_prop:s0\", \"string\", false},\n\n      {\"debug.\", \"u:object_r:debug_prop:s0\", \"string\", false},\n      {\"debug.db.\", \"u:object_r:debuggerd_prop:s0\", \"string\", false},\n      {\"dumpstate.\", \"u:object_r:dumpstate_prop:s0\", \"string\", false},\n      {\"dumpstate.options\", \"u:object_r:dumpstate_options_prop:s0\", \"string\", false},\n      {\"log.\", \"u:object_r:log_prop:s0\", \"string\", false},\n      {\"log.tag\", \"u:object_r:log_tag_prop:s0\", \"string\", false},\n      {\"log.tag.WifiHAL\", \"u:object_r:wifi_log_prop:s0\", \"string\", false},\n      {\"security.perf_harden\", \"u:object_r:shell_prop:s0\", \"string\", false},\n      {\"service.adb.root\", \"u:object_r:shell_prop:s0\", \"string\", false},\n      {\"service.adb.tcp.port\", \"u:object_r:shell_prop:s0\", \"string\", false},\n\n      {\"persist.audio.\", \"u:object_r:audio_prop:s0\", \"string\", false},\n      {\"persist.bluetooth.\", \"u:object_r:bluetooth_prop:s0\", \"string\", false},\n      {\"persist.debug.\", \"u:object_r:persist_debug_prop:s0\", \"string\", false},\n      {\"persist.logd.\", \"u:object_r:logd_prop:s0\", \"string\", false},\n      {\"persist.logd.security\", \"u:object_r:device_logging_prop:s0\", \"string\", false},\n      {\"persist.logd.logpersistd\", \"u:object_r:logpersistd_logging_prop:s0\", \"string\", false},\n      {\"logd.logpersistd\", \"u:object_r:logpersistd_logging_prop:s0\", \"string\", false},\n      {\"persist.log.tag\", \"u:object_r:log_tag_prop:s0\", \"string\", false},\n      {\"persist.mmc.\", \"u:object_r:mmc_prop:s0\", \"string\", false},\n      {\"persist.netd.stable_secret\", \"u:object_r:netd_stable_secret_prop:s0\", \"string\", false},\n      {\"persist.sys.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"persist.sys.safemode\", \"u:object_r:safemode_prop:s0\", \"string\", false},\n      {\"ro.sys.safemode\", \"u:object_r:safemode_prop:s0\", \"string\", false},\n      {\"persist.sys.audit_safemode\", \"u:object_r:safemode_prop:s0\", \"string\", false},\n      {\"persist.service.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"persist.service.bdroid.\", \"u:object_r:bluetooth_prop:s0\", \"string\", false},\n      {\"persist.security.\", \"u:object_r:system_prop:s0\", \"string\", false},\n      {\"persist.vendor.overlay.\", \"u:object_r:overlay_prop:s0\", \"string\", false},\n      {\"ro.boot.vendor.overlay.\", \"u:object_r:overlay_prop:s0\", \"string\", false},\n      {\"ro.boottime.\", \"u:object_r:boottime_prop:s0\", \"string\", false},\n      {\"ro.serialno\", \"u:object_r:serialno_prop:s0\", \"string\", false},\n      {\"ro.boot.btmacaddr\", \"u:object_r:bluetooth_prop:s0\", \"string\", false},\n      {\"ro.boot.serialno\", \"u:object_r:serialno_prop:s0\", \"string\", false},\n      {\"ro.bt.\", \"u:object_r:bluetooth_prop:s0\", \"string\", false},\n      {\"ro.boot.bootreason\", \"u:object_r:bootloader_boot_reason_prop:s0\", \"string\", false},\n      {\"persist.sys.boot.reason\", \"u:object_r:last_boot_reason_prop:s0\", \"string\", false},\n      {\"sys.boot.reason\", \"u:object_r:system_boot_reason_prop:s0\", \"string\", false},\n      {\"ro.organization_owned\", \"u:object_r:device_logging_prop:s0\", \"string\", false},\n\n      {\"selinux.restorecon_recursive\", \"u:object_r:restorecon_prop:s0\", \"string\", false},\n\n      {\"vold.\", \"u:object_r:vold_prop:s0\", \"string\", false},\n      {\"ro.crypto.\", \"u:object_r:vold_prop:s0\", \"string\", false},\n\n      {\"ro.build.fingerprint\", \"u:object_r:fingerprint_prop:s0\", \"string\", false},\n\n      {\"ro.persistent_properties.ready\", \"u:object_r:persistent_properties_ready_prop:s0\", \"string\",\n       false},\n\n      {\"ctl.bootanim\", \"u:object_r:ctl_bootanim_prop:s0\", \"string\", false},\n      {\"ctl.dumpstate\", \"u:object_r:ctl_dumpstate_prop:s0\", \"string\", false},\n      {\"ctl.fuse_\", \"u:object_r:ctl_fuse_prop:s0\", \"string\", false},\n      {\"ctl.mdnsd\", \"u:object_r:ctl_mdnsd_prop:s0\", \"string\", false},\n      {\"ctl.ril-daemon\", \"u:object_r:ctl_rildaemon_prop:s0\", \"string\", false},\n      {\"ctl.bugreport\", \"u:object_r:ctl_bugreport_prop:s0\", \"string\", false},\n      {\"ctl.console\", \"u:object_r:ctl_console_prop:s0\", \"string\", false},\n      {\"ctl.\", \"u:object_r:ctl_default_prop:s0\", \"string\", false},\n\n      {\"nfc.\", \"u:object_r:nfc_prop:s0\", \"string\", false},\n\n      {\"config.\", \"u:object_r:config_prop:s0\", \"string\", false},\n      {\"ro.config.\", \"u:object_r:config_prop:s0\", \"string\", false},\n      {\"dalvik.\", \"u:object_r:dalvik_prop:s0\", \"string\", false},\n      {\"ro.dalvik.\", \"u:object_r:dalvik_prop:s0\", \"string\", false},\n\n      {\"wlan.\", \"u:object_r:wifi_prop:s0\", \"string\", false},\n\n      {\"lowpan.\", \"u:object_r:lowpan_prop:s0\", \"string\", false},\n      {\"ro.lowpan.\", \"u:object_r:lowpan_prop:s0\", \"string\", false},\n\n      {\"hwservicemanager.\", \"u:object_r:hwservicemanager_prop:s0\", \"string\", false},\n      // Contexts from device/lge/bullhead/sepolicy/property_contexts\n      {\"wc_transport.\", \"u:object_r:wc_transport_prop:s0\", \"string\", false},\n      {\"sys.listeners.\", \"u:object_r:qseecomtee_prop:s0\", \"string\", false},\n      {\"sys.keymaster.\", \"u:object_r:qseecomtee_prop:s0\", \"string\", false},\n      {\"radio.atfwd.\", \"u:object_r:radio_atfwd_prop:s0\", \"string\", false},\n      {\"sys.ims.\", \"u:object_r:qcom_ims_prop:s0\", \"string\", false},\n      {\"sensors.contexthub.\", \"u:object_r:contexthub_prop:s0\", \"string\", false},\n      {\"net.r_rmnet\", \"u:object_r:net_radio_prop:s0\", \"string\", false},\n  };\n\n  auto serialized_trie = std::string();\n  auto build_trie_error = std::string();\n  ASSERT_TRUE(BuildTrie(property_info, \"u:object_r:default_prop:s0\", \"string\", &serialized_trie,\n                        &build_trie_error))\n      << build_trie_error;\n\n  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());\n\n  auto properties_and_contexts = std::vector<std::pair<std::string, std::string>>{\n      // Actual properties on bullhead via `getprop -Z`\n      {\"af.fast_track_multiplier\", \"u:object_r:default_prop:s0\"},\n      {\"audio_hal.period_size\", \"u:object_r:default_prop:s0\"},\n      {\"bluetooth.enable_timeout_ms\", \"u:object_r:bluetooth_prop:s0\"},\n      {\"dalvik.vm.appimageformat\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.boot-dex2oat-cpu-set\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.boot-dex2oat-threads\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.dex2oat-Xms\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.dex2oat-Xmx\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.dex2oat-cpu-set\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.dex2oat-threads\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.dexopt.secondary\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.heapgrowthlimit\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.heapmaxfree\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.heapminfree\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.heapsize\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.heapstartsize\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.heaptargetutilization\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.image-dex2oat-Xms\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.image-dex2oat-Xmx\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.image-dex2oat-cpu-set\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.image-dex2oat-threads\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.isa.arm.features\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.isa.arm.variant\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.isa.arm64.features\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.isa.arm64.variant\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.lockprof.threshold\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.stack-trace-file\", \"u:object_r:dalvik_prop:s0\"},\n      {\"dalvik.vm.usejit\", \"u:object_r:dalvik_prop:s0\"},\n      {\"debug.atrace.tags.enableflags\", \"u:object_r:debug_prop:s0\"},\n      {\"debug.force_rtl\", \"u:object_r:debug_prop:s0\"},\n      {\"dev.bootcomplete\", \"u:object_r:system_prop:s0\"},\n      {\"drm.service.enabled\", \"u:object_r:default_prop:s0\"},\n      {\"gsm.current.phone-type\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.network.type\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.operator.alpha\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.operator.iso-country\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.operator.isroaming\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.operator.numeric\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.sim.operator.alpha\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.sim.operator.iso-country\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.sim.operator.numeric\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.sim.state\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.version.baseband\", \"u:object_r:radio_prop:s0\"},\n      {\"gsm.version.ril-impl\", \"u:object_r:radio_prop:s0\"},\n      {\"hwservicemanager.ready\", \"u:object_r:hwservicemanager_prop:s0\"},\n      {\"init.svc.adbd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.atfwd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.audioserver\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.bootanim\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.bullhead-sh\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.cameraserver\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.cnd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.cnss-daemon\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.cnss_diag\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.configstore-hal-1-0\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.console\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.devstart_sh\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.drm\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.dumpstate-1-0\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.flash-nanohub-fw\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.fps_hal\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.gatekeeperd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.gralloc-2-0\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.healthd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.hidl_memory\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.hostapd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.hwservicemanager\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.imsdatadaemon\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.imsqmidaemon\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.installd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.irsc_util\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.keystore\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.lmkd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.loc_launcher\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.logd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.logd-reinit\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.media\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.mediadrm\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.mediaextractor\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.mediametrics\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.msm_irqbalance\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.netd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.netmgrd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.per_mgr\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.per_proxy\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.perfd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.qcamerasvr\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.qmuxd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.qseecomd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.qti\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.ril-daemon\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.rmt_storage\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.servicemanager\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.ss_ramdump\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.start_hci_filter\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.storaged\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.surfaceflinger\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.thermal-engine\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.time_daemon\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.tombstoned\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.ueventd\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.update_engine\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.usb-hal-1-0\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.vndservicemanager\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.vold\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.webview_zygote32\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.wifi_hal_legacy\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.wificond\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.wpa_supplicant\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.zygote\", \"u:object_r:default_prop:s0\"},\n      {\"init.svc.zygote_secondary\", \"u:object_r:default_prop:s0\"},\n      {\"keyguard.no_require_sim\", \"u:object_r:default_prop:s0\"},\n      {\"log.tag.WifiHAL\", \"u:object_r:wifi_log_prop:s0\"},\n      {\"logd.logpersistd.enable\", \"u:object_r:logpersistd_logging_prop:s0\"},\n      {\"media.aac_51_output_enabled\", \"u:object_r:default_prop:s0\"},\n      {\"media.recorder.show_manufacturer_and_model\", \"u:object_r:default_prop:s0\"},\n      {\"net.bt.name\", \"u:object_r:system_prop:s0\"},\n      {\"net.lte.ims.data.enabled\", \"u:object_r:net_radio_prop:s0\"},\n      {\"net.tcp.default_init_rwnd\", \"u:object_r:system_prop:s0\"},\n      {\"nfc.initialized\", \"u:object_r:nfc_prop:s0\"},\n      {\"persist.audio.fluence.speaker\", \"u:object_r:audio_prop:s0\"},\n      {\"persist.audio.fluence.voicecall\", \"u:object_r:audio_prop:s0\"},\n      {\"persist.audio.fluence.voicecomm\", \"u:object_r:audio_prop:s0\"},\n      {\"persist.audio.fluence.voicerec\", \"u:object_r:audio_prop:s0\"},\n      {\"persist.camera.tnr.preview\", \"u:object_r:default_prop:s0\"},\n      {\"persist.camera.tnr.video\", \"u:object_r:default_prop:s0\"},\n      {\"persist.data.iwlan.enable\", \"u:object_r:default_prop:s0\"},\n      {\"persist.hwc.mdpcomp.enable\", \"u:object_r:default_prop:s0\"},\n      {\"persist.logd.logpersistd\", \"u:object_r:logpersistd_logging_prop:s0\"},\n      {\"persist.media.treble_omx\", \"u:object_r:default_prop:s0\"},\n      {\"persist.qcril.disable_retry\", \"u:object_r:default_prop:s0\"},\n      {\"persist.radio.adb_log_on\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.always_send_plmn\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.apm_sim_not_pwdn\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.custom_ecc\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.data_con_rprt\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.data_no_toggle\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.eons.enabled\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.eri64_as_home\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.mode_pref_nv10\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.process_sups_ind\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.redir_party_num\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.ril_payload_on\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.snapshot_enabled\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.snapshot_timer\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.radio.use_cc_names\", \"u:object_r:radio_prop:s0\"},\n      {\"persist.speaker.prot.enable\", \"u:object_r:default_prop:s0\"},\n      {\"persist.sys.boot.reason\", \"u:object_r:last_boot_reason_prop:s0\"},\n      {\"persist.sys.dalvik.vm.lib.2\", \"u:object_r:system_prop:s0\"},\n      {\"persist.sys.debug.color_temp\", \"u:object_r:system_prop:s0\"},\n      {\"persist.sys.preloads.file_cache_expired\", \"u:object_r:system_prop:s0\"},\n      {\"persist.sys.timezone\", \"u:object_r:system_prop:s0\"},\n      {\"persist.sys.usb.config\", \"u:object_r:system_prop:s0\"},\n      {\"persist.sys.webview.vmsize\", \"u:object_r:system_prop:s0\"},\n      {\"persist.tom\", \"u:object_r:default_prop:s0\"},\n      {\"persist.tom2\", \"u:object_r:default_prop:s0\"},\n      {\"pm.dexopt.ab-ota\", \"u:object_r:default_prop:s0\"},\n      {\"pm.dexopt.bg-dexopt\", \"u:object_r:default_prop:s0\"},\n      {\"pm.dexopt.boot\", \"u:object_r:default_prop:s0\"},\n      {\"pm.dexopt.first-boot\", \"u:object_r:default_prop:s0\"},\n      {\"pm.dexopt.install\", \"u:object_r:default_prop:s0\"},\n      {\"qcom.bluetooth.soc\", \"u:object_r:default_prop:s0\"},\n      {\"radio.atfwd.start\", \"u:object_r:radio_atfwd_prop:s0\"},\n      {\"ril.ecclist\", \"u:object_r:radio_prop:s0\"},\n      {\"ril.nosim.ecc_list_1\", \"u:object_r:radio_prop:s0\"},\n      {\"ril.nosim.ecc_list_count\", \"u:object_r:radio_prop:s0\"},\n      {\"ril.qcril_pre_init_lock_held\", \"u:object_r:radio_prop:s0\"},\n      {\"rild.libpath\", \"u:object_r:default_prop:s0\"},\n      {\"ro.allow.mock.location\", \"u:object_r:default_prop:s0\"},\n      {\"ro.audio.flinger_standbytime_ms\", \"u:object_r:default_prop:s0\"},\n      {\"ro.baseband\", \"u:object_r:default_prop:s0\"},\n      {\"ro.bionic.ld.warning\", \"u:object_r:default_prop:s0\"},\n      {\"ro.board.platform\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.baseband\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.bootloader\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.bootreason\", \"u:object_r:bootloader_boot_reason_prop:s0\"},\n      {\"ro.boot.dlcomplete\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.emmc\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.flash.locked\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.hardware\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.hardware.sku\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.revision\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.serialno\", \"u:object_r:serialno_prop:s0\"},\n      {\"ro.boot.verifiedbootstate\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.veritymode\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boot.wificountrycode\", \"u:object_r:default_prop:s0\"},\n      {\"ro.bootimage.build.date\", \"u:object_r:default_prop:s0\"},\n      {\"ro.bootimage.build.date.utc\", \"u:object_r:default_prop:s0\"},\n      {\"ro.bootimage.build.fingerprint\", \"u:object_r:default_prop:s0\"},\n      {\"ro.bootloader\", \"u:object_r:default_prop:s0\"},\n      {\"ro.bootmode\", \"u:object_r:default_prop:s0\"},\n      {\"ro.boottime.adbd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.atfwd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.audioserver\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.bootanim\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.bullhead-sh\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.cameraserver\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.cnd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.cnss-daemon\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.cnss_diag\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.configstore-hal-1-0\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.console\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.devstart_sh\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.drm\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.dumpstate-1-0\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.flash-nanohub-fw\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.fps_hal\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.gatekeeperd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.gralloc-2-0\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.healthd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.hidl_memory\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.hwservicemanager\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.imsdatadaemon\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.imsqmidaemon\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.init\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.init.first_stage\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.init.cold_boot_wait\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.init.mount_all.default\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.init.selinux\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.installd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.irsc_util\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.keystore\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.lmkd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.loc_launcher\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.logd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.logd-reinit\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.media\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.mediadrm\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.mediaextractor\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.mediametrics\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.msm_irqbalance\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.netd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.netmgrd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.per_mgr\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.per_proxy\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.perfd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.qcamerasvr\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.qmuxd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.qseecomd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.qti\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.ril-daemon\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.rmt_storage\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.servicemanager\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.ss_ramdump\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.start_hci_filter\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.storaged\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.surfaceflinger\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.thermal-engine\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.time_daemon\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.tombstoned\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.ueventd\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.update_engine\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.usb-hal-1-0\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.vndservicemanager\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.vold\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.webview_zygote32\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.wifi_hal_legacy\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.wificond\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.zygote\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.boottime.zygote_secondary\", \"u:object_r:boottime_prop:s0\"},\n      {\"ro.bt.bdaddr_path\", \"u:object_r:bluetooth_prop:s0\"},\n      {\"ro.build.characteristics\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.date\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.date.utc\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.description\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.display.id\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.expect.baseband\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.expect.bootloader\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.fingerprint\", \"u:object_r:fingerprint_prop:s0\"},\n      {\"ro.build.flavor\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.host\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.id\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.product\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.tags\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.type\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.user\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.all_codenames\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.base_os\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.codename\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.incremental\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.preview_sdk\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.release\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.sdk\", \"u:object_r:default_prop:s0\"},\n      {\"ro.build.version.security_patch\", \"u:object_r:default_prop:s0\"},\n      {\"ro.camera.notify_nfc\", \"u:object_r:default_prop:s0\"},\n      {\"ro.carrier\", \"u:object_r:default_prop:s0\"},\n      {\"ro.com.android.dataroaming\", \"u:object_r:default_prop:s0\"},\n      {\"ro.config.alarm_alert\", \"u:object_r:config_prop:s0\"},\n      {\"ro.config.notification_sound\", \"u:object_r:config_prop:s0\"},\n      {\"ro.config.ringtone\", \"u:object_r:config_prop:s0\"},\n      {\"ro.config.vc_call_vol_steps\", \"u:object_r:config_prop:s0\"},\n      {\"ro.crypto.fs_crypto_blkdev\", \"u:object_r:vold_prop:s0\"},\n      {\"ro.crypto.state\", \"u:object_r:vold_prop:s0\"},\n      {\"ro.crypto.type\", \"u:object_r:vold_prop:s0\"},\n      {\"ro.dalvik.vm.native.bridge\", \"u:object_r:dalvik_prop:s0\"},\n      {\"ro.debuggable\", \"u:object_r:default_prop:s0\"},\n      {\"ro.organization_owned\", \"u:object_r:device_logging_prop:s0\"},\n      {\"ro.expect.recovery_id\", \"u:object_r:default_prop:s0\"},\n      {\"ro.frp.pst\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hardware\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.drop_shadow_cache_size\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.gradient_cache_size\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.layer_cache_size\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.path_cache_size\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.r_buffer_cache_size\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.text_large_cache_height\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.text_large_cache_width\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.text_small_cache_height\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.text_small_cache_width\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.texture_cache_flushrate\", \"u:object_r:default_prop:s0\"},\n      {\"ro.hwui.texture_cache_size\", \"u:object_r:default_prop:s0\"},\n      {\"ro.min_freq_0\", \"u:object_r:default_prop:s0\"},\n      {\"ro.min_freq_4\", \"u:object_r:default_prop:s0\"},\n      {\"ro.oem_unlock_supported\", \"u:object_r:default_prop:s0\"},\n      {\"ro.opengles.version\", \"u:object_r:default_prop:s0\"},\n      {\"ro.persistent_properties.ready\", \"u:object_r:persistent_properties_ready_prop:s0\"},\n      {\"ro.product.board\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.brand\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.cpu.abi\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.cpu.abilist\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.cpu.abilist32\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.cpu.abilist64\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.device\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.first_api_level\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.locale\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.manufacturer\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.model\", \"u:object_r:default_prop:s0\"},\n      {\"ro.product.name\", \"u:object_r:default_prop:s0\"},\n      {\"ro.property_service.version\", \"u:object_r:default_prop:s0\"},\n      {\"ro.qc.sdk.audio.fluencetype\", \"u:object_r:default_prop:s0\"},\n      {\"ro.recovery_id\", \"u:object_r:default_prop:s0\"},\n      {\"ro.revision\", \"u:object_r:default_prop:s0\"},\n      {\"ro.ril.svdo\", \"u:object_r:radio_prop:s0\"},\n      {\"ro.ril.svlte1x\", \"u:object_r:radio_prop:s0\"},\n      {\"ro.runtime.firstboot\", \"u:object_r:firstboot_prop:s0\"},\n      {\"ro.secure\", \"u:object_r:default_prop:s0\"},\n      {\"ro.serialno\", \"u:object_r:serialno_prop:s0\"},\n      {\"ro.sf.lcd_density\", \"u:object_r:default_prop:s0\"},\n      {\"ro.telephony.call_ring.multiple\", \"u:object_r:default_prop:s0\"},\n      {\"ro.telephony.default_cdma_sub\", \"u:object_r:default_prop:s0\"},\n      {\"ro.telephony.default_network\", \"u:object_r:default_prop:s0\"},\n      {\"ro.treble.enabled\", \"u:object_r:default_prop:s0\"},\n      {\"ro.vendor.build.date\", \"u:object_r:default_prop:s0\"},\n      {\"ro.vendor.build.date.utc\", \"u:object_r:default_prop:s0\"},\n      {\"ro.vendor.build.fingerprint\", \"u:object_r:default_prop:s0\"},\n      {\"ro.vendor.extension_library\", \"u:object_r:default_prop:s0\"},\n      {\"ro.wifi.channels\", \"u:object_r:default_prop:s0\"},\n      {\"ro.zygote\", \"u:object_r:default_prop:s0\"},\n      {\"security.perf_harden\", \"u:object_r:shell_prop:s0\"},\n      {\"sensors.contexthub.lid_state\", \"u:object_r:contexthub_prop:s0\"},\n      {\"service.adb.root\", \"u:object_r:shell_prop:s0\"},\n      {\"service.bootanim.exit\", \"u:object_r:system_prop:s0\"},\n      {\"service.sf.present_timestamp\", \"u:object_r:system_prop:s0\"},\n      {\"sys.boot.reason\", \"u:object_r:system_boot_reason_prop:s0\"},\n      {\"sys.boot_completed\", \"u:object_r:system_prop:s0\"},\n      {\"sys.ims.QMI_DAEMON_STATUS\", \"u:object_r:qcom_ims_prop:s0\"},\n      {\"sys.listeners.registered\", \"u:object_r:qseecomtee_prop:s0\"},\n      {\"sys.logbootcomplete\", \"u:object_r:system_prop:s0\"},\n      {\"sys.qcom.devup\", \"u:object_r:system_prop:s0\"},\n      {\"sys.sysctl.extra_free_kbytes\", \"u:object_r:system_prop:s0\"},\n      {\"sys.usb.config\", \"u:object_r:system_radio_prop:s0\"},\n      {\"sys.usb.configfs\", \"u:object_r:system_radio_prop:s0\"},\n      {\"sys.usb.controller\", \"u:object_r:system_prop:s0\"},\n      {\"sys.usb.ffs.aio_compat\", \"u:object_r:ffs_prop:s0\"},\n      {\"sys.usb.ffs.max_read\", \"u:object_r:ffs_prop:s0\"},\n      {\"sys.usb.ffs.max_write\", \"u:object_r:ffs_prop:s0\"},\n      {\"sys.usb.ffs.ready\", \"u:object_r:ffs_prop:s0\"},\n      {\"sys.usb.mtp.device_type\", \"u:object_r:system_prop:s0\"},\n      {\"sys.usb.state\", \"u:object_r:system_prop:s0\"},\n      {\"telephony.lteOnCdmaDevice\", \"u:object_r:default_prop:s0\"},\n      {\"tombstoned.max_tombstone_count\", \"u:object_r:default_prop:s0\"},\n      {\"vidc.debug.perf.mode\", \"u:object_r:default_prop:s0\"},\n      {\"vidc.enc.dcvs.extra-buff-count\", \"u:object_r:default_prop:s0\"},\n      {\"vold.decrypt\", \"u:object_r:vold_prop:s0\"},\n      {\"vold.has_adoptable\", \"u:object_r:vold_prop:s0\"},\n      {\"vold.post_fs_data_done\", \"u:object_r:vold_prop:s0\"},\n      {\"wc_transport.clean_up\", \"u:object_r:wc_transport_prop:s0\"},\n      {\"wc_transport.hci_filter_status\", \"u:object_r:wc_transport_prop:s0\"},\n      {\"wc_transport.ref_count\", \"u:object_r:wc_transport_prop:s0\"},\n      {\"wc_transport.soc_initialized\", \"u:object_r:wc_transport_prop:s0\"},\n      {\"wc_transport.start_hci\", \"u:object_r:wc_transport_prop:s0\"},\n      {\"wc_transport.vnd_power\", \"u:object_r:wc_transport_prop:s0\"},\n      {\"wifi.interface\", \"u:object_r:default_prop:s0\"},\n      {\"wifi.supplicant_scan_interval\", \"u:object_r:default_prop:s0\"},\n  };\n\n  for (const auto& [property, context] : properties_and_contexts) {\n    const char* returned_context;\n    property_info_area->GetPropertyInfo(property.c_str(), &returned_context, nullptr);\n    EXPECT_EQ(context, returned_context) << property;\n  }\n}\n\nTEST(propertyinfoserializer, GetPropertyInfo_prefix_without_dot) {\n  auto property_info = std::vector<PropertyInfoEntry>{\n      {\"persist.radio\", \"1st\", \"1st\", false},\n      {\"persist.radio.something.else.here\", \"2nd\", \"2nd\", false},\n  };\n\n  auto serialized_trie = std::string();\n  auto build_trie_error = std::string();\n  ASSERT_TRUE(BuildTrie(property_info, \"default\", \"default\", &serialized_trie, &build_trie_error))\n      << build_trie_error;\n\n  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());\n\n  const char* context;\n  const char* type;\n  property_info_area->GetPropertyInfo(\"persist.radio\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.subproperty\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"persist.radiowords\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.long.long.long.sub.property\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.something.else.here\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.something.else.here2\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.something.else.here.after\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.something.else.nothere\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.something.else\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n}\n\nTEST(propertyinfoserializer, GetPropertyInfo_prefix_with_dot_vs_without) {\n  auto property_info = std::vector<PropertyInfoEntry>{\n      {\"persist.\", \"1st\", \"1st\", false},\n      {\"persist.radio\", \"2nd\", \"2nd\", false},\n      {\"persist.radio.long.property.exact.match\", \"3rd\", \"3rd\", true},\n  };\n\n  auto serialized_trie = std::string();\n  auto build_trie_error = std::string();\n  ASSERT_TRUE(BuildTrie(property_info, \"default\", \"default\", &serialized_trie, &build_trie_error))\n      << build_trie_error;\n\n  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());\n\n  const char* context;\n  const char* type;\n  property_info_area->GetPropertyInfo(\"persist.notradio\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"1st\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.subproperty\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"persist.radiowords\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.long.property.prefix.match\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"2nd\", type);\n  property_info_area->GetPropertyInfo(\"persist.radio.long.property.exact.match\", &context, &type);\n  EXPECT_STREQ(\"3rd\", context);\n  EXPECT_STREQ(\"3rd\", type);\n}\n\nTEST(propertyinfoserializer, GetPropertyInfo_empty_context_and_type) {\n  auto property_info = std::vector<PropertyInfoEntry>{\n      {\"persist.\", \"1st\", \"\", false},\n      {\"persist.dot_prefix.\", \"2nd\", \"\", false},\n      {\"persist.non_dot_prefix\", \"3rd\", \"\", false},\n      {\"persist.exact_match\", \"\", \"\", true},\n      {\"persist.dot_prefix2.\", \"\", \"4th\", false},\n      {\"persist.non_dot_prefix2\", \"\", \"5th\", false},\n  };\n\n  auto serialized_trie = std::string();\n  auto build_trie_error = std::string();\n  ASSERT_TRUE(BuildTrie(property_info, \"default\", \"default\", &serialized_trie, &build_trie_error))\n      << build_trie_error;\n\n  auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());\n\n  const char* context;\n  const char* type;\n  property_info_area->GetPropertyInfo(\"notpersist.radio.something\", &context, &type);\n  EXPECT_STREQ(\"default\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"persist.nomatch\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"persist.dot_prefix.something\", &context, &type);\n  EXPECT_STREQ(\"2nd\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"persist.non_dot_prefix.something\", &context, &type);\n  EXPECT_STREQ(\"3rd\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"persist.exact_match\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"default\", type);\n  property_info_area->GetPropertyInfo(\"persist.dot_prefix2.something\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"4th\", type);\n  property_info_area->GetPropertyInfo(\"persist.non_dot_prefix2.something\", &context, &type);\n  EXPECT_STREQ(\"1st\", context);\n  EXPECT_STREQ(\"5th\", type);\n}\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/space_tokenizer.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef PROPERTY_INFO_SERIALIZER_SPACE_TOKENIZER_H\n#define PROPERTY_INFO_SERIALIZER_SPACE_TOKENIZER_H\n\nnamespace android {\nnamespace properties {\n\nclass SpaceTokenizer {\n public:\n  SpaceTokenizer(const std::string& string)\n      : string_(string), it_(string_.begin()), end_(string_.end()) {}\n\n  std::string GetNext() {\n    auto next = std::string();\n    while (it_ != end_ && !isspace(*it_)) {\n      next.push_back(*it_++);\n    }\n    while (it_ != end_ && isspace(*it_)) {\n      it_++;\n    }\n    return next;\n  }\n\n  std::string GetRemaining() { return std::string(it_, end_); }\n\n private:\n  std::string string_;\n  std::string::const_iterator it_;\n  std::string::const_iterator end_;\n};\n\n}  // namespace properties\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/trie_builder.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"trie_builder.h\"\n\n#include <android-base/strings.h>\n\nusing android::base::Split;\n\nnamespace android {\nnamespace properties {\n\nTrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_type)\n    : builder_root_(\"root\") {\n  auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);\n  builder_root_.set_context(context_pointer);\n  auto* type_pointer = StringPointerFromContainer(default_type, &types_);\n  builder_root_.set_type(type_pointer);\n}\n\nbool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,\n                            const std::string& type, bool exact, std::string* error) {\n  auto* context_pointer = StringPointerFromContainer(context, &contexts_);\n  auto* type_pointer = StringPointerFromContainer(type, &types_);\n  return AddToTrie(name, context_pointer, type_pointer, exact, error);\n}\n\nbool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,\n                            const std::string* type, bool exact, std::string* error) {\n  TrieBuilderNode* current_node = &builder_root_;\n\n  auto name_pieces = Split(name, \".\");\n\n  bool ends_with_dot = false;\n  if (name_pieces.back().empty()) {\n    ends_with_dot = true;\n    name_pieces.pop_back();\n  }\n\n  // Move us to the final node that we care about, adding incremental nodes if necessary.\n  while (name_pieces.size() > 1) {\n    auto child = current_node->FindChild(name_pieces.front());\n    if (child == nullptr) {\n      child = current_node->AddChild(name_pieces.front());\n    }\n    if (child == nullptr) {\n      *error = \"Unable to allocate Trie node\";\n      return false;\n    }\n    current_node = child;\n    name_pieces.erase(name_pieces.begin());\n  }\n\n  // Store our context based on what type of match it is.\n  if (exact) {\n    if (!current_node->AddExactMatchContext(name_pieces.front(), context, type)) {\n      *error = \"Duplicate exact match detected for '\" + name + \"'\";\n      return false;\n    }\n  } else if (!ends_with_dot) {\n    if (!current_node->AddPrefixContext(name_pieces.front(), context, type)) {\n      *error = \"Duplicate prefix match detected for '\" + name + \"'\";\n      return false;\n    }\n  } else {\n    auto child = current_node->FindChild(name_pieces.front());\n    if (child == nullptr) {\n      child = current_node->AddChild(name_pieces.front());\n    }\n    if (child == nullptr) {\n      *error = \"Unable to allocate Trie node\";\n      return false;\n    }\n    if (child->context() != nullptr || child->type() != nullptr) {\n      *error = \"Duplicate prefix match detected for '\" + name + \"'\";\n      return false;\n    }\n    child->set_context(context);\n    child->set_type(type);\n  }\n  return true;\n}\n\nconst std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,\n                                                           std::set<std::string>* container) {\n  // Get a pointer to the string in a given set, such that we only ever serialize each string once.\n  auto [iterator, _] = container->emplace(string);\n  return &(*iterator);\n}\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/trie_builder.h",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#ifndef PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H\n#define PROPERTY_INFO_SERIALIZER_TRIE_BUILDER_H\n\n#include <memory>\n#include <set>\n#include <string>\n#include <vector>\n\nnamespace android {\nnamespace properties {\n\nstruct PropertyEntryBuilder {\n  PropertyEntryBuilder() : context(nullptr), type(nullptr) {}\n  PropertyEntryBuilder(const std::string& name, const std::string* context, const std::string* type)\n      : name(name), context(context), type(type) {}\n  std::string name;\n  const std::string* context;\n  const std::string* type;\n};\n\nclass TrieBuilderNode {\n public:\n  TrieBuilderNode(const std::string& name) : property_entry_(name, nullptr, nullptr) {}\n\n  TrieBuilderNode* FindChild(const std::string& name) {\n    for (auto& child : children_) {\n      if (child.name() == name) return &child;\n    }\n    return nullptr;\n  }\n\n  const TrieBuilderNode* FindChild(const std::string& name) const {\n    for (const auto& child : children_) {\n      if (child.name() == name) return &child;\n    }\n    return nullptr;\n  }\n\n  TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }\n\n  bool AddPrefixContext(const std::string& prefix, const std::string* context,\n                        const std::string* type) {\n    if (std::find_if(prefixes_.begin(), prefixes_.end(),\n                     [&prefix](const auto& t) { return t.name == prefix; }) != prefixes_.end()) {\n      return false;\n    }\n\n    prefixes_.emplace_back(prefix, context, type);\n    return true;\n  }\n\n  bool AddExactMatchContext(const std::string& exact_match, const std::string* context,\n                            const std::string* type) {\n    if (std::find_if(exact_matches_.begin(), exact_matches_.end(), [&exact_match](const auto& t) {\n          return t.name == exact_match;\n        }) != exact_matches_.end()) {\n      return false;\n    }\n\n    exact_matches_.emplace_back(exact_match, context, type);\n    return true;\n  }\n\n  const std::string& name() const { return property_entry_.name; }\n  const std::string* context() const { return property_entry_.context; }\n  void set_context(const std::string* context) { property_entry_.context = context; }\n  const std::string* type() const { return property_entry_.type; }\n  void set_type(const std::string* type) { property_entry_.type = type; }\n\n  const PropertyEntryBuilder property_entry() const { return property_entry_; }\n\n  const std::vector<TrieBuilderNode>& children() const { return children_; }\n  const std::vector<PropertyEntryBuilder>& prefixes() const { return prefixes_; }\n  const std::vector<PropertyEntryBuilder>& exact_matches() const { return exact_matches_; }\n\n private:\n  PropertyEntryBuilder property_entry_;\n  std::vector<TrieBuilderNode> children_;\n  std::vector<PropertyEntryBuilder> prefixes_;\n  std::vector<PropertyEntryBuilder> exact_matches_;\n};\n\nclass TrieBuilder {\n public:\n  TrieBuilder(const std::string& default_context, const std::string& default_type);\n  bool AddToTrie(const std::string& name, const std::string& context, const std::string& type,\n                 bool exact, std::string* error);\n\n  const TrieBuilderNode builder_root() const { return builder_root_; }\n  const std::set<std::string>& contexts() const { return contexts_; }\n  const std::set<std::string>& types() const { return types_; }\n\n private:\n  bool AddToTrie(const std::string& name, const std::string* context, const std::string* type,\n                 bool exact, std::string* error);\n  const std::string* StringPointerFromContainer(const std::string& string,\n                                                std::set<std::string>* container);\n\n  TrieBuilderNode builder_root_;\n  std::set<std::string> contexts_;\n  std::set<std::string> types_;\n};\n\n}  // namespace properties\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/trie_builder_test.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"trie_builder.h\"\n\n#include <gtest/gtest.h>\n\nnamespace android {\nnamespace properties {\n\nTEST(propertyinfoserializer, BuildTrie_Simple) {\n  auto trie_builder = TrieBuilder(\"default\", \"default_type\");\n\n  // Add test data to tree\n  auto error = std::string();\n  EXPECT_TRUE(trie_builder.AddToTrie(\"test.\", \"1st\", \"1st_type\", false, &error));\n  EXPECT_TRUE(trie_builder.AddToTrie(\"test.test\", \"2nd\", \"2nd_type\", false, &error));\n  EXPECT_TRUE(trie_builder.AddToTrie(\"test.test1\", \"3rd\", \"3rd_type\", true, &error));\n  EXPECT_TRUE(trie_builder.AddToTrie(\"test.test2\", \"3rd\", \"3rd_type\", true, &error));\n  EXPECT_TRUE(trie_builder.AddToTrie(\"test.test3\", \"3rd\", \"3rd_type\", true, &error));\n  EXPECT_TRUE(trie_builder.AddToTrie(\"this.is.a.long.string\", \"4th\", \"4th_type\", true, &error));\n\n  ASSERT_EQ(5U, trie_builder.contexts().size());\n  ASSERT_EQ(5U, trie_builder.types().size());\n\n  auto& builder_root = trie_builder.builder_root();\n\n  // Check the root node\n  EXPECT_EQ(\"root\", builder_root.name());\n  ASSERT_NE(nullptr, builder_root.context());\n  EXPECT_EQ(\"default\", *builder_root.context());\n  ASSERT_NE(nullptr, builder_root.type());\n  EXPECT_EQ(\"default_type\", *builder_root.type());\n\n  EXPECT_EQ(0U, builder_root.prefixes().size());\n  EXPECT_EQ(0U, builder_root.exact_matches().size());\n\n  ASSERT_EQ(2U, builder_root.children().size());\n\n  // Check the 'test.' node\n  auto* test_node = builder_root.FindChild(\"test\");\n  EXPECT_EQ(\"test\", test_node->name());\n  ASSERT_NE(nullptr, test_node->context());\n  EXPECT_EQ(\"1st\", *test_node->context());\n  ASSERT_NE(nullptr, test_node->type());\n  EXPECT_EQ(\"1st_type\", *test_node->type());\n\n  EXPECT_EQ(0U, test_node->children().size());\n  EXPECT_EQ(1U, test_node->prefixes().size());\n  {\n    auto& property_entry = test_node->prefixes()[0];\n    EXPECT_EQ(\"test\", property_entry.name);\n    ASSERT_NE(nullptr, property_entry.context);\n    EXPECT_EQ(\"2nd\", *property_entry.context);\n    ASSERT_NE(nullptr, property_entry.type);\n    EXPECT_EQ(\"2nd_type\", *property_entry.type);\n  }\n  EXPECT_EQ(3U, test_node->exact_matches().size());\n  EXPECT_EQ(\"test1\", test_node->exact_matches()[0].name);\n  EXPECT_EQ(\"test2\", test_node->exact_matches()[1].name);\n  EXPECT_EQ(\"test3\", test_node->exact_matches()[2].name);\n\n  ASSERT_NE(nullptr, test_node->exact_matches()[0].context);\n  ASSERT_NE(nullptr, test_node->exact_matches()[1].context);\n  ASSERT_NE(nullptr, test_node->exact_matches()[2].context);\n  EXPECT_EQ(\"3rd\", *test_node->exact_matches()[0].context);\n  EXPECT_EQ(\"3rd\", *test_node->exact_matches()[1].context);\n  EXPECT_EQ(\"3rd\", *test_node->exact_matches()[2].context);\n\n  ASSERT_NE(nullptr, test_node->exact_matches()[0].type);\n  ASSERT_NE(nullptr, test_node->exact_matches()[1].type);\n  ASSERT_NE(nullptr, test_node->exact_matches()[2].type);\n  EXPECT_EQ(\"3rd_type\", *test_node->exact_matches()[0].type);\n  EXPECT_EQ(\"3rd_type\", *test_node->exact_matches()[1].type);\n  EXPECT_EQ(\"3rd_type\", *test_node->exact_matches()[2].type);\n\n  // Check the long string node\n  auto expect_empty_one_child = [](auto* node) {\n    ASSERT_NE(nullptr, node);\n    EXPECT_EQ(nullptr, node->context());\n    EXPECT_EQ(nullptr, node->type());\n    EXPECT_EQ(0U, node->prefixes().size());\n    EXPECT_EQ(0U, node->exact_matches().size());\n    EXPECT_EQ(1U, node->children().size());\n  };\n\n  // Start with 'this'\n  auto* long_string_node = builder_root.FindChild(\"this\");\n  expect_empty_one_child(long_string_node);\n\n  // Move to 'is'\n  long_string_node = long_string_node->FindChild(\"is\");\n  expect_empty_one_child(long_string_node);\n\n  // Move to 'a'\n  long_string_node = long_string_node->FindChild(\"a\");\n  expect_empty_one_child(long_string_node);\n\n  // Move to 'long'\n  long_string_node = long_string_node->FindChild(\"long\");\n  EXPECT_EQ(0U, long_string_node->prefixes().size());\n  EXPECT_EQ(1U, long_string_node->exact_matches().size());\n  EXPECT_EQ(0U, long_string_node->children().size());\n\n  {\n    auto& property_entry = long_string_node->exact_matches()[0];\n    EXPECT_EQ(\"string\", property_entry.name);\n    ASSERT_NE(nullptr, property_entry.context);\n    EXPECT_EQ(\"4th\", *property_entry.context);\n    ASSERT_NE(nullptr, property_entry.type);\n    EXPECT_EQ(\"4th_type\", *property_entry.type);\n  }\n}\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/trie_node_arena.h",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#ifndef PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H\n#define PROPERTY_INFO_SERIALIZER_TRIE_NODE_ARENA_H\n\n#include <string>\n#include <vector>\n\nnamespace android {\nnamespace properties {\n\ntemplate <typename T>\nclass ArenaObjectPointer {\n public:\n  ArenaObjectPointer(std::string& arena_data, uint32_t offset)\n      : arena_data_(arena_data), offset_(offset) {}\n\n  T* operator->() { return reinterpret_cast<T*>(arena_data_.data() + offset_); }\n\n private:\n  std::string& arena_data_;\n  uint32_t offset_;\n};\n\nclass TrieNodeArena {\n public:\n  TrieNodeArena() : current_data_pointer_(0) {}\n\n  // We can't return pointers to objects since data_ may move when reallocated, thus invalidating\n  // any pointers.  Therefore we return an ArenaObjectPointer, which always accesses elements via\n  // data_ + offset.\n  template <typename T>\n  ArenaObjectPointer<T> AllocateObject(uint32_t* return_offset) {\n    uint32_t offset;\n    AllocateData(sizeof(T), &offset);\n    if (return_offset) *return_offset = offset;\n    return ArenaObjectPointer<T>(data_, offset);\n  }\n\n  uint32_t AllocateUint32Array(int length) {\n    uint32_t offset;\n    AllocateData(sizeof(uint32_t) * length, &offset);\n    return offset;\n  }\n\n  uint32_t* uint32_array(uint32_t offset) {\n    return reinterpret_cast<uint32_t*>(data_.data() + offset);\n  }\n\n  uint32_t AllocateAndWriteString(const std::string& string) {\n    uint32_t offset;\n    char* data = static_cast<char*>(AllocateData(string.size() + 1, &offset));\n    strcpy(data, string.c_str());\n    return offset;\n  }\n\n  void AllocateAndWriteUint32(uint32_t value) {\n    auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));\n    *location = value;\n  }\n\n  void* AllocateData(size_t size, uint32_t* offset) {\n    size_t aligned_size = size + (sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);\n\n    if (current_data_pointer_ + aligned_size > data_.size()) {\n      auto new_size = (current_data_pointer_ + aligned_size + data_.size()) * 2;\n      data_.resize(new_size, '\\0');\n    }\n    if (offset) *offset = current_data_pointer_;\n\n    uint32_t return_offset = current_data_pointer_;\n    current_data_pointer_ += aligned_size;\n    return &data_[0] + return_offset;\n  }\n\n  uint32_t size() const { return current_data_pointer_; }\n\n  const std::string& data() const { return data_; }\n\n  std::string truncated_data() const {\n    auto result = data_;\n    result.resize(current_data_pointer_);\n    return result;\n  }\n\n private:\n  std::string data_;\n  uint32_t current_data_pointer_;\n};\n\n}  // namespace properties\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/trie_serializer.cpp",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#include \"trie_serializer.h\"\n\n#include <algorithm>\n\nnamespace android {\nnamespace properties {\n\n// Serialized strings contains:\n// 1) A uint32_t count of elements in the below array\n// 2) A sorted array of uint32_t offsets pointing to null terminated strings\n// 3) Each of the null terminated strings themselves packed back to back\n// This returns the offset into arena where the serialized strings start.\nvoid TrieSerializer::SerializeStrings(const std::set<std::string>& strings) {\n  arena_->AllocateAndWriteUint32(strings.size());\n\n  // Allocate space for the array.\n  uint32_t offset_array_offset = arena_->AllocateUint32Array(strings.size());\n\n  // Write offset pointers and strings; these are already alphabetically sorted by virtue of being\n  // in an std::set.\n  auto it = strings.begin();\n  for (unsigned int i = 0; i < strings.size(); ++i, ++it) {\n    uint32_t string_offset = arena_->AllocateAndWriteString(*it);\n    arena_->uint32_array(offset_array_offset)[i] = string_offset;\n  }\n}\n\nuint32_t TrieSerializer::WritePropertyEntry(const PropertyEntryBuilder& property_entry) {\n  uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()\n                               ? serialized_info()->FindContextIndex(property_entry.context->c_str())\n                               : ~0u;\n  uint32_t type_index = property_entry.type != nullptr && !property_entry.type->empty()\n                            ? serialized_info()->FindTypeIndex(property_entry.type->c_str())\n                            : ~0u;\n  uint32_t offset;\n  auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);\n  serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);\n  serialized_property_entry->namelen = property_entry.name.size();\n  serialized_property_entry->context_index = context_index;\n  serialized_property_entry->type_index = type_index;\n  return offset;\n}\n\nuint32_t TrieSerializer::WriteTrieNode(const TrieBuilderNode& builder_node) {\n  uint32_t trie_offset;\n  auto trie = arena_->AllocateObject<TrieNodeInternal>(&trie_offset);\n\n  trie->property_entry = WritePropertyEntry(builder_node.property_entry());\n\n  // Write prefix matches\n  auto sorted_prefix_matches = builder_node.prefixes();\n  // Prefixes are sorted by descending length\n  std::sort(sorted_prefix_matches.begin(), sorted_prefix_matches.end(),\n            [](const auto& lhs, const auto& rhs) { return lhs.name.size() > rhs.name.size(); });\n\n  trie->num_prefixes = sorted_prefix_matches.size();\n\n  uint32_t prefix_entries_array_offset = arena_->AllocateUint32Array(sorted_prefix_matches.size());\n  trie->prefix_entries = prefix_entries_array_offset;\n\n  for (unsigned int i = 0; i < sorted_prefix_matches.size(); ++i) {\n    uint32_t property_entry_offset = WritePropertyEntry(sorted_prefix_matches[i]);\n    arena_->uint32_array(prefix_entries_array_offset)[i] = property_entry_offset;\n  }\n\n  // Write exact matches\n  auto sorted_exact_matches = builder_node.exact_matches();\n  // Exact matches are sorted alphabetically\n  std::sort(sorted_exact_matches.begin(), sorted_exact_matches.end(),\n            [](const auto& lhs, const auto& rhs) { return lhs.name < rhs.name; });\n\n  trie->num_exact_matches = sorted_exact_matches.size();\n\n  uint32_t exact_match_entries_array_offset =\n      arena_->AllocateUint32Array(sorted_exact_matches.size());\n  trie->exact_match_entries = exact_match_entries_array_offset;\n\n  for (unsigned int i = 0; i < sorted_exact_matches.size(); ++i) {\n    uint32_t property_entry_offset = WritePropertyEntry(sorted_exact_matches[i]);\n    arena_->uint32_array(exact_match_entries_array_offset)[i] = property_entry_offset;\n  }\n\n  // Write children\n  auto sorted_children = builder_node.children();\n  std::sort(sorted_children.begin(), sorted_children.end(),\n            [](const auto& lhs, const auto& rhs) { return lhs.name() < rhs.name(); });\n\n  trie->num_child_nodes = sorted_children.size();\n  uint32_t children_offset_array_offset = arena_->AllocateUint32Array(sorted_children.size());\n  trie->child_nodes = children_offset_array_offset;\n\n  for (unsigned int i = 0; i < sorted_children.size(); ++i) {\n    arena_->uint32_array(children_offset_array_offset)[i] = WriteTrieNode(sorted_children[i]);\n  }\n  return trie_offset;\n}\n\nTrieSerializer::TrieSerializer() {}\n\nstd::string TrieSerializer::SerializeTrie(const TrieBuilder& trie_builder) {\n  arena_.reset(new TrieNodeArena());\n\n  auto header = arena_->AllocateObject<PropertyInfoAreaHeader>(nullptr);\n  header->current_version = 1;\n  header->minimum_supported_version = 1;\n\n  // Store where we're about to write the contexts.\n  header->contexts_offset = arena_->size();\n  SerializeStrings(trie_builder.contexts());\n\n  // Store where we're about to write the types.\n  header->types_offset = arena_->size();\n  SerializeStrings(trie_builder.types());\n\n  // We need to store size() up to this point now for Find*Offset() to work.\n  header->size = arena_->size();\n\n  uint32_t root_trie_offset = WriteTrieNode(trie_builder.builder_root());\n  header->root_offset = root_trie_offset;\n\n  // Record the real size now that we've written everything\n  header->size = arena_->size();\n\n  return arena_->truncated_data();\n}\n\n}  // namespace properties\n}  // namespace android\n"
  },
  {
    "path": "property_service/libpropertyinfoserializer/trie_serializer.h",
    "content": "//\n// Copyright (C) 2017 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n#ifndef PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H\n#define PROPERTY_INFO_SERIALIZER_TRIE_SERIALIZER_H\n\n#include <string>\n#include <vector>\n\n#include \"property_info_parser/property_info_parser.h\"\n\n#include \"trie_builder.h\"\n#include \"trie_node_arena.h\"\n\nnamespace android {\nnamespace properties {\n\nclass TrieSerializer {\n public:\n  TrieSerializer();\n\n  std::string SerializeTrie(const TrieBuilder& trie_builder);\n\n private:\n  void SerializeStrings(const std::set<std::string>& strings);\n  uint32_t WritePropertyEntry(const PropertyEntryBuilder& property_entry);\n\n  // Writes a new TrieNode to arena, and recursively writes its children.\n  // Returns the offset within arena.\n  uint32_t WriteTrieNode(const TrieBuilderNode& builder_node);\n\n  const PropertyInfoArea* serialized_info() const {\n    return reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());\n  }\n\n  std::unique_ptr<TrieNodeArena> arena_;\n};\n\n}  // namespace properties\n}  // namespace android\n\n#endif\n"
  },
  {
    "path": "property_service/property_info_checker/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"property_info_checker\",\n    host_supported: true,\n    static_executable: true,\n    static_libs: [\n        \"libpropertyinfoserializer\",\n        \"libpropertyinfoparser\",\n        \"libbase\",\n        \"liblog\",\n        \"libsepol\",\n    ],\n    srcs: [\"property_info_checker.cpp\"],\n}\n"
  },
  {
    "path": "property_service/property_info_checker/property_info_checker.cpp",
    "content": "#include <iostream>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include <android-base/file.h>\n#include <property_info_parser/property_info_parser.h>\n#include <property_info_serializer/property_info_serializer.h>\n#include <sepol/context.h>\n#include <sepol/context_record.h>\n#include <sepol/handle.h>\n#include <sepol/policydb.h>\n#include <sepol/policydb/policydb.h>\n\nusing android::base::ReadFileToString;\nusing android::properties::BuildTrie;\nusing android::properties::ParsePropertyInfoFile;\nusing android::properties::PropertyInfoArea;\nusing android::properties::PropertyInfoEntry;\n\nclass ContextChecker {\n public:\n  ContextChecker()\n      : policy_file_(nullptr),\n        sepol_handle_(nullptr),\n        sepol_policy_file_(nullptr),\n        sepol_policy_db_(nullptr) {}\n\n  ~ContextChecker() {\n    if (sepol_policy_db_ != nullptr) {\n      sepol_policydb_free(sepol_policy_db_);\n    }\n\n    if (sepol_policy_file_ != nullptr) {\n      sepol_policy_file_free(sepol_policy_file_);\n    }\n\n    if (sepol_handle_ != nullptr) {\n      sepol_handle_destroy(sepol_handle_);\n    }\n\n    if (policy_file_ != nullptr) {\n      fclose(policy_file_);\n    }\n  }\n\n  bool Initialize(const char* policy_file) {\n    policy_file_ = fopen(policy_file, \"re\");\n    if (policy_file_ == nullptr) {\n      std::cerr << \"Could not open policy file, \" << policy_file << std::endl;\n      return false;\n    }\n\n    sepol_handle_ = sepol_handle_create();\n    if (sepol_handle_ == nullptr) {\n      std::cerr << \"Could not create policy handle.\" << std::endl;\n      return false;\n    }\n\n    if (sepol_policy_file_create(&sepol_policy_file_) < 0) {\n      std::cerr << \"Could not create policy file.\" << std::endl;\n      return false;\n    }\n\n    if (sepol_policydb_create(&sepol_policy_db_) < 0) {\n      std::cerr << \"Could not create policy db.\" << std::endl;\n      return false;\n    }\n\n    sepol_policy_file_set_fp(sepol_policy_file_, policy_file_);\n    sepol_policy_file_set_handle(sepol_policy_file_, sepol_handle_);\n\n    if (sepol_policydb_read(sepol_policy_db_, sepol_policy_file_) < 0) {\n      std::cerr << \"Could not read policy file into policy db.\" << std::endl;\n      return false;\n    }\n\n    auto* attr =\n        reinterpret_cast<type_datum*>(hashtab_search(policy_db_->p_types.table, \"property_type\"));\n    if (attr == nullptr || attr->flavor != TYPE_ATTRIB) {\n      std::cerr << \"'property_type' is not defined correctly.\" << std::endl;\n      return false;\n    }\n\n    property_type_bit_ = attr->s.value - 1;\n\n    return true;\n  }\n\n  bool CheckContext(const char* context) {\n    sepol_context_t* sepol_context_raw;\n    if (sepol_context_from_string(sepol_handle_, context, &sepol_context_raw) < 0) {\n      std::cerr << \"Could not allocate context for \" << context << std::endl;\n      return false;\n    }\n    auto sepol_context = std::unique_ptr<sepol_context_t, decltype(&sepol_context_free)>{\n        sepol_context_raw, sepol_context_free};\n\n    if (sepol_context_check(sepol_handle_, sepol_policy_db_, sepol_context.get()) < 0) {\n      std::cerr << \"Sepol context check failed for \" << context << std::endl;\n      return false;\n    }\n\n    const char* context_type = sepol_context_get_type(sepol_context.get());\n\n    auto* type =\n        reinterpret_cast<type_datum*>(hashtab_search(policy_db_->p_types.table, context_type));\n    if (type == nullptr) {\n      std::cerr << \"Could not find context '\" << context << \"' in policy database\" << std::endl;\n      return false;\n    }\n\n    if (type->flavor != TYPE_TYPE) {\n      std::cerr << \"Context '\" << context << \"' is not defined as a type in policy database\"\n                << std::endl;\n      return false;\n    }\n\n    if (!ebitmap_get_bit(&policy_db_->type_attr_map[type->s.value - 1], property_type_bit_)) {\n      std::cerr << \"Context '\" << context << \"' does not have property_type attribute\" << std::endl;\n      return false;\n    }\n\n    return true;\n  }\n\n private:\n  FILE* policy_file_;\n  sepol_handle_t* sepol_handle_;\n  sepol_policy_file_t* sepol_policy_file_;\n  union {\n    sepol_policydb_t* sepol_policy_db_;\n    policydb_t* policy_db_;\n  };\n  unsigned int property_type_bit_;\n};\n\nint main(int argc, char** argv) {\n  if (argc < 3) {\n    std::cerr << \"usage: \" << argv[0]\n              << \" COMPILED_SEPOLICY PROPERTY_INFO_FILE [PROPERTY_INFO_FILE]...\" << std::endl;\n    return -1;\n  }\n\n  auto property_info_entries = std::vector<PropertyInfoEntry>{};\n\n  for (int i = 2; i < argc; ++i) {\n    auto filename = argv[i];\n    auto file_contents = std::string{};\n    if (!ReadFileToString(filename, &file_contents)) {\n      std::cerr << \"Could not read properties from '\" << filename << \"'\" << std::endl;\n      return -1;\n    }\n\n    auto errors = std::vector<std::string>{};\n    ParsePropertyInfoFile(file_contents, true, &property_info_entries, &errors);\n    if (!errors.empty()) {\n      for (const auto& error : errors) {\n        std::cerr << \"Could not read line from '\" << filename << \"': \" << error << std::endl;\n      }\n      return -1;\n    }\n  }\n\n  auto serialized_contexts = std::string{};\n  auto build_trie_error = std::string{};\n\n  if (!BuildTrie(property_info_entries, \"u:object_r:default_prop:s0\", \"\\\\s*\", &serialized_contexts,\n                 &build_trie_error)) {\n    std::cerr << \"Unable to serialize property contexts: \" << build_trie_error << std::endl;\n    return -1;\n  }\n\n  auto checker = ContextChecker{};\n  if (!checker.Initialize(argv[1])) {\n    return -1;\n  }\n\n  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(serialized_contexts.data());\n  for (size_t i = 0; i < property_info_area->num_contexts(); ++i) {\n    if (!checker.CheckContext(property_info_area->context(i))) {\n      return -1;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "reboot/Android.bp",
    "content": "// Copyright 2013 The Android Open Source Project\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"reboot_defaults\",\n    srcs: [\"reboot.c\"],\n    shared_libs: [\"libcutils\"],\n    cflags: [\"-Werror\"],\n}\n\ncc_binary {\n    name: \"reboot\",\n    defaults: [\n        \"reboot_defaults\",\n    ],\n}\n\ncc_binary {\n    name: \"reboot.recovery\",\n    defaults: [\n        \"reboot_defaults\",\n    ],\n    recovery: true,\n    stem: \"reboot\",\n}\n"
  },
  {
    "path": "reboot/reboot.c",
    "content": "/*\n * Copyright (C) 2013 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <cutils/properties.h>\n#include <cutils/android_reboot.h>\n#include <unistd.h>\n\nint main(int argc, char* argv[]) {\n    int ret;\n    size_t prop_len;\n    char property_val[PROPERTY_VALUE_MAX];\n    static const char reboot[] = \"reboot\";\n    const char* cmd = reboot;\n    char* optarg = \"\";\n\n    opterr = 0;\n    do {\n        int c;\n\n        c = getopt(argc, argv, \"p\");\n\n        if (c == -1) {\n            break;\n        }\n\n        switch (c) {\n        case 'p':\n            cmd = \"shutdown\";\n            break;\n        case '?':\n            fprintf(stderr, \"usage: %s [-p] [rebootcommand]\\n\", argv[0]);\n            exit(EXIT_FAILURE);\n        }\n    } while (1);\n\n    if(argc > optind + 1) {\n        fprintf(stderr, \"%s: too many arguments\\n\", argv[0]);\n        exit(EXIT_FAILURE);\n    }\n\n    if (argc > optind)\n        optarg = argv[optind];\n    if (!optarg || !optarg[0]) optarg = \"shell\";\n\n    prop_len = snprintf(property_val, sizeof(property_val), \"%s,%s\", cmd, optarg);\n    if (prop_len >= sizeof(property_val)) {\n        fprintf(stderr, \"%s command too long: %s\\n\", cmd, optarg);\n        exit(EXIT_FAILURE);\n    }\n\n    ret = property_set(ANDROID_RB_PROPERTY, property_val);\n    if (ret < 0) {\n        perror(cmd);\n        exit(EXIT_FAILURE);\n    }\n\n    // Don't return early. Give the reboot command time to take effect\n    // to avoid messing up scripts which do \"adb shell reboot && adb wait-for-device\"\n    if (cmd == reboot) {\n        while (1) {\n            pause();\n        }\n    }\n\n    fprintf(stderr, \"Done\\n\");\n    return 0;\n}\n"
  },
  {
    "path": "rootdir/Android.bp",
    "content": "// Copyright 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nprebuilt_etc {\n    name: \"init.boringssl.zygote64_32.rc\",\n    src: \"init.boringssl.zygote64_32.rc\",\n    sub_dir: \"init/hw\",\n    symlinks: [\n        \"init.boringssl.zygote32.rc\",\n        \"init.boringssl.no_zygote.rc\",\n    ],\n}\n\nprebuilt_etc {\n    name: \"init.boringssl.zygote64.rc\",\n    src: \"init.boringssl.zygote64.rc\",\n    sub_dir: \"init/hw\",\n}\n\nprebuilt_etc {\n    name: \"init.rc\",\n    src: \"init.rc\",\n    sub_dir: \"init/hw\",\n    required: [\n        \"platform-bootclasspath\",\n        \"init.boringssl.zygote64.rc\",\n        \"init.boringssl.zygote64_32.rc\",\n    ],\n}\n\nprebuilt_etc {\n    name: \"ueventd.rc\",\n    src: \"ueventd.rc\",\n}\n\nprebuilt_etc {\n    name: \"ueventd.rc.recovery\",\n    src: \"ueventd.rc\",\n    recovery: true,\n    filename: \"ueventd.rc\",\n}\n\nfilegroup {\n    name: \"system_linker_config_json_file\",\n    srcs: [\"etc/linker.config.json\"],\n}\n\n// TODO(b/185211376) Scope the native APIs that microdroid will provide to the app payload\nprebuilt_etc {\n    name: \"public.libraries.android.txt\",\n    src: \"etc/public.libraries.android.txt\",\n    filename: \"public.libraries.txt\",\n    no_full_install: true,\n}\n\n// adb_debug.prop in debug ramdisk\nprebuilt_root {\n    name: \"adb_debug.prop\",\n    src: \"adb_debug.prop\",\n    debug_ramdisk: true,\n}\n\nprebuilt_etc {\n    name: \"init.zygote64.rc\",\n    src: \"init.zygote64.rc\",\n    sub_dir: \"init/hw\",\n}\n\nprebuilt_etc {\n    name: \"init.zygote32.rc\",\n    src: \"init.zygote32.rc\",\n    sub_dir: \"init/hw\",\n}\n\nprebuilt_etc {\n    name: \"init.zygote64_32.rc\",\n    src: \"init.zygote64_32.rc\",\n    sub_dir: \"init/hw\",\n}\n\nprebuilt_etc {\n    name: \"init.usb.rc\",\n    src: \"init.usb.rc\",\n    sub_dir: \"init/hw\",\n}\n\nprebuilt_etc {\n    name: \"init.usb.configfs.rc\",\n    src: \"init.usb.configfs.rc\",\n    sub_dir: \"init/hw\",\n}\n\nprebuilt_etc {\n    name: \"etc_hosts\",\n    src: \"etc/hosts\",\n    filename: \"hosts\",\n}\n\nprebuilt_etc {\n    name: \"init-debug.rc\",\n    src: \"init-debug.rc\",\n    sub_dir: \"init\",\n}\n\nprebuilt_etc {\n    name: \"init-mmd-prop.rc\",\n    src: \"init-mmd-prop.rc\",\n    sub_dir: \"init\",\n}\n\nprebuilt_etc {\n    name: \"asan.options\",\n    src: \"asan.options\",\n}\n\nsh_binary {\n    name: \"asan_extract\",\n    src: \"asan_extract.sh\",\n    init_rc: [\"asan_extract.rc\"],\n    // We need bzip2 on device for extraction.\n    required: [\"bzip2\"],\n}\n\nllndk_libraries_txt {\n    name: \"llndk.libraries.txt\",\n}\n\nsanitizer_libraries_txt {\n    name: \"sanitizer.libraries.txt\",\n}\n\nEXPORT_GLOBAL_ASAN_OPTIONS = select(soong_config_variable(\"ANDROID\", \"ASAN_ENABLED\"), {\n    true: \"export ASAN_OPTIONS include=/system/asan.options\",\n    default: \"\",\n})\n\nEXPORT_GLOBAL_HWASAN_OPTIONS = select(soong_config_variable(\"ANDROID\", \"HWASAN_ENABLED\"), {\n    true: \"export HWASAN_OPTIONS heap_history_size=1023,stack_history_size=512,export_memory_stats=0,max_malloc_fill_size=131072,malloc_fill_byte=0\",\n    default: \"\",\n})\n\nEXPORT_GLOBAL_GCOV_OPTIONS = select(soong_config_variable(\"ANDROID\", \"GCOV_COVERAGE\"), {\n    true: \"export GCOV_PREFIX /data/misc/trace\",\n    default: \"\",\n})\n\nEXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS = select((soong_config_variable(\"ANDROID\", \"CLANG_COVERAGE\"), soong_config_variable(\"ANDROID\", \"CLANG_COVERAGE_CONTINUOUS_MODE\")), {\n    (true, true): \"export LLVM_PROFILE_FILE /data/misc/trace/clang%c-%20m.profraw\",\n    (true, default): \"export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw\",\n    (default, default): \"\",\n})\n\nEXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE = select(soong_config_variable(\"ANDROID\", \"SCUDO_ALLOCATION_RING_BUFFER_SIZE\"), {\n    \"\": \"\",\n    any @ size: \"export SCUDO_ALLOCATION_RING_BUFFER_SIZE \" + size,\n    default: \"\",\n})\n\ngenrule {\n    name: \"init.environ.rc.gen\",\n    srcs: [\"init.environ.rc.in\"],\n    out: [\"init.environ.rc\"],\n    cmd: \"cp -f $(in) $(out) && \" +\n        \"echo '    \" + EXPORT_GLOBAL_ASAN_OPTIONS + \"' >> $(out) && \" +\n        \"echo '    \" + EXPORT_GLOBAL_GCOV_OPTIONS + \"' >> $(out) && \" +\n        \"echo '    \" + EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS + \"' >> $(out) && \" +\n        \"echo '    \" + EXPORT_GLOBAL_HWASAN_OPTIONS + \"' >> $(out) && \" +\n        \"echo '    \" + EXPORT_GLOBAL_SCUDO_ALLOCATION_RING_BUFFER_SIZE + \"' >> $(out)\",\n}\n\nprebuilt_root {\n    name: \"init.environ.rc-soong\",\n    src: \":init.environ.rc.gen\",\n    filename: \"init.environ.rc\",\n    install_in_root: true,\n    no_full_install: true,\n    required: select((soong_config_variable(\"ANDROID\", \"ASAN_ENABLED\"), soong_config_variable(\"ANDROID\", \"SANITIZE_TARGET_SYSTEM_ENABLED\")), {\n        (true, true): [\n            \"asan.options\",\n            \"asan_extract\",\n        ],\n        (true, default): [\"asan.options\"],\n        (default, default): [],\n    }),\n}\n\nfilegroup {\n    name: \"ramdisk_node_list\",\n    srcs: [\"ramdisk_node_list\"],\n    export_to_make_var: \"RAMDISK_NODE_LIST\",\n}\n"
  },
  {
    "path": "rootdir/OWNERS",
    "content": "bowgotsai@google.com\nccross@google.com\ndvander@google.com\nelsk@google.com\njeffv@google.com\njiyong@google.com\nsmoreland@google.com\n"
  },
  {
    "path": "rootdir/adb_debug.prop",
    "content": "# Note: This file will be loaded with highest priority to override\n# other system properties, if a special ramdisk with \"/force_debuggable\"\n# is used and the device is unlocked.\n\n# Disable adb authentication to allow test automation on user build GSI\nro.adb.secure=0\n\n# Allow 'adb root' on user build GSI\nro.debuggable=1\n\n# Introduce this property to indicate that init has loaded adb_debug.prop\nro.force.debuggable=1\n"
  },
  {
    "path": "rootdir/asan.options",
    "content": "allow_user_segv_handler=1\ndetect_odr_violation=0\nalloc_dealloc_mismatch=0\nallocator_may_return_null=1\ndetect_container_overflow=0\nabort_on_error=1\ninclude_if_exists=/system/asan.options.%b\ninclude_if_exists=/data/asan/system/asan.options.%b\n"
  },
  {
    "path": "rootdir/asan_extract.rc",
    "content": "# When /data is available, look for /system/asan.tar.gz and potentially extract.\non post-fs-data\n    exec - system system -- /system/bin/asan_extract\n"
  },
  {
    "path": "rootdir/asan_extract.sh",
    "content": "#!/system/bin/sh\n\n#\n# Copyright (C) 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# This script will extract ASAN libraries from /system/asan.tar.gz to /data and then reboot.\n\n# TODO:\n#   * Timestamp or something to know when to run this again. Right now take the existence of\n#     /data/lib as we're already done.\n#   * Need to distinguish pre- from post-decryption for FDE.\n\nSRC=/system/asan.tar.bz2\nMD5_FILE=/data/asan.md5sum\nASAN_DIR=/data/asan\n# Minimum /data size in blocks. Arbitrarily 512M.\nMIN_DATA_SIZE=131072\n\n# Checks for FDE pre-decrypt state.\n\nVOLD_STATUS=$(getprop vold.decrypt)\nif [ \"$VOLD_STATUS\" = \"trigger_restart_min_framework\" ] ; then\n  log -p i -t asan_install \"Pre-decrypt FDE detected (by vold property)!\"\n  exit 1\nfi\n\nSTATFS_BLOCKS=$(stat -f -c '%b' /data)\nif [ \"$STATFS_BLOCKS\" -le \"$MIN_DATA_SIZE\" ] ; then\n  log -p i -t asan_install \"Pre-decrypt FDE detected (by /data size)!\"\n  exit 1\nfi\n\n# Check for ASAN source.\n\nif ! test -f $SRC ; then\n  log -p i -t asan_install \"Did not find $SRC!\"\n  exit 1\nfi\n\nlog -p i -t asan_install \"Found $SRC, checking whether we need to apply it.\"\n\n# Checksum check.\n\nASAN_TAR_MD5=$(md5sum $SRC)\nif test -f $MD5_FILE ; then\n  INSTALLED_MD5=$(cat $MD5_FILE)\n  if [ \"x$ASAN_TAR_MD5\" = \"x$INSTALLED_MD5\" ] ; then\n    log -p i -t asan_install \"Checksums match, nothing to be done here.\"\n    exit 0\n  fi\nfi\n\n# Actually apply the source.\n\n# Just clean up, helps with restorecon.\nrm -rf $ASAN_DIR\n\nlog -p i -t asan_install \"Untarring $SRC...\"\n\n# Unzip from /system/asan.tar.gz into data. Need to pipe as gunzip is not on device.\nbzip2 -c -d $SRC | tar -x -f - --no-same-owner -C / || exit 1\n\n# Cannot log here, log would run with system_data_file.\n\n# Set correct permission bits.\nchmod -R 744 $ASAN_DIR\ncd $ASAN_DIR ; find . -type d -exec chmod 755 {} \\;\n\nrestorecon -R -F $ASAN_DIR/*/lib*\n\nlog -p i -t asan_install \"Fixed selinux labels...\"\n\n\n# Now write down our checksum to mark the extraction complete.\necho \"$ASAN_TAR_MD5\" > $MD5_FILE\n\n# We want to reboot now. It seems it is not possible to run \"reboot\" here, the device will\n# just be stuck.\n\nlog -p i -t asan_install \"Signaling init to reboot...\"\n\nsetprop sys.powerctl reboot\n"
  },
  {
    "path": "rootdir/avb/Android.bp",
    "content": "// Copyright (C) 2024 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//     http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nsoong_config_module_type {\n    name: \"avb_keys_prebuilt_avb\",\n    module_type: \"prebuilt_avb\",\n    config_namespace: \"ANDROID\",\n    bool_variables: [\n        \"BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT\",\n    ],\n    properties: [\n        \"ramdisk\",\n        \"vendor_ramdisk\",\n    ],\n}\n\navb_keys_prebuilt_avb {\n    name: \"q-developer-gsi.avbpubkey\",\n    src: \"q-developer-gsi.avbpubkey\",\n    soong_config_variables: {\n        BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT: {\n            ramdisk: false,\n            vendor_ramdisk: true,\n            conditions_default: {\n                ramdisk: true,\n                vendor_ramdisk: false,\n            },\n        },\n    },\n}\n\navb_keys_prebuilt_avb {\n    name: \"r-developer-gsi.avbpubkey\",\n    src: \"r-developer-gsi.avbpubkey\",\n    soong_config_variables: {\n        BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT: {\n            ramdisk: false,\n            vendor_ramdisk: true,\n            conditions_default: {\n                ramdisk: true,\n                vendor_ramdisk: false,\n            },\n        },\n    },\n}\n\navb_keys_prebuilt_avb {\n    name: \"s-developer-gsi.avbpubkey\",\n    src: \"s-developer-gsi.avbpubkey\",\n    soong_config_variables: {\n        BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT: {\n            ramdisk: false,\n            vendor_ramdisk: true,\n            conditions_default: {\n                ramdisk: true,\n                vendor_ramdisk: false,\n            },\n        },\n    },\n}\n"
  },
  {
    "path": "rootdir/create_root_structure.mk",
    "content": "LOCAL_PATH:= $(call my-dir)\n\n#######################################\nifneq ($(filter address,$(SANITIZE_TARGET)),)\nASAN_EXTRACT_FILES :=\nifeq ($(SANITIZE_TARGET_SYSTEM),true)\nASAN_EXTRACT_FILES := asan_extract\nendif\nendif\n#######################################\n# init.environ.rc\n# TODO(b/353429422): move LOCAL_POST_INSTALL_CMD to other rules and remove Android.mk module.\n\ninclude $(CLEAR_VARS)\nLOCAL_MODULE_CLASS := ETC\nLOCAL_MODULE := init.environ.rc\nLOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0\nLOCAL_LICENSE_CONDITIONS := notice\nLOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)\n\nifneq ($(filter address,$(SANITIZE_TARGET)),)\n  LOCAL_REQUIRED_MODULES := asan.options $(ASAN_EXTRACT_FILES)\nendif\n\n# Put it here instead of in init.rc module definition,\n# because init.rc is conditionally included.\n#\n# create some directories (some are mount points) and symlinks\nLOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \\\n    dev proc sys system data data_mirror odm oem config storage mnt apex bootstrap-apex debug_ramdisk \\\n    linkerconfig second_stage_resources postinstall tmp $(BOARD_ROOT_EXTRA_FOLDERS)); \\\n    ln -sf /system/bin $(TARGET_ROOT_OUT)/bin; \\\n    ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \\\n    ln -sf /data/user_de/0/com.android.shell/files/bugreports $(TARGET_ROOT_OUT)/bugreports; \\\n    ln -sfn /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \\\n    ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard; \\\n    ln -sf /product/etc/security/adb_keys $(TARGET_ROOT_OUT)/adb_keys\n\nALL_ROOTDIR_SYMLINKS := \\\n  $(TARGET_ROOT_OUT)/bin \\\n  $(TARGET_ROOT_OUT)/etc \\\n  $(TARGET_ROOT_OUT)/bugreports \\\n  $(TARGET_ROOT_OUT)/d \\\n  $(TARGET_ROOT_OUT)/sdcard \\\n  $(TARGET_ROOT_OUT)/adb_keys \\\n\nifdef BOARD_USES_VENDORIMAGE\n  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor\nelse\n  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor\n  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor\nendif\nifdef BOARD_USES_PRODUCTIMAGE\n  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/product\nelse\n  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/product $(TARGET_ROOT_OUT)/product\n  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/product\nendif\nifdef BOARD_USES_SYSTEM_EXTIMAGE\n  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_ext\nelse\n  LOCAL_POST_INSTALL_CMD += ; ln -sf /system/system_ext $(TARGET_ROOT_OUT)/system_ext\n  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/system_ext\nendif\nifdef BOARD_USES_METADATA_PARTITION\n  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/metadata\nendif\n\n# For /odm partition.\nLOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm\n# For Treble Generic System Image (GSI), system-as-root GSI needs to work on\n# both devices with and without /odm partition. Those symlinks are for devices\n# without /odm partition. For devices with /odm partition, mount odm.img under\n# /odm will hide those symlinks.\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/app $(TARGET_ROOT_OUT)/odm/app\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/bin $(TARGET_ROOT_OUT)/odm/bin\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/etc $(TARGET_ROOT_OUT)/odm/etc\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/firmware $(TARGET_ROOT_OUT)/odm/firmware\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/framework $(TARGET_ROOT_OUT)/odm/framework\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib $(TARGET_ROOT_OUT)/odm/lib\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/lib64 $(TARGET_ROOT_OUT)/odm/lib64\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/overlay $(TARGET_ROOT_OUT)/odm/overlay\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/priv-app $(TARGET_ROOT_OUT)/odm/priv-app\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/odm/usr $(TARGET_ROOT_OUT)/odm/usr\n\nALL_ROOTDIR_SYMLINKS += \\\n  $(TARGET_ROOT_OUT)/odm/app \\\n  $(TARGET_ROOT_OUT)/odm/bin \\\n  $(TARGET_ROOT_OUT)/odm/etc \\\n  $(TARGET_ROOT_OUT)/odm/firmware \\\n  $(TARGET_ROOT_OUT)/odm/framework \\\n  $(TARGET_ROOT_OUT)/odm/lib \\\n  $(TARGET_ROOT_OUT)/odm/lib64 \\\n  $(TARGET_ROOT_OUT)/odm/overlay \\\n  $(TARGET_ROOT_OUT)/odm/priv-app \\\n  $(TARGET_ROOT_OUT)/odm/usr\n\n\n# For /vendor_dlkm partition.\nLOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor_dlkm\n# For Treble Generic System Image (GSI), system-as-root GSI needs to work on\n# both devices with and without /vendor_dlkm partition. Those symlinks are for\n# devices without /vendor_dlkm partition. For devices with /vendor_dlkm\n# partition, mount vendor_dlkm.img under /vendor_dlkm will hide those symlinks.\n# Note that /vendor_dlkm/lib is omitted because vendor DLKMs should be accessed\n# via /vendor/lib/modules directly.\nLOCAL_POST_INSTALL_CMD += ; ln -sf /vendor/vendor_dlkm/etc $(TARGET_ROOT_OUT)/vendor_dlkm/etc\nALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/vendor_dlkm/etc\n\n# For /odm_dlkm partition.\nLOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/odm_dlkm\n# For Treble Generic System Image (GSI), system-as-root GSI needs to work on\n# both devices with and without /odm_dlkm partition. Those symlinks are for\n# devices without /odm_dlkm partition. For devices with /odm_dlkm\n# partition, mount odm_dlkm.img under /odm_dlkm will hide those symlinks.\n# Note that /odm_dlkm/lib is omitted because odm DLKMs should be accessed\n# via /odm/lib/modules directly.\nLOCAL_POST_INSTALL_CMD += ; ln -sf /odm/odm_dlkm/etc $(TARGET_ROOT_OUT)/odm_dlkm/etc\nALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/odm_dlkm/etc\n\n# For /system_dlkm partition\nLOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/system_dlkm\n\nifdef BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE\n  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/cache\nelse\n  LOCAL_POST_INSTALL_CMD += ; ln -sf /data/cache $(TARGET_ROOT_OUT)/cache\n  ALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/cache\nendif\nifdef BOARD_ROOT_EXTRA_SYMLINKS\n# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.\n  LOCAL_POST_INSTALL_CMD += $(foreach s, $(BOARD_ROOT_EXTRA_SYMLINKS),\\\n    $(eval p := $(subst :,$(space),$(s)))\\\n    ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \\\n    ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))\n  ALL_ROOTDIR_SYMLINKS += $(foreach s,$(BOARD_ROOT_EXTRA_SYMLINKS),$(TARGET_ROOT_OUT)/$(call word-colon,2,$s))\nendif\n\n# The init symlink must be a post install command of a file that is to TARGET_ROOT_OUT.\n# Since init.environ.rc is required for init and satisfies that requirement, we hijack it to create the symlink.\nLOCAL_POST_INSTALL_CMD += ; ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init\nALL_ROOTDIR_SYMLINKS += $(TARGET_ROOT_OUT)/init\n\nALL_DEFAULT_INSTALLED_MODULES += $(ALL_ROOTDIR_SYMLINKS)\n\ninclude $(BUILD_SYSTEM)/base_rules.mk\n\n$(ALL_ROOTDIR_SYMLINKS): $(LOCAL_BUILT_MODULE)\n\ninit.environ.rc-soong := $(call intermediates-dir-for,ETC,init.environ.rc-soong)/init.environ.rc-soong\n$(eval $(call copy-one-file,$(init.environ.rc-soong),$(LOCAL_BUILT_MODULE)))\ninit.environ.rc-soong :=\n"
  },
  {
    "path": "rootdir/etc/OWNERS",
    "content": "danalbert@google.com\nenh@google.com\njiyong@google.com\nrprichard@google.com\n"
  },
  {
    "path": "rootdir/etc/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n    {\n      \"name\": \"CtsBionicTestCases\"\n    }\n  ],\n  \"hwasan-postsubmit\": [\n    {\n      \"name\": \"CtsBionicTestCases\"\n    }\n  ]\n}\n"
  },
  {
    "path": "rootdir/etc/hosts",
    "content": "127.0.0.1       localhost\n::1             ip6-localhost\n"
  },
  {
    "path": "rootdir/etc/ld.config.legacy.txt",
    "content": "# This file is no longer in use.\n# Please update linker configuration generator instead.\n# You can find the code from /system/linkerconfig"
  },
  {
    "path": "rootdir/etc/ld.config.recovery.txt",
    "content": "# This file is no longer in use.\n# Please update linker configuration generator instead.\n# You can find the code from /system/linkerconfig"
  },
  {
    "path": "rootdir/etc/ld.config.txt",
    "content": "# This file is no longer in use.\n# Please update linker configuration generator instead.\n# You can find the code from /system/linkerconfig"
  },
  {
    "path": "rootdir/etc/ld.config.vndk_lite.txt",
    "content": "# This file is no longer in use.\n# Please update linker configuration generator instead.\n# You can find the code from /system/linkerconfig"
  },
  {
    "path": "rootdir/etc/linker.config.json",
    "content": "{\n  \"requireLibs\": [\n    \"libandroidicu.so\",\n    \"libdexfile.so\",\n    \"libdexfiled.so\",\n    \"libicu.so\",\n    \"libjdwp.so\",\n    \"libnativebridge.so\",\n    \"libnativehelper.so\",\n    \"libnativeloader.so\",\n    \"libsigchain.so\",\n    // TODO(b/120786417 or b/134659294): libicuuc.so\n    // and libicui18n.so are kept for app compat.\n    \"libicui18n.so\",\n    \"libicuuc.so\",\n    // resolv\n    \"libnetd_resolv.so\",\n    // netd\n    \"libnetd_updatable.so\",\n    // statsd\n    \"libstatspull.so\",\n    \"libstatssocket.so\",\n    // adbd\n    \"libadb_pairing_auth.so\",\n    \"libadb_pairing_connection.so\",\n    \"libadb_pairing_server.so\"\n\n    // LLNDK libraries in APEXes will be added automatically from the build,\n    // using build variable LLNDK_MOVED_TO_APEX_LIBRARIES.\n  ],\n  \"provideLibs\": [\n    \"libaptX_encoder.so\",\n    \"libaptXHD_encoder.so\",\n    \"libEGL.so\",\n    \"libGLESv2.so\"\n  ]\n}\n"
  },
  {
    "path": "rootdir/etc/public.libraries.android.txt",
    "content": "# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md\nlibandroid.so\nlibaaudio.so\nlibamidi.so\nlibbinder_ndk.so\nlibc.so\nlibcamera2ndk.so\nlibclang_rt.hwasan-aarch64-android.so 64 nopreload\nlibdl.so\nlibEGL.so\nlibGLESv1_CM.so\nlibGLESv2.so\nlibGLESv3.so\nlibicu.so\nlibicui18n.so\nlibicuuc.so\nlibjnigraphics.so\nliblog.so\nlibmediandk.so\nlibm.so\nlibnativehelper.so\nlibnativewindow.so\nlibneuralnetworks.so nopreload\nlibOpenMAXAL.so\nlibOpenSLES.so\nlibRS.so\nlibstdc++.so\nlibsync.so\nlibvulkan.so\nlibwebviewchromium_plat_support.so\nlibz.so\n"
  },
  {
    "path": "rootdir/etc/public.libraries.iot.txt",
    "content": "# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md\nlibandroid.so\nlibandroidthings.so\nlibaaudio.so\nlibamidi.so\nlibbinder_ndk.so\nlibc.so\nlibcamera2ndk.so\nlibdl.so\nlibEGL.so\nlibGLESv1_CM.so\nlibGLESv2.so\nlibGLESv3.so\nlibicui18n.so\nlibicuuc.so\nlibjnigraphics.so\nliblog.so\nlibmediandk.so\nlibm.so\nlibnativehelper.so\nlibnativewindow.so\nlibneuralnetworks.so\nlibOpenMAXAL.so\nlibOpenSLES.so\nlibRS.so\nlibstdc++.so\nlibsync.so\nlibvulkan.so\nlibwebviewchromium_plat_support.so\nlibz.so\n"
  },
  {
    "path": "rootdir/etc/public.libraries.wear.txt",
    "content": "# See https://android.googlesource.com/platform/ndk/+/main/docs/PlatformApis.md\nlibandroid.so\nlibaaudio.so\nlibamidi.so\nlibbinder_ndk.so\nlibc.so\nlibcamera2ndk.so\nlibdl.so\nlibEGL.so\nlibGLESv1_CM.so\nlibGLESv2.so\nlibGLESv3.so\nlibicu.so\nlibicui18n.so\nlibicuuc.so\nlibjnigraphics.so\nliblog.so\nlibmediandk.so\nlibm.so\nlibnativehelper.so\nlibnativewindow.so\nlibneuralnetworks.so\nlibOpenMAXAL.so\nlibOpenSLES.so\nlibRS.so\nlibstdc++.so\nlibsync.so\nlibvulkan.so\nlibz.so\n"
  },
  {
    "path": "rootdir/init-debug.rc",
    "content": "on property:persist.mmc.max_read_speed=*\n    write /sys/block/mmcblk0/max_read_speed ${persist.mmc.max_read_speed}\n\non property:persist.mmc.max_write_speed=*\n    write /sys/block/mmcblk0/max_write_speed ${persist.mmc.max_write_speed}\n\non property:persist.mmc.cache_size=*\n    write /sys/block/mmcblk0/cache_size ${persist.mmc.cache_size}\n\non early-init && property:ro.product.debugfs_restrictions.enabled=true\n    mount debugfs debugfs /sys/kernel/debug\n    chmod 0755 /sys/kernel/debug\n\non property:sys.boot_completed=1 && property:ro.product.debugfs_restrictions.enabled=true && \\\n   property:persist.dbg.keep_debugfs_mounted=\"\"\n   umount /sys/kernel/debug\n"
  },
  {
    "path": "rootdir/init-mmd-prop.rc",
    "content": "on property:sys.boot_completed=1\n    # When mmd package is not included in the image, we need to initialize\n    # `mmd.enabled_aconfig` sysprop instead of `mmd --set-property`.\n    #\n    # This is because of the consideration for devices in Trunkfood and Nextfood\n    # under mmd being launched via AConfig flag. The devices set up zram with\n    # mmd if `mmd_enabled` AConfig flag is enabled, otherwise set up zram with\n    # swapon_all init command. Since AConfig does not support any init script\n    # integration, we use `mmd.enabled_aconfig` copied by `mmd --set-property`\n    # instead of AConfig flag itself and we need mmd.enabled_aconfig to be empty\n    # by default, to let swapon_all command wait until aconfig flag value is\n    # loaded to the system property.\n    # Devices in Trunkfood and Nextfood needs to execute swapon_all command on\n    # `on property:mmd.enabled_aconfig=*` trigger. So initializing\n    # `mmd.enabled_aconfig` sysprop is required on images without mmd package.\n    #\n    # Note that this init file must not be in the image if mmd is built into the\n    # image.\n    setprop mmd.enabled_aconfig false"
  },
  {
    "path": "rootdir/init.boringssl.zygote64.rc",
    "content": "on init && property:ro.product.cpu.abilist64=*\n    exec_start boringssl_self_test64\non property:apexd.status=ready && property:ro.product.cpu.abilist64=*\n    exec_start boringssl_self_test_apex64\n"
  },
  {
    "path": "rootdir/init.boringssl.zygote64_32.rc",
    "content": "on init && property:ro.product.cpu.abilist32=*\n    exec_start boringssl_self_test32\non init && property:ro.product.cpu.abilist64=*\n    exec_start boringssl_self_test64\non property:apexd.status=ready && property:ro.product.cpu.abilist32=*\n    exec_start boringssl_self_test_apex32\non property:apexd.status=ready && property:ro.product.cpu.abilist64=*\n    exec_start boringssl_self_test_apex64\n"
  },
  {
    "path": "rootdir/init.environ.rc.in",
    "content": "# set up the global environment\non early-init\n    export ANDROID_BOOTLOGO 1\n    export ANDROID_ROOT /system\n    export ANDROID_ASSETS /system/app\n    export ANDROID_DATA /data\n    export ANDROID_STORAGE /storage\n    export ANDROID_ART_ROOT /apex/com.android.art\n    export ANDROID_I18N_ROOT /apex/com.android.i18n\n    export ANDROID_TZDATA_ROOT /apex/com.android.tzdata\n    export EXTERNAL_STORAGE /sdcard\n    export ASEC_MOUNTPOINT /mnt/asec\n    # Additional environment variables will be appended here during build (see Android.bp).\n    # DO NOT ADD additional sections like 'on <event>' here.\n"
  },
  {
    "path": "rootdir/init.no_zygote.rc",
    "content": "# This is an empty file that does not specify any zygote services.\n# It exists to support targets that do not include a zygote.\n"
  },
  {
    "path": "rootdir/init.rc",
    "content": "# Copyright (C) 2012 The Android Open Source Project\n#\n# IMPORTANT: Do not create world writable files or directories.\n# This is a common source of Android security bugs.\n#\n\nimport /init.environ.rc\nimport /system/etc/init/hw/init.usb.rc\nimport /init.${ro.hardware}.rc\nimport /vendor/etc/init/hw/init.${ro.hardware}.rc\nimport /system/etc/init/hw/init.usb.configfs.rc\nimport /system/etc/init/hw/init.${ro.zygote}.rc\n\n# Cgroups are mounted right before early-init using list from /etc/cgroups.json\non early-init\n    # Disable sysrq from keyboard\n    write /proc/sys/kernel/sysrq 0\n\n    # Android doesn't need kernel module autoloading, and it causes SELinux\n    # denials.  So disable it by setting modprobe to the empty string.  Note: to\n    # explicitly set a sysctl to an empty string, a trailing newline is needed.\n    write /proc/sys/kernel/modprobe \\n\n\n    # Set the security context of /adb_keys if present.\n    restorecon /adb_keys\n\n    # Set the security context of /postinstall if present.\n    restorecon /postinstall\n\n    # memory.pressure_level used by lmkd\n    chown root system /dev/memcg/memory.pressure_level\n    chmod 0040 /dev/memcg/memory.pressure_level\n    # app mem cgroups, used by activity manager, lmkd and zygote\n    mkdir /dev/memcg/apps/ 0755 system system\n    # cgroup for system_server and surfaceflinger\n    mkdir /dev/memcg/system 0550 system system\n\n    # symlink the Android specific /dev/tun to Linux expected /dev/net/tun\n    mkdir /dev/net 0755 root root\n    symlink ../tun /dev/net/tun\n\n    # set RLIMIT_NICE to allow priorities from 19 to -20\n    setrlimit nice 40 40\n\n    # Allow up to 32K FDs per process\n    setrlimit nofile 32768 32768\n\n    # set RLIMIT_MEMLOCK to 64KB\n    setrlimit memlock 65536 65536\n\n    # Set up linker config subdirectories based on mount namespaces\n    mkdir /linkerconfig/bootstrap 0755\n    mkdir /linkerconfig/default 0755\n\n    # Greatly extend dm-verity's Merkle tree cache timeout.  The default timeout\n    # is much too short and is unnecessary, given that there is also a shrinker.\n    write /sys/module/dm_bufio/parameters/max_age_seconds 86400\n\n    # Disable dm-verity hash prefetching, since it doesn't help performance\n    # Read more in b/136247322\n    write /sys/module/dm_verity/parameters/prefetch_cluster 0\n\n    # Generate empty ld.config.txt for early executed processes which rely on\n    # /system/lib libraries.\n    write /linkerconfig/bootstrap/ld.config.txt \\#\n    write /linkerconfig/default/ld.config.txt \\#\n    chmod 644 /linkerconfig/bootstrap/ld.config.txt\n    chmod 644 /linkerconfig/default/ld.config.txt\n\n    # Mount bootstrap linker configuration as current\n    mount none /linkerconfig/bootstrap /linkerconfig bind rec\n\n    start ueventd\n\n    # Mount tracefs (with GID=AID_READTRACEFS)\n    mount tracefs tracefs /sys/kernel/tracing gid=3012\n\n    # Run apexd-bootstrap so that APEXes that provide critical libraries\n    # become available. Note that this is executed as exec_start to ensure that\n    # the libraries are available to the processes started after this statement.\n    exec_start apexd-bootstrap\n    perform_apex_config --bootstrap\n\n    # These must already exist by the time boringssl_self_test32 / boringssl_self_test64 run.\n    mkdir /dev/boringssl 0755 root root\n    mkdir /dev/boringssl/selftest 0755 root root\n\n    # create sys dirctory\n    mkdir /dev/sys 0755 system system\n    mkdir /dev/sys/fs 0755 system system\n    mkdir /dev/sys/block 0755 system system\n\n    # Create location for fs_mgr to store abbreviated output from filesystem\n    # checker programs.\n    mkdir /dev/fscklogs 0770 root system\n\n    # Create tmpfs for use by the shell user.\n    mount tmpfs tmpfs /tmp\n    restorecon /tmp\n    chown shell shell /tmp\n    chmod 0771 /tmp\n\non init\n    sysclktz 0\n\n    # Mix device-specific information into the entropy pool\n    copy /proc/cmdline /dev/urandom\n    copy /proc/bootconfig /dev/urandom\n\n    symlink /proc/self/fd/0 /dev/stdin\n    symlink /proc/self/fd/1 /dev/stdout\n    symlink /proc/self/fd/2 /dev/stderr\n\n    # Create socket dir for ot-daemon\n    mkdir /dev/socket/ot-daemon 0770 thread_network thread_network\n\n    # cpuctl hierarchy for devices using utilclamp\n    mkdir /dev/cpuctl/foreground\n    mkdir /dev/cpuctl/foreground_window\n    mkdir /dev/cpuctl/background\n    mkdir /dev/cpuctl/top-app\n    mkdir /dev/cpuctl/rt\n    mkdir /dev/cpuctl/system\n    mkdir /dev/cpuctl/system-background\n    mkdir /dev/cpuctl/dex2oat\n    chown system system /dev/cpuctl\n    chown system system /dev/cpuctl/foreground\n    chown system system /dev/cpuctl/foreground_window\n    chown system system /dev/cpuctl/background\n    chown system system /dev/cpuctl/top-app\n    chown system system /dev/cpuctl/rt\n    chown system system /dev/cpuctl/system\n    chown system system /dev/cpuctl/system-background\n    chown system system /dev/cpuctl/dex2oat\n    chown system system /dev/cpuctl/tasks\n    chown system system /dev/cpuctl/foreground/tasks\n    chown system system /dev/cpuctl/foreground_window/tasks\n    chown system system /dev/cpuctl/background/tasks\n    chown system system /dev/cpuctl/top-app/tasks\n    chown system system /dev/cpuctl/rt/tasks\n    chown system system /dev/cpuctl/system/tasks\n    chown system system /dev/cpuctl/system-background/tasks\n    chown system system /dev/cpuctl/dex2oat/tasks\n    chown system system /dev/cpuctl/cgroup.procs\n    chown system system /dev/cpuctl/foreground/cgroup.procs\n    chown system system /dev/cpuctl/foreground_window/cgroup.procs\n    chown system system /dev/cpuctl/background/cgroup.procs\n    chown system system /dev/cpuctl/top-app/cgroup.procs\n    chown system system /dev/cpuctl/rt/cgroup.procs\n    chown system system /dev/cpuctl/system/cgroup.procs\n    chown system system /dev/cpuctl/system-background/cgroup.procs\n    chown system system /dev/cpuctl/dex2oat/cgroup.procs\n    chmod 0664 /dev/cpuctl/tasks\n    chmod 0664 /dev/cpuctl/foreground/tasks\n    chmod 0664 /dev/cpuctl/foreground_window/tasks\n    chmod 0664 /dev/cpuctl/background/tasks\n    chmod 0664 /dev/cpuctl/top-app/tasks\n    chmod 0664 /dev/cpuctl/rt/tasks\n    chmod 0664 /dev/cpuctl/system/tasks\n    chmod 0664 /dev/cpuctl/system-background/tasks\n    chmod 0664 /dev/cpuctl/dex2oat/tasks\n    chmod 0664 /dev/cpuctl/cgroup.procs\n    chmod 0664 /dev/cpuctl/foreground/cgroup.procs\n    chmod 0664 /dev/cpuctl/foreground_window/cgroup.procs\n    chmod 0664 /dev/cpuctl/background/cgroup.procs\n    chmod 0664 /dev/cpuctl/top-app/cgroup.procs\n    chmod 0664 /dev/cpuctl/rt/cgroup.procs\n    chmod 0664 /dev/cpuctl/system/cgroup.procs\n    chmod 0664 /dev/cpuctl/system-background/cgroup.procs\n    chmod 0664 /dev/cpuctl/dex2oat/cgroup.procs\n\n    # Create a cpu group for NNAPI HAL processes\n    mkdir /dev/cpuctl/nnapi-hal\n    chown system system /dev/cpuctl/nnapi-hal\n    chown system system /dev/cpuctl/nnapi-hal/tasks\n    chown system system /dev/cpuctl/nnapi-hal/cgroup.procs\n    chmod 0664 /dev/cpuctl/nnapi-hal/tasks\n    chmod 0664 /dev/cpuctl/nnapi-hal/cgroup.procs\n    write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1\n    write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1\n\n    # Create a cpu group for camera daemon processes\n    mkdir /dev/cpuctl/camera-daemon\n    chown system system /dev/cpuctl/camera-daemon\n    chown system system /dev/cpuctl/camera-daemon/tasks\n    chown system system /dev/cpuctl/camera-daemon/cgroup.procs\n    chmod 0664 /dev/cpuctl/camera-daemon/tasks\n    chmod 0664 /dev/cpuctl/camera-daemon/cgroup.procs\n\n    # Create blkio group and apply initial settings.\n    # This feature needs kernel to support it, and the\n    # device's init.rc must actually set the correct values.\n    mkdir /dev/blkio/background\n    chown system system /dev/blkio\n    chown system system /dev/blkio/background\n    chown system system /dev/blkio/tasks\n    chown system system /dev/blkio/background/tasks\n    chown system system /dev/blkio/cgroup.procs\n    chown system system /dev/blkio/background/cgroup.procs\n    chmod 0664 /dev/blkio/tasks\n    chmod 0664 /dev/blkio/background/tasks\n    chmod 0664 /dev/blkio/cgroup.procs\n    chmod 0664 /dev/blkio/background/cgroup.procs\n    write /dev/blkio/blkio.weight 1000\n    write /dev/blkio/background/blkio.weight 200\n    write /dev/blkio/background/blkio.bfq.weight 10\n    write /dev/blkio/blkio.group_idle 0\n    write /dev/blkio/background/blkio.group_idle 0\n    write /dev/blkio/background/blkio.prio.class restrict-to-be\n\n    restorecon_recursive /mnt\n\n    mount configfs none /config nodev noexec nosuid\n    chmod 0770 /config/sdcardfs\n    chown system package_info /config/sdcardfs\n\n    # Mount binderfs\n    mkdir /dev/binderfs\n    mount binder binder /dev/binderfs stats=global\n    chmod 0755 /dev/binderfs\n\n    # Mount fusectl\n    mount fusectl none /sys/fs/fuse/connections\n\n    symlink /dev/binderfs/binder /dev/binder\n    symlink /dev/binderfs/hwbinder /dev/hwbinder\n    symlink /dev/binderfs/vndbinder /dev/vndbinder\n\n    chmod 0666 /dev/binderfs/hwbinder\n    chmod 0666 /dev/binderfs/binder\n    chmod 0666 /dev/binderfs/vndbinder\n\n    mkdir /mnt/secure 0700 root root\n    mkdir /mnt/secure/asec 0700 root root\n    mkdir /mnt/asec 0755 root system\n    mkdir /mnt/obb 0755 root system\n    mkdir /mnt/media_rw 0750 root external_storage\n    mkdir /mnt/user 0755 root root\n    mkdir /mnt/user/0 0755 root root\n    mkdir /mnt/user/0/self 0755 root root\n    mkdir /mnt/user/0/emulated 0755 root root\n    mkdir /mnt/user/0/emulated/0 0755 root root\n\n    # Prepare directories for pass through processes\n    mkdir /mnt/pass_through 0700 root root\n    mkdir /mnt/pass_through/0 0710 root media_rw\n    mkdir /mnt/pass_through/0/self 0710 root media_rw\n    mkdir /mnt/pass_through/0/emulated 0710 root media_rw\n    mkdir /mnt/pass_through/0/emulated/0 0710 root media_rw\n\n    mkdir /mnt/expand 0771 system system\n    mkdir /mnt/appfuse 0711 root root\n\n    # Storage views to support runtime permissions\n    mkdir /mnt/runtime 0700 root root\n    mkdir /mnt/runtime/default 0755 root root\n    mkdir /mnt/runtime/default/self 0755 root root\n    mkdir /mnt/runtime/read 0755 root root\n    mkdir /mnt/runtime/read/self 0755 root root\n    mkdir /mnt/runtime/write 0755 root root\n    mkdir /mnt/runtime/write/self 0755 root root\n    mkdir /mnt/runtime/full 0755 root root\n    mkdir /mnt/runtime/full/self 0755 root root\n\n    # For Pre-reboot Dexopt\n    mkdir /mnt/pre_reboot_dexopt 0755 artd artd\n\n    # Symlink to keep legacy apps working in multi-user world\n    symlink /storage/self/primary /mnt/sdcard\n    symlink /mnt/user/0/primary /mnt/runtime/default/self/primary\n\n    write /proc/sys/kernel/panic_on_oops 1\n    write /proc/sys/kernel/hung_task_timeout_secs 0\n    write /proc/cpu/alignment 4\n\n    # scheduler tunables\n    # Disable auto-scaling of scheduler tunables with hotplug. The tunables\n    # will vary across devices in unpredictable ways if allowed to scale with\n    # cpu cores.\n    write /proc/sys/kernel/sched_tunable_scaling 0\n    write /proc/sys/kernel/sched_latency_ns 10000000\n    write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000\n    write /proc/sys/kernel/sched_child_runs_first 0\n\n    write /proc/sys/kernel/randomize_va_space 2\n    write /proc/sys/vm/mmap_min_addr 32768\n    write /proc/sys/net/ipv4/ping_group_range \"0 2147483647\"\n    write /proc/sys/net/unix/max_dgram_qlen 2400\n\n    # Assign reasonable ceiling values for socket rcv/snd buffers.\n    # These should almost always be overridden by the target per the\n    # the corresponding technology maximums.\n    write /proc/sys/net/core/rmem_max  262144\n    write /proc/sys/net/core/wmem_max  262144\n\n    # reflect fwmark from incoming packets onto generated replies\n    write /proc/sys/net/ipv4/fwmark_reflect 1\n    write /proc/sys/net/ipv6/fwmark_reflect 1\n\n    # set fwmark on accepted sockets\n    write /proc/sys/net/ipv4/tcp_fwmark_accept 1\n\n    # disable icmp redirects\n    write /proc/sys/net/ipv4/conf/all/accept_redirects 0\n    write /proc/sys/net/ipv6/conf/all/accept_redirects 0\n\n    # /proc/net/fib_trie leaks interface IP addresses\n    chmod 0400 /proc/net/fib_trie\n\n    # sets up initial cpusets for ActivityManager\n    # this ensures that the cpusets are present and usable, but the device's\n    # init.rc must actually set the correct cpus\n    mkdir /dev/cpuset/foreground\n    copy /dev/cpuset/cpus /dev/cpuset/foreground/cpus\n    copy /dev/cpuset/mems /dev/cpuset/foreground/mems\n    mkdir /dev/cpuset/foreground_window\n    copy /dev/cpuset/cpus /dev/cpuset/foreground_window/cpus\n    copy /dev/cpuset/mems /dev/cpuset/foreground_window/mems\n    mkdir /dev/cpuset/background\n    copy /dev/cpuset/cpus /dev/cpuset/background/cpus\n    copy /dev/cpuset/mems /dev/cpuset/background/mems\n\n    # system-background is for system tasks that should only run on\n    # little cores, not on bigs\n    mkdir /dev/cpuset/system-background\n    copy /dev/cpuset/cpus /dev/cpuset/system-background/cpus\n    copy /dev/cpuset/mems /dev/cpuset/system-background/mems\n\n    # restricted is for system tasks that are being throttled\n    # due to screen off.\n    mkdir /dev/cpuset/restricted\n    copy /dev/cpuset/cpus /dev/cpuset/restricted/cpus\n    copy /dev/cpuset/mems /dev/cpuset/restricted/mems\n\n    mkdir /dev/cpuset/top-app\n    copy /dev/cpuset/cpus /dev/cpuset/top-app/cpus\n    copy /dev/cpuset/mems /dev/cpuset/top-app/mems\n\n    # create a cpuset for camera daemon processes\n    mkdir /dev/cpuset/camera-daemon\n    copy /dev/cpuset/cpus /dev/cpuset/camera-daemon/cpus\n    copy /dev/cpuset/mems /dev/cpuset/camera-daemon/mems\n\n    # change permissions for all cpusets we'll touch at runtime\n    chown system system /dev/cpuset\n    chown system system /dev/cpuset/foreground\n    chown system system /dev/cpuset/foreground_window\n    chown system system /dev/cpuset/background\n    chown system system /dev/cpuset/system-background\n    chown system system /dev/cpuset/top-app\n    chown system system /dev/cpuset/restricted\n    chown system system /dev/cpuset/camera-daemon\n    chown system system /dev/cpuset/tasks\n    chown system system /dev/cpuset/foreground/tasks\n    chown system system /dev/cpuset/foreground_window/tasks\n    chown system system /dev/cpuset/background/tasks\n    chown system system /dev/cpuset/system-background/tasks\n    chown system system /dev/cpuset/top-app/tasks\n    chown system system /dev/cpuset/restricted/tasks\n    chown system system /dev/cpuset/camera-daemon/tasks\n    chown system system /dev/cpuset/cgroup.procs\n    chown system system /dev/cpuset/foreground/cgroup.procs\n    chown system system /dev/cpuset/foreground_window/cgroup.procs\n    chown system system /dev/cpuset/background/cgroup.procs\n    chown system system /dev/cpuset/system-background/cgroup.procs\n    chown system system /dev/cpuset/top-app/cgroup.procs\n    chown system system /dev/cpuset/restricted/cgroup.procs\n    chown system system /dev/cpuset/camera-daemon/cgroup.procs\n\n    # set system-background to 0775 so SurfaceFlinger can touch it\n    chmod 0775 /dev/cpuset/system-background\n\n    chmod 0664 /dev/cpuset/foreground/tasks\n    chmod 0664 /dev/cpuset/foreground_window/tasks\n    chmod 0664 /dev/cpuset/background/tasks\n    chmod 0664 /dev/cpuset/system-background/tasks\n    chmod 0664 /dev/cpuset/top-app/tasks\n    chmod 0664 /dev/cpuset/restricted/tasks\n    chmod 0664 /dev/cpuset/tasks\n    chmod 0664 /dev/cpuset/camera-daemon/tasks\n    chmod 0664 /dev/cpuset/foreground/cgroup.procs\n    chmod 0664 /dev/cpuset/foreground_window/cgroup.procs\n    chmod 0664 /dev/cpuset/background/cgroup.procs\n    chmod 0664 /dev/cpuset/system-background/cgroup.procs\n    chmod 0664 /dev/cpuset/top-app/cgroup.procs\n    chmod 0664 /dev/cpuset/restricted/cgroup.procs\n    chmod 0664 /dev/cpuset/cgroup.procs\n    chmod 0664 /dev/cpuset/camera-daemon/cgroup.procs\n\n    # make the PSI monitor accessible to others\n    chown system system /proc/pressure/memory\n    chmod 0664 /proc/pressure/memory\n\n    mount bpf bpf /sys/fs/bpf nodev noexec nosuid\n\n    # pstore/ramoops previous console log\n    mount pstore pstore /sys/fs/pstore nodev noexec nosuid\n    chown system log /sys/fs/pstore\n    chmod 0550 /sys/fs/pstore\n    chown system log /sys/fs/pstore/console-ramoops\n    chmod 0440 /sys/fs/pstore/console-ramoops\n    chown system log /sys/fs/pstore/console-ramoops-0\n    chmod 0440 /sys/fs/pstore/console-ramoops-0\n    chown system log /sys/fs/pstore/pmsg-ramoops-0\n    chmod 0440 /sys/fs/pstore/pmsg-ramoops-0\n\n    # enable armv8_deprecated instruction hooks\n    write /proc/sys/abi/swp 1\n\n    # Linux's execveat() syscall may construct paths containing /dev/fd\n    # expecting it to point to /proc/self/fd\n    symlink /proc/self/fd /dev/fd\n\n    export DOWNLOAD_CACHE /data/cache\n\n    # This allows the ledtrig-transient properties to be created here so\n    # that they can be chown'd to system:system later on boot\n    write /sys/class/leds/vibrator/trigger \"transient\"\n\n    # This is used by Bionic to select optimized routines.\n    write /dev/cpu_variant:${ro.bionic.arch} ${ro.bionic.cpu_variant}\n    chmod 0444 /dev/cpu_variant:${ro.bionic.arch}\n    write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant}\n    chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch}\n\n    # Allow system processes to read / write power state.\n    chown system system /sys/power/state\n    chown system system /sys/power/wakeup_count\n    chmod 0660 /sys/power/state\n\n    chown radio wakelock /sys/power/wake_lock\n    chown radio wakelock /sys/power/wake_unlock\n    chmod 0660 /sys/power/wake_lock\n    chmod 0660 /sys/power/wake_unlock\n\n    # Start logd before any other services run to ensure we capture all of their logs.\n    start logd\n    # Start lmkd before any other services run so that it can register them\n    write /proc/sys/vm/watermark_boost_factor 0\n    chown root system /sys/module/lowmemorykiller/parameters/adj\n    chmod 0664 /sys/module/lowmemorykiller/parameters/adj\n    chown root system /sys/module/lowmemorykiller/parameters/minfree\n    chmod 0664 /sys/module/lowmemorykiller/parameters/minfree\n    start lmkd\n\n    # Start essential services.\n    start servicemanager\n    start hwservicemanager\n    start vndservicemanager\n\n    # Mount /mnt/vm ASAP to allow early VMs to run.\n    mkdir /mnt/vm 0755 root root\n    mount tmpfs tmpfs /mnt/vm nosuid nodev noexec rw\n    restorecon /mnt/vm\n    chown system system /mnt/vm\n    chmod 0770 /mnt/vm\n    mkdir /mnt/vm/early 0770 system system\n\n# Run boringssl self test for each ABI.  Any failures trigger reboot to firmware.\nimport /system/etc/init/hw/init.boringssl.${ro.zygote}.rc\n\nservice boringssl_self_test32 /system/bin/boringssl_self_test32\n    reboot_on_failure reboot,boringssl-self-check-failed\n    stdio_to_kmsg\n    # Explicitly specify that boringssl_self_test32 doesn't require any capabilities\n    capabilities\n    user nobody\n\nservice boringssl_self_test64 /system/bin/boringssl_self_test64\n    reboot_on_failure reboot,boringssl-self-check-failed\n    stdio_to_kmsg\n    # Explicitly specify that boringssl_self_test64 doesn't require any capabilities\n    capabilities\n    user nobody\n\nservice boringssl_self_test_apex32 /apex/com.android.conscrypt/bin/boringssl_self_test32\n    reboot_on_failure reboot,boringssl-self-check-failed\n    stdio_to_kmsg\n    # Explicitly specify that boringssl_self_test_apex32 doesn't require any capabilities\n    capabilities\n    user nobody\n\nservice boringssl_self_test_apex64 /apex/com.android.conscrypt/bin/boringssl_self_test64\n    reboot_on_failure reboot,boringssl-self-check-failed\n    stdio_to_kmsg\n    # Explicitly specify that boringssl_self_test_apex64 doesn't require any capabilities\n    capabilities\n    user nobody\n\n# Healthd can trigger a full boot from charger mode by signaling this\n# property when the power button is held.\non property:sys.boot_from_charger_mode=1\n    class_stop charger\n    trigger late-init\n\n# Indicate to fw loaders that the relevant mounts are up.\non firmware_mounts_complete\n    rm /dev/.booting\n\n# Mount filesystems and start core system services.\non late-init\n    trigger early-fs\n\n    # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter\n    # '--early' can be specified to skip entries with 'latemount'.\n    # /system and /vendor must be mounted by the end of the fs stage,\n    # while /data is optional.\n    trigger fs\n    trigger post-fs\n\n    # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter\n    # to only mount entries with 'latemount'. This is needed if '--early' is\n    # specified in the previous mount_all command on the fs stage.\n    # With /system mounted and properties form /system + /factory available,\n    # some services can be started.\n    trigger late-fs\n\n    # Now we can mount /data. File encryption requires keymaster to decrypt\n    # /data, which in turn can only be loaded when system properties are present.\n    trigger post-fs-data\n\n    # Should be before netd, but after apex, properties and logging is available.\n    trigger load-bpf-programs\n    trigger bpf-progs-loaded\n\n    # Now we can start zygote.\n    trigger zygote-start\n\n    # Remove a file to wake up anything waiting for firmware.\n    trigger firmware_mounts_complete\n\n    trigger early-boot\n    trigger boot\n\non early-fs\n    # Once metadata has been mounted, we'll need vold to deal with userdata checkpointing\n    start vold\n\non post-fs\n    exec - system system -- /system/bin/vdc checkpoint markBootAttempt\n\n    # Once everything is setup, no need to modify /.\n    # The bind+remount combination allows this to work in containers.\n    mount rootfs rootfs / remount bind ro nodev\n\n    # Mount default storage into root namespace\n    mount none /mnt/user/0 /storage bind rec\n    mount none none /storage slave rec\n\n    # Make sure /sys/kernel/debug (if present) is labeled properly\n    # Note that tracefs may be mounted under debug, so we need to cross filesystems\n    restorecon --recursive --cross-filesystems /sys/kernel/debug\n\n    # We chown/chmod /cache again so because mount is run as root + defaults\n    chown system cache /cache\n    chmod 0770 /cache\n    # We restorecon /cache in case the cache partition has been reset.\n    restorecon_recursive /cache\n\n    # Create /cache/recovery in case it's not there. It'll also fix the odd\n    # permissions if created by the recovery system.\n    mkdir /cache/recovery 0770 system cache\n\n    # Backup/restore mechanism uses the cache partition\n    mkdir /cache/backup_stage 0700 system system\n    mkdir /cache/backup 0700 system system\n\n    #change permissions on vmallocinfo so we can grab it from bugreports\n    chown root log /proc/vmallocinfo\n    chmod 0440 /proc/vmallocinfo\n\n    chown root log /proc/allocinfo\n    chmod 0440 /proc/allocinfo\n\n    chown root log /proc/slabinfo\n    chmod 0440 /proc/slabinfo\n\n    chown root log /proc/pagetypeinfo\n    chmod 0440 /proc/pagetypeinfo\n\n    #change permissions on kmsg & sysrq-trigger so bugreports can grab kthread stacks\n    chown root system /proc/kmsg\n    chmod 0440 /proc/kmsg\n    chown root system /proc/sysrq-trigger\n    chmod 0220 /proc/sysrq-trigger\n    chown system log /proc/last_kmsg\n    chmod 0440 /proc/last_kmsg\n\n    # make the selinux kernel policy world-readable\n    chmod 0444 /sys/fs/selinux/policy\n\n    # create the lost+found directories, so as to enforce our permissions\n    mkdir /cache/lost+found 0770 root root\n\n    restorecon_recursive /metadata\n    mkdir /metadata/vold\n    chmod 0700 /metadata/vold\n    mkdir /metadata/password_slots 0771 root system\n    mkdir /metadata/bootstat 0750 system log\n    mkdir /metadata/ota 0750 root system\n    mkdir /metadata/ota/snapshots 0750 root system\n    mkdir /metadata/watchdog 0770 root system\n    mkdir /metadata/tradeinmode 0770 root system\n    mkdir /metadata/prefetch 0770 root system\n\n    mkdir /metadata/apex 0700 root system\n    mkdir /metadata/apex/sessions 0700 root system\n    # On some devices we see a weird behaviour in which /metadata/apex doesn't\n    # have a correct label. To workaround this bug, explicitly call restorecon\n    # on /metadata/apex. For most of the boot sequences /metadata/apex will\n    # already have a correct selinux label, meaning that this call will be a\n    # no-op.\n    restorecon_recursive /metadata/apex\n\n    mkdir /metadata/staged-install 0770 root system\n\non late-fs\n    # Ensure that tracefs has the correct permissions.\n    # This does not work correctly if it is called in post-fs.\n    chmod 0755 /sys/kernel/tracing\n    chmod 0755 /sys/kernel/debug/tracing\n\n    # HALs required before storage encryption can get unlocked (FBE)\n    class_start early_hal\n\n# Only enable the bootreceiver tracing instance for kernels 5.10 and above.\non late-fs && property:ro.kernel.version=4.19\n    setprop bootreceiver.enable 0\non late-fs && property:ro.kernel.version=5.4\n    setprop bootreceiver.enable 0\non late-fs\n    # Bootreceiver tracing instance is enabled by default.\n    setprop bootreceiver.enable ${bootreceiver.enable:-1}\n\non property:ro.product.cpu.abilist64=* && property:bootreceiver.enable=1\n    # Set up a tracing instance for system_server to monitor error_report_end events.\n    # These are sent by kernel tools like KASAN and KFENCE when a memory corruption\n    # is detected. This is only needed for 64-bit systems.\n    mkdir /sys/kernel/tracing/instances/bootreceiver 0700 system system\n    restorecon_recursive /sys/kernel/tracing/instances/bootreceiver\n    write /sys/kernel/tracing/instances/bootreceiver/buffer_size_kb 1\n    write /sys/kernel/tracing/instances/bootreceiver/trace_options disable_on_free\n    write /sys/kernel/tracing/instances/bootreceiver/events/error_report/error_report_end/enable 1\n\non post-fs-data\n\n    # Start checkpoint before we touch data\n    exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint\n\n    # We chown/chmod /data again so because mount is run as root + defaults\n    chown system system /data\n    chmod 0771 /data\n    # We restorecon /data in case the userdata partition has been reset.\n    restorecon /data\n\n    # Make sure we have the device encryption key.\n    installkey /data\n\n    # Start bootcharting as soon as possible after the data partition is\n    # mounted to collect more data.\n    mkdir /data/bootchart 0755 shell shell encryption=Require\n    bootchart start\n\n    # Avoid predictable entropy pool. Carry over entropy from previous boot.\n    copy /data/system/entropy.dat /dev/urandom\n\n    mkdir /data/vendor 0771 root root encryption=Require\n    mkdir /data/vendor/hardware 0771 root root\n\n    # Start tombstoned early to be able to store tombstones.\n    mkdir /data/anr 0775 system system encryption=Require\n    mkdir /data/tombstones 0775 system system encryption=Require\n    mkdir /data/vendor/tombstones 0771 root root\n    mkdir /data/vendor/tombstones/wifi 0771 wifi wifi\n    start tombstoned\n\n    # Make sure that apexd is started in the default namespace\n    enter_default_mount_ns\n\n    # set up keystore directory structure first so that we can end early boot\n    # and start apexd\n    mkdir /data/misc 01771 system misc encryption=Require\n    mkdir /data/misc/keystore 0700 keystore keystore\n    # work around b/183668221\n    restorecon /data/misc /data/misc/keystore\n\n    # Boot level 30\n    # odsign signing keys have MAX_BOOT_LEVEL=30\n    # This is currently the earliest boot level, but we start at 30\n    # to leave room for earlier levels.\n    setprop keystore.boot_level 30\n\n    # Now that /data is mounted and we have created /data/misc/keystore,\n    # we can tell keystore to stop allowing use of early-boot keys,\n    # and access its database for the first time to support creation and\n    # use of MAX_BOOT_LEVEL keys.\n    exec - system system -- /system/bin/vdc keymaster earlyBootEnded\n\n    # Multi-installed APEXes are selected using persist props.\n    # Load persist properties and override properties (if enabled) from /data,\n    # before starting apexd.\n    # /data/property should be created before `load_persist_props`\n    mkdir /data/property 0700 root root encryption=Require\n    load_persist_props\n\n    start logd\n    start logd-reinit\n\n    # Some existing vendor rc files use 'on load_persist_props_action' to know\n    # when persist props are ready. These are difficult to change due to GRF,\n    # so continue triggering this action here even though props are already loaded\n    # by the 'load_persist_props' call above.\n    trigger load_persist_props_action\n\n    # /data/apex is now available. Start apexd to scan and activate APEXes.\n    #\n    # To handle userspace reboots, make sure that apexd is started cleanly here\n    # (set apexd.status=\"\") and that it is restarted if it's already running.\n    #\n    # /data/apex uses encryption=None because direct I/O support is needed on\n    # APEX files, but some devices don't support direct I/O on encrypted files.\n    # Also, APEXes are public information, similar to the system image.\n    # /data/apex/decompressed and /data/apex/ota_reserved override this setting;\n    # they are encrypted so that files in them can be hard-linked into\n    # /data/rollback which is encrypted.\n    mkdir /data/apex 0755 root system encryption=None\n    mkdir /data/apex/active 0755 root system\n    mkdir /data/apex/backup 0700 root system\n    mkdir /data/apex/decompressed 0755 root system encryption=Require\n    mkdir /data/app-staging 0751 system system encryption=DeleteIfNecessary\n    mkdir /data/apex/ota_reserved 0700 root system encryption=Require\n    setprop apexd.status \"\"\n    restart apexd\n\n    # create rest of basic filesystem structure\n    mkdir /data/misc/recovery 0770 system log\n    copy /data/misc/recovery/ro.build.fingerprint /data/misc/recovery/ro.build.fingerprint.1\n    chmod 0440 /data/misc/recovery/ro.build.fingerprint.1\n    chown system log /data/misc/recovery/ro.build.fingerprint.1\n    write /data/misc/recovery/ro.build.fingerprint ${ro.build.fingerprint}\n    chmod 0440 /data/misc/recovery/ro.build.fingerprint\n    chown system log /data/misc/recovery/ro.build.fingerprint\n    mkdir /data/misc/recovery/proc 0770 system log\n    copy /data/misc/recovery/proc/version /data/misc/recovery/proc/version.1\n    chmod 0440 /data/misc/recovery/proc/version.1\n    chown system log /data/misc/recovery/proc/version.1\n    copy /proc/version /data/misc/recovery/proc/version\n    chmod 0440 /data/misc/recovery/proc/version\n    chown system log /data/misc/recovery/proc/version\n    mkdir /data/misc/bluedroid 02770 bluetooth bluetooth\n    # Fix the access permissions and group ownership for 'bt_config.conf'\n    chmod 0660 /data/misc/bluedroid/bt_config.conf\n    chown bluetooth bluetooth /data/misc/bluedroid/bt_config.conf\n    mkdir /data/misc/bluetooth 0770 bluetooth bluetooth\n    mkdir /data/misc/bluetooth/logs 0770 bluetooth bluetooth\n    mkdir /data/misc/nfc 0770 nfc nfc\n    mkdir /data/misc/nfc/logs 0770 nfc nfc\n    mkdir /data/misc/credstore 0700 credstore credstore\n    mkdir /data/misc/gatekeeper 0700 system system\n    mkdir /data/misc/keychain 0771 system system\n    mkdir /data/misc/net 0750 root shell\n    mkdir /data/misc/radio 0770 system radio\n    mkdir /data/misc/sms 0770 system radio\n    mkdir /data/misc/carrierid 0770 system radio\n    mkdir /data/misc/apns 0770 system radio\n    mkdir /data/misc/emergencynumberdb 0770 system radio\n    mkdir /data/misc/network_watchlist 0774 system system\n    mkdir /data/misc/telephonyconfig 0770 system radio\n    mkdir /data/misc/textclassifier 0771 system system\n    mkdir /data/misc/vpn 0770 system vpn\n    mkdir /data/misc/shared_relro 0771 shared_relro shared_relro\n    mkdir /data/misc/systemkeys 0700 system system\n    mkdir /data/misc/wifi 0770 wifi wifi\n    mkdir /data/misc/wifi/mainline_supplicant 0770 wifi wifi\n    mkdir /data/misc/wifi/mainline_supplicant/sockets 0770 wifi wifi\n    mkdir /data/misc/wifi/sockets 0770 wifi wifi\n    mkdir /data/misc/wifi/wpa_supplicant 0770 wifi wifi\n    mkdir /data/misc/ethernet 0770 system system\n    mkdir /data/misc/dhcp 0770 dhcp dhcp\n    mkdir /data/misc/user 0771 root root\n    # give system access to wpa_supplicant.conf for backup and restore\n    chmod 0660 /data/misc/wifi/wpa_supplicant.conf\n    mkdir /data/local 0751 root root encryption=Require\n    mkdir /data/misc/media 0700 media media\n    mkdir /data/misc/audioserver 0700 audioserver audioserver\n    mkdir /data/misc/cameraserver 0700 cameraserver cameraserver\n    mkdir /data/misc/vold 0700 root root\n    mkdir /data/misc/boottrace 0771 system shell\n    mkdir /data/misc/update_engine 0700 root root\n    mkdir /data/misc/update_engine_log 02750 root log\n    mkdir /data/misc/trace 0700 root root\n    # create location to store surface and window trace files\n    mkdir /data/misc/wmtrace 0700 system system\n    # create location to store accessibility trace files\n    mkdir /data/misc/a11ytrace 0700 system system\n    # profile file layout\n    mkdir /data/misc/profiles 0771 system system\n    mkdir /data/misc/profiles/cur 0771 system system\n    mkdir /data/misc/profiles/ref 0771 system system\n    mkdir /data/misc/profman 0770 system shell\n    mkdir /data/misc/gcov 0770 root root\n    mkdir /data/misc/installd 0700 root root\n    mkdir /data/misc/apexdata 0711 root root\n    mkdir /data/misc/apexrollback 0700 root root\n    mkdir /data/misc/appcompat/ 0700 system system\n    mkdir /data/misc/uprobestats-configs/ 0777 uprobestats uprobestats\n    mkdir /data/misc/snapshotctl_log 0755 root root\n    # create location to store pre-reboot information\n    mkdir /data/misc/prereboot 0700 system system\n    # directory used for on-device refresh metrics file.\n    mkdir /data/misc/odrefresh 0777 system system\n    # directory used for on-device signing key blob\n    mkdir /data/misc/odsign 0710 root system\n    # directory used for odsign metrics\n    mkdir /data/misc/odsign/metrics 0770 root system\n    # directory used for connectivity blob store.\n    mkdir /data/misc/connectivityblobdb 0770 system system\n\n    # Directory for VirtualizationService temporary image files.\n    # Delete any stale files owned by the old virtualizationservice uid (b/230056726).\n    chmod 0770 /data/misc/virtualizationservice\n    exec - virtualizationservice system -- /bin/rm -rf /data/misc/virtualizationservice\n    mkdir /data/misc/virtualizationservice 0771 system system\n\n    # /data/preloads uses encryption=None because it only contains preloaded\n    # files that are public information, similar to the system image.\n    mkdir /data/preloads 0775 system system encryption=None\n\n    # For security reasons, /data/local/tmp should always be empty.\n    # Do not place files or directories in /data/local/tmp\n    mkdir /data/local/tmp 0771 shell shell\n    mkdir /data/local/traces 0777 shell shell\n    mkdir /data/app-private 0771 system system encryption=Require\n    mkdir /data/app-ephemeral 0771 system system encryption=Require\n    mkdir /data/app-asec 0700 root root encryption=Require\n    mkdir /data/app-lib 0771 system system encryption=Require\n    mkdir /data/app 0771 system system encryption=Require\n\n    # Create directory for app metadata files\n    mkdir /data/app-metadata 0700 system system encryption=Require\n\n    # create directory for updated font files.\n    mkdir /data/fonts/ 0771 root root encryption=Require\n    mkdir /data/fonts/files 0771 system system\n    mkdir /data/fonts/config 0770 system system\n\n    # Create directories to push tests to for each linker namespace.\n    # Create the subdirectories in case the first test is run as root\n    # so it doesn't end up owned by root.\n    # Set directories to be executable by any process so that debuggerd,\n    # aka crash_dump, can read any executables/shared libraries.\n    mkdir /data/local/tests 0701 shell shell\n    mkdir /data/local/tests/product 0701 shell shell\n    mkdir /data/local/tests/system 0701 shell shell\n    mkdir /data/local/tests/unrestricted 0701 shell shell\n    mkdir /data/local/tests/vendor 0701 shell shell\n\n    # create dalvik-cache, so as to enforce our permissions\n    mkdir /data/dalvik-cache 0771 root root encryption=Require\n    # create the A/B OTA directory, so as to enforce our permissions\n    mkdir /data/ota 0771 root root encryption=Require\n\n    # create the OTA package directory. It will be accessed by GmsCore (cache\n    # group), update_engine and update_verifier.\n    mkdir /data/ota_package 0770 system cache encryption=Require\n\n    # create resource-cache and double-check the perms\n    mkdir /data/resource-cache 0771 system system encryption=Require\n    chown system system /data/resource-cache\n    chmod 0771 /data/resource-cache\n\n    # Ensure that lost+found exists and has the correct permissions.  Linux\n    # filesystems expect this directory to exist; it's where the fsck tool puts\n    # any recovered files that weren't present in any directory.  It must be\n    # unencrypted, as fsck must be able to write to it.\n    mkdir /data/lost+found 0770 root root encryption=None\n\n    # create directory for DRM plug-ins - give drm the read/write access to\n    # the following directory.\n    mkdir /data/drm 0770 drm drm encryption=Require\n\n    # create directory for MediaDrm plug-ins - give drm the read/write access to\n    # the following directory.\n    mkdir /data/mediadrm 0770 mediadrm mediadrm encryption=Require\n\n    # NFC: create data/nfc for nv storage\n    mkdir /data/nfc 0770 nfc nfc encryption=Require\n    mkdir /data/nfc/param 0770 nfc nfc\n\n    # Create all remaining /data root dirs so that they are made through init\n    # and get proper encryption policy installed\n    mkdir /data/backup 0700 system system encryption=Require\n    mkdir /data/ss 0700 system system encryption=Require\n\n    mkdir /data/system 0775 system system encryption=Require\n    mkdir /data/system/environ 0700 system system\n    # b/183861600 attempt to fix selinux label before running derive_classpath service\n    restorecon /data/system/environ\n    mkdir /data/system/dropbox 0700 system system\n    mkdir /data/system/heapdump 0700 system system\n    mkdir /data/system/users 0775 system system\n    # Mkdir and set SELinux security contexts for shutdown-checkpoints.\n    # TODO(b/270286197): remove these after couple releases.\n    mkdir /data/system/shutdown-checkpoints 0755 system system\n    restorecon_recursive /data/system/shutdown-checkpoints\n\n    # Create the parent directories of the user CE and DE storage directories.\n    # These parent directories must use encryption=None, since each of their\n    # subdirectories uses a different encryption policy (a per-user one), and\n    # encryption policies apply recursively.  These directories should never\n    # contain any subdirectories other than the per-user ones.  /data/media/obb\n    # is an exception that exists for legacy reasons.\n    #\n    # Don't use any write mode bits (0222) for any of these directories, since\n    # the only process that should write to them directly is vold (since it\n    # needs to set up file-based encryption on the subdirectories), which runs\n    # as root with CAP_DAC_OVERRIDE.  This is also fully enforced via the\n    # SELinux policy.  But we also set the DAC file modes accordingly, to try to\n    # minimize differences in behavior if SELinux is set to permissive mode.\n    mkdir /data/media 0550 media_rw media_rw encryption=None\n    mkdir /data/misc_ce 0551 system misc encryption=None\n    mkdir /data/misc_de 0551 system misc encryption=None\n    mkdir /data/system_ce 0550 system system encryption=None\n    mkdir /data/system_de 0550 system system encryption=None\n    mkdir /data/user 0511 system system encryption=None\n    mkdir /data/user_de 0511 system system encryption=None\n    mkdir /data/vendor_ce 0551 root root encryption=None\n    mkdir /data/vendor_de 0551 root root encryption=None\n\n    # Similar to the top-level CE and DE directories, /data/storage_area must\n    # itself be unencrypted, since it contains encrypted directories.\n    mkdir /data/storage_area 0551 root root encryption=None\n\n    # Set the casefold flag on /data/media.  For upgrades, a restorecon can be\n    # needed first to relabel the directory from media_rw_data_file.\n    restorecon /data/media\n    exec - media_rw media_rw -- /system/bin/chattr +F /data/media\n\n    # A tmpfs directory, which will contain all apps and sdk sandbox CE and DE\n    # data directory that bind mount from the original source.\n    mount tmpfs tmpfs /data_mirror nodev noexec nosuid mode=0700,uid=0,gid=1000\n    restorecon /data_mirror\n    mkdir /data_mirror/data_ce 0700 root root\n    mkdir /data_mirror/data_de 0700 root root\n    mkdir /data_mirror/misc_ce 0700 root root\n    mkdir /data_mirror/misc_de 0700 root root\n    mkdir /data_mirror/storage_area 0700 root root\n\n    # Create CE and DE data directory for default volume\n    # Not needed for storage_area directory, since this is\n    # not supported for non-default volumes and the path\n    # does not include the volume ID\n    mkdir /data_mirror/data_ce/null 0700 root root\n    mkdir /data_mirror/data_de/null 0700 root root\n    mkdir /data_mirror/misc_ce/null 0700 root root\n    mkdir /data_mirror/misc_de/null 0700 root root\n\n    # Bind mount CE and DE data directory to mirror's default volume directory.\n    # Note that because the /data mount has the \"shared\" propagation type, the\n    # later bind mount of /data/data onto /data/user/0 will automatically\n    # propagate to /data_mirror/data_ce/null/0 as well.\n    mount none /data/user /data_mirror/data_ce/null bind rec\n    mount none /data/user_de /data_mirror/data_de/null bind rec\n    mount none /data/misc_ce /data_mirror/misc_ce/null bind rec\n    mount none /data/misc_de /data_mirror/misc_de/null bind rec\n\n    # Also bind mount for the storage area directory (minus the volume ID)\n    mount none /data/storage_area /data_mirror/storage_area bind rec\n\n    # Create mirror directory for jit profiles\n    mkdir /data_mirror/cur_profiles 0700 root root\n    mount none /data/misc/profiles/cur /data_mirror/cur_profiles bind rec\n    mkdir /data_mirror/ref_profiles 0700 root root\n    mount none /data/misc/profiles/ref /data_mirror/ref_profiles bind rec\n\n    mkdir /data/cache 0770 system cache encryption=Require\n    mkdir /data/cache/recovery 0770 system cache\n    mkdir /data/cache/backup_stage 0700 system system\n    mkdir /data/cache/backup 0700 system system\n\n    # Delete these if need be, per b/139193659\n    mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary\n    mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary\n    mkdir /data/rollback-history 0700 system system encryption=DeleteIfNecessary\n\n    # Create root dir for Incremental Service\n    mkdir /data/incremental 0771 system system encryption=Require\n\n    # Create directories for statsd\n    mkdir /data/misc/stats-active-metric/ 0770 statsd system\n    mkdir /data/misc/stats-data/ 0770 statsd system\n    mkdir /data/misc/stats-data/restricted-data 0770 statsd system\n    mkdir /data/misc/stats-metadata/ 0770 statsd system\n    mkdir /data/misc/stats-service/ 0770 statsd system\n    mkdir /data/misc/train-info/ 0770 statsd system\n\n    # Wait for apexd to finish activating APEXes before starting more processes.\n    wait_for_prop apexd.status activated\n    perform_apex_config\n\n    exec_start system_aconfigd_mainline_init\n    start system_aconfigd_socket_service\n\n    # start mainline aconfigd init, after transition, the above system_aconfigd_mainline_init\n    # will be deprecated\n    exec_start mainline_aconfigd_init\n    start mainline_aconfigd_socket_service\n\n    # Create directories for boot animation.\n    mkdir /data/misc/bootanim 0755 system system\n\n    exec_start derive_sdk\n\n    init_user0\n\n    # Set SELinux security contexts on upgrade or policy update.\n    restorecon --recursive --skip-ce /data\n\n    # Define and export *CLASSPATH variables\n    # Must start before 'odsign', as odsign depends on *CLASSPATH variables\n    exec_start derive_classpath\n    load_exports /data/system/environ/classpath\n\n    # Start ART's oneshot boot service to propagate boot experiment flags to\n    # dalvik.vm.*. This needs to be done before odsign since odrefresh uses and\n    # validates those properties against the signed cache-info.xml.\n    exec_start art_boot\n\n    # Start the on-device signing daemon, and wait for it to finish, to ensure\n    # ART artifacts are generated if needed.\n    # Must start after 'derive_classpath' to have *CLASSPATH variables set.\n    start odsign\n\n    # Wait for odsign to be done with the key.\n    wait_for_prop odsign.key.done 1\n\n    # Bump the boot level to 1000000000; this prevents further on-device signing.\n    # This is a special value that shuts down the thread which listens for\n    # further updates.\n    setprop keystore.boot_level 1000000000\n\n    # Allow apexd to snapshot and restore device encrypted apex data in the case\n    # of a rollback. This should be done immediately after DE_user data keys\n    # are loaded. APEXes should not access this data until this has been\n    # completed and apexd.status becomes \"ready\".\n    exec_start apexd-snapshotde\n\n    # sys.memfd_use set to false by default, which keeps it disabled\n    # until it is confirmed that apps and vendor processes don't make\n    # IOCTLs on ashmem fds any more.\n    setprop sys.use_memfd false\n\n    # Set fscklog permission\n    chown root system /dev/fscklogs/log\n    chmod 0770 /dev/fscklogs/log\n\n    # Enable FUSE by default\n    setprop persist.sys.fuse true\n\n    # Update dm-verity state and set partition.*.verified properties.\n    verity_update_state\n\non property:vold.checkpoint_committed=1\n    trigger post-fs-data-checkpointed\n\n# It is important that we start bpfloader after:\n#   - /sys/fs/bpf is already mounted,\n#   - apex (incl. rollback) is initialized (so that we can load bpf\n#     programs shipped as part of apex mainline modules)\n#   - logd is ready for us to log stuff\n#\n# At the same time we want to be as early as possible to reduce races and thus\n# failures (before memory is fragmented, and cpu is busy running tons of other\n# stuff) and we absolutely want to be before netd and the system boot slot is\n# considered to have booted successfully.\non load-bpf-programs\n    exec_start bpfloader\n\non bpf-progs-loaded\n    start netd\n\n# It is recommended to put unnecessary data/ initialization from post-fs-data\n# to start-zygote in device's init.rc to unblock zygote start.\non zygote-start\n    wait_for_prop odsign.verification.done 1\n    # A/B update verifier that marks a successful boot.\n    exec_start update_verifier\n    start statsd\n    start zygote\n    start zygote_secondary\n\non boot && property:ro.config.low_ram=true\n    # Tweak background writeout\n    write /proc/sys/vm/dirty_expire_centisecs 200\n    write /proc/sys/vm/dirty_background_ratio  5\n\non boot && property:suspend.disable_sync_on_suspend=true\n    write /sys/power/sync_on_suspend 0\n\non boot\n    # basic network init\n    ifup lo\n    hostname localhost\n    domainname localdomain\n\n    # IPsec SA default expiration length\n    write /proc/sys/net/core/xfrm_acq_expires 3600\n\n    # Memory management.  Basic kernel parameters, and allow the high\n    # level system server to be able to adjust the kernel OOM driver\n    # parameters to match how it is managing things.\n    write /proc/sys/vm/overcommit_memory 1\n    write /proc/sys/vm/min_free_order_shift 4\n\n    # System server manages zram writeback\n    chown root system /sys/block/zram0/idle\n    chmod 0220 /sys/block/zram0/idle\n    chown root system /sys/block/zram0/writeback\n    chmod 0220 /sys/block/zram0/writeback\n\n    # to access F2FS sysfs on dm-<num> directly\n    mkdir /dev/sys/fs/by-name 0755 system system\n    symlink /sys/fs/f2fs/${dev.mnt.dev.data} /dev/sys/fs/by-name/userdata\n\n    # dev.mnt.dev.data=dm-N, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0, or\n    # dev.mnt.dev.data=sdaN/mmcblk0pN, dev.mnt.blk.data=sdaN/mmcblk0pN, dev.mnt.rootdisk.data=sda/mmcblk0\n    mkdir /dev/sys/block/by-name 0755 system system\n    symlink /sys/class/block/${dev.mnt.dev.data} /dev/sys/block/by-name/userdata\n    symlink /sys/class/block/${dev.mnt.rootdisk.data} /dev/sys/block/by-name/rootdisk\n\n    # F2FS tuning. Set cp_interval larger than dirty_expire_centisecs, 30 secs,\n    # to avoid power consumption when system becomes mostly idle. Be careful\n    # to make it too large, since it may bring userdata loss, if they\n    # are not aware of using fsync()/sync() to prepare sudden power-cut.\n    write /dev/sys/fs/by-name/userdata/cp_interval 200\n    write /dev/sys/fs/by-name/userdata/gc_urgent_sleep_time 50\n    write /dev/sys/fs/by-name/userdata/iostat_period_ms 1000\n    write /dev/sys/fs/by-name/userdata/iostat_enable 1\n\n    # set readahead multiplier for POSIX_FADV_SEQUENTIAL files\n    write /dev/sys/fs/by-name/userdata/seq_file_ra_mul 128\n\n    # limit discard size to 128MB in order to avoid long IO latency\n    # for filesystem tuning first (dm or sda)\n    # this requires enabling selinux entry for sda/mmcblk0 in vendor side\n    write /dev/sys/block/by-name/userdata/queue/discard_max_bytes 134217728\n    write /dev/sys/block/by-name/rootdisk/queue/discard_max_bytes 134217728\n\n    # Permissions for System Server and daemons.\n    chown system system /sys/power/autosleep\n\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_rate\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_rate\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/timer_slack\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/timer_slack\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/min_sample_time\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/min_sample_time\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/hispeed_freq\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/hispeed_freq\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/target_loads\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/target_loads\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/go_hispeed_load\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/above_hispeed_delay\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/boost\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/boost\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/input_boost\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/input_boost\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/boostpulse_duration\n    chown system system /sys/devices/system/cpu/cpufreq/interactive/io_is_busy\n    chmod 0660 /sys/devices/system/cpu/cpufreq/interactive/io_is_busy\n\n    chown system system /sys/class/leds/vibrator/trigger\n    chown system system /sys/class/leds/vibrator/activate\n    chown system system /sys/class/leds/vibrator/brightness\n    chown system system /sys/class/leds/vibrator/duration\n    chown system system /sys/class/leds/vibrator/state\n    chown system system /sys/class/timed_output/vibrator/enable\n    chown system system /sys/class/leds/keyboard-backlight/brightness\n    chown system system /sys/class/leds/lcd-backlight/brightness\n    chown system system /sys/class/leds/button-backlight/brightness\n    chown system system /sys/class/leds/jogball-backlight/brightness\n    chown system system /sys/class/leds/red/brightness\n    chown system system /sys/class/leds/green/brightness\n    chown system system /sys/class/leds/blue/brightness\n    chown system system /sys/class/leds/red/device/grpfreq\n    chown system system /sys/class/leds/red/device/grppwm\n    chown system system /sys/class/leds/red/device/blink\n    chown system system /sys/module/sco/parameters/disable_esco\n    chown system system /sys/kernel/ipv4/tcp_wmem_min\n    chown system system /sys/kernel/ipv4/tcp_wmem_def\n    chown system system /sys/kernel/ipv4/tcp_wmem_max\n    chown system system /sys/kernel/ipv4/tcp_rmem_min\n    chown system system /sys/kernel/ipv4/tcp_rmem_def\n    chown system system /sys/kernel/ipv4/tcp_rmem_max\n    chown system system /sys/firmware/acpi/tables\n    chown system system /sys/firmware/acpi/tables/BERT\n    chown system system /sys/firmware/acpi/tables/data/BERT\n    chown root radio /proc/cmdline\n    chown root system /proc/bootconfig\n\n    # Define default initial receive window size in segments.\n    setprop net.tcp_def_init_rwnd 60\n\n    # Start standard binderized HAL daemons\n    class_start hal\n\n    class_start core\n\non nonencrypted\n    class_start main\n    class_start late_start\n\non property:sys.init_log_level=*\n    loglevel ${sys.init_log_level}\n\non charger\n    class_start charger\n\non property:sys.boot_completed=1\n    bootchart stop\n    # Setup per_boot directory so other .rc could start to use it on boot_completed\n    exec - system system -- /bin/rm -rf /data/per_boot\n    mkdir /data/per_boot 0700 system system encryption=Require key=per_boot_ref\n\n# system server cannot write to /proc/sys files,\n# and chown/chmod does not work for /proc/sys/ entries.\n# So proxy writes through init.\non property:sys.sysctl.extra_free_kbytes=*\n    exec -- /system/bin/extra_free_kbytes.sh ${sys.sysctl.extra_free_kbytes}\n\n# Allow users to drop caches\non property:perf.drop_caches=3\n    write /proc/sys/vm/drop_caches 3\n    setprop perf.drop_caches 0\n\n# \"tcp_default_init_rwnd\" Is too long!\non property:net.tcp_def_init_rwnd=*\n    write /proc/sys/net/ipv4/tcp_default_init_rwnd ${net.tcp_def_init_rwnd}\n\n# perf_event_open syscall security:\n# Newer kernels have the ability to control the use of the syscall via SELinux\n# hooks. init tests for this, and sets sys_init.perf_lsm_hooks to 1 if the\n# kernel has the hooks. In this case, the system-wide perf_event_paranoid\n# sysctl is set to -1 (unrestricted use), and the SELinux policy is used for\n# controlling access. On older kernels, the paranoid value is the only means of\n# controlling access. It is normally 3 (allow only root), but the shell user\n# can lower it to 1 (allowing thread-scoped pofiling) via security.perf_harden.\non load-bpf-programs && property:sys.init.perf_lsm_hooks=1\n    write /proc/sys/kernel/perf_event_paranoid -1\non property:security.perf_harden=0 && property:sys.init.perf_lsm_hooks=\"\"\n    write /proc/sys/kernel/perf_event_paranoid 1\non property:security.perf_harden=1 && property:sys.init.perf_lsm_hooks=\"\"\n    write /proc/sys/kernel/perf_event_paranoid 3\n\n# Additionally, simpleperf profiler uses debug.* and security.perf_harden\n# sysprops to be able to indirectly set these sysctls.\non property:security.perf_harden=0\n    write /proc/sys/kernel/perf_event_max_sample_rate ${debug.perf_event_max_sample_rate:-100000}\n    write /proc/sys/kernel/perf_cpu_time_max_percent ${debug.perf_cpu_time_max_percent:-25}\n    write /proc/sys/kernel/perf_event_mlock_kb ${debug.perf_event_mlock_kb:-516}\n# Default values.\non property:security.perf_harden=1\n    write /proc/sys/kernel/perf_event_max_sample_rate 100000\n    write /proc/sys/kernel/perf_cpu_time_max_percent 25\n    write /proc/sys/kernel/perf_event_mlock_kb 516\n\n# This property can be set only on userdebug/eng. See neverallow rule in\n# /system/sepolicy/private/property.te .\non property:security.lower_kptr_restrict=1\n    write /proc/sys/kernel/kptr_restrict 0\n\non property:security.lower_kptr_restrict=0\n    write /proc/sys/kernel/kptr_restrict 2\n\n\n# on shutdown\n# In device's init.rc, this trigger can be used to do device-specific actions\n# before shutdown. e.g disable watchdog and mask error handling\n\n## Daemon processes to be run by init.\n##\nservice ueventd /system/bin/ueventd\n    class core\n    critical\n    seclabel u:r:ueventd:s0\n    user root\n    shutdown critical\n\nservice console /system/bin/sh\n    class core\n    console\n    disabled\n    user shell\n    group shell log readproc\n    seclabel u:r:shell:s0\n    setenv HOSTNAME console\n    shutdown critical\n\non property:ro.debuggable=1\n    # Give writes to the same group for the trace folder on debug builds,\n    # it's further protected by selinux policy.\n    # The folder is used to store method traces.\n    chmod 0773 /data/misc/trace\n    # Give writes and reads to anyone for the window trace folder on debug builds,\n    # it's further protected by selinux policy.\n    chmod 0777 /data/misc/wmtrace\n    # Give reads to anyone for the accessibility trace folder on debug builds.\n    chmod 0775 /data/misc/a11ytrace\n\non init && property:ro.debuggable=1\n    start console\n\n# Multi-Gen LRU Experiment\non property:persist.device_config.mglru_native.lru_gen_config=none\n  write /sys/kernel/mm/lru_gen/enabled 0\non property:persist.device_config.mglru_native.lru_gen_config=core\n  write /sys/kernel/mm/lru_gen/enabled 1\non property:persist.device_config.mglru_native.lru_gen_config=core_and_mm_walk\n  write /sys/kernel/mm/lru_gen/enabled 3\non property:persist.device_config.mglru_native.lru_gen_config=core_and_nonleaf_young\n  write /sys/kernel/mm/lru_gen/enabled 5\non property:persist.device_config.mglru_native.lru_gen_config=all\n  write /sys/kernel/mm/lru_gen/enabled 7\n\n# Allow other processes to run `snapshotctl` through `init`. This requires\n# `set_prop` permission on `snapshotctl_prop`.\non property:sys.snapshotctl.map=requested\n    # \"root\" is needed to talk to gsid and pass its check on uid.\n    # \"system\" is needed to write to \"/dev/socket/snapuserd\" to talk to\n    # snapuserd.\n    exec - root root system -- /system/bin/snapshotctl map\n    setprop sys.snapshotctl.map \"finished\"\n\non property:sys.snapshotctl.unmap=requested\n    exec - root root system -- /system/bin/snapshotctl unmap\n    setprop sys.snapshotctl.unmap \"finished\"\n"
  },
  {
    "path": "rootdir/init.usb.configfs.rc",
    "content": "on property:sys.usb.config=none && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/UDC \"none\"\n    stop adbd\n    setprop sys.usb.ffs.ready 0\n    write /config/usb_gadget/g1/bDeviceClass 0\n    write /config/usb_gadget/g1/bDeviceSubClass 0\n    write /config/usb_gadget/g1/bDeviceProtocol 0\n    rm /config/usb_gadget/g1/configs/b.1/f1\n    rm /config/usb_gadget/g1/configs/b.1/f2\n    rm /config/usb_gadget/g1/configs/b.1/f3\n    rmdir /config/usb_gadget/g1/functions/rndis.gs4\n    setprop sys.usb.state ${sys.usb.config}\n\non property:init.svc.adbd=stopped\n    setprop sys.usb.ffs.ready 0\n\non property:sys.usb.config=adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=adb && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"adb\"\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=mtp && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"mtp\"\n    symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"mtp_adb\"\n    symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=ptp && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"ptp\"\n    symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"ptp_adb\"\n    symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=accessory && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"accessory\"\n    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"accessory_adb\"\n    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=audio_source && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"audiosource\"\n    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"audiosource_adb\"\n    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"accessory_audiosource\"\n    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"accessory_audiosource_adb\"\n    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f3\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=midi && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"midi\"\n    symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=midi,adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=midi,adb && property:sys.usb.configfs=1\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"midi_adb\"\n    symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=rndis && property:sys.usb.configfs=1\n    mkdir /config/usb_gadget/g1/functions/rndis.gs4\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"rndis\"\n    symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n\non property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1\n    start adbd\n\non property:sys.usb.ffs.ready=1 && property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1\n    mkdir /config/usb_gadget/g1/functions/rndis.gs4\n    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration \"rndis_adb\"\n    symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1\n    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2\n    write /config/usb_gadget/g1/UDC ${sys.usb.controller}\n    setprop sys.usb.state ${sys.usb.config}\n"
  },
  {
    "path": "rootdir/init.usb.rc",
    "content": "# Copyright (C) 2012 The Android Open Source Project\n#\n# USB configuration common for all android devices\n#\n\non post-fs-data\n    chown system system /sys/class/android_usb/android0/f_mass_storage/lun/file\n    chmod 0660 /sys/class/android_usb/android0/f_mass_storage/lun/file\n    chown system system /sys/class/android_usb/android0/f_rndis/ethaddr\n    chmod 0660 /sys/class/android_usb/android0/f_rndis/ethaddr\n    mkdir /data/misc/adb 02750 system shell\n    mkdir /data/adb 0700 root root encryption=Require\n\n# adbd is controlled via property triggers in init.<platform>.usb.rc\nservice adbd /system/bin/adbd --root_seclabel=u:r:su:s0\n    class core\n    socket adbd seqpacket 660 system system\n    disabled\n    updatable\n    seclabel u:r:adbd:s0\n    user root\n\non property:vendor.sys.usb.adb.disabled=*\n    setprop sys.usb.adb.disabled ${vendor.sys.usb.adb.disabled}\n\n# Set default value on sys.usb.configfs early in boot sequence. It will be\n# overridden in `on boot` action of init.hardware.rc.\non init\n    setprop sys.usb.configfs 0\n\n# Used to disable USB when switching states\non property:sys.usb.config=none && property:sys.usb.configfs=0\n    stop adbd\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/bDeviceClass 0\n    setprop sys.usb.state ${sys.usb.config}\n\n# adb only USB configuration\n# This is the fallback configuration if the\n# USB manager fails to set a standard configuration\non property:sys.usb.config=adb && property:sys.usb.configfs=0\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/idVendor 18d1\n    write /sys/class/android_usb/android0/idProduct 4EE7\n    write /sys/class/android_usb/android0/functions ${sys.usb.config}\n    write /sys/class/android_usb/android0/enable 1\n    start adbd\n    setprop sys.usb.state ${sys.usb.config}\n\n# USB accessory configuration\non property:sys.usb.config=accessory && property:sys.usb.configfs=0\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/idVendor 18d1\n    write /sys/class/android_usb/android0/idProduct 2d00\n    write /sys/class/android_usb/android0/functions ${sys.usb.config}\n    write /sys/class/android_usb/android0/enable 1\n    setprop sys.usb.state ${sys.usb.config}\n\n# USB accessory configuration, with adb\non property:sys.usb.config=accessory,adb && property:sys.usb.configfs=0\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/idVendor 18d1\n    write /sys/class/android_usb/android0/idProduct 2d01\n    write /sys/class/android_usb/android0/functions ${sys.usb.config}\n    write /sys/class/android_usb/android0/enable 1\n    start adbd\n    setprop sys.usb.state ${sys.usb.config}\n\n# audio accessory configuration\non property:sys.usb.config=audio_source && property:sys.usb.configfs=0\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/idVendor 18d1\n    write /sys/class/android_usb/android0/idProduct 2d02\n    write /sys/class/android_usb/android0/functions ${sys.usb.config}\n    write /sys/class/android_usb/android0/enable 1\n    setprop sys.usb.state ${sys.usb.config}\n\n# audio accessory configuration, with adb\non property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=0\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/idVendor 18d1\n    write /sys/class/android_usb/android0/idProduct 2d03\n    write /sys/class/android_usb/android0/functions ${sys.usb.config}\n    write /sys/class/android_usb/android0/enable 1\n    start adbd\n    setprop sys.usb.state ${sys.usb.config}\n\n# USB and audio accessory configuration\non property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=0\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/idVendor 18d1\n    write /sys/class/android_usb/android0/idProduct 2d04\n    write /sys/class/android_usb/android0/functions ${sys.usb.config}\n    write /sys/class/android_usb/android0/enable 1\n    setprop sys.usb.state ${sys.usb.config}\n\n# USB and audio accessory configuration, with adb\non property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=0\n    write /sys/class/android_usb/android0/enable 0\n    write /sys/class/android_usb/android0/idVendor 18d1\n    write /sys/class/android_usb/android0/idProduct 2d05\n    write /sys/class/android_usb/android0/functions ${sys.usb.config}\n    write /sys/class/android_usb/android0/enable 1\n    start adbd\n    setprop sys.usb.state ${sys.usb.config}\n\n# Used to set USB configuration at boot and to switch the configuration\n# when changing the default configuration\non boot && property:persist.sys.usb.config=*\n    setprop sys.usb.config ${persist.sys.usb.config}\n\n#\n# USB type C\n#\n\n# USB mode changes\non property:sys.usb.typec.mode=dfp\n    write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}\n    setprop sys.usb.typec.state ${sys.usb.typec.mode}\n\non property:sys.usb.typec.mode=ufp\n    write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}\n    setprop sys.usb.typec.state ${sys.usb.typec.mode}\n\n# USB data role changes\non property:sys.usb.typec.data_role=device\n    write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}\n    setprop sys.usb.typec.state ${sys.usb.typec.data_role}\n\non property:sys.usb.typec.data_role=host\n    write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}\n    setprop sys.usb.typec.state ${sys.usb.typec.data_role}\n\n# USB power role changes\non property:sys.usb.typec.power_role=source\n    write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}\n    setprop sys.usb.typec.state ${sys.usb.typec.power_role}\n\non property:sys.usb.typec.power_role=sink\n    write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}\n    setprop sys.usb.typec.state ${sys.usb.typec.power_role}\n"
  },
  {
    "path": "rootdir/init.zygote32.rc",
    "content": "service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server\n    class main\n    priority -20\n    user root\n    group root readproc reserved_disk\n    socket zygote stream 660 root system\n    socket usap_pool_primary stream 660 root system\n    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse\n    onrestart write /sys/power/state on\n    # NOTE: If the wakelock name here is changed, then also\n    # update it in SystemSuspend.cpp\n    onrestart write /sys/power/wake_lock zygote_kwl\n    onrestart restart audioserver\n    onrestart restart cameraserver\n    onrestart restart media\n    onrestart restart --only-if-running media.tuner\n    onrestart restart netd\n    onrestart restart wificond\n    task_profiles ProcessCapacityHigh MaxPerformance\n    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal\n"
  },
  {
    "path": "rootdir/init.zygote64.rc",
    "content": "service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote\n    class main\n    priority -20\n    user root\n    group root readproc reserved_disk\n    socket zygote stream 660 root system\n    socket usap_pool_primary stream 660 root system\n    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse\n    onrestart write /sys/power/state on\n    # NOTE: If the wakelock name here is changed, then also\n    # update it in SystemSuspend.cpp\n    onrestart write /sys/power/wake_lock zygote_kwl\n    onrestart restart audioserver\n    onrestart restart cameraserver\n    onrestart restart media\n    onrestart restart --only-if-running media.tuner\n    onrestart restart netd\n    onrestart restart wificond\n    task_profiles ProcessCapacityHigh MaxPerformance\n    critical window=${zygote.critical_window.minute:-off} target=zygote-fatal\n"
  },
  {
    "path": "rootdir/init.zygote64_32.rc",
    "content": "import /system/etc/init/hw/init.zygote64.rc\n\nservice zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload\n    class main\n    priority -20\n    user root\n    group root readproc reserved_disk\n    socket zygote_secondary stream 660 root system\n    socket usap_pool_secondary stream 660 root system\n    onrestart restart zygote\n    task_profiles ProcessCapacityHigh MaxPerformance\n"
  },
  {
    "path": "rootdir/ramdisk_node_list",
    "content": "dir dev 0755 0 0\nnod dev/null 0600 0 0 c 1 3\nnod dev/console 0600 0 0 c 5 1\nnod dev/urandom 0600 0 0 c 1 9\n"
  },
  {
    "path": "rootdir/ueventd.rc",
    "content": "import /vendor/etc/ueventd.rc\nimport /odm/etc/ueventd.rc\n\nfirmware_directories /etc/firmware/ /odm/firmware/ /vendor/firmware/ /firmware/image/\nuevent_socket_rcvbuf_size 16M\n\nsubsystem graphics\n    devname uevent_devpath\n    dirname /dev/graphics\n\nsubsystem drm\n    devname uevent_devpath\n    dirname /dev/dri\n\nsubsystem input\n    devname uevent_devpath\n    dirname /dev/input\n\nsubsystem sound\n    devname uevent_devpath\n    dirname /dev/snd\n\nsubsystem dma_heap\n   devname uevent_devpath\n   dirname /dev/dma_heap\n\nsubsystem vfio\n    devname uevent_devpath\n    dirname /dev/vfio\n\n# ueventd can only set permissions on device nodes and their associated\n# sysfs attributes, not on arbitrary paths.\n#\n# format for /dev rules: devname mode uid gid\n# format for /sys rules: nodename attr mode uid gid\n# shortcut: \"mtd@NN\" expands to \"/dev/mtd/mtdNN\"\n\n/dev/null                 0666   root       root\n/dev/zero                 0666   root       root\n/dev/full                 0666   root       root\n/dev/ptmx                 0666   root       root\n/dev/tty                  0666   root       root\n/dev/random               0666   root       root\n/dev/urandom              0666   root       root\n# Aside from kernel threads, only prng_seeder needs access to HW RNG\n/dev/hw_random            0400   prng_seeder prng_seeder\n/dev/ashmem*              0666   root       root\n/dev/binder               0666   root       root\n/dev/hwbinder             0666   root       root\n/dev/vndbinder            0666   root       root\n/dev/vfio/*               0666   root       root\n\n/dev/pmsg0                0222   root       log\n/dev/dma_heap/system      0444   system     system\n/dev/dma_heap/system-uncached      0444   system     system\n/dev/dma_heap/system-secure        0444   system     system\n\n# kms driver for drm based gpu\n/dev/dri/*                0666   root       graphics\n\n# these should not be world writable\n/dev/uhid                 0660   uhid       uhid\n/dev/uinput               0660   uhid       uhid\n/dev/rtc0                 0640   system     system\n/dev/tty0                 0660   root       system\n/dev/graphics/*           0660   root       graphics\n/dev/input/*              0660   root       input\n/dev/v4l-touch*           0660   root       input\n/dev/snd/*                0660   system     audio\n/dev/bus/usb/*            0660   root       usb\n/dev/mtp_usb              0660   root       mtp\n/dev/usb_accessory        0660   root       usb\n/dev/tun                  0660   system     vpn\n/dev/hidraw*              0660   system     system\n\n# CDMA radio interface MUX\n/dev/ppp                  0660   radio      vpn\n\n/dev/kvm                  0666   root       root\n/dev/vhost-vsock          0666   root       root\n\n# sysfs properties\n/sys/devices/platform/trusty.*      trusty_version        0440  root   log\n/sys/devices/virtual/input/input*   enable      0660  root   input\n/sys/devices/virtual/input/input*   poll_delay  0660  root   input\n/sys/devices/virtual/usb_composite/*   enable      0664  root   system\n/sys/devices/system/cpu/cpu*   cpufreq/scaling_max_freq   0664  system system\n/sys/devices/system/cpu/cpu*   cpufreq/scaling_min_freq   0664  system system\n/sys/devices/virtual/misc/uhid/*/leds/* brightness   0664  system system\n/sys/devices/virtual/misc/uhid/*/leds/* multi_intensity   0664  system system\n"
  },
  {
    "path": "run-as/Android.bp",
    "content": "//\n// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"system_core_run-as_license\"],\n}\n\n// Added automatically by a large-scale-change\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_run-as_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\ncc_binary {\n    name: \"run-as\",\n    srcs: [\n        \"run-as.cpp\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libselinux\",\n        \"libpackagelistparser\",\n        \"libminijail\",\n    ],\n    header_libs: [\"libcutils_headers\"],\n}\n"
  },
  {
    "path": "run-as/NOTICE",
    "content": "\n   Copyright (c) 2005-2008, The Android Open Source Project\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n\n\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n"
  },
  {
    "path": "run-as/run-as.cpp",
    "content": "/*\n * Copyright (C) 2010 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <error.h>\n#include <paths.h>\n#include <pwd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/capability.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include <string>\n#include <vector>\n\n#include <libminijail.h>\n#include <scoped_minijail.h>\n\n#include <android-base/properties.h>\n#include <packagelistparser/packagelistparser.h>\n#include <private/android_filesystem_config.h>\n#include <selinux/android.h>\n\n// The purpose of this program is to run a command as a specific\n// application user-id. Typical usage is:\n//\n//   run-as <package-name> <command> <args>\n//\n//  The 'run-as' binary is installed with CAP_SETUID and CAP_SETGID file\n//  capabilities, but will check the following:\n//\n//  - that the ro.boot.disable_runas property is not set\n//  - that it is invoked from the 'shell' or 'root' user (abort otherwise)\n//  - that '<package-name>' is the name of an installed and debuggable package\n//  - that the package's data directory is well-formed\n//\n//  If so, it will drop to the application's user id / group id, cd to the\n//  package's data directory, then run the command there.\n//\n//  This can be useful for a number of different things on production devices:\n//\n//  - Allow application developers to look at their own application data\n//    during development.\n//\n//  - Run the 'gdbserver' binary executable to allow native debugging\n//\n\nstatic bool packagelist_parse_callback(pkg_info* this_package, void* userdata) {\n  pkg_info* p = reinterpret_cast<pkg_info*>(userdata);\n  if (strcmp(p->name, this_package->name) == 0) {\n    *p = *this_package;\n    return false; // Stop searching.\n  }\n  packagelist_free(this_package);\n  return true; // Keep searching.\n}\n\nstatic void check_directory(const char* path, uid_t uid) {\n  struct stat st;\n  if (TEMP_FAILURE_RETRY(lstat(path, &st)) == -1) {\n    error(1, errno, \"couldn't stat %s\", path);\n  }\n\n  // Must be a real directory, not a symlink.\n  if (!S_ISDIR(st.st_mode)) {\n    error(1, 0, \"%s not a directory: %o\", path, st.st_mode);\n  }\n\n  // Must be owned by specific uid/gid.\n  if (st.st_uid != uid || st.st_gid != uid) {\n    error(1, 0, \"%s has wrong owner: %d/%d, not %d\", path, st.st_uid, st.st_gid, uid);\n  }\n\n  // Must not be readable or writable by others.\n  if ((st.st_mode & (S_IROTH | S_IWOTH)) != 0) {\n    error(1, 0, \"%s readable or writable by others: %o\", path, st.st_mode);\n  }\n}\n\n// This function is used to check the data directory path for safety.\n// We check that every sub-directory is owned by the 'system' user\n// and exists and is not a symlink. We also check that the full directory\n// path is properly owned by the user ID.\nstatic void check_data_path(const char* package_name, const char* data_path, uid_t uid) {\n  // The path should be absolute.\n  if (data_path[0] != '/') {\n    error(1, 0, \"%s data path not absolute: %s\", package_name, data_path);\n  }\n\n  // Look for all sub-paths, we do that by finding\n  // directory separators in the input path and\n  // checking each sub-path independently.\n  for (int nn = 1; data_path[nn] != '\\0'; nn++) {\n    char subpath[PATH_MAX];\n\n    /* skip non-separator characters */\n    if (data_path[nn] != '/') continue;\n\n    /* handle trailing separator case */\n    if (data_path[nn+1] == '\\0') break;\n\n    /* found a separator, check that data_path is not too long. */\n    if (nn >= (int)(sizeof subpath)) {\n      error(1, 0, \"%s data path too long: %s\", package_name, data_path);\n    }\n\n    /* reject any '..' subpath */\n    if (nn >= 3               &&\n        data_path[nn-3] == '/' &&\n        data_path[nn-2] == '.' &&\n        data_path[nn-1] == '.') {\n      error(1, 0, \"%s contains '..': %s\", package_name, data_path);\n    }\n\n    /* copy to 'subpath', then check ownership */\n    memcpy(subpath, data_path, nn);\n    subpath[nn] = '\\0';\n\n    check_directory(subpath, AID_SYSTEM);\n  }\n\n  // All sub-paths were checked, now verify that the full data\n  // directory is owned by the application uid.\n  check_directory(data_path, uid);\n}\n\nstd::vector<gid_t> get_supplementary_gids(uid_t userAppId) {\n  std::vector<gid_t> gids;\n  int size = getgroups(0, &gids[0]);\n  if (size < 0) {\n    error(1, errno, \"getgroups failed\");\n  }\n  gids.resize(size);\n  size = getgroups(size, &gids[0]);\n  if (size != static_cast<int>(gids.size())) {\n    error(1, errno, \"getgroups failed\");\n  }\n  // Profile guide compiled oat files (like /data/app/xxx/oat/arm64/base.odex) are not readable\n  // worldwide (DEXOPT_PUBLIC flag isn't set). To support reading them (needed by simpleperf for\n  // profiling), add shared app gid to supplementary groups.\n  gid_t shared_app_gid = userAppId % AID_USER_OFFSET - AID_APP_START + AID_SHARED_GID_START;\n  gids.push_back(shared_app_gid);\n  return gids;\n}\n\nint main(int argc, char* argv[]) {\n  // Check arguments.\n  if (argc < 2) {\n    error(1, 0, \"usage: run-as <package-name> [--user <uid>] <command> [<args>]\\n\");\n  }\n\n  // This program runs with CAP_SETUID and CAP_SETGID capabilities on Android\n  // production devices. Check user id of caller --- must be 'shell' or 'root'.\n  if (getuid() != AID_SHELL && getuid() != AID_ROOT) {\n    error(1, 0, \"only 'shell' or 'root' users can run this program\");\n  }\n\n  // Some devices can disable running run-as, such as Chrome OS when running in\n  // non-developer mode.\n  if (android::base::GetBoolProperty(\"ro.boot.disable_runas\", false)) {\n    error(1, 0, \"run-as is disabled from the kernel commandline\");\n  }\n\n  char* pkgname = argv[1];\n  int cmd_argv_offset = 2;\n\n  // Get user_id from command line if provided.\n  int userId = 0;\n  if ((argc >= 4) && !strcmp(argv[2], \"--user\")) {\n    userId = atoi(argv[3]);\n    if (userId < 0) error(1, 0, \"negative user id: %d\", userId);\n    cmd_argv_offset += 2;\n  }\n\n  // Retrieve package information from system, switching egid so we can read the file.\n  pkg_info info = {.name = pkgname};\n  gid_t old_egid = getegid();\n  if (setegid(AID_PACKAGE_INFO) == -1) error(1, errno, \"setegid(AID_PACKAGE_INFO) failed\");\n  if (!packagelist_parse(packagelist_parse_callback, &info)) {\n    error(1, errno, \"packagelist_parse failed\");\n  }\n  if (setegid(old_egid) == -1) error(1, errno, \"couldn't restore egid\");\n\n  if (info.uid == 0) {\n    error(1, 0, \"unknown package: %s\", pkgname);\n  }\n\n  // Verify that user id is not too big.\n  if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {\n    error(1, 0, \"user id too big: %d\", userId);\n  }\n\n  // Calculate user app ID.\n  uid_t userAppId = (AID_USER_OFFSET * userId) + info.uid;\n\n  // Reject system packages.\n  if (userAppId < AID_APP) {\n    error(1, 0, \"package not an application: %s\", pkgname);\n  }\n\n  // Reject any non-debuggable package.\n  if (!info.debuggable) {\n    error(1, 0, \"package not debuggable: %s\", pkgname);\n  }\n\n  // Ensure we have the right data path for the specific user.\n  free(info.data_dir);\n  if (asprintf(&info.data_dir, \"/data/user/%d/%s\", userId, pkgname) == -1) {\n    error(1, errno, \"asprintf failed\");\n  }\n\n  // Check that the data directory path is valid.\n  check_data_path(pkgname, info.data_dir, userAppId);\n\n  // Ensure that we change all real/effective/saved IDs at the\n  // same time to avoid nasty surprises.\n  uid_t uid = userAppId;\n  uid_t gid = userAppId;\n  std::vector<gid_t> supplementary_gids = get_supplementary_gids(userAppId);\n  ScopedMinijail j(minijail_new());\n  minijail_change_uid(j.get(), uid);\n  minijail_change_gid(j.get(), gid);\n  minijail_set_supplementary_gids(j.get(), supplementary_gids.size(), supplementary_gids.data());\n  minijail_enter(j.get());\n\n  std::string seinfo = std::string(info.seinfo) + \":fromRunAs\";\n  if (selinux_android_setcontext(uid, 0, seinfo.c_str(), pkgname) < 0) {\n    error(1, errno, \"couldn't set SELinux security context '%s'\", seinfo.c_str());\n  }\n\n  // cd into the data directory, and set $HOME correspondingly.\n  if (TEMP_FAILURE_RETRY(chdir(info.data_dir)) == -1) {\n    error(1, errno, \"couldn't chdir to package's data directory '%s'\", info.data_dir);\n  }\n  setenv(\"HOME\", info.data_dir, 1);\n\n  // Reset parts of the environment, like su would.\n  setenv(\"PATH\", _PATH_DEFPATH, 1);\n  unsetenv(\"IFS\");\n\n  // Set the user-specific parts for this user.\n  passwd* pw = getpwuid(uid);\n  setenv(\"LOGNAME\", pw->pw_name, 1);\n  setenv(\"SHELL\", pw->pw_shell, 1);\n  setenv(\"USER\", pw->pw_name, 1);\n\n  // User specified command for exec.\n  if ((argc >= cmd_argv_offset + 1) &&\n      (execvp(argv[cmd_argv_offset], argv+cmd_argv_offset) == -1)) {\n    error(1, errno, \"exec failed for %s\", argv[cmd_argv_offset]);\n  }\n\n  // Default exec shell.\n  execlp(_PATH_BSHELL, \"sh\", NULL);\n  error(1, errno, \"exec failed\");\n}\n"
  },
  {
    "path": "sdcard/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    srcs: [\"sdcard.cpp\"],\n    name: \"sdcard\",\n    cflags: [\n        \"-Wall\",\n        \"-Wno-unused-parameter\",\n        \"-Werror\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libcutils\",\n        \"libminijail\",\n    ],\n    sanitize: {\n        misc_undefined: [\"integer\"],\n    },\n}\n"
  },
  {
    "path": "sdcard/OWNERS",
    "content": "drosen@google.com\n"
  },
  {
    "path": "sdcard/sdcard.cpp",
    "content": "// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n#define LOG_TAG \"sdcard\"\n\n#include <dirent.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/fuse.h>\n#include <pthread.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/inotify.h>\n#include <sys/mount.h>\n#include <sys/resource.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <unistd.h>\n#include <vector>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n\n#include <cutils/fs.h>\n#include <cutils/multiuser.h>\n#include <cutils/properties.h>\n\n#include <libminijail.h>\n#include <scoped_minijail.h>\n\n#include <private/android_filesystem_config.h>\n\n#define PROP_SDCARDFS_DEVICE \"ro.sys.sdcardfs\"\n#define PROP_SDCARDFS_USER \"persist.sys.sdcardfs\"\n\nstatic bool supports_esdfs(void) {\n    std::string filesystems;\n    if (!android::base::ReadFileToString(\"/proc/filesystems\", &filesystems)) {\n        PLOG(ERROR) << \"Could not read /proc/filesystems\";\n        return false;\n    }\n    for (const auto& fs : android::base::Split(filesystems, \"\\n\")) {\n        if (fs.find(\"esdfs\") != std::string::npos) return true;\n    }\n    return false;\n}\n\nstatic bool should_use_sdcardfs(void) {\n    char property[PROPERTY_VALUE_MAX];\n\n    // Allow user to have a strong opinion about state\n    property_get(PROP_SDCARDFS_USER, property, \"\");\n    if (!strcmp(property, \"force_on\")) {\n        LOG(WARNING) << \"User explicitly enabled sdcardfs\";\n        return true;\n    } else if (!strcmp(property, \"force_off\")) {\n        LOG(WARNING) << \"User explicitly disabled sdcardfs\";\n        return !supports_esdfs();\n    }\n\n    // Fall back to device opinion about state\n    if (property_get_bool(PROP_SDCARDFS_DEVICE, true)) {\n        LOG(WARNING) << \"Device explicitly enabled sdcardfs\";\n        return true;\n    } else {\n        LOG(WARNING) << \"Device explicitly disabled sdcardfs\";\n        return !supports_esdfs();\n    }\n}\n\n// NOTE: This is a vestigial program that simply exists to mount the in-kernel\n// sdcardfs filesystem.  The older FUSE-based design that used to live here has\n// been completely removed to avoid confusion.\n\n/* Supplementary groups to execute with. */\nstatic const gid_t kGroups[1] = { AID_PACKAGE_INFO };\n\nstatic void drop_privs(uid_t uid, gid_t gid) {\n    ScopedMinijail j(minijail_new());\n    minijail_set_supplementary_gids(j.get(), arraysize(kGroups), kGroups);\n    minijail_change_gid(j.get(), gid);\n    minijail_change_uid(j.get(), uid);\n    /* minijail_enter() will abort if priv-dropping fails. */\n    minijail_enter(j.get());\n}\n\nstatic bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,\n                           uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,\n                           mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb,\n                           bool use_esdfs) {\n    // Add new options at the end of the vector.\n    std::vector<std::string> new_opts_list;\n    if (multi_user) new_opts_list.push_back(\"multiuser,\");\n    if (derive_gid) new_opts_list.push_back(\"derive_gid,\");\n    if (default_normal) new_opts_list.push_back(\"default_normal,\");\n    if (unshared_obb) new_opts_list.push_back(\"unshared_obb,\");\n    // Try several attempts, each time with one less option, to gracefully\n    // handle older kernels that aren't updated yet.\n    for (int i = 0; i <= new_opts_list.size(); ++i) {\n        std::string new_opts;\n        for (int j = 0; j < new_opts_list.size() - i; ++j) {\n            new_opts += new_opts_list[j];\n        }\n\n        auto opts = android::base::StringPrintf(\"fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d\",\n                                                fsuid, fsgid, new_opts.c_str(), mask, userid, gid);\n        if (mount(source_path.c_str(), dest_path.c_str(), use_esdfs ? \"esdfs\" : \"sdcardfs\",\n                  MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {\n            PLOG(WARNING) << \"Failed to mount sdcardfs with options \" << opts;\n        } else {\n            return true;\n        }\n    }\n\n    return false;\n}\n\nstatic bool sdcardfs_setup_bind_remount(const std::string& source_path, const std::string& dest_path,\n                                        gid_t gid, mode_t mask) {\n    std::string opts = android::base::StringPrintf(\"mask=%d,gid=%d\", mask, gid);\n\n    if (mount(source_path.c_str(), dest_path.c_str(), nullptr,\n            MS_BIND, nullptr) != 0) {\n        PLOG(ERROR) << \"failed to bind mount sdcardfs filesystem\";\n        return false;\n    }\n\n    if (mount(source_path.c_str(), dest_path.c_str(), \"none\",\n            MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) != 0) {\n        PLOG(ERROR) << \"failed to mount sdcardfs filesystem\";\n        if (umount2(dest_path.c_str(), MNT_DETACH))\n            PLOG(WARNING) << \"Failed to unmount bind\";\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool sdcardfs_setup_secondary(const std::string& default_path,\n                                     const std::string& source_path, const std::string& dest_path,\n                                     uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid,\n                                     gid_t gid, mode_t mask, bool derive_gid, bool default_normal,\n                                     bool unshared_obb, bool use_esdfs) {\n    if (use_esdfs) {\n        return sdcardfs_setup(source_path, dest_path, fsuid, fsgid, multi_user, userid, gid, mask,\n                              derive_gid, default_normal, unshared_obb, use_esdfs);\n    } else {\n        return sdcardfs_setup_bind_remount(default_path, dest_path, gid, mask);\n    }\n}\n\nstatic void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,\n                         gid_t gid, userid_t userid, bool multi_user, bool full_write,\n                         bool derive_gid, bool default_normal, bool unshared_obb, bool use_esdfs) {\n    std::string dest_path_default = \"/mnt/runtime/default/\" + label;\n    std::string dest_path_read = \"/mnt/runtime/read/\" + label;\n    std::string dest_path_write = \"/mnt/runtime/write/\" + label;\n    std::string dest_path_full = \"/mnt/runtime/full/\" + label;\n\n    umask(0);\n    if (multi_user) {\n        // Multi-user storage is fully isolated per user, so \"other\"\n        // permissions are completely masked off.\n        if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,\n                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,\n                            use_esdfs) ||\n            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,\n                                      multi_user, userid, AID_EVERYBODY, 0027, derive_gid,\n                                      default_normal, unshared_obb, use_esdfs) ||\n            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,\n                                      multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0027,\n                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||\n            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,\n                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,\n                                      default_normal, unshared_obb, use_esdfs)) {\n            LOG(FATAL) << \"failed to sdcardfs_setup\";\n        }\n    } else {\n        // Physical storage is readable by all users on device, but\n        // the Android directories are masked off to a single user\n        // deep inside attr_from_stat().\n        if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,\n                            AID_SDCARD_RW, 0006, derive_gid, default_normal, unshared_obb,\n                            use_esdfs) ||\n            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_read, uid, gid,\n                                      multi_user, userid, AID_EVERYBODY, full_write ? 0027 : 0022,\n                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||\n            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_write, uid, gid,\n                                      multi_user, userid, AID_EVERYBODY, full_write ? 0007 : 0022,\n                                      derive_gid, default_normal, unshared_obb, use_esdfs) ||\n            !sdcardfs_setup_secondary(dest_path_default, source_path, dest_path_full, uid, gid,\n                                      multi_user, userid, AID_EVERYBODY, 0007, derive_gid,\n                                      default_normal, unshared_obb, use_esdfs)) {\n            LOG(FATAL) << \"failed to sdcardfs_setup\";\n        }\n    }\n\n    // Will abort if priv-dropping fails.\n    drop_privs(uid, gid);\n\n    if (multi_user) {\n        std::string obb_path = source_path + \"/obb\";\n        fs_prepare_dir(obb_path.c_str(), 0775, uid, gid);\n    }\n\n    exit(0);\n}\n\nstatic int usage() {\n    LOG(ERROR) << \"usage: sdcard [OPTIONS] <source_path> <label>\"\n               << \"    -u: specify UID to run as\"\n               << \"    -g: specify GID to run as\"\n               << \"    -U: specify user ID that owns device\"\n               << \"    -m: source_path is multi-user\"\n               << \"    -w: runtime write mount has full write access\"\n               << \"    -P: preserve owners on the lower file system\"\n               << \"    -o: obb dir doesn't need to be shared between users\";\n    return 1;\n}\n\nint main(int argc, char **argv) {\n    const char *source_path = NULL;\n    const char *label = NULL;\n    uid_t uid = 0;\n    gid_t gid = 0;\n    userid_t userid = 0;\n    bool multi_user = false;\n    bool full_write = false;\n    bool derive_gid = false;\n    bool default_normal = false;\n    bool unshared_obb = false;\n    int i;\n    struct rlimit rlim;\n    int fs_version;\n\n    setenv(\"ANDROID_LOG_TAGS\", \"*:v\", 1);\n    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));\n\n    int opt;\n    while ((opt = getopt(argc, argv, \"u:g:U:mwGio\")) != -1) {\n        switch (opt) {\n            case 'u':\n                uid = strtoul(optarg, NULL, 10);\n                break;\n            case 'g':\n                gid = strtoul(optarg, NULL, 10);\n                break;\n            case 'U':\n                userid = strtoul(optarg, NULL, 10);\n                break;\n            case 'm':\n                multi_user = true;\n                break;\n            case 'w':\n                full_write = true;\n                break;\n            case 'G':\n                derive_gid = true;\n                break;\n            case 'i':\n                default_normal = true;\n                break;\n            case 'o':\n                unshared_obb = true;\n                break;\n            case '?':\n            default:\n                LOG(ERROR) << \"Unknown option: '\" << opt << \"'\";\n                return usage();\n        }\n    }\n\n    for (i = optind; i < argc; i++) {\n        char* arg = argv[i];\n        if (!source_path) {\n            source_path = arg;\n        } else if (!label) {\n            label = arg;\n        } else {\n            LOG(ERROR) << \"too many arguments\";\n            return usage();\n        }\n    }\n\n    if (!source_path) {\n        LOG(ERROR) << \"no source path specified\";\n        return usage();\n    }\n    if (!label) {\n        LOG(ERROR) << \"no label specified\";\n        return usage();\n    }\n    if (!uid || !gid) {\n        LOG(ERROR) << \"uid and gid must be nonzero\";\n        return usage();\n    }\n\n    rlim.rlim_cur = 8192;\n    rlim.rlim_max = 8192;\n    if (setrlimit(RLIMIT_NOFILE, &rlim) == -1) {\n        PLOG(ERROR) << \"setting RLIMIT_NOFILE failed\";\n    }\n\n    while ((fs_read_atomic_int(\"/data/misc/installd/layout_version\", &fs_version) == -1) ||\n           (fs_version < 3)) {\n        LOG(ERROR) << \"installd fs upgrade not yet complete; waiting...\";\n        sleep(1);\n    }\n\n    run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,\n                 default_normal, unshared_obb, !should_use_sdcardfs());\n    return 1;\n}\n"
  },
  {
    "path": "shell_and_utilities/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nphony {\n    name: \"shell_and_utilities\",\n    required: [\n        \"shell_and_utilities_system\",\n        \"shell_and_utilities_recovery\",\n        \"shell_and_utilities_vendor\",\n    ],\n}\n\nphony {\n    name: \"shell_and_utilities_system\",\n    required: [\n        \"auditctl\",\n        \"awk\",\n        \"bc\",\n        \"bzip2\",\n        \"cpu-target-features\",\n        \"fsck.exfat\",\n        \"ldd\",\n        \"logwrapper\",\n        \"mini-keyctl\",\n        \"mkfs.exfat\",\n        \"mkshrc\",\n        \"newfs_msdos\",\n        \"reboot\",\n        \"settaskprofile\",\n        \"sh\",\n        \"simpleperf\",\n        \"simpleperf_app_runner\",\n        \"tcpdump\",\n        \"toolbox\",\n        \"toybox\",\n        \"ziptool\",\n    ],\n}\n\nphony {\n    name: \"shell_and_utilities_recovery\",\n    required: [\n        \"sh.recovery\",\n        \"toolbox.recovery\",\n        \"toybox_recovery\",\n        \"ziptool.recovery\",\n    ],\n    recovery: true,\n}\n\nphony {\n    name: \"shell_and_utilities_vendor\",\n    required: [\n        \"awk_vendor\",\n        \"logwrapper_vendor\",\n        \"mkshrc_vendor\",\n        \"sh_vendor\",\n        \"toolbox_vendor\",\n        \"toybox_vendor\",\n    ],\n    vendor: true,\n}\n\n// shell and utilities for first stage console. The list of binaries are\n// enough for debugging purposes.\nphony {\n    name: \"shell_and_utilities_vendor_ramdisk\",\n    required: [\n        \"sh.vendor_ramdisk\",\n        \"toybox.vendor_ramdisk\",\n    ],\n}\n"
  },
  {
    "path": "shell_and_utilities/OWNERS",
    "content": "enh@google.com\n"
  },
  {
    "path": "shell_and_utilities/README.md",
    "content": "# Android's shell and utilities\n\nSince IceCreamSandwich Android has used\n[mksh](https://www.mirbsd.org/mksh.htm) as its shell. Before then it used\n[ash](https://en.wikipedia.org/wiki/Almquist_shell) (which actually\nremained unused in the tree up to and including KitKat).\n\nInitially Android had a very limited command-line provided by its own\n\"toolbox\" binary. Since Marshmallow almost everything is supplied by\n[toybox](http://landley.net/toybox/) instead.\n\nWe started moving a few of the more important tools to full\nBSD implementations in JellyBean, and continued this work in\nLollipop. Lollipop was a major break with the past in many ways (LP64\nsupport and the switch to ART both having lots of knock-on effects around\nthe system), so although this was the beginning of the end of toolbox it\n(a) didn't stand out given all the other systems-level changes and (b)\nin Marshmallow we changed direction and started the move to toybox.\n\nNot everything is provided by toybox, though. For the bzip2 command-line tools\nwe use the ones that are part of the bzip2 distribution.\nThe awk added in Android P is the\n[\"one true\" awk](https://github.com/onetrueawk/awk).\nThe bc added in Android Q is\n[Gavin Howard's bc](https://github.com/gavinhoward/bc).\n\nThe lists below show what tools were provided and where they came from in\neach release starting with Gingerbread. This doesn't tell the full story,\nbecause the toolbox implementations did have bugs fixed and options added\nover the years. Gingerbread's rm, for example, supported `-r`/`-R` but not\n`-f`. But this gives you an idea of what was available in any given release,\nand how usable it was likely to be. (**Bold** marks where we switched to toybox\nor first added something to toybox.)\n\nAlso note that in any given release `toybox` probably contains more\ncommands than there are symlinks for in `/system/bin`. You can get the\nfull list for a release by running `toybox` directly.\n\n\n## Android 15 (API level 35, \"Vanilla Ice Cream\")\n\nBSD: fsck\\_msdos newfs\\_msdos\n\nbzip2: bzcat bzip2 bunzip2\n\ngavinhoward/bc: bc\n\none-true-awk: awk\n\ntoolbox: getevent getprop setprop start stop\n\ntoybox ([0.8.11](https://landley.net/toybox/news.html#08-04-2024)-ish):\n[ acpi base64 basename blkdiscard blkid blockdev brctl cal cat chattr\nchcon chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut\ndate dd devmem df diff dirname dmesg dos2unix du echo egrep env expand\nexpr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze\nfsync getconf getenforce **getfattr** getopt **gpiodetect** **gpiofind**\n**gpioget** **gpioinfo** **gpioset** grep groups gunzip gzip head help hostname\nhwclock i2cdetect i2cdump i2cget i2cset **i2ctransfer** iconv id ifconfig\ninotifyd insmod install ionice iorenice iotop kill killall ln load\\_policy\nlog logger logname losetup ls lsattr lsmod lsof lspci lsusb makedevs\nmd5sum **memeater** microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe\nmore mount mountpoint mv nbd-client nc netcat netstat nice nl nohup\nnproc nsenter od partprobe paste patch pgrep pidof ping ping6 pivot\\_root\npkill pmap printenv printf prlimit ps pwd pwdx readelf readlink realpath\nrenice restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent\nseq setenforce **setfattr** setsid sha1sum sha224sum sha256sum sha384sum\nsha512sum sleep sort split stat strings stty swapoff swapon sync sysctl\ntac tail tar taskset tee test time timeout top touch tr traceroute\ntraceroute6 true truncate tty tunctl uclampset ulimit umount uname\nuniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen\nvconfig vi vmstat watch wc which whoami xargs xxd yes zcat\n\nNote: technically getfattr and setfattr were available in earlier versions,\nbut the symlinks were missing until this release, so they were only available\nas `toybox getfattr` and `toybox setfattr` rather than directly.\n\n\n## Android 14 (API level 34, \"Upside Down Cake\")\n\nBSD: fsck\\_msdos newfs\\_msdos\n\nbzip2: bzcat bzip2 bunzip2\n\ngavinhoward/bc: bc\n\none-true-awk: awk\n\ntoolbox: getevent getprop setprop start stop\n\ntoybox ([0.8.9](http://landley.net/toybox/#10-01-2023)-ish):\n[ acpi base64 basename blkdiscard blkid blockdev **brctl** cal cat chattr\nchcon chgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut\ndate dd devmem df diff dirname dmesg dos2unix du echo egrep env expand\nexpr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze\nfsync getconf getenforce getfattr getopt grep groups gunzip gzip head\nhelp hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig\ninotifyd insmod install ionice iorenice iotop kill killall ln load\\_policy\nlog **logger** logname losetup ls lsattr lsmod lsof lspci lsusb makedevs\nmd5sum microcom mkdir mkfifo mknod mkswap mktemp modinfo modprobe\nmore mount mountpoint mv nbd-client nc netcat netstat nice nl nohup\nnproc nsenter od partprobe paste patch pgrep pidof ping ping6 pivot\\_root\npkill pmap printenv printf prlimit ps pwd pwdx readelf readlink realpath\nrenice restorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent\nseq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum\nsha512sum sleep sort split stat strings stty swapoff swapon sync sysctl\ntac tail tar taskset tee test time timeout top touch tr traceroute\ntraceroute6 true truncate tty tunctl uclampset ulimit umount uname\nuniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen\nvconfig vi vmstat watch wc which whoami xargs xxd yes zcat\n\n\n## Android 13 (33, \"Tiramisu\")\n\nBSD: fsck\\_msdos newfs\\_msdos\n\nbzip2: bzcat bzip2 bunzip2\n\ngavinhoward/bc: bc\n\none-true-awk: awk\n\ntoolbox: getevent getprop setprop start stop\n\ntoybox ([0.8.6](http://landley.net/toybox/#30-11-2021)-ish):\n[ acpi base64 basename blkdiscard blkid blockdev cal cat chattr chcon\nchgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date\ndd devmem df diff dirname dmesg dos2unix du echo egrep env expand\nexpr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze\nfsync getconf getenforce getfattr getopt grep groups gunzip gzip head\nhelp hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig\ninotifyd insmod install ionice iorenice iotop kill killall ln load\\_policy\nlog logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum\nmicrocom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount\nmountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter\nod partprobe paste patch pgrep pidof ping ping6 pivot\\_root pkill pmap\nprintenv printf prlimit ps pwd pwdx readelf readlink realpath renice\nrestorecon rev rfkill rm rmdir rmmod rtcwake runcon sed sendevent\nseq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum\nsha512sum sleep sort split stat strings stty swapoff swapon sync sysctl\ntac tail tar taskset tee test time timeout top touch tr traceroute\ntraceroute6 true truncate tty tunctl **uclampset** ulimit umount uname\nuniq unix2dos unlink unshare uptime usleep uudecode uuencode uuidgen\nvconfig vi vmstat watch wc which whoami xargs xxd yes zcat\n\n\n## Android 12 (31, \"Snow Cone\")\n\nBSD: fsck\\_msdos newfs\\_msdos\n\nbzip2: bzcat bzip2 bunzip2\n\ngavinhoward/bc: bc\n\none-true-awk: awk\n\ntoolbox: getevent getprop setprop start stop\n\ntoybox ([0.8.4](http://landley.net/toybox/#24-10-2020)-ish):\n**[** acpi base64 basename **blkdiscard** blkid blockdev cal cat chattr chcon\nchgrp chmod chown chroot chrt cksum clear cmp comm cp cpio cut date\ndd devmem df diff dirname dmesg dos2unix du echo egrep env expand\nexpr fallocate false fgrep file find flock fmt free freeramdisk fsfreeze\nfsync getconf getenforce getfattr getopt grep groups gunzip gzip head\nhelp hostname hwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig\ninotifyd insmod install ionice iorenice iotop kill killall ln load\\_policy\nlog logname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum\nmicrocom mkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount\nmountpoint mv nbd-client nc netcat netstat nice nl nohup nproc nsenter\nod partprobe paste patch pgrep pidof ping ping6 pivot\\_root pkill pmap\nprintenv printf prlimit ps pwd pwdx readelf readlink realpath renice\nrestorecon rev rfkill rm rmdir rmmod **rtcwake** runcon sed sendevent\nseq setenforce setfattr setsid sha1sum sha224sum sha256sum sha384sum\nsha512sum sleep sort split stat strings stty swapoff swapon sync sysctl\ntac tail tar taskset tee **test** time timeout top touch tr traceroute\ntraceroute6 true truncate tty tunctl ulimit umount uname uniq unix2dos\nunlink unshare uptime usleep uudecode uuencode uuidgen vconfig vi\nvmstat watch wc which whoami xargs xxd yes zcat\n\n\n## Android 11 (API level 30, \"Red Velvet Cake\")\n\nBSD: fsck\\_msdos newfs\\_msdos\n\nbzip2: bzcat bzip2 bunzip2\n\ngavinhoward/bc: bc\n\none-true-awk: awk\n\ntoolbox: getevent getprop setprop start stop\n\ntoybox ([0.8.3](http://landley.net/toybox/#11-05-2020)-ish):\nacpi base64 basename blkid blockdev cal cat chattr chcon chgrp chmod\nchown chroot chrt cksum clear cmp comm cp cpio cut date dd **devmem**\ndf diff dirname dmesg dos2unix du echo egrep env expand expr fallocate\nfalse fgrep file find flock fmt free freeramdisk fsfreeze **fsync** getconf\ngetenforce getfattr **getopt** grep groups gunzip gzip head help hostname\nhwclock i2cdetect i2cdump i2cget i2cset iconv id ifconfig inotifyd\ninsmod install ionice iorenice iotop kill killall ln load\\_policy log\nlogname losetup ls lsattr lsmod lsof lspci lsusb makedevs md5sum microcom\nmkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint\nmv nbd-client nc netcat netstat nice nl nohup nproc nsenter od partprobe\npaste patch pgrep pidof ping ping6 pivot\\_root pkill pmap printenv\nprintf prlimit ps pwd pwdx **readelf** readlink realpath renice restorecon\nrev rfkill rm rmdir rmmod runcon sed sendevent seq setenforce setfattr\nsetsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep sort\nsplit stat strings stty swapoff swapon sync sysctl tac tail tar taskset\ntee time timeout top touch tr traceroute traceroute6 true truncate\ntty tunctl ulimit umount uname uniq unix2dos unlink unshare uptime\nusleep uudecode uuencode uuidgen vconfig **vi** vmstat watch wc which\nwhoami xargs xxd yes zcat\n\n\n## Android 10 (API level 29, \"Quince Tart\")\n\nBSD: grep fsck\\_msdos newfs\\_msdos\n\nbzip2: bzcat bzip2 bunzip2\n\none-true-awk: awk\n\ntoolbox: getevent getprop\n\ntoybox ([0.8.0](http://landley.net/toybox/#08-02-2019)-ish):\nacpi base64 basename **bc** **blkid** blockdev cal cat **chattr** chcon chgrp\nchmod chown chroot chrt cksum clear cmp comm cp cpio cut date dd df\ndiff dirname dmesg dos2unix du echo **egrep** env expand expr fallocate\nfalse **fgrep** file find flock fmt free **freeramdisk** **fsfreeze** **getconf**\ngetenforce **getfattr** grep groups gunzip gzip head **help** hostname hwclock\n**i2cdetect** **i2cdump** **i2cget** **i2cset** **iconv** id ifconfig inotifyd insmod\n**install** ionice iorenice **iotop** kill killall ln load\\_policy log logname\nlosetup ls **lsattr** lsmod lsof lspci lsusb **makedevs** md5sum microcom\nmkdir mkfifo mknod mkswap mktemp modinfo modprobe more mount mountpoint\nmv **nbd-client** **nc** **netcat** netstat nice nl nohup **nproc** **nsenter** od **partprobe**\npaste patch pgrep pidof **ping** **ping6** **pivot\\_root** pkill pmap printenv\nprintf **prlimit** ps pwd **pwdx** readlink realpath renice restorecon **rev**\n**rfkill** rm rmdir rmmod runcon sed sendevent seq setenforce **setfattr**\nsetprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep\nsort split start stat stop strings stty swapoff swapon sync sysctl\ntac tail tar taskset tee time timeout top touch tr **traceroute** **traceroute6**\ntrue truncate tty **tunctl** ulimit umount uname uniq unix2dos **unlink**\n**unshare** uptime usleep uudecode uuencode **uuidgen** **vconfig** vmstat **watch**\nwc which whoami xargs xxd yes zcat\n\n\n## Android 9.0 (API level 28, \"Pie\")\n\nBSD: dd grep\n\nbzip2: bzcat bzip2 bunzip2\n\none-true-awk: awk\n\ntoolbox: getevent getprop newfs\\_msdos\n\ntoybox ([0.7.6](http://landley.net/toybox/#24-02-2018)-ish):\nacpi base64 basename blockdev cal cat chcon chgrp chmod chown\nchroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg\ndos2unix du echo env expand expr fallocate false file find flock **fmt** free\ngetenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd\ninsmod ionice iorenice kill killall ln load\\_policy log logname losetup ls\nlsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap mktemp\nmodinfo modprobe more mount mountpoint mv netstat nice nl nohup od paste\npatch pgrep pidof pkill pmap printenv printf ps pwd readlink realpath\nrenice restorecon rm rmdir rmmod runcon sed sendevent seq setenforce\nsetprop setsid sha1sum sha224sum sha256sum sha384sum sha512sum sleep\nsort split start stat stop strings **stty** swapoff swapon sync sysctl tac\ntail tar taskset tee time timeout top touch tr true truncate tty ulimit\numount uname uniq unix2dos uptime usleep uudecode uuencode vmstat wc\nwhich whoami xargs xxd yes zcat\n\n\n## Android 8.0 (API level 26, \"Oreo\")\n\nBSD: dd grep\n\nbzip2: bzcat bzip2 bunzip2\n\ntoolbox: getevent newfs\\_msdos\n\ntoybox ([0.7.3](http://landley.net/toybox/#21-02-2017)-ish):\nacpi base64 basename blockdev cal cat chcon chgrp chmod chown\nchroot chrt cksum clear cmp comm cp cpio cut date df **diff** dirname dmesg\ndos2unix du echo env expand expr fallocate false **file** find flock free\ngetenforce getprop groups **gunzip** **gzip** head hostname hwclock id ifconfig\ninotifyd insmod ionice iorenice kill killall ln load\\_policy **log** logname\nlosetup ls lsmod lsof **lspci** lsusb md5sum **microcom** mkdir **mkfifo** mknod\nmkswap mktemp modinfo **modprobe** more mount mountpoint mv netstat nice\nnl nohup od paste patch pgrep pidof pkill pmap printenv printf **ps** pwd\nreadlink realpath renice restorecon rm rmdir rmmod runcon sed **sendevent**\nseq setenforce setprop setsid sha1sum **sha224sum** **sha256sum** **sha384sum**\n**sha512sum** sleep sort split start stat stop strings swapoff swapon sync\nsysctl tac tail tar taskset tee time timeout **top** touch tr true truncate\ntty ulimit umount uname uniq unix2dos uptime usleep **uudecode** **uuencode**\nvmstat wc which whoami xargs xxd yes **zcat**\n\n\n## Android 7.0 (API level 24, \"Nougat\")\n\nBSD: dd grep\n\ntoolbox: getevent iftop ioctl log nandread newfs\\_msdos ps prlimit\nsendevent start stop top\n\ntoybox ([0.7.0](http://landley.net/toybox/#02-02-2016)-ish):\nacpi **base64** basename blockdev bzcat cal cat chcon chgrp chmod\nchown chroot cksum clear comm cmp cp cpio cut date **df** dirname dmesg\ndos2unix **du** echo env expand expr fallocate false find **flock** free\ngetenforce getprop groups head hostname hwclock id ifconfig inotifyd\ninsmod **ionice** **iorenice** kill **killall** load\\_policy ln logname losetup **ls**\nlsmod **lsof** lsusb md5sum mkdir mknod mkswap mktemp modinfo more *mount*\nmountpoint mv netstat nice nl nohup od paste patch pgrep pidof pkill\npmap printenv printf pwd readlink realpath **renice** restorecon rm rmdir\nrmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort\nsplit stat strings swapoff swapon sync sysctl tac tail tar taskset tee\ntime timeout touch tr true truncate **tty** **ulimit** umount uname uniq unix2dos\n**uptime** usleep vmstat wc which whoami xargs **xxd** yes\n\n\n## Android 6.0 (API level 23, \"Marshmallow\")\n\nBSD: dd du grep\n\ntoolbox: df getevent iftop ioctl ionice log ls lsof mount nandread\nnewfs\\_msdos ps prlimit renice sendevent start stop top uptime watchprops\n\ntoybox ([0.5.2](http://landley.net/toybox/#25-02-2015)-ish):\nacpi basename blockdev bzcat cal cat chcon chgrp chmod chown\nchroot cksum clear comm cmp cp cpio cut date dirname dmesg dos2unix echo\nenv expand expr fallocate false find free getenforce getprop groups\nhead hostname hwclock id ifconfig inotifyd insmod kill load\\_policy ln\nlogname losetup lsmod lsusb md5sum mkdir mknod mkswap mktemp modinfo\nmore mountpoint mv netstat nice nl nohup od paste patch pgrep pidof\npkill pmap printenv printf pwd readlink realpath restorecon rm rmdir\nrmmod route runcon sed seq setenforce setprop setsid sha1sum sleep sort\nsplit stat strings swapoff swapon sync sysctl tac tail tar taskset tee\ntime timeout touch tr true truncate umount uname uniq unix2dos usleep\nvmstat wc which whoami xargs yes\n\n\n## Android 5.0 (API level 21, \"Lollipop\")\n\nBSD: cat chown cp dd du grep kill ln mv printenv rm rmdir sleep sync\n\ntoolbox: chcon chmod clear cmp date df dmesg getenforce getevent getprop\ngetsebool hd id ifconfig iftop insmod ioctl ionice load\\_policy log ls\nlsmod lsof md5 mkdir mknod mkswap mount nandread netstat newfs\\_msdos\nnohup notify ps readlink renice restorecon rmmod route runcon schedtop\nsendevent setenforce setprop setsebool smd start stop swapoff swapon\ntop touch umount uptime vmstat watchprops wipe\n\n\n## Android 4.4 (API level 19, \"KitKat\")\n\nBSD: cat cp dd du grep newfs\\_msdos\n\ntoolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent\ngetprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln\nload\\_policy log ls lsmod lsof md5 mkdir mkswap mount mv nandread netstat\nnotify printenv ps readlink renice restorecon rm rmdir rmmod route runcon\nschedtop sendevent setconsole setenforce setprop setsebool sleep smd start\nstop swapoff swapon sync top touch umount uptime vmstat watchprops wipe\n\n\n## Android 4.1-4.3 (API level 16, \"Jelly Bean\")\n\nBSD: cat cp dd du grep newfs\\_msdos\n\ntoolbox: chcon chmod chown clear cmp date df dmesg getenforce getevent\ngetprop getsebool hd id ifconfig iftop insmod ioctl ionice kill ln\nload\\_policy log ls lsmod lsof md5 mkdir mount mv nandread netstat notify\nprintenv ps reboot renice restorecon rm rmdir rmmod route runcon schedtop\nsendevent setconsole setenforce setprop setsebool sleep smd start stop\nsync top touch umount uptime vmstat watchprops wipe\n\n\n## Android 4.0 (API level 14, \"Ice Cream Sandwich\")\n\nBSD: cat dd newfs\\_msdos\n\ntoolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig\niftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv\nnandread netstat notify printenv ps reboot renice rm rmdir rmmod route\nschedtop sendevent setconsole setprop sleep smd start stop sync top\ntouch umount uptime vmstat watchprops wipe\n\n\n## Android 2.3 (API level 9, \"Gingerbread\")\n\nBSD: cat dd newfs\\_msdos\n\ntoolbox: chmod chown cmp date df dmesg getevent getprop hd id ifconfig\niftop insmod ioctl ionice kill ln log ls lsmod lsof mkdir mount mv\nnandread netstat notify printenv ps reboot renice rm rmdir rmmod route\nschedtop sendevent setconsole setprop sleep smd start stop sync top\numount uptime vmstat watchprops wipe\n"
  },
  {
    "path": "storaged/Android.bp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"storaged_defaults\",\n\n    shared_libs: [\n        \"android.hardware.health@1.0\",\n        \"android.hardware.health@2.0\",\n        \"android.hardware.health-V4-ndk\",\n        \"libbase\",\n        \"libbinder\",\n        \"libbinder_ndk\",\n        \"libcutils\",\n        \"libhidlbase\",\n        \"liblog\",\n        \"libprotobuf-cpp-lite\",\n        \"libutils\",\n        \"libz\",\n        \"packagemanager_aidl-cpp\",\n    ],\n\n    static_libs: [\n        \"android.hardware.health-translate-ndk\",\n        \"libhealthhalutils\",\n        \"libhealthshim\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-Wextra\",\n        \"-Wno-unused-parameter\",\n    ],\n}\n\ncc_library_static {\n    name: \"libstoraged\",\n\n    defaults: [\"storaged_defaults\"],\n\n    aidl: {\n        export_aidl_headers: true,\n        local_include_dirs: [\"binder\"],\n        include_dirs: [\"frameworks/native/aidl/binder\"],\n    },\n\n    srcs: [\n        \"storaged.cpp\",\n        \"storaged_diskstats.cpp\",\n        \"storaged_info.cpp\",\n        \"storaged_service.cpp\",\n        \"storaged_utils.cpp\",\n        \"storaged_uid_monitor.cpp\",\n        \"uid_info.cpp\",\n        \"storaged.proto\",\n        \":storaged_aidl\",\n        \":storaged_aidl_private\",\n    ],\n\n    header_libs: [\"libbatteryservice_headers\"],\n\n    logtags: [\"EventLogTags.logtags\"],\n\n    proto: {\n        type: \"lite\",\n        export_proto_headers: true,\n    },\n\n    export_include_dirs: [\"include\"],\n}\n\ncc_binary {\n    name: \"storaged\",\n\n    defaults: [\"storaged_defaults\"],\n\n    init_rc: [\"storaged.rc\"],\n\n    srcs: [\"main.cpp\"],\n\n    static_libs: [\n        \"libstoraged\",\n    ],\n}\n\n/*\n * Run with:\n *  adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests\n */\ncc_test {\n    name: \"storaged-unit-tests\",\n\n    defaults: [\"storaged_defaults\"],\n\n    srcs: [\"tests/storaged_test.cpp\"],\n\n    static_libs: [\n        \"libstoraged\",\n    ],\n    test_suites: [\n        \"general-tests\",\n    ],\n}\n\n// AIDL interface between storaged and framework.jar\nfilegroup {\n    name: \"storaged_aidl\",\n    srcs: [\n        \"binder/android/os/IStoraged.aidl\",\n    ],\n    path: \"binder\",\n}\n\nfilegroup {\n    name: \"storaged_aidl_private\",\n    srcs: [\n        \"binder/android/os/storaged/IStoragedPrivate.aidl\",\n    ],\n    path: \"binder\",\n}\n\ncc_defaults {\n    name: \"storaged_service_fuzzer_defaults\",\n    defaults: [\n        \"storaged_defaults\",\n        \"service_fuzzer_defaults\",\n        \"fuzzer_disable_leaks\",\n    ],\n    static_libs: [\n        \"libstoraged\",\n    ],\n    fuzz_config: {\n        cc: [\n            \"dvander@google.com\",\n        ],\n        triage_assignee: \"waghpawan@google.com\",\n    },\n}\n\ncc_fuzz {\n    name: \"storaged_service_fuzzer\",\n    defaults: [\n        \"storaged_service_fuzzer_defaults\",\n    ],\n    srcs: [\"tests/fuzzers/storaged_service_fuzzer.cpp\"],\n}\n\ncc_fuzz {\n    name: \"storaged_private_service_fuzzer\",\n    defaults: [\n        \"storaged_service_fuzzer_defaults\",\n    ],\n    srcs: [\"tests/fuzzers/storaged_private_service_fuzzer.cpp\"],\n}\n"
  },
  {
    "path": "storaged/EventLogTags.logtags",
    "content": "# The entries in this file map a sparse set of log tag numbers to tag names.\n# This is installed on the device, in /system/etc, and parsed by logcat.\n#\n# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the\n# negative values alone for now.)\n#\n# Tag names are one or more ASCII letters and numbers or underscores, i.e.\n# \"[A-Z][a-z][0-9]_\".  Do not include spaces or punctuation (the former\n# impacts log readability, the latter makes regex searches more annoying).\n#\n# Tag numbers and names are separated by whitespace.  Blank lines and lines\n# starting with '#' are ignored.\n#\n# Optionally, after the tag names can be put a description for the value(s)\n# of the tag. Description are in the format\n#    (<name>|data type[|data unit])\n# Multiple values are separated by commas.\n#\n# The data type is a number from the following values:\n# 1: int\n# 2: long\n# 3: string\n# 4: list\n# 5: float\n#\n# The data unit is a number taken from the following list:\n# 1: Number of objects\n# 2: Number of bytes\n# 3: Number of milliseconds\n# 4: Number of allocations\n# 5: Id\n# 6: Percent\n# Default value for data of type int/long is 2 (bytes).\n#\n# TODO: generate \".java\" and \".h\" files with integer constants from this file.\n\n2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)\n\n2733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)"
  },
  {
    "path": "storaged/OWNERS",
    "content": "dvander@google.com\n"
  },
  {
    "path": "storaged/README.properties",
    "content": "ro.storaged.event.interval    # interval storaged scans for IO stats, in seconds\nro.storaged.event.perf_check  # check for time spent in event loop, in microseconds\nro.storaged.disk_stats_pub    # interval storaged publish disk stats, in seconds\nro.storaged.uid_io.interval   # interval storaged checks Per UID IO usage, in seconds\nro.storaged.uid_io.threshold  # Per UID IO usage limit, in bytes\n"
  },
  {
    "path": "storaged/binder/android/os/IStoraged.aidl",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os;\n\n/** {@hide} */\ninterface IStoraged {\n    void onUserStarted(int userId);\n    void onUserStopped(int userId);\n    int getRecentPerf();\n}"
  },
  {
    "path": "storaged/binder/android/os/storaged/IStoragedPrivate.aidl",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.storaged;\n\nimport android.os.storaged.UidInfo;\n\n/** {@hide} */\ninterface IStoragedPrivate {\n    UidInfo[] dumpUids();\n    int[] dumpPerfHistory();\n}"
  },
  {
    "path": "storaged/binder/android/os/storaged/UidInfo.aidl",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage android.os.storaged;\n\nparcelable UidInfo cpp_header \"include/uid_info.h\";\n"
  },
  {
    "path": "storaged/include/storaged.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _STORAGED_H_\n#define _STORAGED_H_\n\n#include <semaphore.h>\n#include <stdint.h>\n#include <time.h>\n\n#include <queue>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include <utils/Mutex.h>\n\n#include <aidl/android/hardware/health/IHealth.h>\n#include <android/hardware/health/2.0/IHealth.h>\n\n#define FRIEND_TEST(test_case_name, test_name) \\\nfriend class test_case_name##_##test_name##_Test\n\n#define ARRAY_SIZE(x)   (sizeof(x) / sizeof((x)[0]))\n\n#define IS_ALIGNED(x, align)   (!((x) & ((align) - 1)))\n#define ROUND_UP(x, align)     (((x) + ((align) - 1)) & ~((align) - 1))\n\n#define SECTOR_SIZE ( 512 )\n#define SEC_TO_MSEC ( 1000 )\n#define MSEC_TO_USEC ( 1000 )\n#define USEC_TO_NSEC ( 1000 )\n#define SEC_TO_USEC ( 1000000 )\n#define HOUR_TO_SEC ( 3600 )\n#define DAY_TO_SEC ( 3600 * 24 )\n#define WEEK_TO_DAYS ( 7 )\n#define YEAR_TO_WEEKS ( 52 )\n\n#include \"storaged_diskstats.h\"\n#include \"storaged_info.h\"\n#include \"storaged_uid_monitor.h\"\n#include \"storaged.pb.h\"\n#include \"uid_info.h\"\n\nusing namespace std;\nusing namespace android;\n\n// Periodic chores intervals in seconds\n#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 60 )\n#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 3600 )\n#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO ( 3600 )\n#define DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT ( 300 )\n#define DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO ( 3600 )\n\n// UID IO threshold in bytes\n#define DEFAULT_PERIODIC_CHORES_UID_IO_THRESHOLD ( 1024 * 1024 * 1024ULL )\n\nclass storaged_t;\n\nstruct storaged_config {\n    int periodic_chores_interval_unit;\n    int periodic_chores_interval_disk_stats_publish;\n    int periodic_chores_interval_uid_io;\n    int periodic_chores_interval_flush_proto;\n    int event_time_check_usec;  // check how much cputime spent in event loop\n};\n\nstruct HealthServicePair {\n    std::shared_ptr<aidl::android::hardware::health::IHealth> aidl_health;\n    android::sp<android::hardware::health::V2_0::IHealth> hidl_health;\n    static HealthServicePair get();\n};\n\nclass hidl_health_death_recipient : public android::hardware::hidl_death_recipient {\n  public:\n    hidl_health_death_recipient(const android::sp<android::hardware::health::V2_0::IHealth>& health)\n        : mHealth(health) {}\n    void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);\n\n  private:\n    android::sp<android::hardware::health::V2_0::IHealth> mHealth;\n};\n\nclass storaged_t : public RefBase {\n  private:\n    time_t mTimer;\n    storaged_config mConfig;\n    unique_ptr<disk_stats_monitor> mDsm;\n    uid_monitor mUidm;\n    time_t mStarttime;\n    std::shared_ptr<aidl::android::hardware::health::IHealth> health;\n    sp<android::hardware::hidl_death_recipient> hidl_death_recp;\n    ndk::ScopedAIBinder_DeathRecipient aidl_death_recp;\n    shared_ptr<aidl::android::hardware::health::IHealthInfoCallback> aidl_health_callback;\n    unique_ptr<storage_info_t> storage_info;\n    static const uint32_t current_version;\n    Mutex proto_lock;\n    unordered_map<userid_t, bool> proto_loaded;\n    void load_proto(userid_t user_id);\n    char* prepare_proto(userid_t user_id, StoragedProto* proto);\n    void flush_proto(userid_t user_id, StoragedProto* proto);\n    void flush_proto_data(userid_t user_id, const char* data, ssize_t size);\n    string proto_path(userid_t user_id) {\n        return string(\"/data/misc_ce/\") + to_string(user_id) +\n               \"/storaged/storaged.proto\";\n    }\n    void init_health_service();\n\n  public:\n    storaged_t(void);\n    void init(void);\n    void event(void);\n    void event_checked(void);\n    void pause(void) {\n        sleep(mConfig.periodic_chores_interval_unit);\n    }\n\n    time_t get_starttime(void) {\n        return mStarttime;\n    }\n\n    unordered_map<uint32_t, uid_info> get_uids(void) {\n        return mUidm.get_uid_io_stats();\n    }\n\n    vector<int> get_perf_history(void) {\n        return storage_info->get_perf_history();\n    }\n\n    uint32_t get_recent_perf(void) { return storage_info->get_recent_perf(); }\n\n    map<uint64_t, struct uid_records> get_uid_records(\n            double hours, uint64_t threshold, bool force_report) {\n        return mUidm.dump(hours, threshold, force_report);\n    }\n\n    void update_uid_io_interval(int interval) {\n        if (interval >= DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO_LIMIT) {\n            mConfig.periodic_chores_interval_uid_io = interval;\n        }\n    }\n\n    void add_user_ce(userid_t user_id);\n    void remove_user_ce(userid_t user_id);\n\n    void report_storage_info();\n\n    void flush_protos(unordered_map<int, StoragedProto>* protos);\n};\n\n// Eventlog tag\n// The content must match the definition in EventLogTags.logtags\n#define EVENTLOGTAG_DISKSTATS ( 2732 )\n#define EVENTLOGTAG_EMMCINFO ( 2733 )\n#define EVENTLOGTAG_UID_IO_ALERT ( 2734 )\n\n#endif /* _STORAGED_H_ */\n"
  },
  {
    "path": "storaged/include/storaged_diskstats.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _STORAGED_DISKSTATS_H_\n#define _STORAGED_DISKSTATS_H_\n\n#include <stdint.h>\n\n#include <aidl/android/hardware/health/IHealth.h>\n\n// number of attributes diskstats has\n#define DISK_STATS_SIZE ( 11 )\n\n#define MMC_DISK_STATS_PATH \"/sys/block/mmcblk0/stat\"\n#define SDA_DISK_STATS_PATH \"/sys/block/sda/stat\"\n\nstruct disk_stats {\n    /* It will be extremely unlikely for any of the following entries to overflow.\n     * For read_bytes(which will be greater than any of the following entries), it\n     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which\n     * is the peak memory transfer rate for current memory.\n     * The diskstats entries (first 11) need to be at top in this structure _after_\n     * compiler's optimization.\n     */\n    uint64_t read_ios;       // number of read I/Os processed\n    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os\n    uint64_t read_sectors;   // number of sectors read\n    uint64_t read_ticks;     // total wait time for read requests\n    uint64_t write_ios;      // number of write I/Os processed\n    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os\n    uint64_t write_sectors;  // number of sectors written\n    uint64_t write_ticks;    // total wait time for write requests\n    uint64_t io_in_flight;   // number of I/Os currently in flight\n    uint64_t io_ticks;       // total time this block device has been active\n    uint64_t io_in_queue;    // total wait time for all requests\n\n    uint64_t start_time;     // monotonic time accounting starts\n    uint64_t end_time;       // monotonic time accounting ends\n    uint32_t counter;        // private counter for accumulate calculations\n    double   io_avg;         // average io_in_flight for accumulate calculations\n\n    bool is_zero() {\n        return read_ios == 0 && write_ios == 0 &&\n               io_in_flight == 0 && io_ticks == 0 && io_in_queue == 0;\n    }\n\n    friend disk_stats operator- (disk_stats curr, const disk_stats& prev) {\n        curr.read_ios -= prev.read_ios;\n        curr.read_merges -= prev.read_merges;\n        curr.read_sectors -= prev.read_sectors;\n        curr.read_ticks -= prev.read_ticks;\n        curr.write_ios -= prev.write_ios;\n        curr.write_merges -= prev.write_merges;\n        curr.write_sectors -= prev.write_sectors;\n        curr.write_ticks -= prev.write_ticks;\n        /* skips io_in_flight, use current value */\n        curr.io_ticks -= prev.io_ticks;\n        curr.io_in_queue -= prev.io_in_queue;\n        return curr;\n    }\n\n    friend bool operator== (const disk_stats& a, const disk_stats& b) {\n        return a.read_ios == b.read_ios &&\n               a.read_merges == b.read_merges &&\n               a.read_sectors == b.read_sectors &&\n               a.read_ticks == b.read_ticks &&\n               a.write_ios == b.write_ios &&\n               a.write_merges == b.write_merges &&\n               a.write_sectors == b.write_sectors &&\n               a.write_ticks == b.write_ticks &&\n               /* skips io_in_flight */\n               a.io_ticks == b.io_ticks &&\n               a.io_in_queue == b.io_in_queue;\n    }\n\n    disk_stats& operator+= (const disk_stats& stats) {\n        read_ios += stats.read_ios;\n        read_merges += stats.read_merges;\n        read_sectors += stats.read_sectors;\n        read_ticks += stats.read_ticks;\n        write_ios += stats.write_ios;\n        write_merges += stats.write_merges;\n        write_sectors += stats.write_sectors;\n        write_ticks += stats.write_ticks;\n        /* skips io_in_flight, use current value */\n        io_ticks += stats.io_ticks;\n        io_in_queue += stats.io_in_queue;\n        return *this;\n    }\n};\n\nstruct disk_perf {\n    uint32_t read_perf;         // read speed (kbytes/s)\n    uint32_t read_ios;          // read I/Os per second\n    uint32_t write_perf;        // write speed (kbytes/s)\n    uint32_t write_ios;         // write I/Os per second\n    uint32_t queue;             // I/Os in queue\n    bool is_zero() {\n        return read_perf == 0 && read_ios == 0 &&\n               write_perf == 0 && write_ios == 0 && queue == 0;\n    }\n};\n\nclass stream_stats {\nprivate:\n    double mSum;\n    double mSquareSum;\n    uint32_t mCnt;\npublic:\n    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};\n    ~stream_stats() {};\n    double get_mean() {\n        return mSum / mCnt;\n    }\n    double get_std() {\n        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));\n    }\n    void add(uint32_t num) {\n        mSum += (double)num;\n        mSquareSum += (double)num * (double)num;\n        mCnt++;\n    }\n    void evict(uint32_t num) {\n        if (mSum < num || mSquareSum < (double)num * (double)num) return;\n        mSum -= (double)num;\n        mSquareSum -= (double)num * (double)num;\n        mCnt--;\n    }\n};\n\nclass disk_stats_monitor {\nprivate:\n    FRIEND_TEST(storaged_test, disk_stats_monitor);\n    const char* const DISK_STATS_PATH;\n    struct disk_stats mPrevious;\n    struct disk_stats mAccumulate;      /* reset after stall */\n    struct disk_stats mAccumulate_pub;  /* reset after publish */\n    bool mStall;\n    std::queue<struct disk_perf> mBuffer;\n    struct {\n        stream_stats read_perf;           // read speed (bytes/s)\n        stream_stats read_ios;            // read I/Os per second\n        stream_stats write_perf;          // write speed (bytes/s)\n        stream_stats write_ios;           // write I/O per second\n        stream_stats queue;               // I/Os in queue\n    } mStats;\n    bool mValid;\n    const uint32_t mWindow;\n    const double mSigma;\n    struct disk_perf mMean;\n    struct disk_perf mStd;\n    std::shared_ptr<aidl::android::hardware::health::IHealth> mHealth;\n\n    void update_mean();\n    void update_std();\n    void add(struct disk_perf* perf);\n    void evict(struct disk_perf* perf);\n    bool detect(struct disk_perf* perf);\n\n    void update(struct disk_stats* stats);\n\npublic:\n  disk_stats_monitor(const std::shared_ptr<aidl::android::hardware::health::IHealth>& healthService,\n                     uint32_t window_size = 5, double sigma = 1.0)\n      : DISK_STATS_PATH(\n                healthService != nullptr\n                        ? nullptr\n                        : (access(MMC_DISK_STATS_PATH, R_OK) == 0\n                                   ? MMC_DISK_STATS_PATH\n                                   : (access(SDA_DISK_STATS_PATH, R_OK) == 0 ? SDA_DISK_STATS_PATH\n                                                                             : nullptr))),\n        mPrevious(),\n        mAccumulate(),\n        mAccumulate_pub(),\n        mStall(false),\n        mValid(false),\n        mWindow(window_size),\n        mSigma(sigma),\n        mMean(),\n        mStd(),\n        mHealth(healthService) {}\n  bool enabled() { return mHealth != nullptr || DISK_STATS_PATH != nullptr; }\n  void update(void);\n  void publish(void);\n};\n\n#endif /* _STORAGED_DISKSTATS_H_ */\n"
  },
  {
    "path": "storaged/include/storaged_info.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _STORAGED_INFO_H_\n#define _STORAGED_INFO_H_\n\n#include <string.h>\n\n#include <chrono>\n\n#include <aidl/android/hardware/health/IHealth.h>\n#include <utils/Mutex.h>\n\n#include \"storaged.h\"\n#include \"storaged.pb.h\"\n\n#define FRIEND_TEST(test_case_name, test_name) \\\nfriend class test_case_name##_##test_name##_Test\n\nusing namespace std;\nusing namespace android;\nusing namespace chrono;\nusing namespace storaged_proto;\n\nclass storage_info_t {\n  protected:\n    FRIEND_TEST(storaged_test, storage_info_t);\n    FRIEND_TEST(storaged_test, storage_info_t_proto);\n    // emmc lifetime\n    uint16_t eol;                   // pre-eol (end of life) information\n    uint16_t lifetime_a;            // device life time estimation (type A)\n    uint16_t lifetime_b;            // device life time estimation (type B)\n    string version;                 // version string\n    // free space\n    const string userdata_path = \"/data\";\n    uint64_t userdata_total_kb;\n    uint64_t userdata_free_kb;\n    // io perf history\n    time_point<system_clock> day_start_tp;\n    vector<uint32_t> recent_perf;\n    uint32_t nr_samples;\n    vector<uint32_t> daily_perf;\n    uint32_t nr_days;\n    vector<uint32_t> weekly_perf;\n    uint32_t nr_weeks;\n    Mutex si_mutex;\n\n    storage_info_t() : eol(0), lifetime_a(0), lifetime_b(0),\n        userdata_total_kb(0), userdata_free_kb(0), nr_samples(0),\n        daily_perf(WEEK_TO_DAYS, 0), nr_days(0),\n        weekly_perf(YEAR_TO_WEEKS, 0), nr_weeks(0) {\n            day_start_tp = system_clock::now();\n            day_start_tp -= chrono::seconds(duration_cast<chrono::seconds>(\n                day_start_tp.time_since_epoch()).count() % DAY_TO_SEC);\n    }\n    void publish();\n    storage_info_t* s_info;\n\n  public:\n    static storage_info_t* get_storage_info(\n            const shared_ptr<aidl::android::hardware::health::IHealth>& healthService);\n    virtual ~storage_info_t(){};\n    virtual void report() {};\n    void load_perf_history_proto(const IOPerfHistory& perf_history);\n    void refresh(IOPerfHistory* perf_history);\n    void update_perf_history(uint32_t bw,\n                             const time_point<system_clock>& tp);\n    vector<int> get_perf_history();\n    uint32_t get_recent_perf();\n};\n\nclass emmc_info_t : public storage_info_t {\nprivate:\n    bool report_sysfs();\n    bool report_debugfs();\npublic:\n    static const string emmc_sysfs;\n    static const string emmc_debugfs;\n    static const char* emmc_ver_str[];\n\n    virtual ~emmc_info_t() {}\n    virtual void report();\n};\n\nclass ufs_info_t : public storage_info_t {\npublic:\n    static const string health_file;\n\n    virtual ~ufs_info_t() {}\n    virtual void report();\n};\n\nclass health_storage_info_t : public storage_info_t {\n  private:\n    using IHealth = aidl::android::hardware::health::IHealth;\n    using StorageInfo = aidl::android::hardware::health::StorageInfo;\n\n    shared_ptr<IHealth> mHealth;\n    void set_values_from_hal_storage_info(const StorageInfo& halInfo);\n\n  public:\n    health_storage_info_t(const shared_ptr<IHealth>& service) : mHealth(service){};\n    virtual ~health_storage_info_t() {}\n    virtual void report();\n};\n\n#endif /* _STORAGED_INFO_H_ */\n"
  },
  {
    "path": "storaged/include/storaged_service.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _STORAGED_SERVICE_H_\n#define _STORAGED_SERVICE_H_\n\n#include <vector>\n\n#include <binder/BinderService.h>\n\n#include \"android/os/BnStoraged.h\"\n#include \"android/os/storaged/BnStoragedPrivate.h\"\n\nusing namespace std;\nusing namespace android::os;\nusing namespace android::os::storaged;\n\nnamespace android {\nclass StoragedService : public BinderService<StoragedService>, public BnStoraged {\nprivate:\n    void dumpUidRecordsDebug(int fd, const vector<struct uid_record>& entries);\n    void dumpUidRecords(int fd, const vector<struct uid_record>& entries);\npublic:\n    static status_t start();\n    static char const* getServiceName() { return \"storaged\"; }\n    virtual status_t dump(int fd, const Vector<String16> &args) override;\n\n    binder::Status onUserStarted(int32_t userId);\n    binder::Status onUserStopped(int32_t userId);\n    binder::Status getRecentPerf(int32_t* _aidl_return);\n};\n\nclass StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {\npublic:\n    static status_t start();\n    static char const* getServiceName() { return \"storaged_pri\"; }\n\n    binder::Status dumpUids(vector<UidInfo>* _aidl_return);\n    binder::Status dumpPerfHistory(vector<int32_t>* _aidl_return);\n};\n\nsp<IStoragedPrivate> get_storaged_pri_service();\n\n}  // namespace android\n#endif /* _STORAGED_SERVICE_H_ */"
  },
  {
    "path": "storaged/include/storaged_uid_monitor.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _STORAGED_UID_MONITOR_H_\n#define _STORAGED_UID_MONITOR_H_\n\n#include <stdint.h>\n\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include <cutils/multiuser.h>\n#include <utils/Mutex.h>\n\n#include \"storaged.pb.h\"\n#include \"uid_info.h\"\n\n#define FRIEND_TEST(test_case_name, test_name) \\\nfriend class test_case_name##_##test_name##_Test\n\nusing namespace std;\nusing namespace storaged_proto;\nusing namespace android;\nusing namespace android::os::storaged;\n\nclass uid_info : public UidInfo {\npublic:\n    bool parse_uid_io_stats(string&& s);\n};\n\nclass io_usage {\npublic:\n    io_usage() : bytes{{{0}}} {};\n    uint64_t bytes[IO_TYPES][UID_STATS][CHARGER_STATS];\n    bool is_zero() const;\n    io_usage& operator+= (const io_usage& stats) {\n        for (int i = 0; i < IO_TYPES; i++) {\n            for (int j = 0; j < UID_STATS; j++) {\n                for (int k = 0; k < CHARGER_STATS; k++) {\n                    bytes[i][j][k] += stats.bytes[i][j][k];\n                }\n            }\n        }\n        return *this;\n    }\n};\n\nstruct uid_io_usage {\n    userid_t user_id;\n    io_usage uid_ios;\n    // mapped from task comm to task io usage\n    map<string, io_usage> task_ios;\n};\n\nstruct uid_record {\n    string name;\n    uid_io_usage ios;\n};\n\nstruct uid_records {\n    uint64_t start_ts;\n    vector<uid_record> entries;\n};\n\nclass uid_monitor {\nprivate:\n    FRIEND_TEST(storaged_test, uid_monitor);\n    FRIEND_TEST(storaged_test, load_uid_io_proto);\n\n    // last dump from /proc/uid_io/stats, uid -> uid_info\n    unordered_map<uint32_t, uid_info> last_uid_io_stats_;\n    // current io usage for next report, app name -> uid_io_usage\n    unordered_map<string, uid_io_usage> curr_io_stats_;\n    // io usage records, end timestamp -> {start timestamp, vector of records}\n    map<uint64_t, uid_records> io_history_;\n    // charger ON/OFF\n    charger_stat_t charger_stat_;\n    // protects curr_io_stats, last_uid_io_stats, records and charger_stat\n    Mutex uidm_mutex_;\n    // start time for IO records\n    uint64_t start_ts_;\n    // true if UID_IO_STATS_PATH is accessible\n    const bool enabled_;\n\n    // reads from /proc/uid_io/stats\n    unordered_map<uint32_t, uid_info> get_uid_io_stats_locked();\n    // flushes curr_io_stats to records\n    void add_records_locked(uint64_t curr_ts);\n    // updates curr_io_stats and set last_uid_io_stats\n    void update_curr_io_stats_locked();\n    // writes io_history to protobuf\n    void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);\n\n    // Ensure that io_history_ can append |n| items without exceeding\n    // MAX_UID_RECORDS_SIZE in size.\n    void maybe_shrink_history_for_items(size_t nitems);\n\npublic:\n    uid_monitor();\n    // called by storaged main thread\n    void init(charger_stat_t stat);\n    // called by storaged -u\n    unordered_map<uint32_t, uid_info> get_uid_io_stats();\n    // called by dumpsys\n    map<uint64_t, uid_records> dump(\n        double hours, uint64_t threshold, bool force_report);\n    // called by battery properties listener\n    void set_charger_state(charger_stat_t stat);\n    // called by storaged periodic_chore or dump with force_report\n    bool enabled() { return enabled_; };\n    void report(unordered_map<int, StoragedProto>* protos);\n    // restores io_history from protobuf\n    void load_uid_io_proto(userid_t user_id, const UidIOUsage& proto);\n    void clear_user_history(userid_t user_id);\n\n    map<uint64_t, uid_records>& io_history() { return io_history_; }\n\n    static constexpr int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours\n};\n\n#endif /* _STORAGED_UID_MONITOR_H_ */\n"
  },
  {
    "path": "storaged/include/storaged_utils.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _STORAGED_UTILS_H_\n#define _STORAGED_UTILS_H_\n\n#include <stdint.h>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"storaged.h\"\n\nusing namespace android::os::storaged;\n\n// Diskstats\nbool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);\nstruct disk_perf get_disk_perf(struct disk_stats* stats);\nvoid get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr, struct disk_stats* inc);\nvoid add_disk_stats(struct disk_stats* src, struct disk_stats* dst);\n\n// UID I/O\nmap<string, io_usage> merge_io_usage(const vector<uid_record>& entries);\nvoid sort_running_uids_info(std::vector<UidInfo> &uids);\n\n// Logging\nvoid log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task);\nvoid log_console_perf_history(const vector<int>& perf_history);\n\n#endif /* _STORAGED_UTILS_H_ */\n"
  },
  {
    "path": "storaged/include/uid_info.h",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#ifndef _UID_INFO_H_\n#define _UID_INFO_H_\n\n#include <string>\n#include <unordered_map>\n\n#include <binder/Parcelable.h>\n\nnamespace android {\nnamespace os {\nnamespace storaged {\n\nenum uid_stat_t {\n    FOREGROUND = 0,\n    BACKGROUND = 1,\n    UID_STATS = 2\n};\n\nenum charger_stat_t {\n    CHARGER_OFF = 0,\n    CHARGER_ON = 1,\n    CHARGER_STATS = 2\n};\n\nenum io_type_t {\n    READ = 0,\n    WRITE = 1,\n    IO_TYPES = 2\n};\n\nstruct io_stats {\n    uint64_t rchar;                 // characters read\n    uint64_t wchar;                 // characters written\n    uint64_t read_bytes;            // bytes read (from storage layer)\n    uint64_t write_bytes;           // bytes written (to storage layer)\n    uint64_t fsync;                 // number of fsync syscalls\n};\n\nclass task_info {\npublic:\n    std::string comm;\n    pid_t pid;\n    io_stats io[UID_STATS];\n    bool parse_task_io_stats(std::string&& s);\n};\n\nclass UidInfo : public Parcelable {\npublic:\n    uint32_t uid;                     // user id\n    std::string name;                 // package name\n    io_stats io[UID_STATS];           // [0]:foreground [1]:background\n    std::unordered_map<uint32_t, task_info> tasks; // mapped from pid\n\n    status_t writeToParcel(Parcel* parcel) const override;\n    status_t readFromParcel(const Parcel* parcel) override;\n};\n\n} // namespace storaged\n} // namespace os\n} // namespace android\n\n#endif /*  _UID_INFO_H_ */"
  },
  {
    "path": "storaged/main.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"storaged\"\n#define KLOG_LEVEL 6\n\n#include <fcntl.h>\n#include <getopt.h>\n#include <pthread.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <vector>\n\n#include <android-base/macros.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <binder/ProcessState.h>\n#include <binder/IServiceManager.h>\n#include <binder/IPCThreadState.h>\n#include <cutils/android_get_control_file.h>\n#include <cutils/sched_policy.h>\n#include <private/android_filesystem_config.h>\n\n#include <storaged.h>\n#include <storaged_service.h>\n#include <storaged_utils.h>\n\nusing namespace std;\nusing namespace android;\n\nsp<storaged_t> storaged_sp;\n\n// Function of storaged's main thread\nvoid* storaged_main(void* /* unused */) {\n    LOG(INFO) << \"storaged: Start\";\n\n    for (;;) {\n        storaged_sp->event_checked();\n        storaged_sp->pause();\n    }\n    return NULL;\n}\n\nvoid help_message(void) {\n    printf(\"usage: storaged [OPTION]\\n\");\n    printf(\"  -u    --uid                   Dump uid I/O usage to stdout\\n\");\n    printf(\"  -t    --task                  Dump task I/O usage to stdout\\n\");\n    printf(\"  -p    --perf                  Dump I/O perf history to stdout\\n\");\n    printf(\"  -s    --start                 Start storaged (default)\\n\");\n    fflush(stdout);\n}\n\nint main(int argc, char** argv) {\n    bool flag_main_service = false;\n    bool flag_dump_uid = false;\n    bool flag_dump_task = false;\n    bool flag_dump_perf = false;\n    int opt;\n\n    signal(SIGPIPE, SIG_IGN);\n    android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));\n\n    for (;;) {\n        int opt_idx = 0;\n        static struct option long_options[] = {\n            {\"perf\",        no_argument,    nullptr, 'p'},\n            {\"start\",       no_argument,    nullptr, 's'},\n            {\"task\",        no_argument,    nullptr, 't'},\n            {\"uid\",         no_argument,    nullptr, 'u'},\n            {nullptr,       0,              nullptr,  0}\n        };\n        opt = getopt_long(argc, argv, \":pstu\", long_options, &opt_idx);\n        if (opt == -1) {\n            break;\n        }\n\n        switch (opt) {\n        case 'p':\n            flag_dump_perf = true;\n            break;\n        case 's':\n            flag_main_service = true;\n            break;\n        case 't':\n            flag_dump_task = true;\n            break;\n        case 'u':\n            flag_dump_uid = true;\n            break;\n        default:\n            help_message();\n            return 0;\n        }\n    }\n\n    if (argc == 1) {\n        flag_main_service = true;\n    }\n\n    if (flag_main_service && (flag_dump_uid || flag_dump_task)) {\n        fprintf(stderr, \"Invalid arguments. Option \\\"start\\\" and \\\"dump\\\" cannot be used together.\\n\");\n        help_message();\n        return -1;\n    }\n\n    if (flag_main_service) { // start main thread\n        // Start the main thread of storaged\n        storaged_sp = new storaged_t();\n        storaged_sp->init();\n        storaged_sp->report_storage_info();\n        pthread_t storaged_main_thread;\n        errno = pthread_create(&storaged_main_thread, NULL, storaged_main, NULL);\n        if (errno != 0) {\n            PLOG(ERROR) << \"Failed to create main thread\";\n            return -1;\n        }\n\n        if (StoragedService::start() != android::OK ||\n            StoragedPrivateService::start() != android::OK) {\n            PLOG(ERROR) << \"Failed to start storaged service\";\n            return -1;\n        }\n\n        android::ProcessState::self()->startThreadPool();\n        IPCThreadState::self()->joinThreadPool();\n        pthread_join(storaged_main_thread, NULL);\n\n        return 0;\n    }\n\n    sp<IStoragedPrivate> storaged_service = get_storaged_pri_service();\n    if (storaged_service == NULL) {\n        fprintf(stderr, \"Cannot find storaged service.\\nMaybe run storaged --start first?\\n\");\n        return -1;\n    }\n\n    if (flag_dump_uid || flag_dump_task) {\n        vector<UidInfo> uid_io;\n        binder::Status status = storaged_service->dumpUids(&uid_io);\n        if (!status.isOk() || uid_io.size() == 0) {\n            fprintf(stderr, \"UID I/O info is not available.\\n\");\n            return 0;\n        }\n\n        sort_running_uids_info(uid_io);\n        log_console_running_uids_info(uid_io, flag_dump_task);\n    }\n\n    if (flag_dump_perf) {\n        vector<int> perf_history;\n        binder::Status status = storaged_service->dumpPerfHistory(&perf_history);\n        if (!status.isOk() || perf_history.size() == 0) {\n            fprintf(stderr, \"I/O perf history is not available.\\n\");\n            return 0;\n        }\n\n        log_console_perf_history(perf_history);\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "storaged/storaged.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"storaged\"\n\n#include <dirent.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <time.h>\n#include <unistd.h>\n#include <zlib.h>\n\n#include <chrono>\n#include <fstream>\n#include <sstream>\n#include <string>\n#include <utility>\n\n#include <aidl/android/hardware/health/BnHealthInfoCallback.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <android/binder_ibinder.h>\n#include <android/binder_manager.h>\n#include <android/hidl/manager/1.0/IServiceManager.h>\n#include <batteryservice/BatteryServiceConstants.h>\n#include <cutils/properties.h>\n#include <health-shim/shim.h>\n#include <healthhalutils/HealthHalUtils.h>\n#include <hidl/HidlTransportSupport.h>\n#include <hwbinder/IPCThreadState.h>\n#include <log/log.h>\n\n#include <storaged.h>\n#include <storaged_utils.h>\n\nusing namespace android::base;\nusing namespace chrono;\nusing namespace google::protobuf::io;\nusing namespace storaged_proto;\n\nnamespace {\n\n/*\n * The system user is the initial user that is implicitly created on first boot\n * and hosts most of the system services. Keep this in sync with\n * frameworks/base/core/java/android/os/UserManager.java\n */\nconstexpr int USER_SYSTEM = 0;\n\nconstexpr ssize_t benchmark_unit_size = 16 * 1024;  // 16KB\n\nconstexpr size_t min_benchmark_size = 128 * 1024;  // 128KB\n\n}  // namespace\n\nconst uint32_t storaged_t::current_version = 4;\n\nusing aidl::android::hardware::health::BatteryStatus;\nusing aidl::android::hardware::health::BnHealthInfoCallback;\nusing aidl::android::hardware::health::HealthInfo;\nusing aidl::android::hardware::health::IHealth;\nusing aidl::android::hardware::health::IHealthInfoCallback;\nusing android::hardware::interfacesEqual;\nusing android::hardware::health::V2_0::get_health_service;\nusing android::hidl::manager::V1_0::IServiceManager;\nusing HidlHealth = android::hardware::health::V2_0::IHealth;\nusing aidl::android::hardware::health::HealthShim;\nusing ndk::ScopedAIBinder_DeathRecipient;\nusing ndk::ScopedAStatus;\n\nHealthServicePair HealthServicePair::get() {\n    HealthServicePair ret;\n    auto service_name = IHealth::descriptor + \"/default\"s;\n    if (AServiceManager_isDeclared(service_name.c_str())) {\n        ndk::SpAIBinder binder(AServiceManager_waitForService(service_name.c_str()));\n        ret.aidl_health = IHealth::fromBinder(binder);\n        if (ret.aidl_health == nullptr) {\n            LOG(WARNING) << \"AIDL health service is declared, but it cannot be retrieved.\";\n        }\n    }\n    if (ret.aidl_health == nullptr) {\n        LOG(INFO) << \"Unable to get AIDL health service, trying HIDL...\";\n        ret.hidl_health = get_health_service();\n        if (ret.hidl_health != nullptr) {\n            ret.aidl_health = ndk::SharedRefBase::make<HealthShim>(ret.hidl_health);\n        }\n    }\n    if (ret.aidl_health == nullptr) {\n        LOG(WARNING) << \"health: failed to find IHealth service\";\n        return {};\n    }\n    return ret;\n}\n\ninline charger_stat_t is_charger_on(BatteryStatus prop) {\n    return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?\n        CHARGER_ON : CHARGER_OFF;\n}\n\nclass HealthInfoCallback : public BnHealthInfoCallback {\n  public:\n    HealthInfoCallback(uid_monitor* uidm) : mUidm(uidm) {}\n    ScopedAStatus healthInfoChanged(const HealthInfo& info) override {\n        mUidm->set_charger_state(is_charger_on(info.batteryStatus));\n        return ScopedAStatus::ok();\n    }\n\n  private:\n    uid_monitor* mUidm;\n};\n\nvoid storaged_t::init() {\n    init_health_service();\n    mDsm = std::make_unique<disk_stats_monitor>(health);\n    storage_info.reset(storage_info_t::get_storage_info(health));\n}\n\nstatic void onHealthBinderDied(void*) {\n    LOG(ERROR) << \"health service died, exiting\";\n    android::hardware::IPCThreadState::self()->stopProcess();\n    exit(1);\n}\n\nvoid storaged_t::init_health_service() {\n    if (!mUidm.enabled())\n        return;\n\n    auto [aidlHealth, hidlHealth] = HealthServicePair::get();\n    health = aidlHealth;\n    if (health == nullptr) return;\n\n    BatteryStatus status = BatteryStatus::UNKNOWN;\n    auto ret = health->getChargeStatus(&status);\n    if (!ret.isOk()) {\n        LOG(WARNING) << \"health: cannot get battery status: \" << ret.getDescription();\n    }\n    if (status == BatteryStatus::UNKNOWN) {\n        LOG(WARNING) << \"health: invalid battery status\";\n    }\n\n    mUidm.init(is_charger_on(status));\n    // register listener after init uid_monitor\n    aidl_health_callback = ndk::SharedRefBase::make<HealthInfoCallback>(&mUidm);\n    ret = health->registerCallback(aidl_health_callback);\n    if (!ret.isOk()) {\n        LOG(WARNING) << \"health: failed to register callback: \" << ret.getDescription();\n    }\n\n    if (hidlHealth != nullptr) {\n        hidl_death_recp = new hidl_health_death_recipient(hidlHealth);\n        auto ret = hidlHealth->linkToDeath(hidl_death_recp, 0 /* cookie */);\n        if (!ret.isOk()) {\n            LOG(WARNING) << \"Failed to link to death (HIDL): \" << ret.description();\n        }\n    } else {\n        aidl_death_recp =\n                ScopedAIBinder_DeathRecipient(AIBinder_DeathRecipient_new(onHealthBinderDied));\n        auto ret = AIBinder_linkToDeath(health->asBinder().get(), aidl_death_recp.get(),\n                                        nullptr /* cookie */);\n        if (ret != STATUS_OK) {\n            LOG(WARNING) << \"Failed to link to death (AIDL): \"\n                         << ScopedAStatus(AStatus_fromStatus(ret)).getDescription();\n        }\n    }\n}\n\nvoid hidl_health_death_recipient::serviceDied(uint64_t cookie,\n                                              const wp<::android::hidl::base::V1_0::IBase>& who) {\n    if (mHealth != nullptr && interfacesEqual(mHealth, who.promote())) {\n        onHealthBinderDied(reinterpret_cast<void*>(cookie));\n    } else {\n        LOG(ERROR) << \"unknown service died\";\n    }\n}\n\nvoid storaged_t::report_storage_info() {\n    storage_info->report();\n}\n\n/* storaged_t */\nstoraged_t::storaged_t(void) {\n    mConfig.periodic_chores_interval_unit =\n        property_get_int32(\"ro.storaged.event.interval\",\n                           DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT);\n\n    mConfig.event_time_check_usec =\n        property_get_int32(\"ro.storaged.event.perf_check\", 0);\n\n    mConfig.periodic_chores_interval_disk_stats_publish =\n        property_get_int32(\"ro.storaged.disk_stats_pub\",\n                           DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH);\n\n    mConfig.periodic_chores_interval_uid_io =\n        property_get_int32(\"ro.storaged.uid_io.interval\",\n                           DEFAULT_PERIODIC_CHORES_INTERVAL_UID_IO);\n\n    mConfig.periodic_chores_interval_flush_proto =\n        property_get_int32(\"ro.storaged.flush_proto.interval\",\n                           DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);\n\n    mStarttime = time(NULL);\n    mTimer = 0;\n}\n\nvoid storaged_t::add_user_ce(userid_t user_id) {\n    Mutex::Autolock _l(proto_lock);\n\n    if (!proto_loaded[user_id]) {\n        load_proto(user_id);\n        proto_loaded[user_id] = true;\n    }\n}\n\nvoid storaged_t::remove_user_ce(userid_t user_id) {\n    Mutex::Autolock _l(proto_lock);\n\n    proto_loaded[user_id] = false;\n    mUidm.clear_user_history(user_id);\n    RemoveFileIfExists(proto_path(user_id), nullptr);\n}\n\nvoid storaged_t::load_proto(userid_t user_id) {\n    string proto_file = proto_path(user_id);\n    ifstream in(proto_file, ofstream::in | ofstream::binary);\n\n    if (!in.good()) return;\n\n    stringstream ss;\n    ss << in.rdbuf();\n    StoragedProto proto;\n    proto.ParseFromString(ss.str());\n\n    const UidIOUsage& uid_io_usage = proto.uid_io_usage();\n    uint32_t computed_crc =\n            crc32(current_version,\n                  reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),\n                  uid_io_usage.ByteSizeLong());\n    if (proto.crc() != computed_crc) {\n        LOG(WARNING) << \"CRC mismatch in \" << proto_file;\n        return;\n    }\n\n    mUidm.load_uid_io_proto(user_id, proto.uid_io_usage());\n\n    if (user_id == USER_SYSTEM) {\n        storage_info->load_perf_history_proto(proto.perf_history());\n    }\n}\n\nchar* storaged_t:: prepare_proto(userid_t user_id, StoragedProto* proto) {\n    proto->set_version(current_version);\n\n    const UidIOUsage& uid_io_usage = proto->uid_io_usage();\n    proto->set_crc(crc32(current_version,\n                         reinterpret_cast<const Bytef*>(uid_io_usage.SerializeAsString().c_str()),\n                         uid_io_usage.ByteSizeLong()));\n\n    uint32_t pagesize = sysconf(_SC_PAGESIZE);\n    if (user_id == USER_SYSTEM) {\n        proto->set_padding(\"\", 1);\n        vector<char> padding;\n        ssize_t size = ROUND_UP(std::max(min_benchmark_size, proto->ByteSizeLong()), pagesize);\n        padding = vector<char>(size - proto->ByteSizeLong(), 0xFD);\n        proto->set_padding(padding.data(), padding.size());\n        while (!IS_ALIGNED(proto->ByteSizeLong(), pagesize)) {\n            padding.push_back(0xFD);\n            proto->set_padding(padding.data(), padding.size());\n        }\n    }\n\n    char* data = nullptr;\n    if (posix_memalign(reinterpret_cast<void**>(&data), pagesize, proto->ByteSizeLong())) {\n        PLOG(ERROR) << \"Faied to alloc aligned buffer (size: \" << proto->ByteSizeLong() << \")\";\n        return data;\n    }\n\n    proto->SerializeToArray(data, proto->ByteSizeLong());\n    return data;\n}\n\nvoid storaged_t::flush_proto_data(userid_t user_id,\n                                  const char* data, ssize_t size) {\n    string proto_file = proto_path(user_id);\n    string tmp_file = proto_file + \"_tmp\";\n    unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_file.c_str(),\n                 O_SYNC | O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC |\n                    (user_id == USER_SYSTEM ? O_DIRECT : 0),\n                 S_IRUSR | S_IWUSR)));\n    if (fd == -1) {\n        PLOG(ERROR) << \"Faied to open tmp file: \" << tmp_file;\n        return;\n    }\n\n    if (user_id == USER_SYSTEM) {\n        time_point<steady_clock> start, end;\n        uint32_t benchmark_size = 0;\n        uint64_t benchmark_time_ns = 0;\n        ssize_t ret;\n        bool first_write = true;\n\n        while (size > 0) {\n            start = steady_clock::now();\n            ret = write(fd, data, std::min(benchmark_unit_size, size));\n            if (ret <= 0) {\n                PLOG(ERROR) << \"Faied to write tmp file: \" << tmp_file;\n                return;\n            }\n            end = steady_clock::now();\n            /*\n            * compute bandwidth after the first write and if write returns\n            * exactly unit size.\n            */\n            if (!first_write && ret == benchmark_unit_size) {\n                benchmark_size += benchmark_unit_size;\n                benchmark_time_ns += duration_cast<nanoseconds>(end - start).count();\n            }\n            size -= ret;\n            data += ret;\n            first_write = false;\n        }\n\n        if (benchmark_size && benchmark_time_ns) {\n            int perf = benchmark_size * 1000000LLU / benchmark_time_ns;\n            storage_info->update_perf_history(perf, system_clock::now());\n        }\n    } else {\n        if (!WriteFully(fd, data, size)) {\n            PLOG(ERROR) << \"Faied to write tmp file: \" << tmp_file;\n            return;\n        }\n    }\n\n    fd.reset(-1);\n    rename(tmp_file.c_str(), proto_file.c_str());\n}\n\nvoid storaged_t::flush_proto(userid_t user_id, StoragedProto* proto) {\n    unique_ptr<char> proto_data(prepare_proto(user_id, proto));\n    if (proto_data == nullptr) return;\n\n    flush_proto_data(user_id, proto_data.get(), proto->ByteSizeLong());\n}\n\nvoid storaged_t::flush_protos(unordered_map<int, StoragedProto>* protos) {\n    Mutex::Autolock _l(proto_lock);\n\n    for (auto& it : *protos) {\n        /*\n         * Don't flush proto if we haven't attempted to load it from file.\n         */\n        if (proto_loaded[it.first]) {\n            flush_proto(it.first, &it.second);\n        }\n    }\n}\n\nvoid storaged_t::event(void) {\n    unordered_map<int, StoragedProto> protos;\n\n    if (mDsm->enabled()) {\n        mDsm->update();\n        if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {\n            mDsm->publish();\n        }\n    }\n\n    if (!(mTimer % mConfig.periodic_chores_interval_uid_io)) {\n        mUidm.report(&protos);\n    }\n\n    if (storage_info) {\n        storage_info->refresh(protos[USER_SYSTEM].mutable_perf_history());\n    }\n\n    if (!(mTimer % mConfig.periodic_chores_interval_flush_proto)) {\n        flush_protos(&protos);\n    }\n\n    mTimer += mConfig.periodic_chores_interval_unit;\n}\n\nvoid storaged_t::event_checked(void) {\n    struct timespec start_ts, end_ts;\n    bool check_time = true;\n\n    if (mConfig.event_time_check_usec &&\n        clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start_ts) < 0) {\n        check_time = false;\n        PLOG(ERROR) << \"clock_gettime() failed\";\n    }\n\n    event();\n\n    if (mConfig.event_time_check_usec && check_time) {\n        if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &end_ts) < 0) {\n            PLOG(ERROR) << \"clock_gettime() failed\";\n            return;\n        }\n        int64_t cost = (end_ts.tv_sec - start_ts.tv_sec) * SEC_TO_USEC +\n                       (end_ts.tv_nsec - start_ts.tv_nsec) / USEC_TO_NSEC;\n        if (cost > mConfig.event_time_check_usec) {\n            LOG(ERROR) << \"event loop spent \" << cost << \" usec, threshold \"\n                       << mConfig.event_time_check_usec << \" usec\";\n        }\n    }\n}\n"
  },
  {
    "path": "storaged/storaged.proto",
    "content": "syntax = \"proto2\";\noption optimize_for = LITE_RUNTIME;\npackage storaged_proto;\noption java_package = \"com.android.storaged.proto\";\noption java_outer_classname = \"Storaged\";\n\nmessage IOUsage {\n  optional uint64 rd_fg_chg_on  = 1;\n  optional uint64 rd_fg_chg_off = 2;\n  optional uint64 rd_bg_chg_on  = 3;\n  optional uint64 rd_bg_chg_off = 4;\n  optional uint64 wr_fg_chg_on  = 5;\n  optional uint64 wr_fg_chg_off = 6;\n  optional uint64 wr_bg_chg_on  = 7;\n  optional uint64 wr_bg_chg_off = 8;\n}\n\nmessage TaskIOUsage {\n  optional string task_name = 1;\n  optional IOUsage ios = 2;\n}\n\nmessage UidRecord {\n  optional string uid_name = 1;\n  optional uint32 user_id = 2;\n  optional IOUsage uid_io = 3;\n  repeated TaskIOUsage task_io = 4;\n}\n\nmessage UidIORecords {\n  optional uint64 start_ts = 1;\n  repeated UidRecord entries = 2;\n}\n\nmessage UidIOItem {\n  optional uint64 end_ts = 1;\n  optional UidIORecords records = 2;\n}\n\nmessage UidIOUsage {\n  repeated UidIOItem uid_io_items = 2;\n}\n\nmessage IOPerfHistory {\n  optional uint64 day_start_sec = 1;\n  repeated uint32 recent_perf = 2;\n  optional uint32 nr_samples = 3;\n  repeated uint32 daily_perf = 4;\n  optional uint32 nr_days = 5;\n  repeated uint32 weekly_perf = 6;\n  optional uint32 nr_weeks = 7;\n}\n\nmessage StoragedProto {\n  optional uint32 crc = 1;\n  optional uint32 version = 2;\n  optional UidIOUsage uid_io_usage = 3;\n  optional IOPerfHistory perf_history = 4;\n  optional bytes padding = 5;\n}\n"
  },
  {
    "path": "storaged/storaged.rc",
    "content": "service storaged /system/bin/storaged\n    class main\n    capabilities DAC_READ_SEARCH\n    priority 10\n    file /d/mmc0/mmc0:0001/ext_csd r\n    task_profiles ServiceCapacityLow\n    user root\n    group package_info\n"
  },
  {
    "path": "storaged/storaged_diskstats.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"storaged\"\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#include <sstream>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <log/log_event_list.h>\n\n#include \"storaged.h\"\n#include \"storaged_diskstats.h\"\n\nnamespace {\n\nusing aidl::android::hardware::health::DiskStats;\nusing aidl::android::hardware::health::IHealth;\n\n#ifdef DEBUG\nvoid log_debug_disk_perf(struct disk_perf* perf, const char* type) {\n    // skip if the input structure are all zeros\n    if (perf == NULL || perf->is_zero()) return;\n\n    LOG(INFO) << \"disk_perf \" << type << \" rd: \" << perf->read_perf << \" kbps, \" << perf->read_ios\n              << \" iops\"\n              << \" wr: \" << perf->write_perf << \" kbps, \" << perf->write_ios << \" iops\"\n              << \" q: \" << perf->queue;\n}\n#else\nvoid log_debug_disk_perf(struct disk_perf* perf, const char* type) {}\n#endif\n\nvoid log_event_disk_stats(struct disk_stats* stats, const char* type) {\n    // skip if the input structure are all zeros\n    if (stats == NULL || stats->is_zero()) return;\n\n    android_log_event_list(EVENTLOGTAG_DISKSTATS)\n        << type << stats->start_time << stats->end_time\n        << stats->read_ios << stats->read_merges\n        << stats->read_sectors << stats->read_ticks\n        << stats->write_ios << stats->write_merges\n        << stats->write_sectors << stats->write_ticks\n        << (uint64_t)stats->io_avg << stats->io_ticks << stats->io_in_queue\n        << LOG_ID_EVENTS;\n}\n\n} // namespace\n\nbool get_time(struct timespec* ts) {\n    // Use monotonic to exclude suspend time so that we measure IO bytes/sec\n    // when system is running.\n    int ret = clock_gettime(CLOCK_MONOTONIC, ts);\n    if (ret < 0) {\n        PLOG(ERROR) << \"clock_gettime() failed\";\n        return false;\n    }\n    return true;\n}\n\nvoid init_disk_stats_other(const struct timespec& ts, struct disk_stats* stats) {\n    stats->start_time = 0;\n    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);\n    stats->counter = 1;\n    stats->io_avg = (double)stats->io_in_flight;\n}\n\nbool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {\n    // Get time\n    struct timespec ts;\n    if (!get_time(&ts)) {\n        return false;\n    }\n\n    std::string buffer;\n    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {\n        PLOG(ERROR) << disk_stats_path << \": ReadFileToString failed.\";\n        return false;\n    }\n\n    // Regular diskstats entries\n    std::stringstream ss(buffer);\n    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {\n        ss >> *((uint64_t*)stats + i);\n    }\n    // Other entries\n    init_disk_stats_other(ts, stats);\n    return true;\n}\n\nvoid convert_hal_disk_stats(struct disk_stats* dst, const DiskStats& src) {\n    dst->read_ios = src.reads;\n    dst->read_merges = src.readMerges;\n    dst->read_sectors = src.readSectors;\n    dst->read_ticks = src.readTicks;\n    dst->write_ios = src.writes;\n    dst->write_merges = src.writeMerges;\n    dst->write_sectors = src.writeSectors;\n    dst->write_ticks = src.writeTicks;\n    dst->io_in_flight = src.ioInFlight;\n    dst->io_ticks = src.ioTicks;\n    dst->io_in_queue = src.ioInQueue;\n}\n\nbool get_disk_stats_from_health_hal(const std::shared_ptr<IHealth>& service,\n                                    struct disk_stats* stats) {\n    struct timespec ts;\n    if (!get_time(&ts)) {\n        return false;\n    }\n\n    std::vector<DiskStats> halStats;\n    auto ret = service->getDiskStats(&halStats);\n    if (ret.isOk()) {\n        if (halStats.size() > 0) {\n            convert_hal_disk_stats(stats, halStats[0]);\n            init_disk_stats_other(ts, stats);\n            return true;\n        }\n        LOG(ERROR) << \"getDiskStats succeeded but size is 0\";\n        return false;\n    }\n    if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {\n        LOG(DEBUG) << \"getDiskStats is not supported on health HAL.\";\n        return false;\n    }\n    LOG(ERROR) << \"getDiskStats failed with \" << ret.getDescription();\n    return false;\n}\n\nstruct disk_perf get_disk_perf(struct disk_stats* stats)\n{\n    struct disk_perf perf = {};\n\n    if (stats->io_ticks) {\n        if (stats->read_ticks) {\n            unsigned long long divisor = stats->read_ticks * stats->io_ticks;\n            perf.read_perf = ((unsigned long long)SECTOR_SIZE *\n                              stats->read_sectors * stats->io_in_queue +\n                              (divisor >> 1)) / divisor;\n            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *\n                             stats->read_ios * stats->io_in_queue +\n                             (divisor >> 1)) / divisor;\n        }\n        if (stats->write_ticks) {\n            unsigned long long divisor = stats->write_ticks * stats->io_ticks;\n            perf.write_perf = ((unsigned long long)SECTOR_SIZE *\n                               stats->write_sectors * stats->io_in_queue +\n                               (divisor >> 1)) / divisor;\n            perf.write_ios = ((unsigned long long)SEC_TO_MSEC *\n                              stats->write_ios * stats->io_in_queue +\n                              (divisor >> 1)) / divisor;\n        }\n        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /\n                     stats->io_ticks;\n    }\n    return perf;\n}\n\nvoid get_inc_disk_stats(const struct disk_stats* prev, const struct disk_stats* curr,\n                        struct disk_stats* inc)\n{\n    *inc = *curr - *prev;\n    inc->start_time = prev->end_time;\n    inc->end_time = curr->end_time;\n    inc->io_avg = curr->io_avg;\n    inc->counter = 1;\n}\n\n// Add src to dst\nvoid add_disk_stats(struct disk_stats* src, struct disk_stats* dst)\n{\n    if (dst->end_time != 0 && dst->end_time != src->start_time) {\n        LOG(WARNING) << \"Two dis-continuous periods of diskstats\"\n                     << \" are added. dst end with \" << dst->end_time << \", src start with \"\n                     << src->start_time;\n    }\n\n    *dst += *src;\n\n    dst->io_in_flight = src->io_in_flight;\n    if (dst->counter + src->counter) {\n        dst->io_avg =\n            ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /\n            (dst->counter + src->counter);\n    }\n    dst->counter += src->counter;\n    dst->end_time = src->end_time;\n    if (dst->start_time == 0) {\n        dst->start_time = src->start_time;\n    }\n}\n\n/* disk_stats_monitor */\nvoid disk_stats_monitor::update_mean()\n{\n    CHECK(mValid);\n    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();\n    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();\n    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();\n    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();\n    mMean.queue = (uint32_t)mStats.queue.get_mean();\n}\n\nvoid disk_stats_monitor::update_std()\n{\n    CHECK(mValid);\n    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();\n    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();\n    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();\n    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();\n    mStd.queue = (uint32_t)mStats.queue.get_std();\n}\n\nvoid disk_stats_monitor::add(struct disk_perf* perf)\n{\n    mStats.read_perf.add(perf->read_perf);\n    mStats.read_ios.add(perf->read_ios);\n    mStats.write_perf.add(perf->write_perf);\n    mStats.write_ios.add(perf->write_ios);\n    mStats.queue.add(perf->queue);\n}\n\nvoid disk_stats_monitor::evict(struct disk_perf* perf) {\n    mStats.read_perf.evict(perf->read_perf);\n    mStats.read_ios.evict(perf->read_ios);\n    mStats.write_perf.evict(perf->write_perf);\n    mStats.write_ios.evict(perf->write_ios);\n    mStats.queue.evict(perf->queue);\n}\n\nbool disk_stats_monitor::detect(struct disk_perf* perf)\n{\n    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&\n        ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&\n        ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);\n}\n\nvoid disk_stats_monitor::update(struct disk_stats* curr)\n{\n    disk_stats inc;\n    get_inc_disk_stats(&mPrevious, curr, &inc);\n    add_disk_stats(&inc, &mAccumulate_pub);\n\n    struct disk_perf perf = get_disk_perf(&inc);\n    log_debug_disk_perf(&perf, \"regular\");\n\n    add(&perf);\n    mBuffer.push(perf);\n    if (mBuffer.size() > mWindow) {\n        evict(&mBuffer.front());\n        mBuffer.pop();\n        mValid = true;\n    }\n\n    // Update internal data structures\n    if (LIKELY(mValid)) {\n        CHECK_EQ(mBuffer.size(), mWindow);\n        update_mean();\n        update_std();\n        if (UNLIKELY(detect(&perf))) {\n            mStall = true;\n            add_disk_stats(&inc, &mAccumulate);\n            log_debug_disk_perf(&mMean, \"stalled_mean\");\n            log_debug_disk_perf(&mStd, \"stalled_std\");\n        } else {\n            if (mStall) {\n                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);\n                log_debug_disk_perf(&acc_perf, \"stalled\");\n                log_event_disk_stats(&mAccumulate, \"stalled\");\n                mStall = false;\n                memset(&mAccumulate, 0, sizeof(mAccumulate));\n            }\n        }\n    }\n\n    mPrevious = *curr;\n}\n\nvoid disk_stats_monitor::update() {\n    disk_stats curr;\n    if (mHealth != nullptr) {\n        if (!get_disk_stats_from_health_hal(mHealth, &curr)) {\n            return;\n        }\n    } else {\n        if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {\n            return;\n        }\n    }\n\n    update(&curr);\n}\n\nvoid disk_stats_monitor::publish(void)\n{\n    struct disk_perf perf = get_disk_perf(&mAccumulate_pub);\n    log_debug_disk_perf(&perf, \"regular\");\n    log_event_disk_stats(&mAccumulate_pub, \"regular\");\n    // Reset global structures\n    memset(&mAccumulate_pub, 0, sizeof(struct disk_stats));\n}\n"
  },
  {
    "path": "storaged/storaged_info.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"storaged\"\n\n#include <stdio.h>\n#include <string.h>\n#include <sys/statvfs.h>\n\n#include <numeric>\n\n#include <android-base/file.h>\n#include <android-base/parseint.h>\n#include <android-base/logging.h>\n#include <android-base/strings.h>\n#include <log/log_event_list.h>\n\n#include \"storaged.h\"\n#include \"storaged_info.h\"\n\nusing namespace std;\nusing namespace chrono;\nusing namespace android::base;\nusing namespace storaged_proto;\n\nusing aidl::android::hardware::health::IHealth;\nusing aidl::android::hardware::health::StorageInfo;\n\nconst string emmc_info_t::emmc_sysfs = \"/sys/bus/mmc/devices/mmc0:0001/\";\nconst char* emmc_info_t::emmc_ver_str[9] = {\n    \"4.0\", \"4.1\", \"4.2\", \"4.3\", \"Obsolete\", \"4.41\", \"4.5\", \"5.0\", \"5.1\"\n};\n\nconst string ufs_info_t::health_file = \"/sys/devices/soc/624000.ufshc/health\";\n\nnamespace {\n\nbool FileExists(const std::string& filename)\n{\n  struct stat buffer;\n  return stat(filename.c_str(), &buffer) == 0;\n}\n\n} // namespace\n\nstorage_info_t* storage_info_t::get_storage_info(const shared_ptr<IHealth>& healthService) {\n    if (healthService != nullptr) {\n        return new health_storage_info_t(healthService);\n    }\n    if (FileExists(emmc_info_t::emmc_sysfs)) return new emmc_info_t;\n\n    if (FileExists(ufs_info_t::health_file)) {\n        return new ufs_info_t;\n    }\n    return new storage_info_t;\n}\n\nvoid storage_info_t::load_perf_history_proto(const IOPerfHistory& perf_history)\n{\n    Mutex::Autolock _l(si_mutex);\n\n    if (!perf_history.has_day_start_sec() ||\n        perf_history.daily_perf_size() > (int)daily_perf.size() ||\n        perf_history.weekly_perf_size() > (int)weekly_perf.size()) {\n        LOG(ERROR) << \"Invalid IOPerfHistory proto\";\n        return;\n    }\n\n    day_start_tp = {};\n    day_start_tp += chrono::seconds(perf_history.day_start_sec());\n\n    nr_samples = perf_history.nr_samples();\n    if (nr_samples < recent_perf.size()) {\n        recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());\n    }\n    size_t i = 0;\n    for (auto bw : perf_history.recent_perf()) {\n        if (i < recent_perf.size()) {\n            recent_perf[i] = bw;\n        } else {\n            recent_perf.push_back(bw);\n        }\n        ++i;\n    }\n\n    nr_days = perf_history.nr_days();\n    i = 0;\n    for (auto bw : perf_history.daily_perf()) {\n        daily_perf[i++] = bw;\n    }\n\n    nr_weeks = perf_history.nr_weeks();\n    i = 0;\n    for (auto bw : perf_history.weekly_perf()) {\n        weekly_perf[i++] = bw;\n    }\n}\n\nvoid storage_info_t::refresh(IOPerfHistory* perf_history)\n{\n    struct statvfs buf;\n    if (statvfs(userdata_path.c_str(), &buf) != 0) {\n        PLOG(WARNING) << \"Failed to get userdata info\";\n        return;\n    }\n\n    userdata_total_kb = buf.f_bsize * buf.f_blocks >> 10;\n    userdata_free_kb = buf.f_bfree * buf.f_blocks >> 10;\n\n    Mutex::Autolock _l(si_mutex);\n\n    perf_history->Clear();\n    perf_history->set_day_start_sec(\n        duration_cast<chrono::seconds>(day_start_tp.time_since_epoch()).count());\n    for (const uint32_t& bw : recent_perf) {\n        perf_history->add_recent_perf(bw);\n    }\n    perf_history->set_nr_samples(nr_samples);\n    for (const uint32_t& bw : daily_perf) {\n        perf_history->add_daily_perf(bw);\n    }\n    perf_history->set_nr_days(nr_days);\n    for (const uint32_t& bw : weekly_perf) {\n        perf_history->add_weekly_perf(bw);\n    }\n    perf_history->set_nr_weeks(nr_weeks);\n}\n\nvoid storage_info_t::publish()\n{\n    android_log_event_list(EVENTLOGTAG_EMMCINFO)\n        << version << eol << lifetime_a << lifetime_b\n        << LOG_ID_EVENTS;\n}\n\nvoid storage_info_t::update_perf_history(uint32_t bw,\n                                         const time_point<system_clock>& tp)\n{\n    Mutex::Autolock _l(si_mutex);\n\n    if (tp > day_start_tp &&\n        duration_cast<chrono::seconds>(tp - day_start_tp).count() < DAY_TO_SEC) {\n        if (nr_samples >= recent_perf.size()) {\n            recent_perf.push_back(bw);\n        } else {\n            recent_perf[nr_samples] = bw;\n        }\n        nr_samples++;\n        return;\n    }\n\n    if (nr_samples < recent_perf.size()) {\n        recent_perf.erase(recent_perf.begin() + nr_samples, recent_perf.end());\n    }\n\n    uint32_t daily_avg_bw = 0;\n    if (!recent_perf.empty()) {\n        daily_avg_bw = accumulate(recent_perf.begin(), recent_perf.end(), 0) / recent_perf.size();\n    }\n\n    day_start_tp = tp - chrono::seconds(duration_cast<chrono::seconds>(\n        tp.time_since_epoch()).count() % DAY_TO_SEC);\n\n    nr_samples = 0;\n    if (recent_perf.empty())\n        recent_perf.resize(1);\n    recent_perf[nr_samples++] = bw;\n\n    if (nr_days < WEEK_TO_DAYS) {\n        daily_perf[nr_days++] = daily_avg_bw;\n        return;\n    }\n\n    DCHECK(nr_days > 0);\n    uint32_t week_avg_bw = accumulate(daily_perf.begin(),\n        daily_perf.begin() + nr_days, 0) / nr_days;\n\n    nr_days = 0;\n    daily_perf[nr_days++] = daily_avg_bw;\n\n    if (nr_weeks >= YEAR_TO_WEEKS) {\n        nr_weeks = 0;\n    }\n    weekly_perf[nr_weeks++] = week_avg_bw;\n}\n\nvector<int> storage_info_t::get_perf_history()\n{\n    Mutex::Autolock _l(si_mutex);\n\n    vector<int> ret(3 + recent_perf.size() + daily_perf.size() + weekly_perf.size());\n\n    ret[0] = recent_perf.size();\n    ret[1] = daily_perf.size();\n    ret[2] = weekly_perf.size();\n\n    int start = 3;\n    for (size_t i = 0; i < recent_perf.size(); i++) {\n        int idx = (recent_perf.size() + nr_samples - 1 - i) % recent_perf.size();\n        ret[start + i] = recent_perf[idx];\n    }\n\n    start += recent_perf.size();\n    for (size_t i = 0; i < daily_perf.size(); i++) {\n        int idx = (daily_perf.size() + nr_days - 1 - i) % daily_perf.size();\n        ret[start + i] = daily_perf[idx];\n    }\n\n    start += daily_perf.size();\n    for (size_t i = 0; i < weekly_perf.size(); i++) {\n        int idx = (weekly_perf.size() + nr_weeks - 1 - i) % weekly_perf.size();\n        ret[start + i] = weekly_perf[idx];\n    }\n\n    return ret;\n}\n\nuint32_t storage_info_t::get_recent_perf() {\n    Mutex::Autolock _l(si_mutex);\n    if (recent_perf.size() == 0) return 0;\n    return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /\n           recent_perf.size();\n}\n\nvoid emmc_info_t::report()\n{\n    if (!report_sysfs()) return;\n\n    publish();\n}\n\nbool emmc_info_t::report_sysfs()\n{\n    string buffer;\n    uint16_t rev = 0;\n\n    if (!ReadFileToString(emmc_sysfs + \"rev\", &buffer)) {\n        return false;\n    }\n\n    if (sscanf(buffer.c_str(), \"0x%hx\", &rev) < 1 ||\n        rev < 7 || rev > ARRAY_SIZE(emmc_ver_str)) {\n        return false;\n    }\n\n    version = \"emmc \";\n    version += emmc_ver_str[rev];\n\n    if (!ReadFileToString(emmc_sysfs + \"pre_eol_info\", &buffer)) {\n        return false;\n    }\n\n    if (sscanf(buffer.c_str(), \"%hx\", &eol) < 1 || eol == 0) {\n        return false;\n    }\n\n    if (!ReadFileToString(emmc_sysfs + \"life_time\", &buffer)) {\n        return false;\n    }\n\n    if (sscanf(buffer.c_str(), \"0x%hx 0x%hx\", &lifetime_a, &lifetime_b) < 2 ||\n        (lifetime_a == 0 && lifetime_b == 0)) {\n        return false;\n    }\n\n    return true;\n}\n\nvoid ufs_info_t::report()\n{\n    string buffer;\n    if (!ReadFileToString(health_file, &buffer)) {\n        return;\n    }\n\n    vector<string> lines = Split(buffer, \"\\n\");\n    if (lines.empty()) {\n        return;\n    }\n\n    char rev[8];\n    if (sscanf(lines[0].c_str(), \"ufs version: 0x%7s\\n\", rev) < 1) {\n        return;\n    }\n\n    version = \"ufs \" + string(rev);\n\n    for (size_t i = 1; i < lines.size(); i++) {\n        char token[32];\n        uint16_t val;\n        int ret;\n        if ((ret = sscanf(lines[i].c_str(),\n                   \"Health Descriptor[Byte offset 0x%*d]: %31s = 0x%hx\",\n                   token, &val)) < 2) {\n            continue;\n        }\n\n        if (string(token) == \"bPreEOLInfo\") {\n            eol = val;\n        } else if (string(token) == \"bDeviceLifeTimeEstA\") {\n            lifetime_a = val;\n        } else if (string(token) == \"bDeviceLifeTimeEstB\") {\n            lifetime_b = val;\n        }\n    }\n\n    if (eol == 0 || (lifetime_a == 0 && lifetime_b == 0)) {\n        return;\n    }\n\n    publish();\n}\n\nvoid health_storage_info_t::report() {\n    vector<StorageInfo> halInfos;\n    auto ret = mHealth->getStorageInfo(&halInfos);\n    if (ret.isOk()) {\n        if (halInfos.size() != 0) {\n            set_values_from_hal_storage_info(halInfos[0]);\n            publish();\n            return;\n        }\n        LOG(ERROR) << \"getStorageInfo succeeded but size is 0\";\n        return;\n    }\n    if (ret.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {\n        LOG(DEBUG) << \"getStorageInfo is not supported on health HAL.\";\n        return;\n    }\n    LOG(ERROR) << \"getStorageInfo failed with \" << ret.getDescription();\n}\n\nvoid health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {\n    eol = halInfo.eol;\n    lifetime_a = halInfo.lifetimeA;\n    lifetime_b = halInfo.lifetimeB;\n    version = halInfo.version;\n}\n"
  },
  {
    "path": "storaged/storaged_service.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <inttypes.h>\n#include <stdint.h>\n\n#include <vector>\n\n#include <android-base/parseint.h>\n#include <android-base/parsedouble.h>\n#include <binder/IBinder.h>\n#include <binder/IInterface.h>\n\n#include <binder/IPCThreadState.h>\n#include <binder/IServiceManager.h>\n#include <binder/PermissionCache.h>\n#include <private/android_filesystem_config.h>\n\n#include <storaged.h>\n#include <storaged_utils.h>\n#include <storaged_service.h>\n\nusing namespace std;\nusing namespace android::base;\n\nextern sp<storaged_t> storaged_sp;\n\nnamespace android {\nstatus_t StoragedService::start() {\n    return BinderService<StoragedService>::publish();\n}\n\nvoid StoragedService::dumpUidRecords(int fd, const vector<uid_record>& entries) {\n    map<string, io_usage> merged_entries = merge_io_usage(entries);\n    for (const auto& rec : merged_entries) {\n        dprintf(fd, \"%s %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64\n                \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \"\\n\",\n                rec.first.c_str(),\n                rec.second.bytes[READ][FOREGROUND][CHARGER_OFF],\n                rec.second.bytes[WRITE][FOREGROUND][CHARGER_OFF],\n                rec.second.bytes[READ][BACKGROUND][CHARGER_OFF],\n                rec.second.bytes[WRITE][BACKGROUND][CHARGER_OFF],\n                rec.second.bytes[READ][FOREGROUND][CHARGER_ON],\n                rec.second.bytes[WRITE][FOREGROUND][CHARGER_ON],\n                rec.second.bytes[READ][BACKGROUND][CHARGER_ON],\n                rec.second.bytes[WRITE][BACKGROUND][CHARGER_ON]);\n    }\n}\n\nvoid StoragedService::dumpUidRecordsDebug(int fd, const vector<uid_record>& entries) {\n    for (const auto& record : entries) {\n        const io_usage& uid_usage = record.ios.uid_ios;\n        dprintf(fd, \"%s_%d %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64\n                \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \"\\n\",\n                record.name.c_str(), record.ios.user_id,\n                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF],\n                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],\n                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF],\n                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],\n                uid_usage.bytes[READ][FOREGROUND][CHARGER_ON],\n                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],\n                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON],\n                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);\n\n        for (const auto& task_it : record.ios.task_ios) {\n            const io_usage& task_usage = task_it.second;\n            const string& comm = task_it.first;\n            dprintf(fd, \"-> %s %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64\n                    \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \"\\n\",\n                    comm.c_str(),\n                    task_usage.bytes[READ][FOREGROUND][CHARGER_OFF],\n                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF],\n                    task_usage.bytes[READ][BACKGROUND][CHARGER_OFF],\n                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF],\n                    task_usage.bytes[READ][FOREGROUND][CHARGER_ON],\n                    task_usage.bytes[WRITE][FOREGROUND][CHARGER_ON],\n                    task_usage.bytes[READ][BACKGROUND][CHARGER_ON],\n                    task_usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);\n        }\n    }\n}\n\nstatus_t StoragedService::dump(int fd, const Vector<String16>& args) {\n    IPCThreadState* self = IPCThreadState::self();\n    const int pid = self->getCallingPid();\n    const int uid = self->getCallingUid();\n    if ((uid != AID_SHELL) &&\n        !PermissionCache::checkPermission(\n                String16(\"android.permission.DUMP\"), pid, uid)) {\n        return PERMISSION_DENIED;\n    }\n\n    double hours = 0;\n    int time_window = 0;\n    uint64_t threshold = 0;\n    bool force_report = false;\n    bool debug = false;\n    for (size_t i = 0; i < args.size(); i++) {\n        const auto& arg = args[i];\n        if (arg == String16(\"--hours\")) {\n            if (++i >= args.size())\n                break;\n            if(!ParseDouble(String8(args[i]).c_str(), &hours))\n                return BAD_VALUE;\n            continue;\n        }\n        if (arg == String16(\"--time_window\")) {\n            if (++i >= args.size())\n                break;\n            if(!ParseInt(String8(args[i]).c_str(), &time_window))\n                return BAD_VALUE;\n            continue;\n        }\n        if (arg == String16(\"--threshold\")) {\n            if (++i >= args.size())\n                break;\n            if(!ParseUint(String8(args[i]).c_str(), &threshold))\n                return BAD_VALUE;\n            continue;\n        }\n        if (arg == String16(\"--force\")) {\n            force_report = true;\n            continue;\n        }\n        if (arg == String16(\"--debug\")) {\n            debug = true;\n            continue;\n        }\n    }\n\n    uint64_t last_ts = 0;\n    map<uint64_t, struct uid_records> records =\n                storaged_sp->get_uid_records(hours, threshold, force_report);\n    for (const auto& it : records) {\n        if (last_ts != it.second.start_ts) {\n            dprintf(fd, \"%\" PRIu64, it.second.start_ts);\n        }\n        dprintf(fd, \",%\" PRIu64 \"\\n\", it.first);\n        last_ts = it.first;\n\n        if (!debug) {\n            dumpUidRecords(fd, it.second.entries);\n        } else {\n            dumpUidRecordsDebug(fd, it.second.entries);\n        }\n    }\n\n    if (time_window) {\n        storaged_sp->update_uid_io_interval(time_window);\n    }\n\n    return OK;\n}\n\nbinder::Status StoragedService::onUserStarted(int32_t userId) {\n    storaged_sp->add_user_ce(userId);\n    return binder::Status::ok();\n}\n\nbinder::Status StoragedService::onUserStopped(int32_t userId) {\n    storaged_sp->remove_user_ce(userId);\n    return binder::Status::ok();\n}\n\nbinder::Status StoragedService::getRecentPerf(int32_t* _aidl_return) {\n    uint32_t recent_perf = storaged_sp->get_recent_perf();\n    if (recent_perf > INT32_MAX) {\n        *_aidl_return = INT32_MAX;\n    } else {\n        *_aidl_return = static_cast<int32_t>(recent_perf);\n    }\n    return binder::Status::ok();\n}\n\nstatus_t StoragedPrivateService::start() {\n    return BinderService<StoragedPrivateService>::publish();\n}\n\nbinder::Status StoragedPrivateService::dumpUids(\n        vector<::android::os::storaged::UidInfo>* _aidl_return) {\n    unordered_map<uint32_t, uid_info> uids_m = storaged_sp->get_uids();\n\n    for (const auto& it : uids_m) {\n        UidInfo uinfo;\n        uinfo.uid = it.second.uid;\n        uinfo.name = it.second.name;\n        uinfo.tasks = it.second.tasks;\n        memcpy(&uinfo.io, &it.second.io, sizeof(uinfo.io));\n        _aidl_return->push_back(uinfo);\n    }\n    return binder::Status::ok();\n}\n\nbinder::Status StoragedPrivateService::dumpPerfHistory(\n        vector<int32_t>* _aidl_return) {\n    *_aidl_return = storaged_sp->get_perf_history();\n    return binder::Status::ok();\n}\n\nsp<IStoragedPrivate> get_storaged_pri_service() {\n    sp<IServiceManager> sm = defaultServiceManager();\n    if (sm == NULL) return NULL;\n\n    sp<IBinder> binder = sm->getService(String16(\"storaged_pri\"));\n    if (binder == NULL) return NULL;\n\n    return interface_cast<IStoragedPrivate>(binder);\n}\n}  // namespace android"
  },
  {
    "path": "storaged/storaged_uid_monitor.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"storaged\"\n\n#include <stdint.h>\n#include <time.h>\n\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n\n#include <android/content/pm/IPackageManagerNative.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/parseint.h>\n#include <android-base/strings.h>\n#include <android-base/stringprintf.h>\n#include <binder/IServiceManager.h>\n#include <log/log_event_list.h>\n\n#include \"storaged.h\"\n#include \"storaged_uid_monitor.h\"\n\nusing namespace android;\nusing namespace android::base;\nusing namespace android::content::pm;\nusing namespace android::os::storaged;\nusing namespace storaged_proto;\n\nnamespace {\n\nbool refresh_uid_names;\nconst char* UID_IO_STATS_PATH = \"/proc/uid_io/stats\";\n\n} // namepsace\n\nstd::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats()\n{\n    Mutex::Autolock _l(uidm_mutex_);\n    return get_uid_io_stats_locked();\n};\n\n/* return true on parse success and false on failure */\nbool uid_info::parse_uid_io_stats(std::string&& s)\n{\n    std::vector<std::string> fields = Split(s, \" \");\n    if (fields.size() < 11 ||\n        !ParseUint(fields[0],  &uid) ||\n        !ParseUint(fields[1],  &io[FOREGROUND].rchar) ||\n        !ParseUint(fields[2],  &io[FOREGROUND].wchar) ||\n        !ParseUint(fields[3],  &io[FOREGROUND].read_bytes) ||\n        !ParseUint(fields[4],  &io[FOREGROUND].write_bytes) ||\n        !ParseUint(fields[5],  &io[BACKGROUND].rchar) ||\n        !ParseUint(fields[6],  &io[BACKGROUND].wchar) ||\n        !ParseUint(fields[7],  &io[BACKGROUND].read_bytes) ||\n        !ParseUint(fields[8],  &io[BACKGROUND].write_bytes) ||\n        !ParseUint(fields[9],  &io[FOREGROUND].fsync) ||\n        !ParseUint(fields[10], &io[BACKGROUND].fsync)) {\n        LOG(WARNING) << \"Invalid uid I/O stats: \\\"\" << s << \"\\\"\";\n        return false;\n    }\n    return true;\n}\n\n/* return true on parse success and false on failure */\nbool task_info::parse_task_io_stats(std::string&& s)\n{\n    std::vector<std::string> fields = Split(s, \",\");\n    size_t size = fields.size();\n    if (size < 13 ||\n        !ParseInt(fields[size - 11],  &pid) ||\n        !ParseUint(fields[size - 10],  &io[FOREGROUND].rchar) ||\n        !ParseUint(fields[size - 9],  &io[FOREGROUND].wchar) ||\n        !ParseUint(fields[size - 8],  &io[FOREGROUND].read_bytes) ||\n        !ParseUint(fields[size - 7],  &io[FOREGROUND].write_bytes) ||\n        !ParseUint(fields[size - 6],  &io[BACKGROUND].rchar) ||\n        !ParseUint(fields[size - 5],  &io[BACKGROUND].wchar) ||\n        !ParseUint(fields[size - 4],  &io[BACKGROUND].read_bytes) ||\n        !ParseUint(fields[size - 3], &io[BACKGROUND].write_bytes) ||\n        !ParseUint(fields[size - 2], &io[FOREGROUND].fsync) ||\n        !ParseUint(fields[size - 1], &io[BACKGROUND].fsync)) {\n        LOG(WARNING) << \"Invalid task I/O stats: \\\"\" << s << \"\\\"\";\n        return false;\n    }\n    comm = Join(std::vector<std::string>(\n                fields.begin() + 1, fields.end() - 11), ',');\n    return true;\n}\n\nbool io_usage::is_zero() const\n{\n    for (int i = 0; i < IO_TYPES; i++) {\n        for (int j = 0; j < UID_STATS; j++) {\n            for (int k = 0; k < CHARGER_STATS; k++) {\n                if (bytes[i][j][k])\n                    return false;\n            }\n        }\n    }\n    return true;\n}\n\nnamespace {\n\nvoid get_uid_names(const vector<int>& uids, const vector<std::string*>& uid_names)\n{\n    sp<IServiceManager> sm = defaultServiceManager();\n    if (sm == NULL) {\n        LOG(ERROR) << \"defaultServiceManager failed\";\n        return;\n    }\n\n    sp<IBinder> binder = sm->getService(String16(\"package_native\"));\n    if (binder == NULL) {\n        LOG(ERROR) << \"getService package_native failed\";\n        return;\n    }\n\n    sp<IPackageManagerNative> package_mgr = interface_cast<IPackageManagerNative>(binder);\n    std::vector<std::string> names;\n    binder::Status status = package_mgr->getNamesForUids(uids, &names);\n    if (!status.isOk()) {\n        LOG(ERROR) << \"package_native::getNamesForUids failed: \" << status.exceptionMessage();\n        return;\n    }\n\n    for (uint32_t i = 0; i < uid_names.size(); i++) {\n        if (!names[i].empty()) {\n            *uid_names[i] = names[i];\n        }\n    }\n\n    refresh_uid_names = false;\n}\n\n} // namespace\n\nstd::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats_locked()\n{\n    std::unordered_map<uint32_t, uid_info> uid_io_stats;\n    std::string buffer;\n    if (!ReadFileToString(UID_IO_STATS_PATH, &buffer)) {\n        PLOG(ERROR) << UID_IO_STATS_PATH << \": ReadFileToString failed\";\n        return uid_io_stats;\n    }\n\n    std::vector<std::string> io_stats = Split(std::move(buffer), \"\\n\");\n    uid_info u;\n    vector<int> uids;\n    vector<std::string*> uid_names;\n\n    for (uint32_t i = 0; i < io_stats.size(); i++) {\n        if (io_stats[i].empty()) {\n            continue;\n        }\n\n        if (io_stats[i].compare(0, 4, \"task\")) {\n            if (!u.parse_uid_io_stats(std::move(io_stats[i])))\n                continue;\n            uid_io_stats[u.uid] = u;\n            uid_io_stats[u.uid].name = std::to_string(u.uid);\n            uids.push_back(u.uid);\n            uid_names.push_back(&uid_io_stats[u.uid].name);\n            if (last_uid_io_stats_.find(u.uid) == last_uid_io_stats_.end()) {\n                refresh_uid_names = true;\n            } else {\n                uid_io_stats[u.uid].name = last_uid_io_stats_[u.uid].name;\n            }\n        } else {\n            task_info t;\n            if (!t.parse_task_io_stats(std::move(io_stats[i])))\n                continue;\n            uid_io_stats[u.uid].tasks[t.pid] = t;\n        }\n    }\n\n    if (!uids.empty() && refresh_uid_names) {\n        get_uid_names(uids, uid_names);\n    }\n\n    return uid_io_stats;\n}\n\nnamespace {\n\ninline size_t history_size(\n    const std::map<uint64_t, struct uid_records>& history)\n{\n    size_t count = 0;\n    for (auto const& it : history) {\n        count += it.second.entries.size();\n    }\n    return count;\n}\n\n} // namespace\n\nvoid uid_monitor::add_records_locked(uint64_t curr_ts)\n{\n    // remove records more than 5 days old\n    if (curr_ts > 5 * DAY_TO_SEC) {\n        auto it = io_history_.lower_bound(curr_ts - 5 * DAY_TO_SEC);\n        io_history_.erase(io_history_.begin(), it);\n    }\n\n    struct uid_records new_records;\n    for (const auto& p : curr_io_stats_) {\n        struct uid_record record = {};\n        record.name = p.first;\n        if (!p.second.uid_ios.is_zero()) {\n            record.ios.user_id = p.second.user_id;\n            record.ios.uid_ios = p.second.uid_ios;\n            for (const auto& p_task : p.second.task_ios) {\n                if (!p_task.second.is_zero())\n                    record.ios.task_ios[p_task.first] = p_task.second;\n            }\n            new_records.entries.push_back(record);\n        }\n    }\n\n    curr_io_stats_.clear();\n    new_records.start_ts = start_ts_;\n    start_ts_ = curr_ts;\n\n    if (new_records.entries.empty())\n      return;\n\n    // make some room for new records\n    maybe_shrink_history_for_items(new_records.entries.size());\n\n    io_history_[curr_ts] = new_records;\n}\n\nvoid uid_monitor::maybe_shrink_history_for_items(size_t nitems) {\n    ssize_t overflow = history_size(io_history_) + nitems - MAX_UID_RECORDS_SIZE;\n    while (overflow > 0 && io_history_.size() > 0) {\n        auto del_it = io_history_.begin();\n        overflow -= del_it->second.entries.size();\n        io_history_.erase(io_history_.begin());\n    }\n}\n\nstd::map<uint64_t, struct uid_records> uid_monitor::dump(\n    double hours, uint64_t threshold, bool force_report)\n{\n    if (force_report) {\n        report(nullptr);\n    }\n\n    Mutex::Autolock _l(uidm_mutex_);\n\n    std::map<uint64_t, struct uid_records> dump_records;\n    uint64_t first_ts = 0;\n\n    if (hours != 0) {\n        first_ts = time(NULL) - hours * HOUR_TO_SEC;\n    }\n\n    for (auto it = io_history_.lower_bound(first_ts); it != io_history_.end(); ++it) {\n        const std::vector<struct uid_record>& recs = it->second.entries;\n        struct uid_records filtered;\n\n        for (const auto& rec : recs) {\n            const io_usage& uid_usage = rec.ios.uid_ios;\n            if (uid_usage.bytes[READ][FOREGROUND][CHARGER_ON] +\n                uid_usage.bytes[READ][FOREGROUND][CHARGER_OFF] +\n                uid_usage.bytes[READ][BACKGROUND][CHARGER_ON] +\n                uid_usage.bytes[READ][BACKGROUND][CHARGER_OFF] +\n                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_ON] +\n                uid_usage.bytes[WRITE][FOREGROUND][CHARGER_OFF] +\n                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_ON] +\n                uid_usage.bytes[WRITE][BACKGROUND][CHARGER_OFF] > threshold) {\n                filtered.entries.push_back(rec);\n            }\n        }\n\n        if (filtered.entries.empty())\n            continue;\n\n        filtered.start_ts = it->second.start_ts;\n        dump_records.insert(\n            std::pair<uint64_t, struct uid_records>(it->first, filtered));\n    }\n\n    return dump_records;\n}\n\nvoid uid_monitor::update_curr_io_stats_locked()\n{\n    std::unordered_map<uint32_t, uid_info> uid_io_stats =\n        get_uid_io_stats_locked();\n    if (uid_io_stats.empty()) {\n        return;\n    }\n\n    for (const auto& it : uid_io_stats) {\n        const uid_info& uid = it.second;\n        if (curr_io_stats_.find(uid.name) == curr_io_stats_.end()) {\n            curr_io_stats_[uid.name] = {};\n        }\n\n        struct uid_io_usage& usage = curr_io_stats_[uid.name];\n        usage.user_id = multiuser_get_user_id(uid.uid);\n\n        int64_t fg_rd_delta = uid.io[FOREGROUND].read_bytes -\n            last_uid_io_stats_[uid.uid].io[FOREGROUND].read_bytes;\n        int64_t bg_rd_delta = uid.io[BACKGROUND].read_bytes -\n            last_uid_io_stats_[uid.uid].io[BACKGROUND].read_bytes;\n        int64_t fg_wr_delta = uid.io[FOREGROUND].write_bytes -\n            last_uid_io_stats_[uid.uid].io[FOREGROUND].write_bytes;\n        int64_t bg_wr_delta = uid.io[BACKGROUND].write_bytes -\n            last_uid_io_stats_[uid.uid].io[BACKGROUND].write_bytes;\n\n        usage.uid_ios.bytes[READ][FOREGROUND][charger_stat_] +=\n            (fg_rd_delta < 0) ? 0 : fg_rd_delta;\n        usage.uid_ios.bytes[READ][BACKGROUND][charger_stat_] +=\n            (bg_rd_delta < 0) ? 0 : bg_rd_delta;\n        usage.uid_ios.bytes[WRITE][FOREGROUND][charger_stat_] +=\n            (fg_wr_delta < 0) ? 0 : fg_wr_delta;\n        usage.uid_ios.bytes[WRITE][BACKGROUND][charger_stat_] +=\n            (bg_wr_delta < 0) ? 0 : bg_wr_delta;\n\n        for (const auto& task_it : uid.tasks) {\n            const task_info& task = task_it.second;\n            const pid_t pid = task_it.first;\n            const std::string& comm = task_it.second.comm;\n            int64_t task_fg_rd_delta = task.io[FOREGROUND].read_bytes -\n                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].read_bytes;\n            int64_t task_bg_rd_delta = task.io[BACKGROUND].read_bytes -\n                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].read_bytes;\n            int64_t task_fg_wr_delta = task.io[FOREGROUND].write_bytes -\n                last_uid_io_stats_[uid.uid].tasks[pid].io[FOREGROUND].write_bytes;\n            int64_t task_bg_wr_delta = task.io[BACKGROUND].write_bytes -\n                last_uid_io_stats_[uid.uid].tasks[pid].io[BACKGROUND].write_bytes;\n\n            io_usage& task_usage = usage.task_ios[comm];\n            task_usage.bytes[READ][FOREGROUND][charger_stat_] +=\n                (task_fg_rd_delta < 0) ? 0 : task_fg_rd_delta;\n            task_usage.bytes[READ][BACKGROUND][charger_stat_] +=\n                (task_bg_rd_delta < 0) ? 0 : task_bg_rd_delta;\n            task_usage.bytes[WRITE][FOREGROUND][charger_stat_] +=\n                (task_fg_wr_delta < 0) ? 0 : task_fg_wr_delta;\n            task_usage.bytes[WRITE][BACKGROUND][charger_stat_] +=\n                (task_bg_wr_delta < 0) ? 0 : task_bg_wr_delta;\n        }\n    }\n\n    last_uid_io_stats_ = uid_io_stats;\n}\n\nvoid uid_monitor::report(unordered_map<int, StoragedProto>* protos)\n{\n    if (!enabled()) return;\n\n    Mutex::Autolock _l(uidm_mutex_);\n\n    update_curr_io_stats_locked();\n    add_records_locked(time(NULL));\n\n    if (protos) {\n        update_uid_io_proto(protos);\n    }\n}\n\nnamespace {\n\nvoid set_io_usage_proto(IOUsage* usage_proto, const io_usage& usage)\n{\n    usage_proto->set_rd_fg_chg_on(usage.bytes[READ][FOREGROUND][CHARGER_ON]);\n    usage_proto->set_rd_fg_chg_off(usage.bytes[READ][FOREGROUND][CHARGER_OFF]);\n    usage_proto->set_rd_bg_chg_on(usage.bytes[READ][BACKGROUND][CHARGER_ON]);\n    usage_proto->set_rd_bg_chg_off(usage.bytes[READ][BACKGROUND][CHARGER_OFF]);\n    usage_proto->set_wr_fg_chg_on(usage.bytes[WRITE][FOREGROUND][CHARGER_ON]);\n    usage_proto->set_wr_fg_chg_off(usage.bytes[WRITE][FOREGROUND][CHARGER_OFF]);\n    usage_proto->set_wr_bg_chg_on(usage.bytes[WRITE][BACKGROUND][CHARGER_ON]);\n    usage_proto->set_wr_bg_chg_off(usage.bytes[WRITE][BACKGROUND][CHARGER_OFF]);\n}\n\nvoid get_io_usage_proto(io_usage* usage, const IOUsage& io_proto)\n{\n    usage->bytes[READ][FOREGROUND][CHARGER_ON] = io_proto.rd_fg_chg_on();\n    usage->bytes[READ][FOREGROUND][CHARGER_OFF] = io_proto.rd_fg_chg_off();\n    usage->bytes[READ][BACKGROUND][CHARGER_ON] = io_proto.rd_bg_chg_on();\n    usage->bytes[READ][BACKGROUND][CHARGER_OFF] = io_proto.rd_bg_chg_off();\n    usage->bytes[WRITE][FOREGROUND][CHARGER_ON] = io_proto.wr_fg_chg_on();\n    usage->bytes[WRITE][FOREGROUND][CHARGER_OFF] = io_proto.wr_fg_chg_off();\n    usage->bytes[WRITE][BACKGROUND][CHARGER_ON] = io_proto.wr_bg_chg_on();\n    usage->bytes[WRITE][BACKGROUND][CHARGER_OFF] = io_proto.wr_bg_chg_off();\n}\n\n} // namespace\n\nvoid uid_monitor::update_uid_io_proto(unordered_map<int, StoragedProto>* protos)\n{\n    for (const auto& item : io_history_) {\n        const uint64_t& end_ts = item.first;\n        const struct uid_records& recs = item.second;\n        unordered_map<userid_t, UidIOItem*> user_items;\n\n        for (const auto& entry : recs.entries) {\n            userid_t user_id = entry.ios.user_id;\n            UidIOItem* item_proto = user_items[user_id];\n            if (item_proto == nullptr) {\n                item_proto = (*protos)[user_id].mutable_uid_io_usage()\n                             ->add_uid_io_items();\n                user_items[user_id] = item_proto;\n            }\n            item_proto->set_end_ts(end_ts);\n\n            UidIORecords* recs_proto = item_proto->mutable_records();\n            recs_proto->set_start_ts(recs.start_ts);\n\n            UidRecord* rec_proto = recs_proto->add_entries();\n            rec_proto->set_uid_name(entry.name);\n            rec_proto->set_user_id(user_id);\n\n            IOUsage* uid_io_proto = rec_proto->mutable_uid_io();\n            const io_usage& uio_ios = entry.ios.uid_ios;\n            set_io_usage_proto(uid_io_proto, uio_ios);\n\n            for (const auto& task_io : entry.ios.task_ios) {\n                const std::string& task_name = task_io.first;\n                const io_usage& task_ios = task_io.second;\n\n                TaskIOUsage* task_io_proto = rec_proto->add_task_io();\n                task_io_proto->set_task_name(task_name);\n                set_io_usage_proto(task_io_proto->mutable_ios(), task_ios);\n            }\n        }\n    }\n}\n\nvoid uid_monitor::clear_user_history(userid_t user_id)\n{\n    Mutex::Autolock _l(uidm_mutex_);\n\n    for (auto& item : io_history_) {\n        vector<uid_record>* entries = &item.second.entries;\n        entries->erase(\n            remove_if(entries->begin(), entries->end(),\n                [user_id](const uid_record& rec) {\n                    return rec.ios.user_id == user_id;}),\n            entries->end());\n    }\n\n    for (auto it = io_history_.begin(); it != io_history_.end(); ) {\n        if (it->second.entries.empty()) {\n            it = io_history_.erase(it);\n        } else {\n            it++;\n        }\n    }\n}\n\nvoid uid_monitor::load_uid_io_proto(userid_t user_id, const UidIOUsage& uid_io_proto)\n{\n    if (!enabled()) return;\n\n    Mutex::Autolock _l(uidm_mutex_);\n\n    for (const auto& item_proto : uid_io_proto.uid_io_items()) {\n        const UidIORecords& records_proto = item_proto.records();\n        struct uid_records* recs = &io_history_[item_proto.end_ts()];\n\n        // It's possible that the same uid_io_proto file gets loaded more than\n        // once, for example, if system_server crashes. In this case we avoid\n        // adding duplicate entries, so we build a quick way to check for\n        // duplicates.\n        std::unordered_set<std::string> existing_uids;\n        for (const auto& rec : recs->entries) {\n            if (rec.ios.user_id == user_id) {\n                existing_uids.emplace(rec.name);\n            }\n        }\n\n        recs->start_ts = records_proto.start_ts();\n        for (const auto& rec_proto : records_proto.entries()) {\n            if (existing_uids.find(rec_proto.uid_name()) != existing_uids.end()) {\n                continue;\n            }\n\n            struct uid_record record;\n            record.name = rec_proto.uid_name();\n            record.ios.user_id = rec_proto.user_id();\n            get_io_usage_proto(&record.ios.uid_ios, rec_proto.uid_io());\n\n            for (const auto& task_io_proto : rec_proto.task_io()) {\n                get_io_usage_proto(\n                    &record.ios.task_ios[task_io_proto.task_name()],\n                    task_io_proto.ios());\n            }\n            recs->entries.push_back(record);\n        }\n\n        // We already added items, so this will just cull down to the maximum\n        // length. We do not remove anything if there is only one entry.\n        if (io_history_.size() > 1) {\n            maybe_shrink_history_for_items(0);\n        }\n    }\n}\n\nvoid uid_monitor::set_charger_state(charger_stat_t stat)\n{\n    Mutex::Autolock _l(uidm_mutex_);\n\n    if (charger_stat_ == stat) {\n        return;\n    }\n\n    update_curr_io_stats_locked();\n    charger_stat_ = stat;\n}\n\nvoid uid_monitor::init(charger_stat_t stat)\n{\n    charger_stat_ = stat;\n\n    start_ts_ = time(NULL);\n    last_uid_io_stats_ = get_uid_io_stats();\n}\n\nuid_monitor::uid_monitor()\n    : enabled_(!access(UID_IO_STATS_PATH, R_OK)) {\n}\n"
  },
  {
    "path": "storaged/storaged_utils.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"storaged\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <linux/time.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <time.h>\n#include <unistd.h>\n\n#include <iomanip>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/stringprintf.h>\n#include <android-base/strings.h>\n#include <log/log_event_list.h>\n\n#include <storaged.h>\n#include <storaged_utils.h>\n\nbool cmp_uid_info(const UidInfo& l, const UidInfo& r) {\n    // Compare background I/O first.\n    for (int i = UID_STATS - 1; i >= 0; i--) {\n        uint64_t l_bytes = l.io[i].read_bytes + l.io[i].write_bytes;\n        uint64_t r_bytes = r.io[i].read_bytes + r.io[i].write_bytes;\n        uint64_t l_chars = l.io[i].rchar + l.io[i].wchar;\n        uint64_t r_chars = r.io[i].rchar + r.io[i].wchar;\n\n        if (l_bytes != r_bytes) {\n            return l_bytes > r_bytes;\n        }\n        if (l_chars != r_chars) {\n            return l_chars > r_chars;\n        }\n    }\n\n    return l.name < r.name;\n}\n\nvoid sort_running_uids_info(std::vector<UidInfo> &uids) {\n    std::sort(uids.begin(), uids.end(), cmp_uid_info);\n}\n\n// Logging functions\nvoid log_console_running_uids_info(const std::vector<UidInfo>& uids, bool flag_dump_task) {\n    printf(\"name/uid fg_rchar fg_wchar fg_rbytes fg_wbytes \"\n           \"bg_rchar bg_wchar bg_rbytes bg_wbytes fg_fsync bg_fsync\\n\");\n\n    for (const auto& uid : uids) {\n        printf(\"%s %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64\n                \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \"\\n\",\n            uid.name.c_str(),\n            uid.io[0].rchar, uid.io[0].wchar, uid.io[0].read_bytes, uid.io[0].write_bytes,\n            uid.io[1].rchar, uid.io[1].wchar, uid.io[1].read_bytes, uid.io[1].write_bytes,\n            uid.io[0].fsync, uid.io[1].fsync);\n        if (flag_dump_task) {\n            for (const auto& task_it : uid.tasks) {\n                const task_info& task = task_it.second;\n                printf(\"-> %s %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64\n                        \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \" %\" PRIu64 \"\\n\",\n                    task.comm.c_str(),\n                    task.io[0].rchar, task.io[0].wchar, task.io[0].read_bytes, task.io[0].write_bytes,\n                    task.io[1].rchar, task.io[1].wchar, task.io[1].read_bytes, task.io[1].write_bytes,\n                    task.io[0].fsync, task.io[1].fsync);\n            }\n        }\n    }\n    fflush(stdout);\n}\n\nvoid log_console_perf_history(const vector<int>& perf_history) {\n    if (perf_history.size() < 3 ||\n        perf_history.size() != perf_history[0] +\n                               perf_history[1] +\n                               perf_history[2] + (size_t)3) {\n        return;\n    }\n\n    printf(\"\\nI/O perf history (KB/s) :  most_recent  <---------  least_recent \\n\");\n\n    std::stringstream line;\n    int start = 3;\n    int end = 3 + perf_history[0];\n    std::copy(perf_history.begin() + start, perf_history.begin() + end,\n              std::ostream_iterator<int>(line, \" \"));\n    printf(\"last 24 hours : %s\\n\", line.str().c_str());\n\n    line.str(\"\");\n    start = end;\n    end += perf_history[1];\n    std::copy(perf_history.begin() + start, perf_history.begin() + end,\n              std::ostream_iterator<int>(line, \" \"));\n    printf(\"last 7 days   : %s\\n\", line.str().c_str());\n\n    line.str(\"\");\n    start = end;\n    std::copy(perf_history.begin() + start, perf_history.end(),\n              std::ostream_iterator<int>(line, \" \"));\n    printf(\"last 52 weeks : %s\\n\", line.str().c_str());\n}\n\nmap<string, io_usage> merge_io_usage(const vector<uid_record>& entries) {\n    map<string, io_usage> merged_entries;\n    for (const auto& record : entries) {\n        merged_entries[record.name] += record.ios.uid_ios;\n    }\n    return merged_entries;\n}\n"
  },
  {
    "path": "storaged/tests/fuzzers/storaged_private_service_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fuzzbinder/libbinder_driver.h>\n\n#include <storaged.h>\n#include <storaged_service.h>\n\nsp<storaged_t> storaged_sp;\n\nextern \"C\" int LLVMFuzzerInitialize(int /**argc*/, char /****argv*/) {\n    storaged_sp = new storaged_t();\n    storaged_sp->init();\n    return 0;\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    auto storagedPrivateService = new StoragedPrivateService();\n    fuzzService(storagedPrivateService, FuzzedDataProvider(data, size));\n    return 0;\n}"
  },
  {
    "path": "storaged/tests/fuzzers/storaged_service_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <fuzzbinder/libbinder_driver.h>\n\n#include <storaged.h>\n#include <storaged_service.h>\n\nsp<storaged_t> storaged_sp;\n\nextern \"C\" int LLVMFuzzerInitialize(int /**argc*/, char /****argv*/) {\n    storaged_sp = new storaged_t();\n    storaged_sp->init();\n    return 0;\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    auto storagedService = new StoragedService();\n    fuzzService(storagedService, FuzzedDataProvider(data, size));\n    return 0;\n}"
  },
  {
    "path": "storaged/tests/storaged_test.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <chrono>\n#include <deque>\n#include <fcntl.h>\n#include <random>\n#include <string.h>\n#include <stdio.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <gtest/gtest.h>\n\n#include <aidl/android/hardware/health/IHealth.h>\n#include <healthhalutils/HealthHalUtils.h>\n#include <storaged.h>               // data structures\n#include <storaged_utils.h>         // functions to test\n\n#define MMC_DISK_STATS_PATH \"/sys/block/mmcblk0/stat\"\n#define SDA_DISK_STATS_PATH \"/sys/block/sda/stat\"\n\nusing namespace std;\nusing namespace chrono;\nusing namespace storaged_proto;\n\nnamespace {\n\nvoid write_and_pause(uint32_t sec) {\n    const char* path = \"/cache/test\";\n    int fd = open(path, O_WRONLY | O_CREAT, 0600);\n    ASSERT_LT(-1, fd);\n    char buffer[2048];\n    memset(buffer, 1, sizeof(buffer));\n    int loop_size = 100;\n    for (int i = 0; i < loop_size; ++i) {\n        ASSERT_EQ(2048, write(fd, buffer, sizeof(buffer)));\n    }\n    fsync(fd);\n    close(fd);\n\n    fd = open(path, O_RDONLY);\n    ASSERT_LT(-1, fd);\n    for (int i = 0; i < loop_size; ++i) {\n        ASSERT_EQ(2048, read(fd, buffer, sizeof(buffer)));\n    }\n    close(fd);\n\n    sleep(sec);\n}\n\n} // namespace\n\n// the return values of the tested functions should be the expected ones\nconst char* get_disk_stats_path() {\n    if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {\n        return MMC_DISK_STATS_PATH;\n    } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {\n        return SDA_DISK_STATS_PATH;\n    } else {\n        return nullptr;\n    }\n}\nTEST(storaged_test, retvals) {\n    struct disk_stats stats;\n    memset(&stats, 0, sizeof(struct disk_stats));\n\n    auto disk_stats_path = get_disk_stats_path();\n    if (disk_stats_path == nullptr) GTEST_SKIP();\n\n    EXPECT_TRUE(parse_disk_stats(disk_stats_path, &stats));\n\n    struct disk_stats old_stats;\n    memset(&old_stats, 0, sizeof(struct disk_stats));\n    old_stats = stats;\n\n    const char wrong_path[] = \"/this/is/wrong\";\n    EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));\n\n    // reading a wrong path should not damage the output structure\n    EXPECT_EQ(stats, old_stats);\n}\n\nTEST(storaged_test, disk_stats) {\n    struct disk_stats stats = {};\n    auto disk_stats_path = get_disk_stats_path();\n    if (disk_stats_path == nullptr) GTEST_SKIP();\n    ASSERT_TRUE(parse_disk_stats(disk_stats_path, &stats));\n\n    // every entry of stats (except io_in_flight) should all be greater than 0\n    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {\n        if (i == 8) continue; // skip io_in_flight which can be 0\n        EXPECT_LT((uint64_t)0, *((uint64_t*)&stats + i));\n    }\n\n    // accumulation of the increments should be the same with the overall increment\n    struct disk_stats base = {}, tmp = {}, curr, acc = {}, inc[5];\n    for (uint i = 0; i < 5; ++i) {\n        ASSERT_TRUE(parse_disk_stats(disk_stats_path, &curr));\n        if (i == 0) {\n            base = curr;\n            tmp = curr;\n            sleep(5);\n            continue;\n        }\n        get_inc_disk_stats(&tmp, &curr, &inc[i]);\n        add_disk_stats(&inc[i], &acc);\n        tmp = curr;\n        write_and_pause(5);\n    }\n    struct disk_stats overall_inc = {};\n    get_inc_disk_stats(&base, &curr, &overall_inc);\n\n    EXPECT_EQ(overall_inc, acc);\n}\n\ndouble mean(std::deque<uint32_t> nums) {\n    double sum = 0.0;\n    for (uint32_t i : nums) {\n    sum += i;\n    }\n    return sum / nums.size();\n}\n\ndouble standard_deviation(std::deque<uint32_t> nums) {\n    double sum = 0.0;\n    double avg = mean(nums);\n    for (uint32_t i : nums) {\n    sum += ((double)i - avg) * ((double)i - avg);\n    }\n    return sqrt(sum / nums.size());\n}\n\nTEST(storaged_test, stream_stats) {\n    // 100 random numbers\n    std::vector<uint32_t> data = {8147,9058,1270,9134,6324,975,2785,5469,9575,9649,1576,9706,9572,4854,8003,1419,4218,9157,7922,9595,6557,357,8491,9340,6787,7577,7431,3922,6555,1712,7060,318,2769,462,971,8235,6948,3171,9502,344,4387,3816,7655,7952,1869,4898,4456,6463,7094,7547,2760,6797,6551,1626,1190,4984,9597,3404,5853,2238,7513,2551,5060,6991,8909,9593,5472,1386,1493,2575,8407,2543,8143,2435,9293,3500,1966,2511,6160,4733,3517,8308,5853,5497,9172,2858,7572,7537,3804,5678,759,540,5308,7792,9340,1299,5688,4694,119,3371};\n    std::deque<uint32_t> test_data;\n    stream_stats sstats;\n    for (uint32_t i : data) {\n        test_data.push_back(i);\n        sstats.add(i);\n\n        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());\n        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());\n    }\n\n    for (uint32_t i : data) {\n        test_data.pop_front();\n        sstats.evict(i);\n\n        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());\n        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());\n    }\n\n    // some real data\n    std::vector<uint32_t> another_data = {113875,81620,103145,28327,86855,207414,96526,52567,28553,250311};\n    test_data.clear();\n    uint32_t window_size = 2;\n    uint32_t idx;\n    stream_stats sstats1;\n    for (idx = 0; idx < window_size; ++idx) {\n        test_data.push_back(another_data[idx]);\n        sstats1.add(another_data[idx]);\n    }\n    EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());\n    EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());\n    for (;idx < another_data.size(); ++idx) {\n        test_data.pop_front();\n        sstats1.evict(another_data[idx - window_size]);\n        test_data.push_back(another_data[idx]);\n        sstats1.add(another_data[idx]);\n        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());\n        EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());\n    }\n}\n\nstruct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {\n    struct disk_perf retval;\n    retval.read_perf = (double)perf.read_perf * mul;\n    retval.read_ios = (double)perf.read_ios * mul;\n    retval.write_perf = (double)perf.write_perf * mul;\n    retval.write_ios = (double)perf.write_ios * mul;\n    retval.queue = (double)perf.queue * mul;\n\n    return retval;\n}\n\nstruct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {\n    struct disk_stats retval;\n    retval.read_ios = stats1.read_ios + stats2.read_ios;\n    retval.read_merges = stats1.read_merges + stats2.read_merges;\n    retval.read_sectors = stats1.read_sectors + stats2.read_sectors;\n    retval.read_ticks = stats1.read_ticks + stats2.read_ticks;\n    retval.write_ios = stats1.write_ios + stats2.write_ios;\n    retval.write_merges = stats1.write_merges + stats2.write_merges;\n    retval.write_sectors = stats1.write_sectors + stats2.write_sectors;\n    retval.write_ticks = stats1.write_ticks + stats2.write_ticks;\n    retval.io_in_flight = stats1.io_in_flight + stats2.io_in_flight;\n    retval.io_ticks = stats1.io_ticks + stats2.io_ticks;\n    retval.io_in_queue = stats1.io_in_queue + stats2.io_in_queue;\n    retval.end_time = stats1.end_time + stats2.end_time;\n\n    return retval;\n}\n\nvoid expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {\n    EXPECT_LE(stats1.read_ios, stats2.read_ios);\n    EXPECT_LE(stats1.read_merges, stats2.read_merges);\n    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);\n    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);\n    EXPECT_LE(stats1.write_ios, stats2.write_ios);\n    EXPECT_LE(stats1.write_merges, stats2.write_merges);\n    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);\n    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);\n    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);\n    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);\n\n    EXPECT_TRUE(stats1.read_ios < stats2.read_ios ||\n        stats1.read_merges < stats2.read_merges ||\n        stats1.read_sectors < stats2.read_sectors ||\n        stats1.read_ticks < stats2.read_ticks ||\n        stats1.write_ios < stats2.write_ios ||\n        stats1.write_merges < stats2.write_merges ||\n        stats1.write_sectors < stats2.write_sectors ||\n        stats1.write_ticks < stats2.write_ticks ||\n        stats1.io_ticks < stats2.io_ticks ||\n        stats1.io_in_queue < stats2.io_in_queue);\n}\n\nTEST(storaged_test, disk_stats_monitor) {\n    auto [healthService, hidlHealth] = HealthServicePair::get();\n\n    // asserting that there is one file for diskstats\n    ASSERT_TRUE(healthService != nullptr || access(MMC_DISK_STATS_PATH, R_OK) >= 0 ||\n                access(SDA_DISK_STATS_PATH, R_OK) >= 0);\n\n    // testing if detect() will return the right value\n    disk_stats_monitor dsm_detect{healthService};\n    ASSERT_TRUE(dsm_detect.enabled());\n\n    // Even if enabled(), healthService may not support disk stats. Check if it is supported.\n    std::vector<aidl::android::hardware::health::DiskStats> halStats;\n    if (healthService->getDiskStats(&halStats).getExceptionCode() == EX_UNSUPPORTED_OPERATION) {\n        GTEST_SKIP();\n    }\n\n    // feed monitor with constant perf data for io perf baseline\n    // using constant perf is reasonable since the functionality of stream_stats\n    // has already been tested\n    struct disk_perf norm_perf = {\n        .read_perf = 10 * 1024,\n        .read_ios = 50,\n        .write_perf = 5 * 1024,\n        .write_ios = 25,\n        .queue = 5\n    };\n\n    std::random_device rd;\n    std::mt19937 gen(rd());\n    std::uniform_real_distribution<> rand(0.8, 1.2);\n\n    for (uint i = 0; i < dsm_detect.mWindow; ++i) {\n        struct disk_perf perf = disk_perf_multiply(norm_perf, rand(gen));\n\n        dsm_detect.add(&perf);\n        dsm_detect.mBuffer.push(perf);\n        EXPECT_EQ(dsm_detect.mBuffer.size(), (uint64_t)i + 1);\n    }\n\n    dsm_detect.mValid = true;\n    dsm_detect.update_mean();\n    dsm_detect.update_std();\n\n    // FixLater: avoid floating point loop counters\n    // NOLINTNEXTLINE(clang-analyzer-security.FloatLoopCounter,cert-flp30-c)\n    for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {\n        struct disk_perf test_perf;\n        struct disk_perf test_mean = dsm_detect.mMean;\n        struct disk_perf test_std = dsm_detect.mStd;\n\n        test_perf.read_perf = (double)test_mean.read_perf - i * test_std.read_perf;\n        test_perf.read_ios = (double)test_mean.read_ios - i * test_std.read_ios;\n        test_perf.write_perf = (double)test_mean.write_perf - i * test_std.write_perf;\n        test_perf.write_ios = (double)test_mean.write_ios - i * test_std.write_ios;\n        test_perf.queue = (double)test_mean.queue + i * test_std.queue;\n\n        EXPECT_EQ((i > dsm_detect.mSigma), dsm_detect.detect(&test_perf));\n    }\n\n    // testing if stalled disk_stats can be correctly accumulated in the monitor\n    disk_stats_monitor dsm_acc{healthService};\n    struct disk_stats norm_inc = {\n        .read_ios = 200,\n        .read_merges = 0,\n        .read_sectors = 200,\n        .read_ticks = 200,\n        .write_ios = 100,\n        .write_merges = 0,\n        .write_sectors = 100,\n        .write_ticks = 100,\n        .io_in_flight = 0,\n        .io_ticks = 600,\n        .io_in_queue = 300,\n        .start_time = 0,\n        .end_time = 100,\n        .counter = 0,\n        .io_avg = 0\n    };\n\n    struct disk_stats stall_inc = {\n        .read_ios = 200,\n        .read_merges = 0,\n        .read_sectors = 20,\n        .read_ticks = 200,\n        .write_ios = 100,\n        .write_merges = 0,\n        .write_sectors = 10,\n        .write_ticks = 100,\n        .io_in_flight = 0,\n        .io_ticks = 600,\n        .io_in_queue = 1200,\n        .start_time = 0,\n        .end_time = 100,\n        .counter = 0,\n        .io_avg = 0\n    };\n\n    struct disk_stats stats_base = {};\n    int loop_size = 100;\n    for (int i = 0; i < loop_size; ++i) {\n        stats_base = disk_stats_add(stats_base, norm_inc);\n        dsm_acc.update(&stats_base);\n        EXPECT_EQ(dsm_acc.mValid, (uint32_t)i >= dsm_acc.mWindow);\n        EXPECT_FALSE(dsm_acc.mStall);\n    }\n\n    stats_base = disk_stats_add(stats_base, stall_inc);\n    dsm_acc.update(&stats_base);\n    EXPECT_TRUE(dsm_acc.mValid);\n    EXPECT_TRUE(dsm_acc.mStall);\n\n    for (int i = 0; i < 10; ++i) {\n        stats_base = disk_stats_add(stats_base, norm_inc);\n        dsm_acc.update(&stats_base);\n        EXPECT_TRUE(dsm_acc.mValid);\n        EXPECT_FALSE(dsm_acc.mStall);\n    }\n\n    struct disk_stats stats_prev = {};\n    loop_size = 10;\n    write_and_pause(5);\n    for (int i = 0; i < loop_size; ++i) {\n        dsm_detect.update();\n        expect_increasing(stats_prev, dsm_detect.mPrevious);\n        stats_prev = dsm_detect.mPrevious;\n        write_and_pause(5);\n    }\n}\n\nTEST(storaged_test, storage_info_t) {\n    storage_info_t si;\n    time_point<steady_clock> tp;\n    time_point<system_clock> stp;\n\n    // generate perf history [least_recent  ------> most recent]\n    // day 1:   5,  10,  15,  20            | daily average 12\n    // day 2:  25,  30,  35,  40,  45       | daily average 35\n    // day 3:  50,  55,  60,  65,  70       | daily average 60\n    // day 4:  75,  80,  85,  90,  95       | daily average 85\n    // day 5: 100, 105, 110, 115,           | daily average 107\n    // day 6: 120, 125, 130, 135, 140       | daily average 130\n    // day 7: 145, 150, 155, 160, 165       | daily average 155\n    // end of week 1:                       | weekly average 83\n    // day 1: 170, 175, 180, 185, 190       | daily average 180\n    // day 2: 195, 200, 205, 210, 215       | daily average 205\n    // day 3: 220, 225, 230, 235            | daily average 227\n    // day 4: 240, 245, 250, 255, 260       | daily average 250\n    // day 5: 265, 270, 275, 280, 285       | daily average 275\n    // day 6: 290, 295, 300, 305, 310       | daily average 300\n    // day 7: 315, 320, 325, 330, 335       | daily average 325\n    // end of week 2:                       | weekly average 251\n    // day 1: 340, 345, 350, 355            | daily average 347\n    // day 2: 360, 365, 370, 375\n    si.day_start_tp = {};\n    for (int i = 0; i < 75; i++) {\n        tp += hours(5);\n        stp = {};\n        stp += duration_cast<chrono::seconds>(tp.time_since_epoch());\n        si.update_perf_history((i + 1) * 5, stp);\n    }\n\n    vector<int> history = si.get_perf_history();\n    EXPECT_EQ(history.size(), 66UL);\n    size_t i = 0;\n    EXPECT_EQ(history[i++], 4);\n    EXPECT_EQ(history[i++], 7);    // 7 days\n    EXPECT_EQ(history[i++], 52);   // 52 weeks\n    // last 24 hours\n    EXPECT_EQ(history[i++], 375);\n    EXPECT_EQ(history[i++], 370);\n    EXPECT_EQ(history[i++], 365);\n    EXPECT_EQ(history[i++], 360);\n    // daily average of last 7 days\n    EXPECT_EQ(history[i++], 347);\n    EXPECT_EQ(history[i++], 325);\n    EXPECT_EQ(history[i++], 300);\n    EXPECT_EQ(history[i++], 275);\n    EXPECT_EQ(history[i++], 250);\n    EXPECT_EQ(history[i++], 227);\n    EXPECT_EQ(history[i++], 205);\n    // weekly average of last 52 weeks\n    EXPECT_EQ(history[i++], 251);\n    EXPECT_EQ(history[i++], 83);\n    for (; i < history.size(); i++) {\n        EXPECT_EQ(history[i], 0);\n    }\n}\n\nTEST(storaged_test, storage_info_t_proto) {\n    storage_info_t si;\n    si.day_start_tp = {};\n\n    IOPerfHistory proto;\n    proto.set_nr_samples(10);\n    proto.set_day_start_sec(0);\n    si.load_perf_history_proto(proto);\n\n    // Skip ahead > 1 day, with no data points in the previous day.\n    time_point<system_clock> stp;\n    stp += hours(36);\n    si.update_perf_history(100, stp);\n\n    vector<int> history = si.get_perf_history();\n    EXPECT_EQ(history.size(), 63UL);\n    EXPECT_EQ(history[0], 1);\n    EXPECT_EQ(history[1], 7);\n    EXPECT_EQ(history[2], 52);\n    EXPECT_EQ(history[3], 100);\n    for (size_t i = 4; i < history.size(); i++) {\n        EXPECT_EQ(history[i], 0);\n    }\n}\n\nTEST(storaged_test, uid_monitor) {\n    uid_monitor uidm;\n    auto& io_history = uidm.io_history();\n\n    io_history[200] = {\n        .start_ts = 100,\n        .entries = {\n            { \"app1\", {\n                .user_id = 0,\n                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,\n              }\n            },\n            { \"app2\", {\n                .user_id = 0,\n                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 1000,\n              }\n            },\n            { \"app1\", {\n                .user_id = 1,\n                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,\n                .uid_ios.bytes[READ][FOREGROUND][CHARGER_ON] = 1000,\n              }\n            },\n        },\n    };\n\n    io_history[300] = {\n        .start_ts = 200,\n        .entries = {\n            { \"app1\", {\n                .user_id = 1,\n                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF] = 1000,\n              }\n            },\n            { \"app3\", {\n                .user_id = 0,\n                .uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF] = 1000,\n              }\n            },\n        },\n    };\n\n    unordered_map<int, StoragedProto> protos;\n\n    uidm.update_uid_io_proto(&protos);\n\n    EXPECT_EQ(protos.size(), 2U);\n    EXPECT_EQ(protos.count(0), 1UL);\n    EXPECT_EQ(protos.count(1), 1UL);\n\n    EXPECT_EQ(protos[0].uid_io_usage().uid_io_items_size(), 2);\n    const UidIOItem& user_0_item_0 = protos[0].uid_io_usage().uid_io_items(0);\n    EXPECT_EQ(user_0_item_0.end_ts(), 200UL);\n    EXPECT_EQ(user_0_item_0.records().start_ts(), 100UL);\n    EXPECT_EQ(user_0_item_0.records().entries_size(), 2);\n    EXPECT_EQ(user_0_item_0.records().entries(0).uid_name(), \"app1\");\n    EXPECT_EQ(user_0_item_0.records().entries(0).user_id(), 0UL);\n    EXPECT_EQ(user_0_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);\n    EXPECT_EQ(user_0_item_0.records().entries(1).uid_name(), \"app2\");\n    EXPECT_EQ(user_0_item_0.records().entries(1).user_id(), 0UL);\n    EXPECT_EQ(user_0_item_0.records().entries(1).uid_io().rd_fg_chg_off(), 1000UL);\n    const UidIOItem& user_0_item_1 = protos[0].uid_io_usage().uid_io_items(1);\n    EXPECT_EQ(user_0_item_1.end_ts(), 300UL);\n    EXPECT_EQ(user_0_item_1.records().start_ts(), 200UL);\n    EXPECT_EQ(user_0_item_1.records().entries_size(), 1);\n    EXPECT_EQ(user_0_item_1.records().entries(0).uid_name(), \"app3\");\n    EXPECT_EQ(user_0_item_1.records().entries(0).user_id(), 0UL);\n    EXPECT_EQ(user_0_item_1.records().entries(0).uid_io().rd_bg_chg_off(), 1000UL);\n\n    EXPECT_EQ(protos[1].uid_io_usage().uid_io_items_size(), 2);\n    const UidIOItem& user_1_item_0 = protos[1].uid_io_usage().uid_io_items(0);\n    EXPECT_EQ(user_1_item_0.end_ts(), 200UL);\n    EXPECT_EQ(user_1_item_0.records().start_ts(), 100UL);\n    EXPECT_EQ(user_1_item_0.records().entries_size(), 1);\n    EXPECT_EQ(user_1_item_0.records().entries(0).uid_name(), \"app1\");\n    EXPECT_EQ(user_1_item_0.records().entries(0).user_id(), 1UL);\n    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().rd_fg_chg_on(), 1000UL);\n    EXPECT_EQ(user_1_item_0.records().entries(0).uid_io().wr_fg_chg_on(), 1000UL);\n    const UidIOItem& user_1_item_1 = protos[1].uid_io_usage().uid_io_items(1);\n    EXPECT_EQ(user_1_item_1.end_ts(), 300UL);\n    EXPECT_EQ(user_1_item_1.records().start_ts(), 200UL);\n    EXPECT_EQ(user_1_item_1.records().entries_size(), 1);\n    EXPECT_EQ(user_1_item_1.records().entries(0).uid_name(), \"app1\");\n    EXPECT_EQ(user_1_item_1.records().entries(0).user_id(), 1UL);\n    EXPECT_EQ(user_1_item_1.records().entries(0).uid_io().wr_fg_chg_off(), 1000UL);\n\n    io_history.clear();\n\n    io_history[300] = {\n        .start_ts = 200,\n        .entries = {\n            { \"app1\", {\n                .user_id = 0,\n                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,\n              }\n            },\n        },\n    };\n\n    io_history[400] = {\n        .start_ts = 300,\n        .entries = {\n            { \"app1\", {\n                .user_id = 0,\n                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,\n              }\n            },\n        },\n    };\n\n    uidm.load_uid_io_proto(0, protos[0].uid_io_usage());\n    uidm.load_uid_io_proto(1, protos[1].uid_io_usage());\n\n    EXPECT_EQ(io_history.size(), 3UL);\n    EXPECT_EQ(io_history.count(200), 1UL);\n    EXPECT_EQ(io_history.count(300), 1UL);\n    EXPECT_EQ(io_history.count(400), 1UL);\n\n    EXPECT_EQ(io_history[200].start_ts, 100UL);\n    const vector<struct uid_record>& entries_0 = io_history[200].entries;\n    EXPECT_EQ(entries_0.size(), 3UL);\n    EXPECT_EQ(entries_0[0].name, \"app1\");\n    EXPECT_EQ(entries_0[0].ios.user_id, 0UL);\n    EXPECT_EQ(entries_0[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);\n    EXPECT_EQ(entries_0[1].name, \"app2\");\n    EXPECT_EQ(entries_0[1].ios.user_id, 0UL);\n    EXPECT_EQ(entries_0[1].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);\n    EXPECT_EQ(entries_0[2].name, \"app1\");\n    EXPECT_EQ(entries_0[2].ios.user_id, 1UL);\n    EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);\n    EXPECT_EQ(entries_0[2].ios.uid_ios.bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);\n\n    EXPECT_EQ(io_history[300].start_ts, 200UL);\n    const vector<struct uid_record>& entries_1 = io_history[300].entries;\n    EXPECT_EQ(entries_1.size(), 3UL);\n    EXPECT_EQ(entries_1[0].name, \"app1\");\n    EXPECT_EQ(entries_1[0].ios.user_id, 0UL);\n    EXPECT_EQ(entries_1[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);\n    EXPECT_EQ(entries_1[1].name, \"app3\");\n    EXPECT_EQ(entries_1[1].ios.user_id, 0UL);\n    EXPECT_EQ(entries_1[1].ios.uid_ios.bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);\n    EXPECT_EQ(entries_1[2].name, \"app1\");\n    EXPECT_EQ(entries_1[2].ios.user_id, 1UL);\n    EXPECT_EQ(entries_1[2].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);\n\n    EXPECT_EQ(io_history[400].start_ts, 300UL);\n    const vector<struct uid_record>& entries_2 = io_history[400].entries;\n    EXPECT_EQ(entries_2.size(), 1UL);\n    EXPECT_EQ(entries_2[0].name, \"app1\");\n    EXPECT_EQ(entries_2[0].ios.user_id, 0UL);\n    EXPECT_EQ(entries_2[0].ios.uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);\n\n    map<string, io_usage> merged_entries_0 = merge_io_usage(entries_0);\n    EXPECT_EQ(merged_entries_0.size(), 2UL);\n    EXPECT_EQ(merged_entries_0.count(\"app1\"), 1UL);\n    EXPECT_EQ(merged_entries_0.count(\"app2\"), 1UL);\n    EXPECT_EQ(merged_entries_0[\"app1\"].bytes[READ][FOREGROUND][CHARGER_ON], 1000UL);\n    EXPECT_EQ(merged_entries_0[\"app1\"].bytes[WRITE][FOREGROUND][CHARGER_ON], 2000UL);\n    EXPECT_EQ(merged_entries_0[\"app2\"].bytes[READ][FOREGROUND][CHARGER_OFF], 1000UL);\n\n    map<string, io_usage> merged_entries_1 = merge_io_usage(entries_1);\n    EXPECT_EQ(merged_entries_1.size(), 2UL);\n    EXPECT_EQ(merged_entries_1.count(\"app1\"), 1UL);\n    EXPECT_EQ(merged_entries_1.count(\"app3\"), 1UL);\n    EXPECT_EQ(merged_entries_1[\"app1\"].bytes[WRITE][FOREGROUND][CHARGER_OFF], 1000UL);\n    EXPECT_EQ(merged_entries_1[\"app1\"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);\n    EXPECT_EQ(merged_entries_1[\"app3\"].bytes[READ][BACKGROUND][CHARGER_OFF], 1000UL);\n\n    map<string, io_usage> merged_entries_2 = merge_io_usage(entries_2);\n    EXPECT_EQ(merged_entries_2.size(), 1UL);\n    EXPECT_EQ(merged_entries_2.count(\"app1\"), 1UL);\n    EXPECT_EQ(merged_entries_2[\"app1\"].bytes[WRITE][FOREGROUND][CHARGER_ON], 1000UL);\n\n    uidm.clear_user_history(0);\n\n    EXPECT_EQ(io_history.size(), 2UL);\n    EXPECT_EQ(io_history.count(200), 1UL);\n    EXPECT_EQ(io_history.count(300), 1UL);\n\n    EXPECT_EQ(io_history[200].entries.size(), 1UL);\n    EXPECT_EQ(io_history[300].entries.size(), 1UL);\n\n    uidm.clear_user_history(1);\n\n    EXPECT_EQ(io_history.size(), 0UL);\n}\n\nTEST(storaged_test, load_uid_io_proto) {\n    uid_monitor uidm;\n    auto& io_history = uidm.io_history();\n\n    static const uint64_t kProtoTime = 200;\n    io_history[kProtoTime] = {\n        .start_ts = 100,\n        .entries = {\n            { \"app1\", {\n                .user_id = 0,\n                .uid_ios.bytes[WRITE][FOREGROUND][CHARGER_ON] = 1000,\n              }\n            },\n            { \"app2\", {\n                .user_id = 0,\n                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 2000,\n              }\n            },\n            { \"app3\", {\n                .user_id = 0,\n                .uid_ios.bytes[READ][FOREGROUND][CHARGER_OFF] = 3000,\n              }\n            },\n        },\n    };\n\n    unordered_map<int, StoragedProto> protos;\n    uidm.update_uid_io_proto(&protos);\n    ASSERT_EQ(protos.size(), size_t(1));\n\n    // Loading the same proto many times should not add duplicate entries.\n    UidIOUsage user_0 = protos[0].uid_io_usage();\n    for (size_t i = 0; i < 10000; i++) {\n        uidm.load_uid_io_proto(0, user_0);\n    }\n    ASSERT_EQ(io_history.size(), size_t(1));\n    ASSERT_EQ(io_history[kProtoTime].entries.size(), size_t(3));\n\n    // Create duplicate entries until we go over the limit.\n    auto record = io_history[kProtoTime];\n    io_history.clear();\n    for (size_t i = 0; i < uid_monitor::MAX_UID_RECORDS_SIZE * 2; i++) {\n        if (i == kProtoTime) {\n            continue;\n        }\n        io_history[i] = record;\n    }\n    ASSERT_GT(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));\n\n    // After loading, the history should be truncated.\n    for (auto& item : *user_0.mutable_uid_io_items()) {\n        item.set_end_ts(io_history.size());\n    }\n    uidm.load_uid_io_proto(0, user_0);\n    ASSERT_LE(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));\n}\n"
  },
  {
    "path": "storaged/tools/ranker.py",
    "content": "# Copyright 2017 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Parser and ranker for dumpsys storaged output.\n\nThis module parses output from dumpsys storaged by ranking uids based on\ntheir io usage measured in 8 different stats. It must be provided the input\nfile through command line argument -i/--input.\n\nFor more details, see:\n    $ python ranker.py -h\n\nExample:\n    $ python ranker.py -i io.txt -o output.txt -u 20 -cnt\n\"\"\"\n\nimport argparse\nimport sys\n\nIO_NAMES = [\"[READ][FOREGROUND][CHARGER_OFF]\",\n            \"[WRITE][FOREGROUND][CHARGER_OFF]\",\n            \"[READ][BACKGROUND][CHARGER_OFF]\",\n            \"[WRITE][BACKGROUND][CHARGER_OFF]\",\n            \"[READ][FOREGROUND][CHARGER_ON]\",\n            \"[WRITE][FOREGROUND][CHARGER_ON]\",\n            \"[READ][BACKGROUND][CHARGER_ON]\",\n            \"[WRITE][BACKGROUND][CHARGER_ON]\"]\n\n\ndef get_args():\n  \"\"\"Get arguments from command line.\n\n  The only required argument is input file.\n\n  Returns:\n    Args containing cmdline arguments\n  \"\"\"\n\n  parser = argparse.ArgumentParser()\n  parser.add_argument(\"-i\", \"--input\", dest=\"input\", required=\"true\",\n                      help=\"input io FILE, must provide\", metavar=\"FILE\")\n  parser.add_argument(\"-o\", \"--output\", dest=\"output\", default=\"stdout\",\n                      help=\"output FILE, default to stdout\", metavar=\"FILE\")\n  parser.add_argument(\"-u\", \"--uidcnt\", dest=\"uidcnt\", type=int, default=10,\n                      help=\"set number of uids to display for each rank, \"\n                      \"default 10\")\n  parser.add_argument(\"-c\", \"--combine\", dest=\"combine\", default=False,\n                      action=\"store_true\", help=\"add io stats for same uids, \"\n                      \"default to take io stats of last appearing uids\")\n  parser.add_argument(\"-n\", \"--native\", dest=\"native\", default=False,\n                      action=\"store_true\", help=\"only include native apps in \"\n                      \"ranking, default to include all apps\")\n  parser.add_argument(\"-t\", \"--task\", dest=\"task\", default=False,\n                      action=\"store_true\", help=\"display task io under uids, \"\n                      \"default to not display tasks\")\n  return parser.parse_args()\n\n\ndef is_number(word):\n  try:\n    int(word)\n    return True\n  except ValueError:\n    return False\n\n\ndef combine_or_filter(args):\n  \"\"\"Parser for io input.\n\n  Either args.combine io stats for the same uids\n  or take the io stats for the last uid and ignore\n  the same uids before it.\n\n  If task is required, store task ios along with uid\n  for later display.\n\n  Returns:\n    The structure for the return value uids is as follows:\n    uids: {uid -> [UID_STATS, TASK_STATS(optional)]}\n    UID_STATS: [io1, io2, ..., io8]\n    TASK_STATS: {task_name -> [io1, io2, ..., io8]}\n  \"\"\"\n  fin = open(args.input, \"r\")\n  uids = {}\n  cur_uid = 0\n  task_enabled = args.task\n  for line in fin:\n    words = line.split()\n    if words[0] == \"->\":\n      # task io\n      if not task_enabled:\n        continue\n      # get task command line\n      i = len(words) - 8\n      task = \" \".join(words[1:i])\n      if task in uids[cur_uid][1]:\n        task_io = uids[cur_uid][1][task]\n        for j in range(8):\n          task_io[j] += long(words[i+j])\n      else:\n        task_io = []\n        for j in range(8):\n          task_io.append(long(words[i+j]))\n      uids[cur_uid][1][task] = task_io\n\n    elif len(words) > 8:\n      if not is_number(words[0]) and args.native:\n        # uid not requested, ignore its tasks as well\n        task_enabled = False\n        continue\n      task_enabled = args.task\n      i = len(words) - 8\n      uid = \" \".join(words[:i])\n      if uid in uids and args.combine:\n        uid_io = uids[uid][0]\n        for j in range(8):\n          uid_io[j] += long(words[i+j])\n        uids[uid][0] = uid_io\n      else:\n        uid_io = [long(words[i+j]) for j in range(8)]\n        uids[uid] = [uid_io]\n        if task_enabled:\n          uids[uid].append({})\n      cur_uid = uid\n\n  return uids\n\n\ndef rank_uids(uids):\n  \"\"\"Sort uids based on eight different io stats.\n\n  Returns:\n    uid_rank is a 2d list of tuples:\n    The first dimension represent the 8 different io stats.\n    The second dimension is a sorted list of tuples by tup[0],\n    each tuple is a uid's perticular stat at the first dimension and the uid.\n  \"\"\"\n  uid_rank = [[(uids[uid][0][i], uid) for uid in uids] for i in range(8)]\n  for i in range(8):\n    uid_rank[i].sort(key=lambda tup: tup[0], reverse=True)\n  return uid_rank\n\n\ndef display_uids(uid_rank, uids, args):\n  \"\"\"Display ranked uid io, along with task io if specified.\"\"\"\n  fout = sys.stdout\n  if args.output != \"stdout\":\n    fout = open(args.output, \"w\")\n\n  for i in range(8):\n    fout.write(\"RANKING BY \" + IO_NAMES[i] + \"\\n\")\n    for j in range(min(args.uidcnt, len(uid_rank[0]))):\n      uid = uid_rank[i][j][1]\n      uid_stat = \" \".join([str(uid_io) for uid_io in uids[uid][0]])\n      fout.write(uid + \" \" + uid_stat + \"\\n\")\n      if args.task:\n        for task in uids[uid][1]:\n          task_stat = \" \".join([str(task_io) for task_io in uids[uid][1][task]])\n          fout.write(\"-> \" + task + \" \" + task_stat + \"\\n\")\n      fout.write(\"\\n\")\n\n\ndef main():\n  args = get_args()\n  uids = combine_or_filter(args)\n  uid_rank = rank_uids(uids)\n  display_uids(uid_rank, uids, args)\n\nif __name__ == \"__main__\":\n  main()\n"
  },
  {
    "path": "storaged/uid_info.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <binder/Parcel.h>\n\n#include \"uid_info.h\"\n\nusing namespace android;\nusing namespace android::os::storaged;\n\nstatus_t UidInfo::writeToParcel(Parcel* parcel) const {\n    parcel->writeInt32(uid);\n    parcel->writeCString(name.c_str());\n    parcel->write(&io, sizeof(io));\n\n    parcel->writeInt32(tasks.size());\n    for (const auto& task_it : tasks) {\n        parcel->writeInt32(task_it.first);\n        parcel->writeCString(task_it.second.comm.c_str());\n        parcel->write(&task_it.second.io, sizeof(task_it.second.io));\n    }\n    return OK;\n}\n\nstatus_t UidInfo::readFromParcel(const Parcel* parcel) {\n    uid = parcel->readInt32();\n    name = parcel->readCString();\n    parcel->read(&io, sizeof(io));\n\n    uint32_t tasks_size = parcel->readInt32();\n    for (uint32_t i = 0; i < tasks_size; i++) {\n        task_info task;\n        task.pid = parcel->readInt32();\n        task.comm = parcel->readCString();\n        parcel->read(&task.io, sizeof(task.io));\n        tasks[task.pid] = task;\n    }\n    return OK;\n}\n"
  },
  {
    "path": "toolbox/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"system_core_toolbox_license\"],\n}\n\n// Added automatically by a large-scale-change\n// See: http://go/android-license-faq\nlicense {\n    name: \"system_core_toolbox_license\",\n    visibility: [\":__subpackages__\"],\n    license_kinds: [\n        \"SPDX-license-identifier-Apache-2.0\",\n    ],\n    license_text: [\n        \"NOTICE\",\n    ],\n}\n\ncc_defaults {\n    name: \"toolbox_defaults\",\n    cflags: [\n        \"-Werror\",\n        \"-Wno-unused-parameter\",\n        \"-Wno-unused-const-variable\",\n        \"-D_FILE_OFFSET_BITS=64\",\n    ],\n}\n\ngenrule {\n    name: \"toolbox_input_labels\",\n    tool_files: [\"generate-input.h-labels.py\"],\n    cmd: \"$(location) $(in) >$(out)\",\n    srcs: [\":kernel_input_headers\"],\n    out: [\"input.h-labels.h\"],\n}\n\ncc_defaults {\n    name: \"toolbox_binary_defaults\",\n    defaults: [\"toolbox_defaults\"],\n    srcs: [\n        \"toolbox.c\",\n        \"getevent.c\",\n        \"getprop.cpp\",\n        \"modprobe.cpp\",\n        \"setprop.cpp\",\n        \"start.cpp\",\n    ],\n    generated_headers: [\n        \"toolbox_input_labels\",\n    ],\n    shared_libs: [\n        \"libbase\",\n    ],\n    static_libs: [\n        \"libmodprobe\",\n        \"libpropertyinfoparser\",\n    ],\n\n    symlinks: [\n        \"getevent\",\n        \"getprop\",\n        \"modprobe\",\n        \"setprop\",\n        \"start\",\n        \"stop\",\n    ],\n}\n\ncc_binary {\n    name: \"toolbox\",\n    defaults: [\"toolbox_binary_defaults\"],\n    vendor_ramdisk_available: true,\n}\n\ncc_binary {\n    name: \"toolbox.recovery\",\n    defaults: [\"toolbox_binary_defaults\"],\n    recovery: true,\n    stem: \"toolbox\",\n}\n\ncc_binary {\n    name: \"toolbox_vendor\",\n    stem: \"toolbox\",\n    vendor: true,\n    defaults: [\"toolbox_binary_defaults\"],\n}\n\n// This one is installed in the generic ramdisk, and can be executed during\n// init-first-stage.\n// As there are no dynamic linker available, this must be statically linked.\ncc_binary {\n    name: \"toolbox_ramdisk\",\n    defaults: [\"toolbox_binary_defaults\"],\n    ramdisk: true,\n    static_executable: true,\n    system_shared_libs: [],\n    exclude_shared_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libbase\",\n        \"liblog\",\n    ],\n}\n"
  },
  {
    "path": "toolbox/MODULE_LICENSE_APACHE2",
    "content": ""
  },
  {
    "path": "toolbox/NOTICE",
    "content": "Copyright (C) 2017 The Android Open Source Project\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n-------------------------------------------------------------------\n\n"
  },
  {
    "path": "toolbox/OWNERS",
    "content": "include platform/system/core:/janitors/OWNERS\nper-file modprobe.c=willmcvicker@google.com,dvander@google.com\nper-file getevent.c=file:platform/frameworks/base:/INPUT_OWNERS\n"
  },
  {
    "path": "toolbox/generate-input.h-labels.py",
    "content": "#!/usr/bin/env python3\n#\n# Copyright (C) 2015 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the 'License');\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an 'AS IS' BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# pylint: disable=bad-indentation,bad-continuation\n\n\nimport os\nimport re\nimport sys\n\ninput_prop_list = []\nev_list = []\nsyn_list = []\nkey_list = []\nrel_list = []\nabs_list = []\nsw_list = []\nmsc_list = []\nled_list = []\nrep_list = []\nsnd_list = []\nmt_tool_list = []\nff_status_list = []\nff_list = []\n\nr = re.compile(r'#define\\s+(\\S+)\\s+((?:0x)?\\d+)')\n\nfor arg in sys.argv[1:]:\n  with open(arg, 'r') as f:\n    for line in f:\n      m = r.match(line)\n      if m:\n        name = m.group(1)\n        if name.startswith(\"INPUT_PROP_\"):\n          input_prop_list.append(name)\n        elif name.startswith(\"EV_\"):\n          ev_list.append(name)\n        elif name.startswith(\"SYN_\"):\n          syn_list.append(name)\n        elif name.startswith(\"KEY_\") or name.startswith(\"BTN_\"):\n          key_list.append(name)\n        elif name.startswith(\"REL_\"):\n          rel_list.append(name)\n        elif name.startswith(\"ABS_\"):\n          abs_list.append(name)\n        elif name.startswith(\"SW_\"):\n          sw_list.append(name)\n        elif name.startswith(\"MSC_\"):\n          msc_list.append(name)\n        elif name.startswith(\"LED_\"):\n          led_list.append(name)\n        elif name.startswith(\"REP_\"):\n          rep_list.append(name)\n        elif name.startswith(\"SND_\"):\n          snd_list.append(name)\n        elif name.startswith(\"MT_TOOL_\"):\n          mt_tool_list.append(name)\n        elif name.startswith(\"FF_STATUS_\"):\n          ff_status_list.append(name)\n        elif name.startswith(\"FF_\"):\n          ff_list.append(name)\n\ndef Dump(struct_name, values):\n  print('static struct label %s[] = {' % (struct_name))\n  for value in values:\n    print('    LABEL(%s),' % (value))\n  print('    LABEL_END,')\n  print('};')\n\nDump(\"input_prop_labels\", input_prop_list)\nDump(\"ev_labels\", ev_list)\nDump(\"syn_labels\", syn_list)\nDump(\"key_labels\", key_list)\nDump(\"rel_labels\", rel_list)\nDump(\"abs_labels\", abs_list)\nDump(\"sw_labels\", sw_list)\nDump(\"msc_labels\", msc_list)\nDump(\"led_labels\", led_list)\nDump(\"rep_labels\", rep_list)\nDump(\"snd_labels\", snd_list)\nDump(\"mt_tool_labels\", mt_tool_list)\nDump(\"ff_status_labels\", ff_status_list)\nDump(\"ff_labels\", ff_list)\n"
  },
  {
    "path": "toolbox/getevent.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <dirent.h>\n#include <fcntl.h>\n#include <sys/ioctl.h>\n#include <sys/inotify.h>\n#include <sys/limits.h>\n#include <sys/poll.h>\n#include <linux/input.h>\n#include <err.h>\n#include <errno.h>\n#include <unistd.h>\n\nstruct label {\n    const char *name;\n    int value;\n};\n\n#define LABEL(constant) { #constant, constant }\n#define LABEL_END { NULL, -1 }\n\nstatic struct label key_value_labels[] = {\n        { \"UP\", 0 },\n        { \"DOWN\", 1 },\n        { \"REPEAT\", 2 },\n        LABEL_END,\n};\n\n#include \"input.h-labels.h\"\n\n#undef LABEL\n#undef LABEL_END\n\nstatic struct pollfd *ufds;\nstatic char **device_names;\nstatic int nfds;\n\nenum {\n    PRINT_DEVICE_ERRORS     = 1U << 0,\n    PRINT_DEVICE            = 1U << 1,\n    PRINT_DEVICE_NAME       = 1U << 2,\n    PRINT_DEVICE_INFO       = 1U << 3,\n    PRINT_VERSION           = 1U << 4,\n    PRINT_POSSIBLE_EVENTS   = 1U << 5,\n    PRINT_INPUT_PROPS       = 1U << 6,\n    PRINT_HID_DESCRIPTOR    = 1U << 7,\n\n    PRINT_ALL_INFO          = (1U << 8) - 1,\n\n    PRINT_LABELS            = 1U << 16,\n};\n\nstatic const char *get_label(const struct label *labels, int value)\n{\n    while(labels->name && value != labels->value) {\n        labels++;\n    }\n    return labels->name;\n}\n\nstatic int print_input_props(int fd)\n{\n    uint8_t bits[INPUT_PROP_CNT / 8];\n    int i, j;\n    int res;\n    int count;\n    const char *bit_label;\n\n    printf(\"  input props:\\n\");\n    res = ioctl(fd, EVIOCGPROP(sizeof(bits)), bits);\n    if(res < 0) {\n        printf(\"    <not available\\n\");\n        return 1;\n    }\n    count = 0;\n    for(i = 0; i < res; i++) {\n        for(j = 0; j < 8; j++) {\n            if (bits[i] & 1 << j) {\n                bit_label = get_label(input_prop_labels, i * 8 + j);\n                if(bit_label)\n                    printf(\"    %s\\n\", bit_label);\n                else\n                    printf(\"    %04x\\n\", i * 8 + j);\n                count++;\n            }\n        }\n    }\n    if (!count)\n        printf(\"    <none>\\n\");\n    return 0;\n}\n\nstatic int print_possible_events(int fd, int print_flags)\n{\n    uint8_t *bits = NULL;\n    ssize_t bits_size = 0;\n    const char* label;\n    int i, j, k;\n    int res, res2;\n    struct label* bit_labels;\n    const char *bit_label;\n\n    printf(\"  events:\\n\");\n    for(i = EV_KEY; i <= EV_MAX; i++) { // skip EV_SYN since we cannot query its available codes\n        int count = 0;\n        while(1) {\n            res = ioctl(fd, EVIOCGBIT(i, bits_size), bits);\n            if(res < bits_size)\n                break;\n            bits_size = res + 16;\n            bits = realloc(bits, bits_size * 2);\n            if (bits == NULL) err(1, \"failed to allocate buffer of size %zd\", bits_size);\n        }\n        res2 = 0;\n        switch(i) {\n            case EV_KEY:\n                res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);\n                label = \"KEY\";\n                bit_labels = key_labels;\n                break;\n            case EV_REL:\n                label = \"REL\";\n                bit_labels = rel_labels;\n                break;\n            case EV_ABS:\n                label = \"ABS\";\n                bit_labels = abs_labels;\n                break;\n            case EV_MSC:\n                label = \"MSC\";\n                bit_labels = msc_labels;\n                break;\n            case EV_LED:\n                res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);\n                label = \"LED\";\n                bit_labels = led_labels;\n                break;\n            case EV_SND:\n                res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);\n                label = \"SND\";\n                bit_labels = snd_labels;\n                break;\n            case EV_SW:\n                res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);\n                label = \"SW \";\n                bit_labels = sw_labels;\n                break;\n            case EV_REP:\n                label = \"REP\";\n                bit_labels = rep_labels;\n                break;\n            case EV_FF:\n                label = \"FF \";\n                bit_labels = ff_labels;\n                break;\n            case EV_PWR:\n                label = \"PWR\";\n                bit_labels = NULL;\n                break;\n            case EV_FF_STATUS:\n                label = \"FFS\";\n                bit_labels = ff_status_labels;\n                break;\n            default:\n                res2 = 0;\n                label = \"???\";\n                bit_labels = NULL;\n        }\n        for(j = 0; j < res; j++) {\n            for(k = 0; k < 8; k++)\n                if(bits[j] & 1 << k) {\n                    char down;\n                    if(j < res2 && (bits[j + bits_size] & 1 << k))\n                        down = '*';\n                    else\n                        down = ' ';\n                    if(count == 0)\n                        printf(\"    %s (%04x):\", label, i);\n                    else if((count & (print_flags & PRINT_LABELS ? 0x3 : 0x7)) == 0 || i == EV_ABS)\n                        printf(\"\\n               \");\n                    if(bit_labels && (print_flags & PRINT_LABELS)) {\n                        bit_label = get_label(bit_labels, j * 8 + k);\n                        if(bit_label)\n                            printf(\" %.20s%c%*s\", bit_label, down, (int) (20 - strlen(bit_label)), \"\");\n                        else\n                            printf(\" %04x%c                \", j * 8 + k, down);\n                    } else {\n                        printf(\" %04x%c\", j * 8 + k, down);\n                    }\n                    if(i == EV_ABS) {\n                        struct input_absinfo abs;\n                        if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) {\n                            printf(\" : value %d, min %d, max %d, fuzz %d, flat %d, resolution %d\",\n                                abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat,\n                                abs.resolution);\n                        }\n                    }\n                    count++;\n                }\n        }\n        if(count)\n            printf(\"\\n\");\n    }\n    free(bits);\n    return 0;\n}\n\nstatic void print_event(int type, int code, int value, int print_flags)\n{\n    const char *type_label, *code_label, *value_label;\n\n    if (print_flags & PRINT_LABELS) {\n        type_label = get_label(ev_labels, type);\n        code_label = NULL;\n        value_label = NULL;\n\n        switch(type) {\n            case EV_SYN:\n                code_label = get_label(syn_labels, code);\n                break;\n            case EV_KEY:\n                code_label = get_label(key_labels, code);\n                value_label = get_label(key_value_labels, value);\n                break;\n            case EV_REL:\n                code_label = get_label(rel_labels, code);\n                break;\n            case EV_ABS:\n                code_label = get_label(abs_labels, code);\n                switch(code) {\n                    case ABS_MT_TOOL_TYPE:\n                        value_label = get_label(mt_tool_labels, value);\n                }\n                break;\n            case EV_MSC:\n                code_label = get_label(msc_labels, code);\n                break;\n            case EV_LED:\n                code_label = get_label(led_labels, code);\n                break;\n            case EV_SND:\n                code_label = get_label(snd_labels, code);\n                break;\n            case EV_SW:\n                code_label = get_label(sw_labels, code);\n                break;\n            case EV_REP:\n                code_label = get_label(rep_labels, code);\n                break;\n            case EV_FF:\n                code_label = get_label(ff_labels, code);\n                break;\n            case EV_FF_STATUS:\n                code_label = get_label(ff_status_labels, code);\n                break;\n        }\n\n        if (type_label)\n            printf(\"%-12.12s\", type_label);\n        else\n            printf(\"%04x        \", type);\n        if (code_label)\n            printf(\" %-20.20s\", code_label);\n        else\n            printf(\" %04x                \", code);\n        if (value_label)\n            printf(\" %-20.20s\", value_label);\n        else\n            printf(\" %08x            \", value);\n    } else {\n        printf(\"%04x %04x %08x\", type, code, value);\n    }\n}\n\nstatic void print_hid_descriptor(int bus, int vendor, int product)\n{\n    const char *dirname = \"/sys/kernel/debug/hid\";\n    char prefix[16];\n    DIR *dir;\n    struct dirent *de;\n    char filename[PATH_MAX];\n    FILE *file;\n    char line[2048];\n\n    snprintf(prefix, sizeof(prefix), \"%04X:%04X:%04X.\", bus, vendor, product);\n\n    dir = opendir(dirname);\n    if(dir == NULL)\n        return;\n    while((de = readdir(dir))) {\n        if (strstr(de->d_name, prefix) == de->d_name) {\n            snprintf(filename, sizeof(filename), \"%s/%s/rdesc\", dirname, de->d_name);\n\n            file = fopen(filename, \"r\");\n            if (file) {\n                printf(\"  HID descriptor: %s\\n\\n\", de->d_name);\n                while (fgets(line, sizeof(line), file)) {\n                    fputs(\"    \", stdout);\n                    fputs(line, stdout);\n                }\n                fclose(file);\n                puts(\"\");\n            }\n        }\n    }\n    closedir(dir);\n}\n\nstatic int open_device(const char *device, int print_flags)\n{\n    int version;\n    int fd;\n    int clkid = CLOCK_MONOTONIC;\n    struct pollfd *new_ufds;\n    char **new_device_names;\n    char name[80];\n    char location[80];\n    char idstr[80];\n    struct input_id id;\n\n    fd = open(device, O_RDONLY | O_CLOEXEC);\n    if(fd < 0) {\n        if(print_flags & PRINT_DEVICE_ERRORS)\n            fprintf(stderr, \"could not open %s, %s\\n\", device, strerror(errno));\n        return -1;\n    }\n    \n    if(ioctl(fd, EVIOCGVERSION, &version)) {\n        if(print_flags & PRINT_DEVICE_ERRORS)\n            fprintf(stderr, \"could not get driver version for %s, %s\\n\", device, strerror(errno));\n        return -1;\n    }\n    if(ioctl(fd, EVIOCGID, &id)) {\n        if(print_flags & PRINT_DEVICE_ERRORS)\n            fprintf(stderr, \"could not get driver id for %s, %s\\n\", device, strerror(errno));\n        return -1;\n    }\n    name[sizeof(name) - 1] = '\\0';\n    location[sizeof(location) - 1] = '\\0';\n    idstr[sizeof(idstr) - 1] = '\\0';\n    if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {\n        //fprintf(stderr, \"could not get device name for %s, %s\\n\", device, strerror(errno));\n        name[0] = '\\0';\n    }\n    if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {\n        //fprintf(stderr, \"could not get location for %s, %s\\n\", device, strerror(errno));\n        location[0] = '\\0';\n    }\n    if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {\n        //fprintf(stderr, \"could not get idstring for %s, %s\\n\", device, strerror(errno));\n        idstr[0] = '\\0';\n    }\n\n    if (ioctl(fd, EVIOCSCLOCKID, &clkid) != 0) {\n        fprintf(stderr, \"Can't enable monotonic clock reporting: %s\\n\", strerror(errno));\n        // a non-fatal error\n    }\n\n    new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));\n    if(new_ufds == NULL) {\n        fprintf(stderr, \"out of memory\\n\");\n        return -1;\n    }\n    ufds = new_ufds;\n    new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));\n    if(new_device_names == NULL) {\n        fprintf(stderr, \"out of memory\\n\");\n        return -1;\n    }\n    device_names = new_device_names;\n\n    if(print_flags & PRINT_DEVICE)\n        printf(\"add device %d: %s\\n\", nfds, device);\n    if(print_flags & PRINT_DEVICE_INFO)\n        printf(\"  bus:      %04x\\n\"\n               \"  vendor    %04x\\n\"\n               \"  product   %04x\\n\"\n               \"  version   %04x\\n\",\n               id.bustype, id.vendor, id.product, id.version);\n    if(print_flags & PRINT_DEVICE_NAME)\n        printf(\"  name:     \\\"%s\\\"\\n\", name);\n    if(print_flags & PRINT_DEVICE_INFO)\n        printf(\"  location: \\\"%s\\\"\\n\"\n               \"  id:       \\\"%s\\\"\\n\", location, idstr);\n    if(print_flags & PRINT_VERSION)\n        printf(\"  version:  %d.%d.%d\\n\",\n               version >> 16, (version >> 8) & 0xff, version & 0xff);\n\n    if(print_flags & PRINT_POSSIBLE_EVENTS) {\n        print_possible_events(fd, print_flags);\n    }\n\n    if(print_flags & PRINT_INPUT_PROPS) {\n        print_input_props(fd);\n    }\n    if(print_flags & PRINT_HID_DESCRIPTOR) {\n        print_hid_descriptor(id.bustype, id.vendor, id.product);\n    }\n\n    ufds[nfds].fd = fd;\n    ufds[nfds].events = POLLIN;\n    device_names[nfds] = strdup(device);\n    nfds++;\n\n    return 0;\n}\n\nint close_device(const char *device, int print_flags)\n{\n    int i;\n    for(i = 1; i < nfds; i++) {\n        if(strcmp(device_names[i], device) == 0) {\n            int count = nfds - i - 1;\n            if(print_flags & PRINT_DEVICE)\n                printf(\"remove device %d: %s\\n\", i, device);\n            free(device_names[i]);\n            memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);\n            memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);\n            nfds--;\n            return 0;\n        }\n    }\n    if(print_flags & PRINT_DEVICE_ERRORS)\n        fprintf(stderr, \"remote device: %s not found\\n\", device);\n    return -1;\n}\n\nstatic int read_notify(const char *dirname, int nfd, int print_flags)\n{\n    int res;\n    char devname[PATH_MAX];\n    char *filename;\n    char event_buf[512];\n    int event_size;\n    int event_pos = 0;\n    struct inotify_event *event;\n\n    res = read(nfd, event_buf, sizeof(event_buf));\n    if(res < (int)sizeof(*event)) {\n        if(errno == EINTR)\n            return 0;\n        fprintf(stderr, \"could not get inotify events, %s\\n\", strerror(errno));\n        return 1;\n    }\n    //printf(\"got %d bytes of event information\\n\", res);\n\n    strcpy(devname, dirname);\n    filename = devname + strlen(devname);\n    *filename++ = '/';\n\n    while(res >= (int)sizeof(*event)) {\n        event = (struct inotify_event *)(event_buf + event_pos);\n        //printf(\"%d: %08x \\\"%s\\\"\\n\", event->wd, event->mask, event->len ? event->name : \"\");\n        if(event->len) {\n            strcpy(filename, event->name);\n            if(event->mask & IN_CREATE) {\n                open_device(devname, print_flags);\n            }\n            else {\n                close_device(devname, print_flags);\n            }\n        }\n        event_size = sizeof(*event) + event->len;\n        res -= event_size;\n        event_pos += event_size;\n    }\n    return 0;\n}\n\nstatic int scan_dir(const char *dirname, int print_flags)\n{\n    char devname[PATH_MAX];\n    char *filename;\n    DIR *dir;\n    struct dirent *de;\n    dir = opendir(dirname);\n    if(dir == NULL)\n        return -1;\n    strcpy(devname, dirname);\n    filename = devname + strlen(devname);\n    *filename++ = '/';\n    while((de = readdir(dir))) {\n        if(de->d_name[0] == '.' &&\n           (de->d_name[1] == '\\0' ||\n            (de->d_name[1] == '.' && de->d_name[2] == '\\0')))\n            continue;\n        strcpy(filename, de->d_name);\n        open_device(devname, print_flags);\n    }\n    closedir(dir);\n    return 0;\n}\n\nstatic void usage(char *name)\n{\n    fprintf(stderr, \"Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\\n\", name);\n    fprintf(stderr, \"    -t: show time stamps\\n\");\n    fprintf(stderr, \"    -n: don't print newlines\\n\");\n    fprintf(stderr, \"    -s: print switch states for given bits\\n\");\n    fprintf(stderr, \"    -S: print all switch states\\n\");\n    fprintf(stderr, \"    -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)\\n\");\n    fprintf(stderr, \"    -d: show HID descriptor, if available\\n\");\n    fprintf(stderr, \"    -p: show possible events (errs, dev, name, pos. events)\\n\");\n    fprintf(stderr, \"    -i: show all device info and possible events\\n\");\n    fprintf(stderr, \"    -l: label event types and names in plain text\\n\");\n    fprintf(stderr, \"    -q: quiet (clear verbosity mask)\\n\");\n    fprintf(stderr, \"    -c: print given number of events then exit\\n\");\n    fprintf(stderr, \"    -r: print rate events are received\\n\");\n}\n\nint getevent_main(int argc, char *argv[])\n{\n    int c;\n    int i;\n    int res;\n    int get_time = 0;\n    int print_device = 0;\n    char *newline = \"\\n\";\n    uint16_t get_switch = 0;\n    struct input_event event;\n    int print_flags = 0;\n    int print_flags_set = 0;\n    int dont_block = -1;\n    int event_count = 0;\n    int sync_rate = 0;\n    int64_t last_sync_time = 0;\n    const char *device = NULL;\n    const char *device_path = \"/dev/input\";\n\n    /* disable buffering on stdout */\n    setbuf(stdout, NULL);\n\n    opterr = 0;\n    do {\n        c = getopt(argc, argv, \"tns:Sv::dpilqc:rh\");\n        if (c == EOF)\n            break;\n        switch (c) {\n        case 't':\n            get_time = 1;\n            break;\n        case 'n':\n            newline = \"\";\n            break;\n        case 's':\n            get_switch = strtoul(optarg, NULL, 0);\n            if(dont_block == -1)\n                dont_block = 1;\n            break;\n        case 'S':\n            get_switch = ~0;\n            if(dont_block == -1)\n                dont_block = 1;\n            break;\n        case 'v':\n            if(optarg)\n                print_flags |= strtoul(optarg, NULL, 0);\n            else\n                print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;\n            print_flags_set = 1;\n            break;\n        case 'd':\n            print_flags |= PRINT_HID_DESCRIPTOR;\n            break;\n        case 'p':\n            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE\n                    | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;\n            print_flags_set = 1;\n            if(dont_block == -1)\n                dont_block = 1;\n            break;\n        case 'i':\n            print_flags |= PRINT_ALL_INFO;\n            print_flags_set = 1;\n            if(dont_block == -1)\n                dont_block = 1;\n            break;\n        case 'l':\n            print_flags |= PRINT_LABELS;\n            break;\n        case 'q':\n            print_flags_set = 1;\n            break;\n        case 'c':\n            event_count = atoi(optarg);\n            dont_block = 0;\n            break;\n        case 'r':\n            sync_rate = 1;\n            break;\n        case '?':\n            fprintf(stderr, \"%s: invalid option -%c\\n\",\n                argv[0], optopt);\n        case 'h':\n            usage(argv[0]);\n            exit(1);\n        }\n    } while (1);\n    if(dont_block == -1)\n        dont_block = 0;\n\n    if (optind + 1 == argc) {\n        device = argv[optind];\n        optind++;\n    }\n    if (optind != argc) {\n        usage(argv[0]);\n        exit(1);\n    }\n    nfds = 1;\n    ufds = calloc(1, sizeof(ufds[0]));\n    ufds[0].fd = inotify_init();\n    ufds[0].events = POLLIN;\n    if(device) {\n        if(!print_flags_set)\n            print_flags |= PRINT_DEVICE_ERRORS;\n        res = open_device(device, print_flags);\n        if(res < 0) {\n            return 1;\n        }\n    } else {\n        if(!print_flags_set)\n            print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;\n        print_device = 1;\n\t\tres = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);\n        if(res < 0) {\n            fprintf(stderr, \"could not add watch for %s, %s\\n\", device_path, strerror(errno));\n            return 1;\n        }\n        res = scan_dir(device_path, print_flags);\n        if(res < 0) {\n            fprintf(stderr, \"scan dir failed for %s\\n\", device_path);\n            return 1;\n        }\n    }\n\n    if(get_switch) {\n        for(i = 1; i < nfds; i++) {\n            uint16_t sw;\n            res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);\n            if(res < 0) {\n                fprintf(stderr, \"could not get switch state, %s\\n\", strerror(errno));\n                return 1;\n            }\n            sw &= get_switch;\n            printf(\"%04x%s\", sw, newline);\n        }\n    }\n\n    if(dont_block)\n        return 0;\n\n    while(1) {\n        //int pollres =\n        poll(ufds, nfds, -1);\n        //printf(\"poll %d, returned %d\\n\", nfds, pollres);\n        if(ufds[0].revents & POLLIN) {\n            read_notify(device_path, ufds[0].fd, print_flags);\n        }\n        for(i = 1; i < nfds; i++) {\n            if(ufds[i].revents) {\n                if(ufds[i].revents & POLLIN) {\n                    res = read(ufds[i].fd, &event, sizeof(event));\n                    if(res < (int)sizeof(event)) {\n                        fprintf(stderr, \"could not get evdev event, %s\\n\", strerror(errno));\n                        return 1;\n                    }\n                    if(get_time) {\n                        printf(\"[%8ld.%06ld] \", event.time.tv_sec, event.time.tv_usec);\n                    }\n                    if(print_device)\n                        printf(\"%s: \", device_names[i]);\n                    print_event(event.type, event.code, event.value, print_flags);\n                    if(sync_rate && event.type == 0 && event.code == 0) {\n                        int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;\n                        if(last_sync_time)\n                            printf(\" rate %lld\", 1000000LL / (now - last_sync_time));\n                        last_sync_time = now;\n                    }\n                    printf(\"%s\", newline);\n                    if(event_count && --event_count == 0)\n                        return 0;\n                }\n            }\n        }\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "toolbox/getprop.cpp",
    "content": "/*\n * Copyright (C) 2017 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <getopt.h>\n#include <sys/system_properties.h>\n\n#include <algorithm>\n#include <iostream>\n#include <string>\n#include <vector>\n\n#include <android-base/properties.h>\n#include <property_info_parser/property_info_parser.h>\n\nusing android::base::GetProperty;\nusing android::properties::PropertyInfoAreaFile;\n\nPropertyInfoAreaFile property_info_file;\n\nenum class ResultType {\n    Value,\n    Context,\n    Type,\n};\n\nvoid PrintAllProperties(ResultType result_type) {\n    std::vector<std::pair<std::string, std::string>> properties;\n    __system_property_foreach(\n        [](const prop_info* pi, void* cookie) {\n            __system_property_read_callback(\n                pi,\n                [](void* cookie, const char* name, const char* value, unsigned) {\n                    auto properties =\n                        reinterpret_cast<std::vector<std::pair<std::string, std::string>>*>(cookie);\n                    properties->emplace_back(name, value);\n                },\n                cookie);\n        },\n        &properties);\n\n    std::sort(properties.begin(), properties.end());\n\n    if (result_type != ResultType::Value) {\n        for (auto& [name, value] : properties) {\n            const char* context = nullptr;\n            const char* type = nullptr;\n            property_info_file->GetPropertyInfo(name.c_str(), &context, &type);\n            if (result_type == ResultType::Context) {\n                value = context;\n            } else {\n                value = type;\n            }\n        }\n    }\n\n    for (const auto& [name, value] : properties) {\n        std::cout << \"[\" << name << \"]: [\" << value << \"]\" << std::endl;\n    }\n}\n\nvoid PrintProperty(const char* name, const char* default_value, ResultType result_type) {\n    switch (result_type) {\n        case ResultType::Value:\n            std::cout << GetProperty(name, default_value) << std::endl;\n            break;\n        case ResultType::Context: {\n            const char* context = nullptr;\n            property_info_file->GetPropertyInfo(name, &context, nullptr);\n            std::cout << context << std::endl;\n            break;\n        }\n        case ResultType::Type: {\n            const char* type = nullptr;\n            property_info_file->GetPropertyInfo(name, nullptr, &type);\n            std::cout << type << std::endl;\n            break;\n        }\n    }\n}\n\nextern \"C\" int getprop_main(int argc, char** argv) {\n    auto result_type = ResultType::Value;\n\n    while (true) {\n        static const struct option long_options[] = {\n            {\"help\", no_argument, nullptr, 'h'},\n            {nullptr, 0, nullptr, 0},\n        };\n\n        int arg = getopt_long(argc, argv, \"TZ\", long_options, nullptr);\n\n        if (arg == -1) {\n            break;\n        }\n\n        switch (arg) {\n            case 'h':\n                std::cout << \"usage: getprop [-TZ] [NAME [DEFAULT]]\\n\"\n                             \"\\n\"\n                             \"Gets an Android system property, or lists them all.\\n\"\n                             \"\\n\"\n                             \"-T\\tShow property types instead of values\\n\"\n                             \"-Z\\tShow property contexts instead of values\\n\"\n                          << std::endl;\n                return 0;\n            case 'T':\n                if (result_type != ResultType::Value) {\n                    std::cerr << \"Only one of -T or -Z may be specified\" << std::endl;\n                    return -1;\n                }\n                result_type = ResultType::Type;\n                break;\n            case 'Z':\n                if (result_type != ResultType::Value) {\n                    std::cerr << \"Only one of -T or -Z may be specified\" << std::endl;\n                    return -1;\n                }\n                result_type = ResultType::Context;\n                break;\n            case '?':\n                return -1;\n            default:\n                std::cerr << \"getprop: getopt returned invalid result: \" << arg << std::endl;\n                return -1;\n        }\n    }\n\n    if (result_type != ResultType::Value) {\n        property_info_file.LoadDefaultPath();\n        if (!property_info_file) {\n            std::cerr << \"Unable to load property info file\" << std::endl;\n            return -1;\n        }\n    }\n\n    if (optind >= argc) {\n        PrintAllProperties(result_type);\n        return 0;\n    }\n\n    if (optind < argc - 2) {\n        std::cerr << \"getprop: Max 2 arguments (see \\\"getprop --help\\\")\" << std::endl;\n        return -1;\n    }\n\n    PrintProperty(argv[optind], (optind == argc - 1) ? \"\" : argv[optind + 1], result_type);\n\n    return 0;\n}\n"
  },
  {
    "path": "toolbox/modprobe.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <ctype.h>\n#include <getopt.h>\n#include <stdlib.h>\n#include <unistd.h>\n\n#include <string>\n\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/macros.h>\n#include <android-base/strings.h>\n#include <android-base/stringprintf.h>\n#include <modprobe/modprobe.h>\n\n#include <sys/utsname.h>\n\nnamespace {\n\nenum modprobe_mode {\n    AddModulesMode,\n    RemoveModulesMode,\n    ListModulesMode,\n    ShowDependenciesMode,\n};\n\nvoid print_usage(void) {\n    LOG(INFO) << \"Usage:\";\n    LOG(INFO);\n    LOG(INFO) << \"  modprobe [options] [-d DIR] [--all=FILE|MODULE]...\";\n    LOG(INFO) << \"  modprobe [options] [-d DIR] MODULE [symbol=value]...\";\n    LOG(INFO);\n    LOG(INFO) << \"Options:\";\n    LOG(INFO) << \"  --all=FILE: FILE to acquire module names from\";\n    LOG(INFO) << \"  -b, --use-blocklist: Apply blocklist to module names too\";\n    LOG(INFO) << \"  -d, --dirname=DIR: Load modules from DIR, option may be used multiple times\";\n    LOG(INFO) << \"  -D, --show-depends: Print dependencies for modules only, do not load\";\n    LOG(INFO) << \"  -h, --help: Print this help\";\n    LOG(INFO) << \"  -l, --list: List modules matching pattern\";\n    LOG(INFO) << \"  -r, --remove: Remove MODULE (multiple modules may be specified)\";\n    LOG(INFO) << \"  -s, --syslog: print to syslog also\";\n    LOG(INFO) << \"  -q, --quiet: disable messages\";\n    LOG(INFO) << \"  -v, --verbose: enable more messages, even more with a second -v\";\n    LOG(INFO);\n}\n\n#define check_mode()                                   \\\n    if (mode != AddModulesMode) {                      \\\n        LOG(ERROR) << \"multiple mode flags specified\"; \\\n        print_usage();                                 \\\n        return EXIT_FAILURE;                           \\\n    }\n\nstd::string stripComments(const std::string& str) {\n    for (std::string rv = str;;) {\n        auto comment = rv.find('#');\n        if (comment == std::string::npos) return rv;\n        auto end = rv.find('\\n', comment);\n        if (end != std::string::npos) end = end - comment;\n        rv.erase(comment, end);\n    }\n    /* NOTREACHED */\n}\n\nauto syslog = false;\n\nvoid MyLogger(android::base::LogId id, android::base::LogSeverity severity, const char* tag,\n              const char* file, unsigned int line, const char* message) {\n    android::base::StdioLogger(id, severity, tag, file, line, message);\n    if (syslog && message[0]) {\n        android::base::KernelLogger(id, severity, tag, file, line, message);\n    }\n}\n\nstatic bool ModDirMatchesKernelPageSize(const char* mod_dir) {\n    static const unsigned int kernel_pgsize_kb = getpagesize() / 1024;\n    unsigned int mod_pgsize_kb = 16;  // 16k default since android15-6.6\n\n    if (mod_dir && strstr(mod_dir, \"-4k\") != NULL) {\n        mod_pgsize_kb = 4;\n    }\n\n    return kernel_pgsize_kb == mod_pgsize_kb;\n}\n\nstatic bool ModDirMatchesKernelPageSizeLegacy(const char* mod_dir) {\n    static const unsigned int kernel_pgsize_kb = getpagesize() / 1024;\n    const char* mod_sfx = strrchr(mod_dir, '_');\n    unsigned int mod_pgsize_kb;\n    int mod_sfx_len;\n\n    if (mod_sfx == NULL || sscanf(mod_sfx, \"_%uk%n\", &mod_pgsize_kb, &mod_sfx_len) != 1 ||\n        strlen(mod_sfx) != mod_sfx_len) {\n        mod_pgsize_kb = 4;\n    }\n\n    return kernel_pgsize_kb == mod_pgsize_kb;\n}\n\n// Find directories in format of \"/lib/modules/x.y.z-*\".\nstatic int KernelVersionNameFilter(const dirent* de) {\n    static unsigned int major, minor;\n    static std::string kernel_version;\n    utsname uts;\n\n    if (kernel_version.empty()) {\n        if ((uname(&uts) != 0) || (sscanf(uts.release, \"%u.%u\", &major, &minor) != 2)) {\n            LOG(ERROR) << \"Could not parse the kernel version from uname\";\n            return 0;\n        }\n        kernel_version = android::base::StringPrintf(\"%u.%u\", major, minor);\n    }\n\n    if (android::base::StartsWith(de->d_name, kernel_version)) {\n        // Check for GKI to avoid breaking non-GKI Android devices.\n        if (UNLIKELY(strstr(de->d_name, \"-android\") == NULL)) {\n            // For non-GKI, just match when the major and minor versions match.\n            return 1;\n        }\n\n        // For android15-6.6 and later, GKI adds `-4k` to the UTS release\n        // string to identify 4kb page size kernels. If there is no page size\n        // suffix, then the kernel page size is 16kb.\n        if (major > 6 || (major == 6 && minor >= 6)) {\n            return ModDirMatchesKernelPageSize(de->d_name);\n        } else {\n            return ModDirMatchesKernelPageSizeLegacy(de->d_name);\n        }\n    }\n    return 0;\n}\n\n}  // anonymous namespace\n\nextern \"C\" int modprobe_main(int argc, char** argv) {\n    android::base::InitLogging(argv, MyLogger);\n    android::base::SetMinimumLogSeverity(android::base::INFO);\n\n    std::vector<std::string> modules;\n    std::string modules_load_file;\n    std::string module_parameters;\n    std::string mods;\n    std::vector<std::string> mod_dirs;\n    modprobe_mode mode = AddModulesMode;\n    bool blocklist = false;\n    int rv = EXIT_SUCCESS;\n\n    int opt, fd;\n    int option_index = 0;\n    // NB: We have non-standard short options -l and -D to make it easier for\n    // OEMs to transition from toybox.\n    // clang-format off\n    static struct option long_options[] = {\n        { \"all\",                 optional_argument, 0, 'a' },\n        { \"use-blocklist\",       no_argument,       0, 'b' },\n        { \"dirname\",             required_argument, 0, 'd' },\n        { \"show-depends\",        no_argument,       0, 'D' },\n        { \"help\",                no_argument,       0, 'h' },\n        { \"list\",                no_argument,       0, 'l' },\n        { \"quiet\",               no_argument,       0, 'q' },\n        { \"remove\",              no_argument,       0, 'r' },\n        { \"syslog\",              no_argument,       0, 's' },\n        { \"verbose\",             no_argument,       0, 'v' },\n    };\n    // clang-format on\n    while ((opt = getopt_long(argc, argv, \"a::bd:Dhlqrsv\", long_options, &option_index)) != -1) {\n        switch (opt) {\n            case 'a':\n                // toybox modprobe supported -a to load multiple modules, this\n                // is supported here by default, ignore flag if no argument.\n                check_mode();\n                if (optarg == NULL) break;\n\n                // Since libmodprobe doesn't fail when the modules load file\n                // doesn't exist, let's check that here so that we don't\n                // silently fail.\n                fd = open(optarg, O_RDONLY | O_CLOEXEC | O_BINARY);\n                if (fd == -1) {\n                    PLOG(ERROR) << \"Failed to open \" << optarg;\n                    return EXIT_FAILURE;\n                }\n                close(fd);\n\n                mod_dirs.emplace_back(android::base::Dirname(optarg));\n                modules_load_file = android::base::Basename(optarg);\n                break;\n            case 'b':\n                blocklist = true;\n                break;\n            case 'd':\n                mod_dirs.emplace_back(optarg);\n                break;\n            case 'D':\n                check_mode();\n                mode = ShowDependenciesMode;\n                break;\n            case 'h':\n                android::base::SetMinimumLogSeverity(android::base::INFO);\n                print_usage();\n                return rv;\n            case 'l':\n                check_mode();\n                mode = ListModulesMode;\n                break;\n            case 'q':\n                android::base::SetMinimumLogSeverity(android::base::WARNING);\n                break;\n            case 'r':\n                check_mode();\n                mode = RemoveModulesMode;\n                break;\n            case 's':\n                syslog = true;\n                break;\n            case 'v':\n                if (android::base::GetMinimumLogSeverity() <= android::base::DEBUG) {\n                    android::base::SetMinimumLogSeverity(android::base::VERBOSE);\n                } else {\n                    android::base::SetMinimumLogSeverity(android::base::DEBUG);\n                }\n                break;\n            default:\n                LOG(ERROR) << \"Unrecognized option: \" << opt;\n                print_usage();\n                return EXIT_FAILURE;\n        }\n    }\n\n    int parameter_count = 0;\n    for (opt = optind; opt < argc; opt++) {\n        if (!strchr(argv[opt], '=')) {\n            modules.emplace_back(argv[opt]);\n        } else {\n            parameter_count++;\n            if (module_parameters.empty()) {\n                module_parameters = argv[opt];\n            } else {\n                module_parameters = module_parameters + \" \" + argv[opt];\n            }\n        }\n    }\n\n    if (mod_dirs.empty()) {\n        static constexpr auto LIB_MODULES_PREFIX = \"/lib/modules/\";\n        dirent** kernel_dirs = NULL;\n\n        int n = scandir(LIB_MODULES_PREFIX, &kernel_dirs, KernelVersionNameFilter, NULL);\n        if (n == -1) {\n            PLOG(ERROR) << \"Failed to scan dir \" << LIB_MODULES_PREFIX;\n            return EXIT_FAILURE;\n        } else if (n > 0) {\n            while (n--) {\n                mod_dirs.emplace_back(LIB_MODULES_PREFIX + std::string(kernel_dirs[n]->d_name));\n            }\n        }\n        free(kernel_dirs);\n\n        if (mod_dirs.empty() || getpagesize() == 4096) {\n            // Allow modules to be directly inside /lib/modules\n            mod_dirs.emplace_back(LIB_MODULES_PREFIX);\n        }\n    }\n\n    LOG(DEBUG) << \"mode is \" << mode;\n    LOG(DEBUG) << \"mod_dirs is: \" << android::base::Join(mod_dirs, \" \");\n    LOG(DEBUG) << \"modules is: \" << android::base::Join(modules, \" \");\n    LOG(DEBUG) << \"modules load file is: \" << modules_load_file;\n    LOG(DEBUG) << \"module parameters is: \" << android::base::Join(module_parameters, \" \");\n\n    if (modules.empty()) {\n        if (mode == ListModulesMode) {\n            // emulate toybox modprobe list with no pattern (list all)\n            modules.emplace_back(\"*\");\n        } else if (modules_load_file.empty()) {\n            LOG(ERROR) << \"No modules given.\";\n            print_usage();\n            return EXIT_FAILURE;\n        }\n    }\n    if (parameter_count && (modules.size() > 1 || !modules_load_file.empty())) {\n        LOG(ERROR) << \"Only one module may be loaded when specifying module parameters.\";\n        print_usage();\n        return EXIT_FAILURE;\n    }\n\n    Modprobe m(mod_dirs, modules_load_file.empty() ? \"modules.load\" : modules_load_file, blocklist);\n    if (mode == AddModulesMode && !modules_load_file.empty()) {\n        if (!m.LoadListedModules(false)) {\n            PLOG(ERROR) << \"Failed to load all the modules from \" << modules_load_file;\n            return EXIT_FAILURE;\n        }\n        /* Fall-through to load modules provided on the command line (if any)*/\n    }\n\n    for (const auto& module : modules) {\n        switch (mode) {\n            case AddModulesMode:\n                if (!m.LoadWithAliases(module, true, module_parameters)) {\n                    if (m.IsBlocklisted(module)) continue;\n                    PLOG(ERROR) << \"Failed to load module \" << module;\n                    rv = EXIT_FAILURE;\n                }\n                break;\n            case RemoveModulesMode:\n                if (!m.Remove(module)) {\n                    PLOG(ERROR) << \"Failed to remove module \" << module;\n                    rv = EXIT_FAILURE;\n                }\n                break;\n            case ListModulesMode: {\n                std::vector<std::string> list = m.ListModules(module);\n                LOG(INFO) << android::base::Join(list, \"\\n\");\n                break;\n            }\n            case ShowDependenciesMode: {\n                std::vector<std::string> pre_deps;\n                std::vector<std::string> deps;\n                std::vector<std::string> post_deps;\n                if (!m.GetAllDependencies(module, &pre_deps, &deps, &post_deps)) {\n                    rv = EXIT_FAILURE;\n                    break;\n                }\n                LOG(INFO) << \"Dependencies for \" << module << \":\";\n                LOG(INFO) << \"Soft pre-dependencies:\";\n                LOG(INFO) << android::base::Join(pre_deps, \"\\n\");\n                LOG(INFO) << \"Hard dependencies:\";\n                LOG(INFO) << android::base::Join(deps, \"\\n\");\n                LOG(INFO) << \"Soft post-dependencies:\";\n                LOG(INFO) << android::base::Join(post_deps, \"\\n\");\n                break;\n            }\n            default:\n                LOG(ERROR) << \"Bad mode\";\n                rv = EXIT_FAILURE;\n        }\n    }\n\n    return rv;\n}\n"
  },
  {
    "path": "toolbox/setprop.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <ctype.h>\n#include <stdlib.h>\n#include <sys/system_properties.h>\n\n#include <iostream>\n\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n\nusing android::base::SetProperty;\nusing android::base::StartsWith;\n\nextern \"C\" int setprop_main(int argc, char** argv) {\n    if (argc != 3) {\n        std::cout << \"usage: setprop NAME VALUE\\n\"\n                     \"\\n\"\n                     \"Sets an Android system property.\"\n                  << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    auto name = std::string{argv[1]};\n    auto value = std::string{argv[2]};\n\n    // SetProperty() doesn't tell us why it failed, and actually can't recognize most failures, so\n    // we duplicate some of init's checks here to help the user.\n\n    if (name.front() == '.' || name.back() == '.') {\n        std::cerr << \"Property names must not start or end with a '.'\" << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    if (name.find(\"..\") != std::string::npos) {\n        std::cerr << \"'..' is not allowed in a property name\" << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    for (const auto& c : name) {\n        if (!isalnum(c) && !strchr(\":@_.-\", c)) {\n            std::cerr << \"Invalid character '\" << c << \"' in name '\" << name << \"'\" << std::endl;\n            return EXIT_FAILURE;\n        }\n    }\n\n    if (value.size() >= PROP_VALUE_MAX && !StartsWith(name, \"ro.\")) {\n        std::cerr << \"Value '\" << value << \"' is too long, \" << value.size()\n                  << \" bytes vs a max of \" << PROP_VALUE_MAX << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {\n        std::cerr << \"Value '\" << value << \"' is not a UTF8 encoded string\" << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    if (!SetProperty(name, value)) {\n        std::cerr << \"Failed to set property '\" << name << \"' to '\" << value\n                  << \"'.\\nSee dmesg for error reason.\" << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}"
  },
  {
    "path": "toolbox/start.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdlib.h>\n#include <unistd.h>\n\n#include <iostream>\n#include <string>\n#include <vector>\n\n#include <android-base/properties.h>\n\nusing android::base::GetProperty;\nusing android::base::SetProperty;\nusing namespace std::literals;\n\nstatic void ControlService(bool start, const std::string& service) {\n    if (!android::base::SetProperty(start ? \"ctl.start\" : \"ctl.stop\", service)) {\n        std::cerr << \"Unable to \" << (start ? \"start\" : \"stop\") << \" service '\" << service\n                  << \"'\\nSee dmesg for error reason.\" << std::endl;\n        exit(EXIT_FAILURE);\n    }\n}\n\nstatic void ControlDefaultServices(bool start) {\n    std::vector<std::string> services = {\n        \"netd\",\n        \"surfaceflinger\",\n        \"audioserver\",\n        \"zygote\",\n    };\n\n    // Only start zygote_secondary if not single arch.\n    std::string zygote_configuration = GetProperty(\"ro.zygote\", \"\");\n    if (zygote_configuration != \"zygote32\" && zygote_configuration != \"zygote64\") {\n        services.emplace_back(\"zygote_secondary\");\n    }\n\n    if (start) {\n        for (const auto& service : services) {\n            ControlService(true, service);\n        }\n    } else {\n        for (auto it = services.crbegin(); it != services.crend(); ++it) {\n            ControlService(false, *it);\n        }\n    }\n}\n\nstatic int StartStop(int argc, char** argv, bool start) {\n    if (getuid()) {\n        std::cerr << \"Must be root\" << std::endl;\n        return EXIT_FAILURE;\n    }\n\n    if (argc == 1) {\n        ControlDefaultServices(start);\n    }\n\n    if (argc == 2 && argv[1] == \"--help\"s) {\n        std::cout << \"usage: \" << (start ? \"start\" : \"stop\")\n                  << \" [SERVICE...]\\n\"\n                     \"\\n\"\n                  << (start ? \"Starts\" : \"Stops\")\n                  << \" the given system service, or netd/surfaceflinger/zygotes.\" << std::endl;\n        return EXIT_SUCCESS;\n    }\n\n    for (int i = 1; i < argc; ++i) {\n        ControlService(start, argv[i]);\n    }\n    return EXIT_SUCCESS;\n}\n\nextern \"C\" int start_main(int argc, char** argv) {\n    return StartStop(argc, argv, true);\n}\n\nextern \"C\" int stop_main(int argc, char** argv) {\n    return StartStop(argc, argv, false);\n}\n"
  },
  {
    "path": "toolbox/toolbox.c",
    "content": "#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#define TOOL(name) int name##_main(int, char**);\n#include \"tools.h\"\n#undef TOOL\n\nstatic struct {\n    const char* name;\n    int (*func)(int, char**);\n} tools[] = {\n#define TOOL(name) { #name, name##_main },\n#include \"tools.h\"\n#undef TOOL\n    { 0, 0 },\n};\n\nstatic void SIGPIPE_handler(int signal) {\n    // Those desktop Linux tools that catch SIGPIPE seem to agree that it's\n    // a successful way to exit, not a failure. (Which makes sense --- we were\n    // told to stop by a reader, rather than failing to continue ourselves.)\n    _exit(0);\n}\n\nint main(int argc, char** argv) {\n    // Let's assume that none of this code handles broken pipes. At least ls,\n    // ps, and top were broken (though I'd previously added this fix locally\n    // to top). We exit rather than use SIG_IGN because tools like top will\n    // just keep on writing to nowhere forever if we don't stop them.\n    signal(SIGPIPE, SIGPIPE_handler);\n\n    char* cmd = strrchr(argv[0], '/');\n    char* name = cmd ? (cmd + 1) : argv[0];\n\n    for (size_t i = 0; tools[i].name; i++) {\n        if (!strcmp(tools[i].name, name)) {\n            return tools[i].func(argc, argv);\n        }\n    }\n\n    printf(\"%s: no such tool\\n\", argv[0]);\n    return 127;\n}\n\nint toolbox_main(int argc, char** argv) {\n    // \"toolbox foo ...\" is equivalent to \"foo ...\"\n    if (argc > 1) {\n        return main(argc - 1, argv + 1);\n    }\n\n    // Plain \"toolbox\" lists the tools.\n    for (size_t i = 1; tools[i].name; i++) {\n        printf(\"%s%c\", tools[i].name, tools[i+1].name ? ' ' : '\\n');\n    }\n    return 0;\n}\n"
  },
  {
    "path": "toolbox/tools.h",
    "content": "TOOL(getevent)\nTOOL(getprop)\nTOOL(modprobe)\nTOOL(setprop)\nTOOL(start)\nTOOL(stop)\nTOOL(toolbox)\n"
  },
  {
    "path": "trusty/Android.bp",
    "content": "//\n// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n"
  },
  {
    "path": "trusty/OWNERS",
    "content": "# include OWNERS from the top level trusty repo\ninclude trusty:main:/OWNERS\n\nmikemcternan@google.com\nswillden@google.com"
  },
  {
    "path": "trusty/apploader/Android.bp",
    "content": "//\n// Copyright (C) 2020 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"trusty_apploader\",\n    vendor: true,\n\n    srcs: [\n        \"apploader.cpp\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"libc\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libdmabufheap\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n"
  },
  {
    "path": "trusty/apploader/apploader.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"TrustyAppLoader\"\n\n#include <BufferAllocator/BufferAllocator.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <assert.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <sys/sendfile.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <trusty/tipc.h>\n#include <unistd.h>\n#include <algorithm>\n#include <string>\n\n#include \"apploader_ipc.h\"\n\nusing android::base::unique_fd;\nusing std::string;\n\nconstexpr const char kTrustyDefaultDeviceName[] = \"/dev/trusty-ipc-dev0\";\n\nstatic const char* dev_name = kTrustyDefaultDeviceName;\n\nstatic const char* _sopts = \"hD:\";\nstatic const struct option _lopts[] = {\n        {\"help\", no_argument, 0, 'h'},\n        {\"dev\", required_argument, 0, 'D'},\n        {0, 0, 0, 0},\n};\n\nstatic const char* usage =\n        \"Usage: %s [options] package-file\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h, --help            prints this message and exit\\n\"\n        \"  -D, --dev name        Trusty device name\\n\"\n        \"\\n\";\n\nstatic void print_usage_and_exit(const char* prog, int code) {\n    fprintf(stderr, usage, prog);\n    exit(code);\n}\n\nstatic void parse_options(int argc, char** argv) {\n    int c;\n    int oidx = 0;\n\n    while (1) {\n        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);\n        if (c == -1) {\n            break; /* done */\n        }\n\n        switch (c) {\n            case 'h':\n                print_usage_and_exit(argv[0], EXIT_SUCCESS);\n                break;\n\n            case 'D':\n                dev_name = strdup(optarg);\n                break;\n\n            default:\n                print_usage_and_exit(argv[0], EXIT_FAILURE);\n        }\n    }\n}\n\nstatic unique_fd read_file(const char* file_name, off64_t* out_file_size) {\n    int rc;\n    long page_size = sysconf(_SC_PAGESIZE);\n    off64_t file_size, file_page_offset, file_page_size;\n    struct stat64 st;\n\n    unique_fd file_fd(TEMP_FAILURE_RETRY(open(file_name, O_RDONLY)));\n    if (!file_fd.ok()) {\n        PLOG(ERROR) << \"Error opening file \" << file_name;\n        return {};\n    }\n\n    rc = fstat64(file_fd, &st);\n    if (rc < 0) {\n        PLOG(ERROR) << \"Error calling stat on file '\" << file_name << \"'\";\n        return {};\n    }\n\n    if (st.st_size == 0) {\n        LOG(ERROR) << \"Zero length file '\" << file_name << \"'\";\n        return {};\n    }\n\n    file_size = st.st_size;\n\n    /* The dmabuf size needs to be a multiple of the page size */\n    file_page_offset = file_size & (page_size - 1);\n    if (file_page_offset) {\n        file_page_offset = page_size - file_page_offset;\n    }\n    if (__builtin_add_overflow(file_size, file_page_offset, &file_page_size)) {\n        LOG(ERROR) << \"Failed to page-align file size\";\n        return {};\n    }\n\n    BufferAllocator alloc;\n    unique_fd dmabuf_fd(alloc.Alloc(kDmabufSystemHeapName, file_page_size));\n    if (!dmabuf_fd.ok()) {\n        LOG(ERROR) << \"Error creating dmabuf for \" << file_page_size\n                   << \" bytes: \" << dmabuf_fd.get();\n        return dmabuf_fd;\n    }\n\n    void* shm = mmap(0, file_page_size, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd, 0);\n    if (shm == MAP_FAILED) {\n        return {};\n    }\n\n    off64_t file_offset = 0;\n    while (file_offset < file_size) {\n        ssize_t num_read = TEMP_FAILURE_RETRY(\n                pread(file_fd, (char*)shm + file_offset, file_size - file_offset, file_offset));\n\n        if (num_read < 0) {\n            PLOG(ERROR) << \"Error reading package file '\" << file_name << \"'\";\n            break;\n        }\n\n        if (num_read == 0) {\n            LOG(ERROR) << \"Unexpected end of file '\" << file_name << \"'\";\n            break;\n        }\n\n        file_offset += (off64_t)num_read;\n    }\n\n    munmap(shm, file_page_size);\n\n    if (file_offset < file_size) {\n        return {};\n    }\n\n    assert(file_offset == file_size);\n    if (out_file_size) {\n        *out_file_size = file_size;\n    }\n\n    return dmabuf_fd;\n}\n\nstatic ssize_t send_load_message(int tipc_fd, int package_fd, off64_t package_size) {\n    struct apploader_header hdr = {\n            .cmd = APPLOADER_CMD_LOAD_APPLICATION,\n    };\n    struct apploader_load_app_req req = {\n            .package_size = static_cast<uint64_t>(package_size),\n    };\n    struct iovec tx[2] = {{&hdr, sizeof(hdr)}, {&req, sizeof(req)}};\n    struct trusty_shm shm = {\n            .fd = package_fd,\n            .transfer = TRUSTY_SHARE,\n    };\n    return tipc_send(tipc_fd, tx, 2, &shm, 1);\n}\n\nstatic ssize_t read_response(int tipc_fd) {\n    struct apploader_resp resp;\n    ssize_t rc = read(tipc_fd, &resp, sizeof(resp));\n    if (rc < 0) {\n        PLOG(ERROR) << \"Failed to read response\";\n        return rc;\n    }\n\n    if (rc < sizeof(resp)) {\n        LOG(ERROR) << \"Not enough data in response: \" << rc;\n        return -EIO;\n    }\n\n    if (resp.hdr.cmd != (APPLOADER_CMD_LOAD_APPLICATION | APPLOADER_RESP_BIT)) {\n        LOG(ERROR) << \"Invalid command in response: \" << resp.hdr.cmd;\n        return -EINVAL;\n    }\n\n    switch (resp.error) {\n        case APPLOADER_NO_ERROR:\n            break;\n        case APPLOADER_ERR_UNKNOWN_CMD:\n            LOG(ERROR) << \"Error: unknown command\";\n            break;\n        case APPLOADER_ERR_INVALID_CMD:\n            LOG(ERROR) << \"Error: invalid command arguments\";\n            break;\n        case APPLOADER_ERR_NO_MEMORY:\n            LOG(ERROR) << \"Error: out of Trusty memory\";\n            break;\n        case APPLOADER_ERR_VERIFICATION_FAILED:\n            LOG(ERROR) << \"Error: failed to verify the package\";\n            break;\n        case APPLOADER_ERR_LOADING_FAILED:\n            LOG(ERROR) << \"Error: failed to load the package\";\n            break;\n        case APPLOADER_ERR_ALREADY_EXISTS:\n            LOG(ERROR) << \"Error: application already exists\";\n            break;\n        case APPLOADER_ERR_INTERNAL:\n            LOG(ERROR) << \"Error: internal apploader error\";\n            break;\n        case APPLOADER_ERR_INVALID_VERSION:\n            LOG(ERROR) << \"Error: invalid application version\";\n            break;\n        case APPLOADER_ERR_POLICY_VIOLATION:\n            LOG(ERROR) << \"Error: loading denied by policy engine\";\n            break;\n        case APPLOADER_ERR_NOT_ENCRYPTED:\n            LOG(ERROR) << \"Error: unmet application encryption requirement\";\n            break;\n        default:\n            LOG(ERROR) << \"Unrecognized error: \" << resp.error;\n            break;\n    }\n\n    return static_cast<ssize_t>(resp.error);\n}\n\nstatic ssize_t send_app_package(const char* package_file_name) {\n    ssize_t rc = 0;\n    int tipc_fd = -1;\n    off64_t package_size;\n\n    unique_fd package_fd = read_file(package_file_name, &package_size);\n    if (!package_fd.ok()) {\n        rc = -1;\n        goto err_read_file;\n    }\n\n    tipc_fd = tipc_connect(dev_name, APPLOADER_PORT);\n    if (tipc_fd < 0) {\n        LOG(ERROR) << \"Failed to connect to Trusty app loader: \" << strerror(-tipc_fd);\n        // print this to stderr too to avoid silently exiting when run as non-root\n        fprintf(stderr, \"Failed to connect to Trusty app loader: %s\\n\", strerror(-tipc_fd));\n        rc = tipc_fd;\n        goto err_tipc_connect;\n    }\n\n    rc = send_load_message(tipc_fd, package_fd, package_size);\n    if (rc < 0) {\n        LOG(ERROR) << \"Failed to send package: \" << rc;\n        goto err_send;\n    }\n\n    rc = read_response(tipc_fd);\n\nerr_send:\n    tipc_close(tipc_fd);\nerr_tipc_connect:\nerr_read_file:\n    return rc;\n}\n\nint main(int argc, char** argv) {\n    parse_options(argc, argv);\n    if (optind + 1 != argc) {\n        print_usage_and_exit(argv[0], EXIT_FAILURE);\n    }\n\n    int rc = send_app_package(argv[optind]);\n    return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n"
  },
  {
    "path": "trusty/apploader/apploader_ipc.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#define APPLOADER_PORT \"com.android.trusty.apploader\"\n\nenum apploader_command : uint32_t {\n    APPLOADER_REQ_SHIFT = 1,\n    APPLOADER_RESP_BIT = 1,\n\n    APPLOADER_CMD_LOAD_APPLICATION = (0 << APPLOADER_REQ_SHIFT),\n    APPLOADER_CMD_GET_VERSION = (1 << APPLOADER_REQ_SHIFT),\n    APPLOADER_CMD_UNLOAD_APPLICATION = (2 << APPLOADER_REQ_SHIFT),\n};\n\n/**\n * enum apploader_error - error codes for apploader\n * @APPLOADER_NO_ERROR:                 no error\n * @APPLOADER_ERR_UNKNOWN_CMD:          unknown or not implemented command\n * @APPLOADER_ERR_INVALID_CMD:          invalid arguments or inputs passed to\n *                                      command\n * @APPLOADER_ERR_NO_MEMORY:            failed to allocate memory\n * @APPLOADER_ERR_VERIFICATION_FAILED:  failed to verify input application\n *                                      package for any reason, e.g., signature\n *                                      verification failed\n * @APPLOADER_ERR_LOADING_FAILED:       Trusty kernel or apploader service\n *                                      failed to load application\n * @APPLOADER_ERR_ALREADY_EXISTS:       application has already been loaded\n * @APPLOADER_ERR_INTERNAL:             miscellaneous or internal apploader\n *                                      error not covered by the above\n * @APPLOADER_ERR_INVALID_VERSION:      invalid application version\n * @APPLOADER_ERR_POLICY_VIOLATION:     signature verification succeeded but\n *                                      key+manifest combination not allowed\n *                                      by app loader policy engine\n * @APPLOADER_ERR_NOT_ENCRYPTED:        unmet application encryption requirement\n */\nenum apploader_error : uint32_t {\n    APPLOADER_NO_ERROR = 0,\n    APPLOADER_ERR_UNKNOWN_CMD,\n    APPLOADER_ERR_INVALID_CMD,\n    APPLOADER_ERR_NO_MEMORY,\n    APPLOADER_ERR_VERIFICATION_FAILED,\n    APPLOADER_ERR_LOADING_FAILED,\n    APPLOADER_ERR_ALREADY_EXISTS,\n    APPLOADER_ERR_INTERNAL,\n    APPLOADER_ERR_INVALID_VERSION,\n    APPLOADER_ERR_POLICY_VIOLATION,\n    APPLOADER_ERR_NOT_ENCRYPTED,\n};\n\n/**\n * apploader_header - Serial header for communicating with apploader\n * @cmd: the command; one of &enum apploader_command values.\n */\nstruct apploader_header {\n    uint32_t cmd;\n} __packed;\n\n/**\n * apploader_load_app_req - Serial arguments for LOAD_APPLICATION command\n * @package_size: size of the application package.\n *\n * Load an application from a given memory region. The request message also\n * contains a handle for a memfd that contains the application package.\n *\n * The response is a &struct apploader_resp with the error code or\n * %APPLOADER_NO_ERROR on success.\n */\nstruct apploader_load_app_req {\n    uint64_t package_size;\n} __packed;\n\n/**\n * apploader_resp - Common header for all apploader responses\n * @hdr - header with command value.\n * @error - error code returned by peer; one of &enum apploader_error values.\n *\n * This structure is followed by the response-specific payload, if the command\n * has one.\n */\nstruct apploader_resp {\n    struct apploader_header hdr;\n    uint32_t error;\n} __packed;\n"
  },
  {
    "path": "trusty/apploader/fuzz/Android.bp",
    "content": "// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\n// Fuzz Trusty IPC messages sent to apploader.\ncc_fuzz {\n    name: \"trusty_apploader_tipc_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\":trusty_tipc_fuzzer\"],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.apploader\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"081ba88f-f1ee-452e-b5e8-a7e9ef173a97\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"apploader.syms.elf\\\"\",\n    ],\n    fuzz_config: {\n       cc: [\"trong@google.com\"],\n    },\n}\n\n// Fuzz app package sent to apploader.\ncc_fuzz {\n    name: \"trusty_apploader_app_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\"app_fuzzer.cpp\"],\n    include_dirs: [\"system/core/trusty/apploader\"],\n    shared_libs: [\n        \"libdmabufheap\",\n    ],\n    fuzz_config: {\n       cc: [\"trong@google.com\"],\n    },\n}\n"
  },
  {
    "path": "trusty/apploader/fuzz/app_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <BufferAllocator/BufferAllocator.h>\n#include <android-base/unique_fd.h>\n#include <apploader_ipc.h>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <trusty/coverage/coverage.h>\n#include <trusty/fuzz/counters.h>\n#include <trusty/fuzz/utils.h>\n#include <trusty/tipc.h>\n#include <unistd.h>\n#include <iostream>\n\nusing android::base::unique_fd;\nusing android::trusty::coverage::CoverageRecord;\nusing android::trusty::fuzz::ExtraCounters;\nusing android::trusty::fuzz::TrustyApp;\n\n#define TIPC_DEV \"/dev/trusty-ipc-dev0\"\n#define APPLOADER_PORT \"com.android.trusty.apploader\"\n#define APPLOADER_MODULE_NAME \"apploader.syms.elf\"\n\n/* Apploader TA's UUID is 081ba88f-f1ee-452e-b5e8-a7e9ef173a97 */\nstatic struct uuid apploader_uuid = {\n        0x081ba88f,\n        0xf1ee,\n        0x452e,\n        {0xb5, 0xe8, 0xa7, 0xe9, 0xef, 0x17, 0x3a, 0x97},\n};\n\nstatic bool SendLoadMsg(int chan, int dma_buf, size_t dma_buf_size) {\n    apploader_header hdr = {\n            .cmd = APPLOADER_CMD_LOAD_APPLICATION,\n    };\n    apploader_load_app_req req = {\n            .package_size = static_cast<uint64_t>(dma_buf_size),\n    };\n    iovec iov[] = {\n            {\n                    .iov_base = &hdr,\n                    .iov_len = sizeof(hdr),\n            },\n            {\n                    .iov_base = &req,\n                    .iov_len = sizeof(req),\n            },\n    };\n    trusty_shm shm = {\n            .fd = dma_buf,\n            .transfer = TRUSTY_SHARE,\n    };\n\n    int rc = tipc_send(chan, iov, 2, &shm, 1);\n    if (rc != static_cast<int>(sizeof(hdr) + sizeof(req))) {\n        std::cerr << \"Failed to send request\" << std::endl;\n        return false;\n    }\n\n    apploader_resp resp;\n    rc = read(chan, &resp, sizeof(resp));\n    if (rc != static_cast<int>(sizeof(resp))) {\n        std::cerr << \"Failed to receive response\" << std::endl;\n        return false;\n    }\n\n    return true;\n}\n\nstatic CoverageRecord record(TIPC_DEV, &apploader_uuid, APPLOADER_MODULE_NAME);\n\nextern \"C\" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {\n    auto ret = record.Open();\n    if (!ret.ok()) {\n        std::cerr << ret.error() << std::endl;\n        exit(-1);\n    }\n    return 0;\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    ExtraCounters counters(&record);\n    counters.Reset();\n\n    android::trusty::fuzz::TrustyApp ta(TIPC_DEV, APPLOADER_PORT);\n    auto ret = ta.Connect();\n    if (!ret.ok()) {\n        std::cerr << ret.error() << std::endl;\n        android::trusty::fuzz::Abort();\n    }\n\n    uint64_t shm_len = size ? size : 4096;\n    BufferAllocator alloc;\n    unique_fd dma_buf(alloc.Alloc(kDmabufSystemHeapName, shm_len));\n    if (dma_buf < 0) {\n        std::cerr << \"Failed to create dmabuf of size: \" << shm_len << std::endl;\n        android::trusty::fuzz::Abort();\n    }\n\n    void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);\n    if (shm_base == MAP_FAILED) {\n        std::cerr << \"Failed to mmap() dmabuf\" << std::endl;\n        android::trusty::fuzz::Abort();\n    }\n\n    memcpy(shm_base, data, size);\n\n    bool success = SendLoadMsg(*ta.GetRawFd(), dma_buf, shm_len);\n    if (!success) {\n        std::cerr << \"Failed to send load message\" << std::endl;\n        android::trusty::fuzz::Abort();\n    }\n\n    munmap(shm_base, shm_len);\n    return 0;\n}\n"
  },
  {
    "path": "trusty/confirmationui/.clang-format",
    "content": "BasedOnStyle: LLVM\nIndentWidth: 4\nUseTab: Never\nBreakBeforeBraces: Attach\nAllowShortFunctionsOnASingleLine: Inline\nAllowShortIfStatementsOnASingleLine: true\nIndentCaseLabels: false\nColumnLimit: 100\nPointerBindsToType: true\nSpacesBeforeTrailingComments: 2\n"
  },
  {
    "path": "trusty/confirmationui/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n// WARNING: Everything listed here will be built on ALL platforms,\n// including x86, the emulator, and the SDK.  Modules must be uniquely\n// named (liblights.panda), and must build everywhere, or limit themselves\n// to only building on ARM if they include assembly. Individual makefiles\n// are responsible for having their own logic, for fine-grained control.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"android.hardware.confirmationui-service.trusty\",\n    relative_install_path: \"hw\",\n    vendor: true,\n    shared_libs: [\n        \"android.hardware.confirmationui-V1-ndk\",\n        \"android.hardware.confirmationui.not-so-secure-input\",\n        \"android.hardware.confirmationui-lib.trusty\",\n        \"libbinder_ndk\",\n        \"libteeui_hal_support\",\n        \"libbase\",\n        \"libhidlbase\",\n        \"libutils\",\n    ],\n\n    init_rc: [\"android.hardware.confirmationui-service.trusty.rc\"],\n\n    vintf_fragments: [\"android.hardware.confirmationui-service.trusty.xml\"],\n\n    srcs: [\n        \"service.cpp\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-DTEEUI_USE_STD_VECTOR\",\n    ],\n}\n\ncc_fuzz {\n    name: \"android.hardware.confirmationui-service.trusty_fuzzer\",\n    defaults: [\"service_fuzzer_defaults\"],\n    vendor: true,\n    shared_libs: [\n        \"android.hardware.confirmationui-V1-ndk\",\n        \"android.hardware.confirmationui.not-so-secure-input\",\n        \"android.hardware.confirmationui-lib.trusty\",\n        \"liblog\",\n    ],\n    srcs: [\"fuzzer.cpp\"],\n    fuzz_config: {\n        cc: [\n            \"nyamagoud@google.com\",\n        ],\n    },\n}\n\ncc_library {\n    name: \"android.hardware.confirmationui-lib.trusty\",\n    defaults: [\n        \"keymint_use_latest_hal_aidl_ndk_shared\",\n    ],\n    vendor: true,\n    shared_libs: [\n        \"android.hardware.confirmationui-V1-ndk\",\n        \"libbase\",\n        \"libcutils\",\n        \"libdmabufheap\",\n        \"libteeui_hal_support\",\n        \"libtrusty\",\n        \"libutils\",\n        \"libbinder_ndk\",\n    ],\n\n    export_include_dirs: [\"include\"],\n\n    srcs: [\n        \"TrustyApp.cpp\",\n        \"TrustyConfirmationUI.cpp\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-DTEEUI_USE_STD_VECTOR\",\n    ],\n}\n\ncc_library {\n    name: \"android.hardware.confirmationui.not-so-secure-input\",\n    vendor: true,\n    shared_libs: [\n        \"libbase\",\n        \"libcrypto\",\n        \"libteeui_hal_support\",\n    ],\n\n    srcs: [\n        \"NotSoSecureInput.cpp\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n        \"-DTEEUI_USE_STD_VECTOR\",\n    ],\n}\n"
  },
  {
    "path": "trusty/confirmationui/NotSoSecureInput.cpp",
    "content": "/*\n * Copyright 2020, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n#include <endian.h>\n#include <memory>\n#include <openssl/hmac.h>\n#include <openssl/rand.h>\n#include <openssl/sha.h>\n#include <secure_input/evdev.h>\n#include <secure_input/secure_input_device.h>\n#include <teeui/utils.h>\n\n#include <initializer_list>\n\nusing namespace secure_input;\n\nusing teeui::AuthTokenKey;\nusing teeui::ByteBufferProxy;\nusing teeui::Hmac;\nusing teeui::optional;\nusing teeui::ResponseCode;\nusing teeui::TestKeyBits;\n\nconstexpr const auto kTestKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE));\n\nclass SecureInputHMacer {\n  public:\n    static optional<Hmac> hmac256(const AuthTokenKey& key,\n                                  std::initializer_list<ByteBufferProxy> buffers) {\n        HMAC_CTX hmacCtx;\n        HMAC_CTX_init(&hmacCtx);\n        if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) {\n            return {};\n        }\n        for (auto& buffer : buffers) {\n            if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {\n                return {};\n            }\n        }\n        Hmac result;\n        if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {\n            return {};\n        }\n        return result;\n    }\n};\n\nusing HMac = teeui::HMac<SecureInputHMacer>;\n\nNonce generateNonce() {\n    /*\n     * Completely random nonce.\n     * Running the secure input protocol from the HAL service is not secure\n     * because we don't trust the non-secure world (i.e., HLOS/Android/Linux). So\n     * using a constant \"nonce\" here does not weaken security. If this code runs\n     * on a truly trustworthy source of input events this function needs to return\n     * hight entropy nonces.\n     * As of this writing the call to RAND_bytes is commented, because the\n     * emulator this HAL service runs on does not have a good source of entropy.\n     * It would block the call to RAND_bytes indefinitely.\n     */\n    Nonce result{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\n                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,\n                 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};\n    // RAND_bytes(result.data(), result.size());\n    return result;\n}\n\n/**\n * This is an implementation of the SecureInput protocol in unserspace. This is\n * just an example and should not be used as is. The protocol implemented here\n * should be used by a trusted input device that can assert user events with\n * high assurance even if the HLOS kernel is compromised. A confirmationui HAL\n * that links directly against this implementation is not secure and shal not be\n * used on a production device.\n */\nclass NotSoSecureInput : public SecureInput {\n  public:\n    NotSoSecureInput(HsBeginCb hsBeginCb, HsFinalizeCb hsFinalizeCb, DeliverEventCb deliverEventCb,\n                     InputResultCb inputResultCb)\n        : hsBeginCb_{hsBeginCb}, hsFinalizeCb_{hsFinalizeCb}, deliverEventCb_{deliverEventCb},\n          inputResultCb_{inputResultCb}, discardEvents_{true} {}\n\n    operator bool() const override { return true; }\n\n    void handleEvent(const EventDev& evdev) override {\n        bool gotEvent;\n        input_event evt;\n        std::tie(gotEvent, evt) = evdev.readEvent();\n        while (gotEvent) {\n            if (!(discardEvents_) && evt.type == EV_KEY &&\n                (evt.code == KEY_POWER || evt.code == KEY_VOLUMEDOWN || evt.code == KEY_VOLUMEUP) &&\n                evt.value == 1) {\n                DTupKeyEvent event = DTupKeyEvent::RESERVED;\n\n                // Translate the event code into DTupKeyEvent which the TA understands.\n                switch (evt.code) {\n                case KEY_POWER:\n                    event = DTupKeyEvent::PWR;\n                    break;\n                case KEY_VOLUMEDOWN:\n                    event = DTupKeyEvent::VOL_DOWN;\n                    break;\n                case KEY_VOLUMEUP:\n                    event = DTupKeyEvent::VOL_UP;\n                    break;\n                }\n\n                // The event goes into the HMAC in network byte order.\n                uint32_t keyEventBE = htobe32(static_cast<uint32_t>(event));\n                auto signature = HMac::hmac256(kTestKey, kConfirmationUIEventLabel,\n                                               teeui::bytesCast(keyEventBE), nCi_);\n\n                teeui::ResponseCode rc;\n                InputResponse ir;\n                auto response = std::tie(rc, ir);\n                if (event != DTupKeyEvent::RESERVED) {\n                    response = deliverEventCb_(event, *signature);\n                    if (rc != ResponseCode::OK) {\n                        LOG(ERROR) << \"DeliverInputEvent returned with \" << uint32_t(rc);\n                        inputResultCb_(rc);\n                    } else {\n                        switch (ir) {\n                        case InputResponse::OK:\n                            inputResultCb_(rc);\n                            break;\n                        case InputResponse::PENDING_MORE:\n                            rc = performDTUPHandshake();\n                            if (rc != ResponseCode::OK) {\n                                inputResultCb_(rc);\n                            }\n                            break;\n                        case InputResponse::TIMED_OUT:\n                            inputResultCb_(rc);\n                            break;\n                        }\n                    }\n                }\n            }\n            std::tie(gotEvent, evt) = evdev.readEvent();\n        }\n    }\n\n    void start() override {\n        auto rc = performDTUPHandshake();\n        if (rc != ResponseCode::OK) {\n            inputResultCb_(rc);\n        }\n        discardEvents_ = false;\n    };\n\n  private:\n    teeui::ResponseCode performDTUPHandshake() {\n        ResponseCode rc;\n        LOG(INFO) << \"Start handshake\";\n        Nonce nCo;\n        std::tie(rc, nCo) = hsBeginCb_();\n        if (rc != ResponseCode::OK) {\n            LOG(ERROR) << \"Failed to begin secure input handshake (\" << uint32_t(rc) << \")\";\n            return rc;\n        }\n\n        nCi_ = generateNonce();\n        rc =\n            hsFinalizeCb_(*HMac::hmac256(kTestKey, kConfirmationUIHandshakeLabel, nCo, nCi_), nCi_);\n\n        if (rc != ResponseCode::OK) {\n            LOG(ERROR) << \"Failed to finalize secure input handshake (\" << uint32_t(rc) << \")\";\n            return rc;\n        }\n        return ResponseCode::OK;\n    }\n\n    HsBeginCb hsBeginCb_;\n    HsFinalizeCb hsFinalizeCb_;\n    DeliverEventCb deliverEventCb_;\n    InputResultCb inputResultCb_;\n\n    std::atomic_bool discardEvents_;\n    Nonce nCi_;\n};\n\nnamespace secure_input {\n\nstd::shared_ptr<SecureInput> createSecureInput(SecureInput::HsBeginCb hsBeginCb,\n                                               SecureInput::HsFinalizeCb hsFinalizeCb,\n                                               SecureInput::DeliverEventCb deliverEventCb,\n                                               SecureInput::InputResultCb inputResultCb) {\n    return std::make_shared<NotSoSecureInput>(hsBeginCb, hsFinalizeCb, deliverEventCb,\n                                              inputResultCb);\n}\n\n}  // namespace secure_input\n"
  },
  {
    "path": "trusty/confirmationui/README",
    "content": "## Secure UI Architecture\n\nTo implement confirmationui a secure UI architecture is required. This entails a way\nto display the confirmation dialog driven by a reduced trusted computing base, typically\na trusted execution environment (TEE), without having to rely on Linux and the Android\nsystem for integrity and authenticity of input events. This implementation provides\nneither. But it provides most of the functionlity required to run a full Android Protected\nConfirmation feature when integrated into a secure UI architecture.\n\n## Secure input (NotSoSecureInput)\n\nThis implementation does not provide any security guaranties.\nThe input method (NotSoSecureInput) runs a cryptographic protocols that is\nsufficiently secure IFF the end point is implemented on a trustworthy\nsecure input device. But since the endpoint is currently in the HAL\nservice itself this implementation is not secure.\n\nNOTE that a secure input device end point needs a good source of entropy\nfor generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce)\nuses a constant nonce."
  },
  {
    "path": "trusty/confirmationui/TrustyApp.cpp",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"TrustyApp.h\"\n\n#include <BufferAllocator/BufferAllocator.h>\n#include <android-base/logging.h>\n#include <sys/mman.h>\n#include <sys/uio.h>\n#include <trusty/tipc.h>\n\n#define countof(arr) (sizeof(arr) / sizeof(arr[0]))\n\nnamespace android {\nnamespace trusty {\nnamespace confirmationui {\n\nusing ::android::base::unique_fd;\n\nssize_t TrustyApp::TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,\n                             uint8_t* iend) {\n    uint32_t olen = oend - obegin;\n\n    if (olen > shm_len_) {\n        LOG(ERROR) << AT << \"request message too long to fit in shared memory\";\n        return -1;\n    }\n\n    memcpy(shm_base_, obegin, olen);\n\n    confirmationui_hdr hdr = {\n        .cmd = CONFIRMATIONUI_CMD_MSG,\n    };\n    confirmationui_msg_args args = {\n        .msg_len = olen,\n    };\n    iovec iov[] = {\n        {\n            .iov_base = &hdr,\n            .iov_len = sizeof(hdr),\n        },\n        {\n            .iov_base = &args,\n            .iov_len = sizeof(args),\n        },\n    };\n\n    int rc = tipc_send(handle_, iov, countof(iov), NULL, 0);\n    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {\n        LOG(ERROR) << AT << \"failed to send MSG request\";\n        return -1;\n    }\n\n    rc = readv(handle_, iov, countof(iov));\n    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {\n        LOG(ERROR) << AT << \"failed to receive MSG response\";\n        return -1;\n    }\n\n    if (hdr.cmd != (CONFIRMATIONUI_CMD_MSG | CONFIRMATIONUI_RESP_BIT)) {\n        LOG(ERROR) << AT << \"unknown response command: \" << hdr.cmd;\n        return -1;\n    }\n\n    uint32_t ilen = iend - ibegin;\n    if (args.msg_len > ilen) {\n        LOG(ERROR) << AT << \"response message too long to fit in return buffer\";\n        return -1;\n    }\n\n    memcpy(ibegin, shm_base_, args.msg_len);\n\n    return args.msg_len;\n}\n\nTrustyApp::TrustyApp(const std::string& path, const std::string& appname)\n    : handle_(kInvalidHandle) {\n    unique_fd tipc_handle(tipc_connect(path.c_str(), appname.c_str()));\n    if (tipc_handle < 0) {\n        LOG(ERROR) << AT << \"failed to connect to Trusty TA \\\"\" << appname << \"\\\" using dev:\"\n                   << \"\\\"\" << path << \"\\\"\";\n        return;\n    }\n\n    uint32_t shm_len = CONFIRMATIONUI_MAX_MSG_SIZE;\n    BufferAllocator allocator;\n    unique_fd dma_buf(allocator.Alloc(\"system\", shm_len));\n    if (dma_buf < 0) {\n        LOG(ERROR) << AT << \"failed to allocate shared memory buffer\";\n        return;\n    }\n\n    confirmationui_hdr hdr = {\n        .cmd = CONFIRMATIONUI_CMD_INIT,\n    };\n    confirmationui_init_req args = {\n        .shm_len = shm_len,\n    };\n    iovec iov[] = {\n        {\n            .iov_base = &hdr,\n            .iov_len = sizeof(hdr),\n        },\n        {\n            .iov_base = &args,\n            .iov_len = sizeof(args),\n        },\n    };\n    trusty_shm shm = {\n        .fd = dma_buf,\n        .transfer = TRUSTY_SHARE,\n    };\n\n    int rc = tipc_send(tipc_handle, iov, 2, &shm, 1);\n    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {\n        LOG(ERROR) << AT << \"failed to send INIT request\";\n        return;\n    }\n\n    rc = read(tipc_handle, &hdr, sizeof(hdr));\n    if (rc != static_cast<int>(sizeof(hdr))) {\n        LOG(ERROR) << AT << \"failed to receive INIT response\";\n        return;\n    }\n\n    if (hdr.cmd != (CONFIRMATIONUI_CMD_INIT | CONFIRMATIONUI_RESP_BIT)) {\n        LOG(ERROR) << AT << \"unknown response command: \" << hdr.cmd;\n        return;\n    }\n\n    void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);\n    if (shm_base == MAP_FAILED) {\n        LOG(ERROR) << AT << \"failed to mmap() shared memory buffer\";\n        return;\n    }\n\n    handle_ = std::move(tipc_handle);\n    shm_base_ = shm_base;\n    shm_len_ = shm_len;\n\n    LOG(INFO) << AT << \"succeeded to connect to Trusty TA \\\"\" << appname << \"\\\"\";\n}\n\nTrustyApp::~TrustyApp() {\n    LOG(INFO) << \"Done shutting down TrustyApp\";\n}\n\n}  // namespace confirmationui\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/confirmationui/TrustyApp.h",
    "content": "/*\n * Copyright 2020, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <TrustyIpc.h>\n\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <errno.h>\n#include <poll.h>\n#include <stdio.h>\n#include <sys/eventfd.h>\n#include <sys/stat.h>\n#include <teeui/msg_formatting.h>\n#include <trusty/tipc.h>\n#include <unistd.h>\n\n#include <fstream>\n#include <functional>\n#include <future>\n#include <iostream>\n#include <sstream>\n#include <thread>\n#include <vector>\n\n#define AT __FILE__ \":\" << __LINE__ << \": \"\n\nnamespace android {\nnamespace trusty {\nnamespace confirmationui {\n\nusing ::teeui::Message;\nusing ::teeui::msg2tuple_t;\nusing ::teeui::ReadStream;\nusing ::teeui::WriteStream;\n\n#ifndef TEEUI_USE_STD_VECTOR\n/*\n * TEEUI_USE_STD_VECTOR makes certain wire types like teeui::MsgString and\n * teeui::MsgVector be aliases for std::vector. This is required for thread safe\n * message serialization. Always compile this with -DTEEUI_USE_STD_VECTOR set in\n * CFLAGS of the HAL service.\n */\n#error \"Must be compiled with -DTEEUI_USE_STD_VECTOR.\"\n#endif\n\nenum class TrustyAppError : int32_t {\n    OK,\n    ERROR = -1,\n    MSG_TOO_LONG = -2,\n};\n\nclass TrustyApp {\n  private:\n    android::base::unique_fd handle_;\n    void* shm_base_;\n    size_t shm_len_;\n    static constexpr const int kInvalidHandle = -1;\n    /*\n     * This mutex serializes communication with the trusted app, not handle_.\n     * Calling issueCmd during construction or deletion is undefined behavior.\n     */\n    std::mutex mutex_;\n\n  public:\n    TrustyApp(const std::string& path, const std::string& appname);\n    ~TrustyApp();\n\n    ssize_t TrustyRpc(const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin, uint8_t* iend);\n\n    template <typename Request, typename Response, typename... T>\n    std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {\n        std::lock_guard<std::mutex> lock(mutex_);\n\n        if (handle_ == kInvalidHandle) {\n            LOG(ERROR) << \"TrustyApp not connected\";\n            return {TrustyAppError::ERROR, {}};\n        }\n\n        uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];\n        WriteStream out(buffer);\n\n        out = write(Request(), out, args...);\n        if (!out) {\n            LOG(ERROR) << AT << \"send command failed: message formatting\";\n            return {TrustyAppError::MSG_TOO_LONG, {}};\n        }\n\n        auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],\n                            &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);\n        if (rc < 0) return {TrustyAppError::ERROR, {}};\n\n        ReadStream in(&buffer[0], rc);\n        auto result = read(Response(), in);\n        if (!std::get<0>(result)) {\n            LOG(ERROR) << \"send command failed: message parsing\";\n            return {TrustyAppError::ERROR, {}};\n        }\n\n        return {std::get<0>(result) ? TrustyAppError::OK : TrustyAppError::ERROR,\n                tuple_tail(std::move(result))};\n    }\n\n    template <typename Request, typename... T> TrustyAppError issueCmd(const T&... args) {\n        std::lock_guard<std::mutex> lock(mutex_);\n\n        if (handle_ == kInvalidHandle) {\n            LOG(ERROR) << \"TrustyApp not connected\";\n            return TrustyAppError::ERROR;\n        }\n\n        uint8_t buffer[CONFIRMATIONUI_MAX_MSG_SIZE];\n        WriteStream out(buffer);\n\n        out = write(Request(), out, args...);\n        if (!out) {\n            LOG(ERROR) << AT << \"send command failed: message formatting\";\n            return TrustyAppError::MSG_TOO_LONG;\n        }\n\n        auto rc = TrustyRpc(&buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],\n                            &buffer[CONFIRMATIONUI_MAX_MSG_SIZE]);\n        if (rc < 0) {\n            LOG(ERROR) << \"send command failed: \" << strerror(errno) << \" (\" << errno << \")\";\n            return TrustyAppError::ERROR;\n        }\n\n        if (rc > 0) {\n            LOG(ERROR) << \"Unexpected non zero length response\";\n            return TrustyAppError::ERROR;\n        }\n        return TrustyAppError::OK;\n    }\n\n    operator bool() const { return handle_ != kInvalidHandle; }\n};\n\n}  // namespace confirmationui\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/confirmationui/TrustyConfirmationUI.cpp",
    "content": "/*\n *\n * Copyright 2019, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"TrustyConfirmationUI.h\"\n\n#include <android-base/logging.h>\n#include <fcntl.h>\n#include <linux/input.h>\n#include <poll.h>\n#include <pthread.h>\n#include <secure_input/evdev.h>\n#include <secure_input/secure_input_device.h>\n#include <secure_input/secure_input_proto.h>\n#include <signal.h>\n#include <sys/ioctl.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <teeui/msg_formatting.h>\n#include <teeui/utils.h>\n#include <time.h>\n\n#include <atomic>\n#include <functional>\n#include <memory>\n#include <thread>\n#include <tuple>\n#include <vector>\n\nnamespace aidl::android::hardware::confirmationui {\nusing namespace secure_input;\n\nusing ::android::trusty::confirmationui::TrustyAppError;\n\nusing ::teeui::AbortMsg;\nusing ::teeui::DeliverTestCommandMessage;\nusing ::teeui::DeliverTestCommandResponse;\nusing ::teeui::FetchConfirmationResult;\nusing ::teeui::MsgString;\nusing ::teeui::MsgVector;\nusing ::teeui::PromptUserConfirmationMsg;\nusing ::teeui::PromptUserConfirmationResponse;\nusing ::teeui::ResultMsg;\n\nusing ::secure_input::createSecureInput;\n\nusing ::std::tie;\n\nusing TeeuiRc = ::teeui::ResponseCode;\n\nconstexpr const char kTrustyDeviceName[] = \"/dev/trusty-ipc-dev0\";\nconstexpr const char kConfirmationuiAppName[] = CONFIRMATIONUI_PORT;\n\nnamespace {\n\nclass Finalize {\n  private:\n    std::function<void()> f_;\n\n  public:\n    Finalize(std::function<void()> f) : f_(f) {}\n    ~Finalize() {\n        if (f_) f_();\n    }\n    void release() { f_ = {}; }\n};\n\nint convertRc(TeeuiRc trc) {\n    static_assert(\n        uint32_t(TeeuiRc::OK) == uint32_t(IConfirmationUI::OK) &&\n            uint32_t(TeeuiRc::Canceled) == uint32_t(IConfirmationUI::CANCELED) &&\n            uint32_t(TeeuiRc::Aborted) == uint32_t(IConfirmationUI::ABORTED) &&\n            uint32_t(TeeuiRc::OperationPending) == uint32_t(IConfirmationUI::OPERATION_PENDING) &&\n            uint32_t(TeeuiRc::Ignored) == uint32_t(IConfirmationUI::IGNORED) &&\n            uint32_t(TeeuiRc::SystemError) == uint32_t(IConfirmationUI::SYSTEM_ERROR) &&\n            uint32_t(TeeuiRc::Unimplemented) == uint32_t(IConfirmationUI::UNIMPLEMENTED) &&\n            uint32_t(TeeuiRc::Unexpected) == uint32_t(IConfirmationUI::UNEXPECTED) &&\n            uint32_t(TeeuiRc::UIError) == uint32_t(IConfirmationUI::UI_ERROR) &&\n            uint32_t(TeeuiRc::UIErrorMissingGlyph) ==\n                uint32_t(IConfirmationUI::UI_ERROR_MISSING_GLYPH) &&\n            uint32_t(TeeuiRc::UIErrorMessageTooLong) ==\n                uint32_t(IConfirmationUI::UI_ERROR_MESSAGE_TOO_LONG) &&\n            uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==\n                uint32_t(IConfirmationUI::UI_ERROR_MALFORMED_UTF8ENCODING),\n        \"teeui::ResponseCode and \"\n        \"::android::hardware::confirmationui::V1_0::Responsecude are out of \"\n        \"sync\");\n    return static_cast<int>(trc);\n}\n\nteeui::UIOption convertUIOption(UIOption uio) {\n    static_assert(uint32_t(UIOption::ACCESSIBILITY_INVERTED) ==\n                          uint32_t(teeui::UIOption::AccessibilityInverted) &&\n                      uint32_t(UIOption::ACCESSIBILITY_MAGNIFIED) ==\n                          uint32_t(teeui::UIOption::AccessibilityMagnified),\n                  \"teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption \"\n                  \"are out of sync\");\n    return teeui::UIOption(uio);\n}\n\ninline MsgString stdString2MsgString(const string& s) {\n    return {s.c_str(), s.c_str() + s.size()};\n}\ntemplate <typename T> inline MsgVector<T> stdVector2MsgVector(const vector<T>& v) {\n    return {v};\n}\n\ninline MsgVector<teeui::UIOption> stdVector2MsgVector(const vector<UIOption>& v) {\n    MsgVector<teeui::UIOption> result(v.size());\n    for (unsigned int i = 0; i < v.size(); ++i) {\n        result[i] = convertUIOption(v[i]);\n    }\n    return result;\n}\n\n}  // namespace\n\nTrustyConfirmationUI::TrustyConfirmationUI()\n    : listener_state_(ListenerState::None), prompt_result_(IConfirmationUI::IGNORED) {}\n\nTrustyConfirmationUI::~TrustyConfirmationUI() {\n    ListenerState state = listener_state_;\n    if (state == ListenerState::SetupDone || state == ListenerState::Interactive) {\n        abort();\n    }\n    if (state != ListenerState::None) {\n        callback_thread_.join();\n    }\n}\n\nstd::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>>\nTrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText,\n                                              const MsgVector<uint8_t>& extraData,\n                                              const MsgString& locale,\n                                              const MsgVector<teeui::UIOption>& uiOptions) {\n    std::unique_lock<std::mutex> stateLock(listener_state_lock_);\n    /*\n     * This is the main listener thread function. The listener thread life cycle\n     * is equivalent to the life cycle of a single confirmation request. The life\n     * cycle is devided in four phases.\n     *  * The starting phase:\n     *    * The Trusted App gets loaded and/or the connection to it gets established.\n     *    * A connection to the secure input device is established.\n     *    * The prompt is initiated. This sends all information required by the\n     *      confirmation dialog to the TA. The dialog is not yet displayed.\n     *    * An event loop is created.\n     *      * The event loop listens for user input events, fetches them from the\n     *        secure input device, and delivers them to the TA.\n     *    * All evdev devices are grabbed to give confirmationui exclusive access\n     *      to user input.\n     *\n     * Note: During the starting phase the hwbinder service thread is blocked and\n     * waiting for possible Errors. If the setup phase concludes sucessfully, the\n     * hwbinder service thread gets unblocked and returns successfully. Errors\n     * that occur after the first phase are delivered by callback interface.\n     *\n     *  * The 2nd phase - non interactive phase\n     *    * The event loop thread is started.\n     *    * After a grace period:\n     *      * A handshake between the secure input device SecureInput and the TA\n     *        is performed.\n     *      * The input event handler are armed to process user input events.\n     *\n     *  * The 3rd phase - interactive phase\n     *    * We wait to any external event\n     *      * Abort\n     *      * Secure user input asserted\n     *      * Secure input delivered (for non interactive VTS testing)\n     *    * The result is fetched from the TA.\n     *\n     *  * The 4th phase - cleanup\n     *    The cleanup phase is given by the scope of automatic variables created\n     *    in this function. The cleanup commences in reverse order of their creation.\n     *    Here is a list of more complex items in the order in which they go out of\n     *    scope\n     *    * finalizeSecureTouch - signals and joins the secure touch thread.\n     *    * eventloop - signals and joins the event loop thread. The event\n     *      handlers also own all EventDev instances which ungrab the event devices.\n     *      When the eventloop goes out of scope the EventDevs get destroyed\n     *      relinquishing the exclusive hold on the event devices.\n     *    * finalizeConfirmationPrompt - calls abort on the TA, making sure a\n     *      pending operation gets canceled. If the prompt concluded successfully this\n     *      is a spurious call but semantically a no op.\n     *    * secureInput - shuts down the connection to the secure input device\n     *      SecureInput.\n     *    * app - disconnects the TA. Since app is a shared pointer this may not\n     *      unload the app here. It is possible that more instances of the shared\n     *      pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and\n     *      TrustyConfirmationUI::abort. But these instances are extremely short lived\n     *      and it is safe if they are destroyed by either.\n     *    * stateLock - unlocks the listener_state_lock_ if it happens to be held\n     *      at the time of return.\n     */\n\n    std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result;\n    TeeuiRc& rc = std::get<TeeuiRc>(result);\n    rc = TeeuiRc::SystemError;\n\n    listener_state_ = ListenerState::Starting;\n\n    auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName);\n    if (!app) return result;  // TeeuiRc::SystemError\n\n    app_ = app;\n\n    auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> {\n        auto [error, result] =\n            app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>();\n        auto& [rc, nCo] = result;\n\n        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {\n            LOG(ERROR) << \"Failed to begin secure input handshake (\" << int32_t(error) << \"/\"\n                       << uint32_t(rc) << \")\";\n            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;\n        }\n        return result;\n    };\n\n    auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc {\n        auto [error, finalizeResponse] =\n            app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>(\n                nCi, sig);\n        auto& [rc] = finalizeResponse;\n        if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {\n            LOG(ERROR) << \"Failed to finalize secure input handshake (\" << int32_t(error) << \"/\"\n                       << uint32_t(rc) << \")\";\n            rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;\n        }\n        return rc;\n    };\n\n    auto deliverInput = [&](DTupKeyEvent event,\n                            const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> {\n        auto [error, result] =\n            app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig);\n        auto& [rc, ir] = result;\n        if (error != TrustyAppError::OK) {\n            LOG(ERROR) << \"Failed to deliver input command\";\n            rc = TeeuiRc::SystemError;\n        }\n        return result;\n    };\n\n    std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending;\n    auto inputResult = [&](TeeuiRc rc) {\n        TeeuiRc expected = TeeuiRc::OperationPending;\n        if (eventRC.compare_exchange_strong(expected, rc)) {\n            listener_state_condv_.notify_all();\n        }\n    };\n\n    // create Secure Input device.\n    auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult);\n    if (!secureInput || !(*secureInput)) {\n        LOG(ERROR) << \"Failed to open secure input device\";\n        return result;  // TeeuiRc::SystemError;\n    }\n\n    Finalize finalizeConfirmationPrompt([app] {\n        LOG(INFO) << \"Calling abort for cleanup\";\n        app->issueCmd<AbortMsg>();\n    });\n\n    // initiate prompt\n    LOG(INFO) << \"Initiating prompt\";\n    TrustyAppError error;\n    auto initResponse = std::tie(rc);\n    std::tie(error, initResponse) =\n        app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>(\n            promptText, extraData, locale, uiOptions);\n    if (error == TrustyAppError::MSG_TOO_LONG) {\n        LOG(ERROR) << \"PromptUserConfirmationMsg failed: message too long\";\n        rc = TeeuiRc::UIErrorMessageTooLong;\n        return result;\n    } else if (error != TrustyAppError::OK) {\n        LOG(ERROR) << \"PromptUserConfirmationMsg failed: \" << int32_t(error);\n        return result;  // TeeuiRc::SystemError;\n    }\n    if (rc != TeeuiRc::OK) {\n        LOG(ERROR) << \"PromptUserConfirmationMsg failed: \" << uint32_t(rc);\n        return result;\n    }\n\n    LOG(INFO) << \"Grabbing event devices\";\n    EventLoop eventloop;\n    bool grabbed =\n        grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) {\n            if (!(flags & POLLIN)) return;\n            secureInput->handleEvent(evDev);\n        });\n\n    if (!grabbed) {\n        rc = TeeuiRc::SystemError;\n        return result;\n    }\n\n    abort_called_ = false;\n    secureInputDelivered_ = false;\n\n    //  ############################## Start 2nd Phase #############################################\n    listener_state_ = ListenerState::SetupDone;\n    stateLock.unlock();\n    listener_state_condv_.notify_all();\n\n    if (!eventloop.start()) {\n        rc = TeeuiRc::SystemError;\n        return result;\n    }\n\n    stateLock.lock();\n\n    LOG(INFO) << \"going to sleep for the grace period\";\n    auto then = std::chrono::system_clock::now() +\n                std::chrono::milliseconds(kUserPreInputGracePeriodMillis) +\n                std::chrono::microseconds(50);\n    listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; });\n    LOG(INFO) << \"waking up\";\n\n    if (abort_called_) {\n        LOG(ERROR) << \"Abort called\";\n        result = {TeeuiRc::Aborted, {}, {}};\n        return result;\n    }\n\n    LOG(INFO) << \"Arming event poller\";\n    // tell the event poller to act on received input events from now on.\n    secureInput->start();\n\n    //  ############################## Start 3rd Phase - interactive phase #########################\n    LOG(INFO) << \"Transition to Interactive\";\n    listener_state_ = ListenerState::Interactive;\n    stateLock.unlock();\n    listener_state_condv_.notify_all();\n\n    stateLock.lock();\n    listener_state_condv_.wait(stateLock, [&]() {\n        return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_;\n    });\n    LOG(INFO) << \"Listener waking up\";\n    if (abort_called_) {\n        LOG(ERROR) << \"Abort called\";\n        result = {TeeuiRc::Aborted, {}, {}};\n        return result;\n    }\n\n    if (!secureInputDelivered_) {\n        if (eventRC != TeeuiRc::OK) {\n            LOG(ERROR) << \"Bad input response\";\n            result = {eventRC, {}, {}};\n            return result;\n        }\n    }\n\n    stateLock.unlock();\n\n    LOG(INFO) << \"Fetching Result\";\n    std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>();\n    LOG(INFO) << \"Result yields \" << int32_t(error) << \"/\" << uint32_t(rc);\n    if (error != TrustyAppError::OK) {\n        result = {TeeuiRc::SystemError, {}, {}};\n    }\n    return result;\n\n    //  ############################## Start 4th Phase - cleanup ##################################\n}\n\n// Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI\n// follow.\n::ndk::ScopedAStatus TrustyConfirmationUI::promptUserConfirmation(\n    const shared_ptr<IConfirmationResultCallback>& resultCB, const vector<uint8_t>& promptTextBytes,\n    const vector<uint8_t>& extraData, const string& locale, const vector<UIOption>& uiOptions) {\n    std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);\n    string promptText(promptTextBytes.begin(), promptTextBytes.end());\n    if (!stateLock.try_lock()) {\n        return ndk::ScopedAStatus(\n            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));\n    }\n    switch (listener_state_) {\n    case ListenerState::None:\n        break;\n    case ListenerState::Starting:\n    case ListenerState::SetupDone:\n    case ListenerState::Interactive:\n        return ndk::ScopedAStatus(\n            AStatus_fromServiceSpecificError(IConfirmationUI::OPERATION_PENDING));\n    case ListenerState::Terminating:\n        callback_thread_.join();\n        listener_state_ = ListenerState::None;\n        break;\n    default:\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::UNEXPECTED));\n    }\n\n    assert(listener_state_ == ListenerState::None);\n\n    callback_thread_ = std::thread(\n        [this](const shared_ptr<IConfirmationResultCallback>& resultCB, const string& promptText,\n               const vector<uint8_t>& extraData, const string& locale,\n               const vector<UIOption>& uiOptions) {\n            auto [trc, msg, token] = promptUserConfirmation_(\n                stdString2MsgString(promptText), stdVector2MsgVector(extraData),\n                stdString2MsgString(locale), stdVector2MsgVector(uiOptions));\n            bool do_callback = (listener_state_ == ListenerState::Interactive ||\n                                listener_state_ == ListenerState::SetupDone) &&\n                               resultCB;\n            prompt_result_ = convertRc(trc);\n            listener_state_ = ListenerState::Terminating;\n            if (do_callback) {\n                auto error = resultCB->result(prompt_result_, msg, token);\n                if (!error.isOk()) {\n                    LOG(ERROR) << \"Result callback failed \" << error.getDescription();\n                }\n            } else {\n                listener_state_condv_.notify_all();\n            }\n        },\n        resultCB, promptText, extraData, locale, uiOptions);\n\n    listener_state_condv_.wait(stateLock, [this] {\n        return listener_state_ == ListenerState::SetupDone ||\n               listener_state_ == ListenerState::Interactive ||\n               listener_state_ == ListenerState::Terminating;\n    });\n    if (listener_state_ == ListenerState::Terminating) {\n        callback_thread_.join();\n        listener_state_ = ListenerState::None;\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(prompt_result_));\n    }\n    return ndk::ScopedAStatus::ok();\n}\n\n::ndk::ScopedAStatus\nTrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {\n    int rc = IConfirmationUI::IGNORED;\n    {\n        /*\n         * deliverSecureInputEvent is only used by the VTS test to mock human input. A correct\n         * implementation responds with a mock confirmation token signed with a test key. The\n         * problem is that the non interactive grace period was not formalized in the HAL spec,\n         * so that the VTS test does not account for the grace period. (It probably should.)\n         * This means we can only pass the VTS test if we block until the grace period is over\n         * (SetupDone -> Interactive) before we deliver the input event.\n         *\n         * The true secure input is delivered by a different mechanism and gets ignored -\n         * not queued - until the grace period is over.\n         *\n         */\n        std::unique_lock<std::mutex> stateLock(listener_state_lock_);\n        listener_state_condv_.wait(stateLock,\n                                   [this] { return listener_state_ != ListenerState::SetupDone; });\n\n        if (listener_state_ != ListenerState::Interactive)\n            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));\n        auto sapp = app_.lock();\n        if (!sapp)\n            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(IConfirmationUI::IGNORED));\n        auto [error, response] =\n            sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(\n                static_cast<teeui::TestModeCommands>(secureInputToken.challenge));\n        if (error != TrustyAppError::OK)\n            return ndk::ScopedAStatus(\n                AStatus_fromServiceSpecificError(IConfirmationUI::SYSTEM_ERROR));\n        auto& [trc] = response;\n        if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;\n        rc = convertRc(trc);\n    }\n    if (secureInputDelivered_) listener_state_condv_.notify_all();\n    // VTS test expect an OK response if the event was successfully delivered.\n    // But since the TA returns the callback response now, we have to translate\n    // Canceled into OK. Canceled is only returned if the delivered event canceled\n    // the operation, which means that the event was successfully delivered. Thus\n    // we return OK.\n    if (rc == IConfirmationUI::CANCELED) return ndk::ScopedAStatus::ok();\n    if (rc != IConfirmationUI::OK) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(rc));\n    }\n    return ndk::ScopedAStatus::ok();\n}\n\n::ndk::ScopedAStatus TrustyConfirmationUI::abort() {\n    {\n        std::unique_lock<std::mutex> stateLock(listener_state_lock_);\n        if (listener_state_ == ListenerState::SetupDone ||\n            listener_state_ == ListenerState::Interactive) {\n            auto sapp = app_.lock();\n            if (sapp) sapp->issueCmd<AbortMsg>();\n            abort_called_ = true;\n        }\n    }\n    listener_state_condv_.notify_all();\n    return ndk::ScopedAStatus::ok();\n}\n\nstd::shared_ptr<IConfirmationUI> createTrustyConfirmationUI() {\n    return ndk::SharedRefBase::make<TrustyConfirmationUI>();\n}\n\n}  // namespace aidl::android::hardware::confirmationui\n"
  },
  {
    "path": "trusty/confirmationui/TrustyConfirmationUI.h",
    "content": "/*\n * Copyright 2020, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H\n#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H\n\n#include <aidl/android/hardware/confirmationui/BnConfirmationUI.h>\n#include <aidl/android/hardware/confirmationui/IConfirmationResultCallback.h>\n#include <aidl/android/hardware/confirmationui/UIOption.h>\n#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>\n#include <android/binder_manager.h>\n\n#include <atomic>\n#include <condition_variable>\n#include <memory>\n#include <mutex>\n#include <teeui/generic_messages.h>\n#include <thread>\n\n#include \"TrustyApp.h\"\n\nnamespace aidl::android::hardware::confirmationui {\n\nusing std::shared_ptr;\nusing std::string;\nusing std::vector;\n\nusing ::aidl::android::hardware::security::keymint::HardwareAuthToken;\nusing ::android::trusty::confirmationui::TrustyApp;\n\nclass TrustyConfirmationUI : public BnConfirmationUI {\n  public:\n    TrustyConfirmationUI();\n    virtual ~TrustyConfirmationUI();\n    // Methods from ::aidl::android::hardware::confirmationui::IConfirmationUI\n    // follow.\n    ::ndk::ScopedAStatus\n    promptUserConfirmation(const shared_ptr<IConfirmationResultCallback>& resultCB,\n                           const vector<uint8_t>& promptText, const vector<uint8_t>& extraData,\n                           const string& locale, const vector<UIOption>& uiOptions) override;\n    ::ndk::ScopedAStatus\n    deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) override;\n\n    ::ndk::ScopedAStatus abort() override;\n\n  private:\n    std::weak_ptr<TrustyApp> app_;\n    std::thread callback_thread_;\n\n    enum class ListenerState : uint32_t {\n        None,\n        Starting,\n        SetupDone,\n        Interactive,\n        Terminating,\n    };\n\n    /*\n     * listener_state is protected by listener_state_lock. It makes transitions between phases\n     * of the confirmation operation atomic.\n     * (See TrustyConfirmationUI.cpp#promptUserConfirmation_ for details about operation phases)\n     */\n    ListenerState listener_state_;\n    /*\n     * abort_called_ is also protected by listener_state_lock_ and indicates that the HAL user\n     * called abort.\n     */\n    bool abort_called_;\n    std::mutex listener_state_lock_;\n    std::condition_variable listener_state_condv_;\n    int prompt_result_;\n    bool secureInputDelivered_;\n\n    std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>\n    promptUserConfirmation_(const teeui::MsgString& promptText,\n                            const teeui::MsgVector<uint8_t>& extraData,\n                            const teeui::MsgString& locale,\n                            const teeui::MsgVector<teeui::UIOption>& uiOptions);\n};\n\n}  // namespace aidl::android::hardware::confirmationui\n\n#endif  // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H\n"
  },
  {
    "path": "trusty/confirmationui/android.hardware.confirmationui-service.trusty.rc",
    "content": "service vendor.confirmationui_default /vendor/bin/hw/android.hardware.confirmationui-service.trusty\n    interface aidl android.hardware.confirmationui.IConfirmationUI/default\n    class hal\n    user system\n    group drmrpc input system\n"
  },
  {
    "path": "trusty/confirmationui/android.hardware.confirmationui-service.trusty.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"aidl\">\n        <name>android.hardware.confirmationui</name>\n        <version>1</version>\n        <interface>\n        <name>IConfirmationUI</name>\n            <instance>default</instance>\n        </interface>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "trusty/confirmationui/fuzz/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_fuzz {\n    name: \"trusty_confirmationui_tipc_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\":trusty_tipc_fuzzer\"],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.confirmationui\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"7dee2364-c036-425b-b086-df0f6c233c1b\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"confirmationui.syms.elf\\\"\",\n    ],\n    fuzz_config: {\n       cc: [\"mikemcternan@google.com\"],\n       componentid: 1290237,\n    },\n\n}\n\ncc_fuzz {\n    name: \"trusty_confirmationui_msg_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\"msg_fuzzer.cpp\"],\n    include_dirs: [\"system/core/trusty/confirmationui/include\"],\n    shared_libs: [\n        \"libdmabufheap\",\n    ],\n    fuzz_config: {\n       cc: [\"mikemcternan@google.com\"],\n       componentid: 1290237,\n    },\n\n    // The initial corpus for this fuzzer was derived by dumping messages from/to\n    // HAL to/from TA triggered by VtsHalConfirmationUIV1_0TargetTest.\n    corpus: [\"msg_corpus/*\"],\n}\n"
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-0AD0Mc",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-3hmWyl",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-7T30a0",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BSmqJ0",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-BdUGLb",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-D2ENNi",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-LUVKQn",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-MdY9ZS",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-QTsc3y",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-VDguJL",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ZjDqjf",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-bMNGfb",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ikJlIo",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-kxugwp",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-nuYOin",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-vg2hAB",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_corpus/confirmationui-recv-ysk3Rj",
    "content": ""
  },
  {
    "path": "trusty/confirmationui/fuzz/msg_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <BufferAllocator/BufferAllocator.h>\n#include <TrustyIpc.h>\n#include <iostream>\n#include <stdlib.h>\n#include <sys/mman.h>\n#include <time.h>\n#include <trusty/coverage/coverage.h>\n#include <trusty/fuzz/counters.h>\n#include <trusty/fuzz/utils.h>\n#include <trusty/tipc.h>\n#include <unistd.h>\n\nusing android::trusty::coverage::CoverageRecord;\nusing android::trusty::fuzz::ExtraCounters;\nusing android::trusty::fuzz::TrustyApp;\n\n#define countof(arr) (sizeof(arr) / sizeof(arr[0]))\n\n#define TIPC_DEV \"/dev/trusty-ipc-dev0\"\n#define CONFIRMATIONUI_PORT \"com.android.trusty.confirmationui\"\n#define CONFIRMATIONUI_MODULE_NAME \"confirmationui.syms.elf\"\n\n/* A request to render to screen may take a while. */\nconst size_t kTimeoutSeconds = 60;\n\n/* ConfirmationUI TA's UUID is 7dee2364-c036-425b-b086-df0f6c233c1b */\nstatic struct uuid confirmationui_uuid = {\n    0x7dee2364,\n    0xc036,\n    0x425b,\n    {0xb0, 0x86, 0xdf, 0x0f, 0x6c, 0x23, 0x3c, 0x1b},\n};\n\nstatic CoverageRecord record(TIPC_DEV, &confirmationui_uuid, CONFIRMATIONUI_MODULE_NAME);\n\nstatic android::base::unique_fd dma_buf;\nstatic void* shm_base;\n\nextern \"C\" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {\n    auto ret = record.Open();\n    if (!ret.ok()) {\n        std::cerr << ret.error() << std::endl;\n        exit(-1);\n    }\n\n    BufferAllocator allocator;\n    dma_buf.reset(allocator.Alloc(kDmabufSystemHeapName, CONFIRMATIONUI_MAX_MSG_SIZE));\n    if (dma_buf < 0) {\n        std::cerr << \"Failed to allocate dma_buf\" << std::endl;\n        exit(-1);\n    }\n\n    shm_base = mmap(0, CONFIRMATIONUI_MAX_MSG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);\n    if (shm_base == MAP_FAILED) {\n        std::cerr << \"Failed to mmap() dma_buf\" << std::endl;\n        exit(-1);\n    }\n\n    return 0;\n}\n\nstatic bool Init(int chan, int dma_buf) {\n    confirmationui_hdr hdr = {\n        .cmd = CONFIRMATIONUI_CMD_INIT,\n    };\n    confirmationui_init_req args = {\n        .shm_len = CONFIRMATIONUI_MAX_MSG_SIZE,\n    };\n    iovec iov[] = {\n        {\n            .iov_base = &hdr,\n            .iov_len = sizeof(hdr),\n        },\n        {\n            .iov_base = &args,\n            .iov_len = sizeof(args),\n        },\n    };\n    trusty_shm shm = {\n        .fd = dma_buf,\n        .transfer = TRUSTY_SHARE,\n    };\n\n    int rc = tipc_send(chan, iov, countof(iov), &shm, 1);\n    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {\n        return false;\n    }\n\n    rc = read(chan, &hdr, sizeof(hdr));\n    if (rc != static_cast<int>(sizeof(hdr))) {\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool Msg(int chan, const uint8_t* data, size_t size) {\n    confirmationui_hdr hdr = {\n        .cmd = CONFIRMATIONUI_CMD_MSG,\n    };\n    confirmationui_msg_args args = {\n        .msg_len = static_cast<uint32_t>(size),\n    };\n    iovec iov[] = {\n        {\n            .iov_base = &hdr,\n            .iov_len = sizeof(hdr),\n        },\n        {\n            .iov_base = &args,\n            .iov_len = sizeof(args),\n        },\n    };\n\n    memset(shm_base, 0, CONFIRMATIONUI_MAX_MSG_SIZE);\n    memcpy(shm_base, data, size);\n\n    int rc = tipc_send(chan, iov, countof(iov), NULL, 0);\n    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {\n        return false;\n    }\n\n    rc = readv(chan, iov, countof(iov));\n    if (rc != static_cast<int>(sizeof(hdr) + sizeof(args))) {\n        return false;\n    }\n\n    return true;\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    ExtraCounters counters(&record);\n    counters.Reset();\n\n    TrustyApp ta(TIPC_DEV, CONFIRMATIONUI_PORT);\n    auto ret = ta.Connect();\n    if (!ret.ok()) {\n        android::trusty::fuzz::Abort();\n    }\n    int chan = *ta.GetRawFd();\n\n    alarm(kTimeoutSeconds);\n    bool success = Init(chan, dma_buf);\n    alarm(0);\n    if (!success) {\n        android::trusty::fuzz::Abort();\n    }\n\n    alarm(kTimeoutSeconds);\n    success = Msg(chan, data, size);\n    alarm(0);\n    if (!success) {\n        android::trusty::fuzz::Abort();\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "trusty/confirmationui/fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <TrustyConfirmationuiHal.h>\n#include <android-base/logging.h>\n#include <fuzzbinder/libbinder_ndk_driver.h>\n#include <fuzzer/FuzzedDataProvider.h>\n\nusing aidl::android::hardware::confirmationui::createTrustyConfirmationUI;\nusing aidl::android::hardware::confirmationui::IConfirmationUI;\nusing android::fuzzService;\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    auto confirmationui = createTrustyConfirmationUI();\n\n    fuzzService(confirmationui->asBinder().get(), FuzzedDataProvider(data, size));\n\n    return 0;\n}\n"
  },
  {
    "path": "trusty/confirmationui/include/TrustyConfirmationuiHal.h",
    "content": "/*\n * Copyright 2020, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <aidl/android/hardware/confirmationui/IConfirmationUI.h>\n\nnamespace aidl::android::hardware::confirmationui {\n\nstd::shared_ptr<IConfirmationUI> createTrustyConfirmationUI();\n\n}  // namespace aidl::android::hardware::confirmationui\n"
  },
  {
    "path": "trusty/confirmationui/include/TrustyIpc.h",
    "content": "/*\n * Copyright 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n/*\n * This interface is shared between Android and Trusty. There is a copy in each\n * repository. They must be kept in sync.\n */\n\n#define CONFIRMATIONUI_PORT \"com.android.trusty.confirmationui\"\n\n/**\n * enum confirmationui_cmd - command identifiers for ConfirmationUI interface\n * @CONFIRMATIONUI_RESP_BIT:  response bit set as part of response\n * @CONFIRMATIONUI_REQ_SHIFT: number of bits used by response bit\n * @CONFIRMATIONUI_CMD_INIT:  command to initialize session\n * @CONFIRMATIONUI_CMD_MSG:   command to send ConfirmationUI messages\n */\nenum confirmationui_cmd : uint32_t {\n    CONFIRMATIONUI_RESP_BIT = 1,\n    CONFIRMATIONUI_REQ_SHIFT = 1,\n\n    CONFIRMATIONUI_CMD_INIT = (1 << CONFIRMATIONUI_REQ_SHIFT),\n    CONFIRMATIONUI_CMD_MSG = (2 << CONFIRMATIONUI_REQ_SHIFT),\n};\n\n/**\n * struct confirmationui_hdr - header for ConfirmationUI messages\n * @cmd: command identifier\n *\n * Note that no messages return a status code. Any error on the server side\n * results in the connection being closed. So, operations can be assumed to be\n * successful if they return a response.\n */\nstruct confirmationui_hdr {\n    uint32_t cmd;\n};\n\n/**\n * struct confirmationui_init_req - arguments for request to initialize a\n *                                  session\n * @shm_len: length of memory region being shared\n *\n * A handle to a memory region must be sent along with this message. This memory\n * is send to ConfirmationUI messages.\n */\nstruct confirmationui_init_req {\n    uint32_t shm_len;\n};\n\n/**\n * struct confirmationui_msg_args - arguments for sending a message\n * @msg_len: length of message being sent\n *\n * Contents of the message are located in the shared memory region that is\n * established using %CONFIRMATIONUI_CMD_INIT.\n *\n * ConfirmationUI messages can travel both ways.\n */\nstruct confirmationui_msg_args {\n    uint32_t msg_len;\n};\n\n#define CONFIRMATIONUI_MAX_MSG_SIZE 0x2000\n"
  },
  {
    "path": "trusty/confirmationui/service.cpp",
    "content": "/*\n * Copyright 2020, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/logging.h>\n#include <android/binder_manager.h>\n#include <android/binder_process.h>\n\n#include <TrustyConfirmationuiHal.h>\n\nusing ::aidl::android::hardware::confirmationui::createTrustyConfirmationUI;\nusing ::aidl::android::hardware::confirmationui::IConfirmationUI;\n\nint main() {\n    ABinderProcess_setThreadPoolMaxThreadCount(0);\n\n    auto confirmationui = createTrustyConfirmationUI();\n\n    const auto instance = std::string(IConfirmationUI::descriptor) + \"/default\";\n    binder_status_t status =\n        AServiceManager_addService(confirmationui->asBinder().get(), instance.c_str());\n    CHECK_EQ(status, STATUS_OK) << \"Could not register \" << instance;\n\n    ABinderProcess_joinThreadPool();\n    return EXIT_FAILURE;\n}\n"
  },
  {
    "path": "trusty/coverage/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libtrusty_coverage\",\n    vendor_available: true,\n    srcs: [\n        \"coverage.cpp\",\n        \"uuid.cpp\",\n    ],\n    export_include_dirs: [\n        \"include\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libext2_uuid\",\n        \"liblog\",\n        \"libdmabufheap\",\n        \"libtrusty\",\n    ],\n}\n\ncc_test {\n    name: \"libtrusty_coverage_test\",\n    srcs: [\n        \"coverage_test.cpp\",\n    ],\n    static_libs: [\n        \"libtrusty_coverage\",\n        \"libtrusty\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n        \"libdmabufheap\",\n    ],\n    require_root: true,\n}\n"
  },
  {
    "path": "trusty/coverage/coverage.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Sourete Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"coverage\"\n\n#include <BufferAllocator/BufferAllocator.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <assert.h>\n#include <log/log.h>\n#include <stdio.h>\n#include <sys/mman.h>\n#include <sys/uio.h>\n#include <trusty/coverage/coverage.h>\n#include <trusty/coverage/record.h>\n#include <trusty/coverage/tipc.h>\n#include <trusty/tipc.h>\n#include <iostream>\n\n#define COVERAGE_CLIENT_PORT \"com.android.trusty.coverage.client\"\n\nnamespace android {\nnamespace trusty {\nnamespace coverage {\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing std::string;\nusing std::to_string;\nusing std::unique_ptr;\n\nCoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)\n    : tipc_dev_(std::move(tipc_dev)),\n      coverage_srv_fd_(-1),\n      uuid_(*uuid),\n      sancov_filename_(),\n      record_len_(0),\n      shm_(NULL),\n      shm_len_(0) {}\n\nCoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid, string module_name)\n    : tipc_dev_(std::move(tipc_dev)),\n      coverage_srv_fd_(-1),\n      uuid_(*uuid),\n      sancov_filename_(module_name + \".\" + to_string(getpid()) + \".sancov\"),\n      record_len_(0),\n      shm_(NULL),\n      shm_len_(0) {}\n\nCoverageRecord::~CoverageRecord() {\n    if (shm_) {\n        if (sancov_filename_) {\n            auto res = SaveSancovFile(*sancov_filename_);\n            if (!res.ok()) {\n                ALOGE(\"Could not write sancov file for module: %s\\n\", sancov_filename_->c_str());\n            }\n        }\n\n        munmap((void*)shm_, shm_len_);\n    }\n}\n\nResult<void> CoverageRecord::Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp) {\n    int rc;\n\n    if (req_fd < 0) {\n        rc = write(coverage_srv_fd_, req, sizeof(*req));\n    } else {\n        iovec iov = {\n                .iov_base = req,\n                .iov_len = sizeof(*req),\n        };\n\n        trusty_shm shm = {\n                .fd = req_fd,\n                .transfer = TRUSTY_SHARE,\n        };\n\n        rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);\n    }\n\n    if (rc != (int)sizeof(*req)) {\n        return ErrnoError() << \"failed to send request to coverage server: \";\n    }\n\n    rc = read(coverage_srv_fd_, resp, sizeof(*resp));\n    if (rc != (int)sizeof(*resp)) {\n        return ErrnoError() << \"failed to read reply from coverage server: \";\n    }\n\n    if (resp->hdr.cmd != (req->hdr.cmd | COVERAGE_CLIENT_CMD_RESP_BIT)) {\n        return ErrnoError() << \"unknown response cmd: \" << resp->hdr.cmd;\n    }\n\n    return {};\n}\n\nResult<void> CoverageRecord::Open() {\n    coverage_client_req req;\n    coverage_client_resp resp;\n\n    if (shm_) {\n        return {}; /* already initialized */\n    }\n\n    int fd = tipc_connect(tipc_dev_.c_str(), COVERAGE_CLIENT_PORT);\n    if (fd < 0) {\n        // Don't error out to support fuzzing builds without coverage, e.g. for repros.\n        std::cerr << \"WARNING!!! Failed to connect to Trusty coverarge server.\" << std::endl;\n        return {};\n    }\n    coverage_srv_fd_.reset(fd);\n\n    req.hdr.cmd = COVERAGE_CLIENT_CMD_OPEN;\n    req.open_args.uuid = uuid_;\n    auto ret = Rpc(&req, -1, &resp);\n    if (!ret.ok()) {\n        return Error() << \"failed to open coverage client: \" << ret.error();\n    }\n    record_len_ = resp.open_args.record_len;\n    shm_len_ = record_len_;\n\n    BufferAllocator allocator;\n\n    fd = allocator.Alloc(\"system\", shm_len_);\n    if (fd < 0) {\n        return ErrnoError() << \"failed to create dmabuf of size \" << shm_len_\n                            << \" err code: \" << fd;\n    }\n    unique_fd dma_buf(fd);\n\n    void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);\n    if (shm == MAP_FAILED) {\n        return ErrnoError() << \"failed to map memfd: \";\n    }\n\n    req.hdr.cmd = COVERAGE_CLIENT_CMD_SHARE_RECORD;\n    req.share_record_args.shm_len = shm_len_;\n    ret = Rpc(&req, dma_buf, &resp);\n    if (!ret.ok()) {\n        return Error() << \"failed to send shared memory: \" << ret.error();\n    }\n\n    shm_ = shm;\n    return {};\n}\n\nbool CoverageRecord::IsOpen() {\n    return shm_;\n}\n\nvoid CoverageRecord::ResetFullRecord() {\n    auto header_region = GetRegionBounds(COV_START);\n    if (!header_region.ok()) {\n        // If the header cannot be parsed, we can't reset the proper region yet.\n        return;\n    }\n\n    for (size_t i = header_region->second; i < shm_len_; i++) {\n        *((volatile uint8_t*)shm_ + i) = 0;\n    }\n}\n\nvoid CoverageRecord::ResetCounts() {\n    volatile uint8_t* begin = nullptr;\n    volatile uint8_t* end = nullptr;\n    GetRawCounts(&begin, &end);\n\n    for (volatile uint8_t* x = begin; x < end; x++) {\n        *x = 0;\n    }\n}\n\nvoid CoverageRecord::ResetPCs() {\n    volatile uintptr_t* begin = nullptr;\n    volatile uintptr_t* end = nullptr;\n    GetRawPCs(&begin, &end);\n\n    for (volatile uintptr_t* x = begin; x < end; x++) {\n        *x = 0;\n    }\n}\n\nResult<std::pair<size_t, size_t>> CoverageRecord::GetRegionBounds(uint32_t region_type) {\n    assert(shm_);\n\n    auto header = (volatile struct coverage_record_header*)shm_;\n\n    if (header->type != COV_START) {\n        return Error() << \"Header not yet valid\";\n    }\n\n    for (++header; header->type != COV_TOTAL_LENGTH; ++header) {\n        if (header->type == region_type) {\n            // Coverage record must end with a COV_TOTAL_LENGTH header entry, so\n            // it is always safe to read the next entry since we don't iterate\n            // over the COV_TOTAL_LENGTH entry.\n            return {{header->offset, (header + 1)->offset}};\n        }\n    }\n\n    return Error() << \"Could not find coverage region type: \" << region_type;\n}\n\nvoid CoverageRecord::GetRawData(volatile void** begin, volatile void** end) {\n    assert(shm_);\n\n    *begin = shm_;\n    *end = (uint8_t*)(*begin) + record_len_;\n}\n\nvoid CoverageRecord::GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end) {\n    auto region = GetRegionBounds(COV_8BIT_COUNTERS);\n    if (!region.ok()) {\n        *begin = 0;\n        *end = 0;\n        return;\n    }\n\n    assert(region->second <= record_len_);\n\n    *begin = (volatile uint8_t*)shm_ + region->first;\n    *end = (volatile uint8_t*)shm_ + region->second;\n}\n\nvoid CoverageRecord::GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end) {\n    auto region = GetRegionBounds(COV_INSTR_PCS);\n    if (!region.ok()) {\n        *begin = 0;\n        *end = 0;\n        return;\n    }\n\n    assert(region->second <= record_len_);\n\n    *begin = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->first);\n    *end = (volatile uintptr_t*)((volatile uint8_t*)shm_ + region->second);\n}\n\nuint64_t CoverageRecord::TotalEdgeCounts() {\n    assert(shm_);\n\n    uint64_t counter = 0;\n\n    volatile uint8_t* begin = NULL;\n    volatile uint8_t* end = NULL;\n\n    GetRawCounts(&begin, &end);\n\n    for (volatile uint8_t* x = begin; x < end; x++) {\n        counter += *x;\n    }\n\n    return counter;\n}\n\nResult<void> CoverageRecord::SaveSancovFile(const std::string& filename) {\n    android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));\n    if (!output_fd.ok()) {\n        return ErrnoError() << \"Could not open sancov file\";\n    }\n\n    uint64_t magic;\n    if (sizeof(uintptr_t) == 8) {\n        magic = 0xC0BFFFFFFFFFFF64;\n    } else if (sizeof(uintptr_t) == 4) {\n        magic = 0xC0BFFFFFFFFFFF32;\n    }\n    WriteFully(output_fd, &magic, sizeof(magic));\n\n    volatile uintptr_t* begin = nullptr;\n    volatile uintptr_t* end = nullptr;\n\n    GetRawPCs(&begin, &end);\n\n    for (volatile uintptr_t* pc_ptr = begin; pc_ptr < end; pc_ptr++) {\n        uintptr_t pc = *pc_ptr;\n        if (pc) {\n            WriteFully(output_fd, &pc, sizeof(pc));\n        }\n    }\n\n    return {};\n}\n\n}  // namespace coverage\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/coverage/coverage_test.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/stringprintf.h>\n#include <gtest/gtest.h>\n#include <trusty/coverage/coverage.h>\n#include <trusty/tipc.h>\n#include <array>\n#include <memory>\n\nusing android::base::unique_fd;\nusing std::array;\nusing std::make_unique;\nusing std::unique_ptr;\n\n#define TIPC_DEV \"/dev/trusty-ipc-dev0\"\n#define TEST_SRV_PORT \"com.android.trusty.sancov.test.srv\"\n#define TEST_SRV_MODULE \"srv.syms.elf\"\n\nnamespace android {\nnamespace trusty {\nnamespace coverage {\n\n/* Test server's UUID is 77f68803-c514-43ba-bdce-3254531c3d24 */\nstatic struct uuid test_srv_uuid = {\n        0x77f68803,\n        0xc514,\n        0x43ba,\n        {0xbd, 0xce, 0x32, 0x54, 0x53, 0x1c, 0x3d, 0x24},\n};\n\nclass CoverageTest : public ::testing::Test {\n  public:\n    void SetUp() override {\n        record_ = make_unique<CoverageRecord>(TIPC_DEV, &test_srv_uuid);\n        auto ret = record_->Open();\n        ASSERT_TRUE(ret.ok()) << ret.error();\n    }\n\n    void TearDown() override { record_.reset(); }\n\n    unique_ptr<CoverageRecord> record_;\n};\n\nTEST_F(CoverageTest, CoverageReset) {\n    record_->ResetFullRecord();\n    auto counter = record_->TotalEdgeCounts();\n    ASSERT_EQ(counter, 0);\n}\n\nTEST_F(CoverageTest, TestServerCoverage) {\n    unique_fd test_srv(tipc_connect(TIPC_DEV, TEST_SRV_PORT));\n    ASSERT_GE(test_srv, 0);\n\n    uint32_t mask = (uint32_t)-1;\n    uint32_t magic = 0xdeadbeef;\n    uint64_t high_watermark = 0;\n\n    for (size_t i = 1; i < sizeof(magic) * 8; i++) {\n        /* Reset coverage */\n        record_->ResetCounts();\n\n        /* Send message to test server */\n        uint32_t msg = magic & ~(mask << i);\n        int rc = write(test_srv, &msg, sizeof(msg));\n        ASSERT_EQ(rc, sizeof(msg));\n\n        /* Read message from test server */\n        rc = read(test_srv, &msg, sizeof(msg));\n        ASSERT_EQ(rc, sizeof(msg));\n\n        /* Count number of non-unique blocks executed */\n        auto counter = record_->TotalEdgeCounts();\n        /* Each consecutive input should exercise more or same blocks */\n        ASSERT_GE(counter, high_watermark);\n        high_watermark = counter;\n\n        auto sancov_filename = android::base::StringPrintf(\n                \"/data/local/tmp/\" TEST_SRV_MODULE \".%d.sancov\", getpid());\n        auto res = record_->SaveSancovFile(sancov_filename);\n        ASSERT_TRUE(res.ok());\n    }\n\n    ASSERT_GT(high_watermark, 0);\n}\n\n}  // namespace coverage\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/coverage/include/trusty/coverage/coverage.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <optional>\n#include <string>\n\n#include <android-base/result.h>\n#include <android-base/unique_fd.h>\n#include <stdint.h>\n#include <trusty/coverage/tipc.h>\n\nnamespace android {\nnamespace trusty {\nnamespace coverage {\n\nusing android::base::Result;\nusing android::base::unique_fd;\n\nclass CoverageRecord {\n  public:\n    /**\n     * Create a coverage record interface. Coverage will not be written to a\n     * sancov output file on completion.\n     */\n    CoverageRecord(std::string tipc_dev, struct uuid* uuid);\n\n    /**\n     * Create a coverage record interface. On destruction, write this coverage\n     * to the given sancov filename.\n     */\n    CoverageRecord(std::string tipc_dev, struct uuid* uuid, std::string module_name);\n\n    ~CoverageRecord();\n    Result<void> Open();\n    bool IsOpen();\n    void ResetFullRecord();\n    void ResetCounts();\n    void ResetPCs();\n    void GetRawData(volatile void** begin, volatile void** end);\n    void GetRawCounts(volatile uint8_t** begin, volatile uint8_t** end);\n    void GetRawPCs(volatile uintptr_t** begin, volatile uintptr_t** end);\n    uint64_t TotalEdgeCounts();\n\n    /**\n     * Save the current set of observed PCs to the given filename.\n     * The resulting .sancov file can be parsed via the LLVM sancov tool to see\n     * coverage statistics and visualize coverage.\n     */\n    Result<void> SaveSancovFile(const std::string& filename);\n\n  private:\n    Result<void> Rpc(coverage_client_req* req, int req_fd, coverage_client_resp* resp);\n\n    Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);\n\n    std::string tipc_dev_;\n    unique_fd coverage_srv_fd_;\n    struct uuid uuid_;\n    std::optional<std::string> sancov_filename_;\n    size_t record_len_;\n    volatile void* shm_;\n    size_t shm_len_;\n};\n\n}  // namespace coverage\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/coverage/include/trusty/coverage/record.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* This file needs to be kept in-sync with its counterpart on Trusty side:\n * trusty/user/base/lib/coverage/common/include/lib/coverage/common/record.h */\n\n#pragma once\n\n#include <stdint.h>\n\n/**\n * enum coverage_record_type - Coverage region header type\n * @COV_START: Magic header start marker\n * @COV_8BIT_COUNTERS: 8bit counter for each instrumentation point\n * @COV_INSTR_PCS: Pointer length offset of each instrumentation point from the\n *                 start of the binary\n * @COV_TOTAL_LENGTH: Total length of the entire coverage record, must be the\n *                    last header item.\n *\n * Describes the type of a region of the coverage record. See &struct\n * coverage_record_header.\n */\nenum coverage_record_type {\n    COV_START = 0x434f5652,\n    COV_8BIT_COUNTERS = 1,\n    COV_INSTR_PCS = 2,\n    COV_TOTAL_LENGTH = 0,\n};\n\n/**\n * struct coverage_record_header - Header entry describing a region of the\n * coverage record.\n * @type: type of the region, must be one of @enum coverage_record_type\n * @offset: offset from the beginning of the header to the start of the region\n *\n * Coverage records start with a header which is a list of struct\n * coverage_record_header, beginning with an entry with type COV_START and\n * terminated with an entry with type COV_TOTAL_LENGTH. Each of these header\n * entries corresponds to a region of the record, with the offset indicating the\n * offset of the start of that region from the beginning of the record (i.e. the\n * beginning of the header). Each record type and offset is 32-bit field with\n * native endianness. The first header item must be COV_START with a 0 offset.\n * The COV_START entry should be initialized when the coverage header is\n * complete and ready for consumption by the client, because coverage record\n * initialization happens asynchronously. The final header item,\n * COV_TOTAL_LENGTH, which must always be present, indicates the total length of\n * the coverage record, including the header.\n *\n * Coverage regions should be contiguous, so the end of one region is the start\n * of the next, and the coverage header must be in the same order as the regions\n * in the record body. Thus we can compute the length of a region by subtracting\n * the region's offset from the offset of the next header item.\n */\nstruct coverage_record_header {\n    uint32_t type;\n    uint32_t offset;\n};\n"
  },
  {
    "path": "trusty/coverage/include/trusty/coverage/tipc.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* This file needs to be kept in-sync with it's counterpart on Trusty side */\n\n#pragma once\n\n#include <stdint.h>\n#include <trusty/coverage/uuid.h>\n\n#define COVERAGE_CLIENT_PORT \"com.android.trusty.coverage.client\"\n\nenum coverage_client_cmd {\n    COVERAGE_CLIENT_CMD_RESP_BIT = 1U,\n    COVERAGE_CLIENT_CMD_SHIFT = 1U,\n    COVERAGE_CLIENT_CMD_OPEN = (1U << COVERAGE_CLIENT_CMD_SHIFT),\n    COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << COVERAGE_CLIENT_CMD_SHIFT),\n};\n\nstruct coverage_client_hdr {\n    uint32_t cmd;\n};\n\nstruct coverage_client_open_req {\n    struct uuid uuid;\n};\n\nstruct coverage_client_open_resp {\n    uint32_t record_len;\n};\n\nstruct coverage_client_share_record_req {\n    uint32_t shm_len;\n};\n\nstruct coverage_client_req {\n    struct coverage_client_hdr hdr;\n    union {\n        struct coverage_client_open_req open_args;\n        struct coverage_client_share_record_req share_record_args;\n    };\n};\n\nstruct coverage_client_resp {\n    struct coverage_client_hdr hdr;\n    union {\n        struct coverage_client_open_resp open_args;\n    };\n};\n"
  },
  {
    "path": "trusty/coverage/include/trusty/coverage/uuid.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\nstruct uuid {\n    uint32_t time_low;\n    uint16_t time_mid;\n    uint16_t time_hi_and_version;\n    uint8_t clock_seq_and_node[8];\n};\n\n/**\n * str_to_uuid() - Converts a C string into a uuid\n * @str: C-string representation of the uuid\n * @uuid: &struct uuid to fill with the converted uuid\n *\n * Return: true on success, false otherwise\n */\nbool str_to_uuid(const char* str, struct uuid* uuid);\n"
  },
  {
    "path": "trusty/coverage/uuid.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Sourete Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <string.h>\n#include <trusty/coverage/uuid.h>\n#include <uuid.h>\n\n#include <stdio.h>\n\nstatic uint16_t reverse_u16(uint16_t u) {\n    return u << 8 | u >> 8;\n}\n\nstatic uint32_t reverse_u32(uint32_t u) {\n    return reverse_u16((uint16_t)u) << 16 | reverse_u16(u >> 16);\n}\n\nbool str_to_uuid(const char* str, struct uuid* uuid) {\n    uuid_t uu;\n    static_assert(sizeof(uu) == sizeof(*uuid));\n\n    if (uuid_parse(str, uu)) {\n        return false;\n    }\n\n    memcpy(uuid, uu, sizeof(*uuid));\n    uuid->time_low = reverse_u32(uuid->time_low);\n    uuid->time_mid = reverse_u16(uuid->time_mid);\n    uuid->time_hi_and_version = reverse_u16(uuid->time_hi_and_version);\n    return true;\n}\n"
  },
  {
    "path": "trusty/fuzz/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"trusty_fuzzer_defaults\",\n    shared_libs: [\n        \"libtrusty\",\n        \"libtrusty_coverage\",\n        \"libtrusty_fuzz_utils\",\n        \"libbase\",\n        \"liblog\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    fuzz_config: {\n        fuzz_on_haiku_host: false,\n    },\n}\n\ncc_library {\n    name: \"libtrusty_fuzz_utils\",\n    srcs: [\n        \"counters.cpp\",\n        \"utils.cpp\",\n    ],\n    export_include_dirs: [\"include\"],\n    shared_libs: [\n        \"libtrusty_coverage\",\n        \"libbase\",\n        \"liblog\",\n        \"libtrusty\",\n    ],\n}\n\n// Generic TIPC fuzzer, must parameterized using:\n//  -DTRUSTY_APP_PORT=<port name of TA being fuzzed>\n//  -DTRUSTY_APP_UUID=<UUID of TA being fuzzed>\n//  -DTRUSTY_APP_FILENAME=<name of symbolized elf binary of the TA>\nfilegroup {\n    name: \"trusty_tipc_fuzzer\",\n    srcs: [\"tipc_fuzzer.cpp\"],\n}\n"
  },
  {
    "path": "trusty/fuzz/counters.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Sourete Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"trusty-fuzz-counters\"\n\n#include <trusty/fuzz/counters.h>\n\n#include <android-base/logging.h>\n#include <assert.h>\n#include <log/log.h>\n#include <string.h>\n#include <trusty/coverage/coverage.h>\n#include <trusty/coverage/tipc.h>\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\n\n/*\n * We don't know how many counters the coverage record will contain. So, eyeball\n * the size of this section.\n */\nstatic const size_t kMaxNumCounters = 0x10000;\n__attribute__((section(\"__libfuzzer_extra_counters\"))) volatile uint8_t counters[kMaxNumCounters];\n\nnamespace android {\nnamespace trusty {\nnamespace fuzz {\n\nExtraCounters::ExtraCounters(coverage::CoverageRecord* record) : record_(record) {\n    if (!record_->IsOpen()) {\n        return;\n    }\n\n    volatile uint8_t* begin = NULL;\n    volatile uint8_t* end = NULL;\n    record_->GetRawCounts(&begin, &end);\n    assert(end - begin <= sizeof(counters));\n}\n\nExtraCounters::~ExtraCounters() {\n    if (!record_->IsOpen()) {\n        return;\n    }\n\n    Flush();\n}\n\nvoid ExtraCounters::Reset() {\n    if (!record_->IsOpen()) {\n        return;\n    }\n    record_->ResetCounts();\n    memset_explicit(const_cast<uint8_t*>(counters), 0, sizeof(counters));\n}\n\nvoid ExtraCounters::Flush() {\n    volatile uint8_t* begin = NULL;\n    volatile uint8_t* end = NULL;\n\n    record_->GetRawCounts(&begin, &end);\n    if (!begin || !end) {\n        ALOGE(\"Could not get raw counts from coverage record\\n\");\n        return;\n    }\n\n    size_t num_counters = end - begin;\n    if (num_counters > kMaxNumCounters) {\n        ALOGE(\"Too many counters (%zu) to fit in the extra counters section!\\n\", num_counters);\n        num_counters = kMaxNumCounters;\n    }\n    for (size_t i = 0; i < num_counters; i++) {\n        *(counters + i) = *(begin + i);\n    }\n}\n\n}  // namespace fuzz\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/fuzz/include/trusty/fuzz/counters.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <android-base/result.h>\n#include <trusty/coverage/coverage.h>\n\nnamespace android {\nnamespace trusty {\nnamespace fuzz {\n\nclass ExtraCounters {\n  public:\n    ExtraCounters(coverage::CoverageRecord* record);\n    ~ExtraCounters();\n\n    void Reset();\n    void Flush();\n\n  private:\n    coverage::CoverageRecord* record_;\n};\n\n}  // namespace fuzz\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/fuzz/include/trusty/fuzz/utils.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <string>\n\n#include <android-base/result.h>\n#include <android-base/unique_fd.h>\n\n#define TIPC_MAX_MSG_SIZE 4096\n\nnamespace android {\nnamespace trusty {\nnamespace fuzz {\n\nclass TrustyApp {\n  public:\n    TrustyApp(std::string tipc_dev, std::string ta_port);\n\n    android::base::Result<void> Connect();\n    android::base::Result<void> Read(void* buf, size_t len);\n    android::base::Result<void> Write(const void* buf, size_t len);\n    void Disconnect();\n\n    android::base::Result<int> GetRawFd();\n\n  private:\n    std::string tipc_dev_;\n    std::string ta_port_;\n    android::base::unique_fd ta_fd_;\n};\n\nvoid Abort();\n\n}  // namespace fuzz\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/fuzz/test/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_fuzz {\n    name: \"trusty_test_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\":trusty_tipc_fuzzer\"],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.sancov.test.srv\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"77f68803-c514-43ba-bdce-3254531c3d24\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"srv.syms.elf\\\"\",\n    ],\n    fuzz_config: {\n        fuzz_on_haiku_device: false,\n    },\n}\n"
  },
  {
    "path": "trusty/fuzz/tipc_fuzzer.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/result.h>\n#include <fuzzer/FuzzedDataProvider.h>\n#include <stdlib.h>\n#include <trusty/coverage/coverage.h>\n#include <trusty/coverage/uuid.h>\n#include <trusty/fuzz/counters.h>\n#include <trusty/fuzz/utils.h>\n#include <unistd.h>\n#include <iostream>\n#include <memory>\n\nusing android::base::Result;\nusing android::trusty::coverage::CoverageRecord;\nusing android::trusty::fuzz::ExtraCounters;\nusing android::trusty::fuzz::TrustyApp;\n\n#define TIPC_DEV \"/dev/trusty-ipc-dev0\"\n\n#ifndef TRUSTY_APP_PORT\n#error \"Port name must be parameterized using -DTRUSTY_APP_PORT.\"\n#endif\n\n#ifndef TRUSTY_APP_UUID\n#error \"UUID must be parameterized using -DTRUSTY_APP_UUID.\"\n#endif\n\n#ifndef TRUSTY_APP_FILENAME\n#error \"Binary file name must be parameterized using -DTRUSTY_APP_FILENAME.\"\n#endif\n\n#ifdef TRUSTY_APP_MAX_CONNECTIONS\nconstexpr size_t MAX_CONNECTIONS = TRUSTY_APP_MAX_CONNECTIONS;\n#else\nconstexpr size_t MAX_CONNECTIONS = 1;\n#endif\n\nstatic std::unique_ptr<CoverageRecord> record;\n\nextern \"C\" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {\n    uuid module_uuid;\n\n    if (!str_to_uuid(TRUSTY_APP_UUID, &module_uuid)) {\n        std::cerr << \"Failed to parse UUID: \" << TRUSTY_APP_UUID << std::endl;\n        exit(-1);\n    }\n\n    /* Make sure lazy-loaded TAs have started and connected to coverage service. */\n    TrustyApp ta(TIPC_DEV, TRUSTY_APP_PORT);\n    auto ret = ta.Connect();\n    if (!ret.ok()) {\n        std::cerr << ret.error() << std::endl;\n        exit(-1);\n    }\n\n    record = std::make_unique<CoverageRecord>(TIPC_DEV, &module_uuid, TRUSTY_APP_FILENAME);\n    if (!record) {\n        std::cerr << \"Failed to allocate coverage record\" << std::endl;\n        exit(-1);\n    }\n\n    ret = record->Open();\n    if (!ret.ok()) {\n        std::cerr << ret.error() << std::endl;\n        exit(-1);\n    }\n    return 0;\n}\n\nvoid abortResult(Result<void> result) {\n    if (result.ok()) {\n        return;\n    }\n    std::cerr << result.error() << std::endl;\n    android::trusty::fuzz::Abort();\n}\n\nvoid testOneInput(FuzzedDataProvider& provider) {\n    std::vector<TrustyApp> trustyApps;\n\n    while (provider.remaining_bytes() > 0) {\n        static_assert(MAX_CONNECTIONS >= 1);\n\n        // Either\n        // 1. (20%) Add a new TA and connect.\n        // 2. (20%) Remove a TA.\n        // 3. (60%) Send a random message to a random TA.\n        auto add_ta = [&]() {\n            if (trustyApps.size() >= MAX_CONNECTIONS) {\n                return;\n            }\n            auto& ta = trustyApps.emplace_back(TIPC_DEV, TRUSTY_APP_PORT);\n            abortResult(ta.Connect());\n        };\n        auto remove_ta = [&]() {\n            if (trustyApps.empty()) {\n                return;\n            }\n            trustyApps.pop_back();\n        };\n        auto send_message = [&]() {\n            if (trustyApps.empty()) {\n                return;\n            }\n\n            // Choose a random TA.\n            const auto i = provider.ConsumeIntegralInRange<size_t>(0, trustyApps.size() - 1);\n            std::swap(trustyApps[i], trustyApps.back());\n            auto& ta = trustyApps.back();\n\n            // Send a random message.\n            const auto data = provider.ConsumeRandomLengthString();\n            abortResult(ta.Write(data.data(), data.size()));\n\n            std::array<uint8_t, TIPC_MAX_MSG_SIZE> buf;\n            abortResult(ta.Read(buf.data(), buf.size()));\n\n            // Reconnect to ensure that the service is still up.\n            ta.Disconnect();\n            abortResult(ta.Connect());\n        };\n        const std::function<void()> options[] = {\n                add_ta,                                    // 1x: 20%\n                remove_ta,                                 // 1x: 20%\n                send_message, send_message, send_message,  // 3x: 60%\n        };\n\n        provider.PickValueInArray(options)();\n    }\n}\n\nextern \"C\" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {\n    ExtraCounters counters(record.get());\n    counters.Reset();\n\n    FuzzedDataProvider provider(data, size);\n    testOneInput(provider);\n    return 0;\n}\n"
  },
  {
    "path": "trusty/fuzz/utils.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Sourete Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"trusty-fuzz-utils\"\n\n#include <trusty/fuzz/utils.h>\n\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <linux/ioctl.h>\n#include <linux/types.h>\n#include <linux/uio.h>\n#include <log/log_read.h>\n#include <time.h>\n#include <trusty/tipc.h>\n#include <iostream>\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\nusing android::base::unique_fd;\n\nnamespace {\n\nconst size_t kTimeoutSeconds = 5;\nconst std::string kTrustyLogTag = \"trusty-log\";\n\nconst time_t kInitialTime = time(nullptr);\n\nvoid PrintTrustyLog() {\n    auto logger_list = android_logger_list_open(LOG_ID_KERNEL, ANDROID_LOG_NONBLOCK, 1000, 0);\n    if (logger_list == nullptr) {\n        std::cerr << \"Could not open android kernel log\\n\";\n        return;\n    }\n\n    while (true) {\n        log_msg log_msg;\n        int rc = android_logger_list_read(logger_list, &log_msg);\n        if (rc < 0) {\n            break;\n        }\n        if (log_msg.entry.sec < kInitialTime) {\n            continue;\n        }\n        char* msg = log_msg.msg();\n        if (msg) {\n            std::string line(msg, log_msg.entry.len);\n            if (line.find(kTrustyLogTag) != std::string::npos) {\n                std::cerr << line.substr(kTrustyLogTag.length() + 2) << std::endl;\n            }\n        }\n    }\n\n    android_logger_list_free(logger_list);\n}\n\n}  // namespace\n\nnamespace android {\nnamespace trusty {\nnamespace fuzz {\n\nTrustyApp::TrustyApp(std::string tipc_dev, std::string ta_port)\n    : tipc_dev_(tipc_dev), ta_port_(ta_port), ta_fd_(-1) {}\n\nResult<void> TrustyApp::Connect() {\n    alarm(kTimeoutSeconds);\n    int fd = tipc_connect(tipc_dev_.c_str(), ta_port_.c_str());\n    alarm(0);\n    if (fd < 0) {\n        return ErrnoError() << \"failed to open TIPC device: \";\n    }\n    ta_fd_.reset(fd);\n\n    return {};\n}\n\nResult<void> TrustyApp::Read(void* buf, size_t len) {\n    if (ta_fd_ == -1) {\n        return Error() << \"TA is not connected to yet: \";\n    }\n\n    alarm(kTimeoutSeconds);\n    int rc = read(ta_fd_, buf, len);\n    alarm(0);\n    if (rc < 0) {\n        return Error() << \"failed to read TIPC message from TA: \";\n    }\n\n    return {};\n}\n\nResult<void> TrustyApp::Write(const void* buf, size_t len) {\n    if (ta_fd_ == -1) {\n        return Error() << \"TA is not connected to yet: \";\n    }\n\n    alarm(kTimeoutSeconds);\n    int rc = write(ta_fd_, buf, len);\n    alarm(0);\n    if (rc < 0) {\n        return Error() << \"failed to write TIPC message to TA: \";\n    }\n\n    return {};\n}\n\nResult<int> TrustyApp::GetRawFd() {\n    if (ta_fd_ == -1) {\n        return Error() << \"TA is not connected to yet: \";\n    }\n\n    return ta_fd_;\n}\n\nvoid TrustyApp::Disconnect() {\n    ta_fd_.reset();\n}\n\nvoid Abort() {\n    PrintTrustyLog();\n    exit(-1);\n}\n\n}  // namespace fuzz\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/gatekeeper/Android.bp",
    "content": "// Copyright (C) 2015 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\n// WARNING: Everything listed here will be built on ALL platforms,\n// including x86, the emulator, and the SDK.  Modules must be uniquely\n// named (liblights.panda), and must build everywhere, or limit themselves\n// to only building on ARM if they include assembly. Individual makefiles\n// are responsible for having their own logic, for fine-grained control.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"android.hardware.gatekeeper-service.trusty\",\n    vendor: true,\n    relative_install_path: \"hw\",\n    init_rc: [\"android.hardware.gatekeeper-service.trusty.rc\"],\n\n    srcs: [\n        \"service.cpp\",\n        \"trusty_gatekeeper_ipc.c\",\n        \"trusty_gatekeeper.cpp\",\n    ],\n\n    cflags: [\n        \"-fvisibility=hidden\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    static_libs: [\n        \"libgflags\",\n    ],\n\n    shared_libs: [\n        \"android.hardware.gatekeeper-V1-ndk\",\n        \"libbase\",\n        \"libbinder_ndk\",\n        \"libgatekeeper\",\n        \"libhardware\",\n        \"libutils\",\n        \"liblog\",\n        \"libcutils\",\n        \"libtrusty\",\n    ],\n\n    vintf_fragments: [\"android.hardware.gatekeeper-service.trusty.xml\"],\n}\n"
  },
  {
    "path": "trusty/gatekeeper/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n      {\n        \"name\": \"VtsHalGatekeeperTargetTest\"\n      }\n  ]\n}\n"
  },
  {
    "path": "trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.rc",
    "content": "service vendor.gatekeeper_default /vendor/bin/hw/android.hardware.gatekeeper-service.trusty \\\n                                          --dev ${ro.hardware.trusty_ipc_dev.gatekeeper:-/dev/trusty-ipc-dev0}\n    class hal\n    user system\n    group system\n"
  },
  {
    "path": "trusty/gatekeeper/android.hardware.gatekeeper-service.trusty.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"aidl\">\n        <name>android.hardware.gatekeeper</name>\n        <version>1</version>\n        <interface>\n            <name>IGatekeeper</name>\n            <instance>default</instance>\n        </interface>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "trusty/gatekeeper/fuzz/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_fuzz {\n    name: \"trusty_gatekeeper_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\":trusty_tipc_fuzzer\"],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.gatekeeper\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"38ba0cdc-df0e-11e4-9869-233fb6ae4795\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"gatekeeper.syms.elf\\\"\",\n    ],\n    fuzz_config: {\n       cc: [\"trong@google.com\"],\n    },\n\n    // The initial corpus for this fuzzer was derived by dumping messages from\n    // the `secure_env` emulator interface for cuttlefish while enrolling a new\n    // password in the emulator.\n    corpus: [\"corpus/*\"],\n}\n"
  },
  {
    "path": "trusty/gatekeeper/gatekeeper_ipc.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#define GATEKEEPER_PORT \"com.android.trusty.gatekeeper\"\n#define GATEKEEPER_MAX_BUFFER_LENGTH 1024\n\nenum gatekeeper_command {\n    GK_REQ_SHIFT = 1,\n    GK_RESP_BIT = 1,\n\n    GK_ENROLL = (0 << GK_REQ_SHIFT),\n    GK_VERIFY = (1 << GK_REQ_SHIFT),\n    GK_DELETE_USER = (2 << GK_REQ_SHIFT),\n    GK_DELETE_ALL_USERS = (3 << GK_REQ_SHIFT),\n};\n\n/**\n * gatekeeper_message - Serial header for communicating with GK server\n * @cmd: the command, one of ENROLL, VERIFY. Payload must be a serialized\n *       buffer of the corresponding request object.\n * @payload: start of the serialized command specific payload\n */\nstruct gatekeeper_message {\n    uint32_t cmd;\n    uint8_t payload[0];\n};\n\n"
  },
  {
    "path": "trusty/gatekeeper/service.cpp",
    "content": "/*\n * Copyright (C) 2019 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#define LOG_TAG \"android.hardware.gatekeeper-service.trusty\"\n\n#include <android-base/logging.h>\n#include <android/binder_manager.h>\n#include <android/binder_process.h>\n#include <getopt.h>\n\n#include \"trusty_gatekeeper.h\"\n#include \"trusty_gatekeeper_ipc.h\"\n\nusing aidl::android::hardware::gatekeeper::TrustyGateKeeperDevice;\n\nstatic const char* _sopts = \"hD:\";\nstatic const struct option _lopts[] = {\n        {\"help\", no_argument, 0, 'h'},\n        {\"dev\", required_argument, 0, 'D'},\n        {0, 0, 0, 0},\n};\n\nstatic const char* usage =\n        \"Usage: %s [options]\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h, --help            prints this message and exit\\n\"\n        \"  -D, --dev name        Trusty device name\\n\"\n        \"\\n\";\n\nstatic const char* usage_long = \"\\n\";\n\nstatic void print_usage_and_exit(const char* prog, int code, bool verbose) {\n    fprintf(stderr, usage, prog);\n    if (verbose) {\n        fprintf(stderr, \"%s\", usage_long);\n    }\n    exit(code);\n}\n\nstatic void parse_options(int argc, char** argv) {\n    int c;\n    int oidx = 0;\n\n    while (1) {\n        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);\n        if (c == -1) {\n            break; /* done */\n        }\n\n        switch (c) {\n            case 'D':\n                trusty_gatekeeper_set_dev_name(optarg);\n                break;\n\n            case 'h':\n                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);\n                break;\n\n            default:\n                print_usage_and_exit(argv[0], EXIT_FAILURE, false);\n        }\n    }\n}\n\nint main(int argc, char** argv) {\n    parse_options(argc, argv);\n\n    ABinderProcess_setThreadPoolMaxThreadCount(0);\n\n    std::shared_ptr<TrustyGateKeeperDevice> gatekeeper =\n            ndk::SharedRefBase::make<TrustyGateKeeperDevice>();\n\n    const std::string instance = std::string() + TrustyGateKeeperDevice::descriptor + \"/default\";\n    binder_status_t status =\n            AServiceManager_addService(gatekeeper->asBinder().get(), instance.c_str());\n    CHECK_EQ(status, STATUS_OK);\n\n    ABinderProcess_joinThreadPool();\n\n    return -1;  // Should never get here.\n}\n"
  },
  {
    "path": "trusty/gatekeeper/trusty_gatekeeper.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"TrustyGateKeeper\"\n\n#include <endian.h>\n#include <limits>\n\n#include <android-base/logging.h>\n#include <gatekeeper/password_handle.h>\n#include <hardware/hw_auth_token.h>\n\n#include \"gatekeeper_ipc.h\"\n#include \"trusty_gatekeeper.h\"\n#include \"trusty_gatekeeper_ipc.h\"\n\nnamespace aidl::android::hardware::gatekeeper {\n\nusing ::gatekeeper::ERROR_INVALID;\nusing ::gatekeeper::ERROR_NONE;\nusing ::gatekeeper::ERROR_RETRY;\nusing ::gatekeeper::SizedBuffer;\nusing ::gatekeeper::VerifyRequest;\nusing ::gatekeeper::VerifyResponse;\n\nconstexpr const uint32_t SEND_BUF_SIZE = 8192;\nconstexpr const uint32_t RECV_BUF_SIZE = 8192;\n\nTrustyGateKeeperDevice::TrustyGateKeeperDevice() {\n    int rc = trusty_gatekeeper_connect();\n    if (rc < 0) {\n        LOG(ERROR) << \"Error initializing trusty session: \" << rc;\n    }\n\n    error_ = rc;\n}\n\nTrustyGateKeeperDevice::~TrustyGateKeeperDevice() {\n    trusty_gatekeeper_disconnect();\n}\n\nSizedBuffer vec2sized_buffer(const std::vector<uint8_t>& vec) {\n    if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) return {};\n    auto buffer = new uint8_t[vec.size()];\n    std::copy(vec.begin(), vec.end(), buffer);\n    return {buffer, static_cast<uint32_t>(vec.size())};\n}\n\nvoid sizedBuffer2AidlHWToken(SizedBuffer& buffer,\n                             android::hardware::security::keymint::HardwareAuthToken* aidlToken) {\n    const hw_auth_token_t* authToken =\n            reinterpret_cast<const hw_auth_token_t*>(buffer.Data<uint8_t>());\n    aidlToken->challenge = authToken->challenge;\n    aidlToken->userId = authToken->user_id;\n    aidlToken->authenticatorId = authToken->authenticator_id;\n    // these are in network order: translate to host\n    aidlToken->authenticatorType =\n            static_cast<android::hardware::security::keymint::HardwareAuthenticatorType>(\n                    be32toh(authToken->authenticator_type));\n    aidlToken->timestamp.milliSeconds = be64toh(authToken->timestamp);\n    aidlToken->mac.insert(aidlToken->mac.begin(), std::begin(authToken->hmac),\n                          std::end(authToken->hmac));\n}\n\n::ndk::ScopedAStatus TrustyGateKeeperDevice::enroll(\n        int32_t uid, const std::vector<uint8_t>& currentPasswordHandle,\n        const std::vector<uint8_t>& currentPassword, const std::vector<uint8_t>& desiredPassword,\n        GatekeeperEnrollResponse* rsp) {\n    if (error_ != 0) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    }\n\n    if (desiredPassword.size() == 0) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    }\n\n    EnrollRequest request(uid, vec2sized_buffer(currentPasswordHandle),\n                          vec2sized_buffer(desiredPassword), vec2sized_buffer(currentPassword));\n    EnrollResponse response;\n    auto error = Send(request, &response);\n    if (error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else if (response.error == ERROR_RETRY) {\n        *rsp = {ERROR_RETRY_TIMEOUT, static_cast<int32_t>(response.retry_timeout), 0, {}};\n        return ndk::ScopedAStatus::ok();\n    } else if (response.error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else {\n        const ::gatekeeper::password_handle_t* password_handle =\n                response.enrolled_password_handle.Data<::gatekeeper::password_handle_t>();\n        *rsp = {STATUS_OK,\n                0,\n                static_cast<int64_t>(password_handle->user_id),\n                {response.enrolled_password_handle.Data<uint8_t>(),\n                 (response.enrolled_password_handle.Data<uint8_t>() +\n                  response.enrolled_password_handle.size())}};\n    }\n    return ndk::ScopedAStatus::ok();\n}\n\n::ndk::ScopedAStatus TrustyGateKeeperDevice::verify(\n        int32_t uid, int64_t challenge, const std::vector<uint8_t>& enrolledPasswordHandle,\n        const std::vector<uint8_t>& providedPassword, GatekeeperVerifyResponse* rsp) {\n    if (error_ != 0) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    }\n\n    if (enrolledPasswordHandle.size() == 0) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    }\n\n    VerifyRequest request(uid, challenge, vec2sized_buffer(enrolledPasswordHandle),\n                          vec2sized_buffer(providedPassword));\n    VerifyResponse response;\n\n    auto error = Send(request, &response);\n    if (error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else if (response.error == ERROR_RETRY) {\n        *rsp = {ERROR_RETRY_TIMEOUT, static_cast<int32_t>(response.retry_timeout), {}};\n        return ndk::ScopedAStatus::ok();\n    } else if (response.error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else {\n        // On Success, return GatekeeperVerifyResponse with Success Status, timeout{0} and\n        // valid HardwareAuthToken.\n        *rsp = {response.request_reenroll ? STATUS_REENROLL : STATUS_OK, 0, {}};\n        // Convert the hw_auth_token_t to HardwareAuthToken in the response.\n        sizedBuffer2AidlHWToken(response.auth_token, &rsp->hardwareAuthToken);\n    }\n    return ndk::ScopedAStatus::ok();\n}\n\n::ndk::ScopedAStatus TrustyGateKeeperDevice::deleteUser(int32_t uid) {\n    if (error_ != 0) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    }\n\n    DeleteUserRequest request(uid);\n    DeleteUserResponse response;\n    auto error = Send(request, &response);\n\n    if (error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else if (response.error == ERROR_NOT_IMPLEMENTED) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_NOT_IMPLEMENTED));\n    } else if (response.error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else {\n        return ndk::ScopedAStatus::ok();\n    }\n}\n\n::ndk::ScopedAStatus TrustyGateKeeperDevice::deleteAllUsers() {\n    if (error_ != 0) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    }\n\n    DeleteAllUsersRequest request;\n    DeleteAllUsersResponse response;\n    auto error = Send(request, &response);\n\n    if (error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else if (response.error == ERROR_NOT_IMPLEMENTED) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_NOT_IMPLEMENTED));\n    } else if (response.error != ERROR_NONE) {\n        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));\n    } else {\n        return ndk::ScopedAStatus::ok();\n    }\n}\n\ngatekeeper_error_t TrustyGateKeeperDevice::Send(uint32_t command, const GateKeeperMessage& request,\n        GateKeeperMessage *response) {\n    uint32_t request_size = request.GetSerializedSize();\n    if (request_size > SEND_BUF_SIZE)\n        return ERROR_INVALID;\n    uint8_t send_buf[SEND_BUF_SIZE];\n    request.Serialize(send_buf, send_buf + request_size);\n\n    // Send it\n    uint8_t recv_buf[RECV_BUF_SIZE];\n    uint32_t response_size = RECV_BUF_SIZE;\n    int rc = trusty_gatekeeper_call(command, send_buf, request_size, recv_buf, &response_size);\n    if (rc < 0) {\n        LOG(ERROR) << \"error (\" << rc << \") calling gatekeeper TA\";\n        return ERROR_INVALID;\n    }\n\n    const gatekeeper_message *msg = reinterpret_cast<gatekeeper_message *>(recv_buf);\n    const uint8_t *payload = msg->payload;\n\n    return response->Deserialize(payload, payload + response_size);\n}\n\n}  // namespace aidl::android::hardware::gatekeeper\n"
  },
  {
    "path": "trusty/gatekeeper/trusty_gatekeeper.h",
    "content": "/*\n * Copyright (C) 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef TRUSTY_GATEKEEPER_H\n#define TRUSTY_GATEKEEPER_H\n\n#include <memory>\n\n#include <aidl/android/hardware/gatekeeper/BnGatekeeper.h>\n\n#include <gatekeeper/gatekeeper_messages.h>\n\n#include \"gatekeeper_ipc.h\"\n\nnamespace aidl::android::hardware::gatekeeper {\n\nusing aidl::android::hardware::gatekeeper::GatekeeperEnrollResponse;\nusing aidl::android::hardware::gatekeeper::GatekeeperVerifyResponse;\nusing ::gatekeeper::DeleteAllUsersRequest;\nusing ::gatekeeper::DeleteAllUsersResponse;\nusing ::gatekeeper::DeleteUserRequest;\nusing ::gatekeeper::DeleteUserResponse;\nusing ::gatekeeper::EnrollRequest;\nusing ::gatekeeper::EnrollResponse;\nusing ::gatekeeper::gatekeeper_error_t;\nusing ::gatekeeper::GateKeeperMessage;\nusing ::gatekeeper::VerifyRequest;\nusing ::gatekeeper::VerifyResponse;\n\nclass TrustyGateKeeperDevice : public BnGatekeeper {\n  public:\n    explicit TrustyGateKeeperDevice();\n    ~TrustyGateKeeperDevice();\n    /**\n     * Enrolls password_payload, which should be derived from a user selected pin or password,\n     * with the authentication factor private key used only for enrolling authentication\n     * factor data.\n     *\n     * Returns: 0 on success or an error code less than 0 on error.\n     * On error, enrolled_password_handle will not be allocated.\n     */\n    ::ndk::ScopedAStatus enroll(int32_t uid, const std::vector<uint8_t>& currentPasswordHandle,\n                                const std::vector<uint8_t>& currentPassword,\n                                const std::vector<uint8_t>& desiredPassword,\n                                GatekeeperEnrollResponse* _aidl_return) override;\n\n    /**\n     * Verifies provided_password matches enrolled_password_handle.\n     *\n     * Implementations of this module may retain the result of this call\n     * to attest to the recency of authentication.\n     *\n     * On success, writes the address of a verification token to auth_token,\n     * usable to attest password verification to other trusted services. Clients\n     * may pass NULL for this value.\n     *\n     * Returns: 0 on success or an error code less than 0 on error\n     * On error, verification token will not be allocated\n     */\n    ::ndk::ScopedAStatus verify(int32_t uid, int64_t challenge,\n                                const std::vector<uint8_t>& enrolledPasswordHandle,\n                                const std::vector<uint8_t>& providedPassword,\n                                GatekeeperVerifyResponse* _aidl_return) override;\n\n    ::ndk::ScopedAStatus deleteAllUsers() override;\n\n    ::ndk::ScopedAStatus deleteUser(int32_t uid) override;\n\n  private:\n    gatekeeper_error_t Send(uint32_t command, const GateKeeperMessage& request,\n                           GateKeeperMessage* response);\n\n    gatekeeper_error_t Send(const EnrollRequest& request, EnrollResponse* response) {\n        return Send(GK_ENROLL, request, response);\n    }\n\n    gatekeeper_error_t Send(const VerifyRequest& request, VerifyResponse* response) {\n        return Send(GK_VERIFY, request, response);\n    }\n\n    gatekeeper_error_t Send(const DeleteUserRequest& request, DeleteUserResponse* response) {\n        return Send(GK_DELETE_USER, request, response);\n    }\n\n    gatekeeper_error_t Send(const DeleteAllUsersRequest& request,\n                            DeleteAllUsersResponse* response) {\n        return Send(GK_DELETE_ALL_USERS, request, response);\n    }\n\n    int error_;\n};\n\n}  // namespace aidl::android::hardware::gatekeeper\n\n#endif\n"
  },
  {
    "path": "trusty/gatekeeper/trusty_gatekeeper_ipc.c",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"TrustyGateKeeper\"\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <log/log.h>\n#include <trusty/tipc.h>\n\n#include \"trusty_gatekeeper_ipc.h\"\n#include \"gatekeeper_ipc.h\"\n\nstatic const char* trusty_device_name = \"/dev/trusty-ipc-dev0\";\nstatic int handle_ = 0;\n\nvoid trusty_gatekeeper_set_dev_name(const char* device_name) {\n    trusty_device_name = device_name;\n}\n\nint trusty_gatekeeper_connect() {\n    int rc = tipc_connect(trusty_device_name, GATEKEEPER_PORT);\n    if (rc < 0) {\n        return rc;\n    }\n\n    handle_ = rc;\n    return 0;\n}\n\nint trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,\n                           uint32_t *out_size) {\n    if (handle_ == 0) {\n        ALOGE(\"not connected\\n\");\n        return -EINVAL;\n    }\n\n    size_t msg_size = in_size + sizeof(struct gatekeeper_message);\n    struct gatekeeper_message *msg = malloc(msg_size);\n    msg->cmd = cmd;\n    memcpy(msg->payload, in, in_size);\n\n    ssize_t rc = write(handle_, msg, msg_size);\n    free(msg);\n\n    if (rc < 0) {\n        ALOGE(\"failed to send cmd (%d) to %s: %s\\n\", cmd,\n                GATEKEEPER_PORT, strerror(errno));\n        return -errno;\n    }\n\n    rc = read(handle_, out, *out_size);\n    if (rc < 0) {\n        ALOGE(\"failed to retrieve response for cmd (%d) to %s: %s\\n\",\n                cmd, GATEKEEPER_PORT, strerror(errno));\n        return -errno;\n    }\n\n    if ((size_t) rc < sizeof(struct gatekeeper_message)) {\n        ALOGE(\"invalid response size (%d)\\n\", (int) rc);\n        return -EINVAL;\n    }\n\n    msg = (struct gatekeeper_message *) out;\n\n    if ((cmd | GK_RESP_BIT) != msg->cmd) {\n        ALOGE(\"invalid command (%d)\\n\", msg->cmd);\n        return -EINVAL;\n    }\n\n    *out_size = ((size_t) rc) - sizeof(struct gatekeeper_message);\n    return rc;\n}\n\nvoid trusty_gatekeeper_disconnect() {\n    if (handle_ != 0) {\n        tipc_close(handle_);\n    }\n}\n\n"
  },
  {
    "path": "trusty/gatekeeper/trusty_gatekeeper_ipc.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n__BEGIN_DECLS\n\nvoid trusty_gatekeeper_set_dev_name(const char* device_name);\nint trusty_gatekeeper_connect();\nint trusty_gatekeeper_call(uint32_t cmd, void *in, uint32_t in_size, uint8_t *out,\n                           uint32_t *out_size);\nvoid trusty_gatekeeper_disconnect();\n\n__END_DECLS\n"
  },
  {
    "path": "trusty/keymaster/3.0/TrustyKeymaster3Device.cpp",
    "content": "/*\n **\n ** Copyright 2018, The Android Open Source Project\n **\n ** Licensed under the Apache License, Version 2.0 (the \"License\");\n ** you may not use this file except in compliance with the License.\n ** You may obtain a copy of the License at\n **\n **     http://www.apache.org/licenses/LICENSE-2.0\n **\n ** Unless required by applicable law or agreed to in writing, software\n ** distributed under the License is distributed on an \"AS IS\" BASIS,\n ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ** See the License for the specific language governing permissions and\n ** limitations under the License.\n */\n\n#define LOG_TAG \"android.hardware.keymaster@3.0-impl.trusty\"\n\n#include <cutils/log.h>\n#include <keymaster/android_keymaster_messages.h>\n#include <keymaster/authorization_set.h>\n#include <keymaster_tags.h>\n#include <trusty_keymaster/TrustyKeymaster3Device.h>\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nusing ::keymaster::AbortOperationRequest;\nusing ::keymaster::AbortOperationResponse;\nusing ::keymaster::AddEntropyRequest;\nusing ::keymaster::AddEntropyResponse;\nusing ::keymaster::AttestKeyRequest;\nusing ::keymaster::AttestKeyResponse;\nusing ::keymaster::AuthorizationSet;\nusing ::keymaster::BeginOperationRequest;\nusing ::keymaster::BeginOperationResponse;\nusing ::keymaster::ExportKeyRequest;\nusing ::keymaster::ExportKeyResponse;\nusing ::keymaster::FinishOperationRequest;\nusing ::keymaster::FinishOperationResponse;\nusing ::keymaster::GenerateKeyRequest;\nusing ::keymaster::GenerateKeyResponse;\nusing ::keymaster::GetKeyCharacteristicsRequest;\nusing ::keymaster::GetKeyCharacteristicsResponse;\nusing ::keymaster::ImportKeyRequest;\nusing ::keymaster::ImportKeyResponse;\nusing ::keymaster::UpdateOperationRequest;\nusing ::keymaster::UpdateOperationResponse;\nusing ::keymaster::ng::Tag;\n\nnamespace keymaster {\n\nnamespace {\n\ninline keymaster_tag_t legacy_enum_conversion(const Tag value) {\n    return keymaster_tag_t(value);\n}\ninline Tag legacy_enum_conversion(const keymaster_tag_t value) {\n    return Tag(value);\n}\ninline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {\n    return keymaster_purpose_t(value);\n}\ninline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {\n    return keymaster_key_format_t(value);\n}\ninline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {\n    return ErrorCode(value);\n}\n\ninline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {\n    return keymaster_tag_get_type(tag);\n}\n\nclass KmParamSet : public keymaster_key_param_set_t {\n  public:\n    KmParamSet(const hidl_vec<KeyParameter>& keyParams) {\n        params = new keymaster_key_param_t[keyParams.size()];\n        length = keyParams.size();\n        for (size_t i = 0; i < keyParams.size(); ++i) {\n            auto tag = legacy_enum_conversion(keyParams[i].tag);\n            switch (typeFromTag(tag)) {\n                case KM_ENUM:\n                case KM_ENUM_REP:\n                    params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);\n                    break;\n                case KM_UINT:\n                case KM_UINT_REP:\n                    params[i] = keymaster_param_int(tag, keyParams[i].f.integer);\n                    break;\n                case KM_ULONG:\n                case KM_ULONG_REP:\n                    params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);\n                    break;\n                case KM_DATE:\n                    params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);\n                    break;\n                case KM_BOOL:\n                    if (keyParams[i].f.boolValue)\n                        params[i] = keymaster_param_bool(tag);\n                    else\n                        params[i].tag = KM_TAG_INVALID;\n                    break;\n                case KM_BIGNUM:\n                case KM_BYTES:\n                    params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],\n                                                     keyParams[i].blob.size());\n                    break;\n                case KM_INVALID:\n                default:\n                    params[i].tag = KM_TAG_INVALID;\n                    /* just skip */\n                    break;\n            }\n        }\n    }\n    KmParamSet(KmParamSet&& other) noexcept\n        : keymaster_key_param_set_t{other.params, other.length} {\n        other.length = 0;\n        other.params = nullptr;\n    }\n    KmParamSet(const KmParamSet&) = delete;\n    ~KmParamSet() { delete[] params; }\n};\n\ninline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {\n    hidl_vec<uint8_t> result;\n    result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);\n    return result;\n}\n\ninline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {\n    hidl_vec<uint8_t> result;\n    result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);\n    return result;\n}\n\ninline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {\n    hidl_vec<uint8_t> result;\n    result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());\n    return result;\n}\n\ninline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(\n        const keymaster_cert_chain_t& cert_chain) {\n    hidl_vec<hidl_vec<uint8_t>> result;\n    if (!cert_chain.entry_count || !cert_chain.entries) return result;\n\n    result.resize(cert_chain.entry_count);\n    for (size_t i = 0; i < cert_chain.entry_count; ++i) {\n        result[i] = kmBlob2hidlVec(cert_chain.entries[i]);\n    }\n\n    return result;\n}\n\nstatic inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {\n    hidl_vec<KeyParameter> result;\n    if (set.length == 0 || set.params == nullptr) return result;\n\n    result.resize(set.length);\n    keymaster_key_param_t* params = set.params;\n    for (size_t i = 0; i < set.length; ++i) {\n        auto tag = params[i].tag;\n        result[i].tag = legacy_enum_conversion(tag);\n        switch (typeFromTag(tag)) {\n            case KM_ENUM:\n            case KM_ENUM_REP:\n                result[i].f.integer = params[i].enumerated;\n                break;\n            case KM_UINT:\n            case KM_UINT_REP:\n                result[i].f.integer = params[i].integer;\n                break;\n            case KM_ULONG:\n            case KM_ULONG_REP:\n                result[i].f.longInteger = params[i].long_integer;\n                break;\n            case KM_DATE:\n                result[i].f.dateTime = params[i].date_time;\n                break;\n            case KM_BOOL:\n                result[i].f.boolValue = params[i].boolean;\n                break;\n            case KM_BIGNUM:\n            case KM_BYTES:\n                result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),\n                                             params[i].blob.data_length);\n                break;\n            case KM_INVALID:\n            default:\n                params[i].tag = KM_TAG_INVALID;\n                /* just skip */\n                break;\n        }\n    }\n    return result;\n}\n\nvoid addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,\n                         ::keymaster::AuthorizationSet* params) {\n    params->Clear();\n    if (clientId.size()) {\n        params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());\n    }\n    if (appData.size()) {\n        params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());\n    }\n}\n\n}  // anonymous namespace\n\nTrustyKeymaster3Device::TrustyKeymaster3Device(TrustyKeymaster* impl) : impl_(impl) {}\n\nTrustyKeymaster3Device::~TrustyKeymaster3Device() {}\n\nReturn<void> TrustyKeymaster3Device::getHardwareFeatures(getHardwareFeatures_cb _hidl_cb) {\n    _hidl_cb(true /* is_secure */, true /* supports_ec */,\n             true /* supports_symmetric_cryptography */, true /* supports_attestation */,\n             true /* supportsAllDigests */, \"TrustyKeymaster\", \"Google\");\n    return Void();\n}\n\nReturn<ErrorCode> TrustyKeymaster3Device::addRngEntropy(const hidl_vec<uint8_t>& data) {\n    if (data.size() == 0) return ErrorCode::OK;\n    AddEntropyRequest request(impl_->message_version());\n    request.random_data.Reinitialize(data.data(), data.size());\n\n    AddEntropyResponse response(impl_->message_version());\n    impl_->AddRngEntropy(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n\nReturn<void> TrustyKeymaster3Device::generateKey(const hidl_vec<KeyParameter>& keyParams,\n                                                 generateKey_cb _hidl_cb) {\n    GenerateKeyRequest request(impl_->message_version());\n    request.key_description.Reinitialize(KmParamSet(keyParams));\n\n    GenerateKeyResponse response(impl_->message_version());\n    impl_->GenerateKey(request, &response);\n\n    KeyCharacteristics resultCharacteristics;\n    hidl_vec<uint8_t> resultKeyBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultKeyBlob = kmBlob2hidlVec(response.key_blob);\n        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);\n        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster3Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,\n                                                           const hidl_vec<uint8_t>& clientId,\n                                                           const hidl_vec<uint8_t>& appData,\n                                                           getKeyCharacteristics_cb _hidl_cb) {\n    GetKeyCharacteristicsRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n    addClientAndAppData(clientId, appData, &request.additional_params);\n\n    GetKeyCharacteristicsResponse response(impl_->message_version());\n    impl_->GetKeyCharacteristics(request, &response);\n\n    KeyCharacteristics resultCharacteristics;\n    if (response.error == KM_ERROR_OK) {\n        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);\n        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster3Device::importKey(const hidl_vec<KeyParameter>& params,\n                                               KeyFormat keyFormat,\n                                               const hidl_vec<uint8_t>& keyData,\n                                               importKey_cb _hidl_cb) {\n    ImportKeyRequest request(impl_->message_version());\n    request.key_description.Reinitialize(KmParamSet(params));\n    request.key_format = legacy_enum_conversion(keyFormat);\n    request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());\n\n    ImportKeyResponse response(impl_->message_version());\n    impl_->ImportKey(request, &response);\n\n    KeyCharacteristics resultCharacteristics;\n    hidl_vec<uint8_t> resultKeyBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultKeyBlob = kmBlob2hidlVec(response.key_blob);\n        resultCharacteristics.teeEnforced = kmParamSet2Hidl(response.enforced);\n        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster3Device::exportKey(KeyFormat exportFormat,\n                                               const hidl_vec<uint8_t>& keyBlob,\n                                               const hidl_vec<uint8_t>& clientId,\n                                               const hidl_vec<uint8_t>& appData,\n                                               exportKey_cb _hidl_cb) {\n    ExportKeyRequest request(impl_->message_version());\n    request.key_format = legacy_enum_conversion(exportFormat);\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n    addClientAndAppData(clientId, appData, &request.additional_params);\n\n    ExportKeyResponse response(impl_->message_version());\n    impl_->ExportKey(request, &response);\n\n    hidl_vec<uint8_t> resultKeyBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultKeyBlob.setToExternal(response.key_data, response.key_data_length);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster3Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,\n                                               const hidl_vec<KeyParameter>& attestParams,\n                                               attestKey_cb _hidl_cb) {\n    AttestKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());\n    request.attest_params.Reinitialize(KmParamSet(attestParams));\n\n    AttestKeyResponse response(impl_->message_version());\n    impl_->AttestKey(request, &response);\n\n    hidl_vec<hidl_vec<uint8_t>> resultCertChain;\n    if (response.error == KM_ERROR_OK) {\n        resultCertChain = kmCertChain2Hidl(response.certificate_chain);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster3Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,\n                                                const hidl_vec<KeyParameter>& upgradeParams,\n                                                upgradeKey_cb _hidl_cb) {\n    UpgradeKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());\n    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));\n\n    UpgradeKeyResponse response(impl_->message_version());\n    impl_->UpgradeKey(request, &response);\n\n    if (response.error == KM_ERROR_OK) {\n        _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));\n    } else {\n        _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());\n    }\n    return Void();\n}\n\nReturn<ErrorCode> TrustyKeymaster3Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {\n    DeleteKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n\n    DeleteKeyResponse response(impl_->message_version());\n    impl_->DeleteKey(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n\nReturn<ErrorCode> TrustyKeymaster3Device::deleteAllKeys() {\n    DeleteAllKeysRequest request(impl_->message_version());\n    DeleteAllKeysResponse response(impl_->message_version());\n    impl_->DeleteAllKeys(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n\nReturn<ErrorCode> TrustyKeymaster3Device::destroyAttestationIds() {\n    return ErrorCode::UNIMPLEMENTED;\n}\n\nReturn<void> TrustyKeymaster3Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,\n                                           const hidl_vec<KeyParameter>& inParams,\n                                           begin_cb _hidl_cb) {\n    BeginOperationRequest request(impl_->message_version());\n    request.purpose = legacy_enum_conversion(purpose);\n    request.SetKeyMaterial(key.data(), key.size());\n    request.additional_params.Reinitialize(KmParamSet(inParams));\n\n    BeginOperationResponse response(impl_->message_version());\n    impl_->BeginOperation(request, &response);\n\n    hidl_vec<KeyParameter> resultParams(impl_->message_version());\n    if (response.error == KM_ERROR_OK) {\n        resultParams = kmParamSet2Hidl(response.output_params);\n    }\n\n    _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster3Device::update(uint64_t operationHandle,\n                                            const hidl_vec<KeyParameter>& inParams,\n                                            const hidl_vec<uint8_t>& input, update_cb _hidl_cb) {\n    UpdateOperationRequest request(impl_->message_version());\n    UpdateOperationResponse response(impl_->message_version());\n    hidl_vec<KeyParameter> resultParams;\n    hidl_vec<uint8_t> resultBlob;\n    uint32_t resultConsumed = 0;\n\n    request.op_handle = operationHandle;\n    request.additional_params.Reinitialize(KmParamSet(inParams));\n\n    size_t inp_size = input.size();\n    size_t ser_size = request.SerializedSize();\n\n    if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {\n        response.error = KM_ERROR_INVALID_INPUT_LENGTH;\n    } else {\n        if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {\n            inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;\n        }\n        request.input.Reinitialize(input.data(), inp_size);\n\n        impl_->UpdateOperation(request, &response);\n\n        if (response.error == KM_ERROR_OK) {\n            resultConsumed = response.input_consumed;\n            resultParams = kmParamSet2Hidl(response.output_params);\n            resultBlob = kmBuffer2hidlVec(response.output);\n        }\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster3Device::finish(uint64_t operationHandle,\n                                            const hidl_vec<KeyParameter>& inParams,\n                                            const hidl_vec<uint8_t>& input,\n                                            const hidl_vec<uint8_t>& signature,\n                                            finish_cb _hidl_cb) {\n    FinishOperationRequest request(impl_->message_version());\n    request.op_handle = operationHandle;\n    request.input.Reinitialize(input.data(), input.size());\n    request.signature.Reinitialize(signature.data(), signature.size());\n    request.additional_params.Reinitialize(KmParamSet(inParams));\n\n    FinishOperationResponse response(impl_->message_version());\n    impl_->FinishOperation(request, &response);\n\n    hidl_vec<KeyParameter> resultParams;\n    hidl_vec<uint8_t> resultBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultParams = kmParamSet2Hidl(response.output_params);\n        resultBlob = kmBuffer2hidlVec(response.output);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);\n    return Void();\n}\n\nReturn<ErrorCode> TrustyKeymaster3Device::abort(uint64_t operationHandle) {\n    AbortOperationRequest request(impl_->message_version());\n    request.op_handle = operationHandle;\n\n    AbortOperationResponse response(impl_->message_version());\n    impl_->AbortOperation(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n}  // namespace keymaster\n"
  },
  {
    "path": "trusty/keymaster/3.0/android.hardware.keymaster@3.0-service.trusty.rc",
    "content": "service vendor.keymaster-3-0 /vendor/bin/hw/android.hardware.keymaster@3.0-service.trusty\n    class early_hal\n    user nobody\n    group drmrpc\n"
  },
  {
    "path": "trusty/keymaster/3.0/service.cpp",
    "content": "/*\n**\n** Copyright 2018, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n\n#include <android-base/logging.h>\n#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>\n#include <hidl/HidlTransportSupport.h>\n#include <trusty_keymaster/TrustyKeymaster.h>\n#include <trusty_keymaster/TrustyKeymaster3Device.h>\n\nint main() {\n    ::android::hardware::configureRpcThreadpool(1, true);\n    auto trustyKeymaster = new keymaster::TrustyKeymaster();\n    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_3);\n    if (err != 0) {\n        LOG(FATAL) << \"Could not initialize TrustyKeymaster (\" << err << \")\";\n        return -1;\n    }\n\n    auto keymaster = new ::keymaster::TrustyKeymaster3Device(trustyKeymaster);\n\n    auto status = keymaster->registerAsService();\n    if (status != android::OK) {\n        LOG(FATAL) << \"Could not register service for Keymaster 3.0 (\" << status << \")\";\n        return -1;\n    }\n\n    android::hardware::joinRpcThreadpool();\n    return -1;  // Should never get here.\n}\n"
  },
  {
    "path": "trusty/keymaster/4.0/TrustyKeymaster4Device.cpp",
    "content": "/*\n **\n ** Copyright 2018, The Android Open Source Project\n **\n ** Licensed under the Apache License, Version 2.0 (the \"License\");\n ** you may not use this file except in compliance with the License.\n ** You may obtain a copy of the License at\n **\n **     http://www.apache.org/licenses/LICENSE-2.0\n **\n ** Unless required by applicable law or agreed to in writing, software\n ** distributed under the License is distributed on an \"AS IS\" BASIS,\n ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ** See the License for the specific language governing permissions and\n ** limitations under the License.\n */\n\n#define LOG_TAG \"android.hardware.keymaster@4.0-impl.trusty\"\n\n#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>\n#include <cutils/log.h>\n#include <keymaster/android_keymaster_messages.h>\n#include <keymaster/authorization_set.h>\n#include <keymaster_tags.h>\n#include <trusty_keymaster/TrustyKeymaster4Device.h>\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nusing ::keymaster::AbortOperationRequest;\nusing ::keymaster::AbortOperationResponse;\nusing ::keymaster::AddEntropyRequest;\nusing ::keymaster::AddEntropyResponse;\nusing ::keymaster::AttestKeyRequest;\nusing ::keymaster::AttestKeyResponse;\nusing ::keymaster::AuthorizationSet;\nusing ::keymaster::BeginOperationRequest;\nusing ::keymaster::BeginOperationResponse;\nusing ::keymaster::ExportKeyRequest;\nusing ::keymaster::ExportKeyResponse;\nusing ::keymaster::FinishOperationRequest;\nusing ::keymaster::FinishOperationResponse;\nusing ::keymaster::GenerateKeyRequest;\nusing ::keymaster::GenerateKeyResponse;\nusing ::keymaster::GetKeyCharacteristicsRequest;\nusing ::keymaster::GetKeyCharacteristicsResponse;\nusing ::keymaster::ImportKeyRequest;\nusing ::keymaster::ImportKeyResponse;\nusing ::keymaster::UpdateOperationRequest;\nusing ::keymaster::UpdateOperationResponse;\nusing ::keymaster::ng::Tag;\n\ntypedef ::android::hardware::keymaster::V3_0::Tag Tag3;\nusing ::android::hardware::keymaster::V4_0::Constants;\n\nnamespace keymaster {\nnamespace V4_0 {\nnamespace {\n\ninline keymaster_tag_t legacy_enum_conversion(const Tag value) {\n    return keymaster_tag_t(value);\n}\ninline Tag legacy_enum_conversion(const keymaster_tag_t value) {\n    return Tag(value);\n}\ninline keymaster_purpose_t legacy_enum_conversion(const KeyPurpose value) {\n    return keymaster_purpose_t(value);\n}\ninline keymaster_key_format_t legacy_enum_conversion(const KeyFormat value) {\n    return keymaster_key_format_t(value);\n}\n\ninline SecurityLevel legacy_enum_conversion(const keymaster_security_level_t value) {\n    return static_cast<SecurityLevel>(value);\n}\n\ninline hw_authenticator_type_t legacy_enum_conversion(const HardwareAuthenticatorType value) {\n    return static_cast<hw_authenticator_type_t>(value);\n}\n\ninline ErrorCode legacy_enum_conversion(const keymaster_error_t value) {\n    return ErrorCode(value);\n}\n\ninline keymaster_tag_type_t typeFromTag(const keymaster_tag_t tag) {\n    return keymaster_tag_get_type(tag);\n}\n\n/*\n * injectAuthToken translates a KM4 authToken into a legacy AUTH_TOKEN tag\n *\n * Currently, system/keymaster's reference implementation only accepts this\n * method for passing an auth token, so until that changes we need to\n * translate to the old format.\n */\ninline hidl_vec<KeyParameter> injectAuthToken(const hidl_vec<KeyParameter>& keyParamsBase,\n                                              const HardwareAuthToken& authToken) {\n    std::vector<KeyParameter> keyParams(keyParamsBase);\n    const size_t mac_len = static_cast<size_t>(Constants::AUTH_TOKEN_MAC_LENGTH);\n    /*\n     * mac.size() == 0 indicates no token provided, so we should not copy.\n     * mac.size() != mac_len means it is incompatible with the old\n     *   hw_auth_token_t structure. This is forbidden by spec, but to be safe\n     *   we only copy if mac.size() == mac_len, e.g. there is an authToken\n     *   with a hw_auth_token_t compatible MAC.\n     */\n    if (authToken.mac.size() == mac_len) {\n        KeyParameter p;\n        p.tag = static_cast<Tag>(Tag3::AUTH_TOKEN);\n        p.blob.resize(sizeof(hw_auth_token_t));\n\n        hw_auth_token_t* auth_token = reinterpret_cast<hw_auth_token_t*>(p.blob.data());\n        auth_token->version = 0;\n        auth_token->challenge = authToken.challenge;\n        auth_token->user_id = authToken.userId;\n        auth_token->authenticator_id = authToken.authenticatorId;\n        auth_token->authenticator_type =\n                htobe32(static_cast<uint32_t>(authToken.authenticatorType));\n        auth_token->timestamp = htobe64(authToken.timestamp);\n        static_assert(mac_len == sizeof(auth_token->hmac));\n        memcpy(auth_token->hmac, authToken.mac.data(), mac_len);\n        keyParams.push_back(p);\n    }\n\n    return hidl_vec<KeyParameter>(std::move(keyParams));\n}\n\nclass KmParamSet : public keymaster_key_param_set_t {\n  public:\n    KmParamSet(const hidl_vec<KeyParameter>& keyParams) {\n        params = new keymaster_key_param_t[keyParams.size()];\n        length = keyParams.size();\n        for (size_t i = 0; i < keyParams.size(); ++i) {\n            auto tag = legacy_enum_conversion(keyParams[i].tag);\n            switch (typeFromTag(tag)) {\n                case KM_ENUM:\n                case KM_ENUM_REP:\n                    params[i] = keymaster_param_enum(tag, keyParams[i].f.integer);\n                    break;\n                case KM_UINT:\n                case KM_UINT_REP:\n                    params[i] = keymaster_param_int(tag, keyParams[i].f.integer);\n                    break;\n                case KM_ULONG:\n                case KM_ULONG_REP:\n                    params[i] = keymaster_param_long(tag, keyParams[i].f.longInteger);\n                    break;\n                case KM_DATE:\n                    params[i] = keymaster_param_date(tag, keyParams[i].f.dateTime);\n                    break;\n                case KM_BOOL:\n                    if (keyParams[i].f.boolValue)\n                        params[i] = keymaster_param_bool(tag);\n                    else\n                        params[i].tag = KM_TAG_INVALID;\n                    break;\n                case KM_BIGNUM:\n                case KM_BYTES:\n                    params[i] = keymaster_param_blob(tag, &keyParams[i].blob[0],\n                                                     keyParams[i].blob.size());\n                    break;\n                case KM_INVALID:\n                default:\n                    params[i].tag = KM_TAG_INVALID;\n                    /* just skip */\n                    break;\n            }\n        }\n    }\n    KmParamSet(KmParamSet&& other) noexcept\n        : keymaster_key_param_set_t{other.params, other.length} {\n        other.length = 0;\n        other.params = nullptr;\n    }\n    KmParamSet(const KmParamSet&) = delete;\n    ~KmParamSet() { delete[] params; }\n};\n\ninline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_key_blob_t& blob) {\n    hidl_vec<uint8_t> result;\n    result.setToExternal(const_cast<unsigned char*>(blob.key_material), blob.key_material_size);\n    return result;\n}\n\ninline hidl_vec<uint8_t> kmBlob2hidlVec(const keymaster_blob_t& blob) {\n    hidl_vec<uint8_t> result;\n    result.setToExternal(const_cast<unsigned char*>(blob.data), blob.data_length);\n    return result;\n}\n\ninline hidl_vec<uint8_t> kmBuffer2hidlVec(const ::keymaster::Buffer& buf) {\n    hidl_vec<uint8_t> result;\n    result.setToExternal(const_cast<unsigned char*>(buf.peek_read()), buf.available_read());\n    return result;\n}\n\ninline static hidl_vec<hidl_vec<uint8_t>> kmCertChain2Hidl(\n        const keymaster_cert_chain_t& cert_chain) {\n    hidl_vec<hidl_vec<uint8_t>> result;\n    if (!cert_chain.entry_count || !cert_chain.entries) return result;\n\n    result.resize(cert_chain.entry_count);\n    for (size_t i = 0; i < cert_chain.entry_count; ++i) {\n        result[i] = kmBlob2hidlVec(cert_chain.entries[i]);\n    }\n\n    return result;\n}\n\nstatic inline hidl_vec<KeyParameter> kmParamSet2Hidl(const keymaster_key_param_set_t& set) {\n    hidl_vec<KeyParameter> result;\n    if (set.length == 0 || set.params == nullptr) return result;\n\n    result.resize(set.length);\n    keymaster_key_param_t* params = set.params;\n    for (size_t i = 0; i < set.length; ++i) {\n        auto tag = params[i].tag;\n        result[i].tag = legacy_enum_conversion(tag);\n        switch (typeFromTag(tag)) {\n            case KM_ENUM:\n            case KM_ENUM_REP:\n                result[i].f.integer = params[i].enumerated;\n                break;\n            case KM_UINT:\n            case KM_UINT_REP:\n                result[i].f.integer = params[i].integer;\n                break;\n            case KM_ULONG:\n            case KM_ULONG_REP:\n                result[i].f.longInteger = params[i].long_integer;\n                break;\n            case KM_DATE:\n                result[i].f.dateTime = params[i].date_time;\n                break;\n            case KM_BOOL:\n                result[i].f.boolValue = params[i].boolean;\n                break;\n            case KM_BIGNUM:\n            case KM_BYTES:\n                result[i].blob.setToExternal(const_cast<unsigned char*>(params[i].blob.data),\n                                             params[i].blob.data_length);\n                break;\n            case KM_INVALID:\n            default:\n                params[i].tag = KM_TAG_INVALID;\n                /* just skip */\n                break;\n        }\n    }\n    return result;\n}\n\nvoid addClientAndAppData(const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,\n                         ::keymaster::AuthorizationSet* params) {\n    params->Clear();\n    if (clientId.size()) {\n        params->push_back(::keymaster::TAG_APPLICATION_ID, clientId.data(), clientId.size());\n    }\n    if (appData.size()) {\n        params->push_back(::keymaster::TAG_APPLICATION_DATA, appData.data(), appData.size());\n    }\n}\n\n}  // anonymous namespace\n\nTrustyKeymaster4Device::TrustyKeymaster4Device(TrustyKeymaster* impl) : impl_(impl) {}\n\nTrustyKeymaster4Device::~TrustyKeymaster4Device() {}\n\nReturn<void> TrustyKeymaster4Device::getHardwareInfo(getHardwareInfo_cb _hidl_cb) {\n    _hidl_cb(SecurityLevel::TRUSTED_ENVIRONMENT, \"TrustyKeymaster\", \"Google\");\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::getHmacSharingParameters(\n        getHmacSharingParameters_cb _hidl_cb) {\n    const GetHmacSharingParametersResponse response = impl_->GetHmacSharingParameters();\n    // response.params is not the same as the HIDL structure, we need to convert it\n    V4_0::HmacSharingParameters params;\n    params.seed.setToExternal(const_cast<uint8_t*>(response.params.seed.data),\n                              response.params.seed.data_length);\n    static_assert(sizeof(response.params.nonce) == params.nonce.size(), \"Nonce sizes don't match\");\n    memcpy(params.nonce.data(), response.params.nonce, params.nonce.size());\n    _hidl_cb(legacy_enum_conversion(response.error), params);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::computeSharedHmac(\n        const hidl_vec<HmacSharingParameters>& params, computeSharedHmac_cb _hidl_cb) {\n    ComputeSharedHmacRequest request(impl_->message_version());\n    request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];\n    request.params_array.num_params = params.size();\n    for (size_t i = 0; i < params.size(); ++i) {\n        request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};\n        static_assert(sizeof(request.params_array.params_array[i].nonce) ==\n                              decltype(params[i].nonce)::size(),\n                      \"Nonce sizes don't match\");\n        memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),\n               params[i].nonce.size());\n    }\n\n    auto response = impl_->ComputeSharedHmac(request);\n    hidl_vec<uint8_t> sharing_check;\n    if (response.error == KM_ERROR_OK) {\n        sharing_check = kmBlob2hidlVec(response.sharing_check);\n    }\n\n    _hidl_cb(legacy_enum_conversion(response.error), sharing_check);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::verifyAuthorization(\n        uint64_t challenge, const hidl_vec<KeyParameter>& parametersToVerify,\n        const HardwareAuthToken& authToken, verifyAuthorization_cb _hidl_cb) {\n    VerifyAuthorizationRequest request(impl_->message_version());\n    request.challenge = challenge;\n    request.parameters_to_verify.Reinitialize(KmParamSet(parametersToVerify));\n    request.auth_token.challenge = authToken.challenge;\n    request.auth_token.user_id = authToken.userId;\n    request.auth_token.authenticator_id = authToken.authenticatorId;\n    request.auth_token.authenticator_type = legacy_enum_conversion(authToken.authenticatorType);\n    request.auth_token.timestamp = authToken.timestamp;\n    KeymasterBlob mac(authToken.mac.data(), authToken.mac.size());\n    request.auth_token.mac = mac;\n\n    auto response = impl_->VerifyAuthorization(request);\n\n    ::android::hardware::keymaster::V4_0::VerificationToken token;\n    token.challenge = response.token.challenge;\n    token.timestamp = response.token.timestamp;\n    token.parametersVerified = kmParamSet2Hidl(response.token.parameters_verified);\n    token.securityLevel = legacy_enum_conversion(response.token.security_level);\n    token.mac = kmBlob2hidlVec(response.token.mac);\n\n    _hidl_cb(legacy_enum_conversion(response.error), token);\n\n    return Void();\n}\n\nReturn<ErrorCode> TrustyKeymaster4Device::addRngEntropy(const hidl_vec<uint8_t>& data) {\n    if (data.size() == 0) return ErrorCode::OK;\n    AddEntropyRequest request(impl_->message_version());\n    request.random_data.Reinitialize(data.data(), data.size());\n\n    AddEntropyResponse response(impl_->message_version());\n    impl_->AddRngEntropy(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n\nReturn<void> TrustyKeymaster4Device::generateKey(const hidl_vec<KeyParameter>& keyParams,\n                                                 generateKey_cb _hidl_cb) {\n    GenerateKeyRequest request(impl_->message_version());\n    request.key_description.Reinitialize(KmParamSet(keyParams));\n\n    GenerateKeyResponse response(impl_->message_version());\n    impl_->GenerateKey(request, &response);\n\n    KeyCharacteristics resultCharacteristics;\n    hidl_vec<uint8_t> resultKeyBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultKeyBlob = kmBlob2hidlVec(response.key_blob);\n        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);\n        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,\n                                                           const hidl_vec<uint8_t>& clientId,\n                                                           const hidl_vec<uint8_t>& appData,\n                                                           getKeyCharacteristics_cb _hidl_cb) {\n    GetKeyCharacteristicsRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n    addClientAndAppData(clientId, appData, &request.additional_params);\n\n    GetKeyCharacteristicsResponse response(impl_->message_version());\n    impl_->GetKeyCharacteristics(request, &response);\n\n    KeyCharacteristics resultCharacteristics;\n    if (response.error == KM_ERROR_OK) {\n        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);\n        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultCharacteristics);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::importKey(const hidl_vec<KeyParameter>& params,\n                                               KeyFormat keyFormat,\n                                               const hidl_vec<uint8_t>& keyData,\n                                               importKey_cb _hidl_cb) {\n    ImportKeyRequest request(impl_->message_version());\n    request.key_description.Reinitialize(KmParamSet(params));\n    request.key_format = legacy_enum_conversion(keyFormat);\n    request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());\n\n    ImportKeyResponse response(impl_->message_version());\n    impl_->ImportKey(request, &response);\n\n    KeyCharacteristics resultCharacteristics;\n    hidl_vec<uint8_t> resultKeyBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultKeyBlob = kmBlob2hidlVec(response.key_blob);\n        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);\n        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::importWrappedKey(\n        const hidl_vec<uint8_t>& wrappedKeyData, const hidl_vec<uint8_t>& wrappingKeyBlob,\n        const hidl_vec<uint8_t>& maskingKey, const hidl_vec<KeyParameter>& unwrappingParams,\n        uint64_t passwordSid, uint64_t biometricSid, importWrappedKey_cb _hidl_cb) {\n    ImportWrappedKeyRequest request(impl_->message_version());\n    request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());\n    request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());\n    request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());\n    request.additional_params.Reinitialize(KmParamSet(unwrappingParams));\n    request.password_sid = passwordSid;\n    request.biometric_sid = biometricSid;\n\n    ImportWrappedKeyResponse response(impl_->message_version());\n    impl_->ImportWrappedKey(request, &response);\n\n    KeyCharacteristics resultCharacteristics;\n    hidl_vec<uint8_t> resultKeyBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultKeyBlob = kmBlob2hidlVec(response.key_blob);\n        resultCharacteristics.hardwareEnforced = kmParamSet2Hidl(response.enforced);\n        resultCharacteristics.softwareEnforced = kmParamSet2Hidl(response.unenforced);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob, resultCharacteristics);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::exportKey(KeyFormat exportFormat,\n                                               const hidl_vec<uint8_t>& keyBlob,\n                                               const hidl_vec<uint8_t>& clientId,\n                                               const hidl_vec<uint8_t>& appData,\n                                               exportKey_cb _hidl_cb) {\n    ExportKeyRequest request(impl_->message_version());\n    request.key_format = legacy_enum_conversion(exportFormat);\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n    addClientAndAppData(clientId, appData, &request.additional_params);\n\n    ExportKeyResponse response(impl_->message_version());\n    impl_->ExportKey(request, &response);\n\n    hidl_vec<uint8_t> resultKeyBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultKeyBlob.setToExternal(response.key_data, response.key_data_length);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultKeyBlob);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::attestKey(const hidl_vec<uint8_t>& keyToAttest,\n                                               const hidl_vec<KeyParameter>& attestParams,\n                                               attestKey_cb _hidl_cb) {\n    AttestKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyToAttest.data(), keyToAttest.size());\n    request.attest_params.Reinitialize(KmParamSet(attestParams));\n\n    AttestKeyResponse response(impl_->message_version());\n    impl_->AttestKey(request, &response);\n\n    hidl_vec<hidl_vec<uint8_t>> resultCertChain;\n    if (response.error == KM_ERROR_OK) {\n        resultCertChain = kmCertChain2Hidl(response.certificate_chain);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultCertChain);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,\n                                                const hidl_vec<KeyParameter>& upgradeParams,\n                                                upgradeKey_cb _hidl_cb) {\n    UpgradeKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());\n    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));\n\n    UpgradeKeyResponse response(impl_->message_version());\n    impl_->UpgradeKey(request, &response);\n\n    if (response.error == KM_ERROR_OK) {\n        _hidl_cb(ErrorCode::OK, kmBlob2hidlVec(response.upgraded_key));\n    } else {\n        _hidl_cb(legacy_enum_conversion(response.error), hidl_vec<uint8_t>());\n    }\n    return Void();\n}\n\nReturn<ErrorCode> TrustyKeymaster4Device::deleteKey(const hidl_vec<uint8_t>& keyBlob) {\n    DeleteKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n\n    DeleteKeyResponse response(impl_->message_version());\n    impl_->DeleteKey(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n\nReturn<ErrorCode> TrustyKeymaster4Device::deleteAllKeys() {\n    DeleteAllKeysRequest request(impl_->message_version());\n    DeleteAllKeysResponse response(impl_->message_version());\n    impl_->DeleteAllKeys(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n\nReturn<ErrorCode> TrustyKeymaster4Device::destroyAttestationIds() {\n    return ErrorCode::UNIMPLEMENTED;\n}\n\nReturn<void> TrustyKeymaster4Device::begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,\n                                           const hidl_vec<KeyParameter>& inParams,\n                                           const HardwareAuthToken& authToken, begin_cb _hidl_cb) {\n    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);\n    BeginOperationRequest request(impl_->message_version());\n    request.purpose = legacy_enum_conversion(purpose);\n    request.SetKeyMaterial(key.data(), key.size());\n    request.additional_params.Reinitialize(KmParamSet(extendedParams));\n\n    BeginOperationResponse response(impl_->message_version());\n    impl_->BeginOperation(request, &response);\n\n    hidl_vec<KeyParameter> resultParams;\n    if (response.error == KM_ERROR_OK) {\n        resultParams = kmParamSet2Hidl(response.output_params);\n    }\n\n    _hidl_cb(legacy_enum_conversion(response.error), resultParams, response.op_handle);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::update(uint64_t operationHandle,\n                                            const hidl_vec<KeyParameter>& inParams,\n                                            const hidl_vec<uint8_t>& input,\n                                            const HardwareAuthToken& authToken,\n                                            const VerificationToken& verificationToken,\n                                            update_cb _hidl_cb) {\n    (void)verificationToken;\n    UpdateOperationRequest request(impl_->message_version());\n    UpdateOperationResponse response(impl_->message_version());\n    hidl_vec<KeyParameter> resultParams;\n    hidl_vec<uint8_t> resultBlob;\n    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);\n    uint32_t resultConsumed = 0;\n\n    request.op_handle = operationHandle;\n    request.additional_params.Reinitialize(KmParamSet(extendedParams));\n\n    size_t inp_size = input.size();\n    size_t ser_size = request.SerializedSize();\n\n    if (ser_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {\n        response.error = KM_ERROR_INVALID_INPUT_LENGTH;\n    } else {\n        if (ser_size + inp_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {\n            inp_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - ser_size;\n        }\n        request.input.Reinitialize(input.data(), inp_size);\n\n        impl_->UpdateOperation(request, &response);\n\n        if (response.error == KM_ERROR_OK) {\n            resultConsumed = response.input_consumed;\n            resultParams = kmParamSet2Hidl(response.output_params);\n            resultBlob = kmBuffer2hidlVec(response.output);\n        }\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultConsumed, resultParams, resultBlob);\n    return Void();\n}\n\nReturn<void> TrustyKeymaster4Device::finish(uint64_t operationHandle,\n                                            const hidl_vec<KeyParameter>& inParams,\n                                            const hidl_vec<uint8_t>& input,\n                                            const hidl_vec<uint8_t>& signature,\n                                            const HardwareAuthToken& authToken,\n                                            const VerificationToken& verificationToken,\n                                            finish_cb _hidl_cb) {\n    (void)verificationToken;\n    FinishOperationRequest request(impl_->message_version());\n    hidl_vec<KeyParameter> extendedParams = injectAuthToken(inParams, authToken);\n    request.op_handle = operationHandle;\n    request.input.Reinitialize(input.data(), input.size());\n    request.signature.Reinitialize(signature.data(), signature.size());\n    request.additional_params.Reinitialize(KmParamSet(extendedParams));\n\n    FinishOperationResponse response(impl_->message_version());\n    impl_->FinishOperation(request, &response);\n\n    hidl_vec<KeyParameter> resultParams;\n    hidl_vec<uint8_t> resultBlob;\n    if (response.error == KM_ERROR_OK) {\n        resultParams = kmParamSet2Hidl(response.output_params);\n        resultBlob = kmBuffer2hidlVec(response.output);\n    }\n    _hidl_cb(legacy_enum_conversion(response.error), resultParams, resultBlob);\n    return Void();\n}\n\nReturn<ErrorCode> TrustyKeymaster4Device::abort(uint64_t operationHandle) {\n    AbortOperationRequest request(impl_->message_version());\n    request.op_handle = operationHandle;\n\n    AbortOperationResponse response(impl_->message_version());\n    impl_->AbortOperation(request, &response);\n\n    return legacy_enum_conversion(response.error);\n}\n}  // namespace V4_0\n}  // namespace keymaster\n"
  },
  {
    "path": "trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.rc",
    "content": "service vendor.keymaster-4-0 /vendor/bin/hw/android.hardware.keymaster@4.0-service.trusty\n    class early_hal\n    user nobody\n    group drmrpc\n"
  },
  {
    "path": "trusty/keymaster/4.0/android.hardware.keymaster@4.0-service.trusty.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"hidl\">\n        <name>android.hardware.keymaster</name>\n        <transport>hwbinder</transport>\n        <fqname>@4.0::IKeymasterDevice/default</fqname>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "trusty/keymaster/4.0/service.cpp",
    "content": "/*\n**\n** Copyright 2018, The Android Open Source Project\n**\n** Licensed under the Apache License, Version 2.0 (the \"License\");\n** you may not use this file except in compliance with the License.\n** You may obtain a copy of the License at\n**\n**     http://www.apache.org/licenses/LICENSE-2.0\n**\n** Unless required by applicable law or agreed to in writing, software\n** distributed under the License is distributed on an \"AS IS\" BASIS,\n** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n** See the License for the specific language governing permissions and\n** limitations under the License.\n*/\n\n#include <android-base/logging.h>\n#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>\n#include <hidl/HidlTransportSupport.h>\n#include <trusty_keymaster/TrustyKeymaster.h>\n#include <trusty_keymaster/TrustyKeymaster4Device.h>\n\nint main() {\n    ::android::hardware::configureRpcThreadpool(1, true);\n    auto trustyKeymaster = new keymaster::TrustyKeymaster();\n    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMASTER_4);\n    if (err != 0) {\n        LOG(FATAL) << \"Could not initialize TrustyKeymaster (\" << err << \")\";\n        return -1;\n    }\n\n    auto keymaster = new ::keymaster::V4_0::TrustyKeymaster4Device(trustyKeymaster);\n\n    auto status = keymaster->registerAsService();\n    if (status != android::OK) {\n        LOG(FATAL) << \"Could not register service for Keymaster 4.0 (\" << status << \")\";\n        return -1;\n    }\n\n    android::hardware::joinRpcThreadpool();\n    return -1;  // Should never get here.\n}\n"
  },
  {
    "path": "trusty/keymaster/Android.bp",
    "content": "//\n// Copyright (C) 2015 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"android.hardware.keymaster@3.0-service.trusty\",\n    defaults: [\"hidl_defaults\"],\n    relative_install_path: \"hw\",\n    vendor: true,\n    init_rc: [\"3.0/android.hardware.keymaster@3.0-service.trusty.rc\"],\n    srcs: [\n        \"3.0/service.cpp\",\n        \"3.0/TrustyKeymaster3Device.cpp\",\n        \"ipc/trusty_keymaster_ipc.cpp\",\n        \"TrustyKeymaster.cpp\",\n    ],\n\n    local_include_dirs: [\"include\"],\n\n    shared_libs: [\n        \"liblog\",\n        \"libcutils\",\n        \"libdl\",\n        \"libbase\",\n        \"libutils\",\n        \"libhardware\",\n        \"libhidlbase\",\n        \"libtrusty\",\n        \"libkeymaster_messages\",\n        \"libkeymaster3device\",\n        \"android.hardware.keymaster@3.0\",\n    ],\n}\n\ncc_binary {\n    name: \"android.hardware.keymaster@4.0-service.trusty\",\n    defaults: [\"hidl_defaults\"],\n    relative_install_path: \"hw\",\n    vendor: true,\n    init_rc: [\"4.0/android.hardware.keymaster@4.0-service.trusty.rc\"],\n    srcs: [\n        \"4.0/service.cpp\",\n        \"4.0/TrustyKeymaster4Device.cpp\",\n        \"ipc/trusty_keymaster_ipc.cpp\",\n        \"TrustyKeymaster.cpp\",\n    ],\n\n    local_include_dirs: [\"include\"],\n\n    shared_libs: [\n        \"liblog\",\n        \"libcutils\",\n        \"libdl\",\n        \"libbase\",\n        \"libutils\",\n        \"libhardware\",\n        \"libhidlbase\",\n        \"libtrusty\",\n        \"libkeymaster_messages\",\n        \"libkeymaster4\",\n        \"android.hardware.keymaster@4.0\",\n    ],\n\n    vintf_fragments: [\"4.0/android.hardware.keymaster@4.0-service.trusty.xml\"],\n}\n\ncc_defaults {\n    name: \"android.hardware.security.keymint-service.trusty.defaults\",\n    relative_install_path: \"hw\",\n    vendor: true,\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n    ],\n    local_include_dirs: [\n        \"include\",\n    ],\n    srcs: [\n        \"TrustyKeymaster.cpp\",\n        \"ipc/trusty_keymaster_ipc.cpp\",\n        \"keymint/TrustyKeyMintDevice.cpp\",\n        \"keymint/TrustyKeyMintOperation.cpp\",\n        \"keymint/TrustyRemotelyProvisionedComponentDevice.cpp\",\n        \"keymint/TrustySecureClock.cpp\",\n        \"keymint/TrustySharedSecret.cpp\",\n        \"keymint/service.cpp\",\n    ],\n    shared_libs: [\n        \"android.hardware.security.keymint-V4-ndk\",\n        \"android.hardware.security.rkp-V3-ndk\",\n        \"android.hardware.security.secureclock-V1-ndk\",\n        \"android.hardware.security.sharedsecret-V1-ndk\",\n        \"lib_android_keymaster_keymint_utils\",\n        \"libbase\",\n        \"libbinder_ndk\",\n        \"libhardware\",\n        \"libkeymaster_messages\",\n        \"libkeymasterconfig\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libutils\",\n    ],\n}\n\n// keymint hal binary for keymint in Trusty TEE prebuilt\ncc_binary {\n    name: \"android.hardware.security.keymint-service.trusty\",\n    defaults: [\"android.hardware.security.keymint-service.trusty.defaults\"],\n    init_rc: [\"keymint/android.hardware.security.keymint-service.trusty.rc\"],\n    vintf_fragments: [\n        \"keymint/android.hardware.security.keymint-service.trusty.xml\",\n    ],\n    required: [\"android.hardware.hardware_keystore.xml\"],\n}\n\n// Keymint hal service in vendor, enabled by vendor apex.\n// This service is disabled by default and does not package a VINTF fragment.\n// This service can be enabled at boot via vendor apex:\n// - at boot, mount a vendor apex for module `com.android.hardware.keymint`\n// - have the vendor init.rc file enable the service when the associated\n//   apex is selected\n// - have the vendor apex package the vintf fragment and the required permissions\ncc_binary {\n    name: \"android.hardware.security.keymint-service.trusty_tee.cpp\",\n    defaults: [\"android.hardware.security.keymint-service.trusty.defaults\"],\n    init_rc: [\"keymint/android.hardware.security.keymint-service.trusty_tee.cpp.rc\"],\n}\n\n// vintf fragment packaged in vendor apex\nprebuilt_etc {\n    name: \"android.hardware.security.keymint-service.trusty.xml\",\n    sub_dir: \"vintf\",\n    vendor: true,\n    src: \"keymint/android.hardware.security.keymint-service.trusty.xml\",\n}\n\nprebuilt_etc {\n    name: \"keymaster_soft_attestation_keys.xml\",\n    vendor: true,\n    src: \"set_attestation_key/keymaster_soft_attestation_keys.xml\",\n}\n\ncc_library {\n    name: \"libtrusty_ipc\",\n    vendor: true,\n    srcs: [\"ipc/trusty_keymaster_ipc.cpp\"],\n    local_include_dirs: [\"include\"],\n    shared_libs: [\n        \"libc\",\n        \"libcrypto\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libhardware\",\n        \"libkeymaster_messages\",\n        \"libutils\",\n        \"libxml2\",\n    ],\n    export_include_dirs: [\"include\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\ncc_binary {\n    name: \"trusty_keymaster_set_attestation_key\",\n    vendor: true,\n\n    srcs: [\n        \"set_attestation_key/set_attestation_key.cpp\",\n        \"ipc/trusty_keymaster_ipc.cpp\",\n    ],\n\n    local_include_dirs: [\"include\"],\n\n    shared_libs: [\n        \"libc\",\n        \"libcrypto\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libhardware\",\n        \"libkeymaster_messages\",\n        \"libutils\",\n        \"libxml2\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\ncc_binary {\n    name: \"trusty_keymaster_set_attestation_ids\",\n    vendor: true,\n\n    srcs: [\n        \"set_attestation_ids/set_attestation_ids.cpp\",\n        \"ipc/trusty_keymaster_ipc.cpp\",\n    ],\n\n    local_include_dirs: [\"include\"],\n\n    shared_libs: [\n        \"libbase\",\n        \"libc\",\n        \"libcrypto\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libhardware\",\n        \"libkeymaster_messages\",\n        \"libutils\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\nprebuilt_etc {\n    name: \"rkp_uds_cert_test.xml\",\n    vendor: true,\n    src: \"set_uds_certs/rkp_uds_cert_test.xml\",\n}\n\ncc_binary {\n    name: \"trusty_rkp_set_uds_cert\",\n    vendor: true,\n\n    srcs: [\n        \"set_uds_certs/set_uds_certificates.cpp\",\n        \"ipc/trusty_keymaster_ipc.cpp\",\n    ],\n\n    local_include_dirs: [\"include\"],\n\n    shared_libs: [\n        \"libc\",\n        \"libcrypto\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libhardware\",\n        \"libkeymaster_messages\",\n        \"libutils\",\n        \"libxml2\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n"
  },
  {
    "path": "trusty/keymaster/TEST_MAPPING",
    "content": "{\n  \"presubmit\": [\n      {\n        \"name\": \"VtsAidlKeyMintTargetTest\"\n      },\n      {\n        \"name\": \"VtsHalRemotelyProvisionedComponentTargetTest\"\n      },\n      {\n        \"name\": \"RkpdAppUnitTests\"\n      },\n      {\n        \"name\": \"RkpdAppGoogleUnitTests\",\n        \"keywords\": [\"internal\"]\n      },\n      {\n        \"name\": \"RkpdAppIntegrationTests\"\n      },\n      {\n        \"name\": \"RkpdAppGoogleIntegrationTests\",\n        \"keywords\": [\"internal\"]\n      }\n  ]\n}\n"
  },
  {
    "path": "trusty/keymaster/TrustyKeymaster.cpp",
    "content": "/*\n * Copyright 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"trusty_keymaster_hal\"\n#include <android-base/logging.h>\n\n#include <keymaster/android_keymaster_messages.h>\n#include <keymaster/keymaster_configuration.h>\n#include <trusty_keymaster/TrustyKeymaster.h>\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nnamespace keymaster {\n\nint TrustyKeymaster::Initialize(KmVersion version) {\n    int err;\n\n    LOG(INFO) << \"Initializing TrustyKeymaster as KmVersion: \" << (int)version;\n\n    err = trusty_keymaster_connect();\n    if (err) {\n        LOG(ERROR) << \"Failed to connect to trusty keymaster (1st try)\" << err;\n        return err;\n    }\n\n    // Try GetVersion2 first.\n    GetVersion2Request versionReq;\n    versionReq.max_message_version = MessageVersion(version);\n    GetVersion2Response versionRsp = GetVersion2(versionReq);\n    if (versionRsp.error != KM_ERROR_OK) {\n        LOG(WARNING) << \"TA appears not to support GetVersion2, falling back (err = \"\n                     << versionRsp.error << \")\";\n\n        err = trusty_keymaster_connect();\n        if (err) {\n            LOG(FATAL) << \"Failed to connect to trusty keymaster (2nd try) \" << err;\n            return err;\n        }\n\n        GetVersionRequest versionReq;\n        GetVersionResponse versionRsp;\n        GetVersion(versionReq, &versionRsp);\n        if (versionRsp.error != KM_ERROR_OK) {\n            LOG(FATAL) << \"Failed to get TA version \" << versionRsp.error;\n            return -1;\n        } else {\n            keymaster_error_t error;\n            message_version_ = NegotiateMessageVersion(versionRsp, &error);\n            if (error != KM_ERROR_OK) {\n                LOG(FATAL) << \"Failed to negotiate message version \" << error;\n                return -1;\n            }\n        }\n    } else {\n        message_version_ = NegotiateMessageVersion(versionReq, versionRsp);\n    }\n\n    ConfigureRequest req(message_version());\n    req.os_version = GetOsVersion();\n    req.os_patchlevel = GetOsPatchlevel();\n\n    ConfigureResponse rsp(message_version());\n    Configure(req, &rsp);\n\n    if (rsp.error != KM_ERROR_OK) {\n        LOG(FATAL) << \"Failed to configure keymaster \" << rsp.error;\n        return -1;\n    }\n\n    // Set the vendor patchlevel to value retrieved from system property (which\n    // requires SELinux permission).\n    ConfigureVendorPatchlevelRequest vendor_req(message_version());\n    vendor_req.vendor_patchlevel = GetVendorPatchlevel();\n    ConfigureVendorPatchlevelResponse vendor_rsp = ConfigureVendorPatchlevel(vendor_req);\n    if (vendor_rsp.error != KM_ERROR_OK) {\n        LOG(ERROR) << \"Failed to configure keymaster vendor patchlevel: \" << vendor_rsp.error;\n        // Don't fail if this message isn't understood.\n    }\n\n    return 0;\n}\n\nTrustyKeymaster::TrustyKeymaster() {}\n\nTrustyKeymaster::~TrustyKeymaster() {\n    trusty_keymaster_disconnect();\n}\n\nstatic void ForwardCommand(enum keymaster_command command, const KeymasterMessage& req,\n                           KeymasterResponse* rsp) {\n    keymaster_error_t err;\n    err = trusty_keymaster_send(command, req, rsp);\n    if (err != KM_ERROR_OK) {\n        LOG(ERROR) << \"Cmd \" << command << \" returned error: \" << err;\n        rsp->error = err;\n    }\n}\n\nvoid TrustyKeymaster::GetVersion(const GetVersionRequest& request, GetVersionResponse* response) {\n    ForwardCommand(KM_GET_VERSION, request, response);\n}\n\nvoid TrustyKeymaster::SupportedAlgorithms(const SupportedAlgorithmsRequest& request,\n                                          SupportedAlgorithmsResponse* response) {\n    ForwardCommand(KM_GET_SUPPORTED_ALGORITHMS, request, response);\n}\n\nvoid TrustyKeymaster::SupportedBlockModes(const SupportedBlockModesRequest& request,\n                                          SupportedBlockModesResponse* response) {\n    ForwardCommand(KM_GET_SUPPORTED_BLOCK_MODES, request, response);\n}\n\nvoid TrustyKeymaster::SupportedPaddingModes(const SupportedPaddingModesRequest& request,\n                                            SupportedPaddingModesResponse* response) {\n    ForwardCommand(KM_GET_SUPPORTED_PADDING_MODES, request, response);\n}\n\nvoid TrustyKeymaster::SupportedDigests(const SupportedDigestsRequest& request,\n                                       SupportedDigestsResponse* response) {\n    ForwardCommand(KM_GET_SUPPORTED_DIGESTS, request, response);\n}\n\nvoid TrustyKeymaster::SupportedImportFormats(const SupportedImportFormatsRequest& request,\n                                             SupportedImportFormatsResponse* response) {\n    ForwardCommand(KM_GET_SUPPORTED_IMPORT_FORMATS, request, response);\n}\n\nvoid TrustyKeymaster::SupportedExportFormats(const SupportedExportFormatsRequest& request,\n                                             SupportedExportFormatsResponse* response) {\n    ForwardCommand(KM_GET_SUPPORTED_EXPORT_FORMATS, request, response);\n}\n\nvoid TrustyKeymaster::AddRngEntropy(const AddEntropyRequest& request,\n                                    AddEntropyResponse* response) {\n    ForwardCommand(KM_ADD_RNG_ENTROPY, request, response);\n}\n\nvoid TrustyKeymaster::Configure(const ConfigureRequest& request, ConfigureResponse* response) {\n    ForwardCommand(KM_CONFIGURE, request, response);\n}\n\nvoid TrustyKeymaster::GenerateKey(const GenerateKeyRequest& request,\n                                  GenerateKeyResponse* response) {\n    if (message_version_ < 4) {\n        // Pre-KeyMint we need to add TAG_CREATION_DATETIME if not provided by the caller.\n        GenerateKeyRequest datedRequest(request.message_version);\n        datedRequest.key_description = request.key_description;\n\n        if (!request.key_description.Contains(TAG_CREATION_DATETIME)) {\n            datedRequest.key_description.push_back(TAG_CREATION_DATETIME, java_time(time(NULL)));\n        }\n\n        ForwardCommand(KM_GENERATE_KEY, datedRequest, response);\n    } else {\n        ForwardCommand(KM_GENERATE_KEY, request, response);\n    }\n}\n\nvoid TrustyKeymaster::GenerateRkpKey(const GenerateRkpKeyRequest& request,\n                                     GenerateRkpKeyResponse* response) {\n    ForwardCommand(KM_GENERATE_RKP_KEY, request, response);\n}\n\nvoid TrustyKeymaster::GenerateCsr(const GenerateCsrRequest& request,\n                                  GenerateCsrResponse* response) {\n    ForwardCommand(KM_GENERATE_CSR, request, response);\n}\n\nvoid TrustyKeymaster::GenerateCsrV2(const GenerateCsrV2Request& request,\n                                    GenerateCsrV2Response* response) {\n    ForwardCommand(KM_GENERATE_CSR_V2, request, response);\n}\n\nvoid TrustyKeymaster::GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,\n                                            GetKeyCharacteristicsResponse* response) {\n    ForwardCommand(KM_GET_KEY_CHARACTERISTICS, request, response);\n}\n\nvoid TrustyKeymaster::ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response) {\n    ForwardCommand(KM_IMPORT_KEY, request, response);\n}\n\nvoid TrustyKeymaster::ImportWrappedKey(const ImportWrappedKeyRequest& request,\n                                       ImportWrappedKeyResponse* response) {\n    ForwardCommand(KM_IMPORT_WRAPPED_KEY, request, response);\n}\n\nvoid TrustyKeymaster::ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response) {\n    ForwardCommand(KM_EXPORT_KEY, request, response);\n}\n\nvoid TrustyKeymaster::AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response) {\n    ForwardCommand(KM_ATTEST_KEY, request, response);\n}\n\nvoid TrustyKeymaster::UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response) {\n    ForwardCommand(KM_UPGRADE_KEY, request, response);\n}\n\nvoid TrustyKeymaster::DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response) {\n    ForwardCommand(KM_DELETE_KEY, request, response);\n}\n\nvoid TrustyKeymaster::DeleteAllKeys(const DeleteAllKeysRequest& request,\n                                    DeleteAllKeysResponse* response) {\n    ForwardCommand(KM_DELETE_ALL_KEYS, request, response);\n}\n\nvoid TrustyKeymaster::DestroyAttestationIds(const DestroyAttestationIdsRequest& request,\n                                            DestroyAttestationIdsResponse* response) {\n    ForwardCommand(KM_DESTROY_ATTESTATION_IDS, request, response);\n}\n\nvoid TrustyKeymaster::BeginOperation(const BeginOperationRequest& request,\n                                     BeginOperationResponse* response) {\n    ForwardCommand(KM_BEGIN_OPERATION, request, response);\n}\n\nvoid TrustyKeymaster::UpdateOperation(const UpdateOperationRequest& request,\n                                      UpdateOperationResponse* response) {\n    ForwardCommand(KM_UPDATE_OPERATION, request, response);\n}\n\nvoid TrustyKeymaster::FinishOperation(const FinishOperationRequest& request,\n                                      FinishOperationResponse* response) {\n    ForwardCommand(KM_FINISH_OPERATION, request, response);\n}\n\nvoid TrustyKeymaster::AbortOperation(const AbortOperationRequest& request,\n                                     AbortOperationResponse* response) {\n    ForwardCommand(KM_ABORT_OPERATION, request, response);\n}\n\nGetHmacSharingParametersResponse TrustyKeymaster::GetHmacSharingParameters() {\n    GetHmacSharingParametersRequest request(message_version());\n    GetHmacSharingParametersResponse response(message_version());\n    ForwardCommand(KM_GET_HMAC_SHARING_PARAMETERS, request, &response);\n    return response;\n}\n\nComputeSharedHmacResponse TrustyKeymaster::ComputeSharedHmac(\n        const ComputeSharedHmacRequest& request) {\n    ComputeSharedHmacResponse response(message_version());\n    ForwardCommand(KM_COMPUTE_SHARED_HMAC, request, &response);\n    return response;\n}\n\nVerifyAuthorizationResponse TrustyKeymaster::VerifyAuthorization(\n        const VerifyAuthorizationRequest& request) {\n    VerifyAuthorizationResponse response(message_version());\n    ForwardCommand(KM_VERIFY_AUTHORIZATION, request, &response);\n    return response;\n}\n\nGetVersion2Response TrustyKeymaster::GetVersion2(const GetVersion2Request& request) {\n    GetVersion2Response response(message_version());\n    ForwardCommand(KM_GET_VERSION_2, request, &response);\n    return response;\n}\n\nEarlyBootEndedResponse TrustyKeymaster::EarlyBootEnded() {\n    EarlyBootEndedResponse response(message_version());\n    ForwardCommand(KM_EARLY_BOOT_ENDED, EarlyBootEndedRequest(message_version()), &response);\n    return response;\n}\n\nDeviceLockedResponse TrustyKeymaster::DeviceLocked(const DeviceLockedRequest& request) {\n    DeviceLockedResponse response(message_version());\n    ForwardCommand(KM_DEVICE_LOCKED, request, &response);\n    return response;\n}\n\nConfigureVendorPatchlevelResponse TrustyKeymaster::ConfigureVendorPatchlevel(\n        const ConfigureVendorPatchlevelRequest& request) {\n    ConfigureVendorPatchlevelResponse response(message_version());\n    ForwardCommand(KM_CONFIGURE_VENDOR_PATCHLEVEL, request, &response);\n    return response;\n}\n\nGetRootOfTrustResponse TrustyKeymaster::GetRootOfTrust(const GetRootOfTrustRequest& request) {\n    GetRootOfTrustResponse response(message_version());\n    ForwardCommand(KM_GET_ROOT_OF_TRUST, request, &response);\n    return response;\n}\n\nSetAdditionalAttestationInfoResponse TrustyKeymaster::SetAdditionalAttestationInfo(\n        const SetAdditionalAttestationInfoRequest& request) {\n    SetAdditionalAttestationInfoResponse response(message_version());\n    ForwardCommand(KM_SET_ADDITIONAL_ATTESTATION_INFO, request, &response);\n    return response;\n}\n\nGetHwInfoResponse TrustyKeymaster::GetHwInfo() {\n    GetHwInfoResponse response(message_version());\n    ForwardCommand(KM_GET_HW_INFO, GetHwInfoRequest(message_version()), &response);\n    return response;\n}\n\n}  // namespace keymaster\n"
  },
  {
    "path": "trusty/keymaster/fuzz/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_fuzz {\n    name: \"trusty_keymaster_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\":trusty_tipc_fuzzer\"],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.keymaster\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"keymaster.syms.elf\\\"\",\n    ],\n    fuzz_config: {\n       cc: [\"trong@google.com\", \"drysdale@google.com\"],\n       componentid: 1084733,\n       hotlists: [\"4271696\"],\n    },\n\n    // The initial corpus for this fuzzer was derived by dumping messages from\n    // the `secure_env` emulator interface for cuttlefish while running the\n    // keystore2 tests in the emulator.\n    corpus: [\"corpus/*\"],\n}\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustyKeyMintDevice.h",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <aidl/android/hardware/security/keymint/BnKeyMintDevice.h>\n#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>\n#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>\n\n#include <trusty_keymaster/TrustyKeymaster.h>\n\nnamespace aidl::android::hardware::security::keymint::trusty {\n\nusing ::keymaster::TrustyKeymaster;\nusing ::ndk::ScopedAStatus;\nusing secureclock::TimeStampToken;\nusing ::std::array;\nusing ::std::optional;\nusing ::std::shared_ptr;\nusing ::std::vector;\n\nclass TrustyKeyMintDevice : public BnKeyMintDevice {\n  public:\n    explicit TrustyKeyMintDevice(shared_ptr<TrustyKeymaster> impl) : impl_(std::move(impl)) {}\n    virtual ~TrustyKeyMintDevice() = default;\n\n    ScopedAStatus getHardwareInfo(KeyMintHardwareInfo* info) override;\n\n    ScopedAStatus addRngEntropy(const vector<uint8_t>& data) override;\n\n    ScopedAStatus generateKey(const vector<KeyParameter>& keyParams,\n                              const optional<AttestationKey>& attestationKey,\n                              KeyCreationResult* creationResult) override;\n\n    ScopedAStatus getKeyCharacteristics(const vector<uint8_t>& keyBlob,\n                                        const vector<uint8_t>& clientId,\n                                        const vector<uint8_t>& appData,\n                                        vector<KeyCharacteristics>* characteristics) override;\n\n    ScopedAStatus importKey(const vector<KeyParameter>& keyParams, KeyFormat keyFormat,\n                            const vector<uint8_t>& keyData,\n                            const optional<AttestationKey>& attestationKey,\n                            KeyCreationResult* creationResult) override;\n\n    ScopedAStatus importWrappedKey(const vector<uint8_t>& wrappedKeyData,\n                                   const vector<uint8_t>& wrappingKeyBlob,\n                                   const vector<uint8_t>& maskingKey,\n                                   const vector<KeyParameter>& unwrappingParams,\n                                   int64_t passwordSid, int64_t biometricSid,\n                                   KeyCreationResult* creationResult) override;\n\n    ScopedAStatus upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,\n                             const vector<KeyParameter>& upgradeParams,\n                             vector<uint8_t>* keyBlob) override;\n\n    ScopedAStatus deleteKey(const vector<uint8_t>& keyBlob) override;\n    ScopedAStatus deleteAllKeys() override;\n    ScopedAStatus destroyAttestationIds() override;\n\n    ScopedAStatus begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,\n                        const vector<KeyParameter>& params,\n                        const optional<HardwareAuthToken>& authToken, BeginResult* result) override;\n\n    ScopedAStatus deviceLocked(bool passwordOnly,\n                               const optional<TimeStampToken>& timestampToken) override;\n    ScopedAStatus earlyBootEnded() override;\n\n    ScopedAStatus convertStorageKeyToEphemeral(const vector<uint8_t>& storageKeyBlob,\n                                               vector<uint8_t>* ephemeralKeyBlob) override;\n\n    ScopedAStatus getRootOfTrustChallenge(array<uint8_t, 16>* challenge) override;\n    ScopedAStatus getRootOfTrust(const array<uint8_t, 16>& challenge,\n                                 vector<uint8_t>* rootOfTrust) override;\n    ScopedAStatus sendRootOfTrust(const vector<uint8_t>& rootOfTrust) override;\n    ScopedAStatus setAdditionalAttestationInfo(const vector<KeyParameter>& info) override;\n\n  protected:\n    std::shared_ptr<TrustyKeymaster> impl_;\n    SecurityLevel securityLevel_;\n};\n\n}  // namespace aidl::android::hardware::security::keymint::trusty\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustyKeyMintOperation.h",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <aidl/android/hardware/security/keymint/BnKeyMintOperation.h>\n#include <aidl/android/hardware/security/secureclock/ISecureClock.h>\n\n#include <trusty_keymaster/TrustyKeymaster.h>\n\n#include <hardware/keymaster_defs.h>\n\nnamespace aidl::android::hardware::security::keymint {\n\nusing ::keymaster::TrustyKeymaster;\nusing ::ndk::ScopedAStatus;\nusing secureclock::TimeStampToken;\nusing std::optional;\nusing std::shared_ptr;\nusing std::string;\nusing std::vector;\n\nclass TrustyKeyMintOperation : public BnKeyMintOperation {\n  public:\n    explicit TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,\n                                    keymaster_operation_handle_t opHandle);\n    virtual ~TrustyKeyMintOperation();\n\n    ScopedAStatus updateAad(const vector<uint8_t>& input,\n                            const optional<HardwareAuthToken>& authToken,\n                            const optional<TimeStampToken>& timestampToken) override;\n\n    ScopedAStatus update(const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,\n                         const optional<TimeStampToken>& timestampToken,\n                         vector<uint8_t>* output) override;\n\n    ScopedAStatus finish(const optional<vector<uint8_t>>& input,        //\n                         const optional<vector<uint8_t>>& signature,    //\n                         const optional<HardwareAuthToken>& authToken,  //\n                         const optional<TimeStampToken>& timestampToken,\n                         const optional<vector<uint8_t>>& confirmationToken,\n                         vector<uint8_t>* output) override;\n\n    ScopedAStatus abort() override;\n\n  protected:\n    std::shared_ptr<TrustyKeymaster> impl_;\n    keymaster_operation_handle_t opHandle_;\n};\n\n}  // namespace aidl::android::hardware::security::keymint\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustyKeymaster.h",
    "content": "/*\n * Copyright 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef TRUSTY_KEYMASTER_H_\n#define TRUSTY_KEYMASTER_H_\n\n#include <keymaster/android_keymaster_messages.h>\n\nnamespace keymaster {\n\nclass TrustyKeymaster {\n  public:\n    TrustyKeymaster();\n    ~TrustyKeymaster();\n    int Initialize(KmVersion version);\n    void GetVersion(const GetVersionRequest& request, GetVersionResponse* response);\n    void SupportedAlgorithms(const SupportedAlgorithmsRequest& request,\n                             SupportedAlgorithmsResponse* response);\n    void SupportedBlockModes(const SupportedBlockModesRequest& request,\n                             SupportedBlockModesResponse* response);\n    void SupportedPaddingModes(const SupportedPaddingModesRequest& request,\n                               SupportedPaddingModesResponse* response);\n    void SupportedDigests(const SupportedDigestsRequest& request,\n                          SupportedDigestsResponse* response);\n    void SupportedImportFormats(const SupportedImportFormatsRequest& request,\n                                SupportedImportFormatsResponse* response);\n    void SupportedExportFormats(const SupportedExportFormatsRequest& request,\n                                SupportedExportFormatsResponse* response);\n    void AddRngEntropy(const AddEntropyRequest& request, AddEntropyResponse* response);\n    void Configure(const ConfigureRequest& request, ConfigureResponse* response);\n    void GenerateKey(const GenerateKeyRequest& request, GenerateKeyResponse* response);\n    void GenerateRkpKey(const GenerateRkpKeyRequest& request, GenerateRkpKeyResponse* response);\n    void GenerateCsr(const GenerateCsrRequest& request, GenerateCsrResponse* response);\n    void GenerateCsrV2(const GenerateCsrV2Request& request, GenerateCsrV2Response* response);\n    void GetKeyCharacteristics(const GetKeyCharacteristicsRequest& request,\n                               GetKeyCharacteristicsResponse* response);\n    void ImportKey(const ImportKeyRequest& request, ImportKeyResponse* response);\n    void ImportWrappedKey(const ImportWrappedKeyRequest& request,\n                          ImportWrappedKeyResponse* response);\n    void ExportKey(const ExportKeyRequest& request, ExportKeyResponse* response);\n    void AttestKey(const AttestKeyRequest& request, AttestKeyResponse* response);\n    void UpgradeKey(const UpgradeKeyRequest& request, UpgradeKeyResponse* response);\n    void DeleteKey(const DeleteKeyRequest& request, DeleteKeyResponse* response);\n    void DeleteAllKeys(const DeleteAllKeysRequest& request, DeleteAllKeysResponse* response);\n    void DestroyAttestationIds(const DestroyAttestationIdsRequest& request,\n                               DestroyAttestationIdsResponse* response);\n    void BeginOperation(const BeginOperationRequest& request, BeginOperationResponse* response);\n    void UpdateOperation(const UpdateOperationRequest& request, UpdateOperationResponse* response);\n    void FinishOperation(const FinishOperationRequest& request, FinishOperationResponse* response);\n    void AbortOperation(const AbortOperationRequest& request, AbortOperationResponse* response);\n    GetHmacSharingParametersResponse GetHmacSharingParameters();\n    ComputeSharedHmacResponse ComputeSharedHmac(const ComputeSharedHmacRequest& request);\n    VerifyAuthorizationResponse VerifyAuthorization(const VerifyAuthorizationRequest& request);\n    GetVersion2Response GetVersion2(const GetVersion2Request& request);\n    EarlyBootEndedResponse EarlyBootEnded();\n    DeviceLockedResponse DeviceLocked(const DeviceLockedRequest& request);\n    ConfigureVendorPatchlevelResponse ConfigureVendorPatchlevel(\n            const ConfigureVendorPatchlevelRequest& request);\n    GetRootOfTrustResponse GetRootOfTrust(const GetRootOfTrustRequest& request);\n    SetAdditionalAttestationInfoResponse SetAdditionalAttestationInfo(\n            const SetAdditionalAttestationInfoRequest& request);\n    GetHwInfoResponse GetHwInfo();\n\n    uint32_t message_version() const { return message_version_; }\n\n  private:\n    uint32_t message_version_;\n};\n\n}  // namespace keymaster\n\n#endif  // TRUSTY_KEYMASTER_H_\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustyKeymaster3Device.h",
    "content": "/*\n **\n ** Copyright 2018, The Android Open Source Project\n **\n ** Licensed under the Apache License, Version 2.0 (the \"License\");\n ** you may not use this file except in compliance with the License.\n ** You may obtain a copy of the License at\n **\n **     http://www.apache.org/licenses/LICENSE-2.0\n **\n ** Unless required by applicable law or agreed to in writing, software\n ** distributed under the License is distributed on an \"AS IS\" BASIS,\n ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ** See the License for the specific language governing permissions and\n ** limitations under the License.\n */\n\n#ifndef HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_\n#define HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_\n\n#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>\n\n#include <hidl/MQDescriptor.h>\n#include <hidl/Status.h>\n\n#include <trusty_keymaster/TrustyKeymaster.h>\n\nnamespace keymaster {\n\nusing ::android::sp;\nusing ::android::hardware::hidl_string;\nusing ::android::hardware::hidl_vec;\nusing ::android::hardware::Return;\nusing ::android::hardware::Void;\nusing ::android::hardware::keymaster::V3_0::ErrorCode;\nusing ::android::hardware::keymaster::V3_0::IKeymasterDevice;\nusing ::android::hardware::keymaster::V3_0::KeyCharacteristics;\nusing ::android::hardware::keymaster::V3_0::KeyFormat;\nusing ::android::hardware::keymaster::V3_0::KeyParameter;\nusing ::android::hardware::keymaster::V3_0::KeyPurpose;\n\nclass TrustyKeymaster3Device : public IKeymasterDevice {\n  public:\n    TrustyKeymaster3Device(TrustyKeymaster* impl);\n    virtual ~TrustyKeymaster3Device();\n\n    Return<void> getHardwareFeatures(getHardwareFeatures_cb _hidl_cb);\n    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;\n    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,\n                             generateKey_cb _hidl_cb) override;\n    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,\n                                       const hidl_vec<uint8_t>& clientId,\n                                       const hidl_vec<uint8_t>& appData,\n                                       getKeyCharacteristics_cb _hidl_cb) override;\n    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,\n                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;\n    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,\n                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,\n                           exportKey_cb _hidl_cb) override;\n    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,\n                           const hidl_vec<KeyParameter>& attestParams,\n                           attestKey_cb _hidl_cb) override;\n    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,\n                            const hidl_vec<KeyParameter>& upgradeParams,\n                            upgradeKey_cb _hidl_cb) override;\n    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;\n    Return<ErrorCode> deleteAllKeys() override;\n    Return<ErrorCode> destroyAttestationIds() override;\n    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,\n                       const hidl_vec<KeyParameter>& inParams, begin_cb _hidl_cb) override;\n    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,\n                        const hidl_vec<uint8_t>& input, update_cb _hidl_cb) override;\n    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,\n                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,\n                        finish_cb _hidl_cb) override;\n    Return<ErrorCode> abort(uint64_t operationHandle) override;\n\n  private:\n    std::unique_ptr<TrustyKeymaster> impl_;\n};\n\n}  // namespace keymaster\n\n#endif  // HIDL_android_hardware_keymaster_V3_0_TrustyKeymaster3Device_H_\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustyKeymaster4Device.h",
    "content": "/*\n **\n ** Copyright 2017, The Android Open Source Project\n **\n ** Licensed under the Apache License, Version 2.0 (the \"License\");\n ** you may not use this file except in compliance with the License.\n ** You may obtain a copy of the License at\n **\n **     http://www.apache.org/licenses/LICENSE-2.0\n **\n ** Unless required by applicable law or agreed to in writing, software\n ** distributed under the License is distributed on an \"AS IS\" BASIS,\n ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n ** See the License for the specific language governing permissions and\n ** limitations under the License.\n */\n\n#ifndef keymaster_V4_0_TrustyKeymaster4Device_H_\n#define keymaster_V4_0_TrustyKeymaster4Device_H_\n\n#include <android/hardware/keymaster/4.0/IKeymasterDevice.h>\n#include <hidl/Status.h>\n#include <trusty_keymaster/TrustyKeymaster.h>\n\nnamespace keymaster {\n\nnamespace V4_0 {\n\nusing ::android::sp;\nusing ::android::hardware::hidl_vec;\nusing ::android::hardware::Return;\nusing ::android::hardware::Void;\nusing ::android::hardware::keymaster::V4_0::ErrorCode;\nusing ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;\nusing ::android::hardware::keymaster::V4_0::HardwareAuthToken;\nusing ::android::hardware::keymaster::V4_0::HmacSharingParameters;\nusing ::android::hardware::keymaster::V4_0::IKeymasterDevice;\nusing ::android::hardware::keymaster::V4_0::KeyCharacteristics;\nusing ::android::hardware::keymaster::V4_0::KeyFormat;\nusing ::android::hardware::keymaster::V4_0::KeyParameter;\nusing ::android::hardware::keymaster::V4_0::KeyPurpose;\nusing ::android::hardware::keymaster::V4_0::SecurityLevel;\nusing ::android::hardware::keymaster::V4_0::Tag;\nusing ::android::hardware::keymaster::V4_0::VerificationToken;\n\nclass TrustyKeymaster4Device : public IKeymasterDevice {\n  public:\n    explicit TrustyKeymaster4Device(TrustyKeymaster* impl);\n    virtual ~TrustyKeymaster4Device();\n\n    Return<void> getHardwareInfo(getHardwareInfo_cb _hidl_cb) override;\n    Return<void> getHmacSharingParameters(getHmacSharingParameters_cb _hidl_cb) override;\n    Return<void> computeSharedHmac(const hidl_vec<HmacSharingParameters>& params,\n                                   computeSharedHmac_cb) override;\n    Return<void> verifyAuthorization(uint64_t challenge,\n                                     const hidl_vec<KeyParameter>& parametersToVerify,\n                                     const HardwareAuthToken& authToken,\n                                     verifyAuthorization_cb _hidl_cb) override;\n    Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;\n    Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,\n                             generateKey_cb _hidl_cb) override;\n    Return<void> getKeyCharacteristics(const hidl_vec<uint8_t>& keyBlob,\n                                       const hidl_vec<uint8_t>& clientId,\n                                       const hidl_vec<uint8_t>& appData,\n                                       getKeyCharacteristics_cb _hidl_cb) override;\n    Return<void> importKey(const hidl_vec<KeyParameter>& params, KeyFormat keyFormat,\n                           const hidl_vec<uint8_t>& keyData, importKey_cb _hidl_cb) override;\n    Return<void> importWrappedKey(const hidl_vec<uint8_t>& wrappedKeyData,\n                                  const hidl_vec<uint8_t>& wrappingKeyBlob,\n                                  const hidl_vec<uint8_t>& maskingKey,\n                                  const hidl_vec<KeyParameter>& unwrappingParams,\n                                  uint64_t passwordSid, uint64_t biometricSid,\n                                  importWrappedKey_cb _hidl_cb) override;\n    Return<void> exportKey(KeyFormat exportFormat, const hidl_vec<uint8_t>& keyBlob,\n                           const hidl_vec<uint8_t>& clientId, const hidl_vec<uint8_t>& appData,\n                           exportKey_cb _hidl_cb) override;\n    Return<void> attestKey(const hidl_vec<uint8_t>& keyToAttest,\n                           const hidl_vec<KeyParameter>& attestParams,\n                           attestKey_cb _hidl_cb) override;\n    Return<void> upgradeKey(const hidl_vec<uint8_t>& keyBlobToUpgrade,\n                            const hidl_vec<KeyParameter>& upgradeParams,\n                            upgradeKey_cb _hidl_cb) override;\n    Return<ErrorCode> deleteKey(const hidl_vec<uint8_t>& keyBlob) override;\n    Return<ErrorCode> deleteAllKeys() override;\n    Return<ErrorCode> destroyAttestationIds() override;\n    Return<void> begin(KeyPurpose purpose, const hidl_vec<uint8_t>& key,\n                       const hidl_vec<KeyParameter>& inParams, const HardwareAuthToken& authToken,\n                       begin_cb _hidl_cb) override;\n    Return<void> update(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,\n                        const hidl_vec<uint8_t>& input, const HardwareAuthToken& authToken,\n                        const VerificationToken& verificationToken, update_cb _hidl_cb) override;\n    Return<void> finish(uint64_t operationHandle, const hidl_vec<KeyParameter>& inParams,\n                        const hidl_vec<uint8_t>& input, const hidl_vec<uint8_t>& signature,\n                        const HardwareAuthToken& authToken,\n                        const VerificationToken& verificationToken, finish_cb _hidl_cb) override;\n    Return<ErrorCode> abort(uint64_t operationHandle) override;\n\n  private:\n    std::unique_ptr<::keymaster::TrustyKeymaster> impl_;\n};\n\n}  // namespace V4_0\n}  // namespace keymaster\n\n#endif  // keymaster_V4_0_TrustyKeymaster4Device_H_\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <aidl/android/hardware/security/keymint/BnRemotelyProvisionedComponent.h>\n#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>\n#include <aidl/android/hardware/security/keymint/SecurityLevel.h>\n\n#include <trusty_keymaster/TrustyKeymaster.h>\n\nnamespace aidl::android::hardware::security::keymint::trusty {\n\nusing ::keymaster::TrustyKeymaster;\nusing ::ndk::ScopedAStatus;\nusing ::std::shared_ptr;\n\nclass TrustyRemotelyProvisionedComponentDevice : public BnRemotelyProvisionedComponent {\n  public:\n    explicit TrustyRemotelyProvisionedComponentDevice(shared_ptr<TrustyKeymaster> impl)\n        : impl_(std::move(impl)) {}\n    virtual ~TrustyRemotelyProvisionedComponentDevice() = default;\n\n    ScopedAStatus getHardwareInfo(RpcHardwareInfo* info) override;\n\n    ScopedAStatus generateEcdsaP256KeyPair(bool testMode, MacedPublicKey* macedPublicKey,\n                                           std::vector<uint8_t>* privateKeyHandle) override;\n\n    ScopedAStatus generateCertificateRequest(bool testMode,\n                                             const std::vector<MacedPublicKey>& keysToSign,\n                                             const std::vector<uint8_t>& endpointEncCertChain,\n                                             const std::vector<uint8_t>& challenge,\n                                             DeviceInfo* deviceInfo, ProtectedData* protectedData,\n                                             std::vector<uint8_t>* keysToSignMac) override;\n\n    ScopedAStatus generateCertificateRequestV2(const std::vector<MacedPublicKey>& keysToSign,\n                                               const std::vector<uint8_t>& challenge,\n                                               std::vector<uint8_t>* csr) override;\n\n  private:\n    std::shared_ptr<::keymaster::TrustyKeymaster> impl_;\n};\n\n}  // namespace aidl::android::hardware::security::keymint::trusty\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustySecureClock.h",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <aidl/android/hardware/security/secureclock/BnSecureClock.h>\n#include <aidl/android/hardware/security/secureclock/TimeStampToken.h>\n#include <aidl/android/hardware/security/secureclock/Timestamp.h>\n\n#include <trusty_keymaster/TrustyKeymaster.h>\n\nnamespace aidl::android::hardware::security::secureclock::trusty {\n\nclass TrustySecureClock : public BnSecureClock {\n  public:\n    explicit TrustySecureClock(std::shared_ptr<::keymaster::TrustyKeymaster> impl)\n        : impl_(std::move(impl)) {}\n    ~TrustySecureClock() = default;\n    ::ndk::ScopedAStatus generateTimeStamp(int64_t challenge, TimeStampToken* token) override;\n\n  private:\n    std::shared_ptr<::keymaster::TrustyKeymaster> impl_;\n};\n\n}  // namespace aidl::android::hardware::security::secureclock::trusty\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/TrustySharedSecret.h",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>\n#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>\n\n#include <trusty_keymaster/TrustyKeymaster.h>\n\nnamespace aidl::android::hardware::security::sharedsecret::trusty {\n\nclass TrustySharedSecret : public BnSharedSecret {\n  public:\n    explicit TrustySharedSecret(std::shared_ptr<::keymaster::TrustyKeymaster> impl)\n        : impl_(std::move(impl)) {}\n    ~TrustySharedSecret() = default;\n\n    ::ndk::ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override;\n    ::ndk::ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params,\n                                             std::vector<uint8_t>* sharingCheck) override;\n\n  private:\n    std::shared_ptr<::keymaster::TrustyKeymaster> impl_;\n};\n}  // namespace aidl::android::hardware::security::sharedsecret::trusty\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/ipc/keymaster_ipc.h",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n// clang-format off\n\n#define KEYMASTER_PORT \"com.android.trusty.keymaster\"\n#define KEYMASTER_MAX_BUFFER_LENGTH 4096\n\n// Commands\nenum keymaster_command : uint32_t {\n    KEYMASTER_RESP_BIT              = 1,\n    KEYMASTER_STOP_BIT              = 2,\n    KEYMASTER_REQ_SHIFT             = 2,\n\n    KM_GENERATE_KEY                 = (0 << KEYMASTER_REQ_SHIFT),\n    KM_BEGIN_OPERATION              = (1 << KEYMASTER_REQ_SHIFT),\n    KM_UPDATE_OPERATION             = (2 << KEYMASTER_REQ_SHIFT),\n    KM_FINISH_OPERATION             = (3 << KEYMASTER_REQ_SHIFT),\n    KM_ABORT_OPERATION              = (4 << KEYMASTER_REQ_SHIFT),\n    KM_IMPORT_KEY                   = (5 << KEYMASTER_REQ_SHIFT),\n    KM_EXPORT_KEY                   = (6 << KEYMASTER_REQ_SHIFT),\n    KM_GET_VERSION                  = (7 << KEYMASTER_REQ_SHIFT),\n    KM_ADD_RNG_ENTROPY              = (8 << KEYMASTER_REQ_SHIFT),\n    KM_GET_SUPPORTED_ALGORITHMS     = (9 << KEYMASTER_REQ_SHIFT),\n    KM_GET_SUPPORTED_BLOCK_MODES    = (10 << KEYMASTER_REQ_SHIFT),\n    KM_GET_SUPPORTED_PADDING_MODES  = (11 << KEYMASTER_REQ_SHIFT),\n    KM_GET_SUPPORTED_DIGESTS        = (12 << KEYMASTER_REQ_SHIFT),\n    KM_GET_SUPPORTED_IMPORT_FORMATS = (13 << KEYMASTER_REQ_SHIFT),\n    KM_GET_SUPPORTED_EXPORT_FORMATS = (14 << KEYMASTER_REQ_SHIFT),\n    KM_GET_KEY_CHARACTERISTICS      = (15 << KEYMASTER_REQ_SHIFT),\n    KM_ATTEST_KEY                   = (16 << KEYMASTER_REQ_SHIFT),\n    KM_UPGRADE_KEY                  = (17 << KEYMASTER_REQ_SHIFT),\n    KM_CONFIGURE                    = (18 << KEYMASTER_REQ_SHIFT),\n    KM_GET_HMAC_SHARING_PARAMETERS  = (19 << KEYMASTER_REQ_SHIFT),\n    KM_COMPUTE_SHARED_HMAC          = (20 << KEYMASTER_REQ_SHIFT),\n    KM_VERIFY_AUTHORIZATION         = (21 << KEYMASTER_REQ_SHIFT),\n    KM_DELETE_KEY                   = (22 << KEYMASTER_REQ_SHIFT),\n    KM_DELETE_ALL_KEYS              = (23 << KEYMASTER_REQ_SHIFT),\n    KM_DESTROY_ATTESTATION_IDS      = (24 << KEYMASTER_REQ_SHIFT),\n    KM_IMPORT_WRAPPED_KEY           = (25 << KEYMASTER_REQ_SHIFT),\n    KM_GET_VERSION_2                = (28 << KEYMASTER_REQ_SHIFT),\n    KM_EARLY_BOOT_ENDED             = (29 << KEYMASTER_REQ_SHIFT),\n    KM_DEVICE_LOCKED                = (30 << KEYMASTER_REQ_SHIFT),\n    KM_GENERATE_RKP_KEY             = (31 << KEYMASTER_REQ_SHIFT),\n    KM_GENERATE_CSR                 = (32 << KEYMASTER_REQ_SHIFT),\n    KM_CONFIGURE_VENDOR_PATCHLEVEL  = (33 << KEYMASTER_REQ_SHIFT),\n    KM_GET_ROOT_OF_TRUST            = (34 << KEYMASTER_REQ_SHIFT),\n    KM_GET_HW_INFO                  = (35 << KEYMASTER_REQ_SHIFT),\n    KM_GENERATE_CSR_V2              = (36 << KEYMASTER_REQ_SHIFT),\n    KM_SET_ADDITIONAL_ATTESTATION_INFO = (37 << KEYMASTER_REQ_SHIFT),\n\n    // Bootloader/provisioning calls.\n    KM_SET_BOOT_PARAMS = (0x1000 << KEYMASTER_REQ_SHIFT),\n    KM_SET_ATTESTATION_KEY = (0x2000 << KEYMASTER_REQ_SHIFT),\n    KM_APPEND_ATTESTATION_CERT_CHAIN = (0x3000 << KEYMASTER_REQ_SHIFT),\n    KM_ATAP_GET_CA_REQUEST = (0x4000 << KEYMASTER_REQ_SHIFT),\n    KM_ATAP_SET_CA_RESPONSE_BEGIN = (0x5000 << KEYMASTER_REQ_SHIFT),\n    KM_ATAP_SET_CA_RESPONSE_UPDATE = (0x6000 << KEYMASTER_REQ_SHIFT),\n    KM_ATAP_SET_CA_RESPONSE_FINISH = (0x7000 << KEYMASTER_REQ_SHIFT),\n    KM_ATAP_READ_UUID = (0x8000 << KEYMASTER_REQ_SHIFT),\n    KM_SET_PRODUCT_ID = (0x9000 << KEYMASTER_REQ_SHIFT),\n    KM_CLEAR_ATTESTATION_CERT_CHAIN = (0xa000 << KEYMASTER_REQ_SHIFT),\n    KM_SET_WRAPPED_ATTESTATION_KEY = (0xb000 << KEYMASTER_REQ_SHIFT),\n    KM_SET_ATTESTATION_IDS = (0xc000 << KEYMASTER_REQ_SHIFT),\n    KM_SET_ATTESTATION_IDS_KM3 = (0xc001 << KEYMASTER_REQ_SHIFT),\n    KM_CONFIGURE_BOOT_PATCHLEVEL = (0xd000 << KEYMASTER_REQ_SHIFT),\n    KM_APPEND_UDS_CERT_CHAIN = (0xe0000 << KEYMASTER_REQ_SHIFT),\n    KM_CLEAR_UDS_CERT_CHAIN = (0xe0001 << KEYMASTER_REQ_SHIFT),\n};\n\n#ifdef __ANDROID__\n\n/**\n * keymaster_message - Serial header for communicating with KM server\n * @cmd: the command, one of keymaster_command.\n * @payload: start of the serialized command specific payload\n */\nstruct keymaster_message {\n    uint32_t cmd;\n    uint8_t payload[0];\n};\n\n#endif\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/ipc/trusty_keymaster_ipc.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_\n#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_\n\n#include <keymaster/android_keymaster_messages.h>\n#include <trusty_keymaster/ipc/keymaster_ipc.h>\n\n__BEGIN_DECLS\n\nconst uint32_t TRUSTY_KEYMASTER_RECV_BUF_SIZE = 2 * 4096;\nconst uint32_t TRUSTY_KEYMASTER_SEND_BUF_SIZE =\n        (4096 - sizeof(struct keymaster_message) - 16 /* tipc header */);\n\nvoid trusty_keymaster_set_dev_name(const char* device_name);\nint trusty_keymaster_connect(void);\nint trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,\n                          uint32_t* out_size);\nvoid trusty_keymaster_disconnect(void);\n\nkeymaster_error_t translate_error(int err);\nkeymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req,\n                                        keymaster::KeymasterResponse* rsp);\n\n__END_DECLS\n\n#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_IPC_H_\n"
  },
  {
    "path": "trusty/keymaster/include/trusty_keymaster/legacy/trusty_keymaster_device.h",
    "content": "/*\n * Copyright 2014 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_\n#define TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_\n\n#include <hardware/keymaster2.h>\n#include <keymaster/android_keymaster_messages.h>\n\nnamespace keymaster {\n\n/**\n * Trusty Keymaster device.\n *\n * IMPORTANT MAINTAINER NOTE: Pointers to instances of this class must be castable to hw_device_t\n * and keymaster_device. This means it must remain a standard layout class (no virtual functions and\n * no data members which aren't standard layout), and device_ must be the first data member.\n * Assertions in the constructor validate compliance with those constraints.\n */\nclass TrustyKeymasterDevice {\n  public:\n    /*\n     * These are the only symbols that will be exported by libtrustykeymaster.  All functionality\n     * can be reached via the function pointers in device_.\n     */\n    __attribute__((visibility(\"default\"))) explicit TrustyKeymasterDevice(\n            const hw_module_t* module);\n    __attribute__((visibility(\"default\"))) hw_device_t* hw_device();\n\n    ~TrustyKeymasterDevice();\n\n    keymaster_error_t session_error() { return error_; }\n\n    keymaster_error_t configure(const keymaster_key_param_set_t* params);\n    keymaster_error_t add_rng_entropy(const uint8_t* data, size_t data_length);\n    keymaster_error_t generate_key(const keymaster_key_param_set_t* params,\n                                   keymaster_key_blob_t* key_blob,\n                                   keymaster_key_characteristics_t* characteristics);\n    keymaster_error_t get_key_characteristics(const keymaster_key_blob_t* key_blob,\n                                              const keymaster_blob_t* client_id,\n                                              const keymaster_blob_t* app_data,\n                                              keymaster_key_characteristics_t* character);\n    keymaster_error_t import_key(const keymaster_key_param_set_t* params,\n                                 keymaster_key_format_t key_format,\n                                 const keymaster_blob_t* key_data, keymaster_key_blob_t* key_blob,\n                                 keymaster_key_characteristics_t* characteristics);\n    keymaster_error_t export_key(keymaster_key_format_t export_format,\n                                 const keymaster_key_blob_t* key_to_export,\n                                 const keymaster_blob_t* client_id,\n                                 const keymaster_blob_t* app_data, keymaster_blob_t* export_data);\n    keymaster_error_t attest_key(const keymaster_key_blob_t* key_to_attest,\n                                 const keymaster_key_param_set_t* attest_params,\n                                 keymaster_cert_chain_t* cert_chain);\n    keymaster_error_t upgrade_key(const keymaster_key_blob_t* key_to_upgrade,\n                                  const keymaster_key_param_set_t* upgrade_params,\n                                  keymaster_key_blob_t* upgraded_key);\n    keymaster_error_t begin(keymaster_purpose_t purpose, const keymaster_key_blob_t* key,\n                            const keymaster_key_param_set_t* in_params,\n                            keymaster_key_param_set_t* out_params,\n                            keymaster_operation_handle_t* operation_handle);\n    keymaster_error_t update(keymaster_operation_handle_t operation_handle,\n                             const keymaster_key_param_set_t* in_params,\n                             const keymaster_blob_t* input, size_t* input_consumed,\n                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);\n    keymaster_error_t finish(keymaster_operation_handle_t operation_handle,\n                             const keymaster_key_param_set_t* in_params,\n                             const keymaster_blob_t* input, const keymaster_blob_t* signature,\n                             keymaster_key_param_set_t* out_params, keymaster_blob_t* output);\n    keymaster_error_t abort(keymaster_operation_handle_t operation_handle);\n    keymaster_error_t delete_key(const keymaster_key_blob_t* key);\n    keymaster_error_t delete_all_keys();\n\n  private:\n    keymaster_error_t Send(uint32_t command, const Serializable& request,\n                           KeymasterResponse* response);\n\n    /*\n     * These static methods are the functions referenced through the function pointers in\n     * keymaster_device.  They're all trivial wrappers.\n     */\n    static int close_device(hw_device_t* dev);\n    static keymaster_error_t configure(const keymaster2_device_t* dev,\n                                       const keymaster_key_param_set_t* params);\n    static keymaster_error_t add_rng_entropy(const keymaster2_device_t* dev, const uint8_t* data,\n                                             size_t data_length);\n    static keymaster_error_t generate_key(const keymaster2_device_t* dev,\n                                          const keymaster_key_param_set_t* params,\n                                          keymaster_key_blob_t* key_blob,\n                                          keymaster_key_characteristics_t* characteristics);\n    static keymaster_error_t get_key_characteristics(const keymaster2_device_t* dev,\n                                                     const keymaster_key_blob_t* key_blob,\n                                                     const keymaster_blob_t* client_id,\n                                                     const keymaster_blob_t* app_data,\n                                                     keymaster_key_characteristics_t* character);\n    static keymaster_error_t import_key(const keymaster2_device_t* dev,\n                                        const keymaster_key_param_set_t* params,\n                                        keymaster_key_format_t key_format,\n                                        const keymaster_blob_t* key_data,\n                                        keymaster_key_blob_t* key_blob,\n                                        keymaster_key_characteristics_t* characteristics);\n    static keymaster_error_t export_key(const keymaster2_device_t* dev,\n                                        keymaster_key_format_t export_format,\n                                        const keymaster_key_blob_t* key_to_export,\n                                        const keymaster_blob_t* client_id,\n                                        const keymaster_blob_t* app_data,\n                                        keymaster_blob_t* export_data);\n    static keymaster_error_t attest_key(const keymaster2_device_t* dev,\n                                        const keymaster_key_blob_t* key_to_attest,\n                                        const keymaster_key_param_set_t* attest_params,\n                                        keymaster_cert_chain_t* cert_chain);\n    static keymaster_error_t upgrade_key(const keymaster2_device_t* dev,\n                                         const keymaster_key_blob_t* key_to_upgrade,\n                                         const keymaster_key_param_set_t* upgrade_params,\n                                         keymaster_key_blob_t* upgraded_key);\n    static keymaster_error_t delete_key(const keymaster2_device_t* dev,\n                                        const keymaster_key_blob_t* key);\n    static keymaster_error_t delete_all_keys(const keymaster2_device_t* dev);\n    static keymaster_error_t begin(const keymaster2_device_t* dev, keymaster_purpose_t purpose,\n                                   const keymaster_key_blob_t* key,\n                                   const keymaster_key_param_set_t* in_params,\n                                   keymaster_key_param_set_t* out_params,\n                                   keymaster_operation_handle_t* operation_handle);\n    static keymaster_error_t update(const keymaster2_device_t* dev,\n                                    keymaster_operation_handle_t operation_handle,\n                                    const keymaster_key_param_set_t* in_params,\n                                    const keymaster_blob_t* input, size_t* input_consumed,\n                                    keymaster_key_param_set_t* out_params,\n                                    keymaster_blob_t* output);\n    static keymaster_error_t finish(const keymaster2_device_t* dev,\n                                    keymaster_operation_handle_t operation_handle,\n                                    const keymaster_key_param_set_t* in_params,\n                                    const keymaster_blob_t* input,\n                                    const keymaster_blob_t* signature,\n                                    keymaster_key_param_set_t* out_params,\n                                    keymaster_blob_t* output);\n    static keymaster_error_t abort(const keymaster2_device_t* dev,\n                                   keymaster_operation_handle_t operation_handle);\n\n    keymaster2_device_t device_;\n    keymaster_error_t error_;\n    int32_t message_version_;\n};\n\n}  // namespace keymaster\n\n#endif  // TRUSTY_KEYMASTER_TRUSTY_KEYMASTER_DEVICE_H_\n"
  },
  {
    "path": "trusty/keymaster/ipc/trusty_keymaster_ipc.cpp",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"TrustyKeymaster\"\n\n// TODO: make this generic in libtrusty\n\n#include <errno.h>\n#include <poll.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/uio.h>\n#include <unistd.h>\n\n#include <algorithm>\n#include <variant>\n#include <vector>\n\n#include <log/log.h>\n#include <trusty/tipc.h>\n\n#include <trusty_keymaster/ipc/keymaster_ipc.h>\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n#include <utils/Timers.h>\n\nstatic const char* trusty_device_name = \"/dev/trusty-ipc-dev0\";\n\nstatic int handle_ = -1;\n\nstatic const int timeout_ms = 10 * 1000;\nstatic const int max_timeout_ms = 60 * 1000;\n\nvoid trusty_keymaster_set_dev_name(const char* device_name) {\n    trusty_device_name = device_name;\n}\nint trusty_keymaster_connect() {\n    int rc = tipc_connect(trusty_device_name, KEYMASTER_PORT);\n    if (rc < 0) {\n        return rc;\n    }\n\n    handle_ = rc;\n    return 0;\n}\n\nclass VectorEraser {\n  public:\n    VectorEraser(std::vector<uint8_t>* v) : _v(v) {}\n    ~VectorEraser() {\n        if (_v) {\n            std::fill(const_cast<volatile uint8_t*>(_v->data()),\n                      const_cast<volatile uint8_t*>(_v->data() + _v->size()), 0);\n        }\n    }\n    void disarm() { _v = nullptr; }\n    VectorEraser(const VectorEraser&) = delete;\n    VectorEraser& operator=(const VectorEraser&) = delete;\n    VectorEraser(VectorEraser&& other) = delete;\n    VectorEraser& operator=(VectorEraser&&) = delete;\n\n  private:\n    std::vector<uint8_t>* _v;\n};\n\nstd::variant<int, std::vector<uint8_t>> trusty_keymaster_call_2(uint32_t cmd, void* in,\n                                                                uint32_t in_size) {\n    if (handle_ < 0) {\n        ALOGE(\"not connected\\n\");\n        return -EINVAL;\n    }\n\n    size_t msg_size = in_size + sizeof(struct keymaster_message);\n    struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size));\n    if (!msg) {\n        ALOGE(\"failed to allocate msg buffer\\n\");\n        return -EINVAL;\n    }\n\n    msg->cmd = cmd;\n    memcpy(msg->payload, in, in_size);\n\n    nsecs_t start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);\n    bool timed_out = false;\n    int poll_timeout_ms = timeout_ms;\n    while (true) {\n        struct pollfd pfd;\n        pfd.fd = handle_;\n        pfd.events = POLLOUT;\n        pfd.revents = 0;\n\n        int p = poll(&pfd, 1, poll_timeout_ms);\n        if (p == 0) {\n            ALOGW(\"write for cmd %d is taking more than %lld nsecs\", cmd,\n                  (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));\n            timed_out = true;\n            poll_timeout_ms *= 2;\n            if (poll_timeout_ms > max_timeout_ms) {\n                poll_timeout_ms = max_timeout_ms;\n            }\n            continue;\n        } else if (p < 0) {\n            ALOGE(\"write poll error: %d\", errno);\n        } else if (pfd.revents != POLLOUT) {\n            ALOGW(\"unexpected poll() result: %d\", pfd.revents);\n        }\n        break;\n    }\n\n    ssize_t rc = write(handle_, msg, msg_size);\n    if (timed_out) {\n        ALOGW(\"write for cmd %d finished after %lld nsecs\", cmd,\n              (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));\n    }\n    free(msg);\n\n    if (rc < 0) {\n        ALOGE(\"failed to send cmd (%d) to %s: %s\\n\", cmd, KEYMASTER_PORT, strerror(errno));\n        return -errno;\n    }\n\n    std::vector<uint8_t> out(TRUSTY_KEYMASTER_RECV_BUF_SIZE);\n    VectorEraser out_eraser(&out);\n    uint8_t* write_pos = out.data();\n    uint8_t* out_end = out.data() + out.size();\n\n    struct iovec iov[2];\n    struct keymaster_message header;\n    iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)};\n    while (true) {\n        if (out_end - write_pos < KEYMASTER_MAX_BUFFER_LENGTH) {\n            // In stead of using std::vector.resize(), allocate a new one to have chance\n            // at zeroing the old buffer.\n            std::vector<uint8_t> new_out(out.size() + KEYMASTER_MAX_BUFFER_LENGTH);\n            // After the swap below this erases the old out buffer.\n            VectorEraser new_out_eraser(&new_out);\n            std::copy(out.data(), write_pos, new_out.begin());\n\n            auto write_offset = write_pos - out.data();\n\n            std::swap(new_out, out);\n\n            write_pos = out.data() + write_offset;\n            out_end = out.data() + out.size();\n        }\n        size_t buffer_size = 0;\n        if (__builtin_sub_overflow(reinterpret_cast<uintptr_t>(out_end),\n                                   reinterpret_cast<uintptr_t>(write_pos), &buffer_size)) {\n            return -EOVERFLOW;\n        }\n        iov[1] = {.iov_base = write_pos, .iov_len = buffer_size};\n        start_time_ns = systemTime(SYSTEM_TIME_MONOTONIC);\n        timed_out = false;\n        poll_timeout_ms = timeout_ms;\n        while (true) {\n            struct pollfd pfd;\n            pfd.fd = handle_;\n            pfd.events = POLLIN;\n            pfd.revents = 0;\n\n            int p = poll(&pfd, 1, poll_timeout_ms);\n            if (p == 0) {\n                ALOGW(\"readv for cmd %d is taking more than %lld nsecs\", cmd,\n                      (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));\n                timed_out = true;\n                poll_timeout_ms *= 2;\n                if (poll_timeout_ms > max_timeout_ms) {\n                    poll_timeout_ms = max_timeout_ms;\n                }\n                continue;\n            } else if (p < 0) {\n                ALOGE(\"read poll error: %d\", errno);\n            } else if (pfd.revents != POLLIN) {\n                ALOGW(\"unexpected poll() result: %d\", pfd.revents);\n            }\n            break;\n        }\n        rc = readv(handle_, iov, 2);\n        if (timed_out) {\n            ALOGW(\"readv for cmd %d finished after %lld nsecs\", cmd,\n                  (long long)(systemTime(SYSTEM_TIME_MONOTONIC) - start_time_ns));\n        }\n        if (rc < 0) {\n            ALOGE(\"failed to retrieve response for cmd (%d) to %s: %s\\n\", cmd, KEYMASTER_PORT,\n                  strerror(errno));\n            return -errno;\n        }\n\n        if ((size_t)rc < sizeof(struct keymaster_message)) {\n            ALOGE(\"invalid response size (%d)\\n\", (int)rc);\n            return -EINVAL;\n        }\n\n        if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) {\n            ALOGE(\"invalid command (%d)\", header.cmd);\n            return -EINVAL;\n        }\n        write_pos += ((size_t)rc - sizeof(struct keymaster_message));\n        if (header.cmd & KEYMASTER_STOP_BIT) {\n            break;\n        }\n    }\n\n    out.resize(write_pos - out.data());\n    out_eraser.disarm();\n    return out;\n}\n\nint trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out,\n                          uint32_t* out_size) {\n    auto result = trusty_keymaster_call_2(cmd, in, in_size);\n    if (auto out_buffer = std::get_if<std::vector<uint8_t>>(&result)) {\n        if (out_buffer->size() <= *out_size) {\n            std::copy(out_buffer->begin(), out_buffer->end(), out);\n            std::fill(const_cast<volatile uint8_t*>(&*out_buffer->begin()),\n                      const_cast<volatile uint8_t*>(&*out_buffer->end()), 0);\n\n            *out_size = out_buffer->size();\n            return 0;\n        } else {\n            ALOGE(\"Message was to large (%zu) for the provided buffer (%u)\", out_buffer->size(),\n                  *out_size);\n            return -EMSGSIZE;\n        }\n    } else {\n        return std::get<int>(result);\n    }\n}\n\nvoid trusty_keymaster_disconnect() {\n    if (handle_ >= 0) {\n        tipc_close(handle_);\n    }\n    handle_ = -1;\n}\n\nkeymaster_error_t translate_error(int err) {\n    switch (err) {\n        case 0:\n            return KM_ERROR_OK;\n        case -EPERM:\n        case -EACCES:\n            return KM_ERROR_SECURE_HW_ACCESS_DENIED;\n\n        case -ECANCELED:\n            return KM_ERROR_OPERATION_CANCELLED;\n\n        case -ENODEV:\n            return KM_ERROR_UNIMPLEMENTED;\n\n        case -ENOMEM:\n            return KM_ERROR_MEMORY_ALLOCATION_FAILED;\n\n        case -EBUSY:\n            return KM_ERROR_SECURE_HW_BUSY;\n\n        case -EIO:\n            return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED;\n\n        case -EOVERFLOW:\n            return KM_ERROR_INVALID_INPUT_LENGTH;\n\n        default:\n            return KM_ERROR_UNKNOWN_ERROR;\n    }\n}\n\nkeymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req,\n                                        keymaster::KeymasterResponse* rsp) {\n    uint32_t req_size = req.SerializedSize();\n    if (req_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {\n        ALOGE(\"Request too big: %u Max size: %u\", req_size, TRUSTY_KEYMASTER_SEND_BUF_SIZE);\n        return KM_ERROR_INVALID_INPUT_LENGTH;\n    }\n\n    uint8_t send_buf[TRUSTY_KEYMASTER_SEND_BUF_SIZE];\n    keymaster::Eraser send_buf_eraser(send_buf, TRUSTY_KEYMASTER_SEND_BUF_SIZE);\n    req.Serialize(send_buf, send_buf + req_size);\n\n    // Send it\n    auto response = trusty_keymaster_call_2(command, send_buf, req_size);\n    if (auto response_buffer = std::get_if<std::vector<uint8_t>>(&response)) {\n        keymaster::Eraser response_buffer_erasor(response_buffer->data(), response_buffer->size());\n        ALOGV(\"Received %zu byte response\\n\", response_buffer->size());\n\n        const uint8_t* p = response_buffer->data();\n        if (!rsp->Deserialize(&p, p + response_buffer->size())) {\n            ALOGE(\"Error deserializing response of size %zu\\n\", response_buffer->size());\n            return KM_ERROR_UNKNOWN_ERROR;\n        } else if (rsp->error != KM_ERROR_OK) {\n            ALOGE(\"Response of size %zu contained error code %d\\n\", response_buffer->size(),\n                  (int)rsp->error);\n        }\n        return rsp->error;\n    } else {\n        auto rc = std::get<int>(response);\n        // Reset the connection on tipc error\n        trusty_keymaster_disconnect();\n        trusty_keymaster_connect();\n        ALOGE(\"tipc error: %d\\n\", rc);\n        // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.\n        return translate_error(rc);\n    }\n}\n"
  },
  {
    "path": "trusty/keymaster/keymint/TEST_MAPPING",
    "content": "{\n  \"presubmit\" : [\n    {\n      \"name\" : \"vts_treble_vintf_framework_test\"\n    }\n  ],\n  \"hwasan-presubmit\" : [\n    {\n      \"name\" : \"vts_treble_vintf_framework_test\"\n    }\n  ]\n}"
  },
  {
    "path": "trusty/keymaster/keymint/TrustyKeyMintDevice.cpp",
    "content": "/*\n\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <trusty_keymaster/TrustyKeyMintDevice.h>\n\n#define TAG TrustyKeyMintDevice\n#include <android-base/logging.h>\n\n#include <keymaster/android_keymaster_messages.h>\n#include <keymaster/authorization_set.h>\n\n#include <KeyMintUtils.h>\n\n#include <trusty_keymaster/TrustyKeyMintOperation.h>\n\nnamespace aidl::android::hardware::security::keymint::trusty {\n\nusing keymaster::KeymasterBlob;\nusing keymaster::KeymasterKeyBlob;\nusing keymaster::TAG_APPLICATION_DATA;\nusing keymaster::TAG_APPLICATION_ID;\nusing keymaster::TAG_AUTH_TOKEN;\nusing km_utils::authToken2AidlVec;\nusing km_utils::kmBlob2vector;\nusing km_utils::kmError2ScopedAStatus;\nusing km_utils::kmParam2Aidl;\nusing km_utils::KmParamSet;\nusing km_utils::kmParamSet2Aidl;\nusing km_utils::legacy_enum_conversion;\n\nnamespace {\n\nauto kSecurityLevel = SecurityLevel::TRUSTED_ENVIRONMENT;\n\nKeyCharacteristics convertAuthSet(SecurityLevel securityLevel,\n                                  const keymaster::AuthorizationSet& authorizations) {\n    KeyCharacteristics retval{securityLevel, {}};\n    std::transform(authorizations.begin(), authorizations.end(),\n                   std::back_inserter(retval.authorizations), kmParam2Aidl);\n    return retval;\n}\n\nvector<KeyCharacteristics> convertKeyCharacteristics(const keymaster::AuthorizationSet& sw_enforced,\n                                                     const keymaster::AuthorizationSet& hw_enforced,\n                                                     bool includeKeystoreEnforced = true) {\n    KeyCharacteristics keyMintEnforced = convertAuthSet(kSecurityLevel, hw_enforced);\n    KeyCharacteristics keystoreEnforced = convertAuthSet(SecurityLevel::KEYSTORE, sw_enforced);\n\n    vector<KeyCharacteristics> retval;\n    retval.reserve(2);\n\n    if (!keyMintEnforced.authorizations.empty()) retval.push_back(std::move(keyMintEnforced));\n    if (includeKeystoreEnforced && !keystoreEnforced.authorizations.empty()) {\n        retval.push_back(std::move(keystoreEnforced));\n    }\n\n    return retval;\n}\n\nCertificate convertCertificate(const keymaster_blob_t& cert) {\n    return {std::vector<uint8_t>(cert.data, cert.data + cert.data_length)};\n}\n\nvector<Certificate> convertCertificateChain(const keymaster::CertificateChain& chain) {\n    vector<Certificate> retval;\n    std::transform(chain.begin(), chain.end(), std::back_inserter(retval), convertCertificate);\n    return retval;\n}\n\nvoid addClientAndAppData(const vector<uint8_t>& clientId, const vector<uint8_t>& appData,\n                         ::keymaster::AuthorizationSet* params) {\n    params->Clear();\n    if (clientId.size()) params->push_back(TAG_APPLICATION_ID, clientId.data(), clientId.size());\n    if (appData.size()) params->push_back(TAG_APPLICATION_DATA, appData.data(), appData.size());\n}\n\n}  // namespace\n\nScopedAStatus TrustyKeyMintDevice::getHardwareInfo(KeyMintHardwareInfo* info) {\n    info->versionNumber = 3;\n    info->securityLevel = kSecurityLevel;\n    info->keyMintName = \"TrustyKeyMintDevice\";\n    info->keyMintAuthorName = \"Google\";\n    info->timestampTokenRequired = false;\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::addRngEntropy(const vector<uint8_t>& data) {\n    if (data.size() == 0) return ScopedAStatus::ok();\n    if (data.size() > 2048) {\n        LOG(DEBUG) << \"Too-large entropy update of \" << data.size() << \" bytes.\";\n        return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);\n    }\n\n    keymaster::AddEntropyRequest request(impl_->message_version());\n    request.random_data.Reinitialize(data.data(), data.size());\n\n    keymaster::AddEntropyResponse response(impl_->message_version());\n    impl_->AddRngEntropy(request, &response);\n\n    return kmError2ScopedAStatus(response.error);\n}\n\nScopedAStatus TrustyKeyMintDevice::generateKey(const vector<KeyParameter>& keyParams,\n                                               const optional<AttestationKey>& attestationKey,\n                                               KeyCreationResult* creationResult) {\n    keymaster::GenerateKeyRequest request(impl_->message_version());\n    request.key_description.Reinitialize(KmParamSet(keyParams));\n    if (attestationKey) {\n        request.attestation_signing_key_blob =\n                KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());\n        request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));\n        request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),\n                                               attestationKey->issuerSubjectName.size());\n    }\n\n    keymaster::GenerateKeyResponse response(impl_->message_version());\n    impl_->GenerateKey(request, &response);\n\n    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);\n\n    creationResult->keyBlob = kmBlob2vector(response.key_blob);\n    creationResult->keyCharacteristics =\n            convertKeyCharacteristics(response.unenforced, response.enforced);\n    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::getKeyCharacteristics(\n        const vector<uint8_t>& keyBlob,\n        const vector<uint8_t>& clientId,  //\n        const vector<uint8_t>& appData,   //\n        vector<KeyCharacteristics>* characteristics) {\n    keymaster::GetKeyCharacteristicsRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n    addClientAndAppData(clientId, appData, &request.additional_params);\n\n    keymaster::GetKeyCharacteristicsResponse response(impl_->message_version());\n    impl_->GetKeyCharacteristics(request, &response);\n\n    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);\n\n    *characteristics = convertKeyCharacteristics(response.unenforced, response.enforced,\n                                                 false /* includeKeystoreEnforced */);\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::importKey(const vector<KeyParameter>& keyParams,\n                                             KeyFormat keyFormat, const vector<uint8_t>& keyData,\n                                             const optional<AttestationKey>& attestationKey,\n                                             KeyCreationResult* creationResult) {\n    keymaster::ImportKeyRequest request(impl_->message_version());\n    request.key_description.Reinitialize(KmParamSet(keyParams));\n    request.key_format = legacy_enum_conversion(keyFormat);\n    request.key_data = KeymasterKeyBlob(keyData.data(), keyData.size());\n    if (attestationKey) {\n        request.attestation_signing_key_blob =\n                KeymasterKeyBlob(attestationKey->keyBlob.data(), attestationKey->keyBlob.size());\n        request.attest_key_params.Reinitialize(KmParamSet(attestationKey->attestKeyParams));\n        request.issuer_subject = KeymasterBlob(attestationKey->issuerSubjectName.data(),\n                                               attestationKey->issuerSubjectName.size());\n    }\n\n    keymaster::ImportKeyResponse response(impl_->message_version());\n    impl_->ImportKey(request, &response);\n\n    if (response.error != KM_ERROR_OK) {\n        return kmError2ScopedAStatus(response.error);\n    }\n\n    creationResult->keyBlob = kmBlob2vector(response.key_blob);\n    creationResult->keyCharacteristics =\n            convertKeyCharacteristics(response.unenforced, response.enforced);\n    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);\n\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::importWrappedKey(const vector<uint8_t>& wrappedKeyData,\n                                                    const vector<uint8_t>& wrappingKeyBlob,  //\n                                                    const vector<uint8_t>& maskingKey,\n                                                    const vector<KeyParameter>& unwrappingParams,\n                                                    int64_t passwordSid,  //\n                                                    int64_t biometricSid,\n                                                    KeyCreationResult* creationResult) {\n    keymaster::ImportWrappedKeyRequest request(impl_->message_version());\n    request.SetWrappedMaterial(wrappedKeyData.data(), wrappedKeyData.size());\n    request.SetWrappingMaterial(wrappingKeyBlob.data(), wrappingKeyBlob.size());\n    request.SetMaskingKeyMaterial(maskingKey.data(), maskingKey.size());\n    request.additional_params.Reinitialize(KmParamSet(unwrappingParams));\n    request.password_sid = static_cast<uint64_t>(passwordSid);\n    request.biometric_sid = static_cast<uint64_t>(biometricSid);\n\n    keymaster::ImportWrappedKeyResponse response(impl_->message_version());\n    impl_->ImportWrappedKey(request, &response);\n\n    if (response.error != KM_ERROR_OK) {\n        return kmError2ScopedAStatus(response.error);\n    }\n\n    creationResult->keyBlob = kmBlob2vector(response.key_blob);\n    creationResult->keyCharacteristics =\n            convertKeyCharacteristics(response.unenforced, response.enforced);\n    creationResult->certificateChain = convertCertificateChain(response.certificate_chain);\n\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::upgradeKey(const vector<uint8_t>& keyBlobToUpgrade,\n                                              const vector<KeyParameter>& upgradeParams,\n                                              vector<uint8_t>* keyBlob) {\n    keymaster::UpgradeKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlobToUpgrade.data(), keyBlobToUpgrade.size());\n    request.upgrade_params.Reinitialize(KmParamSet(upgradeParams));\n\n    keymaster::UpgradeKeyResponse response(impl_->message_version());\n    impl_->UpgradeKey(request, &response);\n\n    if (response.error != KM_ERROR_OK) {\n        return kmError2ScopedAStatus(response.error);\n    }\n\n    *keyBlob = kmBlob2vector(response.upgraded_key);\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::deleteKey(const vector<uint8_t>& keyBlob) {\n    keymaster::DeleteKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n\n    keymaster::DeleteKeyResponse response(impl_->message_version());\n    impl_->DeleteKey(request, &response);\n\n    return kmError2ScopedAStatus(response.error);\n}\n\nScopedAStatus TrustyKeyMintDevice::deleteAllKeys() {\n    // There's nothing to be done to delete software key blobs.\n    keymaster::DeleteAllKeysRequest request(impl_->message_version());\n    keymaster::DeleteAllKeysResponse response(impl_->message_version());\n    impl_->DeleteAllKeys(request, &response);\n\n    return kmError2ScopedAStatus(response.error);\n}\n\nScopedAStatus TrustyKeyMintDevice::destroyAttestationIds() {\n    keymaster::DestroyAttestationIdsRequest request(impl_->message_version());\n    keymaster::DestroyAttestationIdsResponse response(impl_->message_version());\n    impl_->DestroyAttestationIds(request, &response);\n\n    return kmError2ScopedAStatus(response.error);\n}\n\nScopedAStatus TrustyKeyMintDevice::begin(KeyPurpose purpose, const vector<uint8_t>& keyBlob,\n                                         const vector<KeyParameter>& params,\n                                         const optional<HardwareAuthToken>& authToken,\n                                         BeginResult* result) {\n    keymaster::BeginOperationRequest request(impl_->message_version());\n    request.purpose = legacy_enum_conversion(purpose);\n    request.SetKeyMaterial(keyBlob.data(), keyBlob.size());\n    request.additional_params.Reinitialize(KmParamSet(params));\n\n    vector<uint8_t> vector_token = authToken2AidlVec(authToken);\n    request.additional_params.push_back(\n            TAG_AUTH_TOKEN, reinterpret_cast<uint8_t*>(vector_token.data()), vector_token.size());\n\n    keymaster::BeginOperationResponse response(impl_->message_version());\n    impl_->BeginOperation(request, &response);\n\n    if (response.error != KM_ERROR_OK) {\n        return kmError2ScopedAStatus(response.error);\n    }\n\n    result->params = kmParamSet2Aidl(response.output_params);\n    result->challenge = response.op_handle;\n    result->operation = ndk::SharedRefBase::make<TrustyKeyMintOperation>(impl_, response.op_handle);\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::deviceLocked(\n        bool passwordOnly, const std::optional<secureclock::TimeStampToken>& timestampToken) {\n    keymaster::DeviceLockedRequest request(impl_->message_version());\n    request.passwordOnly = passwordOnly;\n    if (timestampToken.has_value()) {\n        request.token.challenge = timestampToken->challenge;\n        request.token.mac = {timestampToken->mac.data(), timestampToken->mac.size()};\n        request.token.timestamp = timestampToken->timestamp.milliSeconds;\n    }\n    keymaster::DeviceLockedResponse response = impl_->DeviceLocked(request);\n    return kmError2ScopedAStatus(response.error);\n}\n\nScopedAStatus TrustyKeyMintDevice::earlyBootEnded() {\n    keymaster::EarlyBootEndedResponse response = impl_->EarlyBootEnded();\n    return kmError2ScopedAStatus(response.error);\n}\n\nScopedAStatus TrustyKeyMintDevice::convertStorageKeyToEphemeral(\n        const vector<uint8_t>& storageKeyBlob, vector<uint8_t>* ephemeralKeyBlob) {\n    keymaster::ExportKeyRequest request(impl_->message_version());\n    request.SetKeyMaterial(storageKeyBlob.data(), storageKeyBlob.size());\n    request.key_format = KM_KEY_FORMAT_RAW;\n\n    keymaster::ExportKeyResponse response(impl_->message_version());\n    impl_->ExportKey(request, &response);\n\n    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);\n    if (response.key_data) {\n        *ephemeralKeyBlob = {response.key_data, response.key_data + response.key_data_length};\n    }\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::getRootOfTrustChallenge(array<uint8_t, 16>* /* challenge */) {\n    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);\n}\n\nScopedAStatus TrustyKeyMintDevice::getRootOfTrust(const array<uint8_t, 16>& challenge,\n                                                  vector<uint8_t>* rootOfTrust) {\n    if (!rootOfTrust) {\n        return kmError2ScopedAStatus(KM_ERROR_UNEXPECTED_NULL_POINTER);\n    }\n    keymaster::GetRootOfTrustRequest request(impl_->message_version(),\n                                             {challenge.begin(), challenge.end()});\n    keymaster::GetRootOfTrustResponse response = impl_->GetRootOfTrust(request);\n    if (response.error != KM_ERROR_OK) {\n        return kmError2ScopedAStatus(response.error);\n    }\n\n    *rootOfTrust = std::move(response.rootOfTrust);\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintDevice::sendRootOfTrust(const vector<uint8_t>& /* rootOfTrust */) {\n    return kmError2ScopedAStatus(KM_ERROR_UNIMPLEMENTED);\n}\n\nScopedAStatus TrustyKeyMintDevice::setAdditionalAttestationInfo(const vector<KeyParameter>& info) {\n    keymaster::SetAdditionalAttestationInfoRequest request(impl_->message_version());\n    request.info.Reinitialize(KmParamSet(info));\n\n    keymaster::SetAdditionalAttestationInfoResponse response =\n            impl_->SetAdditionalAttestationInfo(request);\n\n    if (response.error != KM_ERROR_OK) {\n        return kmError2ScopedAStatus(response.error);\n    } else {\n        return ScopedAStatus::ok();\n    }\n}\n\n}  // namespace aidl::android::hardware::security::keymint::trusty\n"
  },
  {
    "path": "trusty/keymaster/keymint/TrustyKeyMintOperation.cpp",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <trusty_keymaster/TrustyKeyMintOperation.h>\n\n#define TAG TrustyKeyMintOperation\n#include <android-base/logging.h>\n\n#include <aidl/android/hardware/security/keymint/ErrorCode.h>\n#include <aidl/android/hardware/security/secureclock/ISecureClock.h>\n\n#include <KeyMintUtils.h>\n#include <keymaster/android_keymaster.h>\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nnamespace aidl::android::hardware::security::keymint {\n\nusing ::keymaster::AbortOperationRequest;\nusing ::keymaster::AbortOperationResponse;\nusing ::keymaster::FinishOperationRequest;\nusing ::keymaster::FinishOperationResponse;\nusing ::keymaster::TAG_ASSOCIATED_DATA;\nusing ::keymaster::TAG_AUTH_TOKEN;\nusing ::keymaster::TAG_CONFIRMATION_TOKEN;\nusing ::keymaster::UpdateOperationRequest;\nusing ::keymaster::UpdateOperationResponse;\nusing km_utils::authToken2AidlVec;\nusing km_utils::kmError2ScopedAStatus;\nusing secureclock::TimeStampToken;\n\nTrustyKeyMintOperation::TrustyKeyMintOperation(shared_ptr<TrustyKeymaster> implementation,\n                                               keymaster_operation_handle_t opHandle)\n    : impl_(std::move(implementation)), opHandle_(opHandle) {}\n\nTrustyKeyMintOperation::~TrustyKeyMintOperation() {\n    if (opHandle_ != 0) {\n        abort();\n    }\n}\n\nScopedAStatus TrustyKeyMintOperation::updateAad(\n        const vector<uint8_t>& input, const optional<HardwareAuthToken>& authToken,\n        const optional<TimeStampToken>& /* timestampToken */) {\n    UpdateOperationRequest request(impl_->message_version());\n    request.op_handle = opHandle_;\n    request.additional_params.push_back(TAG_ASSOCIATED_DATA, input.data(), input.size());\n    if (authToken) {\n        auto tokenAsVec(authToken2AidlVec(*authToken));\n        request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());\n    }\n\n    UpdateOperationResponse response(impl_->message_version());\n    impl_->UpdateOperation(request, &response);\n\n    return kmError2ScopedAStatus(response.error);\n}\n\nScopedAStatus TrustyKeyMintOperation::update(const vector<uint8_t>& input,\n                                             const optional<HardwareAuthToken>& authToken,\n                                             const optional<TimeStampToken>& /* timestampToken */,\n                                             vector<uint8_t>* output) {\n    if (!output) return kmError2ScopedAStatus(KM_ERROR_OUTPUT_PARAMETER_NULL);\n\n    UpdateOperationRequest request(impl_->message_version());\n    request.op_handle = opHandle_;\n    if (authToken) {\n        auto tokenAsVec(authToken2AidlVec(*authToken));\n        request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());\n    }\n\n    size_t serialized_size = request.SerializedSize();\n    if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {\n        return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);\n    }\n\n    const uint8_t* input_pos = input.data();\n    const uint8_t* input_end = input.data() + input.size();\n    const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;\n    output->clear();\n\n    while (input_pos < input_end) {\n        size_t to_send = std::min(max_chunk_size, static_cast<size_t>(input_end - input_pos));\n        LOG(DEBUG) << \"update:  Sending \" << to_send << \" of \" << (input_end - input_pos)\n                   << \" bytes\";\n        request.input.Reinitialize(input_pos, to_send);\n\n        UpdateOperationResponse response(impl_->message_version());\n        impl_->UpdateOperation(request, &response);\n        if (response.error != KM_ERROR_OK) {\n            opHandle_ = 0;  // Operation has ended, the handle is invalid.  This saves an abort().\n            return kmError2ScopedAStatus(response.error);\n        }\n\n        input_pos += response.input_consumed;\n        output->insert(output->end(), response.output.begin(), response.output.end());\n    }\n\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintOperation::finish(const optional<vector<uint8_t>>& input,      //\n                                             const optional<vector<uint8_t>>& signature,  //\n                                             const optional<HardwareAuthToken>& authToken,\n                                             const optional<TimeStampToken>& /* timestampToken */,\n                                             const optional<vector<uint8_t>>& confirmationToken,\n                                             vector<uint8_t>* output) {\n    if (!output) {\n        return ScopedAStatus(AStatus_fromServiceSpecificError(\n                static_cast<int32_t>(ErrorCode::OUTPUT_PARAMETER_NULL)));\n    }\n    output->clear();\n\n    FinishOperationRequest request(impl_->message_version());\n\n    if (authToken) {\n        auto tokenAsVec(authToken2AidlVec(*authToken));\n        request.additional_params.push_back(TAG_AUTH_TOKEN, tokenAsVec.data(), tokenAsVec.size());\n    }\n    if (confirmationToken) {\n        request.additional_params.push_back(TAG_CONFIRMATION_TOKEN, confirmationToken->data(),\n                                            confirmationToken->size());\n    }\n\n    request.op_handle = opHandle_;\n    if (signature) request.signature.Reinitialize(signature->data(), signature->size());\n    size_t serialized_size = request.SerializedSize();\n    if (serialized_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) {\n        return kmError2ScopedAStatus(KM_ERROR_INVALID_INPUT_LENGTH);\n    }\n\n    if (input) {\n        const size_t max_chunk_size = TRUSTY_KEYMASTER_SEND_BUF_SIZE - serialized_size;\n\n        if (input->size() > max_chunk_size) {\n            LOG(DEBUG) << \"Sending an update to process finish() data\";\n            // Use update to process all but the last max_chunk_size bytes.\n            auto result = update({input->begin(), input->end() - max_chunk_size}, authToken,\n                                 std::nullopt /* timestampToken */, output);\n            if (!result.isOk()) return result;\n\n            // Process the last max_chunk_size with finish.\n            request.input.Reinitialize(input->data() + (input->size() - max_chunk_size),\n                                       max_chunk_size);\n        } else {\n            request.input.Reinitialize(input->data(), input->size());\n        }\n    }\n\n    FinishOperationResponse response(impl_->message_version());\n    impl_->FinishOperation(request, &response);\n    opHandle_ = 0;\n\n    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);\n\n    *output = {response.output.begin(), response.output.end()};\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyKeyMintOperation::abort() {\n    AbortOperationRequest request(impl_->message_version());\n    request.op_handle = opHandle_;\n\n    AbortOperationResponse response(impl_->message_version());\n    impl_->AbortOperation(request, &response);\n    opHandle_ = 0;\n\n    return kmError2ScopedAStatus(response.error);\n}\n\n}  // namespace aidl::android::hardware::security::keymint\n"
  },
  {
    "path": "trusty/keymaster/keymint/TrustyRemotelyProvisionedComponentDevice.cpp",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>\n\n#include <assert.h>\n#include <variant>\n\n#include <KeyMintUtils.h>\n#include <keymaster/keymaster_configuration.h>\n\n#include <trusty_keymaster/TrustyKeyMintDevice.h>\n\nnamespace aidl::android::hardware::security::keymint::trusty {\n\nusing keymaster::GenerateCsrRequest;\nusing keymaster::GenerateCsrResponse;\nusing keymaster::GenerateCsrV2Request;\nusing keymaster::GenerateCsrV2Response;\nusing keymaster::GenerateRkpKeyRequest;\nusing keymaster::GenerateRkpKeyResponse;\nusing keymaster::GetHwInfoRequest;\nusing keymaster::GetHwInfoResponse;\nusing keymaster::KeymasterBlob;\nusing km_utils::kmError2ScopedAStatus;\nusing ::std::string;\nusing ::std::unique_ptr;\nusing ::std::vector;\nusing bytevec = ::std::vector<uint8_t>;\n\nnamespace {\n\nconstexpr auto STATUS_FAILED = IRemotelyProvisionedComponent::STATUS_FAILED;\n\nstruct AStatusDeleter {\n    void operator()(AStatus* p) { AStatus_delete(p); }\n};\n\nclass Status {\n  public:\n    Status() : status_(AStatus_newOk()) {}\n    Status(int32_t errCode, const std::string& errMsg)\n        : status_(AStatus_fromServiceSpecificErrorWithMessage(errCode, errMsg.c_str())) {}\n    explicit Status(const std::string& errMsg)\n        : status_(AStatus_fromServiceSpecificErrorWithMessage(STATUS_FAILED, errMsg.c_str())) {}\n    explicit Status(AStatus* status) : status_(status ? status : AStatus_newOk()) {}\n\n    Status(Status&&) = default;\n    Status(const Status&) = delete;\n\n    operator ::ndk::ScopedAStatus() && {  // NOLINT(google-explicit-constructor)\n        return ndk::ScopedAStatus(status_.release());\n    }\n\n    bool isOk() const { return AStatus_isOk(status_.get()); }\n\n    const char* getMessage() const { return AStatus_getMessage(status_.get()); }\n\n  private:\n    std::unique_ptr<AStatus, AStatusDeleter> status_;\n};\n\n}  // namespace\n\nScopedAStatus TrustyRemotelyProvisionedComponentDevice::getHardwareInfo(RpcHardwareInfo* info) {\n    GetHwInfoResponse response = impl_->GetHwInfo();\n    if (response.error != KM_ERROR_OK) {\n        return Status(-static_cast<int32_t>(response.error), \"Failed to get hardware info.\");\n    }\n\n    info->versionNumber = response.version;\n    info->rpcAuthorName = std::move(response.rpcAuthorName);\n    info->supportedEekCurve = response.supportedEekCurve;\n    info->uniqueId = std::move(response.uniqueId);\n    info->supportedNumKeysInCsr = response.supportedNumKeysInCsr;\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateEcdsaP256KeyPair(\n        bool testMode, MacedPublicKey* macedPublicKey, bytevec* privateKeyHandle) {\n    GenerateRkpKeyRequest request(impl_->message_version());\n    request.test_mode = testMode;\n    GenerateRkpKeyResponse response(impl_->message_version());\n    impl_->GenerateRkpKey(request, &response);\n    if (response.error != KM_ERROR_OK) {\n        return Status(-static_cast<int32_t>(response.error), \"Failure in key generation.\");\n    }\n\n    macedPublicKey->macedKey = km_utils::kmBlob2vector(response.maced_public_key);\n    *privateKeyHandle = km_utils::kmBlob2vector(response.key_blob);\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequest(\n        bool testMode, const vector<MacedPublicKey>& keysToSign,\n        const bytevec& endpointEncCertChain, const bytevec& challenge, DeviceInfo* deviceInfo,\n        ProtectedData* protectedData, bytevec* keysToSignMac) {\n    GenerateCsrRequest request(impl_->message_version());\n    request.test_mode = testMode;\n    request.num_keys = keysToSign.size();\n    request.keys_to_sign_array = new KeymasterBlob[keysToSign.size()];\n    for (size_t i = 0; i < keysToSign.size(); i++) {\n        request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());\n    }\n    request.SetEndpointEncCertChain(endpointEncCertChain.data(), endpointEncCertChain.size());\n    request.SetChallenge(challenge.data(), challenge.size());\n    GenerateCsrResponse response(impl_->message_version());\n    impl_->GenerateCsr(request, &response);\n\n    if (response.error != KM_ERROR_OK) {\n        return Status(-static_cast<int32_t>(response.error), \"Failure in CSR Generation.\");\n    }\n    deviceInfo->deviceInfo = km_utils::kmBlob2vector(response.device_info_blob);\n    protectedData->protectedData = km_utils::kmBlob2vector(response.protected_data_blob);\n    *keysToSignMac = km_utils::kmBlob2vector(response.keys_to_sign_mac);\n    return ScopedAStatus::ok();\n}\n\nScopedAStatus TrustyRemotelyProvisionedComponentDevice::generateCertificateRequestV2(\n        const std::vector<MacedPublicKey>& keysToSign, const std::vector<uint8_t>& challenge,\n        std::vector<uint8_t>* csr) {\n    GenerateCsrV2Request request(impl_->message_version());\n    if (!request.InitKeysToSign(keysToSign.size())) {\n        return kmError2ScopedAStatus(static_cast<keymaster_error_t>(STATUS_FAILED));\n    }\n    for (size_t i = 0; i < keysToSign.size(); i++) {\n        request.SetKeyToSign(i, keysToSign[i].macedKey.data(), keysToSign[i].macedKey.size());\n    }\n    request.SetChallenge(challenge.data(), challenge.size());\n    GenerateCsrV2Response response(impl_->message_version());\n    impl_->GenerateCsrV2(request, &response);\n\n    if (response.error != KM_ERROR_OK) {\n        return Status(-static_cast<int32_t>(response.error), \"Failure in CSR v2 generation.\");\n    }\n    *csr = km_utils::kmBlob2vector(response.csr);\n    return ScopedAStatus::ok();\n}\n\n}  // namespace aidl::android::hardware::security::keymint::trusty\n"
  },
  {
    "path": "trusty/keymaster/keymint/TrustySecureClock.cpp",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <trusty_keymaster/TrustySecureClock.h>\n\n#include <aidl/android/hardware/security/keymint/ErrorCode.h>\n\n#include <KeyMintUtils.h>\n\nnamespace aidl::android::hardware::security::secureclock::trusty {\n\nusing keymint::km_utils::kmBlob2vector;\nusing keymint::km_utils::kmError2ScopedAStatus;\n\n::ndk::ScopedAStatus TrustySecureClock::generateTimeStamp(int64_t challenge,\n                                                          TimeStampToken* token) {\n    keymaster::VerifyAuthorizationRequest request(impl_->message_version());\n    request.challenge = challenge;\n\n    auto response = impl_->VerifyAuthorization(request);\n    if (response.error != KM_ERROR_OK) return kmError2ScopedAStatus(response.error);\n\n    token->challenge = response.token.challenge;\n    token->timestamp.milliSeconds = static_cast<int64_t>(response.token.timestamp);\n    token->mac = kmBlob2vector(response.token.mac);\n    return ::ndk::ScopedAStatus::ok();\n}\n\n}  // namespace aidl::android::hardware::security::secureclock::trusty\n"
  },
  {
    "path": "trusty/keymaster/keymint/TrustySharedSecret.cpp",
    "content": "/*\n * Copyright 2020, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <trusty_keymaster/TrustySharedSecret.h>\n\n#include <aidl/android/hardware/security/keymint/ErrorCode.h>\n#include <keymaster/android_keymaster.h>\n#include \"KeyMintUtils.h\"\n\nnamespace aidl::android::hardware::security::sharedsecret::trusty {\n\nusing keymint::km_utils::kmBlob2vector;\nusing keymint::km_utils::kmError2ScopedAStatus;\n\n::ndk::ScopedAStatus TrustySharedSecret::getSharedSecretParameters(SharedSecretParameters* params) {\n    auto response = impl_->GetHmacSharingParameters();\n    params->seed = kmBlob2vector(response.params.seed);\n    params->nonce = {std::begin(response.params.nonce), std::end(response.params.nonce)};\n    return kmError2ScopedAStatus(response.error);\n}\n\n::ndk::ScopedAStatus TrustySharedSecret::computeSharedSecret(\n        const std::vector<SharedSecretParameters>& params, std::vector<uint8_t>* sharingCheck) {\n    keymaster::ComputeSharedHmacRequest request(impl_->message_version());\n    request.params_array.params_array = new keymaster::HmacSharingParameters[params.size()];\n    request.params_array.num_params = params.size();\n    for (size_t i = 0; i < params.size(); ++i) {\n        request.params_array.params_array[i].seed = {params[i].seed.data(), params[i].seed.size()};\n        if (sizeof(request.params_array.params_array[i].nonce) != params[i].nonce.size()) {\n            return kmError2ScopedAStatus(KM_ERROR_INVALID_ARGUMENT);\n        }\n        memcpy(request.params_array.params_array[i].nonce, params[i].nonce.data(),\n               params[i].nonce.size());\n    }\n\n    auto response = impl_->ComputeSharedHmac(request);\n    if (response.error == KM_ERROR_OK) *sharingCheck = kmBlob2vector(response.sharing_check);\n    return kmError2ScopedAStatus(response.error);\n}\n\n}  // namespace aidl::android::hardware::security::sharedsecret::trusty\n"
  },
  {
    "path": "trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.rc",
    "content": "service vendor.keymint-trusty /vendor/bin/hw/android.hardware.security.keymint-service.trusty \\\n                                          --dev ${ro.hardware.trusty_ipc_dev.keymint:-/dev/trusty-ipc-dev0}\n    class early_hal\n    user nobody\n    group drmrpc\n"
  },
  {
    "path": "trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"aidl\">\n        <name>android.hardware.security.keymint</name>\n        <version>4</version>\n        <fqname>IKeyMintDevice/default</fqname>\n    </hal>\n    <hal format=\"aidl\">\n        <name>android.hardware.security.secureclock</name>\n        <fqname>ISecureClock/default</fqname>\n    </hal>\n    <hal format=\"aidl\">\n        <name>android.hardware.security.sharedsecret</name>\n        <fqname>ISharedSecret/default</fqname>\n    </hal>\n    <hal format=\"aidl\">\n        <name>android.hardware.security.keymint</name>\n        <version>3</version>\n        <fqname>IRemotelyProvisionedComponent/default</fqname>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "trusty/keymaster/keymint/android.hardware.security.keymint-service.trusty_tee.cpp.rc",
    "content": "# service started when selecting `com.android.hardware.keymint.trusty_tee.cpp` vendor apex\nservice vendor.keymint-service.trusty_tee.cpp \\\n  /vendor/bin/hw/android.hardware.security.keymint-service.trusty_tee.cpp \\\n    --dev ${ro.hardware.trusty_ipc_dev.keymint:-/dev/trusty-ipc-dev0}\n    disabled\n    class early_hal\n    user nobody\n    group drmrpc\n    # The keymint service is not allowed to restart.\n    # If it crashes, a device restart is required.\n    oneshot\n"
  },
  {
    "path": "trusty/keymaster/keymint/service.cpp",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"android.hardware.security.keymint-service.trusty\"\n#include <android-base/logging.h>\n#include <android/binder_manager.h>\n#include <android/binder_process.h>\n#include <getopt.h>\n\n#include <trusty_keymaster/TrustyKeyMintDevice.h>\n#include <trusty_keymaster/TrustyRemotelyProvisionedComponentDevice.h>\n#include <trusty_keymaster/TrustySecureClock.h>\n#include <trusty_keymaster/TrustySharedSecret.h>\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nusing aidl::android::hardware::security::keymint::trusty::TrustyKeyMintDevice;\nusing aidl::android::hardware::security::keymint::trusty::TrustyRemotelyProvisionedComponentDevice;\nusing aidl::android::hardware::security::secureclock::trusty::TrustySecureClock;\nusing aidl::android::hardware::security::sharedsecret::trusty::TrustySharedSecret;\n\ntemplate <typename T, class... Args>\nstd::shared_ptr<T> addService(Args&&... args) {\n    std::shared_ptr<T> service = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);\n    auto instanceName = std::string(T::descriptor) + \"/default\";\n    LOG(ERROR) << \"Adding service instance: \" << instanceName;\n    auto status = AServiceManager_addService(service->asBinder().get(), instanceName.c_str());\n    CHECK(status == STATUS_OK) << \"Failed to add service \" << instanceName;\n    return service;\n}\n\nstatic const char* _sopts = \"hD:\";\nstatic const struct option _lopts[] = {\n        {\"help\", no_argument, 0, 'h'},\n        {\"dev\", required_argument, 0, 'D'},\n        {0, 0, 0, 0},\n};\n\nstatic const char* usage =\n        \"Usage: %s [options]\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h, --help            prints this message and exit\\n\"\n        \"  -D, --dev name        Trusty device name\\n\"\n        \"\\n\";\n\nstatic const char* usage_long = \"\\n\";\n\nstatic void print_usage_and_exit(const char* prog, int code, bool verbose) {\n    fprintf(stderr, usage, prog);\n    if (verbose) {\n        fprintf(stderr, \"%s\", usage_long);\n    }\n    exit(code);\n}\n\nstatic void parse_options(int argc, char** argv) {\n    int c;\n    int oidx = 0;\n\n    while (1) {\n        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);\n        if (c == -1) {\n            break; /* done */\n        }\n\n        switch (c) {\n            case 'D':\n                trusty_keymaster_set_dev_name(optarg);\n                break;\n\n            case 'h':\n                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);\n                break;\n\n            default:\n                print_usage_and_exit(argv[0], EXIT_FAILURE, false);\n        }\n    }\n}\n\nint main(int argc, char** argv) {\n    parse_options(argc, argv);\n    auto trustyKeymaster = std::make_shared<keymaster::TrustyKeymaster>();\n    int err = trustyKeymaster->Initialize(keymaster::KmVersion::KEYMINT_3);\n    if (err != 0) {\n        LOG(FATAL) << \"Could not initialize TrustyKeymaster for KeyMint (\" << err << \")\";\n        return -1;\n    }\n\n    // Zero threads seems like a useless pool but below we'll join this thread to it, increasing\n    // the pool size to 1.\n    ABinderProcess_setThreadPoolMaxThreadCount(0);\n\n    auto keyMint = addService<TrustyKeyMintDevice>(trustyKeymaster);\n    auto secureClock = addService<TrustySecureClock>(trustyKeymaster);\n    auto sharedSecret = addService<TrustySharedSecret>(trustyKeymaster);\n    auto remotelyProvisionedComponent =\n            addService<TrustyRemotelyProvisionedComponentDevice>(trustyKeymaster);\n    ABinderProcess_joinThreadPool();\n    return EXIT_FAILURE;  // should not reach\n}\n"
  },
  {
    "path": "trusty/keymaster/set_attestation_ids/set_attestation_ids.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <getopt.h>\n\n#include <string>\n#include <vector>\n\n#include <android-base/properties.h>\n#include <android-base/strings.h>\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nnamespace {\n\nconst char* sopts = \"hb:d:p:s:M:m:i:c:\";\nconst struct option lopts[] = {\n        {\"help\", no_argument, nullptr, 'h'},\n        {\"brand\", required_argument, nullptr, 'b'},\n        {\"device\", required_argument, nullptr, 'd'},\n        {\"product\", required_argument, nullptr, 'p'},\n        {\"serial\", required_argument, nullptr, 's'},\n        {\"manufacturer\", required_argument, nullptr, 'M'},\n        {\"model\", required_argument, nullptr, 'm'},\n        {\"imei\", required_argument, nullptr, 'i'},\n        {\"meid\", required_argument, nullptr, 'c'},\n        {\"imei2\", required_argument, nullptr, '2'},\n        {0, 0, 0, 0},\n};\n\nstd::string TELEPHONY_CMD_GET_IMEI = \"cmd phone get-imei \";\n\n// Run a shell command and collect the output of it. If any error, set an empty string as the\n// output.\nstd::string exec_command(const std::string& command) {\n    char buffer[128];\n    std::string result = \"\";\n\n    FILE* pipe = popen(command.c_str(), \"r\");\n    if (!pipe) {\n        fprintf(stderr, \"popen('%s') failed\\n\", command.c_str());\n        return result;\n    }\n\n    while (!feof(pipe)) {\n        if (fgets(buffer, 128, pipe) != NULL) {\n            result += buffer;\n        }\n    }\n\n    pclose(pipe);\n    return result;\n}\n\n// Get IMEI using Telephony service shell command. If any error while executing the command\n// then empty string will be returned as output.\nstd::string get_imei(int slot) {\n    std::string cmd = TELEPHONY_CMD_GET_IMEI + std::to_string(slot);\n    std::string output = exec_command(cmd);\n\n    if (output.empty()) {\n        fprintf(stderr, \"Retrieve IMEI command ('%s') failed\\n\", cmd.c_str());\n        return \"\";\n    }\n\n    std::vector<std::string> out =\n            ::android::base::Tokenize(::android::base::Trim(output), \"Device IMEI:\");\n\n    if (out.size() != 1) {\n        fprintf(stderr, \"Error parsing command ('%s') output '%s'\\n\", cmd.c_str(), output.c_str());\n        return \"\";\n    }\n\n    std::string imei = ::android::base::Trim(out[0]);\n    if (imei.compare(\"null\") == 0) {\n        fprintf(stderr, \"IMEI value from command ('%s') is null, skipping\", cmd.c_str());\n        return \"\";\n    }\n    return imei;\n}\n\nstd::string buf2string(const keymaster::Buffer& buf) {\n    return std::string(reinterpret_cast<const char*>(buf.peek_read()), buf.available_read());\n}\n\nvoid print_usage(const char* prog, const keymaster::SetAttestationIdsKM3Request& req) {\n    fprintf(stderr,\n            \"Usage: %s [options]\\n\"\n            \"\\n\"\n            \"options:\\n\"\n            \"  -h, --help                 prints this message and exit\\n\"\n            \"  -b, --brand <val>          set brand (default '%s')\\n\"\n            \"  -d, --device <val>         set device (default '%s')\\n\"\n            \"  -p, --product <val>        set product (default '%s')\\n\"\n            \"  -s, --serial <val>         set serial (default '%s')\\n\"\n            \"  -M, --manufacturer <val>   set manufacturer (default '%s')\\n\"\n            \"  -m, --model <val>          set model (default '%s')\\n\"\n            \"  -i, --imei <val>           set IMEI (default '%s')\\n\"\n            \"  -c, --meid <val>           set MEID (default '%s')\\n\"\n            \"  -2, --imei2 <val>          set second IMEI (default '%s')\\n\"\n            \"\\n\",\n            prog, buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),\n            buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),\n            buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),\n            buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),\n            buf2string(req.second_imei).c_str());\n}\n\nvoid set_to(keymaster::Buffer* buf, const std::string& value) {\n    if (!value.empty()) {\n        buf->Reinitialize(value.data(), value.size());\n    }\n}\n\nvoid set_from_prop(keymaster::Buffer* buf, const std::string& prop) {\n    std::string prop_value = ::android::base::GetProperty(prop, /* default_value = */ \"\");\n    set_to(buf, prop_value);\n}\n\nvoid populate_base_ids(keymaster::SetAttestationIdsRequest* req) {\n    set_from_prop(&req->brand, \"ro.product.brand\");\n    set_from_prop(&req->device, \"ro.product.device\");\n    set_from_prop(&req->product, \"ro.product.name\");\n    set_from_prop(&req->serial, \"ro.serialno\");\n    set_from_prop(&req->manufacturer, \"ro.product.manufacturer\");\n    set_from_prop(&req->model, \"ro.product.model\");\n    std::string imei = get_imei(0);\n    set_to(&req->imei, imei);\n}\n\nvoid populate_ids(keymaster::SetAttestationIdsKM3Request* req) {\n    populate_base_ids(&req->base);\n\n    // - \"What about IMEI?\"\n    // - \"You've already had it.\"\n    // - \"We've had one, yes. What about second IMEI?\"\n    // - \"I don't think he knows about second IMEI, Pip.\"\n    std::string imei2 = get_imei(1);\n    set_to(&req->second_imei, imei2);\n}\n\n}  // namespace\n\nint main(int argc, char** argv) {\n    // By default, set attestation IDs to the values in userspace properties.\n    keymaster::SetAttestationIdsKM3Request req(/* ver = */ 4);\n    populate_ids(&req);\n\n    while (true) {\n        int oidx = 0;\n        int c = getopt_long(argc, argv, sopts, lopts, &oidx);\n        if (c == -1) {\n            break; /* done */\n        }\n\n        switch (c) {\n            case 'b':\n                req.base.brand.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 'd':\n                req.base.device.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 'p':\n                req.base.product.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 's':\n                req.base.serial.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 'M':\n                req.base.manufacturer.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 'm':\n                req.base.model.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 'i':\n                req.base.imei.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 'c':\n                req.base.meid.Reinitialize(optarg, strlen(optarg));\n                break;\n            case '2':\n                req.second_imei.Reinitialize(optarg, strlen(optarg));\n                break;\n            case 'h':\n                print_usage(argv[0], req);\n                exit(EXIT_SUCCESS);\n            default:\n                print_usage(argv[0], req);\n                exit(EXIT_FAILURE);\n        }\n    }\n    if (optind != argc) {\n        print_usage(argv[0], req);\n        exit(EXIT_FAILURE);\n    }\n\n    int ret = trusty_keymaster_connect();\n    if (ret) {\n        fprintf(stderr, \"trusty_keymaster_connect failed: %d\\n\", ret);\n        return EXIT_FAILURE;\n    }\n\n    printf(\"Setting:\\n\"\n           \"  brand:        %s\\n\"\n           \"  device:       %s\\n\"\n           \"  product:      %s\\n\"\n           \"  serial:       %s\\n\"\n           \"  manufacturer: %s\\n\"\n           \"  model:        %s\\n\"\n           \"  IMEI:         %s\\n\"\n           \"  MEID:         %s\\n\"\n           \"  SECOND_IMEI:  %s\\n\\n\",\n           buf2string(req.base.brand).c_str(), buf2string(req.base.device).c_str(),\n           buf2string(req.base.product).c_str(), buf2string(req.base.serial).c_str(),\n           buf2string(req.base.manufacturer).c_str(), buf2string(req.base.model).c_str(),\n           buf2string(req.base.imei).c_str(), buf2string(req.base.meid).c_str(),\n           buf2string(req.second_imei).c_str());\n    fflush(stdout);\n\n    keymaster::EmptyKeymasterResponse rsp(/* ver = */ 4);\n    const char* msg;\n    if (req.second_imei.available_read() == 0) {\n        // No SECOND_IMEI set, use base command.\n        ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req.base, &rsp);\n        msg = \"SET_ATTESTATION_IDS\";\n    } else {\n        // SECOND_IMEI is set, use updated command.\n        ret = trusty_keymaster_send(KM_SET_ATTESTATION_IDS_KM3, req, &rsp);\n        msg = \"SET_ATTESTATION_IDS_KM3\";\n    }\n    trusty_keymaster_disconnect();\n\n    if (ret) {\n        fprintf(stderr, \"%s failed: %d\\n\", msg, ret);\n        return EXIT_FAILURE;\n    } else {\n        printf(\"done\\n\");\n        printf(\"\\nNOTE: device reboot may be required before changes take effect.\\n\");\n        return EXIT_SUCCESS;\n    }\n}\n"
  },
  {
    "path": "trusty/keymaster/set_attestation_key/keymaster_soft_attestation_keys.xml",
    "content": "<?xml version=\"1.0\"?>\n<AndroidAttestation>\n  <NumberOfKeyboxes>10</NumberOfKeyboxes>\n  <Keybox DeviceID=\"dev1\">\n    <Key algorithm=\"rsa\">\n      <PrivateKey format=\"pem\">\n-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1qEIEir6LR752/q7yXPKb\nKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX4YVBeuVKvClqOm21wAQI\nO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmdXX0jYvKcXgLocQIDAQAB\nAoGBAL6GCwuZqAKm+xpZQ4p7txUGWwmjbcbpysxr88AsNNfXnpTGYGQo2Ix7f2V3\nwc3qZAdKvo5yht8fCBHclygmCGjeldMu/Ja20IT/JxpfYN78xwPno45uKbqaPF/C\nwoB2tqiWrx0014gozpvdsfNPnJQEQweBKY4gExZyW728mTpBAkEA4cbZJ2RsCRbs\nNoJtWUmDdAwh8bB0xKGlmGfGaXlchdPcRkxbkp6Uv7NODcxQFLEPEzQat/3V9gQU\n0qMmytQcxQJBANpIWZd4XNVjD7D9jFJU+Y5TjhiYOq6ea35qWntdNDdVuSGOvUAy\nDSg4fXifdvohi8wti2il9kGPu+ylF5qzr70CQFD+/DJklVlhbtZTThVFCTKdk6PY\nENvlvbmCKSz3i9i624Agro1X9LcdBThv/p6dsnHKNHejSZnbdvjl7OnA1J0CQBW3\nTPJ8zv+Ls2vwTZ2DRrCaL3DS9EObDyasfgP36dH3fUuRX9KbKCPwOstdUgDghX/y\nqAPpPu6W1iNc6VRCvCECQQCQp0XaiXCyzWSWYDJCKMX4KFb/1mW6moXI1g8bi+5x\nfs0scurgHa2GunZU1M9FrbXx8rMdn4Eiz6XxpVcPmy0l\n-----END RSA PRIVATE KEY-----\n      </PrivateKey>\n      <CertificateChain>\n        <NumberOfCertificates>2</NumberOfCertificates>\n        <Certificate format=\"pem\">\n-----BEGIN CERTIFICATE-----\nMIICtjCCAh+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT\nBgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDAeFw0xNjAxMDQx\nMjQwNTNaFw0zNTEyMzAxMjQwNTNaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApD\nYWxpZm9ybmlhMRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJv\naWQxKTAnBgNVBAMMIEFuZHJvaWQgU29mdHdhcmUgQXR0ZXN0YXRpb24gS2V5MIGf\nMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAgyPcVogbuDAgafWwhWHG7r5/BeL1\nqEIEir6LR752/q7yXPKbKvoyABQWAUKZiaFfz8aBXrNjWDwv0vIL5Jgyg92BSxbX\n4YVBeuVKvClqOm21wAQIO2jFVsHwIzmRZBmGTVC3TUCuykhMdzVsiVoMJ1q/rEmd\nXX0jYvKcXgLocQIDAQABo2YwZDAdBgNVHQ4EFgQU1AwQG/jNY7n3OVK1DhNcpteZ\nk4YwHwYDVR0jBBgwFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wEgYDVR0TAQH/BAgw\nBgEB/wIBADAOBgNVHQ8BAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAni1IX4xn\nM9waha2Z11Aj6hTsQ7DhnerCI0YecrUZ3GAi5KVoMWwLVcTmnKItnzpPk2sxixZ4\nFg2Iy9mLzICdhPDCJ+NrOPH90ecXcjFZNX2W88V/q52PlmEmT7K+gbsNSQQiis6f\n9/VCLiVE+iEHElqDtVWtGIL4QBSbnCBjBH8=\n-----END CERTIFICATE-----\n        </Certificate>\n        <Certificate format=\"pem\">\n-----BEGIN CERTIFICATE-----\nMIICpzCCAhCgAwIBAgIJAP+U2d2fB8gMMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV\nBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBW\naWV3MRUwEwYDVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQwHhcN\nMTYwMTA0MTIzMTA4WhcNMzUxMjMwMTIzMTA4WjBjMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEVMBMGA1UE\nCgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMIGfMA0GCSqGSIb3DQEB\nAQUAA4GNADCBiQKBgQCia63rbi5EYe/VDoLmt5TRdSMfd5tjkWP/96r/C3JHTsAs\nQ+wzfNes7UA+jCigZtX3hwszl94OuE4TQKuvpSe/lWmgMdsGUmX4RFlXYfC78hdL\nt0GAZMAoDo9Sd47b0ke2RekZyOmLw9vCkT/X11DEHTVm+Vfkl5YLCazOkjWFmwID\nAQABo2MwYTAdBgNVHQ4EFgQUKfrxrMxN0kyWQCd1trDpMuUH/i4wHwYDVR0jBBgw\nFoAUKfrxrMxN0kyWQCd1trDpMuUH/i4wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\nAf8EBAMCAoQwDQYJKoZIhvcNAQELBQADgYEAT3LzNlmNDsG5dFsxWfbwjSVJMJ6j\nHBwp0kUtILlNX2S06IDHeHqcOd6os/W/L3BfRxBcxebrTQaZYdKumgf/93y4q+uc\nDyQHXrF/unlx/U1bnt8Uqf7f7XzAiF343ZtkMlbVNZriE/mPzsF83O+kqrJVw4Op\nLvtc9mL1J1IXvmM=\n-----END CERTIFICATE-----\n        </Certificate>\n      </CertificateChain>\n    </Key>\n  </Keybox>\n  <Keybox DeviceID=\"dev1\">\n    <Key algorithm=\"ecdsa\">\n      <PrivateKey format=\"pem\">\n-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEICHghkMqFRmEWc82OlD8FMnarfk19SfC39ceTW28QuVEoAoGCCqGSM49\nAwEHoUQDQgAE6555+EJjWazLKpFMiYbMcK2QZpOCqXMmE/6sy/ghJ0whdJdKKv6l\nuU1/ZtTgZRBmNbxTt6CjpnFYPts+Ea4QFA==\n-----END EC PRIVATE KEY-----\n      </PrivateKey>\n      <CertificateChain>\n        <NumberOfCertificates>2</NumberOfCertificates>\n        <Certificate format=\"pem\">\n-----BEGIN CERTIFICATE-----\nMIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw\nEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD\nVQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu\nZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx\nMTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE\nCAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB\nbmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz\ndGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue\nefhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8\nU7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R\nqTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG\nAQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8\nwDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9\nXvsiu+f+uXc/WT/7\n-----END CERTIFICATE-----\n        </Certificate>\n        <Certificate format=\"pem\">\n-----BEGIN CERTIFICATE-----\nMIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG\nEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll\ndzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD\nVQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw\nHhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx\nEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT\nBgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq\nQW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH\nKoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59\ndx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O\nBBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W\nEOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG\nSM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN\nC/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==\n-----END CERTIFICATE-----\n        </Certificate>\n      </CertificateChain>\n    </Key>\n  </Keybox>\n</AndroidAttestation>\n"
  },
  {
    "path": "trusty/keymaster/set_attestation_key/set_attestation_key.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <getopt.h>\n#include <libxml/xmlreader.h>\n#include <openssl/pem.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/uio.h>\n#include <unistd.h>\n#include <string>\n\nusing std::string;\n\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nstatic const char* _sopts = \"h\";\nstatic const struct option _lopts[] = {\n        {\"help\", no_argument, 0, 'h'},\n        {0, 0, 0, 0},\n};\n\nstatic const char* usage =\n        \"Usage: %s [options] xml-file\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h, --help            prints this message and exit\\n\"\n        \"\\n\";\n\nstatic void print_usage_and_exit(const char* prog, int code) {\n    fprintf(stderr, usage, prog);\n    exit(code);\n}\n\nstatic void parse_options(int argc, char** argv) {\n    int c;\n    int oidx = 0;\n\n    while (1) {\n        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);\n        if (c == -1) {\n            break; /* done */\n        }\n\n        switch (c) {\n            case 'h':\n                print_usage_and_exit(argv[0], EXIT_SUCCESS);\n                break;\n\n            default:\n                print_usage_and_exit(argv[0], EXIT_FAILURE);\n        }\n    }\n}\n\nstruct SetAttestationKeyRequest : public keymaster::KeymasterMessage {\n    explicit SetAttestationKeyRequest(int32_t ver = keymaster::kDefaultMessageVersion)\n        : KeymasterMessage(ver) {}\n\n    size_t SerializedSize() const override { return sizeof(uint32_t) + key_data.SerializedSize(); }\n    uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {\n        buf = keymaster::append_uint32_to_buf(buf, end, algorithm);\n        return key_data.Serialize(buf, end);\n    }\n    bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {\n        return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm) &&\n               key_data.Deserialize(buf_ptr, end);\n    }\n\n    keymaster_algorithm_t algorithm;\n    keymaster::Buffer key_data;\n};\n\nstruct KeymasterNoResponse : public keymaster::KeymasterResponse {\n    explicit KeymasterNoResponse(int32_t ver = keymaster::kDefaultMessageVersion)\n        : keymaster::KeymasterResponse(ver) {}\n\n    size_t NonErrorSerializedSize() const override { return 0; }\n    uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; }\n    bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; }\n};\n\nstruct SetAttestationKeyResponse : public KeymasterNoResponse {};\n\nstruct ClearAttestationCertChainRequest : public keymaster::KeymasterMessage {\n    explicit ClearAttestationCertChainRequest(int32_t ver = keymaster::kDefaultMessageVersion)\n        : KeymasterMessage(ver) {}\n\n    size_t SerializedSize() const override { return sizeof(uint32_t); }\n    uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {\n        return keymaster::append_uint32_to_buf(buf, end, algorithm);\n    }\n    bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {\n        return keymaster::copy_uint32_from_buf(buf_ptr, end, &algorithm);\n    }\n\n    keymaster_algorithm_t algorithm;\n};\n\nstruct ClearAttestationCertChainResponse : public KeymasterNoResponse {};\n\nstatic int set_attestation_key_or_cert_bin(uint32_t cmd, keymaster_algorithm_t algorithm,\n                                           const void* key_data, size_t key_data_size) {\n    int ret;\n\n    SetAttestationKeyRequest req;\n    req.algorithm = algorithm;\n    req.key_data.Reinitialize(key_data, key_data_size);\n    SetAttestationKeyResponse rsp;\n\n    ret = trusty_keymaster_send(cmd, req, &rsp);\n    if (ret) {\n        fprintf(stderr, \"trusty_keymaster_send cmd 0x%x failed %d\\n\", cmd, ret);\n        return ret;\n    }\n\n    return 0;\n}\n\nstatic int set_attestation_key_or_cert_pem(uint32_t cmd, keymaster_algorithm_t algorithm,\n                                           const xmlChar* pemkey) {\n    int ret;\n    int sslret;\n\n    /* Convert from pem to binary */\n    BIO* bio = BIO_new_mem_buf(pemkey, xmlStrlen(pemkey));\n    if (!bio) {\n        fprintf(stderr, \"BIO_new_mem_buf failed\\n\");\n        ERR_print_errors_fp(stderr);\n        return -1;\n    }\n\n    char* key_name;\n    char* key_header;\n    uint8_t* key;\n    long keylen;\n    sslret = PEM_read_bio(bio, &key_name, &key_header, &key, &keylen);\n    BIO_free(bio);\n\n    if (!sslret) {\n        fprintf(stderr, \"PEM_read_bio failed\\n\");\n        ERR_print_errors_fp(stderr);\n        return -1;\n    }\n\n    /* Send key in binary format to trusty */\n    ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);\n\n    OPENSSL_free(key_name);\n    OPENSSL_free(key_header);\n    OPENSSL_free(key);\n\n    return ret;\n}\n\nstatic int set_attestation_key_or_cert_iecs(uint32_t cmd, keymaster_algorithm_t algorithm,\n                                            const xmlChar* key_base64) {\n    int ret;\n    int sslret;\n\n    /* Remove all whitespace. EVP_DecodeBase64 does not support whitespace. */\n    string key_base64_str((const char*)key_base64);\n    key_base64_str.erase(remove_if(key_base64_str.begin(), key_base64_str.end(), isspace),\n                         key_base64_str.end());\n\n    /* Convert from base64 to binary */\n    uint8_t* key;\n    size_t keylen;\n    size_t key_base64_len = key_base64_str.length();\n\n    sslret = EVP_DecodedLength(&keylen, key_base64_len);\n    if (!sslret) {\n        fprintf(stderr, \"invalid input length, %zu\\n\", key_base64_len);\n        return -1;\n    }\n    key = (uint8_t*)malloc(keylen);\n    if (!key) {\n        fprintf(stderr, \"failed to allocate key, size %zu\\n\", key_base64_len);\n        return -1;\n    }\n    sslret = EVP_DecodeBase64(key, &keylen, keylen, (const uint8_t*)key_base64_str.data(),\n                              key_base64_len);\n    if (!sslret) {\n        fprintf(stderr, \"EVP_DecodeBase64 failed\\n\");\n        ERR_print_errors_fp(stderr);\n        free(key);\n        return -1;\n    }\n\n    /* Send key in binary format to trusty */\n    ret = set_attestation_key_or_cert_bin(cmd, algorithm, key, keylen);\n\n    free(key);\n\n    return ret;\n}\n\nstatic int str_to_algorithm(keymaster_algorithm_t* algorithm, const xmlChar* algorithm_str) {\n    if (xmlStrEqual(algorithm_str, BAD_CAST \"rsa\")) {\n        *algorithm = KM_ALGORITHM_RSA;\n    } else if (xmlStrEqual(algorithm_str, BAD_CAST \"ecdsa\")) {\n        *algorithm = KM_ALGORITHM_EC;\n    } else {\n        printf(\"unsupported algorithm: %s\\n\", algorithm_str);\n        return -1;\n    }\n    return 0;\n}\n\nstatic int set_attestation_key_or_cert(uint32_t cmd, const xmlChar* algorithm_str,\n                                       const xmlChar* format, const xmlChar* str) {\n    int ret;\n    keymaster_algorithm_t algorithm;\n\n    ret = str_to_algorithm(&algorithm, algorithm_str);\n    if (ret) {\n        return ret;\n    }\n\n    if (xmlStrEqual(format, BAD_CAST \"pem\")) {\n        ret = set_attestation_key_or_cert_pem(cmd, algorithm, str);\n    } else if (xmlStrEqual(format, BAD_CAST \"iecs\")) {\n        ret = set_attestation_key_or_cert_iecs(cmd, algorithm, str);\n    } else {\n        printf(\"unsupported key/cert format: %s\\n\", format);\n        return -1;\n    }\n    return ret;\n}\n\nstatic int clear_cert_chain(const xmlChar* algorithm_str) {\n    int ret;\n    ClearAttestationCertChainRequest req;\n    ClearAttestationCertChainResponse rsp;\n\n    ret = str_to_algorithm(&req.algorithm, algorithm_str);\n    if (ret) {\n        return ret;\n    }\n\n    ret = trusty_keymaster_send(KM_CLEAR_ATTESTATION_CERT_CHAIN, req, &rsp);\n    if (ret) {\n        fprintf(stderr, \"%s: trusty_keymaster_send failed %d\\n\", __func__, ret);\n        return ret;\n    }\n    return 0;\n}\n\nstatic int process_xml(xmlTextReaderPtr xml) {\n    int ret;\n    const xmlChar* algorithm = NULL;\n    const xmlChar* element = NULL;\n    const xmlChar* element_format = NULL;\n\n    while ((ret = xmlTextReaderRead(xml)) == 1) {\n        int nodetype = xmlTextReaderNodeType(xml);\n        const xmlChar *name, *value;\n        name = xmlTextReaderConstName(xml);\n        switch (nodetype) {\n            case XML_READER_TYPE_ELEMENT:\n                element = name;\n                element_format = xmlTextReaderGetAttribute(xml, BAD_CAST \"format\");\n                if (xmlStrEqual(name, BAD_CAST \"Key\")) {\n                    algorithm = xmlTextReaderGetAttribute(xml, BAD_CAST \"algorithm\");\n                } else if (xmlStrEqual(name, BAD_CAST \"CertificateChain\")) {\n                    ret = clear_cert_chain(algorithm);\n                    if (ret) {\n                        fprintf(stderr, \"%s, algorithm %s: Clear cert chain cmd failed, %d\\n\",\n                                element, algorithm, ret);\n                        return ret;\n                    }\n                    printf(\"%s, algorithm %s: Clear cert chain cmd done\\n\", element, algorithm);\n                }\n                break;\n            case XML_READER_TYPE_TEXT:\n                value = xmlTextReaderConstValue(xml);\n                uint32_t cmd;\n                if (xmlStrEqual(element, BAD_CAST \"PrivateKey\")) {\n                    if (xmlStrEqual(element_format, BAD_CAST \"pem\")) {\n                        cmd = KM_SET_ATTESTATION_KEY;\n                    } else if (xmlStrEqual(element_format, BAD_CAST \"iecs\")) {\n                        cmd = KM_SET_WRAPPED_ATTESTATION_KEY;\n                    } else {\n                        printf(\"unsupported key format: %s\\n\", element_format);\n                        return -1;\n                    }\n                } else if (xmlStrEqual(element, BAD_CAST \"Certificate\")) {\n                    cmd = KM_APPEND_ATTESTATION_CERT_CHAIN;\n                } else {\n                    break;\n                }\n\n                ret = set_attestation_key_or_cert(cmd, algorithm, element_format, value);\n                if (ret) {\n                    fprintf(stderr, \"%s, algorithm %s, format %s: Cmd 0x%x failed, %d\\n\", element,\n                            algorithm, element_format, cmd, ret);\n                    return ret;\n                }\n                printf(\"%s, algorithm %s, format %s: Cmd 0x%x done\\n\", element, algorithm,\n                       element_format, cmd);\n                break;\n            case XML_READER_TYPE_END_ELEMENT:\n                element = NULL;\n                break;\n        }\n    }\n    return ret;\n}\n\nstatic int parse_xml_file(const char* filename) {\n    int ret;\n    xmlTextReaderPtr xml = xmlReaderForFile(filename, NULL, 0);\n    if (!xml) {\n        fprintf(stderr, \"failed to open %s\\n\", filename);\n        return -1;\n    }\n\n    ret = process_xml(xml);\n\n    xmlFreeTextReader(xml);\n    if (ret != 0) {\n        fprintf(stderr, \"Failed to parse or process %s\\n\", filename);\n        return -1;\n    }\n\n    return 0;\n}\n\nstatic int provision_ids(void) {\n    keymaster::SetAttestationIdsRequest req(4 /* ver */);\n    keymaster::EmptyKeymasterResponse rsp(4 /* ver */);\n\n    req.brand.Reinitialize(\"trusty\", 6);\n    req.device.Reinitialize(\"trusty\", 6);\n    req.product.Reinitialize(\"trusty\", 6);\n    req.manufacturer.Reinitialize(\"trusty\", 6);\n    req.model.Reinitialize(\"trusty\", 6);\n\n    return trusty_keymaster_send(KM_SET_ATTESTATION_IDS, req, &rsp);\n}\n\nint main(int argc, char** argv) {\n    int ret = 0;\n\n    parse_options(argc, argv);\n    if (optind + 1 != argc) {\n        print_usage_and_exit(argv[0], EXIT_FAILURE);\n    }\n\n    ret = trusty_keymaster_connect();\n    if (ret) {\n        fprintf(stderr, \"trusty_keymaster_connect failed %d\\n\", ret);\n        return EXIT_FAILURE;\n    }\n\n    ret = parse_xml_file(argv[optind]);\n    if (ret) {\n        fprintf(stderr, \"parse_xml_file failed %d\\n\", ret);\n        trusty_keymaster_disconnect();\n        return EXIT_FAILURE;\n    }\n\n    ret = provision_ids();\n    if (ret) {\n        fprintf(stderr, \"provision_ids failed %d\\n\", ret);\n        trusty_keymaster_disconnect();\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "trusty/keymaster/set_uds_certs/rkp_uds_cert_test.xml",
    "content": "<?xml version=\"1.0\"?>\n<PixelUdsCertificates>\n  <CertificateChain>\n    <NumberOfCertificates>3</NumberOfCertificates>\n    <Certificate format=\"pem\">\n-----BEGIN CERTIFICATE-----\nMIIBWTCB3qADAgECAgUA3q2+7zAMBggqhkjOPQQDAwUAMBIxEDAOBgNVBAMMB0dT\nTUkxLjAwIhgPMjAyNDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4G\nA1UEAwwHR1NNSTEuMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABFsNPdsPmx2NKNiT\n7oReF36HTXjftRfGffYgPf/vEhrT7XLJ7dThkcb+OFYWYlOckdk3DRgqTNWQXsot\nUsZhwRveU8huEjOBO5+dwDiWPs+s9jSRAn5inlTqJ0YGAmdHRzAMBggqhkjOPQQD\nAwUAA2gAMGUCMQCuiJwmRWOgfWbqdSlnXfhCbphjdWc6sHelLkkM21vxQ3RZkhC2\nERh90RA1rxB+XTgCMHZrYG3leS0PEoz5hUviv27LbBHBU6GeOzrS2e0VH0BMSNXN\niP9Wnit+mJw58niEGw==\n-----END CERTIFICATE-----\n    </Certificate>\n    <Certificate format=\"pem\">\n-----BEGIN CERTIFICATE-----\nMIIBWTCB3qADAgECAgUA3q2+7zAMBggqhkjOPQQDAwUAMBIxEDAOBgNVBAMMB0dT\nTUkxLjAwIhgPMjAyNDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4G\nA1UEAwwHR1NNSTEuMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABFsNPdsPmx2NKNiT\n7oReF36HTXjftRfGffYgPf/vEhrT7XLJ7dThkcb+OFYWYlOckdk3DRgqTNWQXsot\nUsZhwRveU8huEjOBO5+dwDiWPs+s9jSRAn5inlTqJ0YGAmdHRzAMBggqhkjOPQQD\nAwUAA2gAMGUCMQCuiJwmRWOgfWbqdSlnXfhCbphjdWc6sHelLkkM21vxQ3RZkhC2\nERh90RA1rxB+XTgCMHZrYG3leS0PEoz5hUviv27LbBHBU6GeOzrS2e0VH0BMSNXN\niP9Wnit+mJw58niEGw==\n-----END CERTIFICATE-----\n    </Certificate>\n    <Certificate format=\"pem\">\n-----BEGIN CERTIFICATE-----\nMIIBWTCB3qADAgECAgUA3q2+7zAMBggqhkjOPQQDAwUAMBIxEDAOBgNVBAMMB0dT\nTUkxLjAwIhgPMjAyNDAxMDEwMDAwMDBaGA85OTk5MTIzMTIzNTk1OVowEjEQMA4G\nA1UEAwwHR1NNSTEuMDB2MBAGByqGSM49AgEGBSuBBAAiA2IABFsNPdsPmx2NKNiT\n7oReF36HTXjftRfGffYgPf/vEhrT7XLJ7dThkcb+OFYWYlOckdk3DRgqTNWQXsot\nUsZhwRveU8huEjOBO5+dwDiWPs+s9jSRAn5inlTqJ0YGAmdHRzAMBggqhkjOPQQD\nAwUAA2gAMGUCMQCuiJwmRWOgfWbqdSlnXfhCbphjdWc6sHelLkkM21vxQ3RZkhC2\nERh90RA1rxB+XTgCMHZrYG3leS0PEoz5hUviv27LbBHBU6GeOzrS2e0VH0BMSNXN\niP9Wnit+mJw58niEGw==\n-----END CERTIFICATE-----\n    </Certificate>\n  </CertificateChain>\n</PixelUdsCertificates>\n"
  },
  {
    "path": "trusty/keymaster/set_uds_certs/set_uds_certificates.cpp",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <getopt.h>\n#include <libxml/xmlreader.h>\n#include <openssl/pem.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/uio.h>\n#include <unistd.h>\n#include <string>\n\n#include <trusty_keymaster/ipc/trusty_keymaster_ipc.h>\n\nstatic const char* _sopts = \"h\";\nstatic const struct option _lopts[] = {\n        {\"help\", no_argument, 0, 'h'},\n        {0, 0, 0, 0},\n};\n\nstatic const char* usage =\n        \"Usage: %s [options] xml-file\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h, --help            prints this message and exit\\n\"\n        \"\\n\";\n\nstatic void print_usage_and_exit(const char* prog, int code) {\n    fprintf(stderr, usage, prog);\n    exit(code);\n}\n\nstatic void parse_options(int argc, char** argv) {\n    int c;\n    int oidx = 0;\n\n    while (1) {\n        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);\n        if (c == -1) {\n            break; /* done */\n        }\n\n        switch (c) {\n            case 'h':\n                print_usage_and_exit(argv[0], EXIT_SUCCESS);\n                break;\n\n            default:\n                print_usage_and_exit(argv[0], EXIT_FAILURE);\n        }\n    }\n}\n\nstruct AppendUdsCertificateRequest : public keymaster::KeymasterMessage {\n    explicit AppendUdsCertificateRequest(int32_t ver = keymaster::kDefaultMessageVersion)\n        : KeymasterMessage(ver) {}\n\n    size_t SerializedSize() const override { return cert_data.SerializedSize(); }\n    uint8_t* Serialize(uint8_t* buf, const uint8_t* end) const override {\n        return cert_data.Serialize(buf, end);\n    }\n    bool Deserialize(const uint8_t** buf_ptr, const uint8_t* end) override {\n        return cert_data.Deserialize(buf_ptr, end);\n    }\n\n    keymaster::Buffer cert_data;\n};\n\nstruct ClearUdsCertificateRequest : public keymaster::KeymasterMessage {\n    explicit ClearUdsCertificateRequest(int32_t ver = keymaster::kDefaultMessageVersion)\n        : KeymasterMessage(ver) {}\n\n    size_t SerializedSize() const override { return 0; }\n    uint8_t* Serialize(uint8_t* buf, const uint8_t*) const override { return buf; }\n    bool Deserialize(const uint8_t**, const uint8_t*) override { return true; };\n};\n\nstruct KeymasterNoResponse : public keymaster::KeymasterResponse{\n    explicit KeymasterNoResponse(int32_t ver = keymaster::kDefaultMessageVersion)\n        : keymaster::KeymasterResponse(ver) {}\n\n    size_t NonErrorSerializedSize() const override { return 0; }\n    uint8_t* NonErrorSerialize(uint8_t* buf, const uint8_t*) const override { return buf; }\n    bool NonErrorDeserialize(const uint8_t**, const uint8_t*) override { return true; }\n};\n\nstruct AppendUdsCertificateResponse : public KeymasterNoResponse {};\nstruct ClearUdsCertificateResponse : public KeymasterNoResponse {};\n\nstatic int set_uds_cert_bin(uint32_t cmd, const void* cert_data, size_t cert_data_size) {\n    int ret;\n\n    AppendUdsCertificateRequest req;\n    req.cert_data.Reinitialize(cert_data, cert_data_size);\n    AppendUdsCertificateResponse rsp;\n\n    ret = trusty_keymaster_send(cmd, req, &rsp);\n    if (ret) {\n        fprintf(stderr, \"trusty_keymaster_send cmd 0x%x failed %d\\n\", cmd, ret);\n        return ret;\n    }\n\n    return 0;\n}\n\nstatic int set_uds_cert_pem(uint32_t cmd, const xmlChar* pemkey) {\n    int ret;\n    int sslret;\n\n    /* Convert from pem to binary */\n    BIO* bio = BIO_new_mem_buf(pemkey, xmlStrlen(pemkey));\n    if (!bio) {\n        fprintf(stderr, \"BIO_new_mem_buf failed\\n\");\n        ERR_print_errors_fp(stderr);\n        return -1;\n    }\n\n    char* key_name;\n    char* key_header;\n    uint8_t* key;\n    long keylen;\n    sslret = PEM_read_bio(bio, &key_name, &key_header, &key, &keylen);\n    BIO_free(bio);\n\n    if (!sslret) {\n        fprintf(stderr, \"PEM_read_bio failed\\n\");\n        ERR_print_errors_fp(stderr);\n        return -1;\n    }\n\n    /* Send key in binary format to trusty */\n    ret = set_uds_cert_bin(cmd, key, keylen);\n\n    OPENSSL_free(key_name);\n    OPENSSL_free(key_header);\n    OPENSSL_free(key);\n\n    return ret;\n}\n\nstatic int set_uds_cert(uint32_t cmd, const xmlChar* format, const xmlChar* str) {\n    int ret;\n\n    if (xmlStrEqual(format, BAD_CAST \"pem\")) {\n        ret = set_uds_cert_pem(cmd, str);\n    } else {\n        printf(\"unsupported key/cert format: %s\\n\", format);\n        return -1;\n    }\n    return ret;\n}\n\n// TODO: Guard by Production Mode\nstatic int clear_cert_chain() {\n    int ret;\n    ClearUdsCertificateRequest req;\n    ClearUdsCertificateResponse rsp;\n\n    ret = trusty_keymaster_send(KM_CLEAR_UDS_CERT_CHAIN, req, &rsp);\n    if (ret) {\n        fprintf(stderr, \"%s: trusty_keymaster_send failed %d\\n\", __func__, ret);\n        return ret;\n    }\n    return 0;\n}\n\nstatic int process_xml(xmlTextReaderPtr xml) {\n    int ret;\n    const xmlChar* element = NULL;\n    const xmlChar* element_format = NULL;\n    bool isPixelUdsCert = false;\n\n    while ((ret = xmlTextReaderRead(xml)) == 1) {\n        int nodetype = xmlTextReaderNodeType(xml);\n        const xmlChar *name, *value;\n        name = xmlTextReaderConstName(xml);\n        switch (nodetype) {\n            case XML_READER_TYPE_ELEMENT:\n                element = name;\n                element_format = xmlTextReaderGetAttribute(xml, BAD_CAST \"format\");\n                if (isPixelUdsCert || xmlStrEqual(name, BAD_CAST \"PixelUdsCertificates\")) {\n                    // The first element name must be \"PixelUdsCertificates\"\n                    isPixelUdsCert = true;\n                } else {\n                    fprintf(stderr, \"Not a PixelUdsCertificates: \\\"%s\\\"\\n\", name);\n                    return -1;\n                }\n                if (xmlStrEqual(name, BAD_CAST \"CertificateChain\")) {\n                    ret = clear_cert_chain();\n                    if (ret) {\n                        fprintf(stderr, \"%s: Clear cert chain cmd failed, %d\\n\", element, ret);\n                        return ret;\n                    }\n                    printf(\"%s: Clear cert chain cmd done\\n\", element);\n                }\n                break;\n            case XML_READER_TYPE_TEXT:\n                value = xmlTextReaderConstValue(xml);\n                uint32_t cmd;\n                if (xmlStrEqual(element, BAD_CAST \"Certificate\")) {\n                    cmd = KM_APPEND_UDS_CERT_CHAIN;\n                } else {\n                    break;\n                }\n\n                ret = set_uds_cert(cmd, element_format, value);\n                if (ret) {\n                    fprintf(stderr, \"%s, format %s: Cmd 0x%x failed, %d\\n\", element, element_format,\n                            cmd, ret);\n                    return ret;\n                }\n                printf(\"%s, format %s: Cmd 0x%x done\\n\", element, element_format, cmd);\n                break;\n            case XML_READER_TYPE_END_ELEMENT:\n                element = NULL;\n                break;\n        }\n    }\n    return ret;\n}\n\nstatic int parse_and_provision_xml_file(const char* filename) {\n    int ret;\n    xmlTextReaderPtr xml = xmlReaderForFile(filename, NULL, 0);\n    if (!xml) {\n        fprintf(stderr, \"failed to open %s\\n\", filename);\n        return -1;\n    }\n\n    ret = process_xml(xml);\n\n    xmlFreeTextReader(xml);\n    if (ret != 0) {\n        fprintf(stderr, \"Failed to parse or process %s\\n\", filename);\n        return -1;\n    }\n\n    return 0;\n}\n\nint main(int argc, char** argv) {\n    int ret = 0;\n\n    parse_options(argc, argv);\n    if (optind + 1 != argc) {\n        print_usage_and_exit(argv[0], EXIT_FAILURE);\n    }\n\n    ret = trusty_keymaster_connect();\n    if (ret) {\n        fprintf(stderr, \"trusty_keymaster_connect failed %d\\n\", ret);\n        return EXIT_FAILURE;\n    }\n\n    ret = parse_and_provision_xml_file(argv[optind]);\n    if (ret) {\n        fprintf(stderr, \"parse_and_provision_xml_file failed %d\\n\", ret);\n        trusty_keymaster_disconnect();\n        return EXIT_FAILURE;\n    }\n\n    return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "trusty/keymint/Android.bp",
    "content": "//\n// Copyright (C) 2022 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_defaults {\n    name: \"android.hardware.security.keymint-service.rust.trusty.default\",\n    relative_install_path: \"hw\",\n    srcs: [\n        \"src/keymint_hal_main.rs\",\n    ],\n    rustlibs: [\n        \"libandroid_logger\",\n        \"libbinder_rs\",\n        \"libclap\",\n        \"libkmr_wire\",\n        \"libkmr_hal\",\n        \"libtrusty-rs\",\n        \"liblibc\",\n        \"liblog_rust\",\n    ],\n    prefer_rlib: true,\n}\n\n// keymint hal binary for keymint in Trusty TEE (legacy approach not using apex)\nrust_binary {\n    name: \"android.hardware.security.keymint-service.rust.trusty\",\n    vendor: true,\n    defaults: [\"android.hardware.security.keymint-service.rust.trusty.default\"],\n    init_rc: [\"android.hardware.security.keymint-service.rust.trusty.rc\"],\n    vintf_fragments: [\"android.hardware.security.keymint-service.rust.trusty.xml\"],\n    required: [\"android.hardware.hardware_keystore.xml\"],\n}\n\n// Keymint hal service in vendor, enabled by vendor apex.\n// This service is disabled by default and does not package a VINTF fragment.\n// This service can be enabled at boot via vendor apex:\n// - at boot, mount a vendor apex for module `com.android.hardware.keymint`\n// - have the vendor apex init.rc file to start the service when the apex is selected\n// - have the vendor apex package the vintf fragment\nrust_binary {\n    name: \"android.hardware.security.keymint-service.trusty_tee\",\n    vendor: true,\n    defaults: [\"android.hardware.security.keymint-service.rust.trusty.default\"],\n    init_rc: [\"android.hardware.security.keymint-service.trusty_tee.rc\"],\n    features: select(soong_config_variable(\"trusty_system_vm\", \"placeholder_trusted_hal\"), {\n        true: [\"nonsecure\"],\n        default: [],\n    }),\n    rustlibs: [\n        \"libkmr_hal_nonsecure\",\n    ],\n}\n\n// Keymint hal service in system_ext, interacting with the Trusty Security VM.\n// This service is disabled by default and does not package a VINTF fragment.\n// This service can be enabled at boot via vendor apex:\n// - at boot, mount a vendor apex for module `com.android.hardware.keymint`\n// - have the vendor apex init.rc file to start the service when the apex is selected\n// - have the vendor apex package the vintf fragment\nrust_binary {\n    name: \"android.hardware.security.keymint-service.trusty_system_vm\",\n    system_ext_specific: true,\n    defaults: [\"android.hardware.security.keymint-service.rust.trusty.default\"],\n    init_rc: [\"android.hardware.security.keymint-service.trusty_system_vm.rc\"],\n    features: select(soong_config_variable(\"trusty_system_vm\", \"placeholder_trusted_hal\"), {\n        true: [\"nonsecure\"],\n        default: [],\n    }),\n    rustlibs: [\n        \"libkmr_hal_nonsecure\",\n    ],\n}\n\n// vintf fragment packaged in vendor apex\nprebuilt_etc {\n    name: \"android.hardware.security.keymint-service.rust.trusty.xml\",\n    sub_dir: \"vintf\",\n    vendor: true,\n    src: \"android.hardware.security.keymint-service.rust.trusty.xml\",\n}\n\nprebuilt_etc {\n    name: \"android.hardware.security.keymint-service.trusty_system_vm.xml\",\n    sub_dir: \"vintf\",\n    vendor: true,\n    src: \"android.hardware.security.keymint-service.trusty_system_vm.xml\",\n}\n"
  },
  {
    "path": "trusty/keymint/android.hardware.security.keymint-service.rust.trusty.rc",
    "content": "service vendor.keymint.rust-trusty /vendor/bin/hw/android.hardware.security.keymint-service.rust.trusty \\\n                                          --dev ${ro.hardware.trusty_ipc_dev.keymint:-/dev/trusty-ipc-dev0}\n    class early_hal\n    user nobody\n    group drmrpc\n    # The keymint service is not allowed to restart.\n    # If it crashes, a device restart is required.\n    oneshot\n"
  },
  {
    "path": "trusty/keymint/android.hardware.security.keymint-service.rust.trusty.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"aidl\">\n        <name>android.hardware.security.keymint</name>\n        <version>4</version>\n        <fqname>IKeyMintDevice/default</fqname>\n    </hal>\n    <hal format=\"aidl\">\n        <name>android.hardware.security.secureclock</name>\n        <fqname>ISecureClock/default</fqname>\n    </hal>\n    <hal format=\"aidl\">\n        <name>android.hardware.security.sharedsecret</name>\n        <fqname>ISharedSecret/default</fqname>\n    </hal>\n    <hal format=\"aidl\">\n        <name>android.hardware.security.keymint</name>\n        <version>3</version>\n        <fqname>IRemotelyProvisionedComponent/default</fqname>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "trusty/keymint/android.hardware.security.keymint-service.trusty_system_vm.rc",
    "content": "# service started when selecting `com.android.hardware.keymint.trusty_system_vm` vendor apex\nservice system.keymint-service.trusty_system_vm \\\n  /system_ext/bin/hw/android.hardware.security.keymint-service.trusty_system_vm \\\n  --dev ${system.keymint.trusty_ipc_dev}\n    disabled\n    user nobody\n    group drmrpc\n    # The keymint service is not allowed to restart.\n    # If it crashes, a device restart is required.\n    oneshot\n\n# TODO(b/357821690): Start the KeyMint HALs when the KeyMint VM is ready once the Trusty VM\n# has a mechanism to notify the host.\non post-fs && property:trusty.security_vm.keymint.enabled=1 && \\\n   property:trusty.security_vm.vm_cid=*\n    setprop system.keymint.trusty_ipc_dev VSOCK:${trusty.security_vm.vm_cid}:1\n    start system.keymint-service.trusty_system_vm\n"
  },
  {
    "path": "trusty/keymint/android.hardware.security.keymint-service.trusty_system_vm.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"aidl\" updatable-via-system=\"true\">\n        <name>android.hardware.security.keymint</name>\n        <version>4</version>\n        <fqname>IKeyMintDevice/default</fqname>\n    </hal>\n    <hal format=\"aidl\" updatable-via-system=\"true\">\n        <name>android.hardware.security.secureclock</name>\n        <fqname>ISecureClock/default</fqname>\n    </hal>\n    <hal format=\"aidl\" updatable-via-system=\"true\">\n        <name>android.hardware.security.sharedsecret</name>\n        <fqname>ISharedSecret/default</fqname>\n    </hal>\n    <hal format=\"aidl\" updatable-via-system=\"true\">\n        <name>android.hardware.security.keymint</name>\n        <version>3</version>\n        <fqname>IRemotelyProvisionedComponent/default</fqname>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "trusty/keymint/android.hardware.security.keymint-service.trusty_tee.rc",
    "content": "# service started when selecting `com.android.hardware.keymint.trusty_tee` vendor apex\nservice vendor.keymint-service.trusty_tee \\\n  /vendor/bin/hw/android.hardware.security.keymint-service.trusty_tee \\\n    --dev ${ro.hardware.trusty_ipc_dev.keymint:-/dev/trusty-ipc-dev0}\n    disabled\n    class early_hal\n    user nobody\n    group drmrpc\n    # The keymint service is not allowed to restart.\n    # If it crashes, a device restart is required.\n    oneshot\n"
  },
  {
    "path": "trusty/keymint/fuzz/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_fuzz {\n    name: \"trusty_keymint_fuzzer\",\n    defaults: [\"trusty_fuzzer_defaults\"],\n    srcs: [\":trusty_tipc_fuzzer\"],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.keymint\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"5f902ace-5e5c-4cd8-ae54-87b88c22ddaf\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"keymint_app.syms.elf\\\"\",\n    ],\n    fuzz_config: {\n       cc: [\"drysdale@google.com\"],\n       componentid: 1084733,\n       hotlists: [\"4271696\"],\n    },\n\n    // The initial corpus for this fuzzer was derived by dumping messages from\n    // the HAL service while running the VTS tests for KeyMint.\n    corpus: [\"corpus/*\"],\n}\n"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-821180-0",
    "content": "\u0011"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82128140-0",
    "content": "\u0012@"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82128143-0",
    "content": "\u0012Cfoo"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82128158-0",
    "content": "\u0012X@\u0013TdÂ%r\u000f\u00191:\u001dz7\u0003X$\u0013v`4rcJ!Rq3?ZD"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82128158-1",
    "content": "\u0012X@vE\u0010)Y0jIꄻ: <q*㺧\n#}4C\u0012T$d\u0003\"Y\u0013O\u001f\u0015S;\teP\u001e"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82168258-0",
    "content": "\u0016X4just some garbage data which is not a valid key blob:oHclientid:oCGappdata"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183184-0",
    "content": "\u00181\u001b?\u0014TDFfoobar"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183184-1",
    "content": "\u00181;Oi^'Ffoobar"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183184-2",
    "content": "\u00181\u001bY͑. Ffoobar"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183184-3",
    "content": "\u00181\u001b\u001abxAN`Ffoobar"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183284-0",
    "content": "\u00182;*(Y\u0006aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183284-2",
    "content": "\u00182\u001b_K\u001d5]rY\u0006aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183284-3",
    "content": "\u00182;\nE=Y\u0006aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183386-1",
    "content": "\u00183\u001b\u001a6.[\u0012LHello World!@"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183481-0",
    "content": "\u00184;z\bʖ"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183481-1",
    "content": "\u00184\u001b\u0015߆\u00106"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183481-2",
    "content": "\u00184\u001b2\rK"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82183481-3",
    "content": "\u00184\u001b 訢\u000f%"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82184180-0",
    "content": "\u0018A"
  },
  {
    "path": "trusty/keymint/fuzz/corpus/keymint-reqs-82184281-0",
    "content": "\u0018B"
  },
  {
    "path": "trusty/keymint/src/keymint_hal_main.rs",
    "content": "//\n// Copyright (C) 2022 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! This module implements the HAL service for Keymint (Rust) in Trusty.\nuse clap::Parser;\nuse kmr_hal::{\n    extract_rsp, keymint, rpc, secureclock, send_hal_info, sharedsecret, SerializedChannel,\n};\nuse log::{error, info, warn};\nuse std::{\n    ffi::CString,\n    ops::DerefMut,\n    panic,\n    sync::{Arc, Mutex},\n};\nuse trusty::DEFAULT_DEVICE;\n\nconst TRUSTY_KEYMINT_RUST_SERVICE_NAME: &str = \"com.android.trusty.keymint\";\n\nstatic SERVICE_INSTANCE: &str = \"default\";\n\nstatic KM_SERVICE_NAME: &str = \"android.hardware.security.keymint.IKeyMintDevice\";\nstatic RPC_SERVICE_NAME: &str = \"android.hardware.security.keymint.IRemotelyProvisionedComponent\";\nstatic SECURE_CLOCK_SERVICE_NAME: &str = \"android.hardware.security.secureclock.ISecureClock\";\nstatic SHARED_SECRET_SERVICE_NAME: &str = \"android.hardware.security.sharedsecret.ISharedSecret\";\n\n/// Local error type for failures in the HAL service.\n#[derive(Debug, Clone)]\nstruct HalServiceError(String);\n\n#[derive(Debug)]\nstruct TipcChannel(trusty::TipcChannel);\n\nimpl SerializedChannel for TipcChannel {\n    const MAX_SIZE: usize = 4000;\n    fn execute(&mut self, serialized_req: &[u8]) -> binder::Result<Vec<u8>> {\n        self.0.send(serialized_req).map_err(|e| {\n            binder::Status::new_exception(\n                binder::ExceptionCode::TRANSACTION_FAILED,\n                Some(\n                    &CString::new(format!(\n                        \"Failed to send the request via tipc channel because of {:?}\",\n                        e\n                    ))\n                    .unwrap(),\n                ),\n            )\n        })?;\n        let mut expect_more_msgs = true;\n        let mut full_rsp = Vec::new();\n        while expect_more_msgs {\n            let mut recv_buf = Vec::new();\n            self.0.recv(&mut recv_buf).map_err(|e| {\n                binder::Status::new_exception(\n                    binder::ExceptionCode::TRANSACTION_FAILED,\n                    Some(\n                        &CString::new(format!(\n                            \"Failed to receive the response via tipc channel because of {:?}\",\n                            e\n                        ))\n                        .unwrap(),\n                    ),\n                )\n            })?;\n            let current_rsp_content;\n            (expect_more_msgs, current_rsp_content) = extract_rsp(&recv_buf)?;\n            full_rsp.extend_from_slice(current_rsp_content);\n        }\n        Ok(full_rsp)\n    }\n}\n\n#[derive(Parser, Debug)]\nstruct Args {\n    /// Tipc device path\n    #[arg(short, long, default_value_t = DEFAULT_DEVICE.to_string())]\n    dev: String,\n}\n\nfn main() {\n    if let Err(HalServiceError(e)) = inner_main() {\n        panic!(\"HAL service failed: {:?}\", e);\n    }\n}\n\nfn inner_main() -> Result<(), HalServiceError> {\n    let args = Args::parse();\n    // Initialize Android logging.\n    android_logger::init_once(\n        android_logger::Config::default()\n            .with_tag(\"keymint-hal-trusty\")\n            .with_max_level(log::LevelFilter::Info)\n            .with_log_buffer(android_logger::LogId::System),\n    );\n    // Redirect panic messages to logcat.\n    panic::set_hook(Box::new(|panic_info| {\n        error!(\"{}\", panic_info);\n    }));\n\n    if cfg!(feature = \"nonsecure\") {\n        warn!(\"Non-secure Trusty KM HAL service is starting.\");\n    } else {\n        info!(\"Trusty KM HAL service is starting.\");\n    }\n\n    info!(\"Starting thread pool now.\");\n    binder::ProcessState::start_thread_pool();\n\n    // Create connection to the TA\n    let connection =\n        trusty::TipcChannel::connect(args.dev.as_str(), TRUSTY_KEYMINT_RUST_SERVICE_NAME).map_err(\n            |e| {\n                HalServiceError(format!(\n                    \"Failed to connect to Trusty Keymint TA at {} because of {:?}.\",\n                    args.dev, e\n                ))\n            },\n        )?;\n    let tipc_channel = Arc::new(Mutex::new(TipcChannel(connection)));\n\n    #[cfg(feature = \"nonsecure\")]\n    {\n        // When the non-secure feature is enabled, retrieve root-of-trust information\n        // (with the exception of the verified boot key hash) from Android properties, and\n        // populate the TA with this information. On a real device, the bootloader should\n        // provide this data to the TA directly.\n        let boot_req = kmr_hal_nonsecure::get_boot_info();\n        info!(\"boot/HAL->TA: boot info is {:?}\", boot_req);\n        kmr_hal::send_boot_info(tipc_channel.lock().unwrap().deref_mut(), boot_req)\n            .map_err(|e| HalServiceError(format!(\"Failed to send boot info: {:?}\", e)))?;\n        // When the non-secure feature is enabled, also retrieve device ID information\n        // (except for IMEI/MEID values) from Android properties and populate the TA with\n        // this information. On a real device, a factory provisioning process would populate\n        // this information.\n        let attest_ids = kmr_hal_nonsecure::attestation_id_info();\n        if let Err(e) =\n            kmr_hal::send_attest_ids(tipc_channel.lock().unwrap().deref_mut(), attest_ids)\n        {\n            error!(\"Failed to send attestation ID info: {:?}\", e);\n        }\n        info!(\"Successfully sent non-secure boot info and attestation IDs to the TA.\");\n    }\n\n    // Register the Keymint service\n    let km_service = keymint::Device::new_as_binder(tipc_channel.clone());\n    let km_service_name = format!(\"{}/{}\", KM_SERVICE_NAME, SERVICE_INSTANCE);\n    binder::add_service(&km_service_name, km_service.as_binder()).map_err(|e| {\n        HalServiceError(format!(\n            \"Failed to register service {} because of {:?}.\",\n            km_service_name, e\n        ))\n    })?;\n\n    // Register the Remotely Provisioned Component service\n    let rpc_service = rpc::Device::new_as_binder(tipc_channel.clone());\n    let rpc_service_name = format!(\"{}/{}\", RPC_SERVICE_NAME, SERVICE_INSTANCE);\n    binder::add_service(&rpc_service_name, rpc_service.as_binder()).map_err(|e| {\n        HalServiceError(format!(\n            \"Failed to register service {} because of {:?}.\",\n            rpc_service_name, e\n        ))\n    })?;\n\n    // Register the Secure Clock service\n    let sclock_service = secureclock::Device::new_as_binder(tipc_channel.clone());\n    let sclock_service_name = format!(\"{}/{}\", SECURE_CLOCK_SERVICE_NAME, SERVICE_INSTANCE);\n    binder::add_service(&sclock_service_name, sclock_service.as_binder()).map_err(|e| {\n        HalServiceError(format!(\n            \"Failed to register service {} because of {:?}.\",\n            sclock_service_name, e\n        ))\n    })?;\n\n    // Register the Shared Secret service\n    let ssecret_service = sharedsecret::Device::new_as_binder(tipc_channel.clone());\n    let ssecret_service_name = format!(\"{}/{}\", SHARED_SECRET_SERVICE_NAME, SERVICE_INSTANCE);\n    binder::add_service(&ssecret_service_name, ssecret_service.as_binder()).map_err(|e| {\n        HalServiceError(format!(\n            \"Failed to register service {} because of {:?}.\",\n            ssecret_service_name, e\n        ))\n    })?;\n\n    // Send the HAL service information to the TA\n    send_hal_info(tipc_channel.lock().unwrap().deref_mut())\n        .map_err(|e| HalServiceError(format!(\"Failed to populate HAL info: {:?}\", e)))?;\n\n    info!(\"Successfully registered KeyMint HAL services.\");\n    info!(\"Joining thread pool now.\");\n    binder::ProcessState::join_thread_pool();\n    info!(\"KeyMint HAL service is terminating.\"); // should not reach here\n    Ok(())\n}\n"
  },
  {
    "path": "trusty/keymint/trusty-keymint-apex.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# This makefile should be included by devices that choose to integrate\n# Keymint HAL via vendor apex\n\nPRODUCT_PACKAGES += \\\n    android.hardware.security.keymint-service.trusty_tee.cpp \\\n    android.hardware.security.keymint-service.trusty_tee \\\n\nifeq ($(findstring enabled, $(TRUSTY_SYSTEM_VM)),enabled)\n    PRODUCT_PACKAGES += \\\n        android.hardware.security.keymint-service.trusty_system_vm \\\n\nendif\n"
  },
  {
    "path": "trusty/keymint/trusty-keymint.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# This makefile should be included by devices that use Trusty TEE\n# to pull in a set of Trusty KeyMint specific modules.\n#\n# Allow KeyMint HAL service implementation selection at build time. This must be\n# synchronized with the TA implementation included in Trusty. Possible values:\n#\n# - Rust implementation for Trusty TEE\n#   export TRUSTY_KEYMINT_IMPL=rust\n# - C++ implementation (default):\n#   any other value or unset TRUSTY_KEYMINT_IMPL\n\nifeq ($(TRUSTY_KEYMINT_IMPL),rust)\n    LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service.rust.trusty\n\nelse\n    # Default to the C++ implementation\n    LOCAL_KEYMINT_PRODUCT_PACKAGE := android.hardware.security.keymint-service.trusty\nendif\n\nPRODUCT_PACKAGES += \\\n    $(LOCAL_KEYMINT_PRODUCT_PACKAGE) \\\n"
  },
  {
    "path": "trusty/libtrusty/Android.bp",
    "content": "// Copyright (C) 2015 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"libtrusty_defaults\",\n    srcs: [\"trusty.c\"],\n    export_include_dirs: [\"include\"],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n\n    shared_libs: [\"liblog\"],\n}\n\ncc_library {\n    name: \"libtrusty\",\n    // TODO(b/170753563): cc_fuzz can't deal with vendor components. Build\n    // libtrusty for system and vendor.\n    vendor_available: true,\n    recovery_available: true,\n    defaults: [\"libtrusty_defaults\"],\n}\n"
  },
  {
    "path": "trusty/libtrusty/include/trusty/ipc.h",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _UAPI_LINUX_TRUSTY_IPC_H_\n#define _UAPI_LINUX_TRUSTY_IPC_H_\n\n#include <linux/ioctl.h>\n#include <linux/types.h>\n#include <linux/uio.h>\n\n/**\n * enum transfer_kind - How to send an fd to Trusty\n * @TRUSTY_SHARE:                Memory will be accessible by Linux and Trusty. On ARM it\n *                               will be mapped as nonsecure. Suitable for shared memory.\n *                               The paired fd must be a \"dma_buf\".\n * @TRUSTY_LEND:                 Memory will be accessible only to Trusty. On ARM it will\n *                               be transitioned to \"Secure\" memory if Trusty is in\n *                               TrustZone. This transfer kind is suitable for donating\n *                               video buffers or other similar resources. The paired fd\n *                               may need to come from a platform-specific allocator for\n *                               memory that may be transitioned to \"Secure\".\n * @TRUSTY_SEND_SECURE:          Send memory that is already \"Secure\". Memory will be\n *                               accessible only to Trusty. The paired fd may need to\n *                               come from a platform-specific allocator that returns\n *                               \"Secure\" buffers.\n * @TRUSTY_SEND_SECURE_OR_SHARE: Acts as TRUSTY_SEND_SECURE if the memory is already\n *                               \"Secure\" and as TRUSTY_SHARE otherwise.\n *\n * Describes how the user would like the resource in question to be sent to\n * Trusty. Options may be valid only for certain kinds of fds.\n */\nenum transfer_kind {\n    TRUSTY_SHARE = 0,\n    TRUSTY_LEND = 1,\n    TRUSTY_SEND_SECURE = 2,\n    TRUSTY_SEND_SECURE_OR_SHARE = 3,\n};\n\n/**\n * struct trusty_shm - Describes a transfer of memory to Trusty\n * @fd:       The fd to transfer\n * @transfer: How to transfer it - see &enum transfer_kind\n */\nstruct trusty_shm {\n    __s32 fd;\n    __u32 transfer;\n};\n\n/**\n * struct tipc_send_msg_req - Request struct for @TIPC_IOC_SEND_MSG\n * @iov:     Pointer to an array of &struct iovec describing data to be sent\n * @shm:     Pointer to an array of &struct trusty_shm describing any file\n *           descriptors to be transferred.\n * @iov_cnt: Number of elements in the @iov array\n * @shm_cnt: Number of elements in the @shm array\n */\nstruct tipc_send_msg_req {\n    __u64 iov;\n    __u64 shm;\n    __u64 iov_cnt;\n    __u64 shm_cnt;\n};\n\n#define TIPC_IOC_MAGIC 'r'\n#define TIPC_IOC_CONNECT _IOW(TIPC_IOC_MAGIC, 0x80, char*)\n#define TIPC_IOC_SEND_MSG _IOW(TIPC_IOC_MAGIC, 0x81, struct tipc_send_msg_req)\n\n#if defined(CONFIG_COMPAT)\n#define TIPC_IOC_CONNECT_COMPAT _IOW(TIPC_IOC_MAGIC, 0x80, compat_uptr_t)\n#endif\n\n#endif\n"
  },
  {
    "path": "trusty/libtrusty/include/trusty/tipc.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef _LIB_TIPC_H\n#define _LIB_TIPC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <sys/uio.h>\n#include <trusty/ipc.h>\n\nint tipc_connect(const char *dev_name, const char *srv_name);\nssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shm, int shmcnt);\nint tipc_close(int fd);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "trusty/libtrusty/tipc-test/Android.bp",
    "content": "// Copyright (C) 2015 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test {\n    name: \"tipc-test\",\n    vendor: true,\n\n    srcs: [\"tipc_test.c\"],\n    shared_libs: [\n        \"libc\",\n        \"libdmabufheap\",\n        \"liblog\",\n        \"libtrusty\",\n    ],\n    gtest: false,\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n"
  },
  {
    "path": "trusty/libtrusty/tipc-test/tipc_test.c",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <stdio.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <string.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <getopt.h>\n#define __USE_GNU\n#include <inttypes.h>\n#include <sys/mman.h>\n#include <sys/uio.h>\n#include <time.h>\n\n#include <BufferAllocator/BufferAllocatorWrapper.h>\n\n#include <trusty/tipc.h>\n\n#define TIPC_DEFAULT_DEVNAME \"/dev/trusty-ipc-dev0\"\n\n/* clang-format off */\n#define BENCH_RESULT_TPL                                    \\\n\"{\"                                                         \\\n\"    \\\"schema_version\\\": 3,\"                                \\\n\"    \\\"suite_name\\\": \\\"tipc\\\",\"                           \\\n\"    \\\"bench_name\\\": \\\"%s\\\",\"                               \\\n\"    \\\"results\\\": [\"                                        \\\n\"        {\"                                                 \\\n\"            \\\"metric_name\\\": \\\"time_micro_sec\\\",\"          \\\n\"            \\\"min\\\": \\\"%\" PRId64 \"\\\",\"                     \\\n\"            \\\"max\\\": \\\"%\" PRId64 \"\\\",\"                     \\\n\"            \\\"avg\\\": \\\"%\" PRId64 \"\\\",\"                     \\\n\"            \\\"cold\\\": \\\"%\" PRId64 \"\\\",\"                    \\\n\"            \\\"raw_min\\\": %\" PRId64 \",\"                     \\\n\"            \\\"raw_max\\\": %\" PRId64 \",\"                     \\\n\"            \\\"raw_avg\\\": %\" PRId64 \",\"                     \\\n\"            \\\"raw_cold\\\": %\" PRId64 \"\"                     \\\n\"        },\"                                                \\\n\"    ]\"                                                     \\\n\"}\"\n/* clang-format on */\n\nstatic const char *uuid_name = \"com.android.ipc-unittest.srv.uuid\";\nstatic const char *echo_name = \"com.android.ipc-unittest.srv.echo\";\nstatic const char *ta_only_name = \"com.android.ipc-unittest.srv.ta_only\";\nstatic const char *ns_only_name = \"com.android.ipc-unittest.srv.ns_only\";\nstatic const char *datasink_name = \"com.android.ipc-unittest.srv.datasink\";\nstatic const char *closer1_name = \"com.android.ipc-unittest.srv.closer1\";\nstatic const char *closer2_name = \"com.android.ipc-unittest.srv.closer2\";\nstatic const char *closer3_name = \"com.android.ipc-unittest.srv.closer3\";\nstatic const char *main_ctrl_name = \"com.android.ipc-unittest.ctrl\";\nstatic const char* receiver_name = \"com.android.trusty.memref.receiver\";\nstatic const size_t memref_chunk_size = 4096;\n\nstatic const char* _sopts = \"hsvD:S:t:r:m:b:B:\";\n/* clang-format off */\nstatic const struct option _lopts[] =  {\n    {\"help\",    no_argument,       0, 'h'},\n    {\"silent\",  no_argument,       0, 's'},\n    {\"variable\",no_argument,       0, 'v'},\n    {\"dev\",     required_argument, 0, 'D'},\n    {\"srv\",     required_argument, 0, 'S'},\n    {\"repeat\",  required_argument, 0, 'r'},\n    {\"burst\",   required_argument, 0, 'b'},\n    {\"msgsize\", required_argument, 0, 'm'},\n    {\"test\",    required_argument, 0, 't'},\n    {\"bench\",   required_argument, 0, 'B'},\n    {0, 0, 0, 0}\n};\n/* clang-format on */\n\nstatic const char* usage =\n        \"Usage: %s [options]\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h, --help            prints this message and exit\\n\"\n        \"  -D, --dev name        device name\\n\"\n        \"  -S, --srv name        service name\\n\"\n        \"  -t, --test name       test to run\\n\"\n        \"  -r, --repeat cnt      repeat count\\n\"\n        \"  -b, --burst cnt       burst count\\n\"\n        \"  -m, --msgsize size    max message size\\n\"\n        \"  -v, --variable        variable message size\\n\"\n        \"  -s, --silent          silent\\n\"\n        \"  -B, --bench           Run as Benchmark N times\\n\"\n        \"\\n\";\n\nstatic const char* usage_long =\n        \"\\n\"\n        \"The following tests are available:\\n\"\n        \"   connect      - connect to specified service, defaults to echo+datasink\\n\"\n        \"   connect_foo  - connect to non existing service\\n\"\n        \"   burst_write  - send messages to datasink service\\n\"\n        \"   echo         - send/receive messages to echo service\\n\"\n        \"   select       - test select call\\n\"\n        \"   blocked_read - test blocked read\\n\"\n        \"   closer1      - connection closed by remote (test1)\\n\"\n        \"   closer2      - connection closed by remote (test2)\\n\"\n        \"   closer3      - connection closed by remote (test3)\\n\"\n        \"   ta2ta-ipc    - execute TA to TA unittest\\n\"\n        \"   dev-uuid     - print device uuid\\n\"\n        \"   ta-access    - test ta-access flags\\n\"\n        \"   writev       - writev test\\n\"\n        \"   readv        - readv test\\n\"\n        \"   send-fd      - transmit dma_buf to trusty, use as shm\\n\"\n        \"\\n\";\n\nstruct tipc_test_params {\n    uint repeat;\n    uint msgsize;\n    uint msgburst;\n    bool variable;\n    bool silent;\n    uint bench;\n    char* srv_name;\n    char* dev_name;\n    char* test_name;\n};\ntypedef int (*tipc_test_func_t)(const struct tipc_test_params*);\n\nstruct tipc_test_def {\n    char* test_name;\n    tipc_test_func_t func;\n};\n\nstatic void init_params(struct tipc_test_params* params) {\n    params->repeat = 1;\n    params->msgsize = 32;\n    params->msgburst = 32;\n    params->variable = false;\n    params->silent = false;\n    params->bench = 0;\n    params->srv_name = NULL;\n    params->test_name = NULL;\n}\n\nstatic void print_usage_and_exit(const char *prog, int code, bool verbose)\n{\n    fprintf(stderr, usage, prog);\n    if (verbose) fprintf(stderr, \"%s\", usage_long);\n    exit(code);\n}\n\nstatic void parse_options(int argc, char** argv, struct tipc_test_params* params) {\n    int c;\n    int oidx = 0;\n\n    while (1) {\n        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);\n        if (c == -1) break; /* done */\n\n        switch (c) {\n            case 'D':\n                params->dev_name = strdup(optarg);\n                break;\n\n            case 'S':\n                params->srv_name = strdup(optarg);\n                break;\n\n            case 't':\n                params->test_name = strdup(optarg);\n                break;\n\n            case 'v':\n                params->variable = true;\n                break;\n\n            case 'r':\n                params->repeat = atoi(optarg);\n                break;\n\n            case 'm':\n                params->msgsize = atoi(optarg);\n                break;\n\n            case 'b':\n                params->msgburst = atoi(optarg);\n                break;\n\n            case 's':\n                params->silent = true;\n                break;\n\n            case 'B':\n                params->bench = atoi(optarg);\n                break;\n\n            case 'h':\n                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);\n                break;\n\n            default:\n                print_usage_and_exit(argv[0], EXIT_FAILURE, false);\n        }\n    }\n}\n\nstatic int connect_test(const struct tipc_test_params* params) {\n    uint i;\n    int echo_fd;\n    int dsink_fd;\n    int custom_fd;\n\n    if (!params->silent) {\n        printf(\"%s: repeat = %u\\n\", __func__, params->repeat);\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        if (params->srv_name) {\n            custom_fd = tipc_connect(params->dev_name, params->srv_name);\n            if (custom_fd < 0) {\n                fprintf(stderr, \"Failed to connect to '%s' service\\n\", params->srv_name);\n            }\n            if (custom_fd >= 0) {\n                tipc_close(custom_fd);\n            }\n        } else {\n            echo_fd = tipc_connect(params->dev_name, echo_name);\n            if (echo_fd < 0) {\n                fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"echo\");\n            }\n            dsink_fd = tipc_connect(params->dev_name, datasink_name);\n            if (dsink_fd < 0) {\n                fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"datasink\");\n            }\n\n            if (echo_fd >= 0) {\n                tipc_close(echo_fd);\n            }\n            if (dsink_fd >= 0) {\n                tipc_close(dsink_fd);\n            }\n        }\n    }\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int connect_foo(const struct tipc_test_params* params) {\n    uint i;\n    int fd;\n\n    if (!params->silent) {\n        printf(\"%s: repeat = %u\\n\", __func__, params->repeat);\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        fd = tipc_connect(params->dev_name, \"foo\");\n        if (fd >= 0) {\n            fprintf(stderr, \"succeeded to connect to '%s' service\\n\", \"foo\");\n            tipc_close(fd);\n        }\n    }\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int closer1_test(const struct tipc_test_params* params) {\n    uint i;\n    int fd;\n\n    if (!params->silent) {\n        printf(\"%s: repeat = %u\\n\", __func__, params->repeat);\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        fd = tipc_connect(params->dev_name, closer1_name);\n        if (fd < 0) {\n            fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"closer1\");\n            continue;\n        }\n        if (!params->silent) {\n            printf(\"%s: connected\\n\", __func__);\n        }\n        tipc_close(fd);\n    }\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int closer2_test(const struct tipc_test_params* params) {\n    uint i;\n    int fd;\n\n    if (!params->silent) {\n        printf(\"%s: repeat = %u\\n\", __func__, params->repeat);\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        fd = tipc_connect(params->dev_name, closer2_name);\n        if (fd < 0) {\n            if (!params->silent) {\n                printf(\"failed to connect to '%s' service\\n\", \"closer2\");\n            }\n        } else {\n            /* this should always fail */\n            fprintf(stderr, \"connected to '%s' service\\n\", \"closer2\");\n            tipc_close(fd);\n        }\n    }\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int closer3_test(const struct tipc_test_params* params) {\n    uint i, j;\n    ssize_t rc;\n    int fd[4];\n    char buf[64];\n\n    if (!params->silent) {\n        printf(\"%s: repeat = %u\\n\", __func__, params->repeat);\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        /* open 4 connections to closer3 service */\n        for (j = 0; j < 4; j++) {\n            fd[j] = tipc_connect(params->dev_name, closer3_name);\n            if (fd[j] < 0) {\n                fprintf(stderr, \"fd[%d]: failed to connect to '%s' service\\n\", j, \"closer3\");\n            } else {\n                if (!params->silent) {\n                    printf(\"%s: fd[%d]=%d: connected\\n\", __func__, j, fd[j]);\n                }\n                memset(buf, i + j, sizeof(buf));\n                rc = write(fd[j], buf, sizeof(buf));\n                if (rc != sizeof(buf)) {\n                    if (!params->silent) {\n                        printf(\"%s: fd[%d]=%d: write returned  = %zd\\n\", __func__, j, fd[j], rc);\n                    }\n                    perror(\"closer3_test: write\");\n                }\n            }\n        }\n\n        /* sleep a bit */\n        sleep(1);\n\n        /* It is expected that they will be closed by remote */\n        for (j = 0; j < 4; j++) {\n            if (fd[j] < 0) continue;\n            rc = write(fd[j], buf, sizeof(buf));\n            if (rc != sizeof(buf)) {\n                if (!params->silent) {\n                    printf(\"%s: fd[%d]=%d: write returned = %zd\\n\", __func__, j, fd[j], rc);\n                }\n                perror(\"closer3_test: write\");\n            }\n        }\n\n        /* then they have to be closed by remote */\n        for (j = 0; j < 4; j++) {\n            if (fd[j] >= 0) {\n                tipc_close(fd[j]);\n            }\n        }\n    }\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int echo_test(const struct tipc_test_params* params) {\n    uint i;\n    ssize_t rc;\n    size_t msg_len;\n    int echo_fd = -1;\n    char tx_buf[params->msgsize];\n    char rx_buf[params->msgsize];\n\n    if (!params->silent) {\n        printf(\"%s: repeat %u: params->msgsize %u: variable %s\\n\", __func__, params->repeat,\n               params->msgsize, params->variable ? \"true\" : \"false\");\n    }\n\n    echo_fd = tipc_connect(params->dev_name, echo_name);\n    if (echo_fd < 0) {\n        fprintf(stderr, \"Failed to connect to service\\n\");\n        return echo_fd;\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        msg_len = params->msgsize;\n        if (params->variable && params->msgsize) {\n            msg_len = rand() % params->msgsize;\n        }\n\n        memset(tx_buf, i + 1, msg_len);\n\n        rc = write(echo_fd, tx_buf, msg_len);\n        if ((size_t)rc != msg_len) {\n            perror(\"echo_test: write\");\n            break;\n        }\n\n        rc = read(echo_fd, rx_buf, msg_len);\n        if (rc < 0) {\n            perror(\"echo_test: read\");\n            break;\n        }\n\n        if ((size_t)rc != msg_len) {\n            fprintf(stderr, \"data truncated (%zu vs. %zu)\\n\", rc, msg_len);\n            continue;\n        }\n\n        if (memcmp(tx_buf, rx_buf, (size_t)rc)) {\n            fprintf(stderr, \"data mismatch\\n\");\n            continue;\n        }\n    }\n\n    tipc_close(echo_fd);\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int burst_write_test(const struct tipc_test_params* params) {\n    int fd;\n    uint i, j;\n    ssize_t rc;\n    size_t msg_len;\n    char tx_buf[params->msgsize];\n\n    if (!params->silent) {\n        printf(\"%s: repeat %u: burst %u: params->msgsize %u: variable %s\\n\", __func__,\n               params->repeat, params->msgburst, params->msgsize,\n               params->variable ? \"true\" : \"false\");\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        fd = tipc_connect(params->dev_name, datasink_name);\n        if (fd < 0) {\n            fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"datasink\");\n            break;\n        }\n\n        for (j = 0; j < params->msgburst; j++) {\n            msg_len = params->msgsize;\n            if (params->variable && params->msgsize) {\n                msg_len = rand() % params->msgsize;\n            }\n\n            memset(tx_buf, i + 1, msg_len);\n            rc = write(fd, tx_buf, msg_len);\n            if ((size_t)rc != msg_len) {\n                perror(\"burst_test: write\");\n                break;\n            }\n        }\n\n        tipc_close(fd);\n    }\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int _wait_for_msg(int fd, int timeout, const struct tipc_test_params* params) {\n    int rc;\n    fd_set rfds;\n    uint msgcnt = 0;\n    char rx_buf[params->msgsize];\n    struct timeval tv;\n\n    if (!params->silent) {\n        printf(\"waiting (%d) for msg\\n\", timeout);\n    }\n\n    FD_ZERO(&rfds);\n    FD_SET(fd, &rfds);\n\n    tv.tv_sec = timeout;\n    tv.tv_usec = 0;\n\n    for (;;) {\n        rc = select(fd + 1, &rfds, NULL, NULL, &tv);\n\n        if (rc == 0) {\n            if (!params->silent) {\n                printf(\"select timedout\\n\");\n            }\n            break;\n        }\n\n        if (rc == -1) {\n            perror(\"select_test: select\");\n            return rc;\n        }\n\n        rc = read(fd, rx_buf, sizeof(rx_buf));\n        if (rc < 0) {\n            perror(\"select_test: read\");\n            return rc;\n        } else {\n            if (rc > 0) {\n                msgcnt++;\n            }\n        }\n    }\n\n    if (!params->silent) {\n        printf(\"got %u messages\\n\", msgcnt);\n    }\n\n    return 0;\n}\n\nstatic int select_test(const struct tipc_test_params* params) {\n    int fd;\n    uint i, j;\n    ssize_t rc;\n    char tx_buf[params->msgsize];\n\n    if (!params->silent) {\n        printf(\"%s: repeat %u\\n\", __func__, params->repeat);\n    }\n\n    fd = tipc_connect(params->dev_name, echo_name);\n    if (fd < 0) {\n        fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"echo\");\n        return fd;\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        _wait_for_msg(fd, 1, params);\n\n        if (!params->silent) {\n            printf(\"sending burst: %u msg\\n\", params->msgburst);\n        }\n\n        for (j = 0; j < params->msgburst; j++) {\n            memset(tx_buf, i + j, params->msgsize);\n            rc = write(fd, tx_buf, params->msgsize);\n            if ((size_t)rc != params->msgsize) {\n                perror(\"burst_test: write\");\n                break;\n            }\n        }\n    }\n\n    tipc_close(fd);\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int blocked_read_test(const struct tipc_test_params* params) {\n    int fd;\n    uint i;\n    ssize_t rc;\n    char rx_buf[512];\n\n    if (!params->silent) {\n        printf(\"%s: repeat %u\\n\", __func__, params->repeat);\n    }\n\n    fd = tipc_connect(params->dev_name, echo_name);\n    if (fd < 0) {\n        fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"echo\");\n        return fd;\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        rc = read(fd, rx_buf, sizeof(rx_buf));\n        if (rc < 0) {\n            perror(\"select_test: read\");\n            break;\n        } else {\n            if (!params->silent) {\n                printf(\"got %zd bytes\\n\", rc);\n            }\n        }\n    }\n\n    tipc_close(fd);\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int ta2ta_ipc_test(const struct tipc_test_params* params) {\n    enum test_message_header {\n        TEST_PASSED = 0,\n        TEST_FAILED = 1,\n        TEST_MESSAGE = 2,\n        TEST_TEXT = 3,\n    };\n\n    int fd;\n    int ret;\n    unsigned char rx_buf[256];\n\n    if (!params->silent) {\n        printf(\"%s:\\n\", __func__);\n    }\n\n    fd = tipc_connect(params->dev_name, main_ctrl_name);\n    if (fd < 0) {\n        fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"main_ctrl\");\n        return fd;\n    }\n\n    /* Wait for tests to complete and read status */\n    while (true) {\n        ret = read(fd, rx_buf, sizeof(rx_buf));\n        if (ret <= 0 || ret >= (int)sizeof(rx_buf)) {\n            fprintf(stderr, \"%s: Read failed: %d\\n\", __func__, ret);\n            tipc_close(fd);\n            return -1;\n        }\n\n        if (rx_buf[0] == TEST_PASSED) {\n            break;\n        } else if (rx_buf[0] == TEST_FAILED) {\n            break;\n        } else if (rx_buf[0] == TEST_MESSAGE || rx_buf[0] == TEST_TEXT) {\n            write(STDOUT_FILENO, rx_buf + 1, ret - 1);\n        } else {\n            fprintf(stderr, \"%s: Bad message header: %d\\n\", __func__, rx_buf[0]);\n            break;\n        }\n    }\n\n    tipc_close(fd);\n\n    return rx_buf[0] == TEST_PASSED ? 0 : -1;\n}\n\ntypedef struct uuid\n{\n    uint32_t time_low;\n    uint16_t time_mid;\n    uint16_t time_hi_and_version;\n    uint8_t clock_seq_and_node[8];\n} uuid_t;\n\nstatic void print_uuid(const char *dev, uuid_t *uuid)\n{\n    printf(\"%s:\", dev);\n    printf(\"uuid: %08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\\n\", uuid->time_low,\n           uuid->time_mid, uuid->time_hi_and_version, uuid->clock_seq_and_node[0],\n           uuid->clock_seq_and_node[1], uuid->clock_seq_and_node[2], uuid->clock_seq_and_node[3],\n           uuid->clock_seq_and_node[4], uuid->clock_seq_and_node[5], uuid->clock_seq_and_node[6],\n           uuid->clock_seq_and_node[7]);\n}\n\nstatic int dev_uuid_test(const struct tipc_test_params* params) {\n    int fd;\n    ssize_t rc;\n    uuid_t uuid;\n\n    fd = tipc_connect(params->dev_name, uuid_name);\n    if (fd < 0) {\n        fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"uuid\");\n        return fd;\n    }\n\n    /* wait for test to complete */\n    rc = read(fd, &uuid, sizeof(uuid));\n    if (rc < 0) {\n        perror(\"dev_uuid_test: read\");\n    } else if (rc != sizeof(uuid)) {\n        fprintf(stderr, \"unexpected uuid size (%d vs. %d)\\n\", (int)rc, (int)sizeof(uuid));\n    } else {\n        print_uuid(params->dev_name, &uuid);\n    }\n\n    tipc_close(fd);\n\n    return 0;\n}\n\nstatic int ta_access_test(const struct tipc_test_params* params) {\n    int fd;\n\n    if (!params->silent) {\n        printf(\"%s:\\n\", __func__);\n    }\n\n    fd = tipc_connect(params->dev_name, ta_only_name);\n    if (fd >= 0) {\n        fprintf(stderr, \"Succeed to connect to '%s' service\\n\", \"ta_only\");\n        tipc_close(fd);\n    }\n\n    fd = tipc_connect(params->dev_name, ns_only_name);\n    if (fd < 0) {\n        fprintf(stderr, \"Failed to connect to '%s' service\\n\", \"ns_only\");\n        return fd;\n    }\n    tipc_close(fd);\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int writev_test(const struct tipc_test_params* params) {\n    uint i;\n    ssize_t rc;\n    size_t msg_len;\n    int echo_fd = -1;\n    char tx0_buf[params->msgsize];\n    char tx1_buf[params->msgsize];\n    char rx_buf[params->msgsize];\n    struct iovec iovs[2] = {{tx0_buf, 0}, {tx1_buf, 0}};\n\n    if (!params->silent) {\n        printf(\"%s: repeat %u: params->msgsize %u: variable %s\\n\", __func__, params->repeat,\n               params->msgsize, params->variable ? \"true\" : \"false\");\n    }\n\n    echo_fd = tipc_connect(params->dev_name, echo_name);\n    if (echo_fd < 0) {\n        fprintf(stderr, \"Failed to connect to service\\n\");\n        return echo_fd;\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        msg_len = params->msgsize;\n        if (params->variable && params->msgsize) {\n            msg_len = rand() % params->msgsize;\n        }\n\n        iovs[0].iov_len = msg_len / 3;\n        iovs[1].iov_len = msg_len - iovs[0].iov_len;\n\n        memset(tx0_buf, i + 1, iovs[0].iov_len);\n        memset(tx1_buf, i + 2, iovs[1].iov_len);\n        memset(rx_buf, i + 3, sizeof(rx_buf));\n\n        rc = writev(echo_fd, iovs, 2);\n        if (rc < 0) {\n            perror(\"writev_test: writev\");\n            break;\n        }\n\n        if ((size_t)rc != msg_len) {\n            fprintf(stderr, \"%s: %s: data size mismatch (%zd vs. %zd)\\n\", __func__, \"writev\",\n                    (size_t)rc, msg_len);\n            break;\n        }\n\n        rc = read(echo_fd, rx_buf, sizeof(rx_buf));\n        if (rc < 0) {\n            perror(\"writev_test: read\");\n            break;\n        }\n\n        if ((size_t)rc != msg_len) {\n            fprintf(stderr, \"%s: %s: data size mismatch (%zd vs. %zd)\\n\", __func__, \"read\",\n                    (size_t)rc, msg_len);\n            break;\n        }\n\n        if (memcmp(tx0_buf, rx_buf, iovs[0].iov_len)) {\n            fprintf(stderr, \"%s: data mismatch: buf 0\\n\", __func__);\n            break;\n        }\n\n        if (memcmp(tx1_buf, rx_buf + iovs[0].iov_len, iovs[1].iov_len)) {\n            fprintf(stderr, \"%s: data mismatch, buf 1\\n\", __func__);\n            break;\n        }\n    }\n\n    tipc_close(echo_fd);\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int readv_test(const struct tipc_test_params* params) {\n    uint i;\n    ssize_t rc;\n    size_t msg_len;\n    int echo_fd = -1;\n    char tx_buf[params->msgsize];\n    char rx0_buf[params->msgsize];\n    char rx1_buf[params->msgsize];\n    struct iovec iovs[2] = {{rx0_buf, 0}, {rx1_buf, 0}};\n\n    if (!params->silent) {\n        printf(\"%s: repeat %u: params->msgsize %u: variable %s\\n\", __func__, params->repeat,\n               params->msgsize, params->variable ? \"true\" : \"false\");\n    }\n\n    echo_fd = tipc_connect(params->dev_name, echo_name);\n    if (echo_fd < 0) {\n        fprintf(stderr, \"Failed to connect to service\\n\");\n        return echo_fd;\n    }\n\n    for (i = 0; i < params->repeat; i++) {\n        msg_len = params->msgsize;\n        if (params->variable && params->msgsize) {\n            msg_len = rand() % params->msgsize;\n        }\n\n        iovs[0].iov_len = msg_len / 3;\n        iovs[1].iov_len = msg_len - iovs[0].iov_len;\n\n        memset(tx_buf, i + 1, sizeof(tx_buf));\n        memset(rx0_buf, i + 2, iovs[0].iov_len);\n        memset(rx1_buf, i + 3, iovs[1].iov_len);\n\n        rc = write(echo_fd, tx_buf, msg_len);\n        if (rc < 0) {\n            perror(\"readv_test: write\");\n            break;\n        }\n\n        if ((size_t)rc != msg_len) {\n            fprintf(stderr, \"%s: %s: data size mismatch (%zd vs. %zd)\\n\", __func__, \"write\",\n                    (size_t)rc, msg_len);\n            break;\n        }\n\n        rc = readv(echo_fd, iovs, 2);\n        if (rc < 0) {\n            perror(\"readv_test: readv\");\n            break;\n        }\n\n        if ((size_t)rc != msg_len) {\n            fprintf(stderr, \"%s: %s: data size mismatch (%zd vs. %zd)\\n\", __func__, \"write\",\n                    (size_t)rc, msg_len);\n            break;\n        }\n\n        if (memcmp(rx0_buf, tx_buf, iovs[0].iov_len)) {\n            fprintf(stderr, \"%s: data mismatch: buf 0\\n\", __func__);\n            break;\n        }\n\n        if (memcmp(rx1_buf, tx_buf + iovs[0].iov_len, iovs[1].iov_len)) {\n            fprintf(stderr, \"%s: data mismatch, buf 1\\n\", __func__);\n            break;\n        }\n    }\n\n    tipc_close(echo_fd);\n\n    if (!params->silent) {\n        printf(\"%s: done\\n\", __func__);\n    }\n\n    return 0;\n}\n\nstatic int send_fd_test(const struct tipc_test_params* params) {\n    int ret;\n    int dma_buf = -1;\n    int fd = -1;\n    volatile char* buf = MAP_FAILED;\n    BufferAllocator* allocator = NULL;\n\n    const size_t num_chunks = 10;\n\n    fd = tipc_connect(params->dev_name, receiver_name);\n    if (fd < 0) {\n        fprintf(stderr, \"Failed to connect to test support TA - is it missing?\\n\");\n        ret = -1;\n        goto cleanup;\n    }\n\n    allocator = CreateDmabufHeapBufferAllocator();\n    if (!allocator) {\n        fprintf(stderr, \"Failed to create dma-buf allocator.\\n\");\n        ret = -1;\n        goto cleanup;\n    }\n\n    size_t buf_size = memref_chunk_size * num_chunks;\n    dma_buf = DmabufHeapAlloc(allocator, \"system\", buf_size, 0, 0 /* legacy align */);\n    if (dma_buf < 0) {\n        ret = dma_buf;\n        fprintf(stderr, \"Failed to create dma-buf fd of size %zu err (%d)\\n\", buf_size, ret);\n        goto cleanup;\n    }\n\n    buf = mmap(0, buf_size, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);\n    if (buf == MAP_FAILED) {\n        fprintf(stderr, \"Failed to map dma-buf: %s\\n\", strerror(errno));\n        ret = -1;\n        goto cleanup;\n    }\n\n    strcpy((char*)buf, \"From NS\");\n\n    struct trusty_shm shm = {\n            .fd = dma_buf,\n            .transfer = TRUSTY_SHARE,\n    };\n\n    ssize_t rc = tipc_send(fd, NULL, 0, &shm, 1);\n    if (rc < 0) {\n        fprintf(stderr, \"tipc_send failed: %zd\\n\", rc);\n        ret = rc;\n        goto cleanup;\n    }\n    char c;\n    read(fd, &c, 1);\n    tipc_close(fd);\n\n    ret = 0;\n    for (size_t skip = 0; skip < num_chunks; skip++) {\n        int cmp = strcmp(\"Hello from Trusty!\",\n                         (const char*)&buf[skip * memref_chunk_size]) ? (-1) : 0;\n        if (cmp)\n            fprintf(stderr, \"Failed: Unexpected content at page %zu in dmabuf\\n\", skip);\n        ret |= cmp;\n    }\n\ncleanup:\n    if (buf != MAP_FAILED) {\n        munmap((char*)buf, buf_size);\n    }\n    close(dma_buf);\n    if (allocator) {\n        FreeDmabufHeapBufferAllocator(allocator);\n    }\n    tipc_close(fd);\n    return ret;\n}\n\nuint64_t get_time_us(void) {\n    struct timespec spec;\n\n    clock_gettime(CLOCK_MONOTONIC, &spec);\n    return spec.tv_sec * 1000000 + spec.tv_nsec / 1000;\n}\n\nstatic const struct tipc_test_def tipc_tests[] = {\n        {\"connect\", connect_test},\n        {\"connect_foo\", connect_foo},\n        {\"burst_write\", burst_write_test},\n        {\"select\", select_test},\n        {\"blocked_read\", blocked_read_test},\n        {\"closer1\", closer1_test},\n        {\"closer2\", closer2_test},\n        {\"closer3\", closer3_test},\n        {\"echo\", echo_test},\n        {\"ta2ta-ipc\", ta2ta_ipc_test},\n        {\"dev-uuid\", dev_uuid_test},\n        {\"ta-access\", ta_access_test},\n        {\"writev\", writev_test},\n        {\"readv\", readv_test},\n        {\"send-fd\", send_fd_test},\n};\n\ntipc_test_func_t get_test_function(const struct tipc_test_params* params) {\n    for (size_t i = 0; i < sizeof(tipc_tests) / sizeof(tipc_tests[0]); i++) {\n        if (strcmp(params->test_name, tipc_tests[i].test_name) == 0) {\n            return tipc_tests[i].func;\n        }\n    }\n    fprintf(stderr, \"Unrecognized test name '%s'\\n\", params->test_name);\n    exit(1);\n}\n\nstatic int run_as_bench(const struct tipc_test_params* params) {\n    int rc = 0;\n    int64_t min = INT64_MAX;\n    int64_t max = 0;\n    int64_t avg = 0;\n    int64_t cold = 0;\n\n    uint64_t start;\n    uint64_t end;\n\n    tipc_test_func_t test = get_test_function(params);\n\n    for (size_t i = 0; (i < params->bench + 1) && rc == 0; ++i) {\n        start = get_time_us();\n        rc |= test(params);\n        end = get_time_us();\n        int64_t t = end - start;\n\n        if (i == 0) {\n            cold = t;\n        } else {\n            min = (t < min) ? t : min;\n            max = (t > max) ? t : max;\n            avg += t;\n        }\n    }\n    avg /= params->bench;\n\n    printf(BENCH_RESULT_TPL, params->test_name, min, max, avg, cold, min, max, avg, cold);\n    return rc;\n}\n\nint main(int argc, char **argv)\n{\n    int rc = 0;\n\n    if (argc <= 1) {\n        print_usage_and_exit(argv[0], EXIT_FAILURE, false);\n    }\n    struct tipc_test_params params;\n    init_params(&params);\n    parse_options(argc, argv, &params);\n\n    if (!params.dev_name) {\n        params.dev_name = TIPC_DEFAULT_DEVNAME;\n    }\n\n    if (!params.test_name) {\n        fprintf(stderr, \"need a Test to run\\n\");\n        print_usage_and_exit(argv[0], EXIT_FAILURE, true);\n    }\n\n    if (params.bench > 0) {\n        rc = run_as_bench(&params);\n        params.bench = 0;\n    } else {\n        tipc_test_func_t test = get_test_function(&params);\n        rc = test(&params);\n    }\n    return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n"
  },
  {
    "path": "trusty/libtrusty/trusty.c",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"libtrusty\"\n\n#include <errno.h>\n#include <fcntl.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <sys/uio.h>\n#include <unistd.h>\n\n#include <linux/vm_sockets.h> /* must be after sys/socket.h */\n#include <log/log.h>\n\n#include <trusty/ipc.h>\n\nstatic const char* strip_prefix(const char* str, const char* prefix) {\n    size_t prefix_len = strlen(prefix);\n    if (strncmp(str, prefix, prefix_len) == 0) {\n        return str + prefix_len;\n    } else {\n        return NULL;\n    }\n}\n\nstatic bool use_vsock_connection = false;\nstatic int tipc_vsock_connect(const char* type_cid_port_str, const char* srv_name) {\n    int ret;\n    const char* cid_port_str;\n    char* port_str;\n    char* end_str;\n    int socket_type;\n    if ((cid_port_str = strip_prefix(type_cid_port_str, \"STREAM:\"))) {\n        socket_type = SOCK_STREAM;\n    } else if ((cid_port_str = strip_prefix(type_cid_port_str, \"SEQPACKET:\"))) {\n        socket_type = SOCK_SEQPACKET;\n    } else {\n        /*\n         * Default to SOCK_STREAM if neither type is specified.\n         *\n         * TODO: use SOCK_SEQPACKET by default instead of SOCK_STREAM when SOCK_SEQPACKET is fully\n         * supported since it matches tipc better. At the moment SOCK_SEQPACKET is not supported by\n         * crosvm. It is also significantly slower since the Linux kernel implementation (as of\n         * v6.7-rc1) sends credit update packets every time it receives a data packet while the\n         * SOCK_STREAM version skips these unless the remaining buffer space is \"low\".\n         */\n        socket_type = SOCK_STREAM;\n        cid_port_str = type_cid_port_str;\n    }\n    long cid = strtol(cid_port_str, &port_str, 0);\n    if (port_str[0] != ':') {\n        ALOGE(\"%s: invalid VSOCK str, \\\"%s\\\", need cid:port missing : after cid\\n\", __func__,\n              cid_port_str);\n        return -EINVAL;\n    }\n    long port = strtol(port_str + 1, &end_str, 0);\n    if (end_str[0] != '\\0') {\n        ALOGE(\"%s: invalid VSOCK str, \\\"%s\\\", need cid:port got %ld:%ld\\n\", __func__, cid_port_str,\n              cid, port);\n        return -EINVAL;\n    }\n    int fd = socket(AF_VSOCK, socket_type, 0);\n    if (fd < 0) {\n        ret = -errno;\n        ALOGE(\"%s: can't get vsock %ld:%ld socket for tipc service \\\"%s\\\" (err=%d)\\n\", __func__,\n              cid, port, srv_name, errno);\n        return ret < 0 ? ret : -1;\n    }\n    struct timeval connect_timeout = {.tv_sec = 60, .tv_usec = 0};\n    ret = setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT, &connect_timeout,\n                     sizeof(connect_timeout));\n    if (ret) {\n        ALOGE(\"%s: vsock %ld:%ld: Failed to set connect timeout (err=%d)\\n\", __func__, cid, port,\n              errno);\n        /* failed to set longer timeout, but try to connect anyway */\n    }\n    struct sockaddr_vm sa = {\n            .svm_family = AF_VSOCK,\n            .svm_port = port,\n            .svm_cid = cid,\n    };\n    int retry = 10;\n    do {\n        ret = TEMP_FAILURE_RETRY(connect(fd, (struct sockaddr*)&sa, sizeof(sa)));\n        if (ret && (errno == ENODEV || errno == ESOCKTNOSUPPORT) && --retry) {\n            /*\n             * The kernel returns ESOCKTNOSUPPORT instead of ENODEV if the socket type is\n             * SOCK_SEQPACKET and the guest CID we are trying to connect to is not ready yet.\n             */\n            ALOGE(\"%s: Can't connect to vsock %ld:%ld for tipc service \\\"%s\\\" (err=%d) %d retries \"\n                  \"remaining\\n\",\n                  __func__, cid, port, srv_name, errno, retry);\n            sleep(1);\n        } else {\n            retry = 0;\n        }\n    } while (retry);\n    if (ret) {\n        ret = -errno;\n        ALOGE(\"%s: Can't connect to vsock %ld:%ld for tipc service \\\"%s\\\" (err=%d)\\n\", __func__,\n              cid, port, srv_name, errno);\n        close(fd);\n        return ret < 0 ? ret : -1;\n    }\n    /*\n     * TODO: Current vsock tipc bridge in trusty expects a port name in the\n     * first packet. We need to replace this with a protocol that also does DICE\n     * based authentication.\n     */\n    ret = TEMP_FAILURE_RETRY(write(fd, srv_name, strlen(srv_name)));\n    if (ret != strlen(srv_name)) {\n        ret = -errno;\n        ALOGE(\"%s: vsock %ld:%ld: failed to send tipc service name \\\"%s\\\" (err=%d)\\n\", __func__,\n              cid, port, srv_name, errno);\n        close(fd);\n        return ret < 0 ? ret : -1;\n    }\n    /*\n     * Work around lack of seq packet support. Read a status byte to prevent\n     * the caller from sending more data until srv_name has been read.\n     */\n    int8_t status;\n    ret = TEMP_FAILURE_RETRY(read(fd, &status, sizeof(status)));\n    if (ret != sizeof(status)) {\n        ALOGE(\"%s: vsock %ld:%ld: failed to read status byte for connect to tipc service name \"\n              \"\\\"%s\\\" (err=%d)\\n\",\n              __func__, cid, port, srv_name, errno);\n        close(fd);\n        return ret < 0 ? ret : -1;\n    }\n    use_vsock_connection = true;\n    return fd;\n}\n\nstatic size_t tipc_vsock_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,\n                              int shmcnt) {\n    int ret;\n\n    (void)shms;\n    if (shmcnt != 0) {\n        ALOGE(\"%s: vsock does not yet support passing fds\\n\", __func__);\n        return -ENOTSUP;\n    }\n    ret = TEMP_FAILURE_RETRY(writev(fd, iov, iovcnt));\n    if (ret < 0) {\n        ret = -errno;\n        ALOGE(\"%s: failed to send message (err=%d)\\n\", __func__, errno);\n        return ret < 0 ? ret : -1;\n    }\n\n    return ret;\n}\n\nint tipc_connect(const char* dev_name, const char* srv_name) {\n    int fd;\n    int rc;\n\n    const char* type_cid_port_str = strip_prefix(dev_name, \"VSOCK:\");\n    if (type_cid_port_str) {\n        return tipc_vsock_connect(type_cid_port_str, srv_name);\n    }\n\n    fd = TEMP_FAILURE_RETRY(open(dev_name, O_RDWR));\n    if (fd < 0) {\n        rc = -errno;\n        ALOGE(\"%s: cannot open tipc device \\\"%s\\\": %s\\n\", __func__, dev_name, strerror(errno));\n        return rc < 0 ? rc : -1;\n    }\n\n    rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_CONNECT, srv_name));\n    if (rc < 0) {\n        rc = -errno;\n        ALOGE(\"%s: can't connect to tipc service \\\"%s\\\" (err=%d)\\n\", __func__, srv_name, errno);\n        close(fd);\n        return rc < 0 ? rc : -1;\n    }\n\n    ALOGV(\"%s: connected to \\\"%s\\\" fd %d\\n\", __func__, srv_name, fd);\n    return fd;\n}\n\nssize_t tipc_send(int fd, const struct iovec* iov, int iovcnt, struct trusty_shm* shms,\n                  int shmcnt) {\n    if (use_vsock_connection) {\n        return tipc_vsock_send(fd, iov, iovcnt, shms, shmcnt);\n    }\n    struct tipc_send_msg_req req;\n    req.iov = (__u64)iov;\n    req.iov_cnt = (__u64)iovcnt;\n    req.shm = (__u64)shms;\n    req.shm_cnt = (__u64)shmcnt;\n\n    int rc = TEMP_FAILURE_RETRY(ioctl(fd, TIPC_IOC_SEND_MSG, &req));\n    if (rc < 0) {\n        ALOGE(\"%s: failed to send message (err=%d)\\n\", __func__, rc);\n    }\n\n    return rc;\n}\n\nvoid tipc_close(int fd) {\n    close(fd);\n}\n"
  },
  {
    "path": "trusty/libtrusty-rs/Android.bp",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_library {\n    name: \"libtrusty-rs\",\n    crate_name: \"trusty\",\n    vendor_available: true,\n    srcs: [\n        \"src/lib.rs\",\n    ],\n    rustlibs: [\n        \"liblog_rust\",\n        \"libnix\",\n        \"liblibc\",\n    ],\n}\n\nrust_test {\n    name: \"libtrusty-rs-tests\",\n    crate_name: \"trusty_test\",\n    srcs: [\"tests/test.rs\"],\n    rustlibs: [\n        \"libtrusty-rs\",\n        \"liblibc\",\n    ],\n}\n"
  },
  {
    "path": "trusty/libtrusty-rs/src/lib.rs",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! Functionality for communicating with Trusty services.\n//!\n//! This crate provides the [`TipcChannel`] type, which allows you to establish a\n//! connection to a Trusty service and then communicate with that service.\n//!\n//! # Usage\n//!\n//! To connect to a Trusty service you need two things:\n//!\n//! * The filesystem path to the Trusty IPC device. This is usually\n//!   `/dev/trusty-ipc-dev0`, which is exposed in the constant [`DEFAULT_DEVICE`].\n//! * The port name defined by the service, e.g. `com.android.ipc-unittest.srv.echo`.\n//!\n//! Pass these values to [`TipcChannel::connect`] to establish a connection to a\n//! service.\n//!\n//! Once connected use the [`send`][TipcChannel::send] and [`recv`][TipcChannel::recv]\n//! methods to communicate with the service. Messages are passed as byte buffers, and\n//! each Trusty service has its own protocol for what data messages are expected to\n//! contain. Consult the documentation for the service you are communicating with to\n//! determine how to format outgoing messages and interpret incoming ones.\n//!\n//! The connection is closed automatically when [`TipcChannel`] is dropped.\n//!\n//! # Examples\n//!\n//! This example is a simplified version of the echo test from `tipc-test-rs`:\n//!\n//! ```no_run\n//! use trusty::{DEFAULT_DEVICE, TipcChannel};\n//! use std::io::{Read, Write};\n//!\n//! let mut chann = TipcChannel::connect(\n//!     DEFAULT_DEVICE,\n//!     \"com.android.ipc-unittest.srv.echo\",\n//! ).unwrap();\n//!\n//! chann.send(\"Hello, world!\".as_bytes()).unwrap();\n//!\n//! let mut read_buf = Vec::new();\n//! let read_len = stream.recv(&mut read_buf).unwrap();\n//!\n//! let response = std::str::from_utf8(&read_buf[..read_len]).unwrap();\n//! assert_eq!(\"Hello, world!\", response);\n//!\n//! // The connection is closed here.\n//! ```\n\nuse crate::sys::tipc_connect;\nuse log::{trace, warn};\nuse nix::sys::socket;\nuse std::convert::From;\nuse std::ffi::CString;\nuse std::fs::File;\nuse std::io;\nuse std::io::prelude::*;\nuse std::io::{ErrorKind, Result};\nuse std::os::unix::prelude::AsRawFd;\nuse std::path::Path;\nuse std::thread;\nuse std::time;\n\nmod sys;\n\n/// The default filesystem path for the Trusty IPC device.\npub const DEFAULT_DEVICE: &str = \"/dev/trusty-ipc-dev0\";\n\n/// The maximum size an incoming TIPC message can be.\n///\n/// This can be used to pre-allocate buffer space in order to ensure that your\n/// read buffer can always hold an incoming message.\npub const MAX_MESSAGE_SIZE: usize = 4096;\n\n/// A channel for communicating with a Trusty service.\n///\n/// See the [crate-level documentation][crate] for usage details and examples.\n#[derive(Debug)]\npub struct TipcChannel(File);\n\nimpl TipcChannel {\n    /// Attempts to establish a connection to the specified Trusty service.\n    ///\n    /// The first argument is the path of the Trusty device in the local filesystem,\n    /// e.g. `/dev/trusty-ipc-dev0`. The second argument is the name of the service\n    /// to connect to, e.g. `com.android.ipc-unittest.srv.echo`.\n    ///\n    /// # Panics\n    ///\n    /// This function will panic if `service` contains any intermediate `NUL`\n    /// bytes. This is handled with a panic because the service names are all\n    /// hard-coded constants, and so such an error should always be indicative of a\n    /// bug in the calling code.\n    pub fn connect(device: &str, service: &str) -> Result<Self> {\n        if let Some(cid_port_str) = device.strip_prefix(\"VSOCK:\") {\n            Self::connect_vsock(cid_port_str, service)\n        } else {\n            Self::connect_tipc(device, service)\n        }\n    }\n\n    fn connect_vsock(type_cid_port_str: &str, service: &str) -> Result<Self> {\n        let cid_port_str;\n        let socket_type;\n        if let Some(stream_cid_port_str) = type_cid_port_str.strip_prefix(\"STREAM:\") {\n            socket_type = socket::SockType::Stream;\n            cid_port_str = stream_cid_port_str;\n        } else if let Some(seqpacket_cid_port_str) = type_cid_port_str.strip_prefix(\"SEQPACKET:\") {\n            socket_type = socket::SockType::SeqPacket;\n            cid_port_str = seqpacket_cid_port_str;\n        } else {\n            /*\n             * Default to SOCK_STREAM if neither type is specified.\n             *\n             * TODO: use SOCK_SEQPACKET by default instead of SOCK_STREAM when SOCK_SEQPACKET is fully\n             * supported since it matches tipc better. At the moment SOCK_SEQPACKET is not supported by\n             * crosvm. It is also significantly slower since the Linux kernel implementation (as of\n             * v6.7-rc1) sends credit update packets every time it receives a data packet while the\n             * SOCK_STREAM version skips these unless the remaining buffer space is \"low\".\n             */\n            socket_type = socket::SockType::Stream;\n            cid_port_str = type_cid_port_str;\n        }\n\n        let [cid, port]: [u32; 2] = cid_port_str\n            .split(':')\n            .map(|v| v.parse::<u32>().map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e)))\n            .collect::<Result<Vec<u32>>>()?\n            .try_into()\n            .map_err(|e| {\n                io::Error::new(io::ErrorKind::InvalidInput, format!(\"Wrong number of args: {e:?}\"))\n            })?;\n\n        trace!(\"got cid, port: {cid}, {port}\");\n        let s = socket::socket(\n            socket::AddressFamily::Vsock,\n            socket_type,\n            socket::SockFlag::SOCK_CLOEXEC,\n            None,\n        )?;\n        trace!(\"got socket\");\n        let sa = socket::VsockAddr::new(cid, port);\n        trace!(\"got sa\");\n\n        //let connect_timeout = libc::timeval {tv_sec: 60, tv_usec: 0};\n        // TODO: Set AF_VSOCK/SO_VM_SOCKETS_CONNECT_TIMEOUT sockopt.\n\n        let mut retry = 10;\n        loop {\n            let res = socket::connect(s.as_raw_fd(), &sa);\n            if res.is_ok() || retry <= 0 {\n                res?;\n                break;\n            }\n            warn!(\"vsock:{cid}:{port} connect failed {res:?}, {retry} retries remaining\");\n            retry -= 1;\n            thread::sleep(time::Duration::from_secs(5));\n        }\n        trace!(\"connected\");\n        // TODO: Current vsock tipc bridge in trusty expects a port name in the\n        // first packet. We need to replace this with a protocol that also does DICE\n        // based authentication.\n        // `s` is a valid file descriptor because it came from socket::socket.\n        let mut channel = Self(File::from(s));\n        channel.send(service.as_bytes())?;\n        trace!(\"sent tipc port name\");\n\n        // Work around lack of seq packet support. Read a status byte to prevent\n        // the caller from sending more data until srv_name has been read.\n        let mut status = [0; 1];\n        channel.recv_no_alloc(&mut status)?;\n        trace!(\"got status byte: {status:?}\");\n        Ok(channel)\n    }\n\n    fn connect_tipc(device: impl AsRef<Path>, service: &str) -> Result<Self> {\n        let file = File::options().read(true).write(true).open(device)?;\n\n        let srv_name = CString::new(service).expect(\"Service name contained null bytes\");\n        // SAFETY: The file descriptor is valid because it came from a `File`, and the name is a\n        // valid C string because it came from a `CString`.\n        unsafe {\n            tipc_connect(file.as_raw_fd(), srv_name.as_ptr())?;\n        }\n\n        Ok(Self(file))\n    }\n\n    /// Sends a message to the connected service.\n    ///\n    /// The entire contents of `buf` will be sent as a single message to the\n    /// connected service.\n    pub fn send(&mut self, buf: &[u8]) -> Result<()> {\n        let write_len = self.0.write(buf)?;\n\n        // Verify that the expected number of bytes were written. The entire message\n        // should always be written with a single `write` call, or an error should have\n        // been returned if the message couldn't be written. An assertion failure here\n        // potentially means a bug in the kernel driver.\n        assert_eq!(\n            buf.len(),\n            write_len,\n            \"Failed to send full message ({} of {} bytes written)\",\n            write_len,\n            buf.len(),\n        );\n\n        Ok(())\n    }\n\n    /// Reads the next incoming message.\n    ///\n    /// Attempts to read the next incoming message from the connected service if any\n    /// exist. If the initial capacity of `buf` is not enough to hold the incoming\n    /// message the function repeatedly attempts to reserve additional space until\n    /// it is able to fully read the message.\n    ///\n    /// Blocks until there is an incoming message if there is not already a message\n    /// ready to be received.\n    ///\n    /// # Errors\n    ///\n    /// If this function encounters an error of the kind [`ErrorKind::Interrupted`]\n    /// then the error is ignored and the operation will be tried again.\n    ///\n    /// If this function encounters an error with the error code `EMSGSIZE` then\n    /// additional space will be reserved in `buf` and the operation will be tried\n    /// again.\n    ///\n    /// If any other read error is encountered then this function immediately\n    /// returns the error to the caller, and the length of `buf` is set to 0.\n    pub fn recv(&mut self, buf: &mut Vec<u8>) -> Result<()> {\n        // If no space has been allocated in the buffer reserve enough space to hold any\n        // incoming message.\n        if buf.capacity() == 0 {\n            buf.reserve(MAX_MESSAGE_SIZE);\n        }\n\n        loop {\n            // Resize the vec to make its full capacity available to write into.\n            buf.resize(buf.capacity(), 0);\n\n            match self.0.read(buf.as_mut_slice()) {\n                Ok(len) => {\n                    buf.truncate(len);\n                    return Ok(());\n                }\n\n                Err(err) => {\n                    if let Some(libc::EMSGSIZE) = err.raw_os_error() {\n                        // Ensure that we didn't get `EMSGSIZE` when we already had enough capacity\n                        // to contain the maximum message size. This should never happen, but if it\n                        // does we don't want to hang by looping infinitely.\n                        assert!(\n                            buf.capacity() < MAX_MESSAGE_SIZE,\n                            \"Received `EMSGSIZE` error when buffer capacity was already at maximum\",\n                        );\n\n                        // If we didn't have enough space to hold the incoming message, reserve\n                        // enough space to fit the maximum message size regardless of how much\n                        // capacity the buffer already had.\n                        buf.reserve(MAX_MESSAGE_SIZE - buf.capacity());\n                    } else if err.kind() == ErrorKind::Interrupted {\n                        // If we get an interrupted error the operation can be retried as-is, i.e.\n                        // we don't need to allocate additional space.\n                        continue;\n                    } else {\n                        buf.truncate(0);\n                        return Err(err);\n                    }\n                }\n            }\n        }\n    }\n\n    /// Reads the next incoming message without allocating.\n    ///\n    /// Returns the number of bytes in the received message, or any error that\n    /// occurred when reading the message.\n    ///\n    /// Blocks until there is an incoming message if there is not already a message\n    /// ready to be received.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error with native error code `EMSGSIZE` if `buf` isn't large\n    /// enough to contain the incoming message. Use\n    /// [`raw_os_error`][std::io::Error::raw_os_error] to check the error code to\n    /// determine if you need to increase the size of `buf`. If error code\n    /// `EMSGSIZE` is returned the incoming message will not be dropped, and a\n    /// subsequent call to `recv_no_alloc` can still read it.\n    ///\n    /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the read\n    /// operation should be retried if there is nothing else to do.\n    pub fn recv_no_alloc(&mut self, buf: &mut [u8]) -> Result<usize> {\n        self.0.read(buf)\n    }\n\n    // TODO: Add method that is equivalent to `tipc_send`, i.e. that supports\n    // sending shared memory buffers.\n}\n"
  },
  {
    "path": "trusty/libtrusty-rs/src/sys.rs",
    "content": "// NOTE: The ioctl definitions are sequestered into this module because the\n// `ioctl_*!` macros provided by the nix crate generate public functions that we\n// don't want to be part of this crate's public API.\n//\n// NOTE: We are manually re-declaring the types and constants here instead of using\n// bindgen and a separate `-sys` crate because the defines used for the ioctl\n// numbers (`TIPC_IOC_CONNECT` and `TIPC_IOC_SEND_MSG`) can't currently be\n// translated by bindgen.\n\nuse std::os::raw::c_char;\n\nconst TIPC_IOC_MAGIC: u8 = b'r';\n\n// NOTE: We use `ioctl_write_ptr_bad!` here due to an error in how the ioctl\n// code is defined in `trusty/ipc.h`.\n//\n// If we were to do `ioctl_write_ptr!(TIPC_IOC_MAGIC, 0x80, c_char)` it would\n// generate a function that takes a `*const c_char` data arg and would use\n// `size_of::<c_char>()` when generating the ioctl number. However, in\n// `trusty/ipc.h` the definition for `TIPC_IOC_CONNECT` declares the ioctl with\n// `char*`, meaning we need to use `size_of::<*const c_char>()` to generate an\n// ioctl number that matches what Trusty expects.\n//\n// To maintain compatibility with the `trusty/ipc.h` and the kernel driver we\n// use `ioctl_write_ptr_bad!` and manually use `request_code_write!` to generate\n// the ioctl number using the correct size.\nnix::ioctl_write_ptr_bad!(\n    tipc_connect,\n    nix::request_code_write!(TIPC_IOC_MAGIC, 0x80, std::mem::size_of::<*const c_char>()),\n    c_char\n);\n"
  },
  {
    "path": "trusty/libtrusty-rs/tests/test.rs",
    "content": "use trusty::{TipcChannel, DEFAULT_DEVICE};\n\nconst ECHO_NAME: &str = \"com.android.ipc-unittest.srv.echo\";\n\n#[test]\nfn recv_no_alloc() {\n    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)\n        .expect(\"Failed to connect to Trusty service\");\n\n    // Send a message to the echo TA.\n    let send_buf = [7u8; 32];\n    connection.send(send_buf.as_slice()).unwrap();\n\n    // Receive the response message from the TA. The response message will be the\n    // same as the message we just sent.\n    let mut recv_buf = [0u8; 32];\n    let read_len = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap();\n\n    assert_eq!(\n        send_buf.len(),\n        read_len,\n        \"Received data was wrong size (expected {} bytes, received {})\",\n        send_buf.len(),\n        read_len,\n    );\n    assert_eq!(send_buf, recv_buf, \"Received data does not match sent data\");\n}\n\n#[test]\nfn recv_small_buf() {\n    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)\n        .expect(\"Failed to connect to Trusty service\");\n\n    // Send a long message to the echo service so that we can test receiving a long\n    // message.\n    let send_buf = [7u8; 2048];\n    connection.send(send_buf.as_slice()).unwrap();\n\n    // Attempt to receive the response message with a buffer that is too small to\n    // contain the message.\n    let mut recv_buf = [0u8; 32];\n    let err = connection.recv_no_alloc(recv_buf.as_mut_slice()).unwrap_err();\n\n    assert_eq!(\n        Some(libc::EMSGSIZE),\n        err.raw_os_error(),\n        \"Unexpected error err when receiving incoming message: {:?}\",\n        err,\n    );\n}\n\n#[test]\nfn recv_empty_vec() {\n    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)\n        .expect(\"Failed to connect to Trusty service\");\n\n    // Send a message to the echo TA.\n    let send_buf = [7u8; 2048];\n    connection.send(send_buf.as_slice()).unwrap();\n\n    // Receive the response message. `recv_buf` is initially empty, and `recv` is\n    // responsible for allocating enough space to hold the message.\n    let mut recv_buf = Vec::new();\n    connection.recv(&mut recv_buf).unwrap();\n\n    assert_eq!(send_buf.as_slice(), recv_buf, \"Received data does not match sent data\");\n}\n\n#[test]\nfn recv_vec_existing_capacity() {\n    let mut connection = TipcChannel::connect(DEFAULT_DEVICE, ECHO_NAME)\n        .expect(\"Failed to connect to Trusty service\");\n\n    // Send a message to the echo TA.\n    let send_buf = [7u8; 2048];\n    connection.send(send_buf.as_slice()).unwrap();\n\n    // Receive the response message into a buffer that already has enough capacity\n    // to hold the message. No additional capacity should be allocated when\n    // receiving the message.\n    let mut recv_buf = Vec::with_capacity(2048);\n    connection.recv(&mut recv_buf).unwrap();\n\n    assert_eq!(send_buf.as_slice(), recv_buf, \"Received data does not match sent data\");\n    assert_eq!(2048, recv_buf.capacity(), \"Additional capacity was allocated when not needed\");\n}\n"
  },
  {
    "path": "trusty/line-coverage/Android.bp",
    "content": "// Copyright (C) 2023 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libtrusty_line_coverage\",\n    vendor_available: true,\n    srcs: [\n        \"coverage.cpp\",\n    ],\n    export_include_dirs: [\n        \"include\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libext2_uuid\",\n        \"liblog\",\n        \"libdmabufheap\",\n        \"libtrusty\",\n    ],\n}\n\n"
  },
  {
    "path": "trusty/line-coverage/coverage.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Sourete Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"line-coverage\"\n\n#include <BufferAllocator/BufferAllocator.h>\n#include <android-base/file.h>\n#include <android-base/logging.h>\n#include <android-base/unique_fd.h>\n#include <assert.h>\n#include <log/log.h>\n#include <stdio.h>\n#include <sys/mman.h>\n#include <sys/uio.h>\n#include <trusty/line-coverage/coverage.h>\n#include <trusty/line-coverage/tipc.h>\n#include <trusty/tipc.h>\n#include <iostream>\n\n#define LINE_COVERAGE_CLIENT_PORT \"com.android.trusty.linecoverage.client\"\n\nstruct control {\n    /* Written by controller, read by instrumented TA */\n    uint64_t        cntrl_flags;\n\n    /* Written by instrumented TA, read by controller */\n    uint64_t        oper_flags;\n    uint64_t        write_buffer_start_count;\n    uint64_t        write_buffer_complete_count;\n};\n\nnamespace android {\nnamespace trusty {\nnamespace line_coverage {\n\nusing ::android::base::ErrnoError;\nusing ::android::base::Error;\nusing ::std::string;\n\nCoverageRecord::CoverageRecord(string tipc_dev, struct uuid* uuid)\n    : tipc_dev_(std::move(tipc_dev)),\n      coverage_srv_fd_(-1),\n      uuid_(*uuid),\n      record_len_(0),\n      shm_(NULL),\n      shm_len_(0) {}\n\nCoverageRecord::~CoverageRecord() {\n    if (shm_) {\n        munmap((void*)shm_, shm_len_);\n    }\n}\n\nvolatile void *CoverageRecord::getShm() {\n    if(!IsOpen()) {\n        fprintf(stderr, \"Warning! SHM is NULL!\\n\");\n    }\n    return shm_;\n}\n\nResult<void> CoverageRecord::Rpc(struct line_coverage_client_req* req, \\\n                                  int req_fd, \\\n                                  struct line_coverage_client_resp* resp) {\n    int rc;\n\n    if (req_fd < 0) {\n        rc = write(coverage_srv_fd_, req, sizeof(*req));\n    } else {\n        iovec iov = {\n                .iov_base = req,\n                .iov_len = sizeof(*req),\n        };\n\n        trusty_shm shm = {\n                .fd = req_fd,\n                .transfer = TRUSTY_SHARE,\n        };\n\n        rc = tipc_send(coverage_srv_fd_, &iov, 1, &shm, 1);\n    }\n\n    if (rc != (int)sizeof(*req)) {\n        return ErrnoError() << \"failed to send request to coverage server: \";\n    }\n\n    rc = read(coverage_srv_fd_, resp, sizeof(*resp));\n    if (rc != (int)sizeof(*resp)) {\n        return ErrnoError() << \"failed to read reply from coverage server: \";\n    }\n\n    if (resp->hdr.cmd != (req->hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {\n        return ErrnoError() << \"unknown response cmd: \" << resp->hdr.cmd;\n    }\n\n    return {};\n}\n\nResult<void> CoverageRecord::Open(int fd) {\n    struct line_coverage_client_req req;\n    struct line_coverage_client_resp resp;\n\n    if (shm_) {\n        return {}; /* already initialized */\n    }\n\n    coverage_srv_fd_= fd;\n\n    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;\n    req.open_args.uuid = uuid_;\n    auto ret = Rpc(&req, -1, &resp);\n    if (!ret.ok()) {\n        return Error() << \"failed to open coverage client: \" << ret.error();\n    }\n    record_len_ = resp.open_args.record_len;\n    shm_len_ = record_len_;\n\n    BufferAllocator allocator;\n\n    fd = allocator.Alloc(\"system\", shm_len_);\n    if (fd < 0) {\n        return ErrnoError() << \"failed to create dmabuf of size \" << shm_len_\n                            << \" err code: \" << fd;\n    }\n    unique_fd dma_buf(fd);\n\n    void* shm = mmap(0, shm_len_, PROT_READ | PROT_WRITE, MAP_SHARED, dma_buf, 0);\n    if (shm == MAP_FAILED) {\n        return ErrnoError() << \"failed to map memfd: \";\n    }\n\n    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD;\n    req.share_record_args.shm_len = shm_len_;\n    ret = Rpc(&req, dma_buf, &resp);\n    if (!ret.ok()) {\n        return Error() << \"failed to send shared memory: \" << ret.error();\n    }\n\n    shm_ = shm;\n\n    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_OPEN;\n    req.open_args.uuid = uuid_;\n    ret = Rpc(&req, -1, &resp);\n    if (!ret.ok()) {\n        return Error() << \"failed to open coverage client: \" << ret.error();\n    }\n\n    return {};\n}\n\nbool CoverageRecord::IsOpen() {\n    return shm_;\n}\n\nResult<void> CoverageRecord::SaveFile(const std::string& filename) {\n    if(!IsOpen()) {\n        return ErrnoError() << \"Warning! SHM is NULL!\";\n    }\n    android::base::unique_fd output_fd(TEMP_FAILURE_RETRY(creat(filename.c_str(), 00644)));\n    if (!output_fd.ok()) {\n        return ErrnoError() << \"Could not open output file\";\n    }\n\n    uintptr_t* begin = (uintptr_t*)((char *)shm_ + sizeof(struct control));\n    bool ret = WriteFully(output_fd, begin, record_len_ - sizeof(struct control));\n    if(!ret) {\n        fprintf(stderr, \"Coverage write to file failed\\n\");\n    }\n\n    return {};\n}\n\n}  // namespace line_coverage\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/line-coverage/include/trusty/line-coverage/coverage.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <optional>\n#include <string>\n\n#include <android-base/result.h>\n#include <android-base/unique_fd.h>\n#include <stdint.h>\n#include <trusty/line-coverage/tipc.h>\n\nnamespace android {\nnamespace trusty {\nnamespace line_coverage {\n\nusing android::base::Result;\nusing android::base::unique_fd;\n\nclass CoverageRecord {\n  public:\n    CoverageRecord(std::string tipc_dev, struct uuid* uuid);\n\n    ~CoverageRecord();\n    Result<void> Open(int fd);\n    bool IsOpen();\n    Result<void> SaveFile(const std::string& filename);\n    volatile void* getShm();\n\n  private:\n    Result<void> Rpc(struct line_coverage_client_req* req, \\\n                      int req_fd, \\\n                      struct line_coverage_client_resp* resp);\n\n    Result<std::pair<size_t, size_t>> GetRegionBounds(uint32_t region_type);\n\n    std::string tipc_dev_;\n    int coverage_srv_fd_;\n    struct uuid uuid_;\n    size_t record_len_;\n    volatile void* shm_;\n    size_t shm_len_;\n};\n\n}  // namespace line_coverage\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/line-coverage/include/trusty/line-coverage/tipc.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* This file needs to be kept in-sync with its counterpart on Trusty side */\n\n#pragma once\n\n#include <stdint.h>\n#include <trusty/line-coverage/uuid.h>\n\n\n#define LINE_COVERAGE_CLIENT_PORT \"com.android.trusty.linecoverage.client\"\n\n/**\n * enum line_coverage_client_cmd - command identifiers for coverage client interface\n * @LINE_COVERAGE_CLIENT_CMD_RESP_BIT:     response bit set as part of response\n * @LINE_COVERAGE_CLIENT_CMD_SHIFT:        number of bits used by response bit\n * @LINE_COVERAGE_CLIENT_CMD_OPEN:         command to open coverage record\n * @LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD: command to register a shared memory region\n *                                    where coverage record will be written to\n */\nenum line_coverage_client_cmd {\n    LINE_COVERAGE_CLIENT_CMD_RESP_BIT = 1U,\n    LINE_COVERAGE_CLIENT_CMD_SHIFT = 1U,\n    LINE_COVERAGE_CLIENT_CMD_OPEN = (1U << LINE_COVERAGE_CLIENT_CMD_SHIFT),\n    LINE_COVERAGE_CLIENT_CMD_SHARE_RECORD = (2U << LINE_COVERAGE_CLIENT_CMD_SHIFT),\n    LINE_COVERAGE_CLIENT_CMD_SEND_LIST = (3U << LINE_COVERAGE_CLIENT_CMD_SHIFT),\n};\n\n/**\n * struct line_coverage_client_hdr - header for coverage client messages\n * @cmd: command identifier\n *\n * Note that no messages return a status code. Any error on the server side\n * results in the connection being closed. So, operations can be assumed to be\n * successful if they return a response.\n */\nstruct line_coverage_client_hdr {\n    uint32_t cmd;\n};\n\n/**\n * struct line_coverage_client_open_req - arguments for request to open coverage\n *                                   record\n * @uuid: UUID of target TA\n *\n * There is one coverage record per TA. @uuid is used to identify both the TA\n * and corresponding coverage record.\n */\nstruct line_coverage_client_open_req {\n    struct uuid uuid;\n};\n\n/**\n * struct line_coverage_client_open_resp - arguments for response to open coverage\n *                                    record\n * @record_len: length of coverage record that will be emitted by target TA\n *\n * Shared memory allocated for this coverage record must larger than\n * @record_len.\n */\nstruct line_coverage_client_open_resp {\n    uint32_t record_len;\n};\n\n/**\n * struct line_coverage_client_send_list_resp - arguments for response to send list\n *                                   record\n * @uuid: UUID of TA that connected to aggregator\n */\nstruct line_coverage_client_send_list_resp {\n    struct uuid uuid;\n};\n\n/**\n * struct line_coverage_client_send_list_resp - arguments for response to send list\n *                                   record\n * @index: index of the list being requested\n */\nstruct line_coverage_client_send_list_req {\n    uint32_t index;\n};\n\n/**\n * struct line_coverage_client_share_record_req - arguments for request to share\n *                                           memory for coverage record\n * @shm_len: length of memory region being shared\n *\n * A handle to a memory region must be sent along with this message. This memory\n * is used to store coverage record.\n *\n * Upon success, this memory region can be assumed to be shared between the\n * client and target TA.\n */\nstruct line_coverage_client_share_record_req {\n    uint32_t shm_len;\n};\n\n/**\n * struct line_coverage_client_req - structure for a coverage client request\n * @hdr:               message header\n * @open_args:         arguments for %COVERAGE_CLIENT_CMD_OPEN request\n * @share_record_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request\n * @index: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD request\n */\nstruct line_coverage_client_req {\n    struct line_coverage_client_hdr hdr;\n    union {\n        struct line_coverage_client_open_req open_args;\n        struct line_coverage_client_share_record_req share_record_args;\n        struct line_coverage_client_send_list_req send_list_args;\n    };\n};\n\n/**\n * struct line_coverage_client_resp - structure for a coverage client response\n * @hdr:       message header\n * @open_args: arguments for %COVERAGE_CLIENT_CMD_OPEN response\n * @send_list_args: arguments for %COVERAGE_CLIENT_CMD_SHARE_RECORD response\n */\nstruct line_coverage_client_resp {\n    struct line_coverage_client_hdr hdr;\n    union {\n        struct line_coverage_client_open_resp open_args;\n        struct line_coverage_client_send_list_resp send_list_args;\n    };\n};\n"
  },
  {
    "path": "trusty/line-coverage/include/trusty/line-coverage/uuid.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n#include <string.h>\n\n#define UUCMP(u1, u2) if (u1 != u2) return u1 < u2\n\nstruct uuid {\n    uint32_t time_low;\n    uint16_t time_mid;\n    uint16_t time_hi_and_version;\n    uint8_t clock_seq_and_node[8];\n\n    bool operator<(const struct uuid& rhs) const\n    {\n        UUCMP(time_low, rhs.time_low);\n        UUCMP(time_mid, rhs.time_mid);\n        UUCMP(time_hi_and_version, rhs.time_hi_and_version);\n        return memcmp(clock_seq_and_node, rhs.clock_seq_and_node, 8);\n    }\n};\n"
  },
  {
    "path": "trusty/metrics/Android.bp",
    "content": "// Copyright (C) 2021 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library {\n    name: \"libtrusty_metrics\",\n    vendor: true,\n    srcs: [\n        \"metrics.cpp\",\n    ],\n    export_include_dirs: [\n        \"include\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n        \"libtrusty\",\n    ],\n}\n\ncc_test {\n    name: \"libtrusty_metrics_test\",\n    vendor: true,\n    srcs: [\n        \"metrics_test.cpp\",\n    ],\n    static_libs: [\n        \"libtrusty_metrics\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libbinder\",\n        \"liblog\",\n        \"libtrusty\",\n    ],\n    require_root: true,\n}\n"
  },
  {
    "path": "trusty/metrics/include/trusty/metrics/metrics.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <functional>\n#include <memory>\n#include <string>\n\n#include <android-base/result.h>\n#include <android-base/unique_fd.h>\n\nnamespace android {\nnamespace trusty {\nnamespace metrics {\n\nusing android::base::Result;\nusing android::base::unique_fd;\n\nclass TrustyMetrics {\n  public:\n    /* Wait for next event with a given timeout. Negative timeout means infinite timeout. */\n    Result<void> WaitForEvent(int timeout_ms = -1);\n    /* Attempt to handle an event from Metrics TA in a non-blocking manner. */\n    Result<void> HandleEvent();\n    /* Expose TIPC channel so that client can integrate it into an event loop with other fds. */\n    int GetRawFd() { return metrics_fd_; };\n\n  protected:\n    TrustyMetrics(std::string tipc_dev) : tipc_dev_(std::move(tipc_dev)), metrics_fd_(-1) {}\n    virtual ~TrustyMetrics(){};\n\n    Result<void> Open();\n    virtual void HandleCrash(const std::string& app_id) = 0;\n    virtual void HandleEventDrop() = 0;\n\n  private:\n    std::string tipc_dev_;\n    unique_fd metrics_fd_;\n};\n\n}  // namespace metrics\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/metrics/include/trusty/metrics/tipc.h",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n/**\n * DOC: Metrics\n *\n * Metrics interface provides a way for Android to get Trusty metrics data.\n *\n * Currently, only \"push\" model is supported. Clients are expected to connect to\n * metrics service, listen for events, e.g. app crash events, and respond to\n * every event with a &struct metrics_req.\n *\n * Communication is driven by metrics service, i.e. requests/responses are all\n * sent from/to metrics service.\n *\n * Note that the type of the event is not known to the client ahead of time.\n *\n * In the future, if we need to have Android \"pull\" metrics data from Trusty,\n * that can be done by introducing a separate port.\n *\n * This interface is shared between Android and Trusty. There is a copy in each\n * repository. They must be kept in sync.\n */\n\n#define METRICS_PORT \"com.android.trusty.metrics.consumer\"\n\n#define UUID_STR_SIZE (37)\n\n#define HASH_SIZE_BYTES 64\n\n/**\n * enum metrics_cmd - command identifiers for metrics interface\n * @METRICS_CMD_RESP_BIT:             message is a response\n * @METRICS_CMD_REQ_SHIFT:            number of bits used by @METRICS_CMD_RESP_BIT\n * @METRICS_CMD_REPORT_EVENT_DROP:    report gaps in the event stream\n * @METRICS_CMD_REPORT_CRASH:         report an app crash event\n * @METRICS_CMD_REPORT_EXIT:          report an app exit\n * @METRICS_CMD_REPORT_STORAGE_ERROR: report trusty storage error\n */\nenum metrics_cmd {\n    METRICS_CMD_RESP_BIT = 1,\n    METRICS_CMD_REQ_SHIFT = 1,\n\n    METRICS_CMD_REPORT_EVENT_DROP = (1 << METRICS_CMD_REQ_SHIFT),\n    METRICS_CMD_REPORT_CRASH = (2 << METRICS_CMD_REQ_SHIFT),\n    METRICS_CMD_REPORT_EXIT = (3 << METRICS_CMD_REQ_SHIFT),\n    METRICS_CMD_REPORT_STORAGE_ERROR = (4 << METRICS_CMD_REQ_SHIFT),\n};\n\n/**\n * enum metrics_error - metrics error codes\n * @METRICS_NO_ERROR:        no error\n * @METRICS_ERR_UNKNOWN_CMD: unknown or not implemented command\n */\nenum metrics_error {\n    METRICS_NO_ERROR = 0,\n    METRICS_ERR_UNKNOWN_CMD = 1,\n};\n\n/**\n * struct metrics_req - common structure for metrics requests\n * @cmd:      command identifier - one of &enum metrics_cmd\n * @reserved: must be 0\n */\nstruct metrics_req {\n    uint32_t cmd;\n    uint32_t reserved;\n} __attribute__((__packed__));\n\n/**\n * struct metrics_resp - common structure for metrics responses\n * @cmd: command identifier - %METRICS_CMD_RESP_BIT or'ed with a cmd in\n *                            one of &enum metrics_cmd\n * @status: response status, one of &enum metrics_error\n */\nstruct metrics_resp {\n    uint32_t cmd;\n    uint32_t status;\n} __attribute__((__packed__));\n\n/**\n * struct metrics_report_exit_req - arguments of %METRICS_CMD_REPORT_EXIT\n *                                   requests\n * @app_id: app_id in the form UUID in ascii format\n *          \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n * @exit_code: architecture-specific exit code\n */\nstruct metrics_report_exit_req {\n    char app_id[UUID_STR_SIZE];\n    uint32_t exit_code;\n} __attribute__((__packed__));\n\n/**\n * struct metrics_report_crash_req - arguments of %METRICS_CMD_REPORT_CRASH\n *                                   requests\n * @app_id: app_id in the form UUID in ascii format\n *          \"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\"\n * @crash_reason: architecture-specific code representing the reason for the\n *                crash\n * @far: Fault Address Register corresponding to the crash. It is set to 0 and\n *       not always revealed\n * @far_hash: Fault Address Register obfuscated, always revealed\n * @elr: Exception Link Register corresponding to the crash. It is set to 0 and\n *       not always revealed\n * @elr_hash: Exception Link Register obfuscated, always revealed\n * @is_hash: Boolean value indicating whether far and elr have been ob\n */\nstruct metrics_report_crash_req {\n    char app_id[UUID_STR_SIZE];\n    uint32_t crash_reason;\n    uint64_t far;\n    uint8_t far_hash[HASH_SIZE_BYTES];\n    uint64_t elr;\n    uint8_t elr_hash[HASH_SIZE_BYTES];\n    bool is_hash;\n} __attribute__((__packed__));\n\nenum TrustyStorageErrorType {\n  TRUSTY_STORAGE_ERROR_UNKNOWN = 0,\n  TRUSTY_STORAGE_ERROR_SUPERBLOCK_INVALID = 1,\n  TRUSTY_STORAGE_ERROR_BLOCK_MAC_MISMATCH = 2,\n  TRUSTY_STORAGE_ERROR_BLOCK_HEADER_INVALID = 3,\n  TRUSTY_STORAGE_ERROR_RPMB_COUNTER_MISMATCH = 4,\n  TRUSTY_STORAGE_ERROR_RPMB_COUNTER_MISMATCH_RECOVERED = 5,\n  TRUSTY_STORAGE_ERROR_RPMB_COUNTER_READ_FAILURE = 6,\n  TRUSTY_STORAGE_ERROR_RPMB_MAC_MISMATCH = 7,\n  TRUSTY_STORAGE_ERROR_RPMB_ADDR_MISMATCH = 8,\n  TRUSTY_STORAGE_ERROR_RPMB_FAILURE_RESPONSE = 9,\n  TRUSTY_STORAGE_ERROR_RPMB_UNKNOWN = 10,\n  TRUSTY_STORAGE_ERROR_RPMB_SCSI_ERROR = 11,\n  TRUSTY_STORAGE_ERROR_IO_ERROR = 12,\n  TRUSTY_STORAGE_ERROR_PROXY_COMMUNICATION_FAILURE = 13,\n};\n\nenum TrustyFileSystem {\n  TRUSTY_FS_UNKNOWN = 0,\n  TRUSTY_FS_TP = 1,\n  TRUSTY_FS_TD = 2,\n  TRUSTY_FS_TDP = 3,\n  TRUSTY_FS_TDEA = 4,\n  TRUSTY_FS_NSP = 5,\n};\n\nenum TrustyBlockType {\n  TRUSTY_BLOCKTYPE_UNKNOWN = 0,\n  TRUSTY_BLOCKTYPE_FILES_ROOT = 1,\n  TRUSTY_BLOCKTYPE_FREE_ROOT = 2,\n  TRUSTY_BLOCKTYPE_FILES_INTERNAL = 3,\n  TRUSTY_BLOCKTYPE_FREE_INTERNAL = 4,\n  TRUSTY_BLOCKTYPE_FILE_ENTRY = 5,\n  TRUSTY_BLOCKTYPE_FILE_BLOCK_MAP = 6,\n  TRUSTY_BLOCKTYPE_FILE_DATA = 7,\n  TRUSTY_BLOCKTYPE_CHECKPOINT_ROOT = 8,\n  TRUSTY_BLOCKTYPE_CHECKPOINT_FILES_ROOT = 9,\n  TRUSTY_BLOCKTYPE_CHECKPOINT_FREE_ROOT = 10,\n};\n\nstruct metrics_report_storage_error_req {\n    enum TrustyStorageErrorType error;\n    char app_id[UUID_STR_SIZE];\n    char client_app_id[UUID_STR_SIZE];\n    uint32_t write;\n    enum TrustyFileSystem file_system;\n    uint64_t file_path_hash;\n    enum TrustyBlockType block_type;\n    uint64_t repair_counter;\n} __attribute__((__packed__));\n\nstruct metrics_msg {\n    struct metrics_req req;\n    union {\n        struct metrics_report_crash_req crash_args;\n        struct metrics_report_exit_req exit_args;\n        struct metrics_report_storage_error_req storage_args;\n    };\n} __attribute__((__packed__));"
  },
  {
    "path": "trusty/metrics/metrics.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Sourete Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"metrics\"\n\n#include <android-base/logging.h>\n#include <fcntl.h>\n#include <poll.h>\n#include <trusty/metrics/metrics.h>\n#include <trusty/metrics/tipc.h>\n#include <trusty/tipc.h>\n#include <unistd.h>\n\nnamespace android {\nnamespace trusty {\nnamespace metrics {\n\nusing android::base::ErrnoError;\nusing android::base::Error;\n\nResult<void> TrustyMetrics::Open() {\n    int fd = tipc_connect(tipc_dev_.c_str(), METRICS_PORT);\n    if (fd < 0) {\n        return ErrnoError() << \"failed to connect to Trusty metrics TA\";\n    }\n\n    int flags = fcntl(fd, F_GETFL, 0);\n    if (flags < 0) {\n        return ErrnoError() << \"failed F_GETFL\";\n    }\n\n    int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK);\n    if (rc < 0) {\n        return ErrnoError() << \"failed F_SETFL\";\n    }\n\n    metrics_fd_.reset(fd);\n    return {};\n}\n\nResult<void> TrustyMetrics::WaitForEvent(int timeout_ms) {\n    if (!metrics_fd_.ok()) {\n        return Error() << \"connection to Metrics TA has not been initialized yet\";\n    }\n\n    struct pollfd pfd = {\n            .fd = metrics_fd_,\n            .events = POLLIN,\n    };\n\n    int rc = poll(&pfd, 1, timeout_ms);\n    if (rc != 1) {\n        return ErrnoError() << \"failed poll()\";\n    }\n\n    if (!(pfd.revents & POLLIN)) {\n        return ErrnoError() << \"channel not ready\";\n    }\n\n    return {};\n}\n\nResult<void> TrustyMetrics::HandleEvent() {\n    if (!metrics_fd_.ok()) {\n        return Error() << \"connection to Metrics TA has not been initialized yet\";\n    }\n\n    struct metrics_msg metrics_msg;\n    int rc = read(metrics_fd_, &metrics_msg, sizeof(metrics_msg));\n    if (rc < 0) {\n        return ErrnoError() << \"failed to read metrics message\";\n    }\n    size_t msg_len = rc;\n\n    if (msg_len < sizeof(metrics_req)) {\n        return Error() << \"message too small: \" << rc;\n    }\n    uint32_t cmd = metrics_msg.req.cmd;\n    uint32_t status = METRICS_NO_ERROR;\n\n    switch (cmd) {\n        case METRICS_CMD_REPORT_CRASH: {\n            struct metrics_report_crash_req crash_args = metrics_msg.crash_args;\n            auto app_id_ptr = crash_args.app_id;\n            std::string app_id(app_id_ptr, UUID_STR_SIZE);\n\n            HandleCrash(app_id);\n            break;\n        }\n\n        case METRICS_CMD_REPORT_EVENT_DROP:\n            HandleEventDrop();\n            break;\n\n        default:\n            status = METRICS_ERR_UNKNOWN_CMD;\n            break;\n    }\n\n    metrics_resp resp = {\n            .cmd = cmd | METRICS_CMD_RESP_BIT,\n            .status = status,\n    };\n\n    rc = write(metrics_fd_, &resp, sizeof(resp));\n    if (rc < 0) {\n        return ErrnoError() << \"failed to request next metrics event\";\n    }\n\n    if (rc != (int)sizeof(resp)) {\n        return Error() << \"unexpected number of bytes sent event: \" << rc;\n    }\n\n    return {};\n}\n\n}  // namespace metrics\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/metrics/metrics_test.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/unique_fd.h>\n#include <binder/IPCThreadState.h>\n#include <gtest/gtest.h>\n#include <poll.h>\n#include <trusty/metrics/metrics.h>\n#include <trusty/tipc.h>\n\n#define TIPC_DEV \"/dev/trusty-ipc-dev0\"\n#define CRASHER_PORT \"com.android.trusty.metrics.test.crasher\"\n\nnamespace android {\nnamespace trusty {\nnamespace metrics {\n\nusing android::base::unique_fd;\n\nstatic void TriggerCrash() {\n    size_t num_retries = 6;\n    int fd = -1;\n\n    for (size_t i = 0; i < num_retries; i++) {\n        /* It's possible to time out waiting for crasher TA to restart. */\n        fd = tipc_connect(TIPC_DEV, CRASHER_PORT);\n        if (fd >= 0) {\n            break;\n        }\n    }\n\n    unique_fd crasher(fd);\n    ASSERT_GE(crasher, 0);\n\n    int msg = 0;\n    int rc = write(crasher, &msg, sizeof(msg));\n    ASSERT_EQ(rc, sizeof(msg));\n}\n\nclass TrustyMetricsTest : public TrustyMetrics, public ::testing::Test {\n  public:\n    TrustyMetricsTest() : TrustyMetrics(TIPC_DEV) {}\n\n    virtual void HandleCrash(const std::string& app_id) override { crashed_app_ = app_id; }\n\n    virtual void HandleEventDrop() override { event_drop_count_++; }\n\n    virtual void SetUp() override {\n        auto ret = Open();\n        ASSERT_TRUE(ret.ok()) << ret.error();\n\n        /* Drain events (if any) and reset state */\n        DrainEvents();\n        crashed_app_.clear();\n        event_drop_count_ = 0;\n    }\n\n    void DrainEvents() {\n        while (WaitForEvent(1000 /* 1 second timeout */).ok()) {\n            auto ret = HandleEvent();\n            ASSERT_TRUE(ret.ok()) << ret.error();\n        }\n    }\n\n    void WaitForAndHandleEvent() {\n        auto ret = WaitForEvent(30000 /* 30 second timeout */);\n        ASSERT_TRUE(ret.ok()) << ret.error();\n\n        ret = HandleEvent();\n        ASSERT_TRUE(ret.ok()) << ret.error();\n    }\n\n    std::string crashed_app_;\n    size_t event_drop_count_;\n};\n\nTEST_F(TrustyMetricsTest, Crash) {\n    TriggerCrash();\n    WaitForAndHandleEvent();\n\n    /* Check that no event was dropped. */\n    ASSERT_EQ(event_drop_count_, 0);\n\n    /* Check that correct TA crashed. */\n    ASSERT_EQ(crashed_app_, \"36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher\");\n}\n\nTEST_F(TrustyMetricsTest, PollSet) {\n    int binder_fd;\n    int rc = IPCThreadState::self()->setupPolling(&binder_fd);\n    ASSERT_EQ(rc, 0);\n    ASSERT_GE(binder_fd, 0);\n\n    TriggerCrash();\n\n    struct pollfd pfds[] = {\n            {\n                    .fd = binder_fd,\n                    .events = POLLIN,\n            },\n            {\n                    .fd = GetRawFd(),\n                    .events = POLLIN,\n            },\n    };\n\n    rc = poll(pfds, 2, 30000 /* 30 second timeout */);\n    /* We expect one event on the metrics fd. */\n    ASSERT_EQ(rc, 1);\n    ASSERT_TRUE(pfds[1].revents & POLLIN);\n\n    auto ret = HandleEvent();\n    ASSERT_TRUE(ret.ok()) << ret.error();\n\n    /* Check that no event was dropped. */\n    ASSERT_EQ(event_drop_count_, 0);\n\n    /* Check that correct TA crashed. */\n    ASSERT_EQ(crashed_app_, \"36f5b435-5bd3-4526-8b76-200e3a7e79f3:crasher\");\n}\n\nTEST_F(TrustyMetricsTest, EventDrop) {\n    /* We know the size of the internal event queue is less than this. */\n    size_t num_events = 3;\n\n    ASSERT_EQ(event_drop_count_, 0);\n\n    for (auto i = 0; i < num_events; i++) {\n        TriggerCrash();\n    }\n\n    for (auto i = 0; i < num_events; i++) {\n        WaitForAndHandleEvent();\n        if (event_drop_count_ > 0) {\n            break;\n        }\n    }\n\n    ASSERT_EQ(event_drop_count_, 1);\n}\n\n}  // namespace metrics\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/secretkeeper/Android.bp",
    "content": "//\n// Copyright (C) 2022 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\nrust_binary {\n    name: \"android.hardware.security.secretkeeper.trusty\",\n    relative_install_path: \"hw\",\n    vendor: true,\n    init_rc: [\"android.hardware.security.secretkeeper.trusty.rc\"],\n    vintf_fragments: [\"android.hardware.security.secretkeeper.trusty.xml\"],\n    srcs: [\n        \"src/hal_main.rs\",\n    ],\n    rustlibs: [\n        \"android.hardware.security.secretkeeper-V1-rust\",\n        \"libandroid_logger\",\n        \"libauthgraph_hal\",\n        \"libauthgraph_wire\",\n        \"libbinder_rs\",\n        \"liblibc\",\n        \"liblog_rust\",\n        \"libsecretkeeper_hal_v1\",\n        \"libtrusty-rs\",\n    ],\n    prefer_rlib: true,\n}\n\ncc_defaults {\n    name: \"trusty_secretkeeper_fuzz_defaults\",\n    srcs: [\":trusty_tipc_fuzzer\"],\n    fuzz_config: {\n        cc: [\n            \"alanstokes@google.com\",\n            \"drysdale@google.com\",\n            \"shikhapanwar@google.com\",\n        ],\n        componentid: 867125,\n        // TODO: add Secretkeeper hotlist\n        // hotlists: [\"\"],\n    },\n}\n\ncc_fuzz {\n    name: \"trusty_secretkeeper_sk_fuzzer\",\n    defaults: [\n        \"trusty_fuzzer_defaults\",\n        \"trusty_secretkeeper_fuzz_defaults\",\n    ],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.secretkeeper\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"4582bf12-1f7d-4830-9be5-36e6bd91c2c6\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"secretkeeper_app.syms.elf\\\"\",\n    ],\n}\n\ncc_fuzz {\n    name: \"trusty_secretkeeper_ag_fuzzer\",\n    defaults: [\n        \"trusty_fuzzer_defaults\",\n        \"trusty_secretkeeper_fuzz_defaults\",\n    ],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.secretkeeper.authgraph\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"4582bf12-1f7d-4830-9be5-36e6bd91c2c6\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"secretkeeper_app.syms.elf\\\"\",\n    ],\n}\n\ncc_fuzz {\n    name: \"trusty_secretkeeper_bl_fuzzer\",\n    defaults: [\n        \"trusty_fuzzer_defaults\",\n        \"trusty_secretkeeper_fuzz_defaults\",\n    ],\n    cflags: [\n        \"-DTRUSTY_APP_PORT=\\\"com.android.trusty.secretkeeper.bootloader\\\"\",\n        \"-DTRUSTY_APP_UUID=\\\"4582bf12-1f7d-4830-9be5-36e6bd91c2c6\\\"\",\n        \"-DTRUSTY_APP_FILENAME=\\\"secretkeeper_app.syms.elf\\\"\",\n    ],\n}\n"
  },
  {
    "path": "trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.rc",
    "content": "service vendor.secretkeeper.trusty /vendor/bin/hw/android.hardware.security.secretkeeper.trusty\n    class hal\n    user nobody\n    group drmrpc"
  },
  {
    "path": "trusty/secretkeeper/android.hardware.security.secretkeeper.trusty.xml",
    "content": "<manifest version=\"1.0\" type=\"device\">\n    <hal format=\"aidl\">\n        <name>android.hardware.security.secretkeeper</name>\n        <version>1</version>\n        <fqname>ISecretkeeper/default</fqname>\n    </hal>\n</manifest>\n"
  },
  {
    "path": "trusty/secretkeeper/src/hal_main.rs",
    "content": "//\n// Copyright (C) 2022 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//! This module implements the HAL service for Secretkeeper in Trusty.\nuse authgraph_hal::channel::SerializedChannel;\nuse authgraph_wire::fragmentation::{Fragmenter, Reassembler};\nuse secretkeeper_hal::SecretkeeperService;\nuse android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::ISecretkeeper::{\n    ISecretkeeper, BpSecretkeeper,\n};\nuse log::{error, info};\nuse std::{\n    ffi::CString,\n    fmt::Debug,\n    panic,\n    sync::{Arc, Mutex},\n};\nuse trusty::DEFAULT_DEVICE;\n\nconst SK_TIPC_SERVICE_PORT: &str = \"com.android.trusty.secretkeeper\";\nconst AG_TIPC_SERVICE_PORT: &str = \"com.android.trusty.secretkeeper.authgraph\";\nconst TIPC_MAX_SIZE: usize = 4000;\n\nstatic SERVICE_INSTANCE: &str = \"default\";\n\n/// Local error type for failures in the HAL service.\n#[derive(Debug, Clone)]\nstruct HalServiceError(String);\n\n#[derive(Debug)]\nstruct TipcChannel {\n    channel: Arc<Mutex<trusty::TipcChannel>>,\n}\n\nimpl TipcChannel {\n    fn new(channel: trusty::TipcChannel) -> Self {\n        Self { channel: Arc::new(Mutex::new(channel)) }\n    }\n}\n\nfn binderr<E: Debug>(msg: &str, e: E) -> binder::Status {\n    binder::Status::new_exception(\n        binder::ExceptionCode::TRANSACTION_FAILED,\n        Some(&CString::new(format!(\"Failed to {msg} via tipc channel: {e:?}\",)).unwrap()),\n    )\n}\n\nimpl SerializedChannel for TipcChannel {\n    // No maximum size for messages passed to `execute()` because it performs fragmentation\n    // and reassembly internally.\n    const MAX_SIZE: usize = usize::MAX;\n\n    fn execute(&self, req_data: &[u8]) -> binder::Result<Vec<u8>> {\n        // Hold lock across both request and response.\n        let mut channel = self.channel.lock().unwrap();\n        let mut pending_rsp = Reassembler::default();\n\n        // Break request message into fragments to send.\n        for req_frag in Fragmenter::new(req_data, TIPC_MAX_SIZE) {\n            channel.send(&req_frag).map_err(|e| binderr(\"send request\", e))?;\n\n            // Every request gets a response.\n            let mut rsp_frag = Vec::new();\n            channel.recv(&mut rsp_frag).map_err(|e| binderr(\"receive response\", e))?;\n\n            if let Some(full_rsp) = pending_rsp.accumulate(&rsp_frag) {\n                return Ok(full_rsp.to_vec());\n            }\n        }\n        // There may be additional response fragments to receive.\n        loop {\n            let mut rsp_frag = Vec::new();\n            channel.recv(&mut rsp_frag).map_err(|e| binderr(\"receive response\", e))?;\n            if let Some(full_rsp) = pending_rsp.accumulate(&rsp_frag) {\n                return Ok(full_rsp.to_vec());\n            }\n        }\n    }\n}\n\nfn main() {\n    if let Err(HalServiceError(e)) = inner_main() {\n        panic!(\"HAL service failed: {:?}\", e);\n    }\n}\n\nfn inner_main() -> Result<(), HalServiceError> {\n    // Initialize Android logging.\n    android_logger::init_once(\n        android_logger::Config::default()\n            .with_tag(\"secretkeeper-hal-trusty\")\n            .with_max_level(log::LevelFilter::Info)\n            .with_log_buffer(android_logger::LogId::System),\n    );\n    // Redirect panic messages to logcat.\n    panic::set_hook(Box::new(|panic_info| {\n        error!(\"{}\", panic_info);\n    }));\n\n    info!(\"Trusty Secretkeeper HAL service is starting.\");\n\n    info!(\"Starting thread pool now.\");\n    binder::ProcessState::start_thread_pool();\n\n    // Create connections to the TA.\n    let ag_connection = trusty::TipcChannel::connect(DEFAULT_DEVICE, AG_TIPC_SERVICE_PORT)\n        .map_err(|e| {\n            HalServiceError(format!(\n                \"Failed to connect to Trusty port {AG_TIPC_SERVICE_PORT} because of {:?}.\",\n                e\n            ))\n        })?;\n    let ag_tipc_channel = TipcChannel::new(ag_connection);\n\n    let sk_connection = trusty::TipcChannel::connect(DEFAULT_DEVICE, SK_TIPC_SERVICE_PORT)\n        .map_err(|e| {\n            HalServiceError(format!(\n                \"Failed to connect to Trusty port {SK_TIPC_SERVICE_PORT} because of {:?}.\",\n                e\n            ))\n        })?;\n    let sk_tipc_channel = TipcChannel::new(sk_connection);\n\n    // Register the AIDL service\n    let service = SecretkeeperService::new_as_binder(sk_tipc_channel, ag_tipc_channel);\n    let service_name =\n        format!(\"{}/{}\", <BpSecretkeeper as ISecretkeeper>::get_descriptor(), SERVICE_INSTANCE);\n    binder::add_service(&service_name, service.as_binder()).map_err(|e| {\n        HalServiceError(format!(\"Failed to register service {} because of {:?}.\", service_name, e))\n    })?;\n\n    info!(\"Successfully registered Secretkeeper HAL service.\");\n    info!(\"Joining thread pool now.\");\n    binder::ProcessState::join_thread_pool();\n    info!(\"Secretkeeper HAL service is terminating.\"); // should not reach here\n    Ok(())\n}\n"
  },
  {
    "path": "trusty/secure_dpu/Android.bp",
    "content": "// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_headers {\n    name: \"secure_dpu_headers\",\n    vendor: true,\n\n    export_include_dirs: [\"include\"],\n}\n"
  },
  {
    "path": "trusty/secure_dpu/include/trusty/secure_dpu/SecureDpu.h",
    "content": "/*\n * Copyright 2020, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n/**\n * DOC: Secure DPU\n *\n * The Secure DPU works as the persistent channel between the non-secure and the\n * secure world. The channel is established during the boot up stage of the\n * non-secure world system. In general, the established channel allows the\n * secure world applications initiate requests or notifications to the non-secure\n * world.\n *\n * For particular devices, the secure world can only perform operations on the\n * display when in the TUI session if device-specific setup is done by the\n * non-secure world. Besides, the non-secure world could allocate framebuffer\n * for the secure world application if the memory is limited in the secure world\n * on specific devices.\n *\n * Currently, supported requests are to start / stop the secure display mode and\n * to allocate framebuffer.\n *\n * This header file needs to be synced on both the Trusty and the Android\n * codebase.\n */\n\n#define SECURE_DPU_PORT_NAME \"com.android.trusty.secure_dpu\"\n#define SECURE_DPU_MAX_MSG_SIZE 64\n\n/**\n * enum secure_dpu_cmd - command identifiers for secure_fb interface\n * @SECURE_DPU_CMD_RESP_BIT:\n *      Message is a response.\n * @SECURE_DPU_CMD_REQ_SHIFT:\n *      Number of bits used by @SECURE_DPU_CMD_RESP_BIT.\n * @SECURE_DPU_CMD_START_SECURE_DISPLAY:\n *      Notify the system to start secure display mode\n * @SECURE_DPU_CMD_STOP_SECURE_DISPLAY:\n *      Notify the system to stop secure display mode\n * @SECURE_DPU_CMD_ALLOCATE_BUFFER:\n *      Request non-secure world to allocate the buffer\n */\nenum secure_dpu_cmd {\n    SECURE_DPU_CMD_RESP_BIT = 1,\n    SECURE_DPU_CMD_REQ_SHIFT = 1,\n    SECURE_DPU_CMD_START_SECURE_DISPLAY = (1 << SECURE_DPU_CMD_REQ_SHIFT),\n    SECURE_DPU_CMD_STOP_SECURE_DISPLAY = (2 << SECURE_DPU_CMD_REQ_SHIFT),\n    SECURE_DPU_CMD_ALLOCATE_BUFFER = (3 << SECURE_DPU_CMD_REQ_SHIFT),\n};\n\n/**\n * struct secure_dpu_allocate_buffer_req - payload for\n *                                         %SECURE_DPU_CMD_ALLOCATE_BUFFER\n *                                         request\n * @buffer_len: Requested length\n */\nstruct secure_dpu_allocate_buffer_req {\n    uint64_t buffer_len;\n};\n\n/**\n * struct secure_dpu_allocate_buffer_resp - payload for\n *                                          %SECURE_DPU_CMD_ALLOCATE_BUFFER\n *                                          response\n * @buffer_len: Allocated length\n */\nstruct secure_dpu_allocate_buffer_resp {\n    uint64_t buffer_len;\n};\n\n/**\n * struct secure_fb_req - common structure for secure_fb requests.\n * @cmd: Command identifier - one of &enum secure_dpu_cmd.\n */\nstruct secure_dpu_req {\n    uint32_t cmd;\n};\n\n/**\n * struct secure_dpu_resp - common structure for secure_dpu responses.\n * @cmd:    Command identifier - %SECURE_DPU_CMD_RESP_BIT or'ed with the\n *                               command identifier of the corresponding\n *                               request.\n * @status: Status of requested operation. One of &enum secure_dpu_error.\n */\nstruct secure_dpu_resp {\n    uint32_t cmd;\n    int32_t status;\n};\n\nenum secure_dpu_error {\n    SECURE_DPU_ERROR_OK = 0,\n    SECURE_DPU_ERROR_FAIL = -1,\n    SECURE_DPU_ERROR_UNINITIALIZED = -2,\n    SECURE_DPU_ERROR_PARAMETERS = -3,\n    SECURE_DPU_ERROR_NO_MEMORY = -4,\n};\n"
  },
  {
    "path": "trusty/stats/aidl/Android.bp",
    "content": "// Copyright (C) 2023 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\naidl_interface {\n    name: \"android.trusty.stats.nw.setter\",\n    unstable: true,\n    vendor_available: true,\n    srcs: [\n        \"android/trusty/stats/nw/setter/IStatsSetter.aidl\",\n    ],\n    imports: [\"android.frameworks.stats-V1\"],\n    backend: {\n        cpp: {\n            enabled: true,\n        },\n        java: {\n            enabled: false,\n            platform_apis: false,\n        },\n        ndk: {\n            enabled: false,\n        },\n    },\n}\n"
  },
  {
    "path": "trusty/stats/aidl/android/trusty/stats/nw/setter/IStatsSetter.aidl",
    "content": "//\n// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\npackage android.trusty.stats.nw.setter;\n\nimport android.frameworks.stats.IStats;\n\ninterface IStatsSetter {\n    /**\n     * Set the IStats interface facet.\n     *\n     * @param istats The IStats facet provided by the caller for the remote\n     *        service to report IStats' VendorAtom.\n     */\n    void setInterface(in IStats istats);\n}\n"
  },
  {
    "path": "trusty/stats/test/Android.bp",
    "content": "// Copyright (C) 2021 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test {\n    name: \"trusty_stats_test\",\n    vendor: true,\n    srcs: [\n        \"stats_test.cpp\",\n    ],\n    static_libs: [\n        \"libgmock\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libbinder\",\n        \"libbinder_trusty\",\n        \"libutils\",\n\n        // AIDL interface deps versions, please refer to below link\n        // https://source.android.com/docs/core/architecture/aidl/stable-aidl#module-naming-rules\n        \"android.frameworks.stats-V1-cpp\",\n        \"android.trusty.stats.nw.setter-cpp\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n    require_root: true,\n    proprietary: true,\n}\n"
  },
  {
    "path": "trusty/stats/test/README.md",
    "content": "# Development Notes\n\n*    First get [repo_pull.py and gerrit.py](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/) from aosp.\n\n*    Although this repo is not currently in Trusty’s manifest, it’s sufficient to copy these two python scripts to the root of the Trusty project and run them from there. Make sure to follow the [repo_pull installation](https://android.googlesource.com/platform/development/+/main/tools/repo_pull/#installation) steps if necessary.\n\n## Build\n\nBuild Android:\n\n```sh\nsource build/envsetup.sh\nlunch qemu_trusty_arm64-userdebug\nm\n```\n\nBuild Trusty:\n\n```sh\n./trusty/vendor/google/aosp/scripts/build.py qemu-generic-arm64-test-debug --skip-tests 2>stderr.log\n```\n\n## Trusty PORT_TEST\n\nOn QEmu:\n\n```sh\n./build-root/build-qemu-generic-arm64-test-debug/run --headless --boot-test \"com.android.trusty.stats.test\" --verbose\n```\n\nOn device: (Build for your device's debug target on both Adroid and Trusty)\n\n```sh\n/vendor/bin/trusty-ut-ctrl -D /dev/trusty-ipc-dev0 \"com.android.trusty.stats.test\"\n```\n\nOn device, in a loop:\n\n```sh\ncat << 'EOF' > metrics.sh\n#!/system/bin/sh\nTIMES=${1:-0}\nX=0\nwhile [ \"$TIMES\" -eq 0 -o \"$TIMES\" -gt \"$X\" ]\ndo\n  echo \"######################## stats.test $X \" $(( X++ ));\n  /vendor/bin/trusty-ut-ctrl -D /dev/trusty-ipc-dev0 \"com.android.trusty.stats.test\"\ndone\nEOF\n\nadb wait-for-device\nadb push metrics.sh /data/user/test/metrics.sh\nadb shell sh /data/user/test/metrics.sh\n```\n\n## Android Native Test\n\nOn QEmu:\n\n```sh\n./build-root/build-qemu-generic-arm64-test-debug/run --headless --android $ANDROID_PROJECT_ROOT --shell-command \"/data/nativetest64/vendor/trusty_stats_test/trusty_stats_test\" --verbose\n```\n\nOn device: (Build for your device's debug target on both Adroid and Trusty)\n\n```sh\n/data/nativetest64/vendor/trusty_stats_test/trusty_stats_test\n```\n\nOn device, in a loop:\n\n```sh\ncat << 'EOF' > metrics-nw.sh\n#!/system/bin/sh\nTIMES=${1:-0}\nX=0\nwhile [ \"$TIMES\" -eq 0 -o \"$TIMES\" -gt \"$X\" ]\ndo\n  echo \"######################## stats.test $X \" $(( X++ ));\n  /data/nativetest64/vendor/trusty_stats_test/trusty_stats_test\ndone\nEOF\n\nadb wait-for-device\nadb push metrics.sh /data/user/test/metrics-nw.sh\nadb shell sh /data/user/test/metrics-nw.sh\n```\n\n## Trusty Backtrace analysis\n\n\n```\n$ export A2L=./prebuilts/clang/host/linux-x86/llvm-binutils-stable/llvm-addr2line\n$ export OD=./prebuilts/clang/host/linux-x86/llvm-binutils-stable/llvm-objdump\n$ $OD -d -C build-root/build-qemu-generic-arm64-test-debug/user_tasks/trusty/user/base/app/metrics/metrics.syms.elf > objdump.lst\n$ $A2L -e build-root/build-qemu-generic-arm64-test-debug/user_tasks/trusty/user/base/app/metrics/metrics.syms.elf 0xe5104\n```\n"
  },
  {
    "path": "trusty/stats/test/stats_test.cpp",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <errno.h>\n#include <getopt.h>\n#include <gmock/gmock.h>\n#include <gtest/gtest.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/uio.h>\n#include <unistd.h>\n\n#include <condition_variable>\n#include <cstddef>\n#include <mutex>\n#include <queue>\n\n#include <android-base/expected.h>\n#include <android-base/logging.h>\n#include <android/frameworks/stats/BnStats.h>\n#include <android/frameworks/stats/IStats.h>\n#include <android/trusty/stats/nw/setter/IStatsSetter.h>\n#include <binder/RpcServer.h>\n#include <binder/RpcSession.h>\n#include <binder/RpcTransportRaw.h>\n#include <binder/RpcTransportTipcAndroid.h>\n#include <binder/RpcTrusty.h>\n#include <trusty/tipc.h>\n\n/** DOC:\n * ./build-root/build-qemu-generic-arm64-test-debug/run \\\n *       --android $ANDROID_PROJECT_ROOT \\\n *       --headless --shell-command \\\n *       \"/data/nativetest64/vendor/trusty_stats_test/trusty_stats_test\"\n *\n * adb -s emulator-5554 shell \\\n *       /data/nativetest64/vendor/trusty_stats_test/trusty_stats_test\n */\nusing ::android::base::unique_fd;\nusing ::android::binder::Status;\nusing ::android::frameworks::stats::BnStats;\nusing ::android::frameworks::stats::IStats;\nusing ::android::frameworks::stats::VendorAtom;\nusing ::android::frameworks::stats::VendorAtomValue;\nusing ::android::trusty::stats::nw::setter::IStatsSetter;\n\nconstexpr const char kTrustyDefaultDeviceName[] = \"/dev/trusty-ipc-dev0\";\nconstexpr const char kTrustyStatsSetterTest[] =\n        \"com.android.frameworks.stats.trusty.test.relayer.istats_setter\";\nconstexpr const char kTrustyStatsSetterMetrics[] =\n        \"com.android.frameworks.stats.trusty.metrics.istats_setter\";\nconstexpr const char kTrustyStatsPortTest[] = \"com.android.trusty.stats.test\";\nconstexpr const char kTrustyCrashPortTest[] = \"com.android.trusty.crashtest\";\nconstexpr const char kTrustyCrasherUuid[] = \"7ee4dddc-177a-420a-96ea-5d413d88228e:crasher\";\n\nenum TrustyAtoms : int32_t {\n    TrustyAppCrashed = 100072,\n    TrustyError = 100145,\n    TrustyStorageError = 100146\n};\n\nenum TestMsgHeader : int32_t {\n    TEST_PASSED = 0,\n    TEST_FAILED = 1,\n    TEST_MESSAGE = 2,\n};\n\nnamespace android {\nnamespace trusty {\nnamespace stats {\n\nclass Stats : public BnStats {\n  public:\n    Stats() : BnStats() {}\n\n    Status reportVendorAtom(const VendorAtom& vendorAtom) override {\n        const char* atomIdStr = vendorAtomStr(vendorAtom.atomId);\n        ALOGD(\"Vendor atom reported of type: %s\\n\", atomIdStr);\n        std::lock_guard lock(mLock);\n        mQueueVendorAtom.push(vendorAtom);\n        mCondVar.notify_one();\n        return Status::ok();\n    }\n\n    status_t getVendorAtom(VendorAtom* pVendorAtom, int64_t waitForMs) {\n        std::unique_lock lock(mLock);\n        while (mQueueVendorAtom.empty()) {\n            auto rc = mCondVar.wait_for(lock, std::chrono::milliseconds(waitForMs));\n            if (rc == std::cv_status::timeout) {\n                return TIMED_OUT;\n            }\n        }\n        *pVendorAtom = mQueueVendorAtom.front();\n        mQueueVendorAtom.pop();\n        return NO_ERROR;\n    }\n\n  private:\n    const char* vendorAtomStr(int32_t atomId) {\n        switch (atomId) {\n            case TrustyAtoms::TrustyAppCrashed:\n                return \"TrustyAtoms::TrustyAppCrashed\";\n            case TrustyAtoms::TrustyError:\n                return \"TrustyAtoms::TrustyError\";\n            case TrustyAtoms::TrustyStorageError:\n                return \"TrustyAtoms::TrustyStorageError\";\n            default:\n                return \"unknown TrustyAtoms type\";\n        }\n    }\n    std::mutex mLock;\n    std::condition_variable mCondVar;\n    std::queue<VendorAtom> mQueueVendorAtom;\n};\n\nclass TrustyStatsTestBase : public ::testing::Test {\n  protected:\n    TrustyStatsTestBase(std::string&& portNameStatsSetter, std::string&& portNamePortTest)\n        : mPortTestFd(-1),\n          mPortNameStatsSetter(std::move(portNameStatsSetter)),\n          mPortNamePortTest(std::move(portNamePortTest)) {}\n\n    void SetUp() override {\n        // Commenting out the server portion because we do not have any direct\n        // incoming call Calls from TA are currently being handled on the mSession's\n        // extra thread. android::sp<::android::RpcServer> server =\n        // ::android::RpcServer::make(::android::RpcTransportCtxFactoryRaw::make());\n\n        mStats = android::sp<Stats>::make();\n        // Increasing number of incoming threads on mSession to be able to receive\n        // callbacks\n        auto session_initializer = [](sp<RpcSession>& session) {\n            session->setMaxIncomingThreads(1);\n        };\n\n        ASSERT_FALSE(mSession);\n        mSession = RpcTrustyConnectWithSessionInitializer(\n                kTrustyDefaultDeviceName, mPortNameStatsSetter.c_str(), session_initializer);\n        ASSERT_TRUE(mSession);\n\n        auto root = mSession->getRootObject();\n        ASSERT_TRUE(root);\n        auto statsSetter = IStatsSetter::asInterface(root);\n        ASSERT_TRUE(statsSetter);\n        statsSetter->setInterface(mStats);\n    }\n    void TearDown() override {\n        // close connection to unitest app\n        if (mPortTestFd != -1) {\n            tipc_close(mPortTestFd);\n        }\n        mPortTestFd = -1;\n\n        if (mSession) {\n            // shutdownAndWait here races with sending out the DecStrong\n            // messages after reportVendorAtom returns, so we delay it a little\n            // bit to give the messages time to go out over the transport\n            usleep(50000);\n            ASSERT_TRUE(mSession->shutdownAndWait(true));\n        }\n        mSession.clear();\n        mStats.clear();\n    }\n    void StartPortTest() {\n        // connect to unitest app\n        mPortTestFd = tipc_connect(kTrustyDefaultDeviceName, mPortNamePortTest.c_str());\n        if (mPortTestFd < 0) {\n            ALOGE(\"Failed to connect to '%s' app: %s\\n\", kTrustyStatsPortTest,\n                  strerror(-mPortTestFd));\n        }\n        ASSERT_GT(mPortTestFd, 0);\n    }\n    void WaitPortTestDone() {\n        // wait for test to complete\n        char rxBuf[1024];\n        const char prolog[] = \"Trusty PORT_TEST:\";\n        strncpy(rxBuf, prolog, sizeof(prolog) - 1);\n        char* pRxBuf = rxBuf + sizeof(prolog) - 1;\n        size_t remainingBufSize = sizeof(rxBuf) - sizeof(prolog) - 1;\n\n        ASSERT_NE(mPortTestFd, -1);\n        for (;;) {\n            int rc = read(mPortTestFd, pRxBuf, remainingBufSize);\n            ASSERT_GT(rc, 0);\n            ASSERT_LT(rc, (int)remainingBufSize);\n            if (pRxBuf[0] == TEST_PASSED) {\n                break;\n            } else if (pRxBuf[0] == TEST_FAILED) {\n                break;\n            } else if (pRxBuf[0] == TEST_MESSAGE) {\n                pRxBuf[0] = ' ';\n                write(STDOUT_FILENO, rxBuf, rc + sizeof(prolog) - 1);\n            } else {\n                ALOGE(\"Bad message header: %d\\n\", rxBuf[0]);\n                break;\n            }\n        }\n        ASSERT_EQ(pRxBuf[0], TEST_PASSED);\n    }\n\n    android::sp<Stats> mStats;\n\n  private:\n    android::sp<RpcSession> mSession;\n    int mPortTestFd;\n    std::string mPortNameStatsSetter;\n    std::string mPortNamePortTest;\n};\n\nclass TrustyStatsTest : public TrustyStatsTestBase {\n  protected:\n    TrustyStatsTest() : TrustyStatsTestBase(kTrustyStatsSetterTest, kTrustyStatsPortTest) {}\n};\n\nclass TrustyMetricsCrashTest : public TrustyStatsTestBase {\n  protected:\n    TrustyMetricsCrashTest()\n        : TrustyStatsTestBase(kTrustyStatsSetterMetrics, kTrustyCrashPortTest) {}\n};\n\nTEST_F(TrustyStatsTest, CheckAtoms) {\n    int atomAppCrashedCnt = 0;\n    int atomStorageErrorCnt = 0;\n    int atomTrustyErrorCnt = 0;\n    uint64_t blockForMs = 500;\n    StartPortTest();\n    WaitPortTestDone();\n    for (;;) {\n        VendorAtom vendorAtom;\n        auto status = mStats->getVendorAtom(&vendorAtom, blockForMs);\n        ASSERT_THAT(status, ::testing::AnyOf(NO_ERROR, TIMED_OUT));\n        if (status == TIMED_OUT) {\n            // No more atoms\n            break;\n        }\n\n        ASSERT_THAT(vendorAtom.atomId,\n                    ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),\n                                     ::testing::Eq(TrustyAtoms::TrustyError),\n                                     ::testing::Eq(TrustyAtoms::TrustyStorageError)));\n        ASSERT_EQ(String8(vendorAtom.reverseDomainName), \"google.android.trusty\");\n        switch (vendorAtom.atomId) {\n            case TrustyAtoms::TrustyAppCrashed:\n                ++atomAppCrashedCnt;\n                ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),\n                          \"5247d19b-cf09-4272-a450-3ef20dbefc14\");\n                break;\n            case TrustyAtoms::TrustyStorageError:\n                ++atomStorageErrorCnt;\n                ASSERT_EQ(vendorAtom.values[0].get<VendorAtomValue::intValue>(), 5);\n                ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()),\n                          \"5247d19b-cf09-4272-a450-3ef20dbefc14\");\n                ASSERT_EQ(String8(vendorAtom.values[2].get<VendorAtomValue::stringValue>()),\n                          \"5247d19b-cf09-4272-a450-3ef20dbefc14\");\n                ASSERT_EQ(vendorAtom.values[3].get<VendorAtomValue::intValue>(), 1);\n                ASSERT_EQ(vendorAtom.values[4].get<VendorAtomValue::intValue>(), 3);\n                ASSERT_EQ(vendorAtom.values[5].get<VendorAtomValue::longValue>(),\n                          0x4BCDEFABBAFEDCBALL);\n                ASSERT_EQ(vendorAtom.values[6].get<VendorAtomValue::intValue>(), 4);\n                ASSERT_EQ(vendorAtom.values[7].get<VendorAtomValue::longValue>(), 1023);\n                break;\n            case TrustyAtoms::TrustyError:\n                ++atomTrustyErrorCnt;\n                break;\n            default:\n                FAIL() << \"Unknown vendor atom ID: \" << vendorAtom.atomId;\n                break;\n        }\n    };\n    ASSERT_EQ(atomAppCrashedCnt, 1);\n    ASSERT_EQ(atomStorageErrorCnt, 1);\n    ASSERT_EQ(atomTrustyErrorCnt, 0);\n}\n\nTEST_F(TrustyMetricsCrashTest, CheckTrustyCrashAtoms) {\n    const std::vector<uint32_t> kExpectedCrashReasonsArm64{\n            0x00000001U,  // exit_failure (twice)\n            0x00000001U,\n            0x92000004U,  // read_null_ptr\n            0xf200002aU,  // brk_instruction\n            0x92000004U,  // read_bad_ptr\n            0x92000044U,  // crash_write_bad_ptr\n            0x9200004fU,  // crash_write_ro_ptr\n            0x8200000fU,  // crash_exec_rodata\n            0x8200000fU,  // crash_exec_data\n    };\n    const std::vector<uint32_t> kExpectedCrashReasonsArm32{\n            0x00000001U,  // exit_failure (twice)\n            0x00000001U,\n            0x20000007U,  // read_null_ptr\n            0x20000007U,  // read_bad_ptr\n            0x20000807U,  // crash_write_bad_ptr\n            0x2000080fU,  // crash_write_ro_ptr\n            0x3000000fU,  // crash_exec_rodata\n            0x3000000fU,  // crash_exec_data\n    };\n\n    int expectedAtomCnt = 7;\n    int atomAppCrashedCnt = 0;\n    int atomStorageErrorCnt = 0;\n    int atomTrustyErrorCnt = 0;\n    std::vector<uint32_t> atomCrashReasons;\n    uint64_t blockForMs = 500;\n    StartPortTest();\n    WaitPortTestDone();\n    for (;;) {\n        VendorAtom vendorAtom;\n        auto status = mStats->getVendorAtom(&vendorAtom, blockForMs);\n        ASSERT_THAT(status, ::testing::AnyOf(NO_ERROR, TIMED_OUT));\n        if (status == TIMED_OUT) {\n            // No more atoms\n            break;\n        }\n\n        ASSERT_THAT(vendorAtom.atomId,\n                    ::testing::AnyOf(::testing::Eq(TrustyAtoms::TrustyAppCrashed),\n                                     ::testing::Eq(TrustyAtoms::TrustyError),\n                                     ::testing::Eq(TrustyAtoms::TrustyStorageError)));\n        ASSERT_EQ(String8(vendorAtom.reverseDomainName), \"google.android.trusty\");\n\n        switch (vendorAtom.atomId) {\n            case TrustyAtoms::TrustyAppCrashed:\n                ++atomAppCrashedCnt;\n                ASSERT_EQ(String8(vendorAtom.values[0].get<VendorAtomValue::stringValue>()),\n                          kTrustyCrasherUuid);\n                atomCrashReasons.push_back(vendorAtom.values[1].get<VendorAtomValue::intValue>());\n                break;\n            case TrustyAtoms::TrustyStorageError:\n                ++atomStorageErrorCnt;\n                break;\n            case TrustyAtoms::TrustyError:\n                ++atomTrustyErrorCnt;\n                ASSERT_EQ(String8(vendorAtom.values[1].get<VendorAtomValue::stringValue>()), \"\");\n                break;\n            default:\n                FAIL() << \"Unknown vendor atom ID: \" << vendorAtom.atomId;\n        }\n    }\n    ASSERT_GE(atomAppCrashedCnt, expectedAtomCnt - 1);\n    ASSERT_EQ(atomStorageErrorCnt, 0);\n    // There is one dropped event left over from Trusty boot,\n    // it may show up here\n    ASSERT_LE(atomTrustyErrorCnt, 1);\n    ASSERT_THAT(atomCrashReasons,\n                ::testing::AnyOf(kExpectedCrashReasonsArm64, kExpectedCrashReasonsArm32));\n};\n\n}  // namespace stats\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/storage/lib/Android.bp",
    "content": "//\n// Copyright (C) 2015 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_library_static {\n    name: \"libtrustystorage\",\n    vendor: true,\n\n    srcs: [\"storage.c\"],\n\n    export_include_dirs: [\"include\"],\n\n    static_libs: [\n        \"libtrusty\",\n        \"libtrustystorageinterface\",\n    ],\n    shared_libs: [\n        \"liblog\",\n    ],\n\n    cflags: [\n        \"-fvisibility=hidden\",\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n"
  },
  {
    "path": "trusty/storage/lib/include/trusty/lib/storage.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <interface/storage/storage.h>\n#include <stdint.h>\n\n#define STORAGE_MAX_NAME_LENGTH_BYTES 159\n\n__BEGIN_DECLS\n\ntypedef uint32_t storage_session_t;\ntypedef uint64_t file_handle_t;\ntypedef uint64_t storage_off_t;\n\n#define STORAGE_INVALID_SESSION ((storage_session_t)-1)\n\n/**\n * storage_ops_flags - storage related operation flags\n * @STORAGE_OP_COMPLETE: forces to commit current transaction\n */\nenum storage_ops_flags {\n    STORAGE_OP_COMPLETE = 0x1,\n};\n\n/**\n * storage_open_session() - Opens a storage session.\n * @device:    device node for talking with Trusty\n * @session_p: pointer to location in which to store session handle\n *             in case of success.\n *\n * Return: 0 on success, or an error code < 0 on failure.\n */\nint storage_open_session(const char *device, storage_session_t *session_p, const char *port);\n\n/**\n * storage_close_session() - Closes the session.\n * @session: the session to close\n */\nvoid storage_close_session(storage_session_t session);\n\n/**\n * storage_open_file() - Opens a file\n * @session:  the storage_session_t returned from a call to storage_open_session\n * @handle_p: pointer to location in which to store file handle in case of success\n * @name:     a null-terminated string identifier of the file to open.\n *            Cannot be more than STORAGE_MAX_NAME_LENGTH_BYTES in length.\n * @flags:    A bitmask consisting any storage_file_flag value or'ed together:\n * - STORAGE_FILE_OPEN_CREATE:           if this file does not exist, create it.\n * - STORAGE_FILE_OPEN_CREATE_EXCLUSIVE: when specified, opening file with\n *                                       STORAGE_OPEN_FILE_CREATE flag will\n *                                       fail if the file already exists.\n *                                       Only meaningful if used in combination\n *                                       with STORAGE_FILE_OPEN_CREATE flag.\n * - STORAGE_FILE_OPEN_TRUNCATE: if this file already exists, discard existing\n *                               content and open it as a new file. No change\n *                               in semantics if the  file does not exist.\n * @opflags: a combination of @storage_op_flags\n *\n * Return: 0 on success, or an error code < 0 on failure.\n */\nint storage_open_file(storage_session_t session, file_handle_t *handle_p,\n                      const char *name, uint32_t flags, uint32_t opflags);\n\n/**\n * storage_close_file() - Closes a file.\n * @handle: the file_handle_t retrieved from storage_open_file\n */\nvoid storage_close_file(file_handle_t handle);\n\n/**\n * storage_delete_file - Deletes a file.\n * @session: the storage_session_t returned from a call to storage_open_session\n * @name: the name of the file to delete\n * @opflags: a combination of @storage_op_flags\n *\n * Return: 0 on success, or an error code < 0 on failure.\n */\nint storage_delete_file(storage_session_t session, const char *name,\n                        uint32_t opflags);\n\n/**\n * storage_read() - Reads a file at a given offset.\n * @handle: the file_handle_t retrieved from storage_open_file\n * @off: the start offset from whence to read in the file\n * @buf: the buffer in which to write the data read\n * @size: the size of buf and number of bytes to read\n *\n * Return: the number of bytes read on success, negative error code on failure\n */\nssize_t storage_read(file_handle_t handle,\n                     storage_off_t off, void *buf, size_t size);\n\n/**\n * storage_write() - Writes to a file at a given offset. Grows the file if necessary.\n * @handle: the file_handle_t retrieved from storage_open_file\n * @off: the start offset from whence to write in the file\n * @buf: the buffer containing the data to write\n * @size: the size of buf and number of bytes to write\n * @opflags: a combination of @storage_op_flags\n *\n * Return: the number of bytes written on success, negative error code on failure\n */\nssize_t storage_write(file_handle_t handle,\n                      storage_off_t off, const void *buf, size_t size,\n                      uint32_t opflags);\n\n/**\n * storage_set_file_size() - Sets the size of the file.\n * @handle: the file_handle_t retrieved from storage_open_file\n * @off: the number of bytes to set as the new size of the file\n * @opflags: a combination of @storage_op_flags\n *\n * Return: 0 on success, negative error code on failure.\n */\nint storage_set_file_size(file_handle_t handle, storage_off_t file_size,\n                          uint32_t opflags);\n\n/**\n * storage_get_file_size() - Gets the size of the file.\n * @session: the storage_session_t returned from a call to storage_open_session\n * @handle: the file_handle_t retrieved from storage_open_file\n * @size: pointer to storage_off_t in which to store the file size\n *\n * Return: 0 on success, negative error code on failure.\n */\nint storage_get_file_size(file_handle_t handle, storage_off_t *size);\n\n\n/**\n * storage_end_transaction: End current transaction\n * @session: the storage_session_t returned from a call to storage_open_session\n * @complete: if true, commit current transaction, discard it otherwise\n *\n * Return: 0 on success, negative error code on failure.\n */\nint storage_end_transaction(storage_session_t session, bool complete);\n\n\n__END_DECLS\n"
  },
  {
    "path": "trusty/storage/lib/storage.c",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"trusty_storage_client\"\n\n#include <errno.h>\n#include <stdarg.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <string.h>\n#include <sys/uio.h>\n\n#include <log/log.h>\n#include <trusty/tipc.h>\n#include <trusty/lib/storage.h>\n\n#define MAX_CHUNK_SIZE 4040\n\nstatic inline file_handle_t make_file_handle(storage_session_t s, uint32_t fid)\n{\n    return ((uint64_t)s << 32) | fid;\n}\n\nstatic inline storage_session_t _to_session(file_handle_t fh)\n{\n    return (storage_session_t)(fh >> 32);\n}\n\nstatic inline uint32_t _to_handle(file_handle_t fh)\n{\n    return (uint32_t) fh;\n}\n\nstatic inline uint32_t _to_msg_flags(uint32_t opflags)\n{\n    uint32_t msg_flags = 0;\n\n    if (opflags & STORAGE_OP_COMPLETE)\n        msg_flags |= STORAGE_MSG_FLAG_TRANSACT_COMPLETE;\n\n    return msg_flags;\n}\n\nstatic ssize_t check_response(struct storage_msg *msg, ssize_t res)\n{\n    if (res < 0)\n        return res;\n\n    if ((size_t)res < sizeof(*msg)) {\n        ALOGE(\"invalid msg length (%zd < %zd)\\n\", res, sizeof(*msg));\n        return -EIO;\n    }\n\n    ALOGV(\"cmd 0x%x: server returned %u\\n\", msg->cmd, msg->result);\n\n    switch(msg->result) {\n        case STORAGE_NO_ERROR:\n            return res - sizeof(*msg);\n\n        case STORAGE_ERR_NOT_FOUND:\n            return -ENOENT;\n\n        case STORAGE_ERR_EXIST:\n            return -EEXIST;\n\n        case STORAGE_ERR_NOT_VALID:\n            return -EINVAL;\n\n        case STORAGE_ERR_UNIMPLEMENTED:\n            ALOGE(\"cmd 0x%x: is unhandles command\\n\", msg->cmd);\n            return -EINVAL;\n\n        case STORAGE_ERR_ACCESS:\n             return -EACCES;\n\n        case STORAGE_ERR_TRANSACT:\n             return -EBUSY;\n\n        case STORAGE_ERR_GENERIC:\n            ALOGE(\"cmd 0x%x: internal server error\\n\", msg->cmd);\n            return -EIO;\n\n        default:\n            ALOGE(\"cmd 0x%x: unhandled server response %u\\n\",\n                   msg->cmd, msg->result);\n    }\n\n    return -EIO;\n}\n\nstatic ssize_t send_reqv(storage_session_t session,\n                         const struct iovec *tx_iovs, uint tx_iovcnt,\n                         const struct iovec *rx_iovs, uint rx_iovcnt)\n{\n    ssize_t rc;\n\n    rc = writev(session, tx_iovs, tx_iovcnt);\n    if (rc < 0) {\n        rc = -errno;\n        ALOGE(\"failed to send request: %s\\n\", strerror(errno));\n        return rc;\n    }\n\n    rc = readv(session, rx_iovs, rx_iovcnt);\n    if (rc < 0) {\n        rc = -errno;\n        ALOGE(\"failed to recv response: %s\\n\", strerror(errno));\n        return rc;\n    }\n\n    return rc;\n}\n\nint storage_open_session(const char *device, storage_session_t *session_p,\n                         const char *port)\n{\n    int rc = tipc_connect(device, port);\n    if (rc < 0)\n        return rc;\n    *session_p = (storage_session_t) rc;\n    return 0;\n}\n\nvoid storage_close_session(storage_session_t session)\n{\n    tipc_close(session);\n}\n\n\nint storage_open_file(storage_session_t session, file_handle_t *handle_p, const char *name,\n                      uint32_t flags, uint32_t opflags)\n{\n    struct storage_msg msg = { .cmd = STORAGE_FILE_OPEN, .flags = _to_msg_flags(opflags)};\n    struct storage_file_open_req req = { .flags = flags };\n    struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)name, strlen(name)}};\n    struct storage_file_open_resp rsp = { 0 };\n    struct iovec rx[2] = {{&msg, sizeof(msg)}, {&rsp, sizeof(rsp)}};\n\n    ssize_t rc = send_reqv(session, tx, 3, rx, 2);\n    rc = check_response(&msg, rc);\n    if (rc < 0)\n        return rc;\n\n    if ((size_t)rc != sizeof(rsp)) {\n        ALOGE(\"%s: invalid response length (%zd != %zd)\\n\", __func__, rc, sizeof(rsp));\n        return -EIO;\n    }\n\n    *handle_p = make_file_handle(session, rsp.handle);\n    return 0;\n}\n\nvoid storage_close_file(file_handle_t fh)\n{\n    struct storage_msg msg = { .cmd = STORAGE_FILE_CLOSE };\n    struct storage_file_close_req req = { .handle = _to_handle(fh)};\n    struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};\n    struct iovec rx[1] = {{&msg, sizeof(msg)}};\n\n    ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 1);\n    rc = check_response(&msg, rc);\n    if (rc < 0) {\n        ALOGE(\"close file failed (%d)\\n\", (int)rc);\n    }\n}\n\nint storage_delete_file(storage_session_t session, const char *name, uint32_t opflags)\n{\n    struct storage_msg msg = { .cmd = STORAGE_FILE_DELETE, .flags = _to_msg_flags(opflags)};\n    struct storage_file_delete_req req = { .flags = 0, };\n    struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)name, strlen(name)}};\n    struct iovec rx[1] = {{&msg, sizeof(msg)}};\n\n    ssize_t rc = send_reqv(session, tx, 3, rx, 1);\n    return check_response(&msg, rc);\n}\n\nstatic int _read_chunk(file_handle_t fh, storage_off_t off, void *buf, size_t size)\n{\n    struct storage_msg msg = { .cmd = STORAGE_FILE_READ };\n    struct storage_file_read_req req = { .handle = _to_handle(fh), .size = size, .offset = off };\n    struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};\n    struct iovec rx[2] = {{&msg, sizeof(msg)}, {buf, size}};\n\n    ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 2);\n    return check_response(&msg, rc);\n}\n\nssize_t storage_read(file_handle_t fh, storage_off_t off, void *buf, size_t size)\n{\n    int rc;\n    size_t bytes_read = 0;\n    size_t chunk = MAX_CHUNK_SIZE;\n    uint8_t *ptr = buf;\n\n    while (size) {\n        if (chunk > size)\n            chunk = size;\n        rc = _read_chunk(fh, off, ptr, chunk);\n        if (rc < 0)\n            return rc;\n        if (rc == 0)\n            break;\n        off += rc;\n        ptr += rc;\n        bytes_read += rc;\n        size -= rc;\n    }\n    return bytes_read;\n}\n\nstatic int _write_req(file_handle_t fh, storage_off_t off,\n                      const void *buf, size_t size, uint32_t msg_flags)\n{\n    struct storage_msg msg = { .cmd = STORAGE_FILE_WRITE, .flags = msg_flags, };\n    struct storage_file_write_req req = { .handle = _to_handle(fh), .offset = off, };\n    struct iovec tx[3] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}, {(void *)buf, size}};\n    struct iovec rx[1] = {{&msg, sizeof(msg)}};\n\n    ssize_t rc = send_reqv(_to_session(fh), tx, 3, rx, 1);\n    rc = check_response(&msg, rc);\n    return rc < 0 ? rc : size;\n}\n\nssize_t storage_write(file_handle_t fh, storage_off_t off,\n                      const void *buf, size_t size, uint32_t opflags)\n{\n    int rc;\n    size_t bytes_written = 0;\n    size_t chunk = MAX_CHUNK_SIZE;\n    const uint8_t *ptr = buf;\n    uint32_t msg_flags = _to_msg_flags(opflags & ~STORAGE_OP_COMPLETE);\n\n    while (size) {\n        if (chunk >= size) {\n            /* last chunk in sequence */\n            chunk = size;\n            msg_flags = _to_msg_flags(opflags);\n        }\n        rc = _write_req(fh, off, ptr, chunk, msg_flags);\n        if (rc < 0)\n            return rc;\n        if ((size_t)rc != chunk) {\n            ALOGE(\"got partial write (%d)\\n\", (int)rc);\n            return -EIO;\n        }\n        off += chunk;\n        ptr += chunk;\n        bytes_written += chunk;\n        size -= chunk;\n    }\n    return bytes_written;\n}\n\nint storage_set_file_size(file_handle_t fh, storage_off_t file_size, uint32_t opflags)\n{\n    struct storage_msg msg = { .cmd = STORAGE_FILE_SET_SIZE, .flags = _to_msg_flags(opflags)};\n    struct storage_file_set_size_req req = { .handle = _to_handle(fh), .size = file_size, };\n    struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};\n    struct iovec rx[1] = {{&msg, sizeof(msg)}};\n\n    ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 1);\n    return check_response(&msg, rc);\n}\n\nint storage_get_file_size(file_handle_t fh, storage_off_t *size_p)\n{\n    struct storage_msg msg = { .cmd = STORAGE_FILE_GET_SIZE };\n    struct storage_file_get_size_req  req = { .handle = _to_handle(fh), };\n    struct iovec tx[2] = {{&msg, sizeof(msg)}, {&req, sizeof(req)}};\n    struct storage_file_get_size_resp rsp;\n    struct iovec rx[2] = {{&msg, sizeof(msg)}, {&rsp, sizeof(rsp)}};\n\n    ssize_t rc = send_reqv(_to_session(fh), tx, 2, rx, 2);\n    rc = check_response(&msg, rc);\n    if (rc < 0)\n        return rc;\n\n    if ((size_t)rc != sizeof(rsp)) {\n        ALOGE(\"%s: invalid response length (%zd != %zd)\\n\", __func__, rc, sizeof(rsp));\n        return -EIO;\n    }\n\n    *size_p = rsp.size;\n    return 0;\n}\n\nint storage_end_transaction(storage_session_t session, bool complete)\n{\n    struct storage_msg msg = {\n        .cmd = STORAGE_END_TRANSACTION,\n        .flags = complete ? STORAGE_MSG_FLAG_TRANSACT_COMPLETE : 0,\n    };\n    struct iovec iov = {&msg, sizeof(msg)};\n\n    ssize_t rc = send_reqv(session, &iov, 1, &iov, 1);\n    return check_response(&msg, rc);\n}\n"
  },
  {
    "path": "trusty/storage/proxy/Android.bp",
    "content": "//\n// Copyright (C) 2016 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"storageproxyd.defaults\",\n    srcs: [\n        \"checkpoint_handling.cpp\",\n        \"ipc.c\",\n        \"rpmb.c\",\n        \"storage.c\",\n        \"proxy.c\",\n        \"watchdog.cpp\",\n    ],\n\n    shared_libs: [\n        \"libbase\",\n        \"libbinder_ndk\",\n        \"libcutils\",\n        \"liblog\",\n        \"libhardware_legacy\",\n    ],\n    header_libs: [\n        \"libgsi_headers\",\n    ],\n\n    static_libs: [\n        \"libfstab\",\n        \"libtrustystorageinterface\",\n        \"libtrusty\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\ncc_binary {\n    name: \"storageproxyd\",\n    defaults: [\"storageproxyd.defaults\"],\n    vendor: true,\n    // vendor variant requires this flag\n    cflags: [\"-DVENDOR_FS_READY_PROPERTY\"],\n}\n\ncc_binary {\n    name: \"storageproxyd.system\",\n    defaults: [\"storageproxyd.defaults\"],\n    system_ext_specific: true,\n}\n"
  },
  {
    "path": "trusty/storage/proxy/checkpoint_handling.cpp",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"checkpoint_handling.h\"\n#include \"log.h\"\n\n#include <fstab/fstab.h>\n#include <unistd.h>\n#include <cstring>\n#include <string>\n\n#include <libgsi/libgsi.h>\n\nnamespace {\n\nbool checkpointingDoneForever = false;\n\n}  // namespace\n\nint is_data_checkpoint_active(bool* active) {\n    if (!active) {\n        ALOGE(\"active out parameter is null\");\n        return 0;\n    }\n\n    *active = false;\n\n    if (checkpointingDoneForever) {\n        return 0;\n    }\n\n    android::fs_mgr::Fstab procMounts;\n    bool success = android::fs_mgr::ReadFstabFromFile(\"/proc/mounts\", &procMounts);\n    if (!success) {\n        ALOGE(\"Could not parse /proc/mounts\\n\");\n        /* Really bad. Tell the caller to abort the write. */\n        return -1;\n    }\n\n    android::fs_mgr::FstabEntry* dataEntry =\n            android::fs_mgr::GetEntryForMountPoint(&procMounts, \"/data\");\n    if (dataEntry == NULL) {\n        ALOGE(\"/data is not mounted yet\\n\");\n        return 0;\n    }\n\n    /* We can't handle e.g., ext4. Nothing we can do about it for now. */\n    if (dataEntry->fs_type != \"f2fs\") {\n        ALOGW(\"Checkpoint status not supported for filesystem %s\\n\", dataEntry->fs_type.c_str());\n        checkpointingDoneForever = true;\n        return 0;\n    }\n\n    /*\n     * The data entry looks like \"... blah,checkpoint=disable:0,blah ...\".\n     * checkpoint=disable means checkpointing is on (yes, arguably reversed).\n     */\n    size_t checkpointPos = dataEntry->fs_options.find(\"checkpoint=disable\");\n    if (checkpointPos == std::string::npos) {\n        /* Assumption is that once checkpointing turns off, it stays off */\n        checkpointingDoneForever = true;\n    } else {\n        *active = true;\n    }\n\n    return 0;\n}\n\n/**\n * is_gsi_running() - Check if a GSI image is running via DSU.\n *\n * This function is equivalent to android::gsi::IsGsiRunning(), but this API is\n * not yet vendor-accessible although the underlying metadata file is.\n *\n */\nbool is_gsi_running() {\n    /* TODO(b/210501710): Expose GSI image running state to vendor storageproxyd */\n    return !access(android::gsi::kGsiBootedIndicatorFile, F_OK);\n}\n"
  },
  {
    "path": "trusty/storage/proxy/checkpoint_handling.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdbool.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/**\n * is_data_checkpoint_active() - Check for an active, uncommitted checkpoint of\n * /data. If a checkpoint is active, storage should not commit any\n * rollback-protected writes to /data.\n * @active: Out parameter that will be set to the result of the check.\n *\n * Return: 0 if active was set and is valid, non-zero otherwise.\n */\nint is_data_checkpoint_active(bool* active);\n\nbool is_gsi_running();\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "trusty/storage/proxy/ipc.c",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <assert.h>\n#include <errno.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/uio.h>\n#include <unistd.h>\n\n#include <trusty/tipc.h>\n\n#include \"ipc.h\"\n#include \"log.h\"\n\n#define MAX_RECONNECT_RETRY_COUNT 5\n#define TRUSTY_RECONNECT_TIMEOUT_SEC 5\n\nstatic int tipc_fd = -1;\n\nint ipc_connect(const char *device, const char *port)\n{\n    int rc;\n    uint retry_cnt = 0;\n\n    assert(tipc_fd == -1);\n\n    while(true) {\n        rc = tipc_connect(device, port);\n        if (rc >= 0)\n            break;\n\n        ALOGE(\"failed (%d) to connect to storage server\\n\", rc);\n        if (++retry_cnt > MAX_RECONNECT_RETRY_COUNT) {\n            ALOGE(\"max number of reconnect retries (%d) has been reached\\n\",\n                   retry_cnt);\n            return -1;\n        }\n        sleep(TRUSTY_RECONNECT_TIMEOUT_SEC);\n    }\n    tipc_fd = rc;\n    return 0;\n}\n\nvoid ipc_disconnect(void)\n{\n    assert(tipc_fd >=  0);\n\n    tipc_close(tipc_fd);\n    tipc_fd = -1;\n}\n\nssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len)\n{\n    ssize_t rc;\n    struct iovec iovs[2] = {{msg, sizeof(*msg)}, {req_buf, req_buf_len}};\n\n    assert(tipc_fd >=  0);\n\n    rc = readv(tipc_fd, iovs, 2);\n    if (rc < 0) {\n        ALOGE(\"failed to read request: %s\\n\", strerror(errno));\n        return rc;\n    }\n\n   /* check for minimum size */\n   if ((size_t)rc < sizeof(*msg)) {\n       ALOGE(\"message is too short (%zu bytes received)\\n\", rc);\n       return -1;\n   }\n\n   /* check for message completeness */\n   if (msg->size != (uint32_t)rc) {\n       ALOGE(\"inconsistent message size [cmd=%d] (%u != %u)\\n\",\n             msg->cmd, msg->size, (uint32_t)rc);\n       return -1;\n   }\n\n   return rc - sizeof(*msg);\n}\n\nint ipc_respond(struct storage_msg *msg, void *out, size_t out_size)\n{\n    ssize_t rc;\n    struct iovec iovs[2] = {{msg, sizeof(*msg)}, {out, out_size}};\n\n    assert(tipc_fd >=  0);\n\n    msg->cmd |= STORAGE_RESP_BIT;\n\n    rc = writev(tipc_fd, iovs, out ? 2 : 1);\n    if (rc < 0) {\n        ALOGE(\"error sending response 0x%x: %s\\n\",\n              msg->cmd, strerror(errno));\n        return -1;\n    }\n\n    return 0;\n}\n\n\n"
  },
  {
    "path": "trusty/storage/proxy/ipc.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <interface/storage/storage.h>\n#include <stdint.h>\n\nint ipc_connect(const char *device, const char *service_name);\nvoid ipc_disconnect(void);\nssize_t ipc_get_msg(struct storage_msg *msg, void *req_buf, size_t req_buf_len);\nint ipc_respond(struct storage_msg *msg, void *out, size_t out_size);\n"
  },
  {
    "path": "trusty/storage/proxy/log.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"storageproxyd\"\n\n#include <log/log.h>\n\n"
  },
  {
    "path": "trusty/storage/proxy/proxy.c",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <errno.h>\n#include <getopt.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/capability.h>\n#include <sys/prctl.h>\n#include <sys/stat.h>\n#include <unistd.h>\n\n#include <android/binder_process.h>\n#include <cutils/android_filesystem_config.h>\n\n#include \"checkpoint_handling.h\"\n#include \"ipc.h\"\n#include \"log.h\"\n#include \"rpmb.h\"\n#include \"storage.h\"\n#include \"watchdog.h\"\n\n#define REQ_BUFFER_SIZE 4096\nstatic uint8_t req_buffer[REQ_BUFFER_SIZE + 1];\n\nstatic const char* ss_data_root;\nstatic const char* trusty_devname;\nstatic const char* rpmb_devname;\nstatic const char* ss_srv_name = STORAGE_DISK_PROXY_PORT;\nstatic const char* max_file_size_from;\n\nstatic enum dev_type dev_type = MMC_RPMB;\n\n/* List head for storage mapping, elements added at init, and never removed */\nstatic struct storage_mapping_node* storage_mapping_head;\n\nstatic enum dev_type parse_dev_type(const char* dev_type_name) {\n    if (!strcmp(dev_type_name, \"mmc\")) {\n        return MMC_RPMB;\n    } else if (!strcmp(dev_type_name, \"virt\")) {\n        return VIRT_RPMB;\n    } else if (!strcmp(dev_type_name, \"sock\")) {\n        return SOCK_RPMB;\n    } else if (!strcmp(dev_type_name, \"ufs\")) {\n        return UFS_RPMB;\n    } else {\n        return UNKNOWN_RPMB;\n    }\n}\n\nstatic int parse_and_append_file_mapping(const char* file_mapping) {\n    if (file_mapping == NULL) {\n        ALOGE(\"Provided file mapping is null\\n\");\n        return -1;\n    }\n    char* file_mapping_dup = strdup(file_mapping);\n    if (file_mapping_dup == NULL) {\n        ALOGE(\"Couldn't duplicate string: %s\\n\", file_mapping);\n        return -1;\n    }\n    const char* file_name = strtok(file_mapping_dup, \":\");\n    if (file_name == NULL) {\n        ALOGE(\"No file name found\\n\");\n        return -1;\n    }\n    const char* backing_storage = strtok(NULL, \":\");\n    if (backing_storage == NULL) {\n        ALOGE(\"No backing storage found\\n\");\n        return -1;\n    }\n\n    struct storage_mapping_node* new_node = malloc(sizeof(struct storage_mapping_node));\n    if (new_node == NULL) {\n        ALOGE(\"Couldn't allocate additional storage_mapping_node\\n\");\n        return -1;\n    }\n    *new_node = (struct storage_mapping_node){.file_name = file_name,\n                                              .backing_storage = backing_storage,\n                                              .next = storage_mapping_head,\n                                              .fd = -1};\n    storage_mapping_head = new_node;\n    return 0;\n}\n\nstatic const char* _sopts = \"hp:d:r:t:m:f:\";\nstatic const struct option _lopts[] = {{\"help\", no_argument, NULL, 'h'},\n                                       {\"trusty_dev\", required_argument, NULL, 'd'},\n                                       {\"data_path\", required_argument, NULL, 'p'},\n                                       {\"rpmb_dev\", required_argument, NULL, 'r'},\n                                       {\"dev_type\", required_argument, NULL, 't'},\n                                       {\"max_file_size_from\", required_argument, NULL, 'm'},\n                                       {\"file_storage_mapping\", required_argument, NULL, 'f'},\n                                       {0, 0, 0, 0}};\n\nstatic void show_usage_and_exit(int code) {\n    ALOGE(\"usage: storageproxyd -d <trusty_dev> -p <data_path> -r <rpmb_dev> -t <dev_type>  [-m \"\n          \"<file>] [-f <file>:<mapping>]\\n\");\n    ALOGE(\"Available dev types: mmc, virt\\n\");\n    ALOGE(\"-f = Maps secure storage files like `0` and `persist/0`\\n\"\n          \"to block devices.  Storageproxyd will handle creating the\\n\"\n          \"appropriate symlinks in the root datapath.\\n\");\n    ALOGE(\"-m = Specifies the max size constraint for file backed storages.\\n\"\n          \"The constraint is chosen by giving a file, this allows for passing a\\n\"\n          \"block device for which a max file size can be queried.  File based\\n\"\n          \"storages will be constrained to that size as well.\\n\");\n    exit(code);\n}\n\nstatic int handle_req(struct storage_msg* msg, const void* req, size_t req_len) {\n    int rc;\n\n    struct watcher* watcher = watch_start(\"request\", msg);\n\n    if ((msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) && msg->cmd != STORAGE_RPMB_SEND &&\n        msg->cmd != STORAGE_FILE_WRITE) {\n        /*\n         * handling post commit messages on commands other than rpmb and write\n         * operations are not implemented as there is no use case for this yet.\n         */\n        ALOGE(\"cmd 0x%x: post commit option is not implemented\\n\", msg->cmd);\n        msg->result = STORAGE_ERR_UNIMPLEMENTED;\n        goto err_response;\n    }\n\n    if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT) {\n        rc = storage_sync_checkpoint(watcher);\n        if (rc < 0) {\n            msg->result = STORAGE_ERR_SYNC_FAILURE;\n            goto err_response;\n        }\n    }\n\n    if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT) {\n        bool is_checkpoint_active = false;\n\n        rc = is_data_checkpoint_active(&is_checkpoint_active);\n        if (rc != 0) {\n            ALOGE(\"is_data_checkpoint_active failed in an unexpected way. Aborting.\\n\");\n            msg->result = STORAGE_ERR_GENERIC;\n            goto err_response;\n        } else if (is_checkpoint_active) {\n            ALOGE(\"Checkpoint in progress, dropping write ...\\n\");\n            msg->result = STORAGE_ERR_GENERIC;\n            goto err_response;\n        }\n    }\n\n    switch (msg->cmd) {\n        case STORAGE_FILE_DELETE:\n            rc = storage_file_delete(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_FILE_OPEN:\n            rc = storage_file_open(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_FILE_CLOSE:\n            rc = storage_file_close(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_FILE_WRITE:\n            rc = storage_file_write(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_FILE_READ:\n            rc = storage_file_read(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_FILE_GET_SIZE:\n            rc = storage_file_get_size(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_FILE_SET_SIZE:\n            rc = storage_file_set_size(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_FILE_GET_MAX_SIZE:\n            rc = storage_file_get_max_size(msg, req, req_len, watcher);\n            break;\n\n        case STORAGE_RPMB_SEND:\n            rc = rpmb_send(msg, req, req_len, watcher);\n            break;\n\n        default:\n            ALOGE(\"unhandled command 0x%x\\n\", msg->cmd);\n            msg->result = STORAGE_ERR_UNIMPLEMENTED;\n            goto err_response;\n    }\n\n    /* response was sent in handler */\n    goto finish;\n\nerr_response:\n    rc = ipc_respond(msg, NULL, 0);\n\nfinish:\n    watch_finish(watcher);\n    return rc;\n}\n\nstatic int proxy_loop(void) {\n    ssize_t rc;\n    struct storage_msg msg;\n\n    /* enter main message handling loop */\n    while (true) {\n        /* get incoming message */\n        rc = ipc_get_msg(&msg, req_buffer, REQ_BUFFER_SIZE);\n        if (rc < 0) return rc;\n\n        /* handle request */\n        req_buffer[rc] = 0; /* force zero termination */\n        rc = handle_req(&msg, req_buffer, rc);\n        if (rc) return rc;\n    }\n\n    return 0;\n}\n\nstatic void parse_args(int argc, char* argv[]) {\n    int opt;\n    int oidx = 0;\n    int rc = 0;\n\n    while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) {\n        switch (opt) {\n            case 'd':\n                trusty_devname = strdup(optarg);\n                break;\n\n            case 'p':\n                ss_data_root = strdup(optarg);\n                break;\n\n            case 'r':\n                rpmb_devname = strdup(optarg);\n                break;\n\n            case 't':\n                dev_type = parse_dev_type(optarg);\n                if (dev_type == UNKNOWN_RPMB) {\n                    ALOGE(\"Unrecognized dev type: %s\\n\", optarg);\n                    show_usage_and_exit(EXIT_FAILURE);\n                }\n                break;\n\n            case 'f':\n                rc = parse_and_append_file_mapping(optarg);\n                if (rc < 0) {\n                    ALOGE(\"Failed to parse file mapping: %s\\n\", optarg);\n                    show_usage_and_exit(EXIT_FAILURE);\n                }\n                break;\n\n            case 'm':\n                max_file_size_from = strdup(optarg);\n                break;\n\n            default:\n                ALOGE(\"unrecognized option (%c):\\n\", opt);\n                show_usage_and_exit(EXIT_FAILURE);\n        }\n    }\n\n    if (ss_data_root == NULL || trusty_devname == NULL || rpmb_devname == NULL) {\n        ALOGE(\"missing required argument(s)\\n\");\n        show_usage_and_exit(EXIT_FAILURE);\n    }\n\n    ALOGI(\"starting storageproxyd\\n\");\n    ALOGI(\"storage data root: %s\\n\", ss_data_root);\n    ALOGI(\"trusty dev: %s\\n\", trusty_devname);\n    ALOGI(\"rpmb dev: %s\\n\", rpmb_devname);\n    ALOGI(\"File Mappings: \\n\");\n    const struct storage_mapping_node* curr = storage_mapping_head;\n    for (; curr != NULL; curr = curr->next) {\n        ALOGI(\"\\t%s -> %s\\n\", curr->file_name, curr->backing_storage);\n    }\n    ALOGI(\"max file size from: %s\\n\", max_file_size_from ? max_file_size_from : \"(unset)\");\n}\n\nint main(int argc, char* argv[]) {\n    int rc;\n\n    /*\n     * No access for group and other. We need execute access for user to create\n     * an accessible directory.\n     */\n    umask(S_IRWXG | S_IRWXO);\n\n    /* parse arguments */\n    parse_args(argc, argv);\n\n    /*\n     * Start binder threadpool. At least one extra binder thread is needed to\n     * connect to the wakelock service without relying on polling. If we poll on\n     * the main thread we end up pausing for at least 1s even if the service\n     * starts faster. We set the max thread count to 0 because startThreadPool\n     * \"Starts one thread, PLUS those requested in setThreadPoolMaxThreadCount,\n     * PLUS those manually requested in joinThreadPool.\" We only need a single\n     * binder thread to receive notifications on.\n     */\n    ABinderProcess_setThreadPoolMaxThreadCount(0);\n    ABinderProcess_startThreadPool();\n\n    /* initialize secure storage directory */\n    rc = storage_init(ss_data_root, storage_mapping_head, max_file_size_from);\n    if (rc < 0) return EXIT_FAILURE;\n\n    /* open rpmb device */\n    rc = rpmb_open(rpmb_devname, dev_type);\n    if (rc < 0) return EXIT_FAILURE;\n\n    /* connect to Trusty secure storage server */\n    rc = ipc_connect(trusty_devname, ss_srv_name);\n    if (rc < 0) return EXIT_FAILURE;\n\n    /* enter main loop */\n    rc = proxy_loop();\n    ALOGE(\"exiting proxy loop with status (%d)\\n\", rc);\n\n    ipc_disconnect();\n    rpmb_close();\n\n    return (rc < 0) ? EXIT_FAILURE : EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "trusty/storage/proxy/rpmb.c",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <fcntl.h>\n#include <scsi/scsi.h>\n#include <scsi/scsi_proto.h>\n#include <scsi/sg.h>\n#include <stdbool.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <sys/un.h>\n#include <unistd.h>\n\n#include <linux/major.h>\n#include <linux/mmc/ioctl.h>\n\n#include <hardware_legacy/power.h>\n\n#include \"ipc.h\"\n#include \"log.h\"\n#include \"rpmb.h\"\n#include \"storage.h\"\n\n#define MMC_READ_MULTIPLE_BLOCK 18\n#define MMC_WRITE_MULTIPLE_BLOCK 25\n#define MMC_RELIABLE_WRITE_FLAG (1 << 31)\n\n#define MMC_RSP_PRESENT (1 << 0)\n#define MMC_RSP_CRC (1 << 2)\n#define MMC_RSP_OPCODE (1 << 4)\n#define MMC_CMD_ADTC (1 << 5)\n#define MMC_RSP_SPI_S1 (1 << 7)\n#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)\n#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)\n\n#define MMC_WRITE_FLAG_R 0\n#define MMC_WRITE_FLAG_W 1\n#define MMC_WRITE_FLAG_RELW (MMC_WRITE_FLAG_W | MMC_RELIABLE_WRITE_FLAG)\n\n#define MMC_BLOCK_SIZE 512\n\n/*\n * Number of retry attempts when an RPMB authenticated write triggers a UNIT\n * ATTENTION\n */\n#define UFS_RPMB_WRITE_RETRY_COUNT 1\n/*\n * Number of retry attempts when an RPMB read operation triggers a UNIT\n * ATTENTION\n */\n#define UFS_RPMB_READ_RETRY_COUNT 3\n\n/*\n * There should be no timeout for security protocol ioctl call, so we choose a\n * large number for timeout.\n * 20000 millisecs == 20 seconds\n */\n#define TIMEOUT 20000\n\n/*\n * The sg device driver that supports new interface has a major version number of \"3\".\n * SG_GET_VERSION_NUM ioctl() will yield a number greater than or 30000.\n */\n#define RPMB_MIN_SG_VERSION_NUM 30000\n\n/*\n * CDB format of SECURITY PROTOCOL IN/OUT commands\n * (JEDEC Standard No. 220D, Page 264)\n */\nstruct sec_proto_cdb {\n    /*\n     * OPERATION CODE = A2h for SECURITY PROTOCOL IN command,\n     * OPERATION CODE = B5h for SECURITY PROTOCOL OUT command.\n     */\n    uint8_t opcode;\n    /* SECURITY PROTOCOL = ECh (JEDEC Universal Flash Storage) */\n    uint8_t sec_proto;\n    /*\n     * The SECURITY PROTOCOL SPECIFIC field specifies the RPMB Protocol ID.\n     * CDB Byte 2 = 00h and CDB Byte 3 = 01h for RPMB Region 0.\n     */\n    uint8_t cdb_byte_2;\n    uint8_t cdb_byte_3;\n    /*\n     * Byte 4 and 5 are reserved.\n     */\n    uint8_t cdb_byte_4;\n    uint8_t cdb_byte_5;\n    /* ALLOCATION/TRANSFER LENGTH in big-endian */\n    uint32_t length;\n    /* Byte 9 is reserved. */\n    uint8_t cdb_byte_10;\n    /* CONTROL = 00h. */\n    uint8_t ctrl;\n} __packed;\n\nstatic int rpmb_fd = -1;\nstatic uint8_t read_buf[4096];\nstatic enum dev_type dev_type = UNKNOWN_RPMB;\n\nstatic const char* UFS_WAKE_LOCK_NAME = \"ufs_seq_wakelock\";\n\n/**\n * log_buf - Log a byte buffer to the android log.\n * @priority: One of ANDROID_LOG_* priority levels from android_LogPriority in\n *            android/log.h\n * @prefix:   A null-terminated string that identifies this buffer. Must be less\n *            than 128 bytes.\n * @buf:      Buffer to dump.\n * @size:     Length of @buf in bytes.\n */\n#define LOG_BUF_SIZE 256\nstatic int log_buf(int priority, const char* prefix, const uint8_t* buf, size_t size) {\n    int rc;\n    size_t i;\n    char line[LOG_BUF_SIZE] = {0};\n    char* cur = line;\n\n    rc = snprintf(line, LOG_BUF_SIZE, \"%s @%p [%zu]\", prefix, buf, size);\n    if (rc < 0 || rc >= LOG_BUF_SIZE) {\n        goto err;\n    }\n    cur += rc;\n    for (i = 0; i < size; i++) {\n        if (i % 32 == 0) {\n            /*\n             * Flush the line out to the log after we have printed 32 bytes\n             * (also flushes the header line on the first iteration and sets up\n             * for printing the buffer itself)\n             */\n            LOG_PRI(priority, LOG_TAG, \"%s\", line);\n            memset(line, 0, LOG_BUF_SIZE);\n            cur = line;\n            /* Shift output over by the length of the prefix */\n            rc = snprintf(line, LOG_BUF_SIZE, \"%*s\", (int)strlen(prefix), \"\");\n            if (rc < 0 || rc >= LOG_BUF_SIZE) {\n                goto err;\n            }\n            cur += rc;\n        }\n        rc = snprintf(cur, LOG_BUF_SIZE - (cur - line), \"%02x \", buf[i]);\n        if (rc < 0 || rc >= LOG_BUF_SIZE - (cur - line)) {\n            goto err;\n        }\n        cur += rc;\n    }\n    LOG_PRI(priority, LOG_TAG, \"%s\", line);\n\n    return 0;\n\nerr:\n    if (rc < 0) {\n        return rc;\n    } else {\n        ALOGE(\"log_buf prefix was too long\");\n        return -1;\n    }\n}\n\nstatic void set_sg_io_hdr(sg_io_hdr_t* io_hdrp, int dxfer_direction, unsigned char cmd_len,\n                          unsigned char mx_sb_len, unsigned int dxfer_len, void* dxferp,\n                          unsigned char* cmdp, void* sbp) {\n    memset(io_hdrp, 0, sizeof(sg_io_hdr_t));\n    io_hdrp->interface_id = 'S';\n    io_hdrp->dxfer_direction = dxfer_direction;\n    io_hdrp->cmd_len = cmd_len;\n    io_hdrp->mx_sb_len = mx_sb_len;\n    io_hdrp->dxfer_len = dxfer_len;\n    io_hdrp->dxferp = dxferp;\n    io_hdrp->cmdp = cmdp;\n    io_hdrp->sbp = sbp;\n    io_hdrp->timeout = TIMEOUT;\n}\n\n/**\n * enum scsi_result - Results of checking the SCSI status and sense buffer\n *\n * @SCSI_RES_OK:    SCSI status and sense are good\n * @SCSI_RES_ERR:   SCSI status or sense contain an unhandled error\n * @SCSI_RES_RETRY: SCSI sense buffer contains a status that indicates that the\n *                  command should be retried\n */\nenum scsi_result {\n    SCSI_RES_OK = 0,\n    SCSI_RES_ERR,\n    SCSI_RES_RETRY,\n};\n\nstatic enum scsi_result check_scsi_sense(const uint8_t* sense_buf, size_t len) {\n    uint8_t response_code = 0;\n    uint8_t sense_key = 0;\n    uint8_t additional_sense_code = 0;\n    uint8_t additional_sense_code_qualifier = 0;\n    uint8_t additional_length = 0;\n\n    if (!sense_buf || len == 0) {\n        ALOGE(\"Invalid SCSI sense buffer, length: %zu\\n\", len);\n        return SCSI_RES_ERR;\n    }\n\n    response_code = 0x7f & sense_buf[0];\n\n    if (response_code < 0x70 || response_code > 0x73) {\n        ALOGE(\"Invalid SCSI sense response code: %hhu\\n\", response_code);\n        return SCSI_RES_ERR;\n    }\n\n    if (response_code >= 0x72) {\n        /* descriptor format, SPC-6 4.4.2 */\n        if (len > 1) {\n            sense_key = 0xf & sense_buf[1];\n        }\n        if (len > 2) {\n            additional_sense_code = sense_buf[2];\n        }\n        if (len > 3) {\n            additional_sense_code_qualifier = sense_buf[3];\n        }\n        if (len > 7) {\n            additional_length = sense_buf[7];\n        }\n    } else {\n        /* fixed format, SPC-6 4.4.3 */\n        if (len > 2) {\n            sense_key = 0xf & sense_buf[2];\n        }\n        if (len > 7) {\n            additional_length = sense_buf[7];\n        }\n        if (len > 12) {\n            additional_sense_code = sense_buf[12];\n        }\n        if (len > 13) {\n            additional_sense_code_qualifier = sense_buf[13];\n        }\n    }\n\n    switch (sense_key) {\n        case NO_SENSE:\n        case 0x0f: /* COMPLETED, not present in kernel headers */\n            ALOGD(\"SCSI success with sense data: key=%hhu, asc=%hhu, ascq=%hhu\\n\", sense_key,\n                  additional_sense_code, additional_sense_code_qualifier);\n            return SCSI_RES_OK;\n        case UNIT_ATTENTION:\n            ALOGD(\"UNIT ATTENTION with sense data: key=%hhu, asc=%hhu, ascq=%hhu\\n\", sense_key,\n                  additional_sense_code, additional_sense_code_qualifier);\n            if (additional_sense_code == 0x29) {\n                /* POWER ON or RESET condition */\n                return SCSI_RES_RETRY;\n            }\n\n            /* treat this UNIT ATTENTION as an error if we don't recognize it */\n            break;\n    }\n\n    ALOGE(\"Unexpected SCSI sense data: key=%hhu, asc=%hhu, ascq=%hhu\\n\", sense_key,\n          additional_sense_code, additional_sense_code_qualifier);\n    log_buf(ANDROID_LOG_ERROR, \"sense buffer: \", sense_buf, len);\n    return SCSI_RES_ERR;\n}\n\nstatic enum scsi_result check_sg_io_hdr(const sg_io_hdr_t* io_hdrp) {\n    if (io_hdrp->status == 0 && io_hdrp->host_status == 0 && io_hdrp->driver_status == 0) {\n        return SCSI_RES_OK;\n    }\n\n    if (io_hdrp->status & 0x01) {\n        ALOGE(\"SG_IO received unknown status, LSB is set: %hhu\", io_hdrp->status);\n    }\n\n    if (io_hdrp->masked_status != GOOD && io_hdrp->sb_len_wr > 0) {\n        enum scsi_result scsi_res = check_scsi_sense(io_hdrp->sbp, io_hdrp->sb_len_wr);\n        if (scsi_res == SCSI_RES_RETRY) {\n            return SCSI_RES_RETRY;\n        } else if (scsi_res != SCSI_RES_OK) {\n            ALOGE(\"Unexpected SCSI sense. masked_status: %hhu, host_status: %hu, driver_status: \"\n                  \"%hu\\n\",\n                  io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);\n            return scsi_res;\n        }\n    }\n\n    switch (io_hdrp->masked_status) {\n        case GOOD:\n            break;\n        case CHECK_CONDITION:\n            /* handled by check_sg_sense above */\n            break;\n        default:\n            ALOGE(\"SG_IO failed with masked_status: %hhu, host_status: %hu, driver_status: %hu\\n\",\n                  io_hdrp->masked_status, io_hdrp->host_status, io_hdrp->driver_status);\n            return SCSI_RES_ERR;\n    }\n\n    if (io_hdrp->host_status != 0) {\n        ALOGE(\"SG_IO failed with host_status: %hu, driver_status: %hu\\n\", io_hdrp->host_status,\n              io_hdrp->driver_status);\n    }\n\n    if (io_hdrp->resid != 0) {\n        ALOGE(\"SG_IO resid was non-zero: %d\\n\", io_hdrp->resid);\n    }\n    return SCSI_RES_ERR;\n}\n\nstatic int send_mmc_rpmb_req(int mmc_fd, const struct storage_rpmb_send_req* req,\n                             struct watcher* watcher) {\n    union {\n        struct mmc_ioc_multi_cmd multi;\n        uint8_t raw[sizeof(struct mmc_ioc_multi_cmd) + sizeof(struct mmc_ioc_cmd) * 3];\n    } mmc = {};\n    struct mmc_ioc_cmd* cmd = mmc.multi.cmds;\n    int rc;\n\n    const uint8_t* write_buf = req->payload;\n    if (req->reliable_write_size) {\n        cmd->write_flag = MMC_WRITE_FLAG_RELW;\n        cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;\n        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;\n        cmd->blksz = MMC_BLOCK_SIZE;\n        cmd->blocks = req->reliable_write_size / MMC_BLOCK_SIZE;\n        mmc_ioc_cmd_set_data((*cmd), write_buf);\n#ifdef RPMB_DEBUG\n        ALOGI(\"opcode: 0x%x, write_flag: 0x%x\\n\", cmd->opcode, cmd->write_flag);\n        log_buf(ANDROID_LOG_INFO, \"request: \", write_buf, req->reliable_write_size);\n#endif\n        write_buf += req->reliable_write_size;\n        mmc.multi.num_of_cmds++;\n        cmd++;\n    }\n\n    if (req->write_size) {\n        cmd->write_flag = MMC_WRITE_FLAG_W;\n        cmd->opcode = MMC_WRITE_MULTIPLE_BLOCK;\n        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;\n        cmd->blksz = MMC_BLOCK_SIZE;\n        cmd->blocks = req->write_size / MMC_BLOCK_SIZE;\n        mmc_ioc_cmd_set_data((*cmd), write_buf);\n#ifdef RPMB_DEBUG\n        ALOGI(\"opcode: 0x%x, write_flag: 0x%x\\n\", cmd->opcode, cmd->write_flag);\n        log_buf(ANDROID_LOG_INFO, \"request: \", write_buf, req->write_size);\n#endif\n        write_buf += req->write_size;\n        mmc.multi.num_of_cmds++;\n        cmd++;\n    }\n\n    if (req->read_size) {\n        cmd->write_flag = MMC_WRITE_FLAG_R;\n        cmd->opcode = MMC_READ_MULTIPLE_BLOCK;\n        cmd->flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC, cmd->blksz = MMC_BLOCK_SIZE;\n        cmd->blocks = req->read_size / MMC_BLOCK_SIZE;\n        mmc_ioc_cmd_set_data((*cmd), read_buf);\n#ifdef RPMB_DEBUG\n        ALOGI(\"opcode: 0x%x, write_flag: 0x%x\\n\", cmd->opcode, cmd->write_flag);\n#endif\n        mmc.multi.num_of_cmds++;\n        cmd++;\n    }\n\n    watch_progress(watcher, \"rpmb mmc ioctl\");\n    rc = ioctl(mmc_fd, MMC_IOC_MULTI_CMD, &mmc.multi);\n    watch_progress(watcher, \"rpmb mmc ioctl done\");\n    if (rc < 0) {\n        ALOGE(\"%s: mmc ioctl failed: %d, %s\\n\", __func__, rc, strerror(errno));\n    }\n    return rc;\n}\n\nstatic int send_ufs_rpmb_req(int sg_fd, const struct storage_rpmb_send_req* req,\n                             struct watcher* watcher) {\n    int rc;\n    int wl_rc;\n    const uint8_t* write_buf = req->payload;\n    /*\n     * Meaning of member values are stated on the definition of struct sec_proto_cdb.\n     */\n    struct sec_proto_cdb in_cdb = {0xA2, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};\n    struct sec_proto_cdb out_cdb = {0xB5, 0xEC, 0x00, 0x01, 0x00, 0x00, 0, 0x00, 0x00};\n    unsigned char sense_buffer[32];\n\n    bool is_request_write = req->reliable_write_size > 0;\n\n    /*\n     * Internally this call connects to the suspend service, which will cause\n     * this service to start if not already running. If the binder thread pool\n     * has not been started at this point, this call will block and poll for the\n     * service every 1s. We need to make sure the thread pool is started to\n     * receive an async notification that the service is started to avoid\n     * blocking (see main).\n     */\n    wl_rc = acquire_wake_lock(PARTIAL_WAKE_LOCK, UFS_WAKE_LOCK_NAME);\n    if (wl_rc < 0) {\n        ALOGE(\"%s: failed to acquire wakelock: %d, %s\\n\", __func__, wl_rc, strerror(errno));\n        return wl_rc;\n    }\n\n    if (req->reliable_write_size) {\n        /* Prepare SECURITY PROTOCOL OUT command. */\n        sg_io_hdr_t io_hdr;\n        int retry_count = UFS_RPMB_WRITE_RETRY_COUNT;\n        do {\n            out_cdb.length = __builtin_bswap32(req->reliable_write_size);\n            set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),\n                          req->reliable_write_size, (void*)write_buf, (unsigned char*)&out_cdb,\n                          sense_buffer);\n            watch_progress(watcher, \"rpmb ufs reliable write\");\n            rc = ioctl(sg_fd, SG_IO, &io_hdr);\n            watch_progress(watcher, \"rpmb ufs reliable write done\");\n            if (rc < 0) {\n                ALOGE(\"%s: ufs ioctl failed: %d, %s\\n\", __func__, rc, strerror(errno));\n                goto err_op;\n            }\n        } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);\n        write_buf += req->reliable_write_size;\n    }\n\n    if (req->write_size) {\n        /* Prepare SECURITY PROTOCOL OUT command. */\n        sg_io_hdr_t io_hdr;\n        /*\n         * We don't retry write response request messages (is_request_write ==\n         * true) because a unit attention condition between the write and\n         * requesting a response means that the device was reset and we can't\n         * get a response to our original write. We can only retry this SG_IO\n         * call when it is the first call in our sequence.\n         */\n        int retry_count = is_request_write ? 0 : UFS_RPMB_READ_RETRY_COUNT;\n        do {\n            out_cdb.length = __builtin_bswap32(req->write_size);\n            set_sg_io_hdr(&io_hdr, SG_DXFER_TO_DEV, sizeof(out_cdb), sizeof(sense_buffer),\n                          req->write_size, (void*)write_buf, (unsigned char*)&out_cdb,\n                          sense_buffer);\n            watch_progress(watcher, \"rpmb ufs write\");\n            rc = ioctl(sg_fd, SG_IO, &io_hdr);\n            watch_progress(watcher, \"rpmb ufs write done\");\n            if (rc < 0) {\n                ALOGE(\"%s: ufs ioctl failed: %d, %s\\n\", __func__, rc, strerror(errno));\n                goto err_op;\n            }\n        } while (check_sg_io_hdr(&io_hdr) == SCSI_RES_RETRY && retry_count-- > 0);\n        write_buf += req->write_size;\n    }\n\n    if (req->read_size) {\n        /* Prepare SECURITY PROTOCOL IN command. */\n        in_cdb.length = __builtin_bswap32(req->read_size);\n        sg_io_hdr_t io_hdr;\n        set_sg_io_hdr(&io_hdr, SG_DXFER_FROM_DEV, sizeof(in_cdb), sizeof(sense_buffer),\n                      req->read_size, read_buf, (unsigned char*)&in_cdb, sense_buffer);\n        watch_progress(watcher, \"rpmb ufs read\");\n        rc = ioctl(sg_fd, SG_IO, &io_hdr);\n        watch_progress(watcher, \"rpmb ufs read done\");\n        if (rc < 0) {\n            ALOGE(\"%s: ufs ioctl failed: %d, %s\\n\", __func__, rc, strerror(errno));\n        }\n        check_sg_io_hdr(&io_hdr);\n    }\n\nerr_op:\n    wl_rc = release_wake_lock(UFS_WAKE_LOCK_NAME);\n    if (wl_rc < 0) {\n        ALOGE(\"%s: failed to release wakelock: %d, %s\\n\", __func__, wl_rc, strerror(errno));\n    }\n\n    return rc;\n}\n\nstatic int send_virt_rpmb_req(int rpmb_fd, void* read_buf, size_t read_size, const void* payload,\n                              size_t payload_size) {\n    int rc;\n    uint16_t res_count = read_size / MMC_BLOCK_SIZE;\n    uint16_t cmd_count = payload_size / MMC_BLOCK_SIZE;\n    rc = write(rpmb_fd, &res_count, sizeof(res_count));\n    if (rc < 0) {\n        return rc;\n    }\n    rc = write(rpmb_fd, &cmd_count, sizeof(cmd_count));\n    if (rc < 0) {\n        return rc;\n    }\n    rc = write(rpmb_fd, payload, payload_size);\n    if (rc < 0) {\n        return rc;\n    }\n    rc = read(rpmb_fd, read_buf, read_size);\n    return rc;\n}\n\nint rpmb_send(struct storage_msg* msg, const void* r, size_t req_len, struct watcher* watcher) {\n    int rc;\n    const struct storage_rpmb_send_req* req = r;\n\n    if (req_len < sizeof(*req)) {\n        ALOGW(\"malformed rpmb request: invalid length (%zu < %zu)\\n\", req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    size_t expected_len = sizeof(*req) + req->reliable_write_size + req->write_size;\n    if (req_len != expected_len) {\n        ALOGW(\"malformed rpmb request: invalid length (%zu != %zu)\\n\", req_len, expected_len);\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    if ((req->reliable_write_size % MMC_BLOCK_SIZE) != 0) {\n        ALOGW(\"invalid reliable write size %u\\n\", req->reliable_write_size);\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    if ((req->write_size % MMC_BLOCK_SIZE) != 0) {\n        ALOGW(\"invalid write size %u\\n\", req->write_size);\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    if (req->read_size % MMC_BLOCK_SIZE != 0 || req->read_size > sizeof(read_buf)) {\n        ALOGE(\"%s: invalid read size %u\\n\", __func__, req->read_size);\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    if (dev_type == MMC_RPMB) {\n        rc = send_mmc_rpmb_req(rpmb_fd, req, watcher);\n        if (rc < 0) {\n            msg->result = STORAGE_ERR_GENERIC;\n            goto err_response;\n        }\n    } else if (dev_type == UFS_RPMB) {\n        rc = send_ufs_rpmb_req(rpmb_fd, req, watcher);\n        if (rc < 0) {\n            ALOGE(\"send_ufs_rpmb_req failed: %d, %s\\n\", rc, strerror(errno));\n            msg->result = STORAGE_ERR_GENERIC;\n            goto err_response;\n        }\n    } else if ((dev_type == VIRT_RPMB) || (dev_type == SOCK_RPMB)) {\n        size_t payload_size = req->reliable_write_size + req->write_size;\n        rc = send_virt_rpmb_req(rpmb_fd, read_buf, req->read_size, req->payload, payload_size);\n        if (rc < 0) {\n            ALOGE(\"send_virt_rpmb_req failed: %d, %s\\n\", rc, strerror(errno));\n            msg->result = STORAGE_ERR_GENERIC;\n            goto err_response;\n        }\n        if (rc != req->read_size) {\n            ALOGE(\"send_virt_rpmb_req got incomplete response: \"\n                  \"(size %d, expected %d)\\n\",\n                  rc, req->read_size);\n            msg->result = STORAGE_ERR_GENERIC;\n            goto err_response;\n        }\n    } else {\n        ALOGE(\"Unsupported dev_type\\n\");\n        msg->result = STORAGE_ERR_GENERIC;\n        goto err_response;\n    }\n#ifdef RPMB_DEBUG\n    if (req->read_size) log_buf(ANDROID_LOG_INFO, \"response: \", read_buf, req->read_size);\n#endif\n\n    if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {\n        /*\n         * Nothing todo for post msg commit request as MMC_IOC_MULTI_CMD\n         * is fully synchronous in this implementation.\n         */\n    }\n\n    msg->result = STORAGE_NO_ERROR;\n    return ipc_respond(msg, read_buf, req->read_size);\n\nerr_response:\n    return ipc_respond(msg, NULL, 0);\n}\n\nint rpmb_open(const char* rpmb_devname, enum dev_type open_dev_type) {\n    int rc, sg_version_num;\n    dev_type = open_dev_type;\n\n    if (dev_type != SOCK_RPMB) {\n        rc = open(rpmb_devname, O_RDWR, 0);\n        if (rc < 0) {\n            ALOGE(\"unable (%d) to open rpmb device '%s': %s\\n\", errno, rpmb_devname, strerror(errno));\n            return rc;\n        }\n        rpmb_fd = rc;\n\n        /* For UFS, it is prudent to check we have a sg device by calling an ioctl */\n        if (dev_type == UFS_RPMB) {\n            if ((ioctl(rpmb_fd, SG_GET_VERSION_NUM, &sg_version_num) < 0) ||\n                (sg_version_num < RPMB_MIN_SG_VERSION_NUM)) {\n                ALOGE(\"%s is not a sg device, or old sg driver\\n\", rpmb_devname);\n                return -1;\n            }\n        }\n    } else {\n        struct sockaddr_un unaddr;\n        struct sockaddr *addr = (struct sockaddr *)&unaddr;\n        rc = socket(AF_UNIX, SOCK_STREAM, 0);\n        if (rc < 0) {\n            ALOGE(\"unable (%d) to create socket: %s\\n\", errno, strerror(errno));\n            return rc;\n        }\n        rpmb_fd = rc;\n\n        memset(&unaddr, 0, sizeof(unaddr));\n        unaddr.sun_family = AF_UNIX;\n        // TODO if it overflowed, bail rather than connecting?\n        strncpy(unaddr.sun_path, rpmb_devname, sizeof(unaddr.sun_path)-1);\n        rc = connect(rpmb_fd, addr, sizeof(unaddr));\n        if (rc < 0) {\n            ALOGE(\"unable (%d) to connect to rpmb socket '%s': %s\\n\", errno, rpmb_devname, strerror(errno));\n            return rc;\n        }\n    }\n\n    return 0;\n}\n\nvoid rpmb_close(void) {\n    close(rpmb_fd);\n    rpmb_fd = -1;\n}\n"
  },
  {
    "path": "trusty/storage/proxy/rpmb.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <interface/storage/storage.h>\n#include <stdint.h>\n\n#include \"watchdog.h\"\n\nenum dev_type { UNKNOWN_RPMB, MMC_RPMB, VIRT_RPMB, UFS_RPMB, SOCK_RPMB };\n\nint rpmb_open(const char* rpmb_devname, enum dev_type dev_type);\nint rpmb_send(struct storage_msg* msg, const void* r, size_t req_len, struct watcher* watcher);\nvoid rpmb_close(void);\n"
  },
  {
    "path": "trusty/storage/proxy/storage.c",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#include <assert.h>\n#include <cutils/properties.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <inttypes.h>\n#include <libgen.h>\n#include <linux/fs.h>\n#include <stdbool.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <sys/syscall.h>\n#include <sys/types.h>\n#include <unistd.h>\n\n#include \"checkpoint_handling.h\"\n#include \"ipc.h\"\n#include \"log.h\"\n#include \"storage.h\"\n#include \"watchdog.h\"\n\n#define FD_TBL_SIZE 64\n#define MAX_READ_SIZE 4096\n\n#define ALTERNATE_DATA_DIR \"alternate/\"\n\n/* Maximum file size for filesystem backed storage (i.e. not block dev backed storage) */\nstatic uint64_t max_file_size = 0x10000000000;\n\nenum sync_state {\n    SS_UNUSED = -1,\n    SS_CLEAN = 0,\n    SS_DIRTY = 1,\n    SS_CLEAN_NEED_SYMLINK = 2,\n};\n\nstatic const char *ssdir_name;\n\n/* List head for storage mapping, elements added at init, and never removed */\nstatic struct storage_mapping_node* storage_mapping_head;\n\n#ifdef VENDOR_FS_READY_PROPERTY\n\n/*\n * Properties set to 1 after we have opened a file under ssdir_name. The backing\n * files for both TD and TDP are currently located under /data/vendor/ss and can\n * only be opened once userdata is mounted. This storageproxyd service is\n * restarted when userdata is available, which causes the Trusty storage service\n * to reconnect and attempt to open the backing files for TD and TDP. Once we\n * set this property, other users can expect that the Trusty storage service\n * ports will be available (although they may block if still being initialized),\n * and connections will not be reset after this point (assuming the\n * storageproxyd service stays running).\n *\n * fs_ready - secure storage is read-only (due to checkpointing after upgrade)\n * fs_ready_rw - secure storage is readable and writable\n */\n#define FS_READY_PROPERTY \"ro.vendor.trusty.storage.fs_ready\"\n#define FS_READY_RW_PROPERTY \"ro.vendor.trusty.storage.fs_ready_rw\"\n\n/* has FS_READY_PROPERTY been set? */\nstatic bool fs_ready_set = false;\nstatic bool fs_ready_rw_set = false;\n\nstatic bool property_set_helper(const char* prop) {\n    int rc = property_set(prop, \"1\");\n    if (rc == 0) {\n        ALOGI(\"Set property %s\\n\", prop);\n    } else {\n        ALOGE(\"Could not set property %s, rc: %d\\n\", prop, rc);\n    }\n\n    return rc == 0;\n}\n\n#endif  // #ifdef VENDOR_FS_READY_PROPERTY\n\nstatic enum sync_state fs_state;\nstatic enum sync_state fd_state[FD_TBL_SIZE];\n\nstatic bool alternate_mode;\n\nstatic struct {\n    struct storage_file_read_resp hdr;\n    uint8_t data[MAX_READ_SIZE];\n} read_rsp;\n\nstatic uint32_t insert_fd(int open_flags, int fd, struct storage_mapping_node* node) {\n    uint32_t handle = fd;\n\n    if (handle < FD_TBL_SIZE) {\n        fd_state[fd] = SS_CLEAN; /* fd clean */\n        if (open_flags & O_TRUNC) {\n            assert(node == NULL);\n            fd_state[fd] = SS_DIRTY; /* set fd dirty */\n        }\n\n        if (node != NULL) {\n            fd_state[fd] = SS_CLEAN_NEED_SYMLINK;\n        }\n    } else {\n            ALOGW(\"%s: untracked fd %u\\n\", __func__, fd);\n            if (open_flags & (O_TRUNC | O_CREAT)) {\n                fs_state = SS_DIRTY;\n            }\n    }\n\n    if (node != NULL) {\n        node->fd = fd;\n    }\n\n    return handle;\n}\n\nstatic void clear_fd_symlink_status(uint32_t handle, struct storage_mapping_node* entry) {\n    /* Always clear FD, in case fd is not in FD_TBL */\n    entry->fd = -1;\n\n    if (handle >= FD_TBL_SIZE) {\n        ALOGE(\"%s: untracked fd=%u\\n\", __func__, handle);\n        return;\n    }\n\n    if (fd_state[handle] == SS_CLEAN_NEED_SYMLINK) {\n        fd_state[handle] = SS_CLEAN;\n    }\n}\n\nstatic struct storage_mapping_node* get_pending_symlink_mapping(uint32_t handle) {\n    /* Fast lookup failure, is it in FD TBL */\n    if (handle < FD_TBL_SIZE && fd_state[handle] != SS_CLEAN_NEED_SYMLINK) {\n        return NULL;\n    }\n\n    /* Go find our mapping */\n    struct storage_mapping_node* curr = storage_mapping_head;\n    for (; curr != NULL; curr = curr->next) {\n        if (curr->fd == handle) {\n            return curr;\n        }\n    }\n\n    /* Safety check: state inconsistent if we get here with handle inside table range */\n    assert(handle >= FD_TBL_SIZE);\n\n    return NULL;\n};\n\nstatic int possibly_symlink_and_clear_mapping(uint32_t handle) {\n    struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);\n    if (entry == NULL) {\n        /* No mappings pending */\n        return 0;\n    }\n\n    /* Create full path */\n    char* path = NULL;\n    int rc = asprintf(&path, \"%s/%s\", ssdir_name, entry->file_name);\n    if (rc < 0) {\n        ALOGE(\"%s: asprintf failed\\n\", __func__);\n        return -1;\n    }\n\n    /* Try and setup the symlinking */\n    ALOGI(\"Creating symlink %s->%s\\n\", path, entry->backing_storage);\n    rc = symlink(entry->backing_storage, path);\n    if (rc < 0) {\n        ALOGE(\"%s: error symlinking %s->%s (%s)\\n\", __func__, path, entry->backing_storage,\n              strerror(errno));\n        free(path);\n        return rc;\n    }\n    free(path);\n\n    clear_fd_symlink_status(handle, entry);\n\n    return rc;\n}\n\nstatic bool is_pending_symlink(uint32_t handle) {\n    struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);\n    return entry != NULL;\n}\n\nstatic int lookup_fd(uint32_t handle, bool dirty)\n{\n    if (dirty) {\n        if (handle < FD_TBL_SIZE) {\n            fd_state[handle] = SS_DIRTY;\n        } else {\n            fs_state = SS_DIRTY;\n        }\n    }\n    return handle;\n}\n\nstatic int remove_fd(uint32_t handle)\n{\n    /* Cleanup fd in symlink mapping if it exists */\n    struct storage_mapping_node* entry = get_pending_symlink_mapping(handle);\n    if (entry != NULL) {\n        entry->fd = -1;\n    }\n\n    if (handle < FD_TBL_SIZE) {\n        fd_state[handle] = SS_UNUSED; /* set to uninstalled */\n    }\n    return handle;\n}\n\nstatic enum storage_err translate_errno(int error)\n{\n    enum storage_err result;\n    switch (error) {\n    case 0:\n        result = STORAGE_NO_ERROR;\n        break;\n    case EBADF:\n    case EINVAL:\n    case ENOTDIR:\n    case EISDIR:\n    case ENAMETOOLONG:\n        result = STORAGE_ERR_NOT_VALID;\n        break;\n    case ENOENT:\n        result = STORAGE_ERR_NOT_FOUND;\n        break;\n    case EEXIST:\n        result = STORAGE_ERR_EXIST;\n        break;\n    case EPERM:\n    case EACCES:\n        result = STORAGE_ERR_ACCESS;\n        break;\n    default:\n        result = STORAGE_ERR_GENERIC;\n        break;\n    }\n\n    return result;\n}\n\nstatic ssize_t write_with_retry(int fd, const void *buf_, size_t size, off_t offset)\n{\n    ssize_t rc;\n    const uint8_t *buf = buf_;\n\n    while (size > 0) {\n        rc = TEMP_FAILURE_RETRY(pwrite(fd, buf, size, offset));\n        if (rc < 0)\n            return rc;\n        size -= rc;\n        buf += rc;\n        offset += rc;\n    }\n    return 0;\n}\n\nstatic ssize_t read_with_retry(int fd, void *buf_, size_t size, off_t offset)\n{\n    ssize_t rc;\n    size_t  rcnt = 0;\n    uint8_t *buf = buf_;\n\n    while (size > 0) {\n        rc = TEMP_FAILURE_RETRY(pread(fd, buf, size, offset));\n        if (rc < 0)\n            return rc;\n        if (rc == 0)\n            break;\n        size -= rc;\n        buf += rc;\n        offset += rc;\n        rcnt += rc;\n    }\n    return rcnt;\n}\n\nint storage_file_delete(struct storage_msg* msg, const void* r, size_t req_len,\n                        struct watcher* watcher) {\n    char *path = NULL;\n    const struct storage_file_delete_req *req = r;\n\n    if (req_len < sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd < %zd)\\n\",\n              __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    size_t fname_len = strlen(req->name);\n    if (fname_len != req_len - sizeof(*req)) {\n        ALOGE(\"%s: invalid filename length (%zd != %zd)\\n\",\n              __func__, fname_len, req_len - sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    int rc = asprintf(&path, \"%s/%s\", ssdir_name, req->name);\n    if (rc < 0) {\n        ALOGE(\"%s: asprintf failed\\n\", __func__);\n        msg->result = STORAGE_ERR_GENERIC;\n        goto err_response;\n    }\n\n    watch_progress(watcher, \"unlinking file\");\n    rc = unlink(path);\n    if (rc < 0) {\n        rc = errno;\n        if (errno == ENOENT) {\n            ALOGV(\"%s: error (%d) unlinking file '%s'\\n\",\n                  __func__, rc, path);\n        } else {\n            ALOGE(\"%s: error (%d) unlinking file '%s'\\n\",\n                  __func__, rc, path);\n        }\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n\n    ALOGV(\"%s: \\\"%s\\\"\\n\", __func__, path);\n    msg->result = STORAGE_NO_ERROR;\n\nerr_response:\n    if (path)\n        free(path);\n    return ipc_respond(msg, NULL, 0);\n}\n\nstatic void sync_parent(const char* path, struct watcher* watcher) {\n    int parent_fd;\n    watch_progress(watcher, \"syncing parent\");\n    char* parent_path = dirname(path);\n    parent_fd = TEMP_FAILURE_RETRY(open(parent_path, O_RDONLY));\n    if (parent_fd >= 0) {\n        fsync(parent_fd);\n        close(parent_fd);\n    } else {\n        ALOGE(\"%s: failed to open parent directory \\\"%s\\\" for sync: %s\\n\", __func__, parent_path,\n              strerror(errno));\n    }\n    watch_progress(watcher, \"done syncing parent\");\n}\n\nstatic struct storage_mapping_node* get_storage_mapping_entry(const char* source) {\n    struct storage_mapping_node* curr = storage_mapping_head;\n    for (; curr != NULL; curr = curr->next) {\n        if (!strcmp(source, curr->file_name)) {\n            ALOGI(\"Found backing file %s for %s\\n\", curr->backing_storage, source);\n            return curr;\n        }\n    }\n    return NULL;\n}\n\nstatic bool is_backing_storage_mapped(const char* source) {\n    const struct storage_mapping_node* curr = storage_mapping_head;\n    for (; curr != NULL; curr = curr->next) {\n        if (!strcmp(source, curr->backing_storage)) {\n            ALOGI(\"Backed storage mapping exists for %s\\n\", curr->backing_storage);\n            return true;\n        }\n    }\n    return false;\n}\n\n/* Attempts to open a backed file, if mapped, without creating the symlink. Symlink will be created\n * later on the first write.  This allows us to continue reporting zero read sizes until the first\n * write. */\nstatic int open_possibly_mapped_file(const char* short_path, const char* full_path, int open_flags,\n                                     struct storage_mapping_node** entry) {\n    /* See if mapping exists, report upstream if there is no mapping. */\n    struct storage_mapping_node* mapping_entry = get_storage_mapping_entry(short_path);\n    if (mapping_entry == NULL) {\n        return TEMP_FAILURE_RETRY(open(full_path, open_flags, S_IRUSR | S_IWUSR));\n    }\n\n    /* Check for existence of root path, we don't allow mappings during early boot */\n    struct stat buf = {0};\n    if (stat(ssdir_name, &buf) != 0) {\n        ALOGW(\"Root path not accessible yet, refuse to open mappings for now.\\n\");\n        return -1;\n    }\n\n    /* We don't support exclusive opening of mapped files */\n    if (open_flags & O_EXCL) {\n        ALOGE(\"Requesting exclusive open on backed storage isn't supported: %s\\n\", full_path);\n        return -1;\n    }\n\n    /* Try and open mapping file */\n    open_flags &= ~(O_CREAT | O_EXCL);\n    ALOGI(\"%s Attempting to open mapped file: %s\\n\", __func__, mapping_entry->backing_storage);\n    int fd =\n            TEMP_FAILURE_RETRY(open(mapping_entry->backing_storage, open_flags, S_IRUSR | S_IWUSR));\n    if (fd < 0) {\n        ALOGE(\"%s Failed to open mapping file: %s\\n\", __func__, mapping_entry->backing_storage);\n        return -1;\n    }\n\n    /* Let caller know which entry we used for opening */\n    *entry = mapping_entry;\n    return fd;\n}\n\nint storage_file_open(struct storage_msg* msg, const void* r, size_t req_len,\n                      struct watcher* watcher) {\n    char* path = NULL;\n    const struct storage_file_open_req *req = r;\n    struct storage_file_open_resp resp = {0};\n    struct storage_mapping_node* mapping_entry = NULL;\n\n    if (req_len < sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd < %zd)\\n\",\n               __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    size_t fname_len = strlen(req->name);\n    if (fname_len != req_len - sizeof(*req)) {\n        ALOGE(\"%s: invalid filename length (%zd != %zd)\\n\",\n              __func__, fname_len, req_len - sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    /*\n     * TODO(b/210501710): Expose GSI image running state to vendor\n     * storageproxyd. We want to control data file paths in vendor_init, but we\n     * don't have access to the necessary property there yet. When we have\n     * access to that property we can set the root data path read-only and only\n     * allow creation of files in alternate/. Checking paths here temporarily\n     * until that is fixed.\n     *\n     * We are just checking for \"/\" instead of \"alternate/\" because we still\n     * want to still allow access to \"persist/\" in alternate mode (for now, this\n     * may change in the future).\n     */\n    if (alternate_mode && !strchr(req->name, '/')) {\n        ALOGE(\"%s: Cannot open root data file \\\"%s\\\" in alternate mode\\n\", __func__, req->name);\n        msg->result = STORAGE_ERR_ACCESS;\n        goto err_response;\n    }\n\n    int rc = asprintf(&path, \"%s/%s\", ssdir_name, req->name);\n    if (rc < 0) {\n        ALOGE(\"%s: asprintf failed\\n\", __func__);\n        msg->result = STORAGE_ERR_GENERIC;\n        goto err_response;\n    }\n\n    int open_flags = O_RDWR;\n\n    if (req->flags & STORAGE_FILE_OPEN_TRUNCATE)\n        open_flags |= O_TRUNC;\n\n    if (req->flags & STORAGE_FILE_OPEN_CREATE) {\n        /*\n         * Create the alternate parent dir if needed & allowed.\n         *\n         * TODO(b/210501710): Expose GSI image running state to vendor\n         * storageproxyd. This directory should be created by vendor_init, once\n         * it has access to the necessary bit of information.\n         */\n        if (strstr(req->name, ALTERNATE_DATA_DIR) == req->name) {\n            char* parent_path = dirname(path);\n            rc = mkdir(parent_path, S_IRWXU);\n            if (rc == 0) {\n                sync_parent(parent_path, watcher);\n            } else if (errno != EEXIST) {\n                ALOGE(\"%s: Could not create parent directory \\\"%s\\\": %s\\n\", __func__, parent_path,\n                      strerror(errno));\n            }\n        }\n\n        /* open or create */\n        if (req->flags & STORAGE_FILE_OPEN_CREATE_EXCLUSIVE) {\n            /* create exclusive */\n            open_flags |= O_CREAT | O_EXCL;\n\n            /* Look for and attempt opening a mapping, else just do normal open. */\n            rc = open_possibly_mapped_file(req->name, path, open_flags, &mapping_entry);\n        } else {\n            /* try open first */\n            rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));\n            if (rc == -1 && errno == ENOENT) {\n                /* then try open with O_CREATE */\n                open_flags |= O_CREAT;\n\n                /* Look for and attempt opening a mapping, else just do normal open. */\n                rc = open_possibly_mapped_file(req->name, path, open_flags, &mapping_entry);\n            }\n\n        }\n    } else {\n        /* open an existing file */\n        rc = TEMP_FAILURE_RETRY(open(path, open_flags, S_IRUSR | S_IWUSR));\n    }\n\n    if (rc < 0) {\n        rc = errno;\n        if (errno == EEXIST || errno == ENOENT) {\n            ALOGV(\"%s: failed to open file \\\"%s\\\": %s\\n\",\n                  __func__, path, strerror(errno));\n        } else {\n            ALOGE(\"%s: failed to open file \\\"%s\\\": %s\\n\",\n                  __func__, path, strerror(errno));\n        }\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n\n    if (open_flags & O_CREAT) {\n        sync_parent(path, watcher);\n    }\n\n    /* at this point rc contains storage file fd */\n    msg->result = STORAGE_NO_ERROR;\n    resp.handle = insert_fd(open_flags, rc, mapping_entry);\n    ALOGV(\"%s: \\\"%s\\\": fd = %u: handle = %d\\n\",\n          __func__, path, rc, resp.handle);\n\n    free(path);\n    path = NULL;\n\n#ifdef VENDOR_FS_READY_PROPERTY\n    /* a backing file has been opened, notify any waiting init steps */\n    if (!fs_ready_set || !fs_ready_rw_set) {\n        bool is_checkpoint_active = false;\n\n        rc = is_data_checkpoint_active(&is_checkpoint_active);\n        if (rc != 0) {\n            ALOGE(\"is_data_checkpoint_active() failed (%d)\\n\", rc);\n        } else {\n            if (!fs_ready_rw_set && !is_checkpoint_active) {\n                fs_ready_rw_set = property_set_helper(FS_READY_RW_PROPERTY);\n            }\n\n            if (!fs_ready_set) {\n                fs_ready_set = property_set_helper(FS_READY_PROPERTY);\n            }\n        }\n    }\n#endif  // #ifdef VENDOR_FS_READY_PROPERTY\n\n    return ipc_respond(msg, &resp, sizeof(resp));\n\nerr_response:\n    if (path)\n        free(path);\n    return ipc_respond(msg, NULL, 0);\n}\n\nint storage_file_close(struct storage_msg* msg, const void* r, size_t req_len,\n                       struct watcher* watcher) {\n    const struct storage_file_close_req *req = r;\n\n    if (req_len != sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd != %zd)\\n\",\n              __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    int fd = remove_fd(req->handle);\n    ALOGV(\"%s: handle = %u: fd = %u\\n\", __func__, req->handle, fd);\n\n    watch_progress(watcher, \"fsyncing before file close\");\n    int rc = fsync(fd);\n    watch_progress(watcher, \"done fsyncing before file close\");\n    if (rc < 0) {\n        rc = errno;\n        ALOGE(\"%s: fsync failed for fd=%u: %s\\n\",\n              __func__, fd, strerror(errno));\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n\n    rc = close(fd);\n    if (rc < 0) {\n        rc = errno;\n        ALOGE(\"%s: close failed for fd=%u: %s\\n\",\n              __func__, fd, strerror(errno));\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n\n    msg->result = STORAGE_NO_ERROR;\n\nerr_response:\n    return ipc_respond(msg, NULL, 0);\n}\n\nint storage_file_write(struct storage_msg* msg, const void* r, size_t req_len,\n                       struct watcher* watcher) {\n    int rc;\n    const struct storage_file_write_req *req = r;\n\n    if (req_len < sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd < %zd)\\n\",\n              __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    /* Handle any delayed symlinking for this handle if any */\n    rc = possibly_symlink_and_clear_mapping(req->handle);\n    if (rc < 0) {\n        ALOGE(\"Failed to symlink storage\\n\");\n        msg->result = STORAGE_ERR_GENERIC;\n        goto err_response;\n    }\n\n    int fd = lookup_fd(req->handle, true);\n    watch_progress(watcher, \"writing\");\n    if (write_with_retry(fd, &req->data[0], req_len - sizeof(*req),\n                         req->offset) < 0) {\n        watch_progress(watcher, \"writing done w/ error\");\n        rc = errno;\n        ALOGW(\"%s: error writing file (fd=%d): %s\\n\",\n              __func__, fd, strerror(errno));\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n    watch_progress(watcher, \"writing done\");\n\n    if (msg->flags & STORAGE_MSG_FLAG_POST_COMMIT) {\n        rc = storage_sync_checkpoint(watcher);\n        if (rc < 0) {\n            msg->result = STORAGE_ERR_SYNC_FAILURE;\n            goto err_response;\n        }\n    }\n\n    msg->result = STORAGE_NO_ERROR;\n\nerr_response:\n    return ipc_respond(msg, NULL, 0);\n}\n\nint storage_file_read(struct storage_msg* msg, const void* r, size_t req_len,\n                      struct watcher* watcher) {\n    int rc;\n    const struct storage_file_read_req *req = r;\n\n    if (req_len != sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd != %zd)\\n\",\n              __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    if (req->size > MAX_READ_SIZE) {\n        ALOGW(\"%s: request is too large (%u > %d) - refusing\\n\",\n              __func__, req->size, MAX_READ_SIZE);\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    /* If this handle has a delayed symlink we should report 0 size reads until first write occurs\n     */\n    if (is_pending_symlink(req->handle)) {\n        ALOGI(\"Pending symlink: Forcing read result 0.\\n\");\n        msg->result = STORAGE_NO_ERROR;\n        return ipc_respond(msg, &read_rsp, sizeof(read_rsp.hdr));\n    }\n\n    int fd = lookup_fd(req->handle, false);\n    watch_progress(watcher, \"reading\");\n    ssize_t read_res = read_with_retry(fd, read_rsp.hdr.data, req->size,\n                                       (off_t)req->offset);\n    watch_progress(watcher, \"reading done\");\n    if (read_res < 0) {\n        rc = errno;\n        ALOGW(\"%s: error reading file (fd=%d): %s\\n\",\n              __func__, fd, strerror(errno));\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n\n    msg->result = STORAGE_NO_ERROR;\n    return ipc_respond(msg, &read_rsp, read_res + sizeof(read_rsp.hdr));\n\nerr_response:\n    return ipc_respond(msg, NULL, 0);\n}\n\nint storage_file_get_size(struct storage_msg* msg, const void* r, size_t req_len,\n                          struct watcher* watcher) {\n    const struct storage_file_get_size_req *req = r;\n    struct storage_file_get_size_resp resp = {0};\n\n    if (req_len != sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd != %zd)\\n\",\n              __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    struct stat stat;\n    int fd = lookup_fd(req->handle, false);\n    watch_progress(watcher, \"fstat\");\n    int rc = fstat(fd, &stat);\n    watch_progress(watcher, \"fstat done\");\n    if (rc < 0) {\n        rc = errno;\n        ALOGE(\"%s: error stat'ing file (fd=%d): %s\\n\",\n              __func__, fd, strerror(errno));\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n\n    resp.size = stat.st_size;\n    msg->result = STORAGE_NO_ERROR;\n    return ipc_respond(msg, &resp, sizeof(resp));\n\nerr_response:\n    return ipc_respond(msg, NULL, 0);\n}\n\nint storage_file_set_size(struct storage_msg* msg, const void* r, size_t req_len,\n                          struct watcher* watcher) {\n    const struct storage_file_set_size_req *req = r;\n\n    if (req_len != sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd != %zd)\\n\",\n              __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    int fd = lookup_fd(req->handle, true);\n    watch_progress(watcher, \"ftruncate\");\n    int rc = TEMP_FAILURE_RETRY(ftruncate(fd, req->size));\n    watch_progress(watcher, \"ftruncate done\");\n    if (rc < 0) {\n        rc = errno;\n        ALOGE(\"%s: error truncating file (fd=%d): %s\\n\",\n              __func__, fd, strerror(errno));\n        msg->result = translate_errno(rc);\n        goto err_response;\n    }\n\n    msg->result = STORAGE_NO_ERROR;\n\nerr_response:\n    return ipc_respond(msg, NULL, 0);\n}\n\nint storage_file_get_max_size(struct storage_msg* msg, const void* r, size_t req_len,\n                              struct watcher* watcher) {\n    const struct storage_file_get_max_size_req* req = r;\n    struct storage_file_get_max_size_resp resp = {0};\n    uint64_t max_size = 0;\n\n    if (req_len != sizeof(*req)) {\n        ALOGE(\"%s: invalid request length (%zd != %zd)\\n\", __func__, req_len, sizeof(*req));\n        msg->result = STORAGE_ERR_NOT_VALID;\n        goto err_response;\n    }\n\n    struct stat stat;\n    int fd = lookup_fd(req->handle, false);\n    watch_progress(watcher, \"fstat to get max size\");\n    int rc = fstat(fd, &stat);\n    watch_progress(watcher, \"fstat to get max size done\");\n    if (rc < 0) {\n        ALOGE(\"%s: error stat'ing file (fd=%d): %s\\n\", __func__, fd, strerror(errno));\n        goto err_response;\n    }\n\n    if ((stat.st_mode & S_IFMT) == S_IFBLK) {\n        rc = ioctl(fd, BLKGETSIZE64, &max_size);\n        if (rc < 0) {\n            rc = errno;\n            ALOGE(\"%s: error calling ioctl on file (fd=%d): %s\\n\", __func__, fd, strerror(errno));\n            msg->result = translate_errno(rc);\n            goto err_response;\n        }\n    } else {\n        max_size = max_file_size;\n    }\n\n    resp.max_size = max_size;\n    msg->result = STORAGE_NO_ERROR;\n    return ipc_respond(msg, &resp, sizeof(resp));\n\nerr_response:\n    return ipc_respond(msg, NULL, 0);\n}\n\nint determine_max_file_size(const char* max_file_size_from) {\n    /* Use default if none passed in */\n    if (max_file_size_from == NULL) {\n        ALOGI(\"No max file source given, continuing to use default: 0x%\" PRIx64 \"\\n\",\n              max_file_size);\n        return 0;\n    }\n\n    /* Check that max_file_size_from is part of our mapping list. */\n    if (!is_backing_storage_mapped(max_file_size_from)) {\n        ALOGE(\"%s: file doesn't match mapped storages (filename=%s)\\n\", __func__,\n              max_file_size_from);\n        return -1;\n    }\n\n    ALOGI(\"Using %s to determine max file size.\\n\", max_file_size_from);\n\n    /* Error if max file size source not found, possible misconfig. */\n    struct stat buf = {0};\n    int rc = stat(max_file_size_from, &buf);\n    if (rc < 0) {\n        ALOGE(\"%s: error stat'ing file (filename=%s): %s\\n\", __func__, max_file_size_from,\n              strerror(errno));\n        return -1;\n    }\n\n    /* Currently only support block device as max file size source */\n    if ((buf.st_mode & S_IFMT) != S_IFBLK) {\n        ALOGE(\"Unsupported max file size source type: %d\\n\", buf.st_mode);\n        return -1;\n    }\n\n    ALOGI(\"%s is a block device, determining block device size\\n\", max_file_size_from);\n    uint64_t max_size = 0;\n    int fd = TEMP_FAILURE_RETRY(open(max_file_size_from, O_RDONLY | O_NONBLOCK));\n    if (fd < 0) {\n        ALOGE(\"%s: failed to open backing file %s for ioctl: %s\\n\", __func__, max_file_size_from,\n              strerror(errno));\n        return -1;\n    }\n    rc = ioctl(fd, BLKGETSIZE64, &max_size);\n    if (rc < 0) {\n        ALOGE(\"%s: error calling ioctl on file (fd=%d): %s\\n\", __func__, fd, strerror(errno));\n        close(fd);\n        return -1;\n    }\n    close(fd);\n    max_file_size = max_size;\n\n    ALOGI(\"Using 0x%\" PRIx64 \" as max file size\\n\", max_file_size);\n    return 0;\n}\n\nint storage_init(const char* dirname, struct storage_mapping_node* mappings,\n                 const char* max_file_size_from) {\n    /* If there is an active DSU image, use the alternate fs mode. */\n    alternate_mode = is_gsi_running();\n\n    fs_state = SS_CLEAN;\n    for (uint i = 0; i < FD_TBL_SIZE; i++) {\n        fd_state[i] = SS_UNUSED; /* uninstalled */\n    }\n\n    ssdir_name = dirname;\n\n    storage_mapping_head = mappings;\n\n    /* Set the max file size based on incoming configuration */\n    int rc = determine_max_file_size(max_file_size_from);\n    if (rc < 0) {\n        return rc;\n    }\n\n    return 0;\n}\n\nint storage_sync_checkpoint(struct watcher* watcher) {\n    int rc;\n\n    watch_progress(watcher, \"sync fd table\");\n    /* sync fd table and reset it to clean state first */\n    for (uint fd = 0; fd < FD_TBL_SIZE; fd++) {\n        if (fd_state[fd] == SS_DIRTY) {\n            if (fs_state == SS_CLEAN) {\n                /* need to sync individual fd */\n                rc = fsync(fd);\n                if (rc < 0) {\n                    ALOGE(\"fsync for fd=%d failed: %s\\n\", fd, strerror(errno));\n                    return rc;\n                }\n            }\n            fd_state[fd] = SS_CLEAN; /* set to clean */\n        }\n    }\n\n    /* check if we need to sync all filesystems */\n    if (fs_state == SS_DIRTY) {\n        /*\n         * We sync all filesystems here because we don't know what filesystem\n         * needs syncing if there happen to be other filesystems symlinked under\n         * the root data directory. This should not happen in the normal case\n         * because our fd table is large enough to handle the few open files we\n         * use.\n         */\n         watch_progress(watcher, \"all fs sync\");\n         sync();\n         fs_state = SS_CLEAN;\n    }\n\n    watch_progress(watcher, \"done syncing\");\n\n    return 0;\n}\n"
  },
  {
    "path": "trusty/storage/proxy/storage.h",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n#pragma once\n\n#include <interface/storage/storage.h>\n#include <stdint.h>\n\n/* Defined in watchdog.h */\nstruct watcher;\n\n/* Is used for managing alternate backing storage, generally will be a block device. */\nstruct storage_mapping_node {\n    struct storage_mapping_node* next;\n    const char* file_name;\n    const char* backing_storage;\n    int fd;\n};\n\nint storage_file_delete(struct storage_msg* msg, const void* req, size_t req_len,\n                        struct watcher* watcher);\n\nint storage_file_open(struct storage_msg* msg, const void* req, size_t req_len,\n                      struct watcher* watcher);\n\nint storage_file_close(struct storage_msg* msg, const void* req, size_t req_len,\n                       struct watcher* watcher);\n\nint storage_file_write(struct storage_msg* msg, const void* req, size_t req_len,\n                       struct watcher* watcher);\n\nint storage_file_read(struct storage_msg* msg, const void* req, size_t req_len,\n                      struct watcher* watcher);\n\nint storage_file_get_size(struct storage_msg* msg, const void* req, size_t req_len,\n                          struct watcher* watcher);\n\nint storage_file_set_size(struct storage_msg* msg, const void* req, size_t req_len,\n                          struct watcher* watcher);\n\nint storage_file_get_max_size(struct storage_msg* msg, const void* req, size_t req_len,\n                              struct watcher* watcher);\n\nint storage_init(const char* dirname, struct storage_mapping_node* head,\n                 const char* max_file_size_from);\n\nint storage_sync_checkpoint(struct watcher* watcher);\n"
  },
  {
    "path": "trusty/storage/proxy/watchdog.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include \"watchdog.h\"\n\n#include <chrono>\n#include <cstdint>\n#include <mutex>\n#include <optional>\n#include <thread>\n#include <vector>\n\n#include <android-base/logging.h>\n\nstruct watcher {\n    watcher(const char* id, const struct storage_msg* request);\n    void SetState(const char* new_state);\n    void LogTimeout();\n    void LogFinished();\n\n    const char* id_;\n    uint32_t cmd_;\n    uint32_t op_id_;\n    uint32_t flags_;\n    const char* state_;\n\n    using clock = std::chrono::high_resolution_clock;\n    clock::time_point start_;\n    clock::time_point state_change_;\n    std::chrono::milliseconds Elapsed(clock::time_point end);\n\n    bool triggered_;\n};\n\nwatcher::watcher(const char* id, const struct storage_msg* request)\n    : id_(id), state_(nullptr), triggered_(false) {\n    cmd_ = request->cmd;\n    op_id_ = request->op_id;\n    flags_ = request->flags;\n\n    start_ = clock::now();\n    state_change_ = start_;\n}\n\nvoid watcher::SetState(const char* new_state) {\n    state_ = new_state;\n    state_change_ = clock::now();\n}\n\nvoid watcher::LogTimeout() {\n    if (!triggered_) {\n        triggered_ = true;\n        LOG(ERROR) << \"Storageproxyd watchdog triggered: \" << id_ << \" cmd: \" << cmd_\n                   << \" op_id: \" << op_id_ << \" flags: \" << flags_;\n    }\n    if (state_) {\n        LOG(ERROR) << \"...elapsed: \" << Elapsed(clock::now()).count() << \"ms (\" << state_ << \" \"\n                   << Elapsed(state_change_).count() << \"ms)\";\n    } else {\n        LOG(ERROR) << \"...elapsed: \" << Elapsed(clock::now()).count() << \"ms\";\n    }\n}\n\nvoid watcher::LogFinished() {\n    if (triggered_) {\n        LOG(ERROR) << \"...completed: \" << Elapsed(clock::now()).count() << \"ms\";\n    }\n}\n\nstd::chrono::milliseconds watcher::Elapsed(watcher::clock::time_point end) {\n    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start_);\n}\n\nnamespace {\n\nclass Watchdog {\n  private:\n    static constexpr std::chrono::milliseconds kDefaultTimeoutMs = std::chrono::milliseconds(500);\n    static constexpr std::chrono::milliseconds kMaxTimeoutMs = std::chrono::seconds(10);\n\n  public:\n    Watchdog() : watcher_(), done_(false) {}\n    ~Watchdog();\n    struct watcher* RegisterWatch(const char* id, const struct storage_msg* request);\n    void AddProgress(struct watcher* watcher, const char* state);\n    void UnRegisterWatch(struct watcher* watcher);\n\n  private:\n    // Syncronizes access to watcher_ and watcher_change_ between the main\n    // thread and watchdog loop thread. watcher_ may only be modified by the\n    // main thread; the watchdog loop is read-only.\n    std::mutex watcher_mutex_;\n    std::unique_ptr<struct watcher> watcher_;\n    std::condition_variable watcher_change_;\n\n    std::thread watchdog_thread_;\n    bool done_;\n\n    void WatchdogLoop();\n    void LogWatchdogTriggerLocked();\n};\n\nWatchdog gWatchdog;\n\n}  // Anonymous namespace\n\n// Assumes that caller is single-threaded. If we want to use this from a\n// multi-threaded context we need to ensure that the watchdog thread is\n// initialized safely once and accessing an existing watcher is done while the\n// watcher lock is held.\nstruct watcher* Watchdog::RegisterWatch(const char* id, const struct storage_msg* request) {\n    if (!watchdog_thread_.joinable()) {\n        watchdog_thread_ = std::thread(&Watchdog::WatchdogLoop, this);\n    }\n    if (watcher_) {\n        LOG(ERROR) << \"Replacing registered watcher \" << watcher_->id_;\n        UnRegisterWatch(watcher_.get());\n    }\n\n    struct watcher* ret = nullptr;\n    {\n        std::unique_lock<std::mutex> watcherLock(watcher_mutex_);\n        watcher_ = std::make_unique<struct watcher>(id, request);\n        ret = watcher_.get();\n    }\n    watcher_change_.notify_one();\n    return ret;\n}\n\nvoid Watchdog::UnRegisterWatch(struct watcher* watcher) {\n    {\n        std::lock_guard<std::mutex> watcherLock(watcher_mutex_);\n        if (!watcher_) {\n            LOG(ERROR) << \"Cannot unregister watcher, no watcher registered\";\n            return;\n        }\n        if (watcher_.get() != watcher) {\n            LOG(ERROR) << \"Unregistering watcher that doesn't match current watcher\";\n        }\n        watcher_->LogFinished();\n        watcher_.reset(nullptr);\n    }\n    watcher_change_.notify_one();\n}\n\nvoid Watchdog::AddProgress(struct watcher* watcher, const char* state) {\n    std::lock_guard<std::mutex> watcherLock(watcher_mutex_);\n    if (watcher_.get() != watcher) {\n        LOG(ERROR) << \"Watcher was not registered, cannot log progress: \" << state;\n        return;\n    }\n    watcher->SetState(state);\n}\n\nvoid Watchdog::WatchdogLoop() {\n    std::unique_lock<std::mutex> lock(watcher_mutex_);\n    std::chrono::milliseconds timeout = kDefaultTimeoutMs;\n\n    while (!done_) {\n        // wait for a watch to be registered\n        watcher_change_.wait(lock, [this] { return !!watcher_; });\n\n        // wait for the timeout or unregistration\n        timeout = kDefaultTimeoutMs;\n        do {\n            if (!watcher_change_.wait_for(lock, timeout, [this] { return !watcher_; })) {\n                watcher_->LogTimeout();\n                timeout = std::min(timeout * 2, kMaxTimeoutMs);\n            }\n        } while (!!watcher_);\n    }\n}\n\nWatchdog::~Watchdog() {\n    {\n        std::lock_guard<std::mutex> watcherLock(watcher_mutex_);\n        watcher_.reset(nullptr);\n        done_ = true;\n    }\n    watcher_change_.notify_one();\n    if (watchdog_thread_.joinable()) {\n        watchdog_thread_.join();\n    }\n}\n\nstruct watcher* watch_start(const char* id, const struct storage_msg* request) {\n    return gWatchdog.RegisterWatch(id, request);\n}\n\nvoid watch_progress(struct watcher* watcher, const char* state) {\n    gWatchdog.AddProgress(watcher, state);\n}\n\nvoid watch_finish(struct watcher* watcher) {\n    gWatchdog.UnRegisterWatch(watcher);\n}\n"
  },
  {
    "path": "trusty/storage/proxy/watchdog.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include \"storage.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct watcher;\n\n/**\n * watch_start() - Create a watcher for a storage request\n * @id:        Identifier string to distinguish watchers\n * @request:   Incoming request from Trusty storage service\n *\n * Create a watcher that will start logging if not finished before a timeout.\n * Only one watcher may be active at a time, and this function may only be\n * called from a single thread.\n */\nstruct watcher* watch_start(const char* id, const struct storage_msg* request);\n\n/**\n * watch_progress() - Note progress on servicing the current request\n * @watcher:   Current watcher, created by watch()\n *\n * Sets the current progress state of the watcher, to allow for more granular\n * reporting of what exactly is stuck if the timeout is reached.\n */\nvoid watch_progress(struct watcher* watcher, const char* state);\n\n/**\n * watch_finish() - Finish watching and unregister the watch\n * @watcher:   Current watcher, created by watch(). Takes ownership of this pointer.\n *\n * Finish the current watch task. This function takes ownership of the watcher\n * and destroys it, so @watcher must not be used again after calling this\n * function.\n */\nvoid watch_finish(struct watcher* watcher);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "trusty/storage/tests/Android.bp",
    "content": "//\n// Copyright (C) 2016 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n//\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_test {\n    name: \"secure-storage-unit-test\",\n    vendor: true,\n\n    cflags: [\n        \"-g\",\n        \"-Wall\",\n        \"-Werror\",\n        \"-Wno-missing-field-initializers\",\n    ],\n\n    static_libs: [\n        \"libtrustystorageinterface\",\n        \"libtrustystorage\",\n        \"libtrusty\",\n    ],\n    shared_libs: [\n        \"liblog\",\n    ],\n\n    srcs: [\"main.cpp\"],\n}\n"
  },
  {
    "path": "trusty/storage/tests/main.cpp",
    "content": "/*\n * Copyright (C) 2016 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <assert.h>\n#include <stdint.h>\n#include <gtest/gtest.h>\n\n#include <trusty/lib/storage.h>\n\n#define TRUSTY_DEVICE_NAME \"/dev/trusty-ipc-dev0\"\n\n#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))\n\nstatic inline bool is_32bit_aligned(size_t sz)\n{\n    return ((sz & 0x3) == 0);\n}\n\nstatic inline bool is_valid_size(size_t sz) {\n    return (sz > 0) && is_32bit_aligned(sz);\n}\n\nstatic bool is_valid_offset(storage_off_t off)\n{\n    return (off & 0x3) == 0ULL;\n}\n\nstatic void fill_pattern32(uint32_t *buf, size_t len, storage_off_t off)\n{\n    size_t cnt = len / sizeof(uint32_t);\n    uint32_t pattern = (uint32_t)(off / sizeof(uint32_t));\n    while (cnt--) {\n        *buf++ = pattern++;\n    }\n}\n\nstatic bool check_pattern32(const uint32_t *buf, size_t len, storage_off_t off)\n{\n    size_t cnt = len / sizeof(uint32_t);\n    uint32_t pattern = (uint32_t)(off / sizeof(uint32_t));\n    while (cnt--) {\n        if (*buf != pattern)\n            return false;\n        buf++;\n        pattern++;\n    }\n    return true;\n}\n\nstatic bool check_value32(const uint32_t *buf, size_t len, uint32_t val)\n{\n    size_t cnt = len / sizeof(uint32_t);\n    while (cnt--) {\n        if (*buf != val)\n            return false;\n        buf++;\n    }\n    return true;\n}\n\nusing testing::TestWithParam;\n\nclass StorageServiceTest : public virtual TestWithParam<const char *> {\npublic:\n    StorageServiceTest() {}\n    virtual ~StorageServiceTest() {}\n\n    virtual void SetUp() {\n        port_ = GetParam();\n        test_buf_ = NULL;\n        aux_session_ = STORAGE_INVALID_SESSION;\n        int rc = storage_open_session(TRUSTY_DEVICE_NAME, &session_, port_);\n        ASSERT_EQ(0, rc);\n    }\n\n    virtual void TearDown() {\n        if (test_buf_) {\n            delete[] test_buf_;\n            test_buf_ = NULL;\n        }\n        storage_close_session(session_);\n\n        if (aux_session_ != STORAGE_INVALID_SESSION) {\n            storage_close_session(aux_session_);\n            aux_session_ = STORAGE_INVALID_SESSION;\n        }\n    }\n\n    void WriteReadAtOffsetHelper(file_handle_t handle, size_t blk, size_t cnt, bool complete);\n\n    void WriteZeroChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, bool complete );\n    void WritePatternChunk(file_handle_t handle, storage_off_t off, size_t chunk_len, bool complete);\n    void WritePattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len, bool complete);\n\n    void ReadChunk(file_handle_t handle, storage_off_t off, size_t chunk_len,\n                   size_t head_len, size_t pattern_len, size_t tail_len);\n    void ReadPattern(file_handle_t handle, storage_off_t off, size_t data_len, size_t chunk_len);\n    void ReadPatternEOF(file_handle_t handle, storage_off_t off, size_t chunk_len, size_t exp_len);\n\nprotected:\n    const char *port_;\n    uint32_t *test_buf_;\n    storage_session_t session_;\n    storage_session_t aux_session_;\n};\n\nINSTANTIATE_TEST_CASE_P(SS_TD_Tests, StorageServiceTest,   ::testing::Values(STORAGE_CLIENT_TD_PORT));\nINSTANTIATE_TEST_CASE_P(SS_TDEA_Tests, StorageServiceTest, ::testing::Values(STORAGE_CLIENT_TDEA_PORT));\nINSTANTIATE_TEST_CASE_P(SS_TP_Tests, StorageServiceTest,   ::testing::Values(STORAGE_CLIENT_TP_PORT));\n\n\nvoid StorageServiceTest::WriteZeroChunk(file_handle_t handle, storage_off_t off,\n                                       size_t chunk_len, bool complete)\n{\n    int rc;\n    uint32_t data_buf[chunk_len/sizeof(uint32_t)];\n\n    ASSERT_PRED1(is_valid_size, chunk_len);\n    ASSERT_PRED1(is_valid_offset, off);\n\n    memset(data_buf, 0, chunk_len);\n\n    rc = storage_write(handle, off, data_buf, sizeof(data_buf),\n                       complete ? STORAGE_OP_COMPLETE : 0);\n    ASSERT_EQ((int)chunk_len, rc);\n}\n\nvoid StorageServiceTest::WritePatternChunk(file_handle_t handle, storage_off_t off,\n                                           size_t chunk_len, bool complete)\n{\n    int rc;\n    uint32_t data_buf[chunk_len/sizeof(uint32_t)];\n\n    ASSERT_PRED1(is_valid_size, chunk_len);\n    ASSERT_PRED1(is_valid_offset, off);\n\n    fill_pattern32(data_buf, chunk_len, off);\n\n    rc = storage_write(handle, off, data_buf, sizeof(data_buf),\n                       complete ? STORAGE_OP_COMPLETE : 0);\n    ASSERT_EQ((int)chunk_len, rc);\n}\n\nvoid StorageServiceTest::WritePattern(file_handle_t handle, storage_off_t off,\n                                      size_t data_len, size_t chunk_len, bool complete)\n{\n    ASSERT_PRED1(is_valid_size, data_len);\n    ASSERT_PRED1(is_valid_size, chunk_len);\n\n    while (data_len) {\n        if (data_len < chunk_len)\n            chunk_len = data_len;\n        WritePatternChunk(handle, off, chunk_len, (chunk_len == data_len) && complete);\n        ASSERT_FALSE(HasFatalFailure());\n        off += chunk_len;\n        data_len -= chunk_len;\n    }\n}\n\nvoid StorageServiceTest::ReadChunk(file_handle_t handle,\n                                   storage_off_t off, size_t chunk_len,\n                                   size_t head_len, size_t pattern_len,\n                                   size_t tail_len)\n{\n    int rc;\n    uint32_t data_buf[chunk_len/sizeof(uint32_t)];\n    uint8_t *data_ptr = (uint8_t *)data_buf;\n\n    ASSERT_PRED1(is_valid_size, chunk_len);\n    ASSERT_PRED1(is_valid_offset, off);\n    ASSERT_EQ(head_len + pattern_len + tail_len, chunk_len);\n\n    rc = storage_read(handle, off, data_buf, chunk_len);\n    ASSERT_EQ((int)chunk_len, rc);\n\n    if (head_len) {\n        ASSERT_TRUE(check_value32((const uint32_t *)data_ptr, head_len, 0));\n        data_ptr += head_len;\n        off += head_len;\n    }\n\n    if (pattern_len) {\n        ASSERT_TRUE(check_pattern32((const uint32_t *)data_ptr, pattern_len, off));\n        data_ptr += pattern_len;\n    }\n\n    if (tail_len) {\n        ASSERT_TRUE(check_value32((const uint32_t *)data_ptr, tail_len, 0));\n    }\n}\n\nvoid StorageServiceTest::ReadPattern(file_handle_t handle, storage_off_t off,\n                                     size_t data_len, size_t chunk_len)\n{\n    int rc;\n    uint32_t data_buf[chunk_len/sizeof(uint32_t)];\n\n    ASSERT_PRED1(is_valid_size, chunk_len);\n    ASSERT_PRED1(is_valid_size, data_len);\n    ASSERT_PRED1(is_valid_offset, off);\n\n    while (data_len) {\n        if (chunk_len > data_len)\n            chunk_len = data_len;\n        rc = storage_read(handle, off, data_buf, sizeof(data_buf));\n        ASSERT_EQ((int)chunk_len, rc);\n        ASSERT_TRUE(check_pattern32(data_buf, chunk_len, off));\n        off += chunk_len;\n        data_len -= chunk_len;\n    }\n}\n\nvoid StorageServiceTest::ReadPatternEOF(file_handle_t handle, storage_off_t off,\n                                        size_t chunk_len, size_t exp_len)\n{\n    int rc;\n    size_t bytes_read = 0;\n    uint32_t data_buf[chunk_len/sizeof(uint32_t)];\n\n    ASSERT_PRED1(is_valid_size, chunk_len);\n    ASSERT_PRED1(is_32bit_aligned, exp_len);\n\n    while (true) {\n         rc = storage_read(handle, off, data_buf, sizeof(data_buf));\n         ASSERT_GE(rc, 0);\n         if (rc == 0)\n             break; // end of file reached\n         ASSERT_PRED1(is_valid_size, (size_t)rc);\n         ASSERT_TRUE(check_pattern32(data_buf, rc, off));\n         off += rc;\n         bytes_read += rc;\n    }\n    ASSERT_EQ(bytes_read, exp_len);\n}\n\nTEST_P(StorageServiceTest, CreateDelete) {\n    int rc;\n    file_handle_t handle;\n    const char *fname = \"test_create_delete_file\";\n\n    // make sure test file does not exist (expect success or -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n\n    // one more time (expect -ENOENT only)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // create file (expect 0)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // try to create it again while it is still opened (expect -EEXIST)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EEXIST, rc);\n\n    // close it\n    storage_close_file(handle);\n\n    // try to create it again while it is closed (expect -EEXIST)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EEXIST, rc);\n\n    // delete file (expect 0)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // one more time (expect -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-ENOENT, rc);\n}\n\n\nTEST_P(StorageServiceTest, DeleteOpened) {\n    int rc;\n    file_handle_t handle;\n    const char *fname = \"delete_opened_test_file\";\n\n    // make sure test file does not exist (expect success or -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n\n    // one more time (expect -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // open/create file (expect 0)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // delete opened file (expect 0)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // one more time (expect -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // close file\n    storage_close_file(handle);\n\n    // one more time (expect -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-ENOENT, rc);\n}\n\n\nTEST_P(StorageServiceTest, OpenNoCreate) {\n    int rc;\n    file_handle_t handle;\n    const char *fname = \"test_open_no_create_file\";\n\n    // make sure test file does not exist (expect success or -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n\n    // open non-existing file (expect -ENOENT)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // create file (expect 0)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle);\n\n    // open existing file (expect 0)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // close it\n    storage_close_file(handle);\n\n    // delete file (expect 0)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n}\n\n\nTEST_P(StorageServiceTest, OpenOrCreate) {\n    int rc;\n    file_handle_t handle;\n    const char *fname = \"test_open_create_file\";\n\n    // make sure test file does not exist (expect success or -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n\n    // open/create a non-existing file (expect 0)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle);\n\n    // open/create an existing file (expect 0)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle);\n\n    // delete file (expect 0)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n}\n\n\nTEST_P(StorageServiceTest, OpenCreateDeleteCharset) {\n    int rc;\n    file_handle_t handle;\n    const char *fname = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz_01234.56789\";\n\n    // open/create file (expect 0)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle);\n\n    // open/create an existing file (expect 0)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle);\n\n    // delete file (expect 0)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open again\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n}\n\n\nTEST_P(StorageServiceTest, WriteReadSequential) {\n    int rc;\n    size_t blk = 2048;\n    file_handle_t handle;\n    const char *fname = \"test_write_read_sequential\";\n\n    // make sure test file does not exist (expect success or -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n\n    // create file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write a bunch of blocks (sequentially)\n    WritePattern(handle, 0, 32 * blk, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPattern(handle, 0, 32 * blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close file\n    storage_close_file(handle);\n\n    // open the same file again\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // read data back (sequentially) and check pattern again\n    ReadPattern(handle, 0, 32 * blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, OpenTruncate) {\n    int rc;\n    uint32_t val;\n    size_t blk = 2048;\n    file_handle_t handle;\n    const char *fname = \"test_open_truncate\";\n\n    // make sure test file does not exist (expect success or -ENOENT)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n\n    // create file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write some data and read it back\n    WritePatternChunk(handle, 0, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPattern(handle, 0, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n     // close file\n    storage_close_file(handle);\n\n    // reopen with truncate\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    /* try to read data back (expect no data) */\n    rc = storage_read(handle, 0LL, &val, sizeof(val));\n    ASSERT_EQ(0, rc);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, OpenSame) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    file_handle_t handle3;\n    const char *fname = \"test_open_same_file\";\n\n    // open/create file (expect 0)\n    rc = storage_open_file(session_, &handle1, fname, STORAGE_FILE_OPEN_CREATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle1);\n\n    // open an existing file first time (expect 0)\n    rc = storage_open_file(session_, &handle1, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // open the same file second time (expect error)\n    rc = storage_open_file(session_, &handle2, fname, 0, 0);\n    ASSERT_NE(0, rc);\n\n    storage_close_file(handle1);\n\n    // delete file (expect 0)\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open deleted file (expect -ENOENT)\n    rc = storage_open_file(session_, &handle3, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n}\n\n\nTEST_P(StorageServiceTest, OpenMany) {\n    int rc;\n    file_handle_t handles[10];\n    char filename[10];\n    const char *fname_fmt = \"mf%d\";\n\n    // open or create a bunch of files (expect 0)\n    for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {\n        snprintf(filename, sizeof(filename), fname_fmt, i);\n        rc = storage_open_file(session_, &handles[i], filename,\n                               STORAGE_FILE_OPEN_CREATE, STORAGE_OP_COMPLETE);\n        ASSERT_EQ(0, rc);\n    }\n\n    // check that all handles are different\n    for (uint i = 0; i < ARRAY_SIZE(handles)-1; i++) {\n        for (uint j = i+1; j < ARRAY_SIZE(handles); j++) {\n            ASSERT_NE(handles[i], handles[j]);\n        }\n    }\n\n    // close them all\n    for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {\n        storage_close_file(handles[i]);\n    }\n\n    // open all files without CREATE flags (expect 0)\n    for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {\n        snprintf(filename, sizeof(filename), fname_fmt, i);\n        rc = storage_open_file(session_, &handles[i], filename, 0, 0);\n        ASSERT_EQ(0, rc);\n    }\n\n    // check that all handles are different\n    for (uint i = 0; i < ARRAY_SIZE(handles)-1; i++) {\n        for (uint j = i+1; j < ARRAY_SIZE(handles); j++) {\n            ASSERT_NE(handles[i], handles[j]);\n        }\n    }\n\n    // close and remove all test files\n    for (uint i = 0; i < ARRAY_SIZE(handles); ++i) {\n        storage_close_file(handles[i]);\n        snprintf(filename, sizeof(filename), fname_fmt, i);\n        rc = storage_delete_file(session_, filename, STORAGE_OP_COMPLETE);\n        ASSERT_EQ(0, rc);\n    }\n}\n\n\nTEST_P(StorageServiceTest, ReadAtEOF) {\n    int rc;\n    uint32_t val;\n    size_t blk = 2048;\n    file_handle_t handle;\n    const char *fname = \"test_read_eof\";\n\n    // open/create/truncate file\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write block at offset 0\n    WritePatternChunk(handle, 0, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close file\n    storage_close_file(handle);\n\n    // open same file again\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // read the whole block back and check pattern again\n    ReadPattern(handle, 0, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read at end of file (expected 0 bytes)\n    rc = storage_read(handle, blk, &val, sizeof(val));\n    ASSERT_EQ(0, rc);\n\n    // partial read at end of the file (expected partial data)\n    ReadPatternEOF(handle, blk/2, blk, blk/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read past end of file\n    rc = storage_read(handle, blk + 2, &val, sizeof(val));\n    ASSERT_EQ(-EINVAL, rc);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, GetFileSize) {\n    int rc;\n    size_t blk = 2048;\n    storage_off_t size;\n    file_handle_t handle;\n    const char *fname = \"test_get_file_size\";\n\n    // open/create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // check file size (expect success and size == 0)\n    size = 1;\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, size);\n\n    // write block\n    WritePatternChunk(handle, 0, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check size\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ(blk, size);\n\n    // write another block\n    WritePatternChunk(handle, blk, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check size again\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ(blk*2, size);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, SetFileSize) {\n    int rc;\n    size_t blk = 2048;\n    storage_off_t size;\n    file_handle_t handle;\n    const char *fname = \"test_set_file_size\";\n\n    // open/create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // check file size (expect success and size == 0)\n    size = 1;\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, size);\n\n    // write block\n    WritePatternChunk(handle, 0, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check size\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ(blk, size);\n\n    storage_close_file(handle);\n\n    // reopen normally\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // check size again\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ(blk, size);\n\n    // set file size to half\n    rc = storage_set_file_size(handle, blk/2, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // check size again (should be half of original size)\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ(blk/2, size);\n\n    // read data back\n    ReadPatternEOF(handle, 0, blk, blk/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // set file size to 0\n    rc = storage_set_file_size(handle, 0, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // check size again (should be 0)\n    rc = storage_get_file_size(handle, &size);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0LL, size);\n\n    // try to read again\n    ReadPatternEOF(handle, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nvoid StorageServiceTest::WriteReadAtOffsetHelper(file_handle_t handle, size_t blk, size_t cnt, bool complete)\n{\n    storage_off_t off1 = blk;\n    storage_off_t off2 = blk * (cnt-1);\n\n    // write known pattern data at non-zero offset1\n    WritePatternChunk(handle, off1, blk, complete);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // write known pattern data at non-zero offset2\n    WritePatternChunk(handle, off2, blk, complete);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read data back at offset1\n    ReadPattern(handle, off1, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read data back at offset2\n    ReadPattern(handle, off2, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read partially written data at end of file(expect to get data only, no padding)\n    ReadPatternEOF(handle, off2 + blk/2, blk, blk/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read data at offset 0 (expect success and zero data)\n    ReadChunk(handle, 0, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read data from gap (expect success and zero data)\n    ReadChunk(handle, off1 + blk, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read partially written data (start pointing within written data)\n    // (expect to get written data back and zeroes at the end)\n    ReadChunk(handle, off1 + blk/2, blk, 0, blk/2, blk/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read partially written data (start pointing withing unwritten data)\n    // expect to get zeroes at the beginning and proper data at the end\n    ReadChunk(handle, off1 - blk/2, blk, blk/2, blk/2, 0);\n    ASSERT_FALSE(HasFatalFailure());\n}\n\n\nTEST_P(StorageServiceTest, WriteReadAtOffset) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t blk_cnt = 32;\n    const char *fname = \"test_write_at_offset\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write a bunch of blocks filled with zeroes\n    for (uint i = 0; i < blk_cnt; i++) {\n        WriteZeroChunk(handle, i * blk, blk, true);\n        ASSERT_FALSE(HasFatalFailure());\n    }\n\n    WriteReadAtOffsetHelper(handle, blk, blk_cnt, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, WriteSparse) {\n    int rc;\n    file_handle_t handle;\n    const char *fname = \"test_write_sparse\";\n\n    // open/create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write value past en of file\n    uint32_t val = 0xDEADBEEF;\n    rc = storage_write(handle, 1, &val, sizeof(val), STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n// Persistent 32k\n\nTEST_P(StorageServiceTest, CreatePersistent32K) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t file_size = 32768;\n    const char *fname = \"test_persistent_32K_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write a bunch of blocks filled with pattern\n    WritePattern(handle, 0, file_size, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, ReadPersistent32k) {\n    int rc;\n    file_handle_t handle;\n    size_t exp_len = 32 * 1024;\n    const char *fname = \"test_persistent_32K_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    ReadPatternEOF(handle, 0, 2048, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPatternEOF(handle, 0, 1024, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPatternEOF(handle, 0,  332, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, CleanUpPersistent32K) {\n    int rc;\n    const char *fname = \"test_persistent_32K_file\";\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n}\n\n// Persistent 1M\nTEST_P(StorageServiceTest, CreatePersistent1M_4040) {\n    int rc;\n    file_handle_t handle;\n    size_t file_size = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write a bunch of blocks filled with pattern\n    WritePattern(handle, 0, file_size, 4040, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, CreatePersistent1M_2032) {\n    int rc;\n    file_handle_t handle;\n    size_t file_size = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write a bunch of blocks filled with pattern\n    WritePattern(handle, 0, file_size, 2032, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\n\nTEST_P(StorageServiceTest, CreatePersistent1M_496) {\n    int rc;\n    file_handle_t handle;\n    size_t file_size = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write a bunch of blocks filled with pattern\n    WritePattern(handle, 0, file_size, 496, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, CreatePersistent1M_240) {\n    int rc;\n    file_handle_t handle;\n    size_t file_size = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write a bunch of blocks filled with pattern\n    WritePattern(handle, 0, file_size, 240, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, ReadPersistent1M_4040) {\n    int rc;\n    file_handle_t handle;\n    size_t exp_len = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    ReadPatternEOF(handle, 0, 4040, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, ReadPersistent1M_2032) {\n    int rc;\n    file_handle_t handle;\n    size_t exp_len = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    ReadPatternEOF(handle, 0, 2032, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, ReadPersistent1M_496) {\n    int rc;\n    file_handle_t handle;\n    size_t exp_len = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    ReadPatternEOF(handle, 0, 496, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, ReadPersistent1M_240) {\n    int rc;\n    file_handle_t handle;\n    size_t exp_len = 1024 * 1024;\n    const char *fname = \"test_persistent_1M_file\";\n\n    // create/truncate file.\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    ReadPatternEOF(handle, 0, 240, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close but do not delete file\n    storage_close_file(handle);\n}\n\nTEST_P(StorageServiceTest, CleanUpPersistent1M) {\n    int rc;\n    const char *fname = \"test_persistent_1M_file\";\n    rc = storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n    rc = (rc == -ENOENT) ? 0 : rc;\n    ASSERT_EQ(0, rc);\n}\n\nTEST_P(StorageServiceTest, WriteReadLong) {\n    int rc;\n    file_handle_t handle;\n    size_t wc = 10000;\n    const char *fname = \"test_write_read_long\";\n\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    test_buf_ = new uint32_t[wc];\n    fill_pattern32(test_buf_, wc * sizeof(uint32_t), 0);\n    rc = storage_write(handle, 0, test_buf_, wc * sizeof(uint32_t), STORAGE_OP_COMPLETE);\n    ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc);\n\n    rc = storage_read(handle, 0, test_buf_, wc * sizeof(uint32_t));\n    ASSERT_EQ((int)(wc * sizeof(uint32_t)), rc);\n    ASSERT_TRUE(check_pattern32(test_buf_, wc * sizeof(uint32_t), 0));\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n// Negative tests\n\nTEST_P(StorageServiceTest, OpenInvalidFileName) {\n    int rc;\n    file_handle_t handle;\n    const char *fname1 = \"\";\n    const char *fname2 = \"ffff$ffff\";\n    const char *fname3 = \"ffff\\\\ffff\";\n    char max_name[STORAGE_MAX_NAME_LENGTH_BYTES+1];\n\n    rc = storage_open_file(session_, &handle, fname1,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    rc = storage_open_file(session_, &handle, fname2,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    rc = storage_open_file(session_, &handle, fname3,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    /* max name */\n    memset(max_name, 'a', sizeof(max_name));\n    max_name[sizeof(max_name)-1] = 0;\n\n    rc = storage_open_file(session_, &handle, max_name,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    max_name[sizeof(max_name)-2] = 0;\n    rc = storage_open_file(session_, &handle, max_name,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    storage_close_file(handle);\n    storage_delete_file(session_, max_name, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, BadFileHnadle) {\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle1;\n    const char *fname = \"test_invalid_file_handle\";\n\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    handle1 = handle + 1;\n\n    // write to invalid file handle\n    uint32_t val = 0xDEDBEEF;\n    rc = storage_write(handle1,  0, &val, sizeof(val), STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // read from invalid handle\n    rc = storage_read(handle1,  0, &val, sizeof(val));\n    ASSERT_EQ(-EINVAL, rc);\n\n    // set size\n    rc = storage_set_file_size(handle1,  0, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // get size\n    storage_off_t fsize = (storage_off_t)(-1);\n    rc = storage_get_file_size(handle1,  &fsize);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // close (there is no way to check errors here)\n    storage_close_file(handle1);\n\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, ClosedFileHnadle) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    const char *fname1 = \"test_invalid_file_handle1\";\n    const char *fname2 = \"test_invalid_file_handle2\";\n\n    rc = storage_open_file(session_, &handle1, fname1,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(session_, &handle2, fname2,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // close first file handle\n    storage_close_file(handle1);\n\n    // write to invalid file handle\n    uint32_t val = 0xDEDBEEF;\n    rc = storage_write(handle1,  0, &val, sizeof(val), STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // read from invalid handle\n    rc = storage_read(handle1,  0, &val, sizeof(val));\n    ASSERT_EQ(-EINVAL, rc);\n\n    // set size\n    rc = storage_set_file_size(handle1,  0, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // get size\n    storage_off_t fsize = (storage_off_t)(-1);\n    rc = storage_get_file_size(handle1,  &fsize);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // close (there is no way to check errors here)\n    storage_close_file(handle1);\n\n    // clean up\n    storage_close_file(handle2);\n    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);\n    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);\n}\n\n// Transactions\n\nTEST_P(StorageServiceTest, TransactDiscardInactive) {\n    int rc;\n\n    // discard current transaction (there should not be any)\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // try it again\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n}\n\nTEST_P(StorageServiceTest, TransactCommitInactive) {\n    int rc;\n\n    // try to commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // try it again\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n}\n\nTEST_P(StorageServiceTest, TransactDiscardWrite) {\n\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_discard_write\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // write (without commit)\n    WritePattern(handle, 0, exp_len, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // abort current transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // cleanup\n    storage_close_file( handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactDiscardWriteAppend) {\n\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_write_append\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data with commit\n    WritePattern(handle, 0, exp_len/2, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // write data without commit\n    WritePattern(handle, exp_len/2, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size (should be exp_len)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // discard transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check file size, it should be exp_len/2\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/2, fsize);\n\n    // check file data\n    ReadPatternEOF(handle, 0, blk, exp_len/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactDiscardWriteRead) {\n\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_discard_write_read\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // Fill with zeroes (with commit)\n    for (uint i = 0; i < 32; i++) {\n        WriteZeroChunk(handle, i * blk, blk, true);\n        ASSERT_FALSE(HasFatalFailure());\n    }\n\n    // check that test chunk is filled with zeroes\n    ReadChunk(handle, blk, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // write test pattern (without commit)\n    WritePattern(handle, blk, blk, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read it back an check pattern\n    ReadChunk(handle, blk, blk, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // abort current transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // read same chunk back (should be filled with zeros)\n    ReadChunk(handle, blk, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactDiscardWriteMany) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    size_t blk = 2048;\n    size_t exp_len1 = 32 * 1024;\n    size_t exp_len2 = 31 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname1 = \"test_transact_discard_write_file1\";\n    const char *fname2 = \"test_transact_discard_write_file2\";\n\n    // open create truncate (with commit)\n    rc = storage_open_file(session_, &handle1, fname1,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate (with commit)\n    rc = storage_open_file(session_, &handle2, fname2,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // file1: fill file with pattern (without commit)\n    WritePattern(handle1, 0, exp_len1, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // file2: fill file with pattern (without commit)\n    WritePattern(handle2, 0, exp_len2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size, it should be exp_len1\n    rc = storage_get_file_size(handle1, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len1, fsize);\n\n    // check file size, it should be exp_len2\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len2, fsize);\n\n    // commit transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check file size, it should be exp_len1\n    rc = storage_get_file_size(handle1, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // check file size, it should be exp_len2\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // check data\n    ReadPatternEOF(handle1, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPatternEOF(handle2, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);\n    storage_close_file(handle2);\n    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactDiscardTruncate) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_discard_truncate\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // close file\n    storage_close_file(handle);\n\n    // open truncate file (without commit)\n    rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // abort current transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check file size (should be an oruginal size)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactDiscardSetSize) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_discard_set_size\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // set file size to half of original (no commit)\n    rc = storage_set_file_size(handle,  (storage_off_t)exp_len/2, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/2, fsize);\n\n    // set file size to 1/3 of original (no commit)\n    rc = storage_set_file_size(handle,  (storage_off_t)exp_len/3, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/3, fsize);\n\n    // abort current transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check file size (should be an original size)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactDiscardDelete) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_discard_delete\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close it\n    storage_close_file(handle);\n\n    // delete file (without commit)\n    rc = storage_delete_file(session_, fname, 0);\n    ASSERT_EQ(0, rc);\n\n    // try to open it (should fail)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // abort current transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // try to open it\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size (should be an original size)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactDiscardDelete2) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_discard_delete\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // delete file (without commit)\n    rc = storage_delete_file(session_, fname, 0);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle);\n\n    // try to open it (should fail)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // abort current transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // try to open it\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size (should be an original size)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactDiscardCreate) {\n    int rc;\n    file_handle_t handle;\n    const char *fname = \"test_transact_discard_create_excl\";\n\n    // delete test file just in case\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n\n    // create file (without commit)\n    rc = storage_open_file(session_, &handle, fname,\n                               STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                               0);\n    ASSERT_EQ(0, rc);\n\n    // abort current transaction\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactCommitWrites) {\n\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle_aux;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_commit_writes\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open the same file in aux session\n    rc = storage_open_file(aux_session_, &handle_aux, fname,  0, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size, it should be 0\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // write data in primary session (without commit)\n    WritePattern(handle, 0, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // write more data in primary session (without commit)\n    WritePattern(handle, exp_len/2, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size in aux session, it should still be 0\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // check file size of aux session, should fail\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // abort transaction in aux session to recover\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check file size in aux session, it should be exp_len\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // check file size in primary session, it should be exp_len\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // check data in primary session\n    ReadPatternEOF(handle, 0, blk, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check data in aux session\n    ReadPatternEOF(handle_aux, 0, blk, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_close_file(handle_aux);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactCommitWrites2) {\n\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle_aux;\n    size_t blk = 2048;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_commit_writes2\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open the same file in separate session\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // discard transaction in aux_session\n    rc = storage_end_transaction(aux_session_,  false);\n    ASSERT_EQ(0, rc);\n\n    // Fill with zeroes (with commit)\n    for (uint i = 0; i < 8; i++) {\n        WriteZeroChunk(handle, i * blk, blk, true);\n        ASSERT_FALSE(HasFatalFailure());\n    }\n\n    // check that test chunks are filled with zeroes\n    ReadChunk(handle, blk, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadChunk(handle, 2 * blk, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // write test pattern (without commit)\n    WritePattern(handle, blk, blk, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // write test pattern (without commit)\n    WritePattern(handle, 2 * blk, blk, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read it back and check pattern\n    ReadChunk(handle, blk, blk, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadChunk(handle, 2 * blk, blk, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // In aux session it still should be empty\n    ReadChunk(handle_aux, blk, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadChunk(handle_aux, 2 * blk, blk, blk, 0, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // read same chunks back in primary session\n    ReadChunk(handle, blk, blk, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadChunk(handle, 2 * blk, blk, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read same chunks back in aux session (should fail)\n    uint32_t val;\n    rc = storage_read(handle_aux, blk, &val, sizeof(val));\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_read(handle_aux, 2 * blk, &val, sizeof(val));\n    ASSERT_EQ(-EBUSY, rc);\n\n    // abort transaction in aux session\n    rc = storage_end_transaction(aux_session_,  false);\n    ASSERT_EQ(0, rc);\n\n    // read same chunk again in aux session\n    ReadChunk(handle_aux, blk, blk, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadChunk(handle_aux, 2 * blk, blk, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n\n    // cleanup\n    storage_close_file(handle);\n    storage_close_file(handle_aux);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactCommitSetSize) {\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle_aux;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_commit_set_size\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open the same file in separate session\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // same in aux session\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // set file size to half of original (no commit)\n    rc = storage_set_file_size(handle,  (storage_off_t)exp_len/2, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/2, fsize);\n\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // set file size to 1/3 of original (no commit)\n    rc = storage_set_file_size(handle,  (storage_off_t)exp_len/3, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/3, fsize);\n\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // check file size (should be 1/3 of an original size)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/3, fsize);\n\n    // check file size from aux session\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // abort transaction in aux_session\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check again\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/3, fsize);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_close_file(handle_aux);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactCommitDelete) {\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle_aux;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    const char *fname = \"test_transact_commit_delete\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close it\n    storage_close_file(handle);\n\n    // open the same file in separate session\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle_aux);\n\n    // delete file (without commit)\n    rc = storage_delete_file(session_, fname, 0);\n    ASSERT_EQ(0, rc);\n\n    // try to open it (should fail)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // open the same file in separate session (should be fine)\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n    storage_close_file(handle_aux);\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // try to open it in primary session (still fails)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // open the same file in aux session (should also fail)\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n}\n\n\nTEST_P(StorageServiceTest, TransactCommitTruncate) {\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle_aux;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_commit_truncate\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // close file\n    storage_close_file(handle);\n\n    // check from different session\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // open truncate file (without commit)\n    rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // check file size (should be 0)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // check file size in aux session (should be -EBUSY)\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // abort transaction in aux session\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // check again\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_close_file(handle_aux);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactCommitCreate) {\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle_aux;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_commit_create\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // delete test file just in case\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n\n    // check from aux session\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // create file (without commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           0);\n    ASSERT_EQ(0, rc);\n\n    // check file size\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // close file\n    storage_close_file(handle);\n\n    // check from aux session (should fail)\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // check open from normal session\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // check open from aux session (should succeed)\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // cleanup\n    storage_close_file(handle);\n    storage_close_file(handle_aux);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactCommitCreateMany) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    file_handle_t handle1_aux;\n    file_handle_t handle2_aux;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname1 = \"test_transact_commit_create1\";\n    const char *fname2 = \"test_transact_commit_create2\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // delete test file just in case\n    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);\n    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);\n\n    // create file (without commit)\n    rc = storage_open_file(session_, &handle1, fname1,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           0);\n    ASSERT_EQ(0, rc);\n\n    // create file (without commit)\n    rc = storage_open_file(session_, &handle2, fname2,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           0);\n    ASSERT_EQ(0, rc);\n\n    // check file sizes\n    rc = storage_get_file_size(handle1, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    rc = storage_get_file_size(handle1, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // close files\n    storage_close_file(handle1);\n    storage_close_file(handle2);\n\n    // open files from aux session\n    rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // open from primary session\n    rc = storage_open_file(session_, &handle1, fname1, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(session_, &handle2, fname2, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // open from aux session\n    rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle1_aux);\n    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);\n    storage_close_file(handle2);\n    storage_close_file(handle2_aux);\n    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactCommitWriteMany) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    file_handle_t handle1_aux;\n    file_handle_t handle2_aux;\n    size_t blk = 2048;\n    size_t exp_len1 = 32 * 1024;\n    size_t exp_len2 = 31 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname1 = \"test_transact_commit_write_file1\";\n    const char *fname2 = \"test_transact_commit_write_file2\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate (with commit)\n    rc = storage_open_file(session_, &handle1, fname1,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate (with commit)\n    rc = storage_open_file(session_, &handle2, fname2,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // open same files from aux session\n    rc = storage_open_file(aux_session_, &handle1_aux, fname1, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(aux_session_, &handle2_aux, fname2, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // file1: fill file with pattern (without commit)\n    WritePattern(handle1, 0, exp_len1, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // file2: fill file with pattern (without commit)\n    WritePattern(handle2, 0, exp_len2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size, it should be exp_len1\n    rc = storage_get_file_size(handle1, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len1, fsize);\n\n    // check file size, it should be exp_len2\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len2, fsize);\n\n    // check file sizes from aux session (should be 0)\n    rc = storage_get_file_size(handle1_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    rc = storage_get_file_size(handle2_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // commit transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // check file size, it should be exp_len1\n    rc = storage_get_file_size(handle1, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len1, fsize);\n\n    // check file size, it should be exp_len2\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len2, fsize);\n\n    // check from aux session (should be -EBUSY)\n    rc = storage_get_file_size(handle1_aux, &fsize);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // abort transaction in aux session\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // and check again\n    rc = storage_get_file_size(handle1_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len1, fsize);\n\n    rc = storage_get_file_size(handle2_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len2, fsize);\n\n    // check data\n    ReadPatternEOF(handle1, 0, blk, exp_len1);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPatternEOF(handle2, 0, blk, exp_len2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPatternEOF(handle1_aux, 0, blk, exp_len1);\n    ASSERT_FALSE(HasFatalFailure());\n\n    ReadPatternEOF(handle2_aux, 0, blk, exp_len2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle1_aux);\n    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);\n    storage_close_file(handle2);\n    storage_close_file(handle2_aux);\n    storage_delete_file(session_, fname2, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactCommitDeleteCreate) {\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle_aux;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_delete_create\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write data (with commit)\n    WritePattern(handle, 0, exp_len, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close it\n    storage_close_file(handle);\n\n    // delete file (without commit)\n    rc = storage_delete_file(session_, fname, 0);\n    ASSERT_EQ(0, rc);\n\n    // try to open it (should fail)\n    rc = storage_open_file(session_, &handle, fname, 0, 0);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // try to open it in aux session (should succeed)\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // create file with the same name (no commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_CREATE_EXCLUSIVE,\n                           0);\n    ASSERT_EQ(0, rc);\n\n    // write half of data (with commit)\n    WritePattern(handle, 0, exp_len/2, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check file size (should be half)\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/2, fsize);\n\n    // commit transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // check data from primary session\n    ReadPatternEOF(handle, 0, blk, exp_len/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // check from aux session (should fail)\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // abort trunsaction in aux session\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // and try again (should still fail)\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // close file and reopen it again\n    storage_close_file(handle_aux);\n    rc = storage_open_file(aux_session_, &handle_aux, fname, 0, 0);\n    ASSERT_EQ(0, rc);\n\n    // try it again (should succeed)\n    rc = storage_get_file_size(handle_aux, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/2, fsize);\n\n    // check data\n    ReadPatternEOF(handle_aux, 0, blk, exp_len/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_close_file(handle_aux);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, TransactRewriteExistingTruncate) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    const char *fname = \"test_transact_rewrite_existing_truncate\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // close it\n    storage_close_file(handle);\n\n    // up\n    for (uint i = 1; i < 32; i++) {\n        // open truncate (no commit)\n        rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);\n        ASSERT_EQ(0, rc);\n\n        // write data (with commit)\n        WritePattern(handle, 0, i * blk, blk, true);\n        ASSERT_FALSE(HasFatalFailure());\n\n        // close\n        storage_close_file(handle);\n    }\n\n    // down\n    for (uint i = 1; i < 32; i++) {\n        // open truncate (no commit)\n        rc = storage_open_file(session_, &handle, fname, STORAGE_FILE_OPEN_TRUNCATE, 0);\n        ASSERT_EQ(0, rc);\n\n        // write data (with commit)\n        WritePattern(handle, 0, (32 - i) * blk, blk, true);\n        ASSERT_FALSE(HasFatalFailure());\n\n        // close\n        storage_close_file(handle);\n    }\n\n    // cleanup\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactRewriteExistingSetSize) {\n    int rc;\n    file_handle_t handle;\n    size_t blk = 2048;\n    const char *fname = \"test_transact_rewrite_existing_set_size\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // close it\n    storage_close_file(handle);\n\n    // up\n    for (uint i = 1; i < 32; i++) {\n        // open truncate (no commit)\n        rc = storage_open_file(session_, &handle, fname, 0, 0);\n        ASSERT_EQ(0, rc);\n\n        // write data (with commit)\n        WritePattern(handle, 0, i * blk, blk, false);\n        ASSERT_FALSE(HasFatalFailure());\n\n        // update size (with commit)\n        rc = storage_set_file_size(handle, i * blk, STORAGE_OP_COMPLETE);\n        ASSERT_EQ(0, rc);\n\n        // close\n        storage_close_file(handle);\n    }\n\n    // down\n    for (uint i = 1; i < 32; i++) {\n        // open trancate (no commit)\n        rc = storage_open_file(session_, &handle, fname, 0, 0);\n        ASSERT_EQ(0, rc);\n\n        // write data (with commit)\n        WritePattern(handle, 0, (32 - i) * blk, blk, false);\n        ASSERT_FALSE(HasFatalFailure());\n\n        // update size (with commit)\n        rc = storage_set_file_size(handle, (32 - i) * blk, STORAGE_OP_COMPLETE);\n        ASSERT_EQ(0, rc);\n\n        // close\n        storage_close_file(handle);\n    }\n\n    // cleanup\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, TransactResumeAfterNonFatalError) {\n\n    int rc;\n    file_handle_t handle;\n    file_handle_t handle1;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_resume_writes\";\n\n    // open create truncate file (with commit)\n    rc = storage_open_file(session_, &handle, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // write (without commit)\n    WritePattern(handle, 0, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // issue some commands that should fail with non-fatal errors\n\n    // write past end of file\n    uint32_t val = 0xDEDBEEF;\n    rc = storage_write(handle,  exp_len/2 + 1, &val, sizeof(val), 0);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // read past end of file\n    rc = storage_read(handle, exp_len/2 + 1, &val, sizeof(val));\n    ASSERT_EQ(-EINVAL, rc);\n\n    // try to extend file past end of file\n    rc = storage_set_file_size(handle, exp_len/2 + 1, 0);\n    ASSERT_EQ(-EINVAL, rc);\n\n    // open non existing file\n    rc = storage_open_file(session_, &handle1, \"foo\",\n                           STORAGE_FILE_OPEN_TRUNCATE, STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // delete non-existing file\n    rc = storage_delete_file(session_, \"foo\", STORAGE_OP_COMPLETE);\n    ASSERT_EQ(-ENOENT, rc);\n\n    // then resume writinga (without commit)\n    WritePattern(handle, exp_len/2, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // commit current transaction\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // check file size, it should be exp_len\n    rc = storage_get_file_size(handle, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // check data\n    ReadPatternEOF(handle, 0, blk, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle);\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\n// Transaction Collisions\n\nTEST_P(StorageServiceTest, Transact2_WriteNC) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    size_t blk = 2048;\n    const char *fname1 = \"test_transact_f1\";\n    const char *fname2 = \"test_transact_f2\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(session_, &handle1, fname1,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(aux_session_, &handle2, fname2,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // session 1\n    WritePattern(handle1, 0, blk, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read it back\n    ReadPatternEOF(handle1, 0, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // session 2\n    WritePattern(handle2, 0, blk, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read it back\n    ReadPatternEOF(handle2, 0, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle2);\n\n    storage_delete_file(session_, fname1, STORAGE_OP_COMPLETE);\n    storage_delete_file(aux_session_, fname2, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, Transact2_DeleteNC) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    size_t blk = 2048;\n    const char *fname1 = \"test_transact_delete_f1\";\n    const char *fname2 = \"test_transact_delete_f2\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(session_, &handle1, fname1,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_open_file(aux_session_, &handle2, fname2,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // session 1\n    WritePattern(handle1, 0, blk, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read it back\n    ReadPatternEOF(handle1, 0, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // session 2\n    WritePattern(handle2, 0, blk, blk, true);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // read it back\n    ReadPatternEOF(handle2, 0, blk, blk);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // close files and delete them\n    storage_close_file(handle1);\n    storage_delete_file(session_, fname1, 0);\n\n    storage_close_file(handle2);\n    storage_delete_file(aux_session_, fname2, 0);\n\n    // commit\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    rc = storage_end_transaction(aux_session_, true);\n    ASSERT_EQ(0, rc);\n}\n\n\nTEST_P(StorageServiceTest, Transact2_Write_Read) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_writeRead\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // S1: open create truncate file\n    rc = storage_open_file(session_, &handle1, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S2: open the same file\n    rc = storage_open_file(aux_session_, &handle2, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S1: write (no commit)\n    WritePattern(handle1, 0, exp_len, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S1: read it back\n    ReadPatternEOF(handle1, 0, blk, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S2: check file size, it should be 0\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // S2: read it back (should no data)\n    ReadPatternEOF(handle2, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S1: commit\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // S2: check file size, it should fail\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // S2: abort transaction\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // S2: check file size again, it should be exp_len\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // S2: read it again (should be exp_len)\n    ReadPatternEOF(handle2, 0, blk, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle2);\n\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, Transact2_Write_Write_Commit_Commit) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    file_handle_t handle3;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_write_write_commit_commit\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // S1: open create truncate file\n    rc = storage_open_file(session_, &handle1, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S2: open the same file\n    rc = storage_open_file(aux_session_, &handle2, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S1: write (no commit)\n    WritePattern(handle1, 0, exp_len, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S2: write (no commit)\n    WritePattern(handle2, 0, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S1: commit\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // S2: read/write/get/set size/delete (all should fail)\n    uint32_t val = 0;\n    rc = storage_read(handle2, 0, &val, sizeof(val));\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_write(handle2, 0, &val, sizeof(val), 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_set_file_size(handle2,  fsize, 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_delete_file(aux_session_, fname, 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_open_file(aux_session_, &handle3, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // S2: commit (should fail, and failed state should be cleared)\n    rc = storage_end_transaction(aux_session_, true);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // S2: check file size, it should be exp_len\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // S2: read it again (should be exp_len)\n    ReadPatternEOF(handle2, 0, blk, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle2);\n\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, Transact2_Write_Write_Commit_Discard) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    file_handle_t handle3;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_write_write_commit_discard\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // S1: open create truncate file\n    rc = storage_open_file(session_, &handle1, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S2: open the same file\n    rc = storage_open_file(aux_session_, &handle2, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S1: write (no commit)\n    WritePattern(handle1, 0, exp_len, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S2: write (no commit)\n    WritePattern(handle2, 0, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S1: commit\n    rc = storage_end_transaction(session_, true);\n    ASSERT_EQ(0, rc);\n\n    // S2: read/write/get/set size/delete (all should fail)\n    uint32_t val = 0;\n    rc = storage_read(handle2, 0, &val, sizeof(val));\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_write(handle2, 0, &val, sizeof(val), 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_set_file_size(handle2,  fsize, 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_delete_file(aux_session_, fname, 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    rc = storage_open_file(aux_session_, &handle3, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE, 0);\n    ASSERT_EQ(-EBUSY, rc);\n\n    // S2: discard (should fail, and failed state should be cleared)\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // S2: check file size, it should be exp_len\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len, fsize);\n\n    // S2: read it again (should be exp_len)\n    ReadPatternEOF(handle2, 0, blk, exp_len);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle2);\n\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\nTEST_P(StorageServiceTest, Transact2_Write_Write_Discard_Commit) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_write_write_discard_commit\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // S1: open create truncate file\n    rc = storage_open_file(session_, &handle1, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S2: open the same file\n    rc = storage_open_file(aux_session_, &handle2, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S1: write (no commit)\n    WritePattern(handle1, 0, exp_len, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S2: write (no commit)\n    WritePattern(handle2, 0, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S1: discard\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // S2: commit (should succeed)\n    rc = storage_end_transaction(aux_session_, true);\n    ASSERT_EQ(0, rc);\n\n    // S2: check file size, it should be exp_len\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)exp_len/2, fsize);\n\n    // S2: read it again (should be exp_len)\n    ReadPatternEOF(handle2, 0, blk, exp_len/2);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle2);\n\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n\nTEST_P(StorageServiceTest, Transact2_Write_Write_Discard_Discard) {\n    int rc;\n    file_handle_t handle1;\n    file_handle_t handle2;\n    size_t blk = 2048;\n    size_t exp_len = 32 * 1024;\n    storage_off_t fsize = (storage_off_t)(-1);\n    const char *fname = \"test_transact_write_write_discard_Discard\";\n\n    // open second session\n    rc = storage_open_session(TRUSTY_DEVICE_NAME, &aux_session_, port_);\n    ASSERT_EQ(0, rc);\n\n    // S1: open create truncate file\n    rc = storage_open_file(session_, &handle1, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S2: open the same file\n    rc = storage_open_file(aux_session_, &handle2, fname,\n                           STORAGE_FILE_OPEN_CREATE | STORAGE_FILE_OPEN_TRUNCATE,\n                           STORAGE_OP_COMPLETE);\n    ASSERT_EQ(0, rc);\n\n    // S1: write (no commit)\n    WritePattern(handle1, 0, exp_len, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S2: write (no commit)\n    WritePattern(handle2, 0, exp_len/2, blk, false);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // S1: discard\n    rc = storage_end_transaction(session_, false);\n    ASSERT_EQ(0, rc);\n\n    // S2: discard\n    rc = storage_end_transaction(aux_session_, false);\n    ASSERT_EQ(0, rc);\n\n    // S2: check file size, it should be 0\n    rc = storage_get_file_size(handle2, &fsize);\n    ASSERT_EQ(0, rc);\n    ASSERT_EQ((storage_off_t)0, fsize);\n\n    // S2: read it again (should be 0)\n    ReadPatternEOF(handle2, 0, blk, 0);\n    ASSERT_FALSE(HasFatalFailure());\n\n    // cleanup\n    storage_close_file(handle1);\n    storage_close_file(handle2);\n\n    storage_delete_file(session_, fname, STORAGE_OP_COMPLETE);\n}\n\n"
  },
  {
    "path": "trusty/sysprops/Android.bp",
    "content": "sysprop_library {\n    name: \"trusty-properties\",\n    srcs: [\"android/sysprop/trusty/security_vm.sysprop\"],\n    property_owner: \"Platform\",\n    api_packages: [\"android.sysprop.trusty\"],\n    apex_available: [\n        \"//apex_available:platform\",\n    ],\n}\n\nrust_binary {\n    name: \"trusty-properties-example\",\n    srcs: [\"example.rs\"],\n    rustlibs: [\"libtrusty_properties_rust\"],\n}\n"
  },
  {
    "path": "trusty/sysprops/android/sysprop/trusty/security_vm.sysprop",
    "content": "# Copyright (C) 2025 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This module accesses properties regarding the Trusty VM that runs apps\n# used to provide security for the system, such as Keymint or Gatekeeper.\n\nmodule: \"android.sysprop.trusty.security_vm\"\nowner: Platform\n\n# The default Context Identifier to connect to Trusty over vsock.\nprop {\n    api_name: \"vm_cid\"\n    prop_name: \"trusty.security_vm.vm_cid\"\n    type: Integer\n    scope: Internal\n    access: Readonly\n}\n\n# Signals when a nonsecure VM is ready.\n#\n# This is used to launch dependent HALs.\n#\n# Trusty security VMs come in two flavors: non-secure and secure.\n#\n# 1. Non-secure VMs run on emulated environments like Cuttlefish, which lack\n#    pVM firmware and TEE support. Consequently, KeyMint's root-of-trust data\n#    is passed into the VM from the host's HAL, and an RPMB proxy provides\n#    secure storage.\n# 2. Secure VMs run on physical devices. Here, pVM firmware handles the\n#    transfer of root-of-trust data via DeviceTree, and a TEE provides secure\n#    storage.\nprop {\n    api_name: \"nonsecure_vm_ready\"\n    prop_name: \"trusty.security_vm.nonsecure_vm_ready\"\n    type: Boolean\n    scope: Internal\n    access: Readonly\n}\n\n# The Trusty Security VM is enabled.\nprop {\n    api_name: \"enabled\"\n    prop_name: \"trusty.security_vm.enabled\"\n    type: Boolean\n    scope: Public\n    access: Readonly\n}\n\n# KeyMint is enabled in the Trusty Security VM.\nprop {\n    api_name: \"keymint_enabled\"\n    prop_name: \"trusty.security_vm.keymint.enabled\"\n    type: Boolean\n    scope: Public\n    access: Readonly\n}\n"
  },
  {
    "path": "trusty/sysprops/api/trusty-properties-current.txt",
    "content": "props {\n  module: \"android.sysprop.trusty.security_vm\"\n  prop {\n    api_name: \"enabled\"\n    prop_name: \"trusty.security_vm.enabled\"\n  }\n  prop {\n    api_name: \"keymint_enabled\"\n    prop_name: \"trusty.security_vm.keymint.enabled\"\n  }\n}\n"
  },
  {
    "path": "trusty/sysprops/api/trusty-properties-latest.txt",
    "content": "props {\n  module: \"android.sysprop.trusty.security_vm\"\n  prop {\n    api_name: \"enabled\"\n    prop_name: \"trusty.security_vm.enabled\"\n  }\n  prop {\n    api_name: \"keymint_enabled\"\n    prop_name: \"trusty.security_vm.keymint.enabled\"\n  }\n}\n"
  },
  {
    "path": "trusty/sysprops/example.rs",
    "content": "//! Example showing how to access the `trusty.security_vm.vm_cid` system property with Rust.\n\nuse trusty_properties::security_vm;\n\nfn main() {\n    match security_vm::vm_cid() {\n        Ok(Some(cid)) => println!(\"CID: {cid}\"),\n        Ok(None) => println!(\"CID property not set\"),\n        Err(e) => println!(\"Error: {e:?}\"),\n    }\n}\n"
  },
  {
    "path": "trusty/test/binder/aidl/com/android/trusty/binder/test/ByteEnum.aidl",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.trusty.binder.test;\n\n/*\n * Hello, world!\n */\n@Backing(type=\"byte\")\nenum ByteEnum {\n    // Comment about FOO.\n    FOO = 1,\n    BAR = 2,\n    BAZ,\n}\n"
  },
  {
    "path": "trusty/test/binder/aidl/com/android/trusty/binder/test/ITestService.aidl",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.trusty.binder.test;\n\nimport com.android.trusty.binder.test.ByteEnum;\nimport com.android.trusty.binder.test.IntEnum;\nimport com.android.trusty.binder.test.LongEnum;\n\ninterface ITestService {\n    const @utf8InCpp String PORT = \"com.android.trusty.binder.test.service\";\n\n    const int TEST_CONSTANT = 42;\n    const int TEST_CONSTANT2 = -42;\n    const int TEST_CONSTANT3 = +42;\n    const int TEST_CONSTANT4 = +4;\n    const int TEST_CONSTANT5 = -4;\n    const int TEST_CONSTANT6 = -0;\n    const int TEST_CONSTANT7 = +0;\n    const int TEST_CONSTANT8 = 0;\n    const int TEST_CONSTANT9 = 0x56;\n    const int TEST_CONSTANT10 = 0xa5;\n    const int TEST_CONSTANT11 = 0xFA;\n    const int TEST_CONSTANT12 = 0xffffffff;\n\n    const byte BYTE_TEST_CONSTANT = 17;\n    const long LONG_TEST_CONSTANT = 1L << 40;\n\n    const String STRING_TEST_CONSTANT = \"foo\";\n    const String STRING_TEST_CONSTANT2 = \"bar\";\n\n    // Test that primitives work as parameters and return types.\n    boolean RepeatBoolean(boolean token);\n    byte RepeatByte(byte token);\n    char RepeatChar(char token);\n    int RepeatInt(int token);\n    long RepeatLong(long token);\n    float RepeatFloat(float token);\n    double RepeatDouble(double token);\n    String RepeatString(String token);\n    ByteEnum RepeatByteEnum(ByteEnum token);\n    IntEnum RepeatIntEnum(IntEnum token);\n    LongEnum RepeatLongEnum(LongEnum token);\n\n    // Test that arrays work as parameters and return types.\n    boolean[] ReverseBoolean(in boolean[] input, out boolean[] repeated);\n    byte[] ReverseByte(in byte[] input, out byte[] repeated);\n    char[] ReverseChar(in char[] input, out char[] repeated);\n    int[] ReverseInt(in int[] input, out int[] repeated);\n    long[] ReverseLong(in long[] input, out long[] repeated);\n    float[] ReverseFloat(in float[] input, out float[] repeated);\n    double[] ReverseDouble(in double[] input, out double[] repeated);\n    String[] ReverseString(in String[] input, out String[] repeated);\n    ByteEnum[] ReverseByteEnum(in ByteEnum[] input, out ByteEnum[] repeated);\n    IntEnum[] ReverseIntEnum(in IntEnum[] input, out IntEnum[] repeated);\n    LongEnum[] ReverseLongEnum(in LongEnum[] input, out LongEnum[] repeated);\n}\n"
  },
  {
    "path": "trusty/test/binder/aidl/com/android/trusty/binder/test/IntEnum.aidl",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.trusty.binder.test;\n\n@JavaDerive(toString=true)\n@Backing(type=\"int\")\nenum IntEnum {\n    FOO = 1000,\n    BAR = 2000,\n    BAZ,\n    /** @deprecated do not use this */\n    QUX,\n}\n"
  },
  {
    "path": "trusty/test/binder/aidl/com/android/trusty/binder/test/LongEnum.aidl",
    "content": "/*\n * Copyright (C) 2022 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.android.trusty.binder.test;\n\n@Backing(type=\"long\")\nenum LongEnum {\n    FOO = 100000000000,\n    BAR = 200000000000,\n    BAZ,\n}\n"
  },
  {
    "path": "trusty/test/binder/aidl/rules.mk",
    "content": "# Copyright (C) 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nLOCAL_DIR := $(GET_LOCAL_DIR)\n\nMODULE := $(LOCAL_DIR)\n\nMODULE_AIDL_PACKAGE := com/android/trusty/binder/test\n\nMODULE_AIDLS := \\\n\t$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/ByteEnum.aidl \\\n\t$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/IntEnum.aidl \\\n\t$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/ITestService.aidl \\\n\t$(LOCAL_DIR)/$(MODULE_AIDL_PACKAGE)/LongEnum.aidl \\\n\ninclude make/aidl.mk\n"
  },
  {
    "path": "trusty/test/driver/Android.bp",
    "content": "// Copyright (C) 2022 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//       http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    // See: http://go/android-license-faq\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\npython_test {\n    name: \"trusty_driver_test\",\n    srcs: [\n        \"**/*.py\",\n    ],\n    test_suites: [\"general-tests\"],\n}\n"
  },
  {
    "path": "trusty/test/driver/trusty_driver_test.py",
    "content": "#!/usr/bin/python\n#\n# Copyright 2022 The Android Open Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n# http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n\"\"\"Test cases for Trusty Linux Driver.\"\"\"\n\nimport os\nimport unittest\n\ndef ReadFile(file_path):\n    with open(file_path, 'r') as f:\n        # Strip all trailing spaces, newline and null characters.\n        return f.read().rstrip(' \\n\\x00')\n\ndef WriteFile(file_path, s):\n    with open(file_path, 'w') as f:\n        f.write(s)\n\ndef IsTrustySupported():\n    return os.path.exists(\"/dev/trusty-ipc-dev0\")\n\ndef MatchTrustyDevice(de: os.DirEntry, suffix: str):\n    for core in [\"-core\", \"\"]:\n        candidate = f\":trusty{core}{suffix}\"\n        if de.name == candidate[1:]:\n            return de\n        if de.name.endswith(candidate):\n            return de\n\n    return None\n\ndef FindTrustyDevice(suffix: str):\n    with os.scandir(\"/sys/bus/platform/devices\") as it:\n        candidates = (MatchTrustyDevice(de, suffix) for de in it)\n        final = (de.name for de in candidates if de is not None)\n        return next(final)\n\n@unittest.skipIf(not IsTrustySupported(), \"Device does not support Trusty\")\nclass TrustyDriverTest(unittest.TestCase):\n    def testIrqDriverBinding(self):\n        dev = FindTrustyDevice(\":irq\")\n        WriteFile(\"/sys/bus/platform/drivers/trusty-irq/unbind\", dev)\n        WriteFile(\"/sys/bus/platform/drivers/trusty-irq/bind\", dev)\n\n    def testLogDriverBinding(self):\n        dev = FindTrustyDevice(\":log\")\n        WriteFile(\"/sys/bus/platform/drivers/trusty-log/unbind\", dev)\n        WriteFile(\"/sys/bus/platform/drivers/trusty-log/bind\", dev)\n\n    @unittest.skip(\"TODO(b/142275662): virtio remove currently hangs\")\n    def testVirtioDriverBinding(self):\n        dev = FindTrustyDevice(\":virtio\")\n        WriteFile(\"/sys/bus/platform/drivers/trusty-virtio/unbind\", dev)\n        WriteFile(\"/sys/bus/platform/drivers/trusty-virtio/bind\", dev)\n\n    @unittest.skip(\"TODO(b/142275662): virtio remove currently hangs\")\n    def testTrustyDriverBinding(self):\n        dev = FindTrustyDevice(\"\")\n        WriteFile(\"/sys/bus/platform/drivers/trusty/unbind\", dev)\n        WriteFile(\"/sys/bus/platform/drivers/trusty/bind\", dev)\n\n    def testTrustyDriverVersion(self):\n        dev = FindTrustyDevice(\"\")\n        ver = ReadFile(f\"/sys/bus/platform/devices/{dev}/trusty_version\")\n        self.assertTrue(ver.startswith(\"Project:\"))\n\n    def testUntaintedLinux(self):\n        tainted = int(ReadFile(\"/proc/sys/kernel/tainted\"))\n        # Filter out the out-of-tree and unsigned module bits\n        tainted &= ~0x3000\n        self.assertEqual(tainted, 0)\n\n    # stdcall test with shared memory buffers.\n    # Each test run takes up to 4 arguments:\n    # <obj_size>,<obj_count=1>,<repeat_share=1>,<repeat_access=3>\n    #\n    # Test single 4K shared memory object.\n    # Test 10 8MB objects, shared twice, each accessed twice. (8MB non-\n    # contiguous object is large enough to need several 4KB messages to\n    # describe)\n    # Test sharing 2 8MB objects 100 times without accessing it.\n    # Test 10 4K shared memory objects, shared 10 times, each accessed\n    # 10 times.\n    def testStdCall(self):\n        dev = FindTrustyDevice(\":test\")\n        test = f\"/sys/bus/platform/devices/{dev}/trusty_test_run\"\n        args = \"0x1000 0x800000,10,2,2 0x800000,2,100,0 0x1000,10,10,10\"\n        WriteFile(test, args)\n\nif __name__ == '__main__':\n  unittest.main()\n"
  },
  {
    "path": "trusty/trusty-base.mk",
    "content": "#\n# Copyright (C) 2016 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# This makefile should be included by devices that use Trusty TEE\n# to pull in the baseline set of Trusty specific modules.\n#\n\n# For gatekeeper, we include the generic -service and -impl to use legacy\n# HAL loading of gatekeeper.trusty.\n\nifeq ($(KEYMINT_HAL_VENDOR_APEX_SELECT),true)\n    $(call inherit-product, system/core/trusty/keymint/trusty-keymint-apex.mk)\n\nelse\n    $(call inherit-product, system/core/trusty/keymint/trusty-keymint.mk)\n\nendif\n\nifeq ($(SECRETKEEPER_ENABLED),true)\n    LOCAL_SECRETKEEPER_PRODUCT_PACKAGE := android.hardware.security.secretkeeper.trusty\nelse\n    LOCAL_SECRETKEEPER_PRODUCT_PACKAGE :=\nendif\n\nPRODUCT_PACKAGES += \\\n\t$(LOCAL_SECRETKEEPER_PRODUCT_PACKAGE) \\\n\tandroid.hardware.gatekeeper-service.trusty \\\n\ttrusty_apploader \\\n\nPRODUCT_PROPERTY_OVERRIDES += \\\n\tro.hardware.keystore_desede=true \\\n\tro.hardware.keystore=trusty \\\n\tro.hardware.gatekeeper=trusty\n\nPRODUCT_COPY_FILES += \\\n\tframeworks/native/data/etc/android.hardware.keystore.app_attest_key.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.keystore.app_attest_key.xml\n"
  },
  {
    "path": "trusty/trusty-storage-cf.mk",
    "content": "#\n# Copyright (C) 2024 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# This makefile should be included by the cuttlefish device\n# when enabling the Trusty VM to pull in the baseline set\n# of storage specific modules\n\nPRODUCT_PACKAGES += \\\n\tstorageproxyd.system \\\n\trpmb_dev.system \\\n\trpmb_dev.test.system \\\n\n"
  },
  {
    "path": "trusty/trusty-storage.mk",
    "content": "#\n# Copyright (C) 2015 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n#\n# Trusty TEE packages\n#\n\n# below statement adds the singleton storage daemon in vendor,\n# storageproxyd vendor interacts with the Secure Storage TA in the\n# Trustzone Trusty TEE\nPRODUCT_PACKAGES += \\\n\tstorageproxyd \\\n\n#\n# Trusty VM packages\n#\nifeq ($(TRUSTY_SYSTEM_VM),enabled_with_placeholder_trusted_hal)\n\n# with placeholder Trusted HALs, the Trusty VMs are standalone (i.e. they don't access\n# remote Trusted HAL services) and thus require their own secure storage.\n# (one secure storage emulation for each Trusty VM - security VM, test VM and WV VM)\n# in secure mode, the secure storage is the services by Trusty in Trustzone\n# and requires a single storageproxyd in vendor.\nPRODUCT_PACKAGES += \\\n\tstorageproxyd.system \\\n\trpmb_dev.test.system \\\n\trpmb_dev.system \\\n\trpmb_dev.wv.system \\\n\nendif\n"
  },
  {
    "path": "trusty/trusty-test.mk",
    "content": "# Copyright (C) 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nPRODUCT_PACKAGES += \\\n\tkeymaster_soft_attestation_keys.xml \\\n\tspiproxyd \\\n\ttrusty_driver_test \\\n\ttrusty_keymaster_set_attestation_key \\\n"
  },
  {
    "path": "trusty/utils/acvp/Android.bp",
    "content": "// Copyright (C) 2021 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at //\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"trusty_acvp_modulewrapper\",\n    vendor: true,\n\n    srcs: [\n        \"trusty_modulewrapper.cpp\",\n    ],\n    static_libs: [\n        \"libacvp_modulewrapper\",\n    ],\n    shared_libs: [\n        \"libbase\",\n        \"libc\",\n        \"libdmabufheap\",\n        \"liblog\",\n        \"libtrusty\",\n        \"libssl\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n"
  },
  {
    "path": "trusty/utils/acvp/acvp_ipc.h",
    "content": "/*\n * Copyright (C) 2021 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#define ACVP_PORT \"com.android.trusty.acvp\"\n\n/*\n * Maximum number of arguments\n */\n#define ACVP_MAX_NUM_ARGUMENTS 9\n\n/*\n * Maximum length of an algorithm name\n */\n#define ACVP_MAX_NAME_LENGTH 30\n\n/*\n * Maximum length of an ACVP request message\n */\n#define ACVP_MAX_MESSAGE_LENGTH sizeof(struct acvp_req)\n\n/*\n * Minimum length of the shared memory buffer\n *\n * This must be at least as long as the longest reply from the ACVP service\n * (currently the reply from getConfig()).\n */\n#define ACVP_MIN_SHARED_MEMORY 32768\n\n/**\n * acvp_req - Request for the Trusty ACVP app\n * @num_args: Number of acvp_arg structures following this struct\n * @buffer_size: Total size of shared memory buffer\n * @lengths: Length of each argument in the shared memory buffer\n *\n * @num_args copies of the acvp_arg struct follow this structure.\n */\nstruct acvp_req {\n    uint32_t num_args;\n    uint32_t buffer_size;\n    uint32_t lengths[ACVP_MAX_NUM_ARGUMENTS];\n};\n\n/**\n * acvp_resp - Response to a ACVP request\n *\n * @num_spans: Number of response sections\n * @lengths: Length of each response section\n */\nstruct acvp_resp {\n    uint32_t num_spans;\n    uint32_t lengths[ACVP_MAX_NUM_ARGUMENTS];\n};\n\n#ifdef __cplusplus\n}  // extern \"C\"\n#endif\n"
  },
  {
    "path": "trusty/utils/acvp/trusty_modulewrapper.cpp",
    "content": "/*\n * Copyright 2021, The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"TrustyAcvpModulewrapper\"\n\n#include <BufferAllocator/BufferAllocator.h>\n#include <android-base/file.h>\n#include <android-base/result.h>\n#include <android-base/unique_fd.h>\n#include <errno.h>\n#include <iostream>\n#include <log/log.h>\n#include <modulewrapper.h>\n#include <openssl/span.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n#include <trusty/tipc.h>\n#include <unistd.h>\n#include <algorithm>\n\n#include \"acvp_ipc.h\"\n\nconstexpr const char kTrustyDeviceName[] = \"/dev/trusty-ipc-dev0\";\n\nusing android::base::ErrnoError;\nusing android::base::Error;\nusing android::base::Result;\nusing android::base::unique_fd;\nusing android::base::WriteFully;\n\n\nnamespace {\n\nclass ModuleWrapper {\n  private:\n    static const char* kAcvpPort_;\n    static const char* kTrustyDeviceName_;\n\n  public:\n    ModuleWrapper();\n    ~ModuleWrapper();\n\n    Result<void> SendMessage(bssl::Span<const bssl::Span<const uint8_t>>);\n\n    Result<void> ForwardResponse();\n\n  private:\n    // Connection to the Trusty ACVP service\n    int tipc_fd_ = -1;\n\n    // Shared memory DMA buf\n    unique_fd dmabuf_fd_;\n\n    // Size of shared memory mapping\n    size_t shm_size_ = 0;\n\n    // Shared memory mapping\n    uint8_t* shm_buffer_ = nullptr;\n};\n\n}  // namespace\n\nconst char* ModuleWrapper::kAcvpPort_ = ACVP_PORT;\nconst char* ModuleWrapper::kTrustyDeviceName_ = kTrustyDeviceName;\n\nModuleWrapper::ModuleWrapper() {\n    tipc_fd_ = tipc_connect(kTrustyDeviceName_, kAcvpPort_);\n    if (tipc_fd_ < 0) {\n        fprintf(stderr, \"Failed to connect to Trusty ACVP test app: %s\\n\", strerror(-tipc_fd_));\n    }\n}\n\nModuleWrapper::~ModuleWrapper() {\n    if (tipc_fd_ >= 0) {\n        tipc_close(tipc_fd_);\n    }\n\n    if (shm_buffer_) {\n        munmap(shm_buffer_, shm_size_);\n    }\n}\n\nResult<void> ModuleWrapper::SendMessage(bssl::Span<const bssl::Span<const uint8_t>> args) {\n    assert(args.size() < ACVP_MAX_NUM_ARGUMENTS);\n    assert(args[0].size() < ACVP_MAX_NAME_LENGTH);\n\n    struct acvp_req request;\n    request.num_args = args.size();\n\n    int total_args_size = 0;\n    for (auto arg : args) {\n        total_args_size += arg.size();\n    }\n\n    shm_size_ = std::max(ACVP_MIN_SHARED_MEMORY, total_args_size);\n    request.buffer_size = shm_size_;\n\n    struct iovec iov = {\n            .iov_base = &request,\n            .iov_len = sizeof(struct acvp_req),\n    };\n\n    BufferAllocator alloc;\n    dmabuf_fd_.reset(alloc.Alloc(kDmabufSystemHeapName, shm_size_));\n    if (!dmabuf_fd_.ok()) {\n        return ErrnoError() << \"Error creating dmabuf\";\n    }\n\n    shm_buffer_ = (uint8_t*)mmap(0, shm_size_, PROT_READ | PROT_WRITE, MAP_SHARED, dmabuf_fd_, 0);\n    if (shm_buffer_ == MAP_FAILED) {\n        return ErrnoError() << \"Failed to map shared memory dmabuf\";\n    }\n\n    size_t cur_offset = 0;\n    for (int i = 0; i < args.size(); ++i) {\n        request.lengths[i] = args[i].size();\n        memcpy(shm_buffer_ + cur_offset, args[i].data(), args[i].size());\n        cur_offset += args[i].size();\n    }\n\n    struct trusty_shm shm = {\n            .fd = dmabuf_fd_.get(),\n            .transfer = TRUSTY_SHARE,\n    };\n\n    int rc = tipc_send(tipc_fd_, &iov, 1, &shm, 1);\n    if (rc != sizeof(struct acvp_req)) {\n        return ErrnoError() << \"Failed to send request to Trusty ACVP service\";\n    }\n\n    return {};\n}\n\nResult<void> ModuleWrapper::ForwardResponse() {\n    struct acvp_resp resp;\n    int bytes_read = read(tipc_fd_, &resp, sizeof(struct acvp_resp));\n    if (bytes_read < 0) {\n        return ErrnoError() << \"Failed to read response from Trusty ACVP service\";\n    }\n\n    if (bytes_read != sizeof(struct acvp_resp)) {\n        return Error() << \"Trusty ACVP response overflowed expected size\";\n    }\n\n    size_t total_args_size = 0;\n    for (size_t i = 0; i < resp.num_spans; i++) {\n        total_args_size += resp.lengths[i];\n    }\n\n    iovec iovs[2];\n    iovs[0].iov_base = &resp;\n    iovs[0].iov_len = sizeof(uint32_t) * (1 + resp.num_spans);\n\n    iovs[1].iov_base = shm_buffer_;\n    iovs[1].iov_len = total_args_size;\n\n    size_t iov_done = 0;\n    while (iov_done < 2) {\n        ssize_t r;\n        do {\n            r = writev(STDOUT_FILENO, &iovs[iov_done], 2 - iov_done);\n        } while (r == -1 && errno == EINTR);\n\n        if (r <= 0) {\n            return Error() << \"Failed to write ACVP response to standard out\";\n        }\n\n        size_t written = r;\n        for (size_t i = iov_done; i < 2 && written > 0; i++) {\n            iovec& iov = iovs[i];\n\n            size_t done = written;\n            if (done > iov.iov_len) {\n                done = iov.iov_len;\n            }\n\n            iov.iov_base = reinterpret_cast<uint8_t*>(iov.iov_base) + done;\n            iov.iov_len -= done;\n            written -= done;\n\n            if (iov.iov_len == 0) {\n                iov_done++;\n            }\n        }\n\n        assert(written == 0);\n    }\n\n    return {};\n}\n\nstatic bool EqString(bssl::Span<const uint8_t> cmd, const char *str) {\n    return cmd.size() == strlen(str) &&\n           memcmp(str, cmd.data(), cmd.size()) == 0;\n}\n\nint main() {\n    for (;;) {\n        auto buffer = bssl::acvp::RequestBuffer::New();\n        auto args = bssl::acvp::ParseArgsFromFd(STDIN_FILENO, buffer.get());\n        if (args.empty()) {\n            ALOGE(\"Could not parse arguments\\n\");\n            return EXIT_FAILURE;\n        }\n\n        if (EqString(args[0], \"flush\")) {\n            if (!bssl::acvp::FlushBuffer(STDOUT_FILENO)) {\n                ALOGE(\"Could not flush the buffer to stdout\\n\");\n                return EXIT_FAILURE;\n            }\n        } else {\n            ModuleWrapper wrapper;\n            auto res = wrapper.SendMessage(args);\n            if (!res.ok()) {\n                std::cerr << res.error() << std::endl;\n                return EXIT_FAILURE;\n            }\n\n            res = wrapper.ForwardResponse();\n            if (!res.ok()) {\n                std::cerr << res.error() << std::endl;\n                return EXIT_FAILURE;\n            }\n        }\n    }\n\n    return EXIT_SUCCESS;\n};\n"
  },
  {
    "path": "trusty/utils/coverage-controller/Android.bp",
    "content": "// Copyright (C) 2023 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"trusty-coverage-controller\",\n    vendor: true,\n\n    srcs: [\"controller.cpp\"],\n    shared_libs: [\n        \"libc\",\n        \"liblog\",\n        \"libbase\",\n        \"libdmabufheap\",\n    ],\n    static_libs: [\n        \"libtrusty\",\n        \"libtrusty_line_coverage\",\n    ],\n}\n"
  },
  {
    "path": "trusty/utils/coverage-controller/controller.cpp",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <android-base/stringprintf.h>\n#include <array>\n#include <getopt.h>\n#include <inttypes.h>\n#include <memory>\n#include <stdbool.h>\n#include <stdio.h>\n#include <string.h>\n#include <trusty/line-coverage/coverage.h>\n#include <trusty/tipc.h>\n#include <vector>\n\n#include \"controller.h\"\n\n#define READ_ONCE(x) (*((volatile __typeof__(x) *) &(x)))\n#define WRITE_ONCE(x, val) (*((volatile __typeof__(val) *) &(x)) = (val))\n\nnamespace android {\nnamespace trusty {\nnamespace controller {\n\nusing ::android::trusty::line_coverage::CoverageRecord;\n\nvoid Controller::run(std::string output_dir) {\n    connectCoverageServer();\n    struct control *control;\n    uint64_t complete_cnt = 0, start_cnt = 0, flags;\n\n    while(1) {\n        setUpShm();\n\n        for (int index = 0; index < record_list_.size(); index++) {\n            control = (struct control *)record_list_[index]->getShm();\n            start_cnt = READ_ONCE((control->write_buffer_start_count));\n            complete_cnt = READ_ONCE(control->write_buffer_complete_count);\n            flags = READ_ONCE(control->cntrl_flags);\n\n            if (complete_cnt != counters[index] && start_cnt == complete_cnt) {\n                WRITE_ONCE(control->cntrl_flags, FLAG_NONE);\n                std::string filename;\n                filename = android::base::StringPrintf(\"/%s.%\" PRIu64 \".profraw\",\n                                                    uuid_list_[index].c_str(),\n                                                    counters[index]);\n                filename.insert(0, output_dir);\n                android::base::Result<void> res = record_list_[index]->SaveFile(filename);\n                counters[index]++;\n                WRITE_ONCE(control->read_buffer_cnt, counters[index]);\n            }\n            if(complete_cnt == counters[index] &&\n                !(flags & FLAG_RUN)) {\n                flags |= FLAG_RUN;\n                WRITE_ONCE(control->cntrl_flags, flags);\n            }\n        }\n    }\n}\n\nvoid Controller::connectCoverageServer() {\n    coverage_srv_fd = tipc_connect(TIPC_DEV, LINE_COVERAGE_CLIENT_PORT);\n    if (coverage_srv_fd < 0) {\n        fprintf(stderr, \\\n                \"Error: Failed to connect to Trusty coverage server: %d\\n\", coverage_srv_fd);\n        return;\n    }\n}\n\nvoid Controller::setUpShm() {\n    struct line_coverage_client_req req;\n    struct line_coverage_client_resp resp;\n    uint32_t cur_index = record_list_.size();\n    struct uuid zero_uuid = {0, 0, 0, { 0 }};\n    char uuid_str[UUID_STR_SIZE];\n    req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;\n    int rc = write(coverage_srv_fd, &req, sizeof(req));\n        if (rc != (int)sizeof(req)) {\n            fprintf(stderr, \"failed to send request to coverage server: %d\\n\", rc);\n            return;\n    }\n\n    while(1) {\n        rc = read(coverage_srv_fd, &resp, sizeof(resp));\n        if (rc != (int)sizeof(resp)) {\n            fprintf(stderr, \"failed to read reply from coverage server:: %d\\n\", rc);\n        }\n\n        if (resp.hdr.cmd == (req.hdr.cmd | LINE_COVERAGE_CLIENT_CMD_RESP_BIT)) {\n            if (!memcmp(&resp.send_list_args.uuid, &zero_uuid, sizeof(struct uuid))) {\n                break;\n            }\n            if(uuid_set_.find(resp.send_list_args.uuid) == uuid_set_.end()) {\n                uuid_set_.insert(resp.send_list_args.uuid);\n                sprintf(uuid_str,\n                    \"%08\" PRIx32 \"-%04\" PRIx16 \"-%04\" PRIx16 \"-%02\" PRIx8 \"%02\" PRIx8\n                    \"-%02\" PRIx8 \"%02\" PRIx8 \"%02\" PRIx8 \"%02\" PRIx8 \"%02\" PRIx8 \"%02\" PRIx8,\n                    resp.send_list_args.uuid.time_low,\n                    resp.send_list_args.uuid.time_mid,\n                    resp.send_list_args.uuid.time_hi_and_version,\n                    resp.send_list_args.uuid.clock_seq_and_node[0],\n                    resp.send_list_args.uuid.clock_seq_and_node[1],\n                    resp.send_list_args.uuid.clock_seq_and_node[2],\n                    resp.send_list_args.uuid.clock_seq_and_node[3],\n                    resp.send_list_args.uuid.clock_seq_and_node[4],\n                    resp.send_list_args.uuid.clock_seq_and_node[5],\n                    resp.send_list_args.uuid.clock_seq_and_node[6],\n                    resp.send_list_args.uuid.clock_seq_and_node[7]);\n                uuid_list_.push_back(uuid_str);\n                record_list_.push_back(std::make_unique<CoverageRecord>(TIPC_DEV,\n                                                                    &resp.send_list_args.uuid));\n                counters.push_back(0);\n            }\n        }\n        else {\n            fprintf(stderr, \"Unknown response header\\n\");\n        }\n        cur_index++;\n        req.hdr.cmd = LINE_COVERAGE_CLIENT_CMD_SEND_LIST;\n        req.send_list_args.index = cur_index;\n        int rc = write(coverage_srv_fd, &req, sizeof(req));\n        if (rc != (int)sizeof(req)) {\n            fprintf(stderr, \"failed to send request to coverage server: %d\\n\", rc);\n        }\n    }\n\n    for(int ind = 0 ; ind < record_list_.size() ; ind++) {\n        record_list_[ind]->Open(coverage_srv_fd);\n    }\n}\n\n\n}  // namespace controller\n}  // namespace trusty\n}  // namespace android\n\nint main(int argc, char* argv[]) {\n\n    std::string optarg = \"\";\n    do {\n        int c;\n        c = getopt(argc, argv, \"o\");\n\n        if (c == -1) {\n            break;\n        }\n\n        switch (c) {\n        case 'o':\n            break;\n        default:\n            fprintf(stderr, \"usage: %s -o [output_directory]\\n\", argv[0]);\n            exit(EXIT_FAILURE);\n        }\n    } while (1);\n\n    if (argc > optind + 1) {\n        fprintf(stderr, \"%s: too many arguments\\n\", argv[0]);\n        exit(EXIT_FAILURE);\n    }\n\n    if (argc > optind) {\n        optarg = argv[optind];\n    }\n    if (optarg.size()==0) {\n        optarg = \"data/local/tmp\";\n    }\n\n    android::trusty::controller::Controller cur;\n    cur.run(optarg);\n\n    return EXIT_SUCCESS;\n}"
  },
  {
    "path": "trusty/utils/coverage-controller/controller.h",
    "content": "/*\n * Copyright (C) 2023 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <trusty/line-coverage/coverage.h>\n#include <array>\n#include <memory>\n#include <vector>\n#include <set>\n\n#define TIPC_DEV \"/dev/trusty-ipc-dev0\"\n#define TEST_SRV_PORT \"com.android.trusty.sancov.test.srv\"\n#define TEST_SRV_MODULE \"srv.syms.elf\"\n\n#define UUID_STR_SIZE (37)\n\n#define FLAG_NONE               0x0\n#define FLAG_RUN                0x1\n#define FLAG_TOGGLE_CLEAR       0x2\n\nstruct control {\n    /* Written by controller, read by instrumented TA */\n    uint64_t        cntrl_flags;\n    uint64_t        read_buffer_cnt;\n\n    /* Written by instrumented TA, read by controller */\n    uint64_t        write_buffer_start_count;\n    uint64_t        write_buffer_complete_count;\n};\n\nnamespace android {\nnamespace trusty {\nnamespace controller {\n\nclass Controller {\n  public:\n    public:\n        void run(std::string output_dir);\n\n    private:\n        std::vector<std::unique_ptr<line_coverage::CoverageRecord>>record_list_;\n        std::set<struct uuid>uuid_set_;\n        std::vector<std::string>uuid_list_;\n        std::vector<uint64_t> counters;\n        int coverage_srv_fd;\n\n        void connectCoverageServer();\n        void setUpShm();\n};\n\n}  // namespace controller\n}  // namespace trusty\n}  // namespace android\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/Android.bp",
    "content": "// Copyright (C) 2019 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at //\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"rpmb_dev.cc_defaults\",\n    srcs: [\n        \"rpmb_dev.c\",\n    ],\n    shared_libs: [\n        \"libcutils\",\n        \"liblog\",\n        \"libcrypto\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\ncc_binary {\n    name: \"rpmb_dev\",\n    defaults: [\"rpmb_dev.cc_defaults\"],\n    vendor: true,\n    host_supported: true,\n    init_rc: [\n        \"rpmb_dev.rc\",\n    ],\n}\n\ncc_binary {\n    name: \"rpmb_dev.system\",\n    defaults: [\"rpmb_dev.cc_defaults\"],\n    system_ext_specific: true,\n    init_rc: [\n        \"rpmb_dev.system.rc\",\n    ],\n}\n\ncc_binary {\n    name: \"rpmb_dev.wv.system\",\n    defaults: [\"rpmb_dev.cc_defaults\"],\n    system_ext_specific: true,\n    init_rc: [\n        \"rpmb_dev.wv.system.rc\",\n    ],\n}\n\ncc_binary {\n    name: \"rpmb_dev.test.system\",\n    defaults: [\"rpmb_dev.cc_defaults\"],\n    system_ext_specific: true,\n    init_rc: [\n        \"rpmb_dev.test.system.rc\",\n    ],\n}\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/rpmb.h",
    "content": "/*\n * Copyright (C) 2015 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#ifndef __RPMB_H__\n#define __RPMB_H__\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n\nstruct rpmb_key {\n    uint8_t byte[32];\n};\n\nstruct rpmb_state;\n\n#define RPMB_BUF_SIZE 256\n\n/* provides */\nint rpmb_init(struct rpmb_state** statep,\n              void* mmc_handle,\n              const struct rpmb_key* key);\nvoid rpmb_uninit(struct rpmb_state* statep);\nint rpmb_read(struct rpmb_state* state,\n              void* buf,\n              uint16_t addr,\n              uint16_t count);\n/* count must be 1 or 2, addr must be aligned */\nint rpmb_write(struct rpmb_state* state,\n               const void* buf,\n               uint16_t addr,\n               uint16_t count,\n               bool sync);\n\n/* needs */\nint rpmb_send(void* mmc_handle,\n              void* reliable_write_buf,\n              size_t reliable_write_size,\n              void* write_buf,\n              size_t write_buf_size,\n              void* read_buf,\n              size_t read_buf_size,\n              bool sync);\n\n#endif\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/rpmb_dev.c",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"rpmb_mock\"\n\n#include \"rpmb_protocol.h\"\n\n#include <assert.h>\n#include <cutils/sockets.h>\n#include <errno.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <log/log.h>\n#include <openssl/hmac.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/socket.h>\n#include <sys/stat.h>\n#include <sys/types.h>\n#include <sys/un.h>\n#include <unistd.h>\n\n/* verbose is an int for getopt */\nstatic int verbose = false;\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n\nHMAC_CTX* HMAC_CTX_new(void) {\n    HMAC_CTX* ctx = malloc(sizeof(*ctx));\n    if (ctx != NULL) {\n        HMAC_CTX_init(ctx);\n    }\n    return ctx;\n}\n\nvoid HMAC_CTX_free(HMAC_CTX* ctx) {\n    if (ctx != NULL) {\n        HMAC_CTX_cleanup(ctx);\n        free(ctx);\n    }\n}\n\n#endif\n\n#define MAX_WRITE_COUNTER (0xffffffff)\n\nstruct rpmb_data_header {\n    uint32_t write_counter;\n    uint16_t max_block;\n    uint8_t pad1;\n    uint8_t key_programmed;\n    struct rpmb_key key;\n    uint8_t pad[512 - 4 - 2 - 1 - 1 - sizeof(struct rpmb_key)];\n};\n\n#define MAX_PACKET_COUNT (8)\n\nstruct rpmb_dev_state {\n    struct rpmb_data_header header;\n    struct rpmb_packet cmd[MAX_PACKET_COUNT];\n    struct rpmb_packet res[MAX_PACKET_COUNT];\n    uint16_t cmd_count;\n    uint16_t res_count;\n    int data_fd;\n};\n\n/* TODO: move to common location */\nstatic int rpmb_mac(struct rpmb_key key, struct rpmb_packet* packet, size_t packet_count,\n                    struct rpmb_key* mac) {\n    size_t i;\n    int hmac_ret;\n    unsigned int md_len;\n    HMAC_CTX* hmac_ctx;\n\n    hmac_ctx = HMAC_CTX_new();\n    hmac_ret = HMAC_Init_ex(hmac_ctx, &key, sizeof(key), EVP_sha256(), NULL);\n    if (!hmac_ret) {\n        ALOGE(\"HMAC_Init_ex failed\\n\");\n        goto err;\n    }\n    for (i = 0; i < packet_count; i++) {\n        hmac_ret = HMAC_Update(hmac_ctx, packet[i].data, 284);\n        if (!hmac_ret) {\n            ALOGE(\"HMAC_Update failed\\n\");\n            goto err;\n        }\n    }\n    hmac_ret = HMAC_Final(hmac_ctx, mac->byte, &md_len);\n    if (md_len != sizeof(mac->byte)) {\n        ALOGE(\"bad md_len %d != %zd\\n\", md_len, sizeof(mac->byte));\n        exit(1);\n    }\n    if (!hmac_ret) {\n        ALOGE(\"HMAC_Final failed\\n\");\n        goto err;\n    }\n\nerr:\n    HMAC_CTX_free(hmac_ctx);\n    return hmac_ret ? 0 : -1;\n}\n\nstatic int rpmb_file_seek(struct rpmb_dev_state* s, uint16_t addr) {\n    int ret;\n    int pos = addr * RPMB_PACKET_DATA_SIZE + sizeof(s->header);\n    ret = lseek(s->data_fd, pos, SEEK_SET);\n    if (ret != pos) {\n        ALOGE(\"rpmb_dev: seek to %d failed, got %d\\n\", pos, ret);\n        return -1;\n    }\n    return 0;\n}\n\nstatic uint16_t rpmb_dev_program_key(struct rpmb_dev_state* s) {\n    int ret;\n\n    if (s->header.key_programmed) {\n        return RPMB_RES_WRITE_FAILURE;\n    }\n\n    s->header.key = s->cmd[0].key_mac;\n    s->header.key_programmed = 1;\n\n    ret = lseek(s->data_fd, 0, SEEK_SET);\n    if (ret) {\n        ALOGE(\"rpmb_dev: Failed to seek rpmb data file\\n\");\n        return RPMB_RES_WRITE_FAILURE;\n    }\n\n    ret = write(s->data_fd, &s->header, sizeof(s->header));\n    if (ret != sizeof(s->header)) {\n        ALOGE(\"rpmb_dev: Failed to write rpmb key: %d, %s\\n\", ret, strerror(errno));\n\n        return RPMB_RES_WRITE_FAILURE;\n    }\n\n    return RPMB_RES_OK;\n}\n\nstatic uint16_t rpmb_dev_get_counter(struct rpmb_dev_state* s) {\n    s->res[0].write_counter = rpmb_u32(s->header.write_counter);\n\n    return RPMB_RES_OK;\n}\n\nstatic uint16_t rpmb_dev_data_write(struct rpmb_dev_state* s) {\n    uint16_t addr = rpmb_get_u16(s->cmd[0].address);\n    uint16_t block_count = s->cmd_count;\n    uint32_t write_counter;\n    int ret;\n\n    if (s->header.write_counter == MAX_WRITE_COUNTER) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Write counter expired\\n\");\n        }\n        return RPMB_RES_WRITE_FAILURE;\n    }\n\n    write_counter = rpmb_get_u32(s->cmd[0].write_counter);\n    if (s->header.write_counter != write_counter) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Invalid write counter %u. Expected: %u\\n\", write_counter,\n                  s->header.write_counter);\n        }\n        return RPMB_RES_COUNT_FAILURE;\n    }\n\n    ret = rpmb_file_seek(s, addr);\n    if (ret) {\n        ALOGE(\"rpmb_dev: Failed to seek rpmb data file\\n\");\n        return RPMB_RES_WRITE_FAILURE;\n    }\n\n    for (int i = 0; i < block_count; i++) {\n        ret = write(s->data_fd, s->cmd[i].data, RPMB_PACKET_DATA_SIZE);\n        if (ret != RPMB_PACKET_DATA_SIZE) {\n            ALOGE(\"rpmb_dev: Failed to write rpmb data file: %d, %s\\n\", ret, strerror(errno));\n            return RPMB_RES_WRITE_FAILURE;\n        }\n    }\n\n    s->header.write_counter++;\n\n    ret = lseek(s->data_fd, 0, SEEK_SET);\n    if (ret) {\n        ALOGE(\"rpmb_dev: Failed to seek rpmb data file\\n\");\n        return RPMB_RES_WRITE_FAILURE;\n    }\n\n    ret = write(s->data_fd, &s->header.write_counter, sizeof(s->header.write_counter));\n    if (ret != sizeof(s->header.write_counter)) {\n        ALOGE(\"rpmb_dev: Failed to write rpmb write counter: %d, %s\\n\", ret, strerror(errno));\n\n        return RPMB_RES_WRITE_FAILURE;\n    }\n\n    s->res[0].write_counter = rpmb_u32(s->header.write_counter);\n    return RPMB_RES_OK;\n}\n\nstatic uint16_t rpmb_dev_data_read(struct rpmb_dev_state* s) {\n    uint16_t addr;\n    uint16_t block_count;\n    int ret;\n\n    addr = rpmb_get_u16(s->cmd[0].address);\n    block_count = s->res_count;\n\n    rpmb_file_seek(s, addr);\n\n    for (int i = 0; i < block_count; i++) {\n        ret = read(s->data_fd, s->res[i].data, RPMB_PACKET_DATA_SIZE);\n        if (ret != 0 && ret != RPMB_PACKET_DATA_SIZE) {\n            ALOGE(\"rpmb_dev: Failed to read rpmb data file: %d, %s\\n\", ret, strerror(errno));\n            return RPMB_RES_READ_FAILURE;\n        }\n    }\n\n    return RPMB_RES_OK;\n}\n\nstruct rpmb_dev_cmd {\n    uint16_t (*func)(struct rpmb_dev_state* s);\n    uint16_t resp;\n    bool key_mac_is_key;\n    bool check_mac;\n    bool check_result_read;\n    bool check_key_programmed;\n    bool check_addr;\n    bool multi_packet_cmd;\n    bool multi_packet_res;\n    bool res_mac;\n};\n\nstatic struct rpmb_dev_cmd rpmb_dev_cmd_table[] = {\n        [RPMB_REQ_PROGRAM_KEY] =\n                {\n                        .func = rpmb_dev_program_key,\n                        .resp = RPMB_RESP_PROGRAM_KEY,\n                        .key_mac_is_key = true,\n                        .check_result_read = true,\n                },\n        [RPMB_REQ_GET_COUNTER] =\n                {\n                        .func = rpmb_dev_get_counter,\n                        .resp = RPMB_RESP_GET_COUNTER,\n                        .check_key_programmed = true,\n                        .res_mac = true,\n                },\n        [RPMB_REQ_DATA_WRITE] =\n                {\n                        .func = rpmb_dev_data_write,\n                        .resp = RPMB_RESP_DATA_WRITE,\n                        .check_mac = true,\n                        .check_result_read = true,\n                        .check_key_programmed = true,\n                        .check_addr = true,\n                        .multi_packet_cmd = true,\n                        .res_mac = true,\n                },\n        [RPMB_REQ_DATA_READ] =\n                {\n                        .func = rpmb_dev_data_read,\n                        .resp = RPMB_RESP_DATA_READ,\n                        .check_key_programmed = true,\n                        .check_addr = true,\n                        .multi_packet_res = true,\n                        .res_mac = true,\n                },\n};\n\n#define countof(arr) (sizeof(arr) / sizeof(arr[0]))\n\nstatic void rpmb_dev_process_cmd(struct rpmb_dev_state* s) {\n    assert(s->cmd_count > 0);\n    assert(s->res_count > 0);\n    uint16_t req_resp = rpmb_get_u16(s->cmd[0].req_resp);\n    uint16_t addr = rpmb_get_u16(s->cmd[0].address);\n    uint16_t sub_req;\n    uint16_t cmd_index = req_resp < countof(rpmb_dev_cmd_table) ? req_resp : 0;\n    struct rpmb_dev_cmd* cmd = &rpmb_dev_cmd_table[cmd_index];\n    uint16_t result = RPMB_RES_GENERAL_FAILURE;\n    struct rpmb_key mac;\n    uint16_t block_count = 0;\n\n    if (cmd->check_result_read) {\n        sub_req = rpmb_get_u16(s->cmd[s->cmd_count - 1].req_resp);\n        if (sub_req != RPMB_REQ_RESULT_READ) {\n            if (verbose) {\n                ALOGE(\"rpmb_dev: Request %d, missing result read request, got %d, cmd_count %d\\n\",\n                      req_resp, sub_req, s->cmd_count);\n            }\n            goto err;\n        }\n        assert(s->cmd_count > 1);\n        s->cmd_count--;\n    }\n\n    if (cmd->check_mac) {\n        if (rpmb_mac(s->header.key, s->cmd, s->cmd_count, &mac) != 0) {\n            ALOGE(\"rpmb_dev: failed to caclulate mac\\n\");\n            goto err;\n        }\n    } else if (cmd->key_mac_is_key) {\n        mac = s->cmd[s->cmd_count - 1].key_mac;\n    } else {\n        memset(mac.byte, 0, sizeof(mac.byte));\n    }\n\n    if (memcmp(&mac, s->cmd[s->cmd_count - 1].key_mac.byte, sizeof(mac))) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Request %d, invalid MAC, cmd_count %d\\n\", req_resp, s->cmd_count);\n        }\n        if (cmd->check_mac) {\n            result = RPMB_RES_AUTH_FAILURE;\n        }\n        goto err;\n    }\n\n    if (cmd->multi_packet_cmd) {\n        block_count = s->cmd_count;\n    }\n    if (cmd->multi_packet_res) {\n        block_count = s->res_count;\n    }\n\n    if (cmd->check_addr && (addr + block_count > s->header.max_block + 1)) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Request %d, invalid addr: 0x%x count 0x%x, Out of bounds. Max addr \"\n                  \"0x%x\\n\",\n                  req_resp, addr, block_count, s->header.max_block + 1);\n        }\n        result = RPMB_RES_ADDR_FAILURE;\n        goto err;\n    }\n    if (!cmd->check_addr && addr) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Request %d, invalid addr: 0x%x != 0\\n\", req_resp, addr);\n        }\n        goto err;\n    }\n\n    for (int i = 1; i < s->cmd_count; i++) {\n        sub_req = rpmb_get_u16(s->cmd[i].req_resp);\n        if (sub_req != req_resp) {\n            if (verbose) {\n                ALOGE(\"rpmb_dev: Request %d, sub-request mismatch, %d, at %d\\n\", req_resp, i,\n                      sub_req);\n            }\n            goto err;\n        }\n    }\n    if (!cmd->multi_packet_cmd && s->cmd_count != 1) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Request %d, bad cmd count %d, expected 1\\n\", req_resp, s->cmd_count);\n        }\n        goto err;\n    }\n    if (!cmd->multi_packet_res && s->res_count != 1) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Request %d, bad res count %d, expected 1\\n\", req_resp, s->res_count);\n        }\n        goto err;\n    }\n\n    if (cmd->check_key_programmed && !s->header.key_programmed) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Request %d, key is not programmed\\n\", req_resp);\n        }\n        s->res[0].result = rpmb_u16(RPMB_RES_NO_AUTH_KEY);\n        return;\n    }\n\n    if (!cmd->func) {\n        if (verbose) {\n            ALOGE(\"rpmb_dev: Unsupported request: %d\\n\", req_resp);\n        }\n        goto err;\n    }\n\n    result = cmd->func(s);\n\nerr:\n    if (s->header.write_counter == MAX_WRITE_COUNTER) {\n        result |= RPMB_RES_WRITE_COUNTER_EXPIRED;\n    }\n\n    for (int i = 0; i < s->res_count; i++) {\n        s->res[i].nonce = s->cmd[0].nonce;\n        s->res[i].address = rpmb_u16(addr);\n        s->res[i].block_count = rpmb_u16(block_count);\n        s->res[i].result = rpmb_u16(result);\n        s->res[i].req_resp = rpmb_u16(cmd->resp);\n    }\n    if (cmd->res_mac) {\n        rpmb_mac(s->header.key, s->res, s->res_count, &s->res[s->res_count - 1].key_mac);\n    }\n}\n\n/*\n * Receives data until one of the following is true:\n * - The buffer is full (return will be len)\n * - The connection closed (return > 0, < len)\n * - An error occurred (return will be the negative error code from recv)\n */\nssize_t recv_until(int sock, void* dest_in, size_t len) {\n    size_t bytes_recvd = 0;\n    char* dest = dest_in;\n    while (bytes_recvd < len) {\n        ssize_t ret = recv(sock, dest, len - bytes_recvd, 0);\n        if (ret < 0) {\n            return ret;\n        }\n        dest += ret;\n        bytes_recvd += ret;\n        if (ret == 0) {\n            break;\n        }\n    }\n    return bytes_recvd;\n}\n\n/*\n * Handles an incoming connection to the rpmb daemon.\n * Returns 0 if the client disconnects without violating the protocol.\n * Returns a negative value if we terminated the connection abnormally.\n *\n * Arguments:\n *   conn_sock - an fd to send/recv on\n *   s - an initialized rpmb device\n */\nint handle_conn(struct rpmb_dev_state* s, int conn_sock) {\n    int ret;\n\n    while (true) {\n        memset(s->res, 0, sizeof(s->res));\n        ret = recv_until(conn_sock, &s->res_count, sizeof(s->res_count));\n\n        /*\n         * Disconnected while not in the middle of anything.\n         */\n        if (ret <= 0) {\n            return 0;\n        }\n\n        if (s->res_count > MAX_PACKET_COUNT) {\n            ALOGE(\"rpmb_dev: Receive count too large: %d\\n\", s->res_count);\n            return -1;\n        }\n        if (s->res_count <= 0) {\n            ALOGE(\"rpmb_dev: Receive count too small: %d\\n\", s->res_count);\n            return -1;\n        }\n\n        ret = recv_until(conn_sock, &s->cmd_count, sizeof(s->cmd_count));\n        if (ret != sizeof(s->cmd_count)) {\n            ALOGE(\"rpmb_dev: Failed to read cmd_count\");\n            return -1;\n        }\n\n        if (s->cmd_count == 0) {\n            ALOGE(\"rpmb_dev: Must contain at least one command\\n\");\n            return -1;\n        }\n\n        if (s->cmd_count > MAX_PACKET_COUNT) {\n            ALOGE(\"rpmb_dev: Command count is too large\\n\");\n            return -1;\n        }\n\n        size_t cmd_size = s->cmd_count * sizeof(s->cmd[0]);\n        ret = recv_until(conn_sock, s->cmd, cmd_size);\n        if (ret != (int)cmd_size) {\n            ALOGE(\"rpmb_dev: Failed to read command: \"\n                  \"cmd_size: %zu ret: %d, %s\\n\",\n                  cmd_size, ret, strerror(errno));\n            return -1;\n        }\n\n        rpmb_dev_process_cmd(s);\n\n        size_t resp_size = sizeof(s->res[0]) * s->res_count;\n        ret = send(conn_sock, s->res, resp_size, 0);\n        if (ret != (int)resp_size) {\n            ALOGE(\"rpmb_dev: Failed to send response: %d, %s\\n\", ret, strerror(errno));\n            return -1;\n        }\n    }\n}\n\nvoid usage(const char* argv0) {\n    fprintf(stderr, \"Usage: %s [-d|--dev] <datafile> [--sock] <socket_path>\\n\", argv0);\n    fprintf(stderr, \"or:    %s [-d|--dev] <datafile> [--size <size>] [--key key]\\n\", argv0);\n}\n\nint main(int argc, char** argv) {\n    struct rpmb_dev_state s;\n    int ret;\n    int cmdres_sock;\n    struct sockaddr_un cmdres_sockaddr;\n    const char* data_file_name = NULL;\n    const char* socket_path = NULL;\n    int open_flags;\n    int init = false;\n\n    struct option long_options[] = {{\"size\", required_argument, 0, 0},\n                                    {\"key\", required_argument, 0, 0},\n                                    {\"sock\", required_argument, 0, 0},\n                                    {\"dev\", required_argument, 0, 'd'},\n                                    {\"init\", no_argument, &init, true},\n                                    {\"verbose\", no_argument, &verbose, true},\n                                    {0, 0, 0, 0}};\n\n    memset(&s.header, 0, sizeof(s.header));\n\n    while (1) {\n        int c;\n        int option_index = 0;\n        c = getopt_long(argc, argv, \"d:\", long_options, &option_index);\n        if (c == -1) {\n            break;\n        }\n\n        switch (c) {\n            /* long args */\n            case 0:\n                switch (option_index) {\n                    /* size */\n                    case 0:\n                        s.header.max_block = atoi(optarg) - 1;\n                        break;\n                    /* key */\n                    case 1:\n                        for (size_t i = 0; i < sizeof(s.header.key.byte); i++) {\n                            if (!optarg) {\n                                break;\n                            }\n                            s.header.key.byte[i] = strtol(optarg, &optarg, 16);\n                            s.header.key_programmed = 1;\n                        }\n                        break;\n                    /* sock */\n                    case 2:\n                        socket_path = optarg;\n                        break;\n                }\n                break;\n            /* dev */\n            case 'd':\n                data_file_name = optarg;\n                break;\n            default:\n                usage(argv[0]);\n                return EXIT_FAILURE;\n        }\n    }\n\n    /*\n     * We always need a data file, and at exactly one of --init or --sock\n     * must be specified.\n     */\n    if (!data_file_name || (!init == !socket_path)) {\n        usage(argv[0]);\n        return EXIT_FAILURE;\n    }\n\n    /*\n     * If the file is already initialized, exit early.\n     */\n    if (init && !access(data_file_name, F_OK)) {\n        return EXIT_SUCCESS;\n    }\n\n    open_flags = O_RDWR | O_SYNC;\n    if (init) {\n        open_flags |= O_CREAT | O_TRUNC;\n    }\n    s.data_fd = open(data_file_name, open_flags, S_IWUSR | S_IRUSR);\n    if (s.data_fd < 0) {\n        ALOGE(\"rpmb_dev: Failed to open rpmb data file, %s: %s\\n\", data_file_name, strerror(errno));\n        return EXIT_FAILURE;\n    }\n\n    if (init) {\n        /* Create new rpmb data file */\n        if (s.header.max_block == 0) {\n            s.header.max_block = 512 - 1;\n        }\n        ret = write(s.data_fd, &s.header, sizeof(s.header));\n        if (ret != sizeof(s.header)) {\n            ALOGE(\"rpmb_dev: Failed to write rpmb data file: %d, %s\\n\", ret, strerror(errno));\n            return EXIT_FAILURE;\n        }\n        return EXIT_SUCCESS;\n    }\n\n    ret = read(s.data_fd, &s.header, sizeof(s.header));\n    if (ret != sizeof(s.header)) {\n        ALOGE(\"rpmb_dev: Failed to read rpmb data file: %d, %s\\n\", ret, strerror(errno));\n        return EXIT_FAILURE;\n    }\n\n    cmdres_sock = android_get_control_socket(socket_path);\n    if (cmdres_sock < 0) {\n        ALOGW(\"android_get_control_socket(%s) failed, fall back to create it\\n\", socket_path);\n        cmdres_sock = socket(AF_UNIX, SOCK_STREAM, 0);\n        if (cmdres_sock < 0) {\n            ALOGE(\"rpmb_dev: Failed to create command/response socket: %s\\n\", strerror(errno));\n            return EXIT_FAILURE;\n        }\n\n        cmdres_sockaddr.sun_family = AF_UNIX;\n        strncpy(cmdres_sockaddr.sun_path, socket_path, sizeof(cmdres_sockaddr.sun_path));\n\n        ret = bind(cmdres_sock, (struct sockaddr*)&cmdres_sockaddr, sizeof(struct sockaddr_un));\n        if (ret < 0) {\n            ALOGE(\"rpmb_dev: Failed to bind command/response socket: %s: %s\\n\", socket_path,\n                  strerror(errno));\n            return EXIT_FAILURE;\n        }\n    }\n\n    ret = listen(cmdres_sock, 1);\n    if (ret < 0) {\n        ALOGE(\"rpmb_dev: Failed to listen on command/response socket: %s\\n\", strerror(errno));\n        return EXIT_FAILURE;\n    }\n\n    while (true) {\n        int conn_sock = accept(cmdres_sock, NULL, NULL);\n        if (conn_sock < 0) {\n            ALOGE(\"rpmb_dev: Could not accept connection: %s\\n\", strerror(errno));\n            return EXIT_FAILURE;\n        }\n        ret = handle_conn(&s, conn_sock);\n        close(conn_sock);\n        if (ret) {\n            ALOGE(\"rpmb_dev: Connection terminated: %d\", ret);\n        }\n    }\n}\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/rpmb_dev.rc",
    "content": "# RPMB Mock\non post-fs\n    mkdir /mnt/vendor/persist/ss 0770 root system\n    exec_start rpmb_mock_init\n    start rpmb_mock\n\non post-fs-data\n    mkdir /data/vendor/ss 0770 root system\n    symlink /mnt/vendor/persist/ss /data/vendor/ss/persist\n    chown root system /data/vendor/ss/persist\n    chmod 0770 /data/vendor/ss/persist\n\n    # Storage proxy\n    restart storageproxyd\n\nservice storageproxyd /vendor/bin/storageproxyd -d ${ro.hardware.trusty_ipc_dev:-/dev/trusty-ipc-dev0} \\\n        -r /dev/socket/rpmb_mock -p /data/vendor/ss -t sock\n    class early_hal\n    user system\n    group system\n\nservice rpmb_mock_init /vendor/bin/rpmb_dev --dev /mnt/vendor/persist/ss/RPMB_DATA --init --size 2048\n    disabled\n    user system\n    group system\n    oneshot\n\nservice rpmb_mock /vendor/bin/rpmb_dev --dev /mnt/vendor/persist/ss/RPMB_DATA \\\n                          --sock rpmb_mock\n    class main\n    disabled\n    user system\n    group system\n    socket rpmb_mock stream 660 system system\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/rpmb_dev.system.rc",
    "content": "service storageproxyd_system /system_ext/bin/storageproxyd.system \\\n        -d ${storageproxyd_system.trusty_ipc_dev:-/dev/trusty-ipc-dev0} \\\n        -r /dev/socket/rpmb_mock_system \\\n        -p /data/secure_storage_system \\\n        -t sock\n    disabled\n    user system\n    group system\n\nservice rpmb_mock_init_system /system_ext/bin/rpmb_dev.system \\\n        --dev /mnt/secure_storage_rpmb_system/persist/RPMB_DATA --init --size 2048\n    disabled\n    user system\n    group system\n    oneshot\n\nservice rpmb_mock_system /system_ext/bin/rpmb_dev.system \\\n        --dev /mnt/secure_storage_rpmb_system/persist/RPMB_DATA \\\n        --sock rpmb_mock_system\n    disabled\n    user system\n    group system\n    socket rpmb_mock_system stream 660 system system\n\n# storageproxyd\non late-fs && \\\n    property:trusty.security_vm.nonsecure_vm_ready=1 && \\\n    property:storageproxyd_system.trusty_ipc_dev=*\n    wait /dev/socket/rpmb_mock_system\n    start storageproxyd_system\n\n\n# RPMB Mock\non post-fs && \\\n    property:trusty.security_vm.nonsecure_vm_ready=1 && \\\n    property:trusty.security_vm.vm_cid=*\n    # Create a persistent location for the RPMB data\n    # (work around lack of RPMb block device on CF).\n    # file contexts secure_storage_rpmb_system_file\n    # (only used on Cuttlefish as this is non secure)\n    mkdir /metadata/secure_storage_rpmb_system 0770 system system\n    mkdir /mnt/secure_storage_rpmb_system 0770 system system\n    symlink /metadata/secure_storage_rpmb_system \\\n            /mnt/secure_storage_rpmb_system/persist\n    # Create a system persist directory in /metadata\n    # (work around lack of dedicated system persist partition).\n    # file contexts secure_storage_persist_system_file\n    mkdir /metadata/secure_storage_persist_system 0770 system system\n    mkdir /mnt/secure_storage_persist_system 0770 system system\n    symlink /metadata/secure_storage_persist_system \\\n            /mnt/secure_storage_persist_system/persist\n    setprop storageproxyd_system.trusty_ipc_dev VSOCK:${trusty.security_vm.vm_cid}:1\n    exec_start rpmb_mock_init_system\n    start rpmb_mock_system\n\non post-fs-data && \\\n    property:trusty.security_vm.nonsecure_vm_ready=1 && \\\n    property:storageproxyd_system.trusty_ipc_dev=*\n    # file contexts secure_storage_system_file\n    mkdir /data/secure_storage_system 0770 root system\n    symlink /mnt/secure_storage_persist_system/persist \\\n            /data/secure_storage_system/persist\n    chown root system /data/secure_storage_system/persist\n    restart storageproxyd_system\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/rpmb_dev.test.system.rc",
    "content": "service storageproxyd_test_vm /system_ext/bin/storageproxyd.system \\\n        -d VSOCK:${trusty.test_vm.vm_cid}:1 \\\n        -r /dev/socket/rpmb_mock_test_system \\\n        -p /data/secure_storage_test_system \\\n        -t sock\n    disabled\n    class hal\n    user system\n    group system\n\nservice storageproxyd_test_vm_os /system_ext/bin/storageproxyd.system \\\n        -d VSOCK:${trusty.test_vm_os.vm_cid}:1 \\\n        -r /dev/socket/rpmb_mock_test_system \\\n        -p /data/secure_storage_test_system \\\n        -t sock\n    disabled\n    class hal\n    user system\n    group system\n\nservice rpmb_mock_init_test_system /system_ext/bin/rpmb_dev.test.system \\\n        --dev /mnt/secure_storage_rpmb_test_system/persist/RPMB_DATA --init --size 2048\n    disabled\n    user system\n    group system\n    oneshot\n\nservice rpmb_mock_test_system /system_ext/bin/rpmb_dev.test.system \\\n        --dev /mnt/secure_storage_rpmb_test_system/persist/RPMB_DATA \\\n        --sock rpmb_mock_test_system\n    disabled\n    user system\n    group system\n    socket rpmb_mock_test_system stream 660 system system\n\n# RPMB Mock\non post-fs-data\n    # Create a persistent location for the RPMB data\n    # (work around lack of RPMb block device on CF).\n    # file contexts secure_storage_rpmb_system_file\n    # (only used on Cuttlefish as this is non secure)\n    mkdir /metadata/secure_storage_rpmb_test_system 0770 system system\n    mkdir /mnt/secure_storage_rpmb_test_system 0770 system system\n    symlink /metadata/secure_storage_rpmb_test_system \\\n            /mnt/secure_storage_rpmb_test_system/persist\n    # Create a system persist directory in /metadata\n    # (work around lack of dedicated system persist partition).\n    # file contexts secure_storage_persist_system_file\n    mkdir /metadata/secure_storage_persist_test_system 0770 system system\n    mkdir /mnt/secure_storage_persist_test_system 0770 system system\n    symlink /metadata/secure_storage_persist_test_system \\\n            /mnt/secure_storage_persist_test_system/persist\n    # file contexts secure_storage_system_file\n    mkdir /data/secure_storage_test_system 0770 root system\n    symlink /mnt/secure_storage_persist_test_system/persist \\\n            /data/secure_storage_test_system/persist\n    chown root system /data/secure_storage_test_system/persist\n    # setprop storageproxyd_test_system.trusty_ipc_dev VSOCK:${trusty.test_vm.vm_cid}:1\n    exec_start rpmb_mock_init_test_system\n    start rpmb_mock_test_system\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/rpmb_dev.wv.system.rc",
    "content": "service storageproxyd_wv_system /system_ext/bin/storageproxyd.system \\\n        -d VSOCK:${trusty.widevine_vm.vm_cid}:1 \\\n        -r /dev/socket/rpmb_mock_wv_system \\\n        -p /data/secure_storage_wv_system \\\n        -t sock\n    disabled\n    user system\n    group system\n\nservice rpmb_mock_init_wv_system /system_ext/bin/rpmb_dev.wv.system \\\n        --dev /mnt/secure_storage_rpmb_wv_system/persist/RPMB_DATA --init --size 2048\n    disabled\n    user system\n    group system\n    oneshot\n\nservice rpmb_mock_wv_system /system_ext/bin/rpmb_dev.wv.system \\\n        --dev /mnt/secure_storage_rpmb_wv_system/persist/RPMB_DATA \\\n        --sock rpmb_mock_wv_system\n    disabled\n    user system\n    group system\n    socket rpmb_mock_wv_system stream 660 system system\n\n# RPMB Mock\non early-boot\n    # Create a persistent location for the RPMB data\n    # (work around lack of RPMb block device on CF).\n    # file contexts secure_storage_rpmb_system_file\n    # (only used on Cuttlefish as this is non secure)\n    mkdir /metadata/secure_storage_rpmb_wv_system 0770 system system\n    mkdir /mnt/secure_storage_rpmb_wv_system 0770 system system\n    symlink /metadata/secure_storage_rpmb_wv_system \\\n            /mnt/secure_storage_rpmb_wv_system/persist\n    # Create a system persist directory in /metadata\n    # (work around lack of dedicated system persist partition).\n    # file contexts secure_storage_persist_system_file\n    mkdir /metadata/secure_storage_persist_wv_system 0770 system system\n    mkdir /mnt/secure_storage_persist_wv_system 0770 system system\n    symlink /metadata/secure_storage_persist_wv_system \\\n            /mnt/secure_storage_persist_wv_system/persist\n    # file contexts secure_storage_system_file\n    mkdir /data/secure_storage_wv_system 0770 root system\n    symlink /mnt/secure_storage_persist_wv_system/persist \\\n            /data/secure_storage_wv_system/persist\n    chown root system /data/secure_storage_wv_system/persist\n    exec_start rpmb_mock_init_wv_system\n    start rpmb_mock_wv_system\n\non post-fs-data && \\\n    property:trusty.widevine_vm.nonsecure_vm_ready=1 && \\\n    property:trusty.widevine_vm.vm_cid=*\n    start storageproxyd_wv_system\n\n"
  },
  {
    "path": "trusty/utils/rpmb_dev/rpmb_protocol.h",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#pragma once\n\n#include <stdint.h>\n\n#include \"rpmb.h\" /* For struct rpmb_key */\n\n#define MMC_READ_MULTIPLE_BLOCK 18\n#define MMC_WRITE_MULTIPLE_BLOCK 25\n#define MMC_RELIABLE_WRITE_FLAG (1 << 31)\n\n#define MMC_RSP_PRESENT (1 << 0)\n#define MMC_RSP_CRC (1 << 2)\n#define MMC_RSP_OPCODE (1 << 4)\n#define MMC_CMD_ADTC (1 << 5)\n#define MMC_RSP_SPI_S1 (1 << 7)\n#define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)\n#define MMC_RSP_SPI_R1 (MMC_RSP_SPI_S1)\n\nstruct rpmb_nonce {\n    uint8_t byte[16];\n};\n\nstruct rpmb_u16 {\n    uint8_t byte[2];\n};\n\nstruct rpmb_u32 {\n    uint8_t byte[4];\n};\n\n#define RPMB_PACKET_DATA_SIZE (256)\n\nstruct rpmb_packet {\n    uint8_t pad[196];\n    struct rpmb_key key_mac;\n    uint8_t data[RPMB_PACKET_DATA_SIZE];\n    struct rpmb_nonce nonce;\n    struct rpmb_u32 write_counter;\n    struct rpmb_u16 address;\n    struct rpmb_u16 block_count;\n    struct rpmb_u16 result;\n    struct rpmb_u16 req_resp;\n};\n\nenum rpmb_request {\n    RPMB_REQ_PROGRAM_KEY = 0x0001,\n    RPMB_REQ_GET_COUNTER = 0x0002,\n    RPMB_REQ_DATA_WRITE = 0x0003,\n    RPMB_REQ_DATA_READ = 0x0004,\n    RPMB_REQ_RESULT_READ = 0x0005,\n};\n\nenum rpmb_response {\n    RPMB_RESP_PROGRAM_KEY = 0x0100,\n    RPMB_RESP_GET_COUNTER = 0x0200,\n    RPMB_RESP_DATA_WRITE = 0x0300,\n    RPMB_RESP_DATA_READ = 0x0400,\n};\n\nenum rpmb_result {\n    RPMB_RES_OK = 0x0000,\n    RPMB_RES_GENERAL_FAILURE = 0x0001,\n    RPMB_RES_AUTH_FAILURE = 0x0002,\n    RPMB_RES_COUNT_FAILURE = 0x0003,\n    RPMB_RES_ADDR_FAILURE = 0x0004,\n    RPMB_RES_WRITE_FAILURE = 0x0005,\n    RPMB_RES_READ_FAILURE = 0x0006,\n    RPMB_RES_NO_AUTH_KEY = 0x0007,\n\n    RPMB_RES_WRITE_COUNTER_EXPIRED = 0x0080,\n};\n\nstatic inline struct rpmb_u16 rpmb_u16(uint16_t val) {\n    struct rpmb_u16 ret = {{\n            (uint8_t)(val >> 8),\n            (uint8_t)(val >> 0),\n    }};\n    return ret;\n}\n\nstatic inline struct rpmb_u32 rpmb_u32(uint32_t val) {\n    struct rpmb_u32 ret = {{\n            (uint8_t)(val >> 24),\n            (uint8_t)(val >> 16),\n            (uint8_t)(val >> 8),\n            (uint8_t)(val >> 0),\n    }};\n    return ret;\n}\n\nstatic inline uint16_t rpmb_get_u16(struct rpmb_u16 u16) {\n    size_t i;\n    uint16_t val;\n\n    val = 0;\n    for (i = 0; i < sizeof(u16.byte); i++)\n        val = val << 8 | u16.byte[i];\n\n    return val;\n}\n\nstatic inline uint32_t rpmb_get_u32(struct rpmb_u32 u32) {\n    size_t i;\n    uint32_t val;\n\n    val = 0;\n    for (i = 0; i < sizeof(u32.byte); i++)\n        val = val << 8 | u32.byte[i];\n\n    return val;\n}\n"
  },
  {
    "path": "trusty/utils/spiproxyd/Android.bp",
    "content": "// Copyright (C) 2020 The Android Open-Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"spiproxyd\",\n    vendor: true,\n\n    srcs: [\n        \"main.c\",\n    ],\n\n    shared_libs: [\n        \"liblog\",\n        \"libtrusty\",\n    ],\n\n    init_rc: [\n        \"proxy.rc\",\n    ],\n\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n"
  },
  {
    "path": "trusty/utils/spiproxyd/main.c",
    "content": "/*\n * Copyright (C) 2020 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"spiproxyd\"\n\n#include <assert.h>\n#include <fcntl.h>\n#include <getopt.h>\n#include <log/log.h>\n#include <stdlib.h>\n#include <string.h>\n#include <trusty/tipc.h>\n#include <unistd.h>\n\nint handle_msg(int trusty_dev_fd, int spi_dev_fd) {\n    int rc;\n    uint8_t msg_buf[4096];\n    size_t msg_len;\n\n    /* read request from SPI Trusty app */\n    rc = read(trusty_dev_fd, &msg_buf, sizeof(msg_buf));\n    if (rc < 0) {\n        ALOGE(\"failed (%d) to read request from TA\\n\", rc);\n        return rc;\n    }\n    msg_len = rc;\n\n    /* forward request to SPI host device */\n    rc = write(spi_dev_fd, &msg_buf, msg_len);\n    if (rc < 0 || (size_t)rc != msg_len) {\n        ALOGE(\"failed (%d) to forward request to host\\n\", rc);\n        return rc < 0 ? rc : -1;\n    }\n\n    /* read response from SPI host device */\n    rc = read(spi_dev_fd, &msg_buf, sizeof(msg_buf));\n    if (rc < 0) {\n        ALOGE(\"failed (%d) to read response from host\\n\", rc);\n        return rc;\n    }\n    msg_len = rc;\n\n    /* forward response to SPI Trusty app */\n    rc = write(trusty_dev_fd, &msg_buf, msg_len);\n    if (rc < 0 || (size_t)rc != msg_len) {\n        ALOGE(\"failed (%d) to forward response to TA\\n\", rc);\n        return rc < 0 ? rc : -1;\n    }\n\n    return 0;\n}\n\nint event_loop(int trusty_dev_fd, int spi_dev_fd) {\n    while (true) {\n        int rc = handle_msg(trusty_dev_fd, spi_dev_fd);\n        if (rc < 0) {\n            ALOGE(\"exiting event loop\\n\");\n            return EXIT_FAILURE;\n        }\n    }\n}\n\nstatic void show_usage() {\n    ALOGE(\"usage: spiproxyd -t TRUSTY_DEVICE -s SPI_DEVICE -p SPI_PROXY_PORT\\n\");\n}\n\nstatic void parse_args(int argc, char* argv[], const char** trusty_dev_name,\n                       const char** spi_dev_name, const char** spi_proxy_port) {\n    int opt;\n    while ((opt = getopt(argc, argv, \"ht:s:p:\")) != -1) {\n        switch (opt) {\n            case 'h':\n                show_usage();\n                exit(EXIT_SUCCESS);\n                break;\n            case 't':\n                *trusty_dev_name = strdup(optarg);\n                break;\n            case 's':\n                *spi_dev_name = strdup(optarg);\n                break;\n            case 'p':\n                *spi_proxy_port = strdup(optarg);\n                break;\n            default:\n                show_usage();\n                exit(EXIT_FAILURE);\n                break;\n        }\n    }\n\n    if (!*trusty_dev_name || !*spi_dev_name || !*spi_proxy_port) {\n        show_usage();\n        exit(EXIT_FAILURE);\n    }\n}\n\nint main(int argc, char* argv[]) {\n    int rc;\n    const char* trusty_dev_name = NULL;\n    const char* spi_dev_name = NULL;\n    const char* spi_proxy_port = NULL;\n    int trusty_dev_fd;\n    int spi_dev_fd;\n\n    parse_args(argc, argv, &trusty_dev_name, &spi_dev_name, &spi_proxy_port);\n\n    rc = tipc_connect(trusty_dev_name, spi_proxy_port);\n    if (rc < 0) {\n        ALOGE(\"failed (%d) to connect to SPI proxy port\\n\", rc);\n        return rc;\n    }\n    trusty_dev_fd = rc;\n\n    rc = open(spi_dev_name, O_RDWR, 0);\n    if (rc < 0) {\n        ALOGE(\"failed (%d) to open SPI device\\n\", rc);\n        return rc;\n    }\n    spi_dev_fd = rc;\n\n    return event_loop(trusty_dev_fd, spi_dev_fd);\n}\n"
  },
  {
    "path": "trusty/utils/spiproxyd/proxy.rc",
    "content": "# Copyright (C) 2020 The Android Open-Source Project\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nservice spiproxyd /vendor/bin/spiproxyd -t /dev/trusty-ipc-dev0 \\\n        -s /dev/vport3p2 -p com.android.trusty.spi.proxy\n    class main\n    user system\n    group system\n    oneshot\n"
  },
  {
    "path": "trusty/utils/trusty-ut-ctrl/Android.bp",
    "content": "// Copyright (C) 2018 The Android Open Source Project\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//      http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"trusty-ut-ctrl.defaults\",\n\n    srcs: [\"ut-ctrl.c\"],\n    shared_libs: [\n        \"libc\",\n        \"liblog\",\n    ],\n    static_libs: [\n        \"libtrusty\",\n    ],\n    cflags: [\n        \"-Wall\",\n        \"-Werror\",\n    ],\n}\n\ncc_binary {\n    name: \"trusty-ut-ctrl\",\n    defaults: [\"trusty-ut-ctrl.defaults\"],\n    vendor: true,\n}\n\ncc_test {\n    name: \"trusty-ut-ctrl.system\",\n    defaults: [\"trusty-ut-ctrl.defaults\"],\n    gtest: false,\n}\n"
  },
  {
    "path": "trusty/utils/trusty-ut-ctrl/ut-ctrl.c",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <getopt.h>\n#include <stdbool.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/uio.h>\n#include <unistd.h>\n\n#include <trusty/tipc.h>\n\n#define TIPC_DEFAULT_DEVNAME \"/dev/trusty-ipc-dev0\"\n\nstatic const char* dev_name = NULL;\nstatic const char* ut_app = NULL;\n\nstatic const char* _sopts = \"hD:\";\nstatic const struct option _lopts[] = {\n        {\"help\", no_argument, 0, 'h'},\n        {\"dev\", required_argument, 0, 'D'},\n        {0, 0, 0, 0},\n};\n\nstatic const char* usage =\n        \"Usage: %s [options] unittest-app\\n\"\n        \"\\n\"\n        \"options:\\n\"\n        \"  -h, --help            prints this message and exit\\n\"\n        \"  -D, --dev name        Trusty device name\\n\"\n        \"\\n\";\n\nstatic const char* usage_long = \"\\n\";\n\nstatic bool opt_silent = false;\n\nstatic void print_usage_and_exit(const char* prog, int code, bool verbose) {\n    fprintf(stderr, usage, prog);\n    if (verbose) {\n        fprintf(stderr, \"%s\", usage_long);\n    }\n    exit(code);\n}\n\nstatic void parse_options(int argc, char** argv) {\n    int c;\n    int oidx = 0;\n\n    while (1) {\n        c = getopt_long(argc, argv, _sopts, _lopts, &oidx);\n        if (c == -1) {\n            break; /* done */\n        }\n\n        switch (c) {\n            case 'D':\n                dev_name = strdup(optarg);\n                break;\n\n            case 's':\n                opt_silent = true;\n                break;\n\n            case 'h':\n                print_usage_and_exit(argv[0], EXIT_SUCCESS, true);\n                break;\n\n            default:\n                print_usage_and_exit(argv[0], EXIT_FAILURE, false);\n        }\n    }\n\n    if (optind < argc) {\n        ut_app = strdup(argv[optind]);\n    }\n}\n\nenum test_message_header {\n    TEST_PASSED = 0,\n    TEST_FAILED = 1,\n    TEST_MESSAGE = 2,\n    TEST_TEXT = 3,\n    TEST_OPCODE_COUNT,\n};\n\nstatic int get_msg_len(const char* buf, int max_buf_len) {\n    int buf_len;\n    for (buf_len = 0; buf_len < max_buf_len; buf_len++) {\n        if ((unsigned char)buf[buf_len] < TEST_OPCODE_COUNT) {\n            break;\n        }\n    }\n    return buf_len;\n}\n\nstatic int run_trusty_unitest(const char* utapp) {\n    int fd;\n    char read_buf[1024];\n    int read_len;\n    char* rx_buf;\n    int rx_buf_len;\n    int cmd = -1;\n\n    /* connect to unitest app */\n    fd = tipc_connect(dev_name, utapp);\n    if (fd < 0) {\n        fprintf(stderr, \"failed to connect to '%s' app: %s\\n\", utapp, strerror(-fd));\n        return fd;\n    }\n\n    /* wait for test to complete */\n    rx_buf_len = 0;\n    for (;;) {\n        if (rx_buf_len == 0) {\n            read_len = read(fd, read_buf, sizeof(read_buf));\n            if (read_len <= 0 || read_len > (int)sizeof(read_buf)) {\n                fprintf(stderr, \"%s: Read failed: %d, %s\\n\", __func__, read_len,\n                        read_len < 0 ? strerror(errno) : \"\");\n                tipc_close(fd);\n                return -1;\n            }\n            rx_buf = read_buf;\n            rx_buf_len = read_len;\n        }\n\n        int msg_len = get_msg_len(rx_buf, rx_buf_len);\n        if (msg_len == 0) {\n            cmd = rx_buf[0];\n            rx_buf++;\n            rx_buf_len--;\n        }\n\n        if (cmd == TEST_PASSED) {\n            break;\n        } else if (cmd == TEST_FAILED) {\n            break;\n        } else if (cmd == TEST_MESSAGE || cmd == TEST_TEXT) {\n            if (msg_len) {\n                write(STDOUT_FILENO, rx_buf, msg_len);\n                rx_buf += msg_len;\n                rx_buf_len -= msg_len;\n            }\n        } else {\n            fprintf(stderr, \"%s: Bad message header: %d\\n\", __func__, cmd);\n            break;\n        }\n    }\n\n    /* close connection to unitest app */\n    tipc_close(fd);\n\n    return cmd == TEST_PASSED ? 0 : -1;\n}\n\nint main(int argc, char** argv) {\n    int rc = 0;\n\n    if (argc <= 1) {\n        print_usage_and_exit(argv[0], EXIT_FAILURE, false);\n    }\n\n    parse_options(argc, argv);\n\n    if (!dev_name) {\n        dev_name = TIPC_DEFAULT_DEVNAME;\n    }\n\n    if (!ut_app) {\n        fprintf(stderr, \"Unittest app must be specified\\n\");\n        print_usage_and_exit(argv[0], EXIT_FAILURE, false);\n    }\n\n    rc = run_trusty_unitest(ut_app);\n\n    return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;\n}\n"
  },
  {
    "path": "usbd/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_binary {\n    name: \"usbd\",\n    init_rc: [\"usbd.rc\"],\n    srcs: [\"usbd.cpp\"],\n    shared_libs: [\n        \"libbase\",\n        \"libbinder_ndk\",\n        \"libhidlbase\",\n        \"liblog\",\n        \"libutils\",\n        \"libhardware\",\n        \"android.hardware.usb.gadget@1.0\",\n        \"android.hardware.usb.gadget-V1-ndk\",\n    ],\n}\n"
  },
  {
    "path": "usbd/usbd.cpp",
    "content": "/*\n * Copyright (C) 2018 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#define LOG_TAG \"usbd\"\n\n#include <string>\n\n#include <aidl/android/hardware/usb/gadget/GadgetFunction.h>\n#include <aidl/android/hardware/usb/gadget/IUsbGadget.h>\n#include <android-base/logging.h>\n#include <android-base/properties.h>\n#include <android/binder_manager.h>\n#include <android/binder_process.h>\n#include <android/hardware/usb/gadget/1.0/IUsbGadget.h>\n\nusing aidl::android::hardware::usb::gadget::GadgetFunction;\nusing android::base::GetProperty;\nusing android::base::SetProperty;\nusing android::hardware::Return;\nusing ndk::ScopedAStatus;\nusing std::shared_ptr;\n\nstd::atomic<int> sUsbOperationCount{};\n\nint main(int /*argc*/, char** /*argv*/) {\n    if (GetProperty(\"ro.bootmode\", \"\") == \"charger\") exit(0);\n    int operationId = sUsbOperationCount++;\n\n    ABinderProcess_setThreadPoolMaxThreadCount(1);\n    ABinderProcess_startThreadPool();\n    const std::string service_name =\n            std::string(aidl::android::hardware::usb::gadget::IUsbGadget::descriptor)\n                    .append(\"/default\");\n\n    std::string function = GetProperty(\"persist.sys.usb.config\", \"\");\n    if (function == \"adb\") {\n        LOG(INFO) << \"persistent prop is adb\";\n        SetProperty(\"ctl.start\", \"adbd\");\n    }\n\n    if (AServiceManager_isDeclared(service_name.c_str())) {\n        shared_ptr<aidl::android::hardware::usb::gadget::IUsbGadget> gadget_aidl =\n                aidl::android::hardware::usb::gadget::IUsbGadget::fromBinder(\n                        ndk::SpAIBinder(AServiceManager_waitForService(service_name.c_str())));\n        ScopedAStatus ret;\n        if (gadget_aidl != nullptr) {\n            LOG(INFO) << \"Usb AIDL HAL found.\";\n            if (function == \"adb\") {\n                ret = gadget_aidl->setCurrentUsbFunctions(\n                        static_cast<uint64_t>(GadgetFunction::ADB), nullptr, 0, operationId);\n            } else {\n                LOG(INFO) << \"Signal MTP to enable default functions\";\n                ret = gadget_aidl->setCurrentUsbFunctions(\n                        static_cast<uint64_t>(GadgetFunction::MTP), nullptr, 0, operationId);\n            }\n\n            if (!ret.isOk()) LOG(ERROR) << \"Error while invoking usb hal\";\n        } else {\n            LOG(INFO) << \"Usb AIDL HAL not found\";\n        }\n    } else {\n        android::sp<android::hardware::usb::gadget::V1_0::IUsbGadget> gadget =\n                android::hardware::usb::gadget::V1_0::IUsbGadget::getService();\n        Return<void> ret;\n        if (gadget != nullptr) {\n            LOG(INFO) << \"Usb HAL found.\";\n            if (function == \"adb\") {\n                ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::ADB),\n                                                     nullptr, 0);\n            } else {\n                LOG(INFO) << \"Signal MTP to enable default functions\";\n                ret = gadget->setCurrentUsbFunctions(static_cast<uint64_t>(GadgetFunction::MTP),\n                                                     nullptr, 0);\n            }\n\n            if (!ret.isOk()) LOG(ERROR) << \"Error while invoking usb hal\";\n        } else {\n            LOG(INFO) << \"Usb HAL not found\";\n        }\n    }\n    exit(0);\n}\n"
  },
  {
    "path": "usbd/usbd.rc",
    "content": "service usbd /system/bin/usbd\n    class late_start\n    oneshot\n    user root\n    group root usb system\n"
  },
  {
    "path": "watchdogd/Android.bp",
    "content": "package {\n    default_applicable_licenses: [\"Android-Apache-2.0\"],\n}\n\ncc_defaults {\n    name: \"watchdogd_defaults\",\n    srcs: [\"watchdogd.cpp\"],\n    cflags: [\n        \"-Wall\",\n        \"-Wextra\",\n        \"-Werror\",\n    ],\n    shared_libs: [\"libbase\"],\n    sanitize: {\n        misc_undefined: [\"signed-integer-overflow\"],\n    },\n}\n\ncc_binary {\n    name: \"watchdogd\",\n    defaults: [\n        \"watchdogd_defaults\",\n    ],\n}\n\ncc_binary {\n    name: \"watchdogd.recovery\",\n    defaults: [\n        \"watchdogd_defaults\",\n    ],\n    recovery: true,\n    stem: \"watchdogd\",\n}\n"
  },
  {
    "path": "watchdogd/watchdogd.cpp",
    "content": "/*\n * Copyright (C) 2012 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n#include <errno.h>\n#include <fcntl.h>\n#include <linux/watchdog.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <android-base/logging.h>\n\n#define DEV_NAME \"/dev/watchdog\"\n\nint main(int argc, char** argv) {\n    android::base::InitLogging(argv, &android::base::KernelLogger);\n\n    int interval = 10;\n    if (argc >= 2) interval = atoi(argv[1]);\n\n    int margin = 10;\n    if (argc >= 3) margin = atoi(argv[2]);\n\n    LOG(INFO) << \"watchdogd started (interval \" << interval << \", margin \" << margin << \")!\";\n\n    int fd = open(DEV_NAME, O_RDWR | O_CLOEXEC);\n    if (fd == -1) {\n        PLOG(ERROR) << \"Failed to open \" << DEV_NAME;\n        return 1;\n    }\n\n    int timeout = interval + margin;\n    int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);\n    if (ret) {\n        PLOG(ERROR) << \"Failed to set timeout to \" << timeout;\n        ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);\n        if (ret) {\n            PLOG(ERROR) << \"Failed to get timeout\";\n        } else {\n            if (timeout > margin) {\n                interval = timeout - margin;\n            } else {\n                interval = 1;\n            }\n            LOG(WARNING) << \"Adjusted interval to timeout returned by driver: \"\n                         << \"timeout \" << timeout << \", interval \" << interval << \", margin \"\n                         << margin;\n        }\n    }\n\n    while (true) {\n        write(fd, \"\", 1);\n        sleep(interval);\n    }\n}\n"
  }
]